Drupal 7 - Views handlers... Once upon a time!

By k, 7 September, 2020

Authored on

Mon, 09/07/2020 - 10:04
Image

Few months ago I wrote an article about an implementation over a D7 site I was working on. A friend of mine told me, "Hey, D7 is dead already, why are you still doing those projects". Well, the truth from my perspective... There are still a lot of sites running D7.

I might agree on, they should be thinking on moving to a must recent version (let's say D8 or D9). But they got an extra few time to planning this migration, so we still need to do stuff on D7. That being said, some of must casual things you are doing is handling stuff by using views. So, I will tell you a brief story about how I made an integration by using Views Handlers

There are always different ways to solve any problem, it's hightly probably I would have taken another solution if I hadn't be working with D8. So, I hope it somehow it can helps you at any moment while you need to get back doing any crazyness on a D7 project.

Once upon a time... "Full Name" computed field! 

Into an entity I have two fields to grab the full name information from someone. You know, typically a "field_first_name" and "field_last_name" fields. So, I created a new "Views field handler", there are few steps you need to do in order to Drupal recognizes the new handler.

Implement hook_views_api (@ /keboca_demo.module)

Be sure, we're using the correct views API version, inside our demo module file, it will have the following lines:

<?php

/**
 * Implements hook_views_api().
 */
function keboca_demo_views_api() {
  return [
    'api' => 3,
    'path' => drupal_get_path('module', 'keboca_demo') . '/views',
  ];
}

/**
 * Helpder function to grab field values from given entity.
 *
 * @param string $entity_type
 * @param string $entity
 * @param string $field_name
 * @param string $key
 *
 * @return mixed|null
 */
function keboca_demo_field_get_items($entity_type, $entity, $field_name, $key = 'value') {
  /** @var array $items */
  if ($items = field_get_items($entity_type, $entity, $field_name)) {
    /** @var array $item */
    $item = reset($items);
    return isset($item[$key]) ? $item[$key] : NULL;
  }

  return NULL;
}

It will allow views module to load all view files to work with, in our case, a new handler. Beside I added a helper function that will use it later on.

Implement hook_views_data_alter (@ /views/keboca_demo.views.inc)

We need to explicit let know views where to load our custom handlers, check it out:

<?php

/**
 * Implements hook_views_data_alter().
 */
function keboca_demo_views_data_alter(&$data) {
  /** @var array $node */
  $node = &$data['node'];

  // Define new `full_name` computed field.
  $node['full_name'] = [
    'field' => [
      'group' => t('Node'),
      'title' => t('Full name'),
      'help' => t('Field computed first and last names fields together.'),
      'handler' => 'keboca_demo_handler_field_node_full_name',
    ],
  ];
}

Extends views_handler_field_entity (@ /views/handlers/keboca_demo_handler_field_node_full_name.inc)

I didn't want to join tables or so on, then I chose to extends from a class which actually allows me to work with the entity loaded at the render time. So, let's see how it looks. 

<?php

/**
 * @file
 * Definition of keboca_demo_handler_field_node_full_name.
 */

/**
 * Class keboca_demo_handler_field_node_full_name
 *
 * @ingroup views_field_handlers
 */
class keboca_demo_handler_field_node_full_name extends views_handler_field_entity {

  /**
   * {@inheritdoc}
   */
  function get_value($values, $field = NULL) {
    /** @var mixed $value */
    $value = parent::get_value($values);

    // Find node to retrieve rendered field value.
    if(false === $value && !empty($values->nid)) {
      /** @var \stdClass $node */
      $node = node_load($values->nid);
      return $this->getFullNameFormatted($node);
    }

    return $value;
  }

  /**
   * {@inheritdoc}
   */
  function render($values) {
    /** @var \stdClass|string $value */
    $value = $this->get_value($values);

    if ($value instanceof \stdClass) {
      return $this->getFullNameFormatted($value);
    } elseif (is_string($value)) {
      return $value;
    }

    return NULL;
  }

  /**
   * Helper method to build up the full name value.
   *
   * @param \stdClass $entity
   *
   * @return string
   */
  public function getFullNameFormatted($entity) {
    /** @var string $first_name */
    $first_name = keboca_field_get_items(
      'node',
      $entity,
      'field_first_name'
    );

    /** @var string $last_name */
    $last_name = keboca_field_get_items(
      'node',
      $entity,
      'field_last_name'
    );

    return sprintf('%s %s', $first_name, $last_name);
  }

}

The code is self-explanatory, I think so, but if not. The class "views_handler_field_entity::get_value" retrieves the entity instance. I ovewrote the method since sometimes it does not load the entity but the "nid" is provided by the "$values". So I manually load it.

The magic happens into "::getFullNameFormatted" method, where I am using a helper function named "keboca_field_get_items" which actuall load the values from the fields properly.

Defining module (@ /keboca_demo.info)

If I am not mistaken, we only need to define our module by using the info file. Take a look:

name = KEBOCA Demo
description = Views handlers. Another demo.
core = 7.x
package = keboca

files[] = views/keboca_demo.views.inc
files[] = views/handlers/keboca_demo_handler_field_node_full_name.inc

Final step

As soon as you create a new node view by using the UI, the new handler will be available:

node handler

I hope it helps somebody somehow!

Happy coding!

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.