Symfony 2 Transformer on entity form type

Lukas Kral

I'm trying to create a new form type in Symfony 2. It is based on entity type, it uses select2 on frontend and I need the user to be able to select existing entity or create the new one.

My idea was to send entity's id and let it to be converted by the default entity type if user select existing entity or send something like "_new:entered text" if user enter new value. Then this string should be converted to the new form entity by my own model transformer, which should look something like this:

<?php
namespace Acme\MainBundle\Form\DataTransformer;

use Symfony\Component\Form\DataTransformerInterface;

class EmptyEntityTransformer
implements DataTransformerInterface
{
    private $entityName;
    public function __construct($entityName)
    {
        $this->entityName = $entityName;
    }
    public function transform($val)
    {
        return $val;
    }
    public function reverseTransform($val)
    {
        $ret = $val;
        if (substr($val, 0, 5) == '_new:') {
            $param = substr($val, 5);
            $ret = new $this->entityName($param);
        }
        return $ret;
    }
}

Unfortunately, the transformer is only called when existing entity is selected. When I enter a new value, the string is sent in the request but transformer's reverseTransform method is not called at all.

I'm new to Symfony so I don't even know if this approach is correct. Do you have any Idea how to solve this?

edit: My form type code is:

<?php

namespace Acme\MainBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Acme\MainBundle\Form\DataTransformer\EmptyEntityTransformer;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

class Select2EntityType
extends AbstractType
{
    protected $router;
    public function __construct(Router $router)
    {
        $this->router = $router;
    }
    /**
     * {@inheritdoc}
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        parent::setDefaultOptions($resolver);
        $resolver->setDefaults(array(
            'placeholder' => null,
            'path' => false,
            'pathParams' => null,
            'allowNew' => false,
            'newClass' => false,
        ));
    }

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

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

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        if ($options['newClass']) {
            $transformer = new EmptyEntityTransformer($options['newClass']);
            $builder->addModelTransformer($transformer);
        }
    }

    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $field = $view->vars['name'];
        $parentData = $form->getParent()->getData();
        $opts = array();
        if (null !== $parentData) {
            $accessor = PropertyAccess::createPropertyAccessor();
            $val = $accessor->getValue($parentData, $field);
            if (is_object($val)) {
                $getter = 'get' . ucfirst($options['property']);
                $opts['selectedLabel'] = $val->$getter();
            }
            elseif ($choices = $options['choices']) {
                if (is_array($choices) && array_key_exists($val, $choices)) {
                    $opts['selectedLabel'] = $choices[$val];
                }
            }
        }

        $jsOpts = array('placeholder');

        foreach ($jsOpts as $jsOpt) {
            if (!empty($options[$jsOpt])) {
                $opts[$jsOpt] = $options[$jsOpt];
            }
        }
        $view->vars['allowNew'] = !empty($options['allowNew']);
        $opts['allowClear'] = !$options['required'];
        if ($options['path']) {
            $ajax = array();
            if (!$options['path']) {
                throw new \RuntimeException('You must define path option to use ajax');
            }
            $ajax['url'] = $this->router->generate($options['path'], array_merge($options['pathParams'], array(
                'fieldName' => $options['property'],
            )));
            $ajax['quietMillis'] = 250;
            $opts['ajax'] = $ajax;
        }
        $view->vars['options'] = $opts;
    }
}

and then I create this form type:

class EditType
extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('masterProject', 's2_entity', array(
                'label' => 'Label',
                'class' => 'MyBundle:MyEntity',
                'property' => 'name',
                'path' => 'my_route',
                'pathParams' => array('entityName' => 'name'),
                'allowNew' => true,
                'newClass' => '\\...\\MyEntity',
            ))

...

Thanks for your suggestions

Lukas Kral

I think I found an answer however I'm not really sure if this is the correct solution. When I tried to understand how EntityType works I noticed that it uses EntityChoiceList to retrive list of available options and in this class there is a getChoicesForValues method which is called when ids are transformed to entities. So I implemented my own ChoiceList which adds my own class to the end of the returned array:

<?php
namespace Acme\MainBundle\Form\ChoiceList;

use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;


class EmptyEntityChoiceList
extends EntityChoiceList
{
    private $newClassName = null;
    public function __construct(ObjectManager $manager, $class, $labelPath = null, EntityLoaderInterface $entityLoader = null, $entities = null,  array $preferredEntities = array(), $groupPath = null, PropertyAccessorInterface $propertyAccessor = null, $newClassName = null)
    {
        parent::__construct($manager, $class, $labelPath, $entityLoader, $entities, $preferredEntities, $groupPath, $propertyAccessor);
        $this->newClassName = $newClassName;
    }
    public function getChoicesForValues(array $values)
    {
        $ret = parent::getChoicesForValues($values);
        foreach ($values as $value) {
            if (is_string($value) && substr($value, 0, 5) == '_new:') {
                $val = substr($value, 5);
                if ($this->newClassName) {
                    $val = new $this->newClassName($val);
                }
                $ret[] = $val;
            }
        }
        return $ret;
    }
}

Registering this ChoiceList to the form type is a bit complicated because the class name of original choice list is hardcoded in the DoctrineType which EntityType extends but it is not difficult to understand how to do it if you have a look into this class.

The reason why DataTransformer is not called probably is that EntityType is capable to return array of results and transform is applied to every item of this collection. If the result array is empty, there is obviously no item to call transformer on.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

symfony2 form create new type combining collection and entity

From Dev

Additional properties to entity Field Type in a form in Symfony2

From Dev

Symfony2 entity form type not saving many to many

From Dev

Symfony2 - How to validate autocomplete entity form type?

From Dev

Symfony2 form type entity add extra option

From Dev

Adding a default value along with entity in form type of Symfony2

From Dev

Symfony2 data transformer string to entity (reverseTransform)

From Dev

symfony entity form type with related data

From Dev

Symfony creating choice from entity in form type

From Dev

Symfony 2.8 form entity type custom property

From Dev

Symfony form type entity data transformerm

From Dev

Symfony form for a general entity with a type and different options

From Dev

Setting default value on form value transformer in Symfony2

From Dev

symfony2 - How to create a form field of type "entity" without values

From Dev

Validate Symfony 2 form with array and entity

From Dev

Entity Not Found error in symfony2 Form

From Dev

Symfony 2 Entity form fields name

From Dev

Symfony 2 - Form for each instance of entity

From Dev

Dynamic form (switch entity) symfony2

From Dev

Symfony2 form with field not in entity

From Dev

Symfony 2 form property selection from entity

From Dev

Symfony 2 Create a Entity in another Entiy Form

From Dev

Symfony 2 form property selection from entity

From Dev

Symfony 2.7 Form Entity type render multiple properties in form

From Dev

Symfony 2.7 Form Entity type render multiple properties in form

From Dev

symfony2 rendering of a form as form type

From Dev

field array type in entity for form choice type field symfony

From Dev

Entity Field type hidden in Symfony2

From Dev

Symfony2 FormType entity field type

Related Related

  1. 1

    symfony2 form create new type combining collection and entity

  2. 2

    Additional properties to entity Field Type in a form in Symfony2

  3. 3

    Symfony2 entity form type not saving many to many

  4. 4

    Symfony2 - How to validate autocomplete entity form type?

  5. 5

    Symfony2 form type entity add extra option

  6. 6

    Adding a default value along with entity in form type of Symfony2

  7. 7

    Symfony2 data transformer string to entity (reverseTransform)

  8. 8

    symfony entity form type with related data

  9. 9

    Symfony creating choice from entity in form type

  10. 10

    Symfony 2.8 form entity type custom property

  11. 11

    Symfony form type entity data transformerm

  12. 12

    Symfony form for a general entity with a type and different options

  13. 13

    Setting default value on form value transformer in Symfony2

  14. 14

    symfony2 - How to create a form field of type "entity" without values

  15. 15

    Validate Symfony 2 form with array and entity

  16. 16

    Entity Not Found error in symfony2 Form

  17. 17

    Symfony 2 Entity form fields name

  18. 18

    Symfony 2 - Form for each instance of entity

  19. 19

    Dynamic form (switch entity) symfony2

  20. 20

    Symfony2 form with field not in entity

  21. 21

    Symfony 2 form property selection from entity

  22. 22

    Symfony 2 Create a Entity in another Entiy Form

  23. 23

    Symfony 2 form property selection from entity

  24. 24

    Symfony 2.7 Form Entity type render multiple properties in form

  25. 25

    Symfony 2.7 Form Entity type render multiple properties in form

  26. 26

    symfony2 rendering of a form as form type

  27. 27

    field array type in entity for form choice type field symfony

  28. 28

    Entity Field type hidden in Symfony2

  29. 29

    Symfony2 FormType entity field type

HotTag

Archive