Creating custom views rows wrapper style formatter

Html markup example with the wrapped rows result

While developing, sometimes I had to group view rows results by two or three in a row and wrap them into some divs as it shown on the screenshot.

In order to make this happen I had to make some coding into views-view-unformatted.tpl.php  template.

Finally, I decided to write a module which would handle this functionality as simple as possible. I have also contributed this module for the Drupal community on drupal.org.

The module defines “Rows wrapper” views style plugin which is available for selection at “Format” section on the view Displays page.

Views rows wrapper style
Views rows wrapper style


This module has the interface under settings page, where we can select number of rows and div or span wrapper element with its class/id attributes.

Views style plugin settings page
Views style plugin settings page


In this tutorial I'm going to show how it was done.

Create a module

For the name of the module I will use view_rows_wrapper name.
The module will implement two views hooks hook_views_api(), hook_views_plugins() and template_preprocess_hook() for the output render.

As the first step in developing this module, create views_rows_wrapper folder into sites/all/modules and put inside the info file:

views_rows_wrapper.info

name = Views Rows Wrapper
description = The view style plugin, that combines a user defined number of rows into sets, wrapped by chosen elements and attributes.
version = 7.x-1.0
core = 7.x
files[] = views_rows_wrapper_plugin_style_rows_wrapper.inc

Then create views_rows_wrapper.views.inc file where hook_views_plugins() hook will be implemented.

views_rows_wrapper.views.inc

<?php

/**
 * Implementation of hook_views_plugins().
 */

function views_rows_wrapper_views_plugins() {
  return array(
    'module' => 'views_rows_wrapper',
    'style' => array(
      'rows_wrapper' => array(
        'title' => t('Rows wrapper'),
        'handler' => 'views_rows_wrapper_plugin_style_rows_wrapper',
        'path' => drupal_get_path('module', 'views_rows_wrapper'),
        'theme' => 'views_rows_wrapper',
        'js' => array(),
        'type' => 'normal',
        'uses row plugin' => TRUE,
        'uses fields' => TRUE,
        'uses options' => TRUE,
        'uses grouping' => FALSE,
        'even empty' => FALSE,
      ),
    ),
  );
}

This hook returns array with parameters of the new view style plugin. Make sure you are using the proper naming for the handler class, the name pattern should be set as [modulename]_plugin_style_[stylename].

In our case the style name we define as "rows_wrapper" so the handler class name will be 'views_rows_wrapper_plugin_style_rows_wrapper

On the next step, create a file views_rows_wrapper_plugin_style_rows_wrapper.inc
The file name should be the same as our style handler name + .inc extension. It was actually listed as an include file in our .info file.

Inside that file will will extend the views_plugin_style class and implement settings page for our view style.
The options_form returns the array with the form fields which we will use for the plugin.

views_rows_wrapper_plugin_style_rows_wrapper.inc

<?php

/**
 * @file
 * Contains views_plugin_style_rows_wrapper.
 */

/**
 * Defines a style plugin that wraps rows inside view.
 *
 * @ingroup views_style_plugins
 */


class views_rows_wrapper_plugin_style_rows_wrapper extends views_plugin_style {
  /**
   * Overrides views_plugin_style::options_definition().
   */
  function option_definition() {
    $options = parent::option_definition();
    $options['use_wrapper'] = array('default' => '1');
    $options['element_type'] = array('default' => '0');
    $options['attribute_type'] = array('default' => '0');
    $options['attribute_name'] = array('default' => '');
    $options['rows_number'] = array('default' => '2');
    $options['wrap_method'] = array('default' => '0');
    $options['default_rows'] = array('default' => '1');
    $options['strip_rows'] = array('default' => '1');
    return $options;
  }

  /**
   * Overrides views_plugin_style::options_form().
   */
  function options_form(&$form, &$form_state) {
    parent::options_form($form, $form_state);
    $element_types = views_rows_wrapper_element_types();
    $attribute_types = views_rows_wrapper_attribute_types();

    $form['use_wrapper'] = array(
      '#type' => 'checkbox',
      '#title' => t('Use this rows wrapper'),
      '#default_value' => $this->options['use_wrapper'],
      '#description' => t('Check if you want to use this plugin.'),
    );

    $form['element_type'] = array(
      '#type' => 'select',
      '#title' => t('Element type'),
      '#options' =>  $element_types,
      '#default_value' => $this->options['element_type'],
      '#description' => t('Select element type.'),
    );
    $form['attribute_type'] = array(
      '#type' => 'select',
      '#title' => t('Attribute type'),
      '#options' => $attribute_types,
      '#default_value' => $this->options['attribute_type'],
      '#description' => t('Select attribute type.'),
    );
    $form['attribute_name'] = array(
      '#title' => t('Class/ID attribute name(s)'),
      '#type' => 'textfield',
      '#default_value' => $this->options['attribute_name'],
    );
    $rows_num = array( 0 => t('Each'));
    $k=1;
    while ($k<=50) {
      $rows_num[$k]=$k;
      $k++;
    }
    $form['rows_number'] = array(
      '#type' => 'select',
      '#title' => t('Number of rows to wrap'),
      '#options' => $rows_num,
      '#default_value' => $this->options['rows_number'],
      '#description' => t('Choose the number of rows to be wrapped by selected element.'),
    );
    $form['wrap_method'] = array(
      '#type' => 'radios',
      '#title' => t('Wrap method'),
      '#default_value' => $this->options['wrap_method'],
      '#options' => array(0 => t('Apply to all items'), 1 => t('Wrap once (first rows only)')),
      '#description' => t('Select the method of how you want to wrap your view results.'),
    );
    $form['default_rows'] = array(
      '#type' => 'checkbox',
      '#title' => t('Add views row classes'),
      '#default_value' => $this->options['default_rows'],
      '#description' => t('Add the default row classes like views-row-1 to the output. You can use this to quickly reduce the amount of markup the view provides by default, at the cost of making it more difficult to apply CSS.'),
    );
    $form['strip_rows'] = array(
      '#type' => 'checkbox',
      '#title' => t('Add striping (odd/even), first/last row classes'),
      '#default_value' => $this->options['strip_rows'],
      '#description' => t('Add css classes to the first and last line, as well as odd/even classes for striping.'),
    );
  }
}

All the fields values will be available at our module under template_preprocess_hook() as $options['field_machine_name'].
Create views_rows_wrapper module file with the logic of our wrapper.

views_rows_wrapper.module

<?php

/**
 * Returns wrapper element types()
 */
function views_rows_wrapper_element_types() {
  return array("DIV", "SPAN");
}

/**
 * Returns wrapper attribute types()
 */
function views_rows_wrapper_attribute_types() {
  return array("CLASS", "ID");
}

/**
 * Implements hook_views_api()
 */
function views_rows_wrapper_views_api() {
  return array('api' => 3);
}

/**
 * Implements hook_preprocess_HOOK() for theme_views_rows_wrapper().
 */
function template_preprocess_views_rows_wrapper(&$vars) {
  $view = $vars['view'];
  $rows = $vars['rows'];
  $style = $view->style_plugin;
  $options = $style->options;
  $element_types = views_rows_wrapper_element_types();
  $attribute_types = views_rows_wrapper_attribute_types();
  $use_wrapper = isset($options['use_wrapper']) ? $options['use_wrapper'] : FALSE;
  $element_type = isset($options['element_type']) ? $element_types[$options['element_type']] : $element_types[0];
  $element_type = strtolower($element_type);
  $attribute_type = isset($options['attribute_type']) ? $attribute_types[$options['attribute_type']] : $attribute_types[0];
  $attribute_type = strtolower($attribute_type);
  $attribute_name = isset($options['attribute_name']) ? $options['attribute_name'] : '';
  $attribute_name = preg_replace('/[^a-zA-Z0-9-_\s]/','',$attribute_name);
  $rows_number = isset($options['rows_number']) ? $options['rows_number'] : 2;
  $wrap_method = isset($options['wrap_method']) ? $options['wrap_method'] : 1;
  $default_rows = isset($options['default_rows']) ? $options['default_rows'] : FALSE;
  $strip_rows = isset($options['strip_rows']) ? $options['strip_rows'] : FALSE;

  $count = 0;
  $k=0;
  $max = count($rows);
  $is_wrapped_once = FALSE;

  foreach ($rows as $id => $row) {
    $classes = array();
    $count++;
    $k++;
    if ($default_rows) {
      $classes[] = 'views-row';
      $classes[] = 'views-row-' . $count;
    }
    if ($strip_rows) {
      $classes[] = 'views-row-' . ($count % 2 ? 'odd' : 'even');
      if ($count == 1) {
        $classes[] = 'views-row-first';
      }
      if ($count == $max) {
        $classes[] = 'views-row-last';
      }
    }
    if (($default_rows) || ($strip_rows)) {
      $class = implode(" ", $classes);
      $row = '<div class="'.$class.'">'.$row.'</div>';
    }
    if(($use_wrapper) && (!$is_wrapped_once)) {
      if($k == 1) {
        $row = '<' . $element_type . ' ' . $attribute_type . '="' . $attribute_name . '">' . $row;
      }
      if (($count % $rows_number == 0) || (($count == $max) && ($k < $rows_number))) {
        $row = $row . '</' . $element_type . '>';
      }
    }
    if($k >= $rows_number) {
      $k = 0;
      if ($wrap_method == 1) {
        $is_wrapped_once = TRUE;
      }
    }
    $vars['rows_wrapped'][] = $row;
  }
}

We also need to add style template file. Note, that rows_wrapped key value into into $vars array on the output will become  $rows_wrapped variable in our style template.

views-rows-wrapper.tpl.php

<?php

/**
 * @file
 * Template to display a views wrapped rows.
 *
 * @ingroup views_templates
 */
?>
<div class="views-rows-wrapper">
<?php
foreach ($rows_wrapped as $row) {
  print $row;
}
?>
</div>

Not difficult, right?

Download this module from drupal.org.

 

Add new comment

CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.