Внедрить шаблон рендеринга - PullRequest
0 голосов
/ 06 сентября 2018

У меня есть метод контроллера, который я использую для «сбора» переменных, которые будут назначены шаблону. Я переопределил метод render () контроллера для объединения «собранных» и рендеринга параметров и назначения их шаблону.

Пример:

class Controller extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
    private $jsVars = [];

    protected function addJsVar($name, $value)
    {
        $this->jsVars[$name] = $value;
    }

    public function render($view, array $parameters = [], Response $response = null)
    {
        return parent::render($view, array_merge($parameters, ['jsVars' => $this->jsVars], $response);
    }

    public function indexAction()
    {
        // collect variables for template
        $this->addJsVar('foo', 'bar');

        return $this->render('@App/index.html.twig', ['foo2' => 'bar2']);
    }
}

Я только что обновился до Symfony 3.4, который жалуется, что, начиная с Symfony4, я не могу переопределить метод render (), так как он будет окончательным.

Как я могу заставить его работать без проблем, т.е. без определения нового метода?

  • Я знаю о глобалах Twig, но они мне не помогают
  • Я мог бы использовать сервис для сбора переменных и внедрить этот сервис в Twig, но это кажется странным
  • Есть ли события, которые я мог бы прослушать, например, TwigPreRender или что-то в этом роде?

Ответы [ 2 ]

0 голосов
/ 12 сентября 2018

Кажется, что нет простого пути.

В основном есть 2 варианта:

  • создайте свой собственный шаблонизатор, расширяя текущий Symfony \ Bundle \ TwigBundle \ TwigEngine
  • украсить текущую службу шаблонизатора templating.engine.mytwig

Я выбрал последнее.

Несколько объяснений:

  • Я создал сервис templating.engine.mytwig, который украшает текущий движок templating.engine.twig. Класс получит текущий ´TwigEngine` в качестве входных данных, и я делегирую большую часть материала
  • Класс также должен быть расширение ветки путем реализации \Twig_ExtensionInterface (или расширения \Twig_Extension мне было достаточно). Также сервис должен иметь тег twig.extension. В противном случае вы получите такие ошибки, как «Не удается найти частный сервис« сборочный »и т. Д.»
  • setParameter / getParameter для сбора и возврата параметров
  • Затем я добавил ярлыки в свой контроллер - setJsVar
  • Шаблон Twig также требует обработки этих переменных, предпочтительно где-то на уровне макета. Но это не входит здесь
  • Можно ли использовать это решение для сбора произвольных параметров шаблона, например, если вы хотите назначить другой метод или любой другой метод
  • Было бы неплохо очистить собранные параметры после рендера

Это все стоило? Я не знаю :) Не могу понять, почему команда Symfony решила сделать Controller :: render финальной. Но в любом случае вот оно:

TwigEnging класс:

namespace My\CommonBundle\Component\Templating\MyTwigEngine;

use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Bundle\TwigBundle\TwigEngine;
use Symfony\Component\HttpFoundation\Response;

class MyTwigEngine extends \Twig_Extension implements EngineInterface
{
    /**
     * @var TwigEngine $twig Original Twig Engine object
     */
    private $twig;
    /**
     * @var array $parameters Collected parameters to be passed to template
     */
    private $parameters = [];


    /**
     * MyTwigEngine constructor.
     *
     * @param TwigEngine $twig
     */
    public function __construct(TwigEngine $twig)
    {
        $this->twig = $twig;
    }

    /**
     * "Collects" parameter to be passed to template.
     *
     * @param string $key
     * @param mixed $value
     *
     * @return static
     */
    public function setParameter($key, $value)
    {
        $this->parameters[$key] = $value;
        return $this;
    }

    /**
     * Returns "collected" parameter
     *
     * @param string $key
     * @return mixed
     */
    public function getParameter($key, $default = null)
    {
        $val = $this->parameters[$key] ?? $default;

        return $val;
    }

    /**
     * @param string|\Symfony\Component\Templating\TemplateReferenceInterface $name
     * @param array $parameters
     *
     * @return string
     * @throws \Twig\Error\Error
     */
    public function render($name, array $parameters = array())
    {
        return $this->twig->render($name, $this->getTemplateParameters($parameters));
    }

    /**
     * @param string $view
     * @param array $parameters
     * @param Response|null $response
     *
     * @return Response
     * @throws \Twig\Error\Error
     */
    public function renderResponse($view, array $parameters = array(), Response $response = null)
    {
        return $this->twig->renderResponse($view, $this->getTemplateParameters($parameters), $response);
    }

    /**
     * @param string|\Symfony\Component\Templating\TemplateReferenceInterface $name
     *
     * @return bool
     */
    public function exists($name)
    {
        return $this->twig->exists($name);
    }

    /**
     * @param string|\Symfony\Component\Templating\TemplateReferenceInterface $name
     *
     * @return bool
     */
    public function supports($name)
    {
        return $this->twig->supports($name);
    }

    /**
     * @param $name
     * @param array $parameters
     *
     * @throws \Twig\Error\Error
     */
    public function stream($name, array $parameters = array())
    {
        $this->twig->stream($name, $this->getTemplateParameters($parameters));
    }


    /**
     * Returns template parameters, with merged jsVars, if there are any
     * @param array $parameters
     * @return array
     */
    protected function getTemplateParameters(array $parameters = [])
    {
        $parameters = array_merge($this->parameters, $parameters);

        return $parameters;
    }
}

Услуги декоратора (services.yml):

services:
    templating.engine.mytwig:
        decorates: templating.engine.twig
        class: My\CommonBundle\Component\Templating\MyTwigEngine
        # pass the old service as an argument
        arguments: [ '@templating.engine.mytwig.inner' ]
        # private, because you probably won't be needing to access "mytwig" directly
        public:    false
        tags:
            - { name: twig.extension }

Изменение базового контроллера:

namespace My\CommonBundle\Controller;

use My\CommonBundle\Component\Templating\MyTwigEngine;


abstract class Controller extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
    /**
    * Allows to set javascript variable from action
    *
    * It also allows to pass arrays and objects - these are later json encoded
    *
    * @param string $name Variable name
    * @param mixed $value - string|int|object|array
    *
    * @return static
    */
    protected function setJsVar($name, $value)
    {
        /** @var MyTwigEngine $templating */
        $templating = $this->getTemplating();
        if (!$templating instanceof MyTwigEngine) {
            throw new \RuntimeException(sprintf(
                'Method %s is implemented only by %s', __METHOD__, MyTwigEngine::class
            ));
        }

        $jsvars = $templating->getParameter('jsVars', []);
        $jsvars[$name] = $value;
        $templating->setParameter('jsVars', $jsvars);

        return $this;
    }

    /**
     * Returns templating service
     * @return null|object|\Twig\Environment
     */
    private function getTemplating()
    {
        if ($this->container->has('templating')) {
            $templating = $this->container->get('templating');
        } elseif ($this->container->has('twig')) {
            $templating = $this->container->get('twig');
        } else {
            $templating = null;
        }

        return $templating;
    }
}
0 голосов
/ 06 сентября 2018

Вы можете визуализировать контроллер из Twig следующим образом:

{{ render(controller('App\\Controller\\YourController::yourAction', { 'args': 'hi' })) }}

Документация здесь

...