Twig 干涉模板渲染

Twig 干涉模板渲染,twig,symfony4,Twig,Symfony4,我有一个控制器方法,我用它来“收集”要分配给模板的变量。我已经重写了控制器的render()方法,以合并“收集的”和渲染参数,并将它们分配给模板 例如: class Controller extends \Symfony\Bundle\FrameworkBundle\Controller\Controller { private $jsVars = []; protected function addJsVar($name, $value) { $thi

我有一个控制器方法,我用它来“收集”要分配给模板的变量。我已经重写了控制器的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,它抱怨说,由于Symfony 4,我不允许重写render()方法,因为它将是最终的

我如何才能使它无缝地工作,即不定义新方法

  • 我知道细枝球,但这些对我没有帮助
  • 我可以使用一个服务来收集变量并将该服务注入到Twig,但这似乎有些奇怪
  • 是否有我可以收听的活动,例如TwigPreRender或smth

您可以从细枝内部渲染控制器,如下所示:

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

文档似乎没有简单的方法

基本上有两种选择:

  • 通过扩展当前的Symfony\Bundle\TwigBundle\TwigEngine创建您自己的模板引擎
  • 装饰当前模板引擎服务
    templating.engine.mytwig
我选择了后者

一些解释:

  • 我创建了服务
    templating.engine.mytwig
    哪个当前引擎
    templating.engine.twig
    。类将获取当前的“TwigEngine”作为输入,我将把大部分内容委托给它
  • 该类还需要通过实现
    \Twig\u ExtensionInterface
    (或者扩展
    \Twig\u Extension
    对我来说就足够了)。服务还需要有标签
    twig.extension
    。否则,您将最终出现错误,例如“找不到私人服务‘assetic’等”
  • setParameter/getParameter用于收集和返回参数
  • 然后我将快捷方式方法添加到我的控制器-
    setJsVar
  • 细枝模板还需要处理这些变量,最好是在布局级别的某个地方。但这不包括在这里
  • 您可以使用此解决方案来收集任意模板参数,例如,如果您希望从其他方法或其他任何方法进行赋值
  • 最好在渲染后清除收集的参数
这一切值得吗?我不知道:)无法理解为什么Symfony团队首先选择了Controller::render final。但无论如何,这是:

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):

基本控制器变更:

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;
    }
}

谢谢,但这不是我要问的:)您的问题不是很清楚,请编辑以使其更清楚。您可以定义自己的渲染方法,例如
renderWithJsVars(…)
。它也可以作为文档的一种形式。是的,但是我怎么能使用相同的方法呢。编辑我的问题。
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;
    }
}