具有选择形式的 Symfony DataTransformers

Symfony DataTransformers with choice forms

我做了一个 DataTransformer,它基本上不是提交的可用选择。

我的问题是我的变压器收到 ['INFO' => 'INFO', 'WARN' => 'WARN'][0 => 'INFO', 1 => 'WARN']。这通常发生在所有复选框都未选中时(也就是选择的模型数据是 ['INFO'、'WARN'])并且我选中任何复选框并提交表单时。

我正在使用 Symfony 2.6.6。

这是表格:

$choice = $builder->create('choice', 'choice', array(
    'choices' => array(
        'INFO' => 'INFO',
        'WARN' => 'WARN'
    ),
    'multiple' => true,
    'expanded' => true
);
$choice->addModelTransformer(new ReverseChoiceFieldTransformer($choice));
$builder->add($choice);

这是变压器:

class ReverseChoiceFieldTransformer implements DataTransformerInterface {
    private $choices;

    public function __construct(FormBuilderInterface $fbi) {
        $this->choices = $fbi->getOption('choices');
    }

    public function transform($value) {
        return $this->reverseTransform($value);
    }

    public function reverseTransform($value) {
        return $value === '' ? null : array_diff_key($this->choices, $value);
    }
}

我不太清楚您为什么要在这种情况下使用 DataTransformer。它们用于简化必须将输入的表单数据转换为其他内容的复杂表单。关于 Data Transformers,您可能想查看 Symfony 的官方 documentation

此外,您的 ReverseChoiceFieldTransformer class 未正确实施。 transform 方法旨在将 Object 转换为简单类型,如 stringint 等,稍后以 ViewData 的形式显示。相反,reverseTransform方法用于将数据从简单类型反向转换回相同的Object。您可以在下图中看到这一点(取自 Symfony 官方文档)

话虽如此,我相信您处理问题的方法可能是错误的。如果您在控制器或表单事件中执行此操作可能会更好。这是如何在控制器中获取数据的示例 class:

// MyFormType
class MyFormType extends AbstractType {

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('choice', 'choice', array(
            'choices' => array(
                'INFO' => 'Info label',
                'WARN' => 'Warn label',
                'CRIT' => 'Crit label',
            ),
            'multiple' => true,
            'expanded' => true,
            'required' => false,
            'mapped'   => false
        ))
        ->add('submit', 'submit', array(
            'attr' => array('class' => 'save'),
        ));;
    }

    public function getName() {
        return "my_form";
    }
}

// MyController
class MyController extends Controller {
    /**
     * @Route("/test")
     * @Template()
     */
    public function testAction(Request $request) {
        $form = $this->createForm(new MyFormType());
        $form->handleRequest($request);

        if($form->isValid())
        {
            // Get selected
            $selectedChoices = $form->get('choice')->getData();
            // outputs the checked values, i.e.
            // array (size=2)
            //    0 => string 'INFO' (length=4)
            //    1 => string 'WARN' (length=4)

            // All choices with their labels
            $allChoices = $form->get('choice')->getConfig()->getOption('choices');
            // outputs all choices, i.e.
            // array (size=3)
            //   'INFO' => string 'Info label' (length=10)
            //   'WARN' => string 'Warn label' (length=10)
            //   'CRIT' => string 'Crit label' (length=10)

            // All unchecked choices. Pay attention to the difference
            // in the arrays.
            $unchecked = array_diff(array_keys($allChoices), array_values($selectedChoices));

        }

        return array('form' => $form->createView());
    }
}


跟进评论:

你说对了一部分。 transform 方法接收用于构建表单的模型数据,而 reverseTransform 方法接收提交给表单的数据。尽管在您的情况下,两种情况下接收到的数据都是 array,但 array 本身包含不同的数据结构:

  1. transform 将收到:

    array (
        'INFO' => 'Info label',
        'WARN' => 'Warn label',
        'CRIT' => 'Crit label'
    )
    
  2. reverseTransform 将收到选中的选项,即:

    array (
        0 => 'INFO',
        1 => 'CRIT'
    )
    


虽然,如果这可以用 FormEvents 完成,我会去寻找控制器或研究,下面的代码应该可以完成工作:

class ReverseChoiceFieldTransformer implements DataTransformerInterface {
    private $choices;

    public function __construct(FormBuilderInterface $fbi) {
        $this->choices = $fbi->getOption('choices');
    }

    /**
     * The received data from the FormBuilder will be passed "AS IS"
     *
     * @param mixed $value
     * @return mixed
     */
    public function transform($value) {
        return $value;
    }

    /**
     * The received data from the submitted from will be modified. Doing
     * $form->get('choice')->getData() in the controller will return only
     * the unselected choices.
     *
     * @param mixed $value
     * @return null|array
     */
    public function reverseTransform($value) {
        return $value === '' ? null : array_diff(array_keys($this->choices),array_values($value));
    }
}