【问题标题】:Nearly completed - just need to finish Twig extension to render ESI - Symfony2几乎完成 - 只需要完成 Twig 扩展来渲染 ESI - Symfony2
【发布时间】:2013-06-04 02:10:30
【问题描述】:

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

{{ new_func(route-name) }}

做同样的事情:

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

...但有一些调整

几乎完成了,但需要更改的是这一行,但我看不出如何从这段代码中调用 ESI(在 Twig 之外):

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

    }

 }

【问题讨论】:

  • 这个问题与您在 3 小时前发布的另一个问题真的不同吗? stackoverflow.com/questions/16907576/…
  • renderstandalone 选项有什么问题? Symfony2 已经很好地处理了 esi。
  • 我并不是说它有什么问题。
  • 好的,请原谅我的问题,但是这样的扩展有什么好处呢?
  • 这是另一个问题的可能解决方案(发布在这些 cmets 的顶部)。无论哪种方式,这两个问题都没有得到解答,所以我需要做一个。当然这应该很简单??

标签: symfony twig


【解决方案1】:

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

如何跟踪和扩展 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() 并将您的实现传递到您的 twig 扩展中。

跟踪配置

你要明白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

【讨论】:

  • 谢谢,这看起来很棒,我刚刚开始解决这个问题,但是在我的 services.yml 中,我需要传递一个 FragmentHandler 满意的参数。如果我通过 '@service_container' 然后它返回此错误: 传递给 NewTwigFunction::__construct() 的参数 1 必须是 Symfony\Component\HttpKernel\Fragment\FragmentHandler 的一个实例,appDevDebugProjectContainer 的实例给定......我试过通过@handler 和其他各种但没有任何作用
  • @user2143356 更新了答案,其中包含有关跟踪配置的信息。另请记住,所有示例都是说明性的,并且在 1:1 复制时可能不起作用:)
  • @fragment.handler 完成了这项工作 - 现在处理剩下的工作。谢谢
  • Inori,毫无疑问,你是 Symfony2 之王。似乎没有人能回答这个问题,如果您能提供 2 分钟的专业知识,我将不胜感激:stackoverflow.com/questions/17937574/…
  • 提示:您应该添加继承 HIncludeFragmentRenderer 或 EsiFragmentRenderer 的自定义 FragmentHandler 并覆盖交付方法。
猜你喜欢
  • 1970-01-01
  • 2011-03-13
  • 2023-03-17
  • 1970-01-01
  • 2011-04-18
  • 1970-01-01
  • 2014-03-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多