symfony : can't we have a hidden entity field?

Sébastien picture Sébastien · Nov 23, 2014 · Viewed 27.1k times · Source

I am rendering a form with an entity field in symfony.

It works well when i choose a regular entity field.

$builder
    ->add('parent','entity',array(
            'class' => 'AppBundle:FoodAnalytics\Recipe',
            'attr' => array(
                'class' => 'hidden'
            )
        ))

It throws the following error when I choose ->add('parent','hidden') :

The form's view data is expected to be of type scalar, array or an instance of \ArrayAccess, but is an instance of class AppBundle\Entity\FoodAnalytics\Recipe. You can avoid this error by setting the "data_class" option to "AppBundle\Entity\FoodAnalytics\Recipe" or by adding a view transformer that transforms an instance of class AppBundle\Entity\FoodAnalytics\Recipe to scalar, array or an instance of \ArrayAccess. 500 Internal Server Error - LogicException

Can't we have hidden entity fields ?? Why not? Am I obliged to put another hidden field to retrieve the entity id?

EDIT :

Basically, what I'm trying to do is to hydrate the form before displaying it but prevent the user to change one of its fields (the parent here). This is because I need to pass the Id as a parameter and I can't do it in the form action url.

Answer

Leo Bedrosian picture Leo Bedrosian · Nov 23, 2014

I think you are simply confused about the field types and what they each represent.

An entity field is a type of choice field. Choice fields are meant to contain values selectable by a user in a form. When this form is rendered, Symfony will generate a list of possible choices based on the underlying class of the entity field, and the value of each choice in the list is the id of the respective entity. Once the form is submitted, Symfony will hydrate an object for you representing the selected entity. The entity field is typically used for rendering entity associations (like for example a list of roles you can select to assign to a user).

If you are simply trying to create a placeholder for an ID field of an entity, then you would use the hidden input. But this only works if the form class you are creating represents an entity (ie the form's data_class refers to an entity you have defined). The ID field will then properly map to the ID of an entity of the type defined by the form's data_class.

EDIT: One solution to your particular situation described below would be to create a new field type (let's call it EntityHidden) that extends the hidden field type but handles data transformation for converting to/from an entity/id. In this way, your form will contain the entity ID as a hidden field, but the application will have access to the entity itself once the form is submitted. The conversion, of course, is performed by the data transformer.

Here is an example of such an implementation, for posterity:

namespace My\Bundle\Form\Extension\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\DataTransformerInterface;

/**
 * Entity hidden custom type class definition
 */
class EntityHiddenType extends AbstractType
{
    /**
     * @var DataTransformerInterface $transformer
     */
     private $transformer;

    /**
     * Constructor
     *
     * @param DataTransformerInterface $transformer
     */
    public function __construct(DataTransformerInterface $transformer)
    {
        $this->transformer = $transformer;
    }

    /**
     * @inheritDoc
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // attach the specified model transformer for this entity list field
        // this will convert data between object and string formats
        $builder->addModelTransformer($this->transformer);
    }

    /**
     * @inheritDoc
     */
    public function getParent()
    {
        return 'hidden';
    }

    /**
     * @inheritDoc
     */
    public function getName()
    {
        return 'entityhidden';
    }
}

Just note that in the form type class, all you have to do is assign your hidden entity to its corresponding form field property (within the form model/data class) and Symfony will generate the hidden input HTML properly with the ID of the entity as its value. Hope that helps.