4

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:

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.

  • I think this is possible with views module's table display. Please see knackforge.com/service/professional-support-quiz. But would like to know about your opinion on this before I make any suggestions. – Gladiator Nov 4 '13 at 11:27
  • With the table display, the columns are the view fields. That means in theory I should add a new field for each term. However, I want to have the columns are also generated from the taxonomy terms. For example, if I a new term is added to the vocabulary, the view should have a new column automatically. – Елин Й. Nov 4 '13 at 11:32
  • @ЕлинЙ. I think you can play with slick grid drupal.org/project/slickgrid – Bala Nov 4 '13 at 12:50
  • @Bala I've taken a look at the Slick Grid, but I didn't see any point on its description that it would suit my needs. I think I didn't express well enough what I want to achieve and where the challenge is. I'll update my question to make it clearer. – Елин Й. Nov 4 '13 at 12:55
  • What about the Views Flipped Table module? Here is a quote about it (from the module's project page): > ... provides a views table style with rows and columns flipped. This is useful for views showing few entities with many fields, such as product comparison. – Kasalla Nov 12 '13 at 22:12
6
+50

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(&$view) {

  if ($view->name == 'YOUR_VIEW_NAME') {
    // the vocabularies defined in the view.
    $vocabularies = array_filter($view->display['default']->handler->options['fields']['term_node_tid']['vocabularies']);
    $terms = YOURMODULE_terms($vocabularies);
    $count = count($terms);
    for ($i=0;$i<$count;$i++) {
      // define the field id.
      $view->display['default']->handler->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->display['default']->handler->options['fields'], 'VIEW FIELDS', TRUE);
      $view->display['default']->handler->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->display['default']->handler->options['fields']['tid_' . $i]['field'] = 'term_node_tid';

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

      // we need only term names, not links.
      $view->display['default']->handler->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 = &drupal_static(__FUNCTION__);
  if (!isset($terms)) {
    $terms = array();
    foreach ($vocabularies as $key => $vocabulary) {
      $vid = taxonomy_vocabulary_machine_name_load($vocabulary)->vid;   
      $taxonomy_tree = taxonomy_get_tree($vid);
      foreach ($taxonomy_tree as $term) {
        $terms[] = $term->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->field_alias value in the view.
 * @param $field_position integer
 *   The column corresponding to the $field->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 > 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.

<?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->field_alias, $field->position, $output);
?>

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

  • Thanks a lot Jack-PL. Actually I have already solved this problem using Views Matrix. However, your answer look very reasonable and I'll definitely test it on this weekend and accept your answer. – Елин Й. Nov 15 '13 at 7:44
  • You're welcome. Please don't forget to share your opinion after testing. It might be useful for the others, including myself :) – Jack-PL Nov 15 '13 at 18:34
  • I've just tested your answer and it works. I've already awarded you the bounty. However, as I already stated, I've done it with Views Matrix and it required no template hacks. – Елин Й. Nov 16 '13 at 17:02
0

I solved this issue using Views Matrix module. Although, as I stated in my question, it has only a dev version, it is working without problems.

Views matrix provides a matrix style plugin for Views. This overrides pager settings to display items grouped by a row and column header field. Row items that match a row and column value are grouped into the same cell.

The steps how I did it:

  1. Create a view showing content in Matrix format, which is provided by Views Matrix module.
  2. Add the Relationship to the taxonomy term Content: Term reference field (field_my_term_reference)
  3. Add fields Taxonomy term: Name and Content: Title and exclude them from display, they are for the table headers.
  4. Add more fields to display in table content, or a Global: Custom text field for just showing an "X" or a check-sign or whatever else..
  5. Under Format -> Settings, select for column header Content: Term reference field and for row header Content: Title

Not the answer you're looking for? Browse other questions tagged or ask your own question.