【问题标题】:Form themes - Add code at n block_name match表单主题 - 在 n 块名称匹配处添加代码
【发布时间】:2026-02-11 18:40:01
【问题描述】:

我有一个前端 symfony 应用程序,它从 API 获取序列化的 symfony 表单,解析它并最终呈现它。

这个应用程序应该是愚蠢的,并且不应该以任何方式了解任何远程应用程序的逻辑。 就是json格式,解析后显示出来。

序列化表单中的字段具有自定义(远程应用定义)块名称,然后在前端应用的表单主题中使用这些名称来构建字段结构。

表示字段示例:

"field_1": {
    "options": {
        "block_name": "block_name_example",
        "label": "Example",
        "required": true,
        "disabled": false,
        "choices": {
            "Choice 1": "1",
            "Choice 2": "2"
        },
        "help_description": "",
        "attr": {
            "name": "field_name_1",
            "short_name": "fieldName1"
        }
    },
    "type": "Symfony\\Component\\Form\\Extension\\Core\\Type\\ChoiceType"
}

我想在 form_theme 块中添加“第一次匹配此块名称”的内容(例如),在远程应用端不添加任何逻辑,例如:

{% block _form_block_name_example %}
    {% if match_occurrence = 1 %}
        {# do something here #}
    {% endif %}
    {{ form_widget(form) }}
{% endblock %}

我知道有很多方法(表单字段额外选项,将其包装到集合类型字段中......)通过远程应用程序代码编辑来解决这个问题,但由于各种原因我不想这样做,主要是以避免对远程应用程序的代码造成任何额外的复杂性。

找不到解决此问题的干净方法。你愿意做我的英雄吗?

【问题讨论】:

    标签: forms symfony twig symfony4


    【解决方案1】:

    我想到了一种非常简洁的方法来仅在前端应用程序上处理此问题。从不相关的代码中最大限度地剥离类并重命名一些东西,以使其尽可能通用。

    我通过表单扩展在我的字段中添加了一个新的额外 first_of_type 选项,这是我在表单生成时设置的。然后,我将它用作表单主题块中的一个随意选项。代码如下。

    表单扩展:

    class ExtraOptionsExtension extends AbstractTypeExtension
    {
        /**
         * Add the width option.
         *
         * @param OptionsResolver $resolver
         */
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefined([
                /* ... */
                'first_of_type'
            ]);
        }
    
        public function buildView(FormView $view, FormInterface $form, array $options)
        {
            /* ... */
            $view->vars['first_of_type'] = false;
            if (!empty($options['first_of_type'])) {
                $view->vars['first_of_type'] = true;
            }
        }
    
        /**
         * Returns the name of the type being extended.
         *
         * @return string The name of the type being extended
         */
        public function getExtendedType()
        {
            return FormType::class;
        }
    }
    

    表单类型:

    abstract class AbstractType extends BaseAbstractType
    {
        /* ... */
    
        /**
         * {@inheritdoc}
         *
         * @param FormBuilderInterface $builder
         * @param array $options
         */
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $blockNames = [];
            if (is_array($this->fields)) {
                foreach ($this->fields as $property => $data) {
                    $type = $data['type'] ?? null;
                    $opts = $data['options'] ?? [];
                    if (!in_array($type, $this::TYPES_WITHOUT_EXTRA) &&
                        isset($data['options']['block_name']) &&
                        !in_array($data['options']['block_name'], $blockNames)) {
                        $blockNames[] = $data['options']['block_name'];
                        $opts['first_of_type'] = true;
                    }
                    $builder->add($property, $type, $opts);
                }
            }
        }
    
        /* ... */
    }
    

    表单主题块:

    {% block custom_block %}
        <div class="form-group form-inline {% if not first_of_type %} hide {% else %}">
            {{ form_label(form, null, {'label_attr': {'class': 'control-label'}}) }}
            {{ form_widget(form) }}
        </div>
    {% endblock %}
    

    与它的对相比,仍然可以改进以提供有关元素位置的更多信息(不仅仅是它是第一个事实)。

    【讨论】: