首页 > 解决方案 > Symfony 表单:如何将不同的主题/小部件应用于特定的表单字段?

问题描述

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

因此,在相同类型的相同表单字段中 ( CheckboxType) 应该使用不同的小部件。我怎样才能做到这一点?


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


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

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

虽然此解决方案有效,但我必须为所有受影响表单中的所有受影响字段创建/覆盖相同的块。这相当麻烦,因此不是一个好的解决方案。


解决方案 3:另一个可行的解决方案是创建一个自定义FormType选项,以供复选框使用,该复选框应显示为开关。这种表单类型会扩展CheckboxType,除了覆盖blockPrefix.

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

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


没有更好的方法吗?我正在寻找类似的东西{{ form_row(form.someCheckbox, 'someWidget') }}。每个主题只会有一个额外的文件,并且可以准确地分辨出哪个字段使用哪个主题。不需要额外的类、配置等。我没有以这种方式找到任何东西。

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

标签: phpsymfonysymfony-forms

解决方案


参考我上面的最后一条评论,以下适用于 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',
]);

推荐阅读