Symfony/Form 根据数据添加属性


Symfony/Form add attribute based on the data

在我的一种自定义表单类型中,我需要向字段添加自定义HTML属性。但是,该属性基于数据。所以我添加了一个事件处理程序,但我不确定我应该在其中做什么。

$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
    $data = $event->getData();
    // not sure what to do here.
};

也许它应该在其他地方完成。请记住,对于我的用例,我需要最初设置为表单的数据,而不是提交的数据。

编辑:我被要求提供更多细节,说明我正在努力实现的目标。基本上,我需要将数据库中的初始数据(在PRE_SET_DATA事件中可用)放入data-* HTML属性中,以便javascript可以使用它。

更新:即使几个月后也没有好的答案,所以我认为目前不可能解决这个问题。

您可以将旧的表单元素替换为新的表单元素:

$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
    $data = $event->getData();
    $form = $event->getForm();
    $val = $data['some_field'];
    $options = $form->get('existing_field_name_to_replace')->getConfig()->getOptions();
    $options['attr']['your-attr'] = $val;
    $form->add('existing_field_name_to_replace', 'type', $options);
};

$form->add()替换以前定义的表单域。但是您也可以使用$form->remove()然后$form->add().

如果这是一个 HTML 属性,则只能通过编辑视图来执行此操作:

<div {% if form.DATA_FIELD.vars.value == 'foo' %}disabled{% endif %}>

在这种情况下,如果数据在DATA_FIELD中具有值"foo",则该<div>将被禁用。如果您的逻辑比简单的数据属性检查更复杂,则可能需要创建 twig 扩展。

解决方案是实现表单类型的buildView方法。此方法接收 FormView 参数,该参数可用于添加任何属性并在需要时操作标记类。它还接收Form实例,以便您可以获取数据并实现所需的任何逻辑。

class MyCustomForm extends AbstractType {
    public function buildForm(FormBuilderInterface $builder, Array $options = []) {
        // Build your form here
    }
    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $data = $form->getData();
        // Checking if data exists as the form may be empty
        if($data and $data->isSomething()) {
            $view->vars['attr']['data-foo'] = $data->getBar();
            $view->vars['attr']['class'] .= $data->isBeautiful() ? ' makeBeautiful' : '';
        }
        
        // You may want to access the child views as well
        if($data and $data->hasBeautifulChild()) {
            $view->children['child']->vars['attr']['class'] .= ' makeBeautiful';
        }
    }
}

就是这样!Symfony在自动渲染表单之前调用buildView,并使用调整后的视图绘制整个内容。

出于

多种原因,我不会通过侦听器做一些直截了当的事情。

  1. 调试/编写复杂
  2. 您添加的每个侦听器都会减慢框架的速度
  3. 在大多数情况下,它矫枉过正

我能看到的最好的方法是通过控制器添加它。 由于您没有提供任何用例,因此为了说明目的,我将编造一个用例。

<小时 />

用例

表单中有一个字段需要禁用或启用,具体取决于它是处理新实体还是编辑实体。

<小时 />

控制器

public function doSomethingAction(Request $request) {
    
    // ......
    $edit = $this->areWeEditing(); // or whatever data youre trying to inject
    // options to send to the form
    $options = [
        'disable_field' => $edit, // <--- put your data in here.
    ];
    $entity = new Something();
    $form = $this->createForm( SomethingType::class, $entity, $options );
    $form->handleRequest( $request );
    if ( $form->isValid() ) {
        //... do something
    }
    // .....
}
<小时 />

某事类型.php

class SomethingType extends AbstractType {
    public function buildForm( FormBuilderInterface $builder, array $options ) {
        $builder->add( 'name', TextType::class, [
            'label'    => 'Name',
            'required' => true,
            'disabled' => $options[ 'disable_field' ],  // <---- use the $option here.
        ] )->add( 'email', EmailType::class, [
             'required' => true,
        ] );
    }
    public function configureOptions( OptionsResolver $resolver ) {
        $resolver->setDefaults( [
            'data_class'     => Something::class,  // whatever entity class youre mapping to
            'disabled_field' => false,  // you need to set this to tell symfony its an allowed option. Its name needs to match the incoming array key.  If the array key isnt present, this default will be used instead.
        ] );
    }
}

您传入的数据可以用作数组键和/或值,具体取决于您要实现的目标。