drupal Dependency Injection Into Twig Extensions


Example

This example will show you how to use Dependency Inject to use other services registered in the Drupal environment.

Imagine you have an SVG image file that changes colors depending on some random CSS/Javascript thing in your project. To be able to target the SVG with CSS you have to actually have the SVG file in the DOM. So you create a base SVG file without any colors and place it in your theme folder.

Of course you could just paste the contents of the file in the Twig template but that wouldn't be nice. You can create a Twig extension but you also don't want to hardcode your theme path in the extension source code.

This means we have to get the path dynamically. You have two options:

  1. Use the equivalent to a global variable by calling \Drupal::theme()->getActiveTheme()->getPath();
  2. Inject the ThemeManager (given by \Drupal::theme()) in your extension class

In this example we will take the second example because it can be widely applicable to any service (you import the Request or the Database Connection if you want).

This assumes that you have a module called twig_svg_extension and a twig_svg_extension.services.yml file:

services:
  twig_svg_extension.twig_extension:
    class: Drupal\twig_svg_extension\TwigExtension\TwigSvgExtension
    arguments: ['@theme.manager']
    tags:
      - { name: twig.extension }

Please not the arguments key that tells Drupal the service to inject.

namespace Drupal\twig_svg_Extension\TwigExtension;

use Drupal\Core\Theme\ThemeManager;
use Twig_Extension;
use Twig_SimpleFilter;

class TwigSvgExtension extends Twig_Extension  {
  private $theme;
  
  // Dependency injection at work!
  public function __construct(ThemeManager $theme) {
    $this->theme = $theme;
  }

  public function getFilters() {
    return [
      'svg' =>new Twig_SimpleFilter('svg', [$this, 'svgFilter']),
    ];
  }

  public function getName() {
    return 'twig_svg_extension.twig_extension';
  }

  public function svgFilter(string $filepath) {
    $realpath = realpath($this->theme->getActiveTheme()->getPath().DIRECTORY_SEPARATOR.$filepath);
    $pathinfo = pathinfo($realpath);

    if($realpath !== false && strtolower($pathinfo['extension']) === 'svg') {
      return file_get_contents($realpath);
    }

    return '"'.$filepath.'" does not exist or is not an SVG';
  }
}

Please note the constructor which contains the dependency we injected in the service configuration as well as the svgFilter that gets the current active theme path.

$filepath should be a relative path to your themes folder. The extension will convert the file path into the contents of the file it points to.