几乎完成 - 只需要完成Twig扩展即可呈现ESI - Symfony2

时间:2013-06-04 02:10:30

标签: symfony twig

我想创建一个Twig扩展并使用它:

{{ new_func(route-name) }}

做同样的事情:

{{ render_esi(url(route-name)) }}

......但有一些调整

它已接近完成但是这条线需要更改,但我看不出如何通过此代码(在Twig之外)调用ESI:

return $environment->render($route);   /// needs to receive route and render an ESI

-

namespace Acme\Bundle\MyBundle\Twig;

class NewTwigFunction extends \Twig_Extension
{

    private $request;

    public function __construct($container)
    {
        $this->request = $container->get('request');
    }

    public function getFunctions() {

        return array(
            'new_func' => new \Twig_Function_Method($this, 'newFunction', array('needs_environment' => true) )
        );

    }

    public function newFunction(\Twig_Environment $environment, $route) {

        $r = $this->request;

        return $environment->render($route);

    }

    public function getName() {

        return "new_func";

    }

 }

1 个答案:

答案 0 :(得分:11)

我不确定我会说你为什么需要这个,但我认为这是一个抽象问题的例子:

如何追踪&扩展Symfony2的核心功能?

查找功能

似乎您无法找到执行render_esi的位置,所以让我们解决这个问题!

这似乎不是标准的Twig功能,所以它必须是一个扩展名,就像你正在创建的那个。

它应该位于Symfony2核心文件的某个位置,因此我们开始查看vendor/symfony/src文件夹。由于我们已经知道我们正在处理Twig的扩展,Component文件夹是不可能的(因为Twig是Symfony2核心组件的独立库)。

所以我们将其缩小到BridgeBundle。如果我们查看它们,我们会看到Bundle/TwigBundleBridge/Twig。我们也知道Symfony2开发人员遵循严格的代码/架构风格,因此我们确切地知道要查找的文件夹 - Extension。现在只需检查它们两者。

长话短说,我们在vendor/symfony/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension中找到了我们正在寻找的内容,我们在其中看到render_*函数。累积奖金!

扩展功能

在更改任何内容之前,我们需要首先模拟已经存在的内容,因此我们创建了这样的内容:

use Symfony\Component\HttpKernel\Fragment\FragmentHandler;

class NewTwigFunction extends \Twig_Extension
{
    private $handler;

    public function __construct(FragmentHandler $handler)
    {
        $this->handler = $handler;
    }

    public function getFunctions() 
    {
        return array(
            'new_func' => new \Twig_Function_Method($this, 'newFunction', array('is_safe' => array('html')) )
        );
    }

    public function newFunction($uri, $options = array()) 
    {
        return $this->handler->render($uri, 'esi', $options);
    }

    public function getName() 
    {
        return "new_func";
    }

 }

现在打电话

{{ new_func(url(route-name)) }}

你应该看到与

相同的结果

{{ render_esi(url(route-name)) }}

但我们仍然需要摆脱url部分。 简单来说,我们只需将router服务添加到我们的扩展程序中!现在我们的扩展程序可能如下所示:

use Symfony\Component\Routing\Router;
use Symfony\Component\HttpKernel\Fragment\FragmentHandler;

class NewTwigFunction extends \Twig_Extension
{
    private $handler;
    private $router;

    public function __construct(FragmentHandler $handler, Router $router)
    {
        $this->handler = $handler;
        $this->router  = $router;
    }

    public function getFunctions() 
    {
        return array(
            'new_func' => new \Twig_Function_Method($this, 'newFunction', array('is_safe' => array('html')) )
        );
    }

    public function newFunction($routeName, $options = array()) 
    {
        $uri = $this->router->generate($routeName);

        return $this->handler->render($uri, 'esi', $options);
    }

    public function getName() 
    {
        return "new_func";
    }

 }

{{ new_func(route-name) }}应该按预期工作。

在中间挂钩

我理解它的方式,你想要与render_esi几乎相同的功能,但输出稍有变化。 这意味着我们需要挂钩return$this->handler->render($uri, $strategy, $options);之间的某个位置。

我们需要去的兔子洞深度取决于变化。

例如,如果要在Response对象变为实际html string之前更改它,则需要首先找到它所在的位置。一个好的选择是调查FragmentHandler

protected function deliver(Response $response)
{
    if (!$response->isSuccessful()) {
        throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $this->request->getUri(), $response->getStatusCode()));
    }

    if (!$response instanceof StreamedResponse) {
        return $response->getContent();
    }

    $response->sendContent();
}

知道了!现在您只需要扩展FragmentHandler::deliver()并将其实现传递到您的枝条扩展中。

追踪配置

您必须了解Symfony2核心代码与您在日常生活中所写的内容没有什么不同,它仍然遵循自己的规则。

例如,在Symfony2中正常创建Twig扩展时,您需要将其配置为服务,对吧?好吧,Symfony2核心扩展以相同的方式配置。您只需要找到配置文件的位置。

遵循扩展功能的逻辑,我们确信它们不在Component中。 Bridge实际上是design pattern的名称 - 不是您放置服务配置的地方:)

所以我们离开了Bundle - 显然我们找到了所需的所有信息:vendor/symfony/src/Bundle/TwigBundle/Resources/config/twig.xml

现在,我们只需查看原始HttpKernelExtension的配置方式并遵循其主导:

    <service id="twig.extension.httpkernel" class="%twig.extension.httpkernel.class%" public="false">
        <argument type="service" id="fragment.handler" />
    </service>

将其转换为更常用的.yml格式,我们的扩展配置可能如下所示:

new_func:
    class: Acme\Bundle\MyBundle\Twig\NewTwigFunction
    arguments:
        - "@fragment.handler"
        # Uncomment when implementing code from 2nd example
        # - "@router"
    tags:
        - { name: twig.extension }
    public: false