Symfony 表单:如何将不同的 themes/widgets 应用于特定的表单字段?

Symfony Form: How to apply different themes/widgets to specific form fields?

基于 Symfony 3.4 的表单包含多个 CheckboxType 字段。其中一些字段应显示为普通复选框(= 使用默认复选框小部件),而其他一些应显示为 on/off 开关(如 iOS UISwitche)。

所以,在同一个表单字段内,同类型(CheckboxType)应该使用不同的widgets。我该怎么做?


解决方案 1: 当然,我可以将自定义 form_theme 应用于完整表单 ({% form_theme form 'AppBundle:Form:myTheme.html.twig' %})。但是这种影响 all 表单内的复选框而不只是一个。所以这不是解决方案。


解决方案 2: 一种可行的解决方案是使用其 id 将不同的小部件应用于一个特定字段:

{% block _user_registration_form_someCheckBoxRow %}
    ...
{% endblock %}

虽然此解决方案有效,但我必须 create/override 对所有受影响表单中的所有受影响字段使用相同的块。这非常麻烦,因此不是一个好的解决方案。


解决方案 3: 另一个可行的解决方案是创建一个自定义 FormType 以供应显示为开关的复选框使用。此表单类型将扩展 CheckboxType,除了覆盖 blockPrefix 之外什么都不做。

虽然这非常接近于一个好的解决方案,但有一个主要缺点:假设复选框开关有不同的主题/小部件。一种标签在顶部,一种标签在开关旁边,等等。这些样式中的每一种不仅需要自己的小部件,还需要自己的表单类型,需要在 FormPass.php 中注册,等等。这当然可行,但也非常麻烦且容易出错。

所以,这将是解决方案,但不是 nice


有没有更好的办法?我正在搜索类似 {{ form_row(form.someCheckbox, 'someWidget') }} 的内容。每个主题只有一个额外的文件,并且可以准确地分辨出哪个字段使用哪个主题。不需要额外的 类、配置等。我以这种方式一无所获。

使用虚拟表单类型真的是最好的解决方案吗?

您可以将 a theme 应用于单个复选框:

{% form_theme form.my_special_checkbox 'Form/Theme/special_checkbox.html.twig' %}

或者如果你不需要一个单独的主题(单个用例)你可以在当前的 Twig 文件中写块:

{% block _my_checkbox_widget_block %}
    {# the theme #}
{% endblock %}

{% form_theme form.my_special_checkbox _self %}

我会使用自定义块名称创建自定义复选框组件并将其放入全局表单主题中,以便我可以在整个应用程序中重复使用它。

参考我上面的最后一条评论,以下内容适用于 Symfony 5。0.x(尽管 3.4 不需要进行重大更改,可能只需要一些额外的配置):

为了抽象主题处理,定义自己的复选框类型,如下(示例):

<?php
declare(strict_types=1);

namespace App\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ThemedCheckboxType extends AbstractType
{
    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver
            ->setDefaults([
                'theme' => 'default',
                'block_prefix' => static function (Options $options): ?string {
                    return [
                        'default' => null,
                        'theme-a' => 'theme_a_checkbox',
                        'theme-b' => 'theme_b_checkbox',
                    ][$options['theme']];
                },
            ])
            ->setAllowedValues('theme', ['default', 'theme-a', 'theme-b']);
    }

    /**
     * @inheritDoc
     */
    public function getParent(): string
    {
        return CheckboxType::class;
    }
}

然后在需要的地方简单地使用它,例如:

$builder->add('test_a1', ThemedCheckboxType::class, [
    'theme' => 'theme-a', // or others
]);

这样,您可以注入自己的表单主题,在需要时直接操作复选框,例如

{% block theme_a_checkbox_widget %}
    <em>Theme-A</em>
    {{ block('checkbox_widget') }}
{% endblock %}

{% block theme_b_checkbox_widget %}
    <em>Theme-B</em>
    {{ block('checkbox_widget') }}
{% endblock %}

如果您不想使用额外的表单类型,可以使用自定义表单扩展来向默认复选框类型添加选项,从而实现同样的效果。参见:https://symfony.com/doc/current/form/create_form_type_extension.html

例如:

<?php
declare(strict_types=1);

namespace App\Form\Extension;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;

class CheckboxTypeExtension extends AbstractTypeExtension
{
    /**
     * @inheritDoc
     */
    public static function getExtendedTypes(): iterable
    {
        return [CheckboxType::class];
    }

    /**
     * @inheritDoc
     */
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver
            ->setDefaults([
                'theme' => 'default',
                'block_prefix' => static function (Options $options): ?string {
                    return [
                        'default' => null,
                        'theme-a' => 'theme_a_checkbox',
                        'theme-b' => 'theme_b_checkbox',
                    ][$options['theme']];
                },
            ])
            ->setAllowedValues('theme', ['default', 'theme-a', 'theme-b']);
    }
}

允许:

$builder->add('test_a1', CheckboxType::class, [
    'theme' => 'theme-a',
]);