Skip to content

Instantly share code, notes, and snippets.

@GubaEvgeniy
Last active October 22, 2019 11:53
Show Gist options
  • Select an option

  • Save GubaEvgeniy/b5db2df8f0f396e3e1906503f3833511 to your computer and use it in GitHub Desktop.

Select an option

Save GubaEvgeniy/b5db2df8f0f396e3e1906503f3833511 to your computer and use it in GitHub Desktop.

Наверх

Создание Twig extentions

Официальная документация

bin/console make:twig-extension - создаст файл, в котором по умолчанию будет 3 метода getFilters(), getFunctions() и doSomething($value), которые служат для создания кастомных фильтров, кастомных функций и соответственно логики описывающих их. Так же этот файл наследуется от AbstractExtension

namespace App\Twig;

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

class AppExtension extends AbstractExtension
{
    public function getFilters(): array
    {
        return [
            new TwigFilter('filter_name', [$this, 'doSomething'], ['is_safe' => ['html']]),
        ];
    }
    public function getFunctions(): array
    {
        return [
            new TwigFunction('function_name', [$this, 'doSomething']),
        ];
    }
    public function doSomething($value)
    {
        // ...
    }
}

При использовании конфигурации service.yaml по умолчанию, Symfony автоматически узнает о новом функционале.

Например, мы хотим добавить extention который будет переводить весь текст в верхний регистр и у нас есть сервис позволяюший это сделать. Логика:

  1. Через getFilters() описывает как будет называться новый фильтр и в какой функции(processUpper) будет описана локика его работы
  2. В конструкторе получаем нужный сервис(StrHelper) через DI
// src/Service/StrHelper.php
namespace App\Service;

class StrHelper
{
  public function toUpper(string $source): string
  {
    return strtoupper($source);
  }
}

// src/Twig/AppExtension.php
namespace App\Twig;

use App\Service\StrHelper;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;

class AppExtension extends AbstractExtension
{
    private $strHelper;
    
    public function __construct(StrHelper $strHelper)
    {
      $this->strHelper = $strHelper;
    }

    public function getFilters(): array
    {
        return [
            new TwigFilter('str_upper', [$this, 'processUpper'], ['is_safe' => ['html']]),
        ];
    }
    public function processUpper($value)
    {
        return $this->strHelper->toUpper($value);
    }
}

Использование в Twig - {{ content|str_upper }}

Ленивая загрузка

В обычной реализации кастомный twig-extention будет загружен всегда и это будет влиять на производительность системы. Однако есть возможность для ленивой(отложенной) загрузки.

Заставим класс AppExtension реализовать интерфейс ServiceSubscriberInterface. Теперь нам необходимо реализовать новый метод - getSubscribedServices. Так же добавим конструктор в который передадим контейнер - это ContainerInterface из PSR.

Таким образом Symfony попытается передам к нам в файл service-container, однако это будет контейнер не со всеми службами(где могут быть сотни сервисов), а мини-контейнер. Что бы сообщить Symfony какие услуги необходимо использовать в своём мини-контейнере мы и воспользуемся методом getSubscribedServices(), в котором будем возвращать необходимый сервис(необходимый сервис - это наш функционал(логика) который хотим использовать как twig-extention - например, мы пишем фильтр который будет переводить текст в верхний регистр см. описание выше).

В итоге, если на странице не используется наш кастомный фильтр, то и загрузка сервиса выполняться не будет.

namespace App\Twig;

use App\Service\StrHelper;
use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;

class AppExtension extends AbstractExtension implements ServiceSubscriberInterface
{
    private $container;
    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }
    public function getFilters(): array
    {
        return [
            new TwigFilter('str_upper', [$this, 'processUpper'], ['is_safe' => ['html']]),
        ];
    }
    public function processUpper($value)
    {
        return $this->container
            ->get(StrHelper::class)
            ->toUpper($value);
    }
    public static function getSubscribedServices()
    {
        return [
            StrHelper::class,
        ];
    }
}

В версии Twig 1.26

Документация

Необходимо содать отдельный класс(напрю AppRuntime), который реализует RuntimeExtensionInterface и внедряет наш кастомынй StrHelper. Так же сюда переедет логика работы(метод processUpper()).

namespace App\Twig;

use App\Service\StrHelper;
use Twig\Extension\RuntimeExtensionInterface;

class AppRuntime implements RuntimeExtensionInterface
{
    private $strHelper;

    public function __construct(StrHelper $strHelper)
    {
        $this->strHelper = $strHelper;
    }

    public function processUpper($value)
    {
        return $this->strHelper->toUpper($value);
    }
}

Т.е. из файла src/Twig/AppExtension.php убирается конструктор и processUpper(). И изменяется вызов в фильтре:

namespace App\Twig;

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

class AppExtension extends AbstractExtension
{
    public function getFilters(): array
    {
        return [
            new TwigFilter('str_upper', [AppRuntime::class, 'processUpper'], ['is_safe' => ['html']]),
        ];
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment