Doctrine manyToMany return PersistentCollection instead of ArrayCollection

Thomas Piard picture Thomas Piard · Jul 16, 2016 · Viewed 16.7k times · Source

I'm working with Symfony 3.1 and Doctrine 2.5.

I setup a manyToMany relationship as I always do :

manyToMany:
        placeServices:
            targetEntity: Acme\MyBundle\Entity\PlaceService
            joinTable:
                name: place_place_service
                joinColumns:
                    place_id:
                        referencedColumnName: id
                inverseJoinColumns:
                    place_service_id:
                        referencedColumnName: id

And add methods to my Entity

    protected $placeServices;

    ...

    public function __construct()
    {
        $this->placeServices = new ArrayCollection();
    }

    ...

    /**
     * @return ArrayCollection
     */
    public function getPlaceServices(): ArrayCollection
    {
        return $this->placeServices;
    }

    /**
     * @param PlaceServiceInterface $placeService
     * @return PlaceInterface
     */
    public function addPlaceService(PlaceServiceInterface $placeService): PlaceInterface
    {
        if(!$this->placeServices->contains($placeService)) {
            $this->placeServices->add($placeService);
        }

        return $this;
    }

    /**
     * @param PlaceServiceInterface $placeService
     * @return PlaceInterface
     */
    public function removePlaceService(PlaceServiceInterface $placeService): PlaceInterface
    {
        if($this->placeServices->contains($placeService)) {
            $this->placeServices->removeElement($placeService);
        }

        return $this;
    }

The thing is, when I load my entity, doctrine put a PersistentCollection in the $this->placeServices property. This does not sound like a big problem, except that when I build a form to connect those two entities (a simple multiple checkboxes with symfony form type), when $form->handleRequest() is triggered, Doctrine try to inject the new data in my entity, and throw an error if get/add/remove method are not using ArrayCollection.

I can force my getter/add/remove methods to transforme the PersistentCollection to ArrayCollection (using unwrap method) but then the relations made are not persisted.

I've found a workaround, if I set fetch="EAGER" on the relation the property is initialized with ArrayCollection, and the relation are persisted. But i'm not sure it's a good solution.

Thanks :)

Answer

Arthur Khusnullin picture Arthur Khusnullin · Jul 17, 2016

Just use Doctrine\Common\Collections\Collection interface instead of ArrayCollection. ArrayCollection and PersistentCollection implement this interface.

Doctrine uses PersistentCollection for lazy loading entities. You are right, using EAGER is not always a good solution - it can cause perfomance issues.