Matrix view with content as columns and taxonomy terms as rows I need to build a view in a matrix format. I have a content type mycontent and a taxonomy vocabulary myvocabulary. The content type has a term reference field in it, which refers to the vocabulary and can have multiple values.

I want to list all contents in a view as columns, and all terms in the vocabulary as a table header, i.e. columns. I want to put a check mark to the corresponding field in the table according to the terms that the content has. 

An illustration of what I want to achieve:

+-----------+--------+--------+--------+--------+-----
|           | Term 1 | Term 2 | Term 3 | Term 4 | ...
+-----------+--------+--------+--------+--------+-----
| Content 1 |   x    |   x    |        |   x    |
+-----------+--------+--------+--------+--------+-----
| Content 2 |        |        |        |   x    |
+-----------+--------+--------+--------+--------+-----
| Content 3 |   x    |        |   x    |        |
+-----------+--------+--------+--------+--------+-----
| Content 4 |   x    |    x   |        |        |
+-----------+--------+--------+--------+--------+-----
|    ...    |        |        |        |        |


So, my challenge is to dynamically create a column for each taxonomy term in the vocabulary. I.e. if I add a new taxonomy term, the view should have this column automatically added to the table.

With the default table display I can list the nodes as rows, but it is not possible to make a column for each term in the vocabulary. 

I have found 3 different modules which seem to related to my problem:


Views Crosstab
Pivot Tables for Views
Views Matrix


None of those modules have a stable version for Drupal 7, nor they have a good documentation, if any at all.

I am going to download and install the dev versions of each module and play around. However, before I do that I wanted to ask if anyone has done such a view and if there would be another better approach to achieve such a matrix view.

Other suggestions that doesn't include using Views are also welcome.
It can be achieved with the help of hook_views_pre_view() and the Views field template file.

Create a View

Create a view showing content in table format and add two fields:

Content: Title (Title)

The title will populate each row of the first column.

Content: All taxonomy terms (All taxonomy terms)

select: Exclude this field from display

select: Limit terms by vocabulary and tick the vocabulary you want to use.

Your module file

Create a column for each term and populate all the fields with the value from the field Content: All taxonomy terms (All taxonomy terms) Then we'll be able to check if a comma-separated list of terms contains the term of the current column.

/**
 * Implements HOOK_views_pre_view().
 *
 * Adds a field to the "YOUR VIEW NAME" view.
 */
function YOURMODULE_views_pre_view(&amp;$view) {

  if ($view-&gt;name == 'YOUR_VIEW_NAME') {
    // the vocabularies defined in the view.
    $vocabularies = array_filter($view-&gt;display['default']-&gt;handler-&gt;options['fields']['term_node_tid']['vocabularies']);
    $terms = YOURMODULE_terms($vocabularies);
    $count = count($terms);
    for ($i=0;$i&lt;$count;$i++) {
      // define the field id.
      $view-&gt;display['default']-&gt;handler-&gt;options['fields']['tid_' . $i]['id'] = 'tid_' . $i;

      // define the table (it has to be the same table for all fields)
      // if you aren't sure what the table name should be, uncomment the line below and take a look at the first field data.
      // debug($view-&gt;display['default']-&gt;handler-&gt;options['fields'], 'VIEW FIELDS', TRUE);
      $view-&gt;display['default']-&gt;handler-&gt;options['fields']['tid_' . $i]['table'] = 'node';

      // here we need to define our custom field output
      // the field "Content: All taxonomy terms (All taxonomy terms)" has a mashine name 'term_node_tid'
      // this field need to be excluded from display because we do not want to display one column only.
      // Instead we will use the data to populate all our custom fields, then we'll be able to define the output for each of them.
      $view-&gt;display['default']-&gt;handler-&gt;options['fields']['tid_' . $i]['field'] = 'term_node_tid';

      // define the field label
      $view-&gt;display['default']-&gt;handler-&gt;options['fields']['tid_' . $i]['label'] = $terms[$i];

      // we need only term names, not links.
      $view-&gt;display['default']-&gt;handler-&gt;options['fields']['tid_' . $i]['link_to_taxonomy'] = 0;
    }
  }
}


A helper function that returns an array of terms. This function will be used twice. First time in hook_views_pre_view() where the argument is available from the View. Second time in YOURMODULE_has_term() where the argument would be hardly to get. That's why I decided to use a static variable here.

/**
 * Helper function that returns an array of terms.
 *
 * @return array
 *   An array of terms from all vocabularies (defined in the view).
 */
function YOURMODULE_terms($vocabularies = NULL) {
  $terms = &amp;drupal_static(__FUNCTION__);
  if (!isset($terms)) {
    $terms = array();
    foreach ($vocabularies as $key =&gt; $vocabulary) {
      $vid = taxonomy_vocabulary_machine_name_load($vocabulary)-&gt;vid;   
      $taxonomy_tree = taxonomy_get_tree($vid);
      foreach ($taxonomy_tree as $term) {
        $terms[] = $term-&gt;name;
      }
    }
  }
  return $terms;
}


A helper function to determine if a field value (comma-separated list of terms) contains the term of current column. This function will be used in a Views field template file.

/**
 * Determines if a comma-seperated list of terms contains the term of the
 * current column.
 *
 * @param $field_alias string
 *   The field alias corresponding to the $field-&gt;field_alias value in the view.
 * @param $field_position integer
 *   The column corresponding to the $field-&gt;position value in the view.
 * @param $output string
 *   Comma seperated list of terms.
 * @return 'X' if TRUE or bool if FALSE.
 */
function YOURMODULE_has_term($field_alias, $field_position, $output) {

  // because we are using two fields to provide the data
  // we need to skip two positions to get appropriate term keys. 
  $field_position = ($field_position &gt; 1) ? $field_position-2 : $field_position;

  // because this is a second call for this function,
  // we don't need to define vocabularies as an argument.
  $terms = testmodule_terms();

  // each column has their own term name.
  $term_name = $terms[$field_position];

  // first column fields.
  if ($field_alias == 'node_title') {
    return $output;
  }

  // the fields in all terms columns.
  else {

    // explode terms and trim whitespaces.
    $term_names = array_map('trim',explode(",",$output));

    // check the term names array contains the term of the current column.
    if (in_array($term_name, $term_names)) {
      return 'X'; // check mark
    }
    return FALSE;
  }
}


Views field template file

Create a View template file views-view-field--[YOUR-VIEW-NAME].tpl.php and place in your templates folder.

&lt;?php
// We're assuming that this view only has two fields (Content: Title)
// and  Content: All taxonomy terms (All taxonomy terms). 
// All other fields are generated by the HOOK_views_pre_view() function in the
// `YOURMODULE.module` and those should be passed through the
// `YOURMODULE_has_term` function which determines if the $output
// (comma-seperated terms) contains the column term name.
print YOURMODULE_has_term($field-&gt;field_alias, $field-&gt;position, $output);
?&gt;




I was able to answer this question thanks to a similar post here and added lots of comments to the code. Hope it helps.