Symfony2 Forms – Entity field type

Featured Image

NOTE: Tested on Symfony2 Beta3. Might not work on later releases!

Symfony2 offers many prebuilt field types for using when creating forms. The one of them I found interesting is ‘EntityType’. There are just basic documentation about that.

I had one case recently that I had to use Entity field type for creating form, but except using it in simple way like documented I wanted to use it on little more advanced way.I have table in database called statuses that have basic filelds like:

id (int11), status_name (varchar 50), status_type (varchar 20). I defined 3 basic statuses that will be used for all items I have to describe their state:

draft, live, deleted

but, also I have more statuses defined that is for other purposes.

Because of that I defined these three types to have status type ‘basic‘ and I wanted to create form with select box, but only filter and show these three statuses and ignore others.

For that purpose, I used ‘Entity‘ field type, but I don’t want to pull all statuses from database, but only those three that have defined status_type: ‘basic‘.

Here is the code:

<?php
//Surgeworks/AdminBundle/Forms/ItemForm.php
namespace Surgeworks\AdminBundle\Forms;
 
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
 
class Itemform extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
 
        $builder->add('item_type','entity', array('class'=>'Surgeworks\AdminBundle\Entity\ItemType', 'property'=>'item_type_name', ));
        $builder->add('item_status','entity', array('class'=>'Surgeworks\AdminBundle\Entity\Status', 'property'=>'status_name',
            'query_builder' => function (\Surgeworks\AdminBundle\Entity\StatusRepository $repository)
            {return $repository->createQueryBuilder('s')->where('s.status_type = ?1')->setParameter(1, 'basic')->add('orderBy', 's.sort_order ASC');} ));
        $builder->add('item_name');
 
    }
    public function getDefaultOptions(array $options)
    {
        return array(
            'data_class' => 'Surgeworks\AdminBundle\Entity\Item',
        );
    }
}

when we ignore rest of the code here is how to use it inside function:

        $builder->add('item_status',
                      'entity',
                       array(
                             'class'=>'Surgeworks\AdminBundle\Entity\Status',
                             'property'=>'status_name',
                             'query_builder' => function (\Surgeworks\AdminBundle\Entity\StatusRepository $repository)
                             {
                                 return $repository->createQueryBuilder('s')
                                        ->where('s.status_type = ?1')
                                        ->setParameter(1, 'basic')
                                        ->add('orderBy', 's.sort_order ASC');
                             }
                            )
                      );

This is basic example how to use query_builder option with entity field type for creating forms.I hope you enjoyed the post and found it helpful.

Interested in hiring us?

Have a chat with us. You would be surprised how small changes can make your business even more successful.


26 comments

  1. Hi Guys,

    Is there a way i can add hyperlink in the muli select list ? If list is of type entity ?

    Thanks,
    Faisal Nasir

  2. Hello, I’m under Symphony 2.3, and I don’t managed to setup my query_builder option to retrieve a Query Builder from a custom Repository.

    I always get the following error :

    “Argument 1 passed to Utl\UserBundle\Form\UserType::Utl\UserBundle\Form\{closure}() must be an instance of Utl\UserBundle\Entity\GroupeRepository, instance of Doctrine\ORM\EntityRepository given”

    The form file (UserType.php)

    <?php
    namespace Utl\UserBundle\Form;
    
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolverInterface;
    use Utl\UserBundle\Entity\GroupeRepository;
    
    class UserType extends AbstractType {
        public function buildForm(FormBuilderInterface $builder, array $options) {
            $builder->add('groupes', 'entity', array(
    	    		'required' => false,
    	    		'class'    => 'UtlUserBundle:Groupe',
    	    		'property' => 'description',
    	    		'multiple' => true,
    	    		'query_builder' => function(GroupeRepository $r) {
    	    			return $r->getSelectList();
    	    		},
    	  	)
    	  );
        }
    
        public function setDefaultOptions(OptionsResolverInterface $resolver) {
            $resolver->setDefaults(array(
                'data_class' => 'Utl\UserBundle\Entity\User',
            	'validation_groups' => array('registration'),
            ));
        }
    
        public function getName() {
            return 'utl_userbundle_usertype';
        }
    }

    The custom repository (GroupeRepository) :

    <?php
    
    namespace Utl\UserBundle\Entity;
    
    use Doctrine\ORM\EntityRepository;
    
    /**
     * Groupe Repository
     */
    class GroupeRepository extends EntityRepository
    {
    
    	public function getSelectList()
    	{
    		$qb = $this->createQueryBuilder('e')
    			->where("e.description LIKE '%test%'");
    
    		return $qb;
    	}
    }

    For information, I managed to make it morking in an old test project under Symfony 2.2. So I don’t know if the problem comes from the version or not.

    Did you already had this issue ?

    Thanks a lot for your help.

  3. When i use in edit case so how may fill a listbox which are showing a selected value from database….

  4. Hi, this works great when we use one database. My issue is i am using two databases(admin_db and client_db), where the two tables in same database(client_db). When i use this it goes and check to admin_db(because it is the default database which is mentioned in config.yml). Is there is any way to do this.

  5. I get the error
    “Neither property “enfants” nor method “getEnfants()” nor method “isEnfants()” exists in class “PAGP\PortailBundle\Entity\Enfant”
    when i use the following code:

    class EnfantType extends AbstractType
    {
    public function buildForm(FormBuilder $builder, array $options)
    {
    $builder
    ->add(‘matricule’)
    ->add(‘enfants’,’entity’,
    array(
    ‘class’=>’PAGP\PortailBundle\Entity\Enfant’,
    ‘property’=>’nom’,
    ‘query_builder’ => function (\PAGP\PortailBundle\Entity\EnfantRepository $repository)
    {
    return $repository->createQueryBuilder(‘e’)
    ->add(‘orderBy’, ‘e.nom ASC’);
    },
    ‘multiple’ => ‘false’,
    ‘expanded’ => ‘false’
    )
    )

    I’m trying to select a single “Enfant” entity by using this entity field, but i don’t know where i should define the first parameter of the “$builder->add” call ($enfants in my case).
    This is certainly a stupid question but it blocks me ..
    Thank you for your help.
    Papynouche

  6. I’m unable to figure out how embed a form within another form type and have the embedded form know it is a child. For example, I have Property and it is linked to a Status through a Status property (StatusId in database).

    When I embed a StatusType form within my PropertyType form, I’m able to display a select list, but it will not preselect a value. Also, when I submit the form, it tries to change the Status record instead of the foreign key relation to Status in the Property record.

  7. Hi..Darko Goles,
    im getting problem at the time of form edition…. when im going to edit the form it displays the error like”expected argument “object”, “string” given…”…. this is because im going to generate drop down, like this….

    ->add(‘ProductGroupId’,'entity’, array( ‘class’ => ‘BuilderSupplierBundle:BuilderProductGroup’, ‘property’ => ‘ProductGroupName’ ))

    It is working exactly for new.html.twig but when im going to edit its showing the above error…. i hope u vl clear my doubt…. thanx

  8. I didn’t understood how to persist the entity selected in the list of the form. I have an entity user which call an entity name. In the name form i have a entity field. when i run the aplication i select in the list of names a name, but it can’t persist it.

    In fact i don’t understand what the entity field of the form returns to controller, because after tests, its not an name object, its not its id or its name (string).

    It seen that is not a entity configuration problem besause if a call a name object in the controller and i set it in the name field of the user name, it persist it without problems.

    Somebody can help me?

  9. I solved my own problem – I’m not sure how this happened, but I had ‘ORM:Entity’ instead of ‘ORM\Entity’ in my annotations…

  10. I’m trying to also override the ‘findAll()’ method in EntityRepository as described by @ibizaman, but I can’t get it to utilize my overridden findAll() in my own Repository class or in one called ‘EntityRepository’…where are these supposed to reside and how do you get them in the flow? I’m just using the standard annotation paths (@ORM\Entity:(RepositoryClass:….))

    Thanks for any insight…

  11. Hello, I’ve been trying to use your example, but I keep getting a “Entities passed to the choice field must be managed” error.

    Could you please post all your entities code that is used on this example?

  12. Hi Darko, nice post! I was wondering if it is possible and how to use a method from a repository class where I already have methods defined t query the databas?

  13. Thank you for your response Darko. I have two tables mapped with a many to many relationship. With doctrinte annotations I created a third table between them. Now I have to create a crud for the first table, in the create form I have to add n fields about the second table. Do I have add entities as fields need? or S2 have a way for do that? Sorry for my “engrish”

  14. @Lubert, Many to Many can be defined in two classes with automatically created table for relations in database (ManyToMany inside annotations) or you can define three classes that use OneToMany -> ManyToOne -> OneToMany relationships to define that kind of relations (Please look at Doctrine2 documentation). You anyway map entity properties to for adding fields to form, so choose property to display on form and make it same way like above. For example: you can have two entities ‘Item’ and ‘Tag’ with defined ManyToMany relationship and inside Item entity you will have property Tags that is Doctrine ArrayCollection and inside entity Tag you will have Tags property that is also Doctrine ArrayCollection. Use these properties to add fields inside form. :-)

  15. Don’t forget to add this in constructor if you have relation to collection of items:

    //...
        function __construct() {
    
            $this->localizations = new \Doctrine\Common\Collections\ArrayCollection();
    //...
  16. Joins are possible ONLY if you defined good relations between entities. For example:

    use Doctrine\Common\Collections\ArrayCollection;
    use Doctrine\ORM\Mapping as ORM;
    
    /**
     * @ORM\Entity(repositoryClass="Surgeworks\CoreBundle\Entity\TagRepository")
     * @ORM\Table(name="tags")
     */
    class Tag {
    
        /**
         * @ORM\OneToMany(targetEntity="TagLocalization", mappedBy="tag")
         */
        protected $localizations;

    And also you have to define relation in TagLocalization class:

    use Doctrine\Common\Collections\ArrayCollection;
    use Doctrine\ORM\Mapping as ORM;
    
    /**
     * @ORM\Entity(repositoryClass="Surgeworks\CoreBundle\Entity\TagLocalizationRepository")
     * @ORM\Table(name="tags_localization")
     */
    class TagLocalization {
    
        /**
         *@ORM\ManyToOne(targetEntity="Language" , cascade={"persist"})
         *@ORM\JoinColumn(name="language_symbol", referencedColumnName="language_symbol")
         */
        protected $language;
        /**
         * @ORM\ManyToOne(targetEntity="Tag", inversedBy="localizations")
         * @ORM\JoinColumn(name="tag_id", referencedColumnName="id")
         */
        protected $tag;

    Then you can use it with querybuilder:

    for example something like this:

    //...Inside TagRepository ...
    $qb = $this->createQueryBuilder('tag')
    ->join('tag.localizations tl');
    //...
  17. How about a join inside the query_builder??

    I have a lot of problems when I do that. What must I have to place in property??

  18. Great comment, @Ibizaman! Thank you for contributing content to whole community that read this. I hope that you will continue to solve symfony2 ‘misteries’ with me here… :-)

  19. Well, you know how it goes : when you post, you found the answer. ^^

    The context was that I had a nestedTree Entity (gedmo_doctrine_extensions) and a form using it with an EntityChoiceList.

    I wanted the select box to be sorted by array(‘root’=>’ASC’, ‘lft’=>’ASC’), and that the name would be prefixed with some hyphens depending on the level. Classic.

    So I created an EntityRepository and overrode the findAll() method :

    return $this->findBy(array(), array('root'=>'ASC', 'lft'=>'ASC'));

    and added a __toString() method to the Entity class :

    public function __toString() {return $this->getNestedPrint();}
    
    public function getNestedPrint()
    {
        $prefix = '';
        for ($i = 0; $i < $this->getLvl(); $i++) {
            $prefix .= '-';
        }
        return $prefix . ' ' . $this->getNom();
    }

    And it’s magic !

    For info :
    – The findAll() method is in vendor/doctrine/lib/Doctrine/ORM/EntityRepository.php
    It calls findBy() that calls loadAll() from the EntityPersister.
    – EntityChoiceList is in vendor/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/EntityChoiceList.php
    and you see in the load() method that it falls back on

    $this->em->getRepository($this->class)->findAll()

    Ibiz

  20. @Ibizaman, why don’t you make a custom function in EntityRepository class of each entity and define function with querybuilder there? Then just call it from form builder procedure… I could also here in code make this function inside StatusesRepository class with somename and call it here in the code, but for purposes of demonstration I left whole code here instead of adding it to RepositoryClass of Status. I hope this was helpful to you. :-)

  21. Sorry for double post.

    Is there a way to add this queryBuilder to ALL queries on an Entity ? Like adding a closure to the Entity class, creating another repo or something like that?

    Sorry I’m quite new to Symfony2.

    Thanks,
    Ibiz

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.