Home » Php » php – How can I apply a Model Transformer to collection items in Symfony2 forms?

php – How can I apply a Model Transformer to collection items in Symfony2 forms?

Posted by: admin July 12, 2020 Leave a comment

Questions:

Context is

I have a Symfony2 form field of type collection, where the collection items are of the entity type. I use Symfony 2.7.

Problem is

So far it works, but in this case, I have to apply a model data transformer to those collection items as described in the Symfony Cookbook. I use this code snippet:

<?php
$builder
    ->add(
        $builder
            ->create('items', 'collection', array(
                'type' => 'entity',
                'options' => array(
                    'class' => 'AppBundle:Item',
                    'property' => 'name',
                    'label' => 'Item',
                ),
                'label' => 'Items',
                'allow_add' => true,
                'allow_delete' => true,
                'delete_empty' => true,
                'prototype' => true,
                'required' => false,
            ))
            // $options['em'] is the entity manager
            ->addModelTransformer(new ItemToNumberTransformer($options['em']))
    )
;

Unfortunately, this applies the model transformer to the whole collection and not one Item item of it. As a workaround, I modified the transformer to also work with arrays of items/ids instead of only a single item/id, but this kinda looks like the wrong place to handle this. Seems to me as if this is more of a syntactical problem.

Question is

Does anybody know how to apply model transformers to each item of a collection? Or ca anybody confirm this is simply not possible due to a limitaion in the Symfony framework?

How to&Answers:

I’d say instead of creating a collection of entity types, you need to make your own type.

namespace AppBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\ORM\EntityManager;
/* Other use statements */

class ItemEntityType extends AbstractType
{
    /**
     * @var \Doctrine\ORM\EntityManager
     */
    protected $em;

    public function __construct(EntityManager $em)
    {
        $this->em = $em
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addModelTransformer(new ItemToNumberTransformer($this->em));
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'class'    => 'AppBundle:Item',
            'property' => 'name',
            'label'    => 'Item',
        ));
    }

    public function getParent()
    {
        return 'entity';
    }

    public function getName()
    {
        return 'appbundle_item_entity';
    }
}

Then define this as a service

app/config/services.yml

services:
    form.type.model.item_entity:
        class: AppBundle\Form\Type\ItemEntityType
        arguments: ["@doctrine.orm.entity_manager"]
        tags:
          - {name: form.type, alias: appbundle_item_entity}

And now you can specify this as the type for your collection

$builder
    ->create('items', 'collection', array(
        'type' => 'appbundle_item_entity'
        'label' => 'Items',
        'allow_add' => true,
        'allow_delete' => true,
        'delete_empty' => true,
        'prototype' => true,
        'required' => false,
    ))

Disclosure: I haven’t tested this but it should work.

Answer:

You should create a Type for your Item entity, apply the transformer to it, and then use it as a type for your collection.