首页 文章

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

提问于
浏览
5

所以,我仍然是Symfony和Twig的新手 . 我想知道如何在模板中最好地包含/创建可重用代码片段 . 例如,假设您有一个要在每个页面上显示的侧边栏 .

{% 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的embed the widget as a controller(s) .

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
    // ...
}

并在'sidebar.html.twig'中包含这样的内容

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

所以它可以简单地在Twig中使用

{# sidebar.html.twig #}

{{ recentArticlesWidget.getRecentArticles()|raw }}

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

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

作为Twig扩展

这个与使用上述服务(see the documentation)非常相似 . 您创建了一个与之前显示的服务几乎相同的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 }

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

{# sidebar.html.twig #}

{{ getRecentArticles() }}

想法

我注意到的一件事是,最后两种方法是逻辑和视图似乎根本不再分离 . 您基本上编写了一个小部件功能,并让该功能输出小部件的完整html . 这似乎违背了Symfony试图强制执行的模块化和模式 .

另一方面,为每个单独的小部件调用不同的控制器或控制器动作(使用它们自己的twig渲染)似乎可能需要比可能需要的更多处理 . 我不确定它是否真的减慢了任何东西,但我不知道它是否过度 .

长话短说,在Symfony中使用可重用小部件是否有最佳实践?我敢肯定其中一些方法也可以混合,所以我只是想知道如何最好地解决这个问题 .

3 回答

  • 1

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

    #reusable.html.twig
    
    {% block reusable_code %}
      ...
    {% endblock %}
    

    #reused.html.twig
    {% extends 'reusable.html.twig' %}
    
    {{ block('reusable_code') }}
    

    如果您希望获得更多的可重用性,或者您的块包含业务逻辑或模型调用,则可以使用twig扩展

  • 1

    Twig扩展和Twig宏应指向正确的方向 .

    将宏用于业务逻辑的视图和扩展 .

    在Twig扩展示例的旁注中,最好只传递您正在使用的服务而不是整个服务容器 .

  • 0

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

    像这样的东西:

    layout.html.twig 将是这样的:

    {% 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 块 .

    希望这可以帮助!

相关问题