August 7, 2016

(This module was tested on drupal 8.1.8)

Twig Extensions, Views and Drupal 8

Problem to solve

Sometimes working with Views can be a challenge, in some cases you want to modify the values or process the content of the field.

Many developers use the preprocess functions, or alter the query of the view, however there is another option: twig filters.

The Goals:

Being able to calculate the reading time of any given node (body field).

The field needs to output: "5 min read", "9 min read", depending on the word count of the node.

Twig Filters:

Twig filters give us the ability to modify variables on twig template.

{% set salutevariable = "Hello Friend" %}"

If we output the variable {{ salutevariable }} is going to show: Hello Friend

If we want to make the variable lowercase, we use a filter.

{{ salutevariable | lower }} the "|" indicates that we are going to use a filter in our case "lower", so the value "Hello Friend" is going to be parsed by the filter lower.

The output will be: hello friend

Official Documentation:

Creating A Module:

Create a new module in: modules/custom/

Our module is going to have the following structure:


|readingtime/
|-- readingtime.info.yml
|-- readingtime.services.yml
|-- src/TwigExtension/ReadingTimeExtension.php

readingtime.info.yml contains:


name: readingtime
type: module
description: Provides a Twig filter that calculates the time of reading for any given string.
core: 8.x
package: Custom

Create a services file: readingtime.services.yml


services:
  readingtime.twig_extension:
    class: Drupal\readingtime\TwigExtension\ReadingTimeExtension
    tags:
      - { name: twig.extension }

The ReadingTimeExtension.php will contain the following:


<?php

namespace Drupal\readingtime\TwigExtension;

use Drupal\Core\Render\Markup;

class ReadingTimeExtension extends \Twig_Extension {
  /**
   * Here is where we declare our new filter.
   * @return array
   */
  public function getFilters() {
    return array(
      'readingtime' => new \Twig_Filter_Function(
        array('Drupal\readingtime\TwigExtension\ReadingTimeExtension', 'readingTimeFilter') // Here we are self referencing the function we use to filter the string value.
      )
    );
  }

  /**
   * This is the same name we used on the services.yml file
   * @return string
   */
  public function getName() {
    return "readingtime.twig_extension";
  }

  /**
   * @param $string
   * @return float
   */
  public static function readingTimeFilter($string) {

    if($string instanceof Markup) { // we check if the $string is an instance of Markup Object.

      // strip_tags help us to remove all the HTML markup.

      // if $string is an instance of Markup we use the method __toString() to convert it to string.

      $striped_markup = strip_tags($string->__toString()); 

      // On avarage we read 130 words per minute.

      // the PHP function str_word_count counts the number of words of any given string.

      $wpm = str_word_count($striped_markup)/130; // $wpm = Words Per Minute.

      $reading_time = ceil($wpm); // Round the float number to an integer.

      return $reading_time; // we return an integer with the number of minutes we need to read something.

    } else {

      return $string;

    }

  }

}

Testing our brand new filter:

Now we install our new module in admin/modules.

It's also helpful to generate dummy content with the devel module.

The View:

Then I proceed to create a simple view of articles:

View configuration:

FORMAT:

show: fields

FIELDS:

Content: Title
Content: Body ( this is the one we are going to apply our new twig filter )
Content: Body

We access the configuration of the second Content: Body

and under the REWRITE RESULTS option we are going to override the output of the field: 

overriding field views drupal 8

and the result is the following:

twig filters views result