Symfony DataTransformers with choice forms

对着背影说爱祢 提交于 2019-12-25 02:55:09

问题


I made a DataTransformer that is made to basically NOT the available choices with the submitted ones.

My issue is that my transformer receives either ['INFO' => 'INFO', 'WARN' => 'WARN'] or [0 => 'INFO', 1 => 'WARN']. This usually happens when all the checkboxes are unchecked (AKA the model data of the choice is ['INFO', 'WARN']) and that I check any and submit the form.

I am using Symfony 2.6.6.

Here is the form :

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

And here is the transformer :

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);
    }
}

回答1:


I'm not quite sure why you'd want to use a DataTransformer in this case. They're used to simplify complex forms where the entered form data has to be transformed into something else. You might want to checkout the official documentation of Symfony, regarding Data Transformers.

Furthermore, your ReverseChoiceFieldTransformer class is not correctly implemented. The transform method is meant to transform an Object to a simple type like string,int,etc which is later displayed in the form as ViewData. On the contrary, the reverseTransform method is used to reverse transform the data from simple type back into the same Object. You can see this in the illustration below (taken from Symfony official docs)

Having said that, I believe your approach to the problem might be wrong. It would probably better if you do this in the controller or in a form event. Here's an example of how to get the data in your controller 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());
    }
}


Follow up on comments:

You're partly correct. The transform method receives the model data which was used to build the form, while the reverseTransform method receives the data submitted to the form. Although in your case the received data in both cases is an array, the array itself contains different data structure:

  1. transform will receive:

    array (
        'INFO' => 'Info label',
        'WARN' => 'Warn label',
        'CRIT' => 'Crit label'
    )
    
  2. reverseTransform will receive the checked choices i.e.:

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


Although, I would go for a controller or research if this could be done with FormEvents, the following code should do the work:

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));
    }
}


来源:https://stackoverflow.com/questions/29656657/symfony-datatransformers-with-choice-forms

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!