Drupal 9: Creating Custom Twig Functions And Filters

Whilst Twig is a powerful tool in its own right there are occasions when you need to pull out data from Drupal or manipulate it in certain ways. I normally do this using preprocess steps, but I recently found that it was also possible to extend Twig within the Drupal framework to provide your own functions and filters. This can be useful if you have custom templates and need to perform special actions on data items to format them in different ways.

Setting Up

Before we can create Twig filters and functions in Drupal we need to tell Drupal that we have a class that contains them. This is done by creating an entry in the your module services file that contains the tag tag.extension. In a module called "custom_twig" the file would be called custom_twig.services.yml and would contain the following.

services:
  custom_twig.twig.CustomTwig:
    class: Drupal\custom_twig\CustomTwig
    tags:
      - { name: twig.extension }

This points to a class that extends the class Twig\Extension\AbstractExtension. The footprint of the class looks like this.

<?php

namespace Drupal\custom_twig;

use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;

/**
 * Custom twig functions.
 */
class CustomTwig extends AbstractExtension {
}

With that in place we can start creating our own custom filters and functions.

Custom Filters

Filters work by taking something as an input and changing it in some way. Although you can pass virtually any value to a filter, the idea is that you should really pass strings that can be easily manipulated.

To define a filter you create a method in the CustomTwig class called getFilters. This method returns an array of TwigFilter objects that have a name and a callback as the arguments to the constructor.

public function getFilters() {
  return [
    new TwigFilter('replace_wth_x', [$this, 'replaceWithX']),
  ];
}

We have defined a callback above called replaceWithX, which we can define as a method within the class CustomTwig. Filters only accept a single value, which is presented to the callback method as a single parameter. The replaceWithX method takes the incoming text and simply replaces everything that isn't a whitespace character with the letter x. The return value is printed out within the Twig template system.

public function replaceWithX($text) {
  $text = preg_replace('/[^\s]/', 'x', $text);
  return $text;
}

To use this filter we use the Twig filter syntax. The following example passes the body text of a node to the filter, which then prints out the contents of the page as a bunch of x's.

<p>{{ node.body.value|replace_wth_x }}</p>

This isn't exactly useful, but you can easily create filters that will format phone numbers or transform a key into a YouTube link. As long as you keep things simple you can do .

Custom Functions

Functions are slightly more complex than filters as they can accept multiple parameters and are intended to slightly more than the filters. To create a custom Twig function create a method called getFunctions(). This should return an array of TwigFunction objects that have a name and a callback as the arguments to the constructor. 

public function getFunctions() {
  return [
    new TwigFunction('print_author', [$this, 'printAuthor']),
  ];
}

In the above example we defined a custom method called print_author. This calls the method printAuthor which can accept more than one parameter. In this case we are just accepting a node object and checking that is the correct type before using it. I tend to do it this way and not through type hinting as it's actually easy to accidentally pass the incorrect type to the method and crash the the method.

public function printAuthor($node) {
  if (!($node instanceof \Drupal\node\Entity\Node)) {
    return;
  }
  $user = $node->getOwner();
  return $user->getDisplayName();
}

This method operates by loading the author of the node and returning this value. To use this Twig function we call it using the following syntax. This assumes we are in the node.html.twig template file as I am passing the full node object to the function.

{{ print_author(node) }}

The printed value here is either 'Anonymous' if the node has no author, or the username of the author if it does. Again, this is a simple example, but you can easily pass objects to this method and return values that you wouldn't be able to do in the default Twig syntax. This would be things like pulling together a list of classes or combining two fields together. The fact that you can add additional parameters or even use dependency injection to add in other types of Drupal object makes this approach quite powerful.

Using custom filters and functions like this can really allow you to empower Twig to another level. If you want to see more of what this technique can do then check out the Twig Tweak Drupal module which adds a few custom filters and functions to the default Drupal Twig feature set.

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
12 + 5 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.