提问者:小点点

如何在Symfony(树枝)中包含可重用的小部件?


所以,我对交响乐和树枝还是相当陌生的。我想知道如何最好地在模板中包含/创建可重用代码片段。例如,假设你有一个侧边栏,你想在每一页上显示。

{% extends 'AppBundle::base.html.twig' %}

{% block body %}
    <div id="wrapper">
        <div id="content-container">
            {# Main content... #}
        </div>
        <div id="sidebar">
            {% include 'sidebar.html.twig' %}
        </div>
    </div>
{% endblock %}

边栏中有几个小部件,它们都有自己的逻辑。你如何去创建/包括这些小部件?

到目前为止,我遇到了几个解决方案。

第一个是将小部件作为控制器嵌入Twig中。

class WidgetController extends Controller
{
    public function recentArticlesWidgetAction()
    {
        // some logic to generate to required widget data
        // ...

        // Render custom widget template with data
        return $this->render('widgets/recentArticles.html.twig', array('data' => $data)
        );
    }
    public function subscribeButtonWidgetAction()
    {
        // ...

        return $this->render('widgets/subscribeButton.html.twig', array('data' => $data)
    }

    // Many more widgets
    // ...
}

并将其包含在“侧边栏”中。html。像这样的树枝

<div id="sidebar">        
    {# Recent Articles widget #}
    {{ render(controller('AppBundle:Widget:recentArticlesWidget' )) }}

    {# Subscribe-Button widget #}
    {{ render(controller('AppBundle:Widget:subscribeButtonWidget' )) }}

    {# and so on #}
</div>

我还看到一些人将小部件注册为服务(可以直接在Twig中使用)。与小部件主类

// src/AppBundle/Service/RecentArticlesWidget.php
namespace AppBundle\Service;

use Symfony\Component\DependencyInjection\ContainerInterface;

class RecentArticlesWidget
{
    protected $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function getRecentArticles()
    {
        // do some logic (use container for doctrine etc.)
    }
}

然后注册为服务,

# src/AppBundle/Resources/config/services.yml
services:
    recentArticlesWidget:
        class:     AppBundle\Service\RecentArticlesWidget
        arguments: ["@service_container"]

传递到控制器中的模板,

namespace AppBundle\Controller;

class SidebarController {

    public function showAction($request) {

        // Get the widget(s) 
        $recentArticlesWidget = $this->get('recentArticlesWidget'); 

        // Pass it (them) along
        return $this->render('sidebar.html.twig', array('recentArticlesWidget' => $recentArticlesWidget));
    }
}

所以它可以像这样简单地用在树枝上

{# sidebar.html.twig #}

{{ recentArticlesWidget.getRecentArticles()|raw }}

或者,您也可以通过将服务添加到Twig配置,直接将其添加到Twig全局变量。这样,它就不需要由控制器传递到视图中。

#app/config/config.yml
twig:
    globals:
        # twig_var_name: symfony_service
        recentArticlesWidget: "@recentArticlesWidget"

这与使用上述服务非常相似(请参阅文档)。您创建了一个与前面显示的服务几乎相同的twig扩展类

// src/AppBundle/Twig/RecentArticlesWidgetExtension.php
namespace AppBundle\Twig;

class RecentArticlesWidgetExtension extends \Twig_Extension
{
    protected $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function getFunctions()
    {
        return array( 
            "getRecentArticles" => new Twig_Function_Method($this, "getRecentArticles")
            // register more functions
        );
    }

    public function getRecentArticles()
    {
        // do some logic (use container for doctrine etc.)
    }

    // Some more functions...

    public function getName()
    {
        return 'WidgetExtension';
    }
}

使用添加的标记将其注册为服务

# src/AppBundle/Resources/config/services.yml
services:
    recentArticlesWidget:
        class: AppBundle\Twig\RecentArticlesWidgetExtension
        arguments: [@service_container]
        tags:
            - { name: twig.extension }

并简单地将其用作细枝中的全局函数

{# sidebar.html.twig #}

{{ getRecentArticles() }}

我注意到的一点是,在最后两种方法中,逻辑和视图似乎不再分离。您基本上编写了一个小部件函数,并让该函数输出小部件的完整html。这似乎与Symfony试图实现的模块化和模式背道而驰。

另一方面,为每个小部件调用不同的控制器或控制器操作(带有它们自己的树枝渲染)似乎需要比需要更多的处理。我不确定它是否真的减慢了速度,但我确实想知道它是否过度了。

长话短说,在Symfony中使用可重用的小部件有最佳实践吗?我相信其中一些方法也可以混合使用,所以我只是想知道如何最好地实现这一点。


共3个答案

匿名用户

细枝延伸和细枝宏应该为您指明正确的方向。

对视图使用宏,对业务逻辑使用扩展。

在您的Twig扩展示例中,顺便提一下,只传入您正在使用的服务而不是整个服务容器可能是个好主意。

匿名用户

我宁愿使用块和父模板。简单地说,在主布局中插入侧栏,并让所有其他需要侧栏的模板从中继承。

像这样的东西:

布局。html。细枝将是这样的:

{% block title}
// title goes here
{%endblock%}

<div id="wrapper">
    <div id="content-container">
        {% block pageContent %}
        {% endblock %}
    </div>
    <div id="sidebar">
        // Side bar html goes here
    </div>
</div>

现在所有的页面都将继承这个layout.html.twig。例如,一个名为home.html.twig的页面将是:

home.html.twig

{% extends 'AppBundle::layout.html.twig' %}

{% block title%}
// this page title goes here
{% endblock %}

{% block pageContent %}
    //This page content goes here
{% endblock %}

您可以根据需要添加任意多的块,例如每个页面的cssjs块。

希望这能有所帮助!

匿名用户

我认为最简单的方法是在模板中定义块,然后扩展该模板以渲染块,如下所示:

#reusable.html.twig

{% block reusable_code %}
  ...
{% endblock %}

#reused.html.twig
{% extends 'reusable.html.twig' %}

{{ block('reusable_code') }}

如果您想要更多的可重用性,或者您的块包含业务逻辑或模型调用,则可以使用细枝扩展