Creating Views 3 Programmatically

Exclusive offer: get 50% off this eBook here
Drupal 7 Views Cookbook

Drupal 7 Views Cookbook — Save 50%

Over 50 recipes to master the creation of views using the Drupal Views 3 module with this book and ebook

$23.99    $12.00
by J. Ayen Green | March 2012 | Drupal

In this chapter, we will switch from the admin user interface to code, creating a view within a module and other examples of using code in conjunction with Drupal and Views architectures to manipulate the content. These methods should only be considered if you are comfortable with PHP and the Drupal architecture and API. The benefits of using code include more granular control, as well as the ability to achieve behaviors otherwise unavailable. The drawbacks are that the Views environment can be very complex, and one can easily break the environment.

The differences between using the Views UI to create a view and doing it in a module is that the UI does the coding for you and makes it more convenient to make changes to the view afterwards. That said, the UI method does not make it easy to distribute a ready-made view, nor does it make it facilitate tying such a view to other code.

In this artice by J. Ayen Green, author of Drupal 7 Views 3 Cookbook, we are going to cover the following topics:

  • Programming a view
  • Handling a view field
  • Styling a view field
  • Fine tuning the query

(For more resources on Drupal, see here.)

Programming a view

Creating a view with a module is a convenient way to have a predefined view available with Drupal. As long as the module is installed and enabled, the view will be there to be used. If you have never created a module in Drupal, or even never written a line of Drupal code, you will still be able to create a simple view using this recipe.

Getting ready

Creating a module involves the creation of the following two files at a minimum:

  • An .info file that gives Drupal the information needed to add the module
  • A .module file that contains the PHP script

More complex modules will consist of more files, but those two are all we will need for now.

How to do it...

Carry out the following steps:

  1. Create a new directory named _custom inside your contributed modules directory (so, probably sites/all/modules/_custom).
  2. Create a subdirectory inside that directory; we will name it d7vr (Drupal 7 Views Recipes).
  3. Open a new file with your editor and add the following lines:
  4. ; $Id: name = Programmatic Views description = Provides supplementary resources such as programmatic views package = D7 Views Recipes version = "7.x-1.0" core = "7.x" php = 5.2

  5. Save the file as d7vrpv.info.
  6. Open a new file with your editor and add the following lines:
  7. Feel free to download this code from the author's web site rather than typing it, at http://theaccidentalcoder.com/ content/drupal-7-views-cookbook

    <?php /** * Implements hook_views_api(). */ function d7vrpv_views_api() { return array( 'api' => 2, 'path' => drupal_get_path('module', 'd7vrpv'), ); } /** * Implements hook_views_default_views(). */ function d7vrpv_views_default_views() { return d7vrpv_list_all_nodes(); } /** * Begin view */ function d7vrpv_list_all_nodes() { /* * View 'list_all_nodes' */ $view = views_new_view(); $view->name = 'list_all_nodes'; $view->description = 'Provide a list of node titles, creation dates, owner and status'; $view->tag = ''; $view->view_php = ''; $view->base_table = 'node'; $view->is_cacheable = FALSE; $view->api_version = '3.0-alpha1'; $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */ /* Display: Defaults */ $handler = $view->new_display('default', 'Defaults', 'default'); $handler->display->display_options['title'] = 'List All Nodes'; $handler->display->display_options['access']['type'] = 'role'; $handler->display->display_options['access']['role'] = array( '3' => '3', ); $handler->display->display_options['cache']['type'] = 'none'; $handler->display->display_options['exposed_form']['type'] = 'basic'; $handler->display->display_options['pager']['type'] = 'full'; $handler->display-> display_options['pager']['options']['items_per_page'] = '15'; $handler->display->display_options['pager']['options'] ['offset'] = '0'; $handler->display->display_options['pager']['options'] ['id'] = '0'; $handler->display->display_options['style_plugin'] = 'table'; $handler->display->display_options['style_options'] ['columns'] = array( 'title' => 'title', 'type' => 'type', 'created' => 'created', 'name' => 'name', 'status' => 'status', ); $handler->display->display_options['style_options'] ['default'] = 'created'; $handler->display->display_options['style_options'] ['info'] = array( 'title' => array( 'sortable' => 1, 'align' => 'views-align-left', 'separator' => '', ), 'type' => array( 'sortable' => 1, 'align' => 'views-align-left', 'separator' => '', ), 'created' => array( 'sortable' => 1, 'align' => 'views-align-left', 'separator' => '', ), 'name' => array( 'sortable' => 1, 'align' => 'views-align-left', 'separator' => '', ), 'status' => array( 'sortable' => 1, 'align' => 'views-align-left', 145 'separator' => '', ), ); $handler->display->display_options['style_options'] ['override'] = 1; $handler->display->display_options['style_options'] ['sticky'] = 0; $handler->display->display_options['style_options'] ['order'] = 'desc'; /* Header: Global: Text area */ $handler->display->display_options['header']['area'] ['id'] = 'area'; $handler->display->display_options['header']['area'] ['table'] = 'views'; $handler->display->display_options['header']['area'] ['field'] = 'area'; $handler->display->display_options['header']['area'] ['empty'] = TRUE; $handler->display->display_options['header']['area'] ['content'] = '<h2>Following is a list of all non-page nodes.</h2>'; $handler->display->display_options['header']['area'] ['format'] = '3'; /* Footer: Global: Text area */ $handler->display->display_options['footer']['area'] ['id'] = 'area'; $handler->display->display_options['footer']['area'] ['table'] = 'views'; $handler->display->display_options['footer']['area'] ['field'] = 'area'; $handler->display->display_options['footer']['area'] ['empty'] = TRUE; $handler->display->display_options['footer']['area'] ['content'] = '<small>This view is brought to you courtesy of the D7 Views Recipes module</small>'; $handler->display->display_options['footer']['area'] ['format'] = '3'; /* Field: Node: Title */ $handler->display->display_options['fields']['title'] ['id'] = 'title'; $handler->display->display_options['fields']['title'] ['table'] = 'node'; $handler->display->display_options['fields']['title'] ['field'] = 'title'; $handler->display-> display_options['fields']['title']['alter']['alter_text'] = 0; $handler->display-> display_options['fields']['title']['alter']['make_link'] = 0; $handler->display-> display_options['fields']['title']['alter']['trim'] = 0; $handler->display-> display_options['fields']['title']['alter'] ['word_boundary'] = 1; $handler->display-> display_options['fields']['title']['alter']['ellipsis'] = 1; $handler->display-> display_options['fields']['title']['alter']['strip_tags'] = 0; $handler->display-> display_options['fields']['title']['alter']['html'] = 0; $handler->display-> display_options['fields']['title']['hide_empty'] = 0; $handler->display-> display_options['fields']['title']['empty_zero'] = 0; $handler->display-> display_options['fields']['title']['link_to_node'] = 0; /* Field: Node: Type */ $handler->display->display_options['fields']['type'] ['id'] = 'type'; $handler->display->display_options['fields']['type'] ['table'] = 'node'; $handler->display->display_options['fields']['type'] ['field'] = 'type'; $handler->display-> display_options['fields']['type']['alter']['alter_text'] = 0; $handler->display-> display_options['fields']['type']['alter']['make_link'] = 0; $handler->display-> display_options['fields']['type']['alter']['trim'] = 0; $handler->display-> display_options['fields']['type']['alter'] ['word_boundary'] = 1; $handler->display-> display_options['fields']['type']['alter']['ellipsis'] = 1; $handler->display-> display_options['fields']['type']['alter']['strip_tags'] = 0; $handler->display-> display_options['fields']['type']['alter']['html'] = 0; $handler->display-> display_options['fields']['type']['hide_empty'] = 0; $handler->display-> display_options['fields']['type']['empty_zero'] = 0; $handler->display-> display_options['fields']['type']['link_to_node'] = 0; $handler->display-> display_options['fields']['type']['machine_name'] = 0; /* Field: Node: Post date */ $handler->display->display_options['fields']['created'] ['id'] = 'created'; $handler->display->display_options['fields']['created'] ['table'] = 'node'; $handler->display->display_options['fields']['created'] ['field'] = 'created'; $handler->display-> display_options['fields']['created']['alter'] ['alter_text'] = 0; $handler->display-> display_options['fields']['created']['alter'] ['make_link'] = 0; $handler->display-> display_options['fields']['created']['alter']['trim'] = 0; $handler->display-> display_options['fields']['created']['alter'] ['word_boundary'] = 1; $handler->display-> display_options['fields']['created']['alter']['ellipsis'] = 1; $handler->display-> display_options['fields']['created']['alter'] ['strip_tags'] = 0; $handler->display-> display_options['fields']['created']['alter']['html'] = 0; $handler->display-> display_options['fields']['created']['hide_empty'] = 0; $handler->display-> display_options['fields']['created']['empty_zero'] = 0; $handler->display-> display_options['fields']['created']['date_format'] = 'custom'; $handler->display-> display_options['fields']['created']['custom_date_format'] = 'Y-m-d'; /* Field: User: Name */ $handler->display->display_options['fields']['name'] ['id'] = 'name'; $handler->display->display_options['fields']['name'] ['table'] = 'users'; $handler->display->display_options['fields']['name'] ['field'] = 'name'; $handler->display->display_options['fields']['name'] ['label'] = 'Author'; $handler->display-> display_options['fields']['name']['alter']['alter_text'] = 0; $handler->display-> display_options['fields']['name']['alter']['make_link'] = 0; $handler->display-> display_options['fields']['name']['alter']['trim'] = 0; $handler->display-> display_options['fields']['name']['alter'] ['word_boundary'] = 1; $handler->display-> display_options['fields']['name']['alter']['ellipsis'] = 1; $handler->display-> display_options['fields']['name']['alter']['strip_tags'] = 0; $handler->display-> display_options['fields']['name']['alter']['html'] = 0; $handler->display-> display_options['fields']['name']['hide_empty'] = 0; $handler->display-> display_options['fields']['name']['empty_zero'] = 0; $handler->display-> display_options['fields']['name']['link_to_user'] = 0; $handler->display-> display_options['fields']['name']['overwrite_anonymous'] = 0; /* Field: Node: Published */ $handler->display->display_options['fields']['status'] ['id'] = 'status'; $handler->display->display_options['fields']['status'] ['table'] = 'node'; $handler->display->display_options['fields']['status'] ['field'] = 'status'; $handler->display-> display_options['fields']['status']['alter'] ['alter_text'] = 0; $handler->display-> display_options['fields']['status']['alter']['make_link'] = 0; $handler->display-> display_options['fields']['status']['alter']['trim'] = 0; $handler->display-> display_options['fields']['status']['alter'] ['word_boundary'] = 1; $handler->display-> display_options['fields']['status']['alter']['ellipsis'] = 1; $handler->display-> display_options['fields']['status']['alter'] ['strip_tags'] = 0; $handler->display-> display_options['fields']['status']['alter']['html'] = 0; $handler->display-> display_options['fields']['status']['hide_empty'] = 0; $handler->display-> display_options['fields']['status']['empty_zero'] = 0; $handler->display->display_options['fields']['status'] ['type'] = 'true-false'; $handler->display->display_options['fields']['status'] ['not'] = 0; /* Sort criterion: Node: Post date */ $handler->display->display_options['sorts']['created'] ['id'] = 'created'; $handler->display->display_options['sorts']['created'] ['table'] = 'node'; $handler->display->display_options['sorts']['created'] ['field'] = 'created'; $handler->display->display_options['sorts']['created'] ['order'] = 'DESC'; /* Filter: Node: Type */ $handler->display->display_options['filters']['type'] ['id'] = 'type'; $handler->display->display_options['filters']['type'] ['table'] = 'node'; $handler->display->display_options['filters']['type'] ['field'] = 'type'; $handler->display-> display_options['filters']['type']['operator'] = 'not in'; $handler->display->display_options['filters']['type'] ['value'] = array( 'page' => 'page', ); /* Display: Page */ $handler = $view->new_display('page', 'Page', 'page_1'); $handler->display->display_options['path'] = 'list-all-nodes'; $views[$view->name] = $view; return $views; } ?>

     

  8. Save the file as d7vrpv.module.
  9. Navigate to the modules admin page at admin/modules.
  10. Scroll down to the new module and activate it, as shown in the following screenshot:
  11. Navigate to the Views Admin page (admin/structure/views) to verify that the view appears in the list:
  12. Finally, navigate to list-all-nodes to see the view, as shown in the following screenshot:

How it works...

The module we have just created could have many other features associated with it, beyond simply a view, and enabling the module will make those features and the view available, while disabling it will hide those same features and view.

When compiling the list of installed modules, Drupal looks first in its own modules directory for .info files, and then in the site's modules directories. As can be deduced from the fact that we put our .info file in a second-level directory of sites/all/modules and it was found there, Drupal will traverse the modules directory tree looking for .info files.

We created a .info file that provided Drupal with the name and description of our module, its version, the version of Drupal it is meant to work with, and a list of files used by the module, in our case just one.

We saved the .info file as d7vrpv.info (Drupal 7 Views Recipes programmatic view); the name of the directory in which the module files appear (d7vr) has no bearing on the module itself.

The module file contains the code that will be executed, at least initially. Drupal does not "call" the module code in an active way. Instead, there are events that occur during Drupal's creation of a page, and modules can elect to register with Drupal to be notifi ed of such events when they occur, so that the module can provide the code to be executed at that time; for example, you registering with a business to receive an e-mail in the event of a sale. Just like you are free to act or not, but the sales go on regardless, so too Drupal continues whether or not the module decides to do something when given the chance.

Our module 'hooks' the views_api and views_default_views events in order to establish the fact that we do have a view to offer. The latter hook instructs the Views module which function in our code executes our view: d7vrpv_list_all_nodes(). The first thing it does is create a view object by calling a function provided by the Views module. Having instantiated the new object, we then proceed to provide the information it needs, such as the name of the view, its description, and all the information that we would have selected through the Views UI had we used it. As we are specifying the view options in the code, we need to provide the information that is needed by each handler of the view functionality.

The net effect of the code is that when we have cleared cache and enabled our module, Drupal then includes it in its list of modules to poll during events. When we navigate to the Views Admin page, an event occurs in which any module wishing to include a view in the list on the admin screen does so, including ours. One of the things our module does is defi ne a path for the page display of our view, which is then used to establish a callback. When that path, list-all-nodes, is requested, it results in the function in our module being invoked, which in turn provides all the information necessary for our view to be rendered and presented.

There's more

The details of the code provided to each handler are outside the scope of this book, but you don't really need to understand it all in order to use it.

You can enable the Views Bulk Export module (it comes with Views), create a view using the Views UI in admin, and choose to Bulk Export it. Give the exporter the name of your new module and it will create a file and populate it with nearly all the code necessary for you.

Handling a view field

As you may have noticed in the preceding code that you typed or pasted, Views makes tremendous use of handlers. What is a handler? It is simply a script that performs a special task on one or more elements. Think of a house being built. The person who comes in to tape, mud, and sand the wallboard is a handler.

In Views, one type of handler is the field handler, which handles any number of things, from providing settings options in the field configuration dialog, to facilitating the field being retrieved from the database if it is not part of the primary record, to rendering the data. We will create a field handler in this recipe that will add to the display of a zip code a string showing how many other nodes have the same zip code, and we will add some formatting options to it in the next recipe.

Getting ready

A handler lives inside a module, so we will create one:

  1. Create a directory in your contributed modules path for this module.
  2. Open a new text file in your editor and paste the following code into it:
  3. ; $Id: name = Zip Code Handler description = Provides a view handler to format a field as a zip code package = D7 Views Recipes ; Handler files[] = d7vrzch_handler_field_zip_code.inc files[] = d7vrzch_views.inc version = "7.x-1.0" core = "7.x" php = 5.2

  4. Save the file as d7vrzch.info.
  5. Create another text file and paste the following code into it:
  6. <?php /** * Implements hook_views_data_alter() */ function d7vrzch_field_views_data_alter(&$data, $field) { if (array_key_exists('field_data_field_zip_code', $data)) { $data['field_data_field_zip_code']['field_zip_code'] ['field']['handler'] = 'd7vrzch_handler_field_zip_code'; } }

  7. Save the file as d7vrzch.views.inc.
  8. Create another text file and paste the following into it:
  9. <?php /** * Implements hook_views_api(). */ function d7vrzch_views_api() { return array( 'api' => 3, 'path' => drupal_get_path('module', 'd7vrzch'), ); }

  10. Save the file as d7vrzch.module.

How to do it...

Carry out the folowing steps:

  1. Create another text file and paste the following into it:
  2. <?php // $Id: $ /** * Field handler to format a zip code. * * @ingroup views_field_handlers */ class d7vrzch_handler_field_zip_code extends views_handler_field_field { function option_definition() { $options = parent::option_definition(); $options['display_zip_totals'] = array( 'contains' => array( 'display_zip_totals' => array('default' => FALSE), ) ); return $options; } /** * Provide a link to the page being visited. */ function options_form(&$form, &$form_state) { parent::options_form($form, $form_state); $form['display_zip_totals'] = array( '#title' => t('Display Zip total'), '#description' => t('Appends in parentheses the number of nodes containing the same zip code'), '#type' => 'checkbox', '#default_value' => !empty($this-> options['display_zip_totals']), ); } function pre_render(&$values) { if (isset($this->view->build_info['summary']) || empty($values)) { return parent::pre_render($values); } static $entity_type_map; if (!empty($values)) { // Cache the entity type map for repeat usage. if (empty($entity_type_map)) { $entity_type_map = db_query('SELECT etid, type FROM {field_config_entity_type}')->fetchAllKeyed(); } // Create an array mapping the Views values to their object types. $objects_by_type = array(); foreach ($values as $key => $object) { // Derive the entity type. For some field types, etid might be empty. if (isset($object->{$this->aliases['etid']}) && isset($entity_type_map[$object->{$this-> aliases['etid']}])) { $entity_type = $entity_type_map[$object->{$this-> aliases['etid']}]; $entity_id = $object->{$this->field_alias}; $objects_by_type[$entity_type][$key] = $entity_id; } } // Load the objects. foreach ($objects_by_type as $entity_type => $oids) { $objects = entity_load($entity_type, $oids); foreach ($oids as $key => $entity_id) { $values[$key]->_field_cache[$this->field_alias] = array( 'entity_type' => $entity_type, 'object' => $objects[$entity_id], ); } } } } function render($values) { $value = $values->_field_cache[$this->field_alias] ['object']->{$this->definition['field_name']} ['und'][0]['safe_value']; $newvalue = $value; if (!empty($this->options['display_zip_totals'])) { $result = db_query("SELECT count(*) AS recs FROM {field_data_field_zip_code} WHERE field_zip_code_value = :zip",array(':zip' => $value)); foreach ($result as $item) { $newvalue .= ' (' . $item->recs . ')'; } } return $newvalue; }

  3. Save the file as d7vrzch_handler_field_zip_code.inc.
  4. Navigate to admin/build/modules and enable the new module, which shows as the Zip Code Handler.
  5. We will test the handler in a quick view. Navigate to admin/build/views.
  6. Click on the +Add new view link , enter test as the View name, check the box for description and enter Zip code handler test; clear the Create a page checkbox , and click on the Continue & edit button .
  7. On the Views edit page, click on the add link in the Filter Criteria pane, check the box next to Content: Type, and click on the Add and configure filter criteria button .
  8. In the Content: Type configuration box , select Home and click on the Apply button .
  9. Click on the add link next to Fields, check the box next to Content: Zip code, and click on the Add and configure fields button.
  10. Check the box at the bottom of the Content: Zip code configuration box titled Display Zip total and click on the Apply button.
  11. Click on the Save button and see the result of our custom handler in the Live preview:

How it works...

The Views field handler is simply a set of functions that provide support for populating and formatting a field for Views, much in the way a printer driver does for the operating system. We created a module in which our handler resides, and whenever that field is requested within a view, our handler will be invoked. We also added a display option to the configuration options for our field, which when selected, takes each zip code value to be displayed, determines how many nodes have the same zip code, and appends the parenthesized total to the output.

The three functions, two in the views.inc file and one in the module file, are very important. Their result is that our custom handler file will be used for field_zip_code instead of the default handler used for entity text fields. In the next recipe, we will add zip code formatting options to our custom handler.

Drupal 7 Views Cookbook Over 50 recipes to master the creation of views using the Drupal Views 3 module with this book and ebook
Published: March 2012
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

(For more resources on Drupal, see here.)

Styling a view field

In the preceding recipe, we created a module for a custom field handler for a zip code and a small test view to see the result. In this recipe, we will add styling options to the handler to offer the user a choice of output styles.

Getting ready

If you have not yet tried that recipe, please do, so that you will have the module and view necessary for this recipe.

Edit one of the home content types (or add a few if you have none). At least two of the nodes should have the same zip code, and at least one should have a nine-digit zip code without a hyphen, for example, 12345789.

How to do it...

Carry out the following steps:

  1. Edit the file d7vrzch_handler_field_zip_code.inc and insert the following highlighted code in the options_form() function:
  2. function options_form(&$form, &$form_state) {
    parent::options_form($form, $form_state);
    $form['display_zip_totals'] = array(
    '#title' => t('Display Zip total'),
    '#description' => t('Appends in parentheses the number
    of nodes containing the same zip code'),
    '#type' => 'checkbox',
    '#default_value' => !empty
    ($this->options['display_zip_totals']),
    );
    $form['type'] = array(
    '#type' => 'select',
    '#title' => t('Formatter'),
    '#options' => array(
    t('Zip+4 or Zip'),
    t('Zip'),
    t('Alphanumeric')
    ),
    '#default_value' => $this->options['type'],
    );
    }

  3. In the same file, just prior to the final }, insert the following code:
  4. function _make_zip($value, $zip_type=2) {
    // remove the hyphen if present
    $zip = explode('-', $value);
    switch ($zip_type) {
    case 0: // zip+4 or zip depending on size
    if (is_numeric($zip[0])) {
    $value = $zip[0];
    if (sizeof($zip) > 1) {
    if (is_numeric($zip[1])) {
    $value .= '-' . $zip[1];
    }
    }
    else {
    if (strlen($zip[0]) > 5) {
    $value = substr($zip[0],0,5);
    if (strlen($zip[0] == 9)) {
    $value .= '-' . substr($zip[0],5,4);
    }
    }
    }
    }
    break;
    case 1: // zip (trim to 5)
    if (is_numeric($zip[0]) && strlen($zip[0] >= 5)) {
    $value = substr($zip[0],0,5);
    }
    break;
    case 2: // no format change
    break;
    }
    return $value;
    }
    }

  5. Save the file.
  6. Navigate to admin/structure/views and edit the test view.
  7. Click on the link in the Fields box for Fields: field_zip_code, and at the bottom of the configuration box, select Zip+4 or Zip from Formatter, clear the Display Zip total checkbox , and click on the Apply button. This will provide a result as shown in the following screenshot:
  8. Click on the same field link once again, and this time select Zip from the Formatter and click on the Apply button, resulting in the output shown in the following screenshot:

How it works...

The Formatter select box in the field configuration screen is merely a form field that passes along the selected value. The code we put in place created three options that are then fulfilled by a formatting function. The first option displays the zip code as either Zip+4 (12345-6789) or zip (12345), depending. If the zip code is numeric and either of the format xxxxxxxxx or of xxxxx-xxxx, it will be displayed as Zip+4, if 5 digits, as a regular zip code. The second option is to always display in a zip format, so that a longer zip code will be truncated to five digits. The third option leaves the zip code unformatted, which would be good for alphanumeric postcodes.

Fine tuning the query

The Views UI is a powerful query builder tool, in addition to its other functionalities, but sometimes the SQL query generated by it is not precisely what you want it to be. In this recipe, we will make a change to a view query.

Getting ready

If you have not yet tried that recipe, please do, so that you will have the module and view necessary for this recipe.

How to do it...

Carry out the following steps:

  1. Edit the module file d7vrzch.module and add the following code:
  2. /**
    * Implements hook_views_query_alter
    */
    function d7vrzch_views_query_alter(&$view, &$query) {
    if ($view->name == 'test') {
    $query->orderby[0]['field'] =
    'field_data_field_zip_code_node_entity_type';
    $query->orderby[0]['direction'] = 'ASC';
    }
    }

  3. Save the file.
  4. Edit the test view to see the preview, as shown in the following screenshot:

How it works...

The underlying query in a view is made available as a data structure at various points in the view rendering process. We made use of a hook into the process and altered the query structure to change the sort fi eld.

Why would you want to do this instead of simply using the Views UI? The Views UI is good for users to manipulate a view, given the applicable permissions, but those changes would be persisted until the view was changed again. In addition, you may want aspects of the underlying query to be handled dynamically, determined based on the data being queried, the user doing the query, or other factors.

Summary

In this article, we saw the benefits of switching from the admin user interface to code. Using code we could achieved more granular control, as well as could achieve behaviors that are otherwise unavailable.


Drupal 7 Views Cookbook Over 50 recipes to master the creation of views using the Drupal Views 3 module with this book and ebook
Published: March 2012
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


J. Ayen Green

J. Ayen Green has developed software since inventing the abacus, created websites since [insert name du jour] created the Web, and has been a Drupaler somewhat longer than his current D.O. uid (you try settling on an id when your real name is Dries Webchick). A writer and columnist of sorts, a poet of metered sorts, husband, father, friend and rascal, when not plugged in Green enjoys nature, dogs, horses and other critters, kayaking, zip-lining, spicy food, the arts and other cultures. He and his wife, Sofía-Aileen, make their home in New York City.

Books From Packt


Drupal 7 Mobile Web Development Beginner’s Guide
Drupal 7 Mobile Web Development Beginner’s Guide

Drupal 7 Multilingual Sites
Drupal 7 Multilingual Sites

Drupal 7 Business Solutions
Drupal 7 Business Solutions

Drupal 7 Module Development
Drupal 7 Module Development

Drupal 7 Themes
Drupal 7 Themes

Drupal 6 JavaScript and jQuery
Drupal 6 JavaScript and jQuery

Drupal 6 Themes
Drupal 6 Themes

Drupal 6
Drupal 6


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software