Laurenz Glück
Laurenz Glück

Reputation: 1762

Symfony Forms: CollectionType - Don't transform null to empty array

We are using Symfony Forms for our API to validate request data. At the moment we are facing a problem with the CollectionType which is converting the supplied value null to an empty array [].

As it is important for me to differentiate between the user suppling null or an empty array I would like to disable this behavior.

I already tried to set the 'empty_data' to null - unfortunately without success.

This is how the configuration of my field looks like:

$builder->add(
    'subjects',
    Type\CollectionType::class,
    [
        'entry_type'    => Type\IntegerType::class,
        'entry_options' => [
            'label'     => 'subjects',
            'required'  => true,
            'empty_data' => null,
        ],
        'required'   => false,
        'allow_add'  => true,
        'empty_data' => null,
    ]
);

The form get's handled like this:

$data = $apiRequest->getData();

$form = $this->formFactory->create($formType, $data, ['csrf_protection' => false, 'allow_extra_fields' => true]);
$form->submit($data);

$formData = $form->getData();

The current behavior is:

Input $data => { 'subjects' => null }

Output $formData => { 'subjects' => [] }

My desired behavior would be:

Input $data => { 'subjects' => null }

Output $formData => { 'subjects' => null }

Upvotes: 1

Views: 1501

Answers (1)

Laurenz Glück
Laurenz Glück

Reputation: 1762

After several tries I finally found a solution by creating a From Type Extension in combination with a Data Transformer

By creating this form type extension I'm able to extend the default configuration of the CollectionType FormType. This way I can set a custom build ModelTransformer to handle my desired behavior.

This is my Form Type Extension:

class KeepNullFormTypeExtension extends AbstractTypeExtension
{

    public static function getExtendedTypes(): iterable
    {
        return [CollectionType::class];
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        parent::buildForm($builder, $options);

        $builder->addModelTransformer(new KeepNullDataTransformer());
    }

}

This one needs to be registered with the 'form.type_extension' tag in your service.yml:

PrivateApiBundle\Form\Extensions\KeepNullFormTypeExtension:
    class: PrivateApiBundle\Form\Extensions\KeepNullFormTypeExtension
    tags: ['form.type_extension']

Please note that you still use the CollectionType in your FormType and not the KeepNullFormTypeExtension as Symfony takes care about the extending...

In the KeepNullFormTypeExtension you can see that I set a custom model transformer with addModelTransformer which is called KeepNullDataTransformer

The KeepNullDataTransformer is responsible for keeping the input null as the output value - it looks like this:

class KeepNullDataTransformer implements DataTransformerInterface
{
    protected $initialInputValue = 'unset';

    /**
     * {@inheritdoc}
     */
    public function transform($data)
    {
        $this->initialInputValue = $data;
        return $data;
    }

    /**
     * {@inheritdoc}
     */
    public function reverseTransform($data)
    {
        return ($this->initialInputValue === null) ? null : $data;
    }
}

And that's it - this way a supplied input of the type null will stay as null.

More details about this can be found in the linked Symfony documentation:

Upvotes: 1

Related Questions