Views are great for a straight listing of nodes. Sometimes, however, you want to be able to group a list of nodes into a series of lists. Say, list nodes by author, or by the date they were created, or by the first letter of their title. That is, actually, easy to do via the view's template function.

Let's start off with a basic view. Copy and paste the following view into the admin/views/import screen and save it, then follow along. It simply creates a listing of all nodes in the system, ordered by author, then title.

  $view = new stdClass();
  $view->name = 'groupview';
  $view->description = '';
  $view->access = array (
);
  $view->view_args_php = '';
  $view->page = TRUE;
  $view->page_title = 'Groupview';
  $view->page_header = '';
  $view->page_header_format = '1';
  $view->page_footer = '';
  $view->page_footer_format = '1';
  $view->page_empty = '';
  $view->page_empty_format = '1';
  $view->page_type = 'list';
  $view->url = 'groupview';
  $view->use_pager = FALSE;
  $view->nodes_per_page = '10';
  $view->sort = array (
    array (
      'tablename' => 'users',
      'field' => 'name',
      'sortorder' => 'ASC',
      'options' => '',
    ),
    array (
      'tablename' => 'node',
      'field' => 'title',
      'sortorder' => 'ASC',
      'options' => '',
    ),
  );
  $view->argument = array (
  );
  $view->field = array (
    array (
      'tablename' => 'node',
      'field' => 'title',
      'label' => '',
      'handler' => 'views_handler_field_nodelink',
      'options' => 'link',
    ),
    array (
      'tablename' => 'node',
      'field' => 'type',
      'label' => '',
    ),
    array (
      'tablename' => 'node',
      'field' => 'created',
      'label' => '',
      'handler' => 'views_handler_field_date_small',
    ),
    array (
      'tablename' => 'users',
      'field' => 'name',
      'label' => '',
    ),
  );
  $view->filter = array (
  );
  $view->exposed_filter = array (
  );
  $view->requires = array(users, node);
  $views[$view->name] = $view;

Go to the groupview path, and you'll see a list of all nodes on the system. Now, let's set up the grouping.

We're going to be doing the grouping in the theme layer, so use the theme wizard to generate the default code for you. Place the phptemplate_views_view_list_groupview() function in your template.php file, then add the .tpl.php file as directed.

Now let's look at the phptemplate_views_view_list_groupview() function. One of the parameters to is is $nodes, which, despite the name, is an array of objects where each object contains just the fields that were added to this view, in the form $table_$field. So if we want to group by the node's author, we want the users_name field. Right after the line that reads $taken = array();, add the following:

$set = array();
foreach ($nodes as $node) {
  $set[$node->users_name][] = $node;
}

That builds an array whose keys are usernames and whose values are all nodes that correspond to that username. Now skip down to the foreach ($nodes as $node) block. Modify it to read as follows:

$output = '';
foreach ($set as $label => $nodes) {
  $items = array();
  foreach ($nodes as $i => $node) {
    $vars = $base_vars;
    $vars['node'] = $node;
    $vars['count'] = $i;
    $vars['stripe'] = $i % 2 ? 'even' : 'odd';
    foreach ($view->field as $id => $field) {
      $name = $field_names[$id];
      $vars[$name] = views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $node, $view);
      if (isset($field['label'])) {
        $vars[$name . '_label'] = $field['label'];
      }
    }
    $items[] = _phptemplate_callback('views-list-groupview', $vars);
  }
  if ($items) {
    $output .= theme('item_list', $items, $label);
  }
}
return $output;

What we're doing is wrapping the original code in another foreach() loop to iterate over each of the usernames we build a moment ago, then for each of those we're doing exactly the same as before. Instead of simply returning the item list, though, we're concatenating them all together (along with a label for each set) and then returning the final string. Save the file and give it a look. You should see a separate list for each user, with a header that is the user's name.

You can also group by a value that is based on a field. For example, edit the view to sort by node title, then by author. Now edit the set building code to read:

$set = array();
foreach ($nodes as $node) {
  $set[$node->node_title[0]][] = $node;
}

Now our nodes will be grouped by the first letter of their title, and we only had to change one line. There's plenty of other ways to group as well.

One word of caution, though. If you're grouping by a date field, such as when the node was created, remember that the $nodes list has times still as a timestamp rather than as a formatted string, so you'll need to format them yourself:

$set = array();
foreach ($nodes as $node) {
  $set[format_date($node->node_created, 'custom', 'd F Y')][] = $node;
}

Other Helpful Information

Just want to point out that if you get stuck, grouping by nodes is covered in other areas of this site:

http://drupal.org/node/42603 - Theming your views: How to provide a view which contains taxonomy headers

http://drupal.org/node/165417 - Using views to group nodes