Twig is a flexible and powerful templating engine that is used in Drupal 8 and other web development frameworks. However, there are times when you need to manipulate data in specific ways, or you need to access data from Drupal that is not directly available in Twig. In these cases, you can use preprocess steps to modify the data before it is passed to Twig.

Alternatively, you can create custom Twig functions and filters that extend the capabilities of Twig within the Drupal framework. This can be especially useful if you have custom templates that require specialized functionality. By creating your own functions and filters, you can manipulate data in custom ways and format it in a way that suits your needs.

Overall, both preprocess steps and custom Twig functions and filters are powerful tools that can help you customize your Drupal site's templates and provide an optimal user experience.

We previously published an article on creating custom Twig functions and filters for Drupal 8. In this article, we will do the same for Drupal 9 and 10 using a single class.

Let's start with defining our extension class.

Step 1: Define twig extension class.

<?php

namespace Drupal\starter_base\TwigExtension;

use Drupal\block\BlockInterface;
use Drupal\block\Entity\Block;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;

/**
 * MyTwigExtension provides function and filter.
 */
class MyTwigExtension extends AbstractExtension {

  /**
   * EntityTypeManager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * MyTwigExtension constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   EntityTypeManager service object.
   */
  public function __construct(EntityTypeManagerInterface $entityTypeManager) {
    $this->entityTypeManager = $entityTypeManager;
  }

  /**
   * Declare your custom twig function here.
   *
   * @return \Twig\TwigFunction[]
   *   TwigFunction array.
   */
  public function getFunctions() {
    return [
      new TwigFunction('display_block_by_id',
        [$this, 'displayBlockById'],
        ['is_safe' => ['html']]
      ),
    ];
  }

  /**
   * Declare your custom twig filter here
   *
   * @return \Twig\TwigFilter[]
   *   TwigFilter array.
   */
  public function getFilters() {
    return [
      new TwigFilter(
        'remove_links',
        [$this, 'removeLinks']
      ),
    ];
  }

  /**
   * Provides render array for block id.
   *
   * @param string $block_id
   *   Valid block id.
   *
   * @return array|string
   *   NUll or render array of block.
   */
  public function displayBlockById(string $block_id) {

    if ($block_id) {
      $block = Block::load($block_id);
      if ($block instanceof BlockInterface) {
        return $this->entityTypeManager
          ->getViewBuilder('block')->view($block);
      }
    }

    return '';
  }

  /**
   * Function to remove only links from the html
   *
   * @param string $string
   *  Html as string
   *
   * @return string
   *  Filtered html
   */
  public static function removeLinks(string $string) {
    return preg_replace('#<a.*?>(.*?)</a>#i',
      '\1',
      $string);
  }

}

In the example provided, the class extends AbstractExtension which allows the definition of custom filters and functions for Twig.

Step 2: Declare twig extension in services.yml

services:
  starter_base.twig_extension:
    class: Drupal\starter_base\TwigExtension\MyTwigExtension
    tags:
      - { name: twig.extension }
    arguments:
      - '@entity_type.manager'

Step 3: Usage

{# Remove links from summary using remove_links filter. #}
{{ node.body.summary|remove_links }}
    
{# Dispaly account menu using display_block_by_id custom twig function. #}
{{ display_block_by_id('olivero_account_menu') }}

The article has provided us with a fundamental understanding of how to create a custom Twig extension. By implementing this knowledge, we can experiment with the extension in our Drupal theme and tailor it to our specific requirements, resulting in a more personalized and optimized Twig template.