在Symfony 2中验证动态加载的选项

19
我在表单中有一个名为 *sub_choice* 的选择字段类型,它的选项将根据父级选择字段 *parent_choice* 中所选值的不同通过 AJAX 来动态加载。加载选项运行良好,但是当提交时验证 sub_choice 值时,出现了问题。由于提交的值不在构建 sub_choice 字段时的选项中,因此会出现“该值无效”的验证错误。那么我是否有一种方法可以正确验证 sub_choice 字段的提交值呢?下面是构建我的表单的代码。我正在使用 Symfony 2.1。
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('parent_choice', 'entity', array(
                    'label' => 'Parent Choice',
                    'class' => 'Acme\TestBundle\Entity\ParentChoice'
    ));

    $builder->add('sub_choice', 'choice', array(
                    'label' => 'Sub Choice',
                    'choices' => array(),
                    'virtual' => true
    ));
}

你有没有在这方面有什么运气?我也卡在类似的问题上了。 - Robbo_UK
一个更近期的类似问题链接到了这个问题,其中一个答案看起来非常不错,涉及使用PRE_BIND事件来解决有效选项列表的问题:https://dev59.com/TmMl5IYBdhLWcg3wr4rn - frumious
这是一个版本,可以接受任何值: http://stackoverflow.com/questions/28245027/symfony-2-choice-ajax-validation-fix - Developer
5个回答

25

要完成这个技巧,您需要在提交表单之前覆盖sub_choice字段:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    ...

    $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
        $parentChoice = $event->getData();
        $subChoices = $this->getValidChoicesFor($parentChoice);

        $event->getForm()->add('sub_choice', 'choice', [
            'label'   => 'Sub Choice',
            'choices' => $subChoices,
        ]);
    });
}

2
我认为这个答案最完整、最直接地给出了问题想要的:根据另一个选择的值设置选项,而不必混乱地提供所有选项以通过验证,然后可能重新设置选项。这让我觉得这是“正确的Symfony 2”解决方案。 - frumious

3

这个可以接受任何值

 $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
    $data = $event->getData();
    if(is_array($data['tags']))$data=array_flip($data['tags']);
    else $data = array();
    $event->getForm()->add('tags', 'tag', [
        'label'   => 'Sub Choice',
        'choices' => $data,
        'mapped'=>false,
        'required'=>false,
        'multiple'=>true,
    ]);
});

这不是一个“正确验证提交的值”的解决方案(正如OP所要求的)。它只是绕过了验证。在Eugene Leonovich的回答之后几个月才添加这个答案的意义何在,而他的回答已经更好地回答了OP的问题? - fbastien

0

为了方便未来的读者,我在这里提供一种替代方法,因为我不得不进行大量调查才能让我的表单正常工作。以下是详细步骤:

  1. 通过jquery向下拉菜单添加“新建”选项
  2. 如果选择“新建”,则显示新的表单字段“自定义选项”
  3. 提交表单
  4. 验证数据
  5. 保存到数据库

twig的jquery代码:

$(function(){
    $(document).ready(function() {
        $("[name*='[custom_option]']").parent().parent().hide(); // hide on load

        $("[name*='[options]']").append('<option value="new">New</option>'); // add "New" option
        $("[name*='[options]']").trigger("chosen:updated");
    });

    $("[name*='[options]']").change(function() {
        var companyGroup = $("[name*='[options]']").val();

        if (companyGroup == 'new') { // when new option is selected display text box to enter custom option
            $("[name*='[custom_option]']").parent().parent().show();
        } else {
            $("[name*='[custom_option]']").parent().parent().hide();
        }
    });
});

// Here's my Symfony 2.6 form code:
    ->add('options', 'entity', [
    'class'         => 'Acme\TestBundle\Entity\Options',
    'property'      => 'display',
    'empty_value'   => 'Select an Option',
    'mapped'        => true,
    'property_path' => 'options.optionGroup',
    'required' => true,
])
->add('custom_option', 'text', [
    'required' => false,
    'mapped'   => false,
])

为了处理表单数据,我们需要使用 PRE_SUBMIT 表单事件。

    $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
    $data = $event->getData();
    $form = $event->getForm();
    if (isset($data['options']) && $data['options'] === 'new') {
        $customOption = $data['custom_option'];

        // todo: handle this better on your own
        if (empty($customOption)) {
            $form->addError(new FormError('Please provide a custom option'));
            return;
        }

        // Check for a duplicate option
        $matches = $this->doctrine->getRepository('Acme\TestBundle\Entity\Options')->matchByName([$customOption]);
        if (count($matches) > 0) {
            $form->addError(new FormError('Duplicate option found'));
            return;
        }

        // More validation can be added here

        // Creates new option in DB
        $newOption = $this->optionsService->createOption($customOption); // return object after persist and flush in service
        $data['options'] = $newOption->getOptionId();
        $event->setData($data);
    }
});

如果你们有任何问题或疑虑,请告诉我。我知道这可能不是最好的解决方案,但它有效。谢谢!


-3

你无法构建sub_choice验证,因为在配置其验证器时,您不知道哪些值是有效的(值取决于parent_choice的值)。

您可以做的是在控制器中创建新的YourFormType()之前将parent_choice解析为实体。然后,您可以通过表单构造函数提供所有可能的sub_choice值 - new YourFormType($subChoice)。

在YourFormType中,您必须添加像这样的__construct方法:

/**
 * @var array
 */
protected $subChoice = array();

public function __construct(array $subChoice)
{
    $this->subChoice = $subChoice;
}

并在表单中使用提供的值添加:

$builder->add('sub_choice', 'choice', array(
                'label' => 'Sub Choice',
                'choices' => $this->subChoice,
                'virtual' => true
));

-6
假设对于子选项,您有ID,对吗? 创建一个具有一定数量值的空数组,并将其作为选择。 $indexedArray = []; for ($i=0; $i<999; $i++){ $indexedArray[$i]= ''; } 然后是'choices' => $indexedArray, :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接