什么是全球为app后端和前端设置2个不同表单主题的最便捷方式

时间:2014-09-13 21:39:18

标签: php forms symfony

因此,在Symfony2中使用表单很容易。您创建一个自定义主题文件,然后将其添加到config.yml文件中以加载它。完成。

但是,我有两种不同的表单主题。一个用于应用程序的前端,另一个用于应用程序的后端。

我浏览了文档(http://symfony.com/doc/2.3/cookbook/form/form_customization.html),但无法找到一种简单易行的方法。

当我将主题添加到config.yml文件时,我在前端和后端都有相同的主题。我也可以在每个视图中包含这样的表单

{% form_theme form 'form_table_layout.html.twig' %}

但是,这意味着我必须在每个视图中执行此操作。

有没有办法为前端和后端创建单独的配置文件?我可以在基本模板文件中以某种方式指出应该使用哪种表单主题吗?

我能做的其他事情吗?

2 个答案:

答案 0 :(得分:2)

如果您使用Symfony2默认目录结构,即前端和后端都有一个内核,您只能(如您所述)在每个模板中设置表单主题,或使用相同的模板应用程序 - 通过在config.yml文件中设置它来扩展。

您提到的替代解决方案,即创建两个基本模板,每个模板设置一个“全局”form_theme标记理论上可行。使用以下标记为所有前端页面创建基本front-end.html.twig模板:

{% form_theme form 'form-front-end.html.twig' %}

这样可行,但您将被迫在每个继承的模板中都有一个form变量。您也无法在同一页面中将主题设置为多个表单。

您可以通过在设置样式之前检查表单变量是否已定义来改进解决方案:

{% if form is defined %}
  {% form_theme form 'form-front-end.html.twig' %}
{% endif %}

甚至更好,如果您希望能够将多个表单传递到同一个模板,可以使用forms数组来完成:

{% if forms is defined %}
  {% for form in forms %}
    {% form_theme form 'form-front-end.html.twig' %}
  {% endfor %}
{% endif %}

好处是,即使您根本没有传递变量,也不会抛出任何异常,但您必须记住将任何表单放到forms数组中。

显然,你会为后端基本模板做同样的事情。

可能有更好的解决方案,但同时我希望这有帮助!

答案 1 :(得分:0)

我之前也遇到过这个问题,找到了这篇文章,并按照Andrea Sprega的建议。最近我出现了一种可以节省一些重复性类型的方法。

警告:这有点“hackish”,可能不是最好的方法,并不能保证在下一个框架版本中工作。当你在下面阅读时,你会明白为什么。

目标:我们希望根据应用程序“环境”使用不同的默认表单模板(最常见的例子是“前端”和“后端”。但从根本上说,Symfony并不知道这样的“环境”。虽然它可以将项目拆分到不同的环境并加载不同的配置,如果我们想要的只是拥有不同的表单模板,这绝对是一种过度杀伤。

我认为最好的方法是在基本模板中设置/覆盖默认表单主题。因此我们可以在前端基本模板中形成主题A,并在后端基本模板中形成主题B.这听起来对我来说是最好的解决方案,因为表单主题是一个“视图”的东西,在视图中更改它是完全合理的。但是,问题似乎是,当执行twig(模板)代码时,表单视图已经初始化。因此,在那里更改默认表单主题为时已晚。 (我可能在这里错了,因为我没有深入挖掘以获得100%的信心)

所以我决定以另一种方式做。它的工作原理如下:

  • 首先,我假设所有前端控制器都将扩展相同的基类(例如“BaseFrontendController”)。同样,所有后端控制器都将扩展相同的BaseBackendController类。以下是我们区分前端和后端环境的方法。实际情况就是我的项目。
  • 默认的树枝模板将添加到这些基本控制器类中。它可以通过方法或注释来完成。在这篇文章中,我将使用一种公共方法。
  • 在执行控制器之前,覆盖已定义的默认树枝模板。
  • 初始化表单视图时,它将使用控制器中定义的默认树枝模板。

以下是它的完成方式:

首先,config.yml中定义的默认表单主题将传递给\Symfony\Component\Form\AbstractRendererEngine的构造函数,并分配给本地实例$defaultThemes。该字段受到保护,因此可以由其派生类使用,但没有setter可以更改其值。

因此,我们需要推出自己的Symfony\Bridge\Twig\Form\TwigRendererEngine

namespace AppBundle\Form\Twig;

use Symfony\Bridge\Twig\Form\TwigRendererEngine as BaseTwigRendererEngine;

class TwigRendererEngine extends BaseTwigRendererEngine
{
    /**
     * @param array $defaultThemes
     */
    public function addDefaultThemes($defaultThemes)
    {
        $this->defaultThemes = array_merge($this->defaultThemes, $defaultThemes);
    }
}

这个自定义渲染器引擎非常简单 - 只需添加一个新方法即可将默认主题附加到现有主题。这就是为什么我说它是“hackish” - 当内部被改变时它将不再起作用。

其次,定义一个接口TwigTemplateProvider,它将由基本前端/后端控制器类实现:

namespace AppBundle\Form\Twig;

interface TwigTemplateProvider
{
    /**
     * @return array|string The form template path
     */
    public function getDefaultFormTwigTemplates();
}

第三,我们需要一个在执行控制器时运行的监听器。

<?php

namespace AppBundle\EventListener;

use AppBundle\Form\Twig\TwigRendererEngine;
use AppBundle\Form\Twig\TwigTemplateProvider;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;

class PerControllerFormTemplateListener
{
    /**
     * @var \Twig_Environment
     */
    private $twig;

    public function __construct(\Twig_Environment $twig)
    {
        $this->twig = $twig;
    }

    public function onKernelController(FilterControllerEvent $event)
    {
        $controller = $event->getController();

        if (!is_array($controller)) {
            return;
        }

        if ($controller[0] instanceof TwigTemplateProvider) {

            /** @var \Symfony\Bridge\Twig\Extension\FormExtension $formExtension */
            $formExtension = $this->twig->getExtension('form');
            $engine = $formExtension->renderer->getEngine();
            if ($engine instanceof TwigRendererEngine) {
                $templates = (array)$controller[0]->getDefaultFormTwigTemplates();
                $engine->addDefaultThemes($templates);
            }
        }
    }
}

侦听器将获取由实现TwigTemplateProvider接口的控制器提供的表单模板名称(在字符串或数组中)。然后它会将它添加到默认主题列表并将其传递给表单渲染器引擎。

现在,将以下内容添加到services.yml

,将它们连接在一起
parameters:
    twig.form.engine.class: AppBundle\Form\Twig\TwigRendererEngine

services:
    app.form.per_controller_template_listener:
        class: AppBundle\EventListener\PerControllerFormTemplateListener
        arguments: ["@twig"]
        tags:
            - { name: kernel.event_listener, event: kernel.controller, method: onKernelController }

这里我们将%twig.form.engine.class%参数设置为我们自己的实现,并将我们的事件监听器添加到堆栈中。

使用它非常简单。例如,在基础前端控制器中,实现TwigTemplateProvider并添加以下方法:

public function getDefaultFormTwigTemplates()
{
    return 'frontend/form_layout.html.twig';
}

然后,当执行前端控制器时,此布局将添加到表单模板堆栈中。