Dennis de Best
Dennis de Best

Reputation: 1108

ManyToMany new value must be an array or an instance of \Traversable, "NULL" given

I have a ManyToMany relation in my Symfony 4.2.6 application and I would like for it to be possible to have this to be null.

So my first entity SpecialOffers is as follows :

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\SpecialOfferRepository")
 */
class SpecialOffer
{
    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\Neighbourhood", inversedBy="specialOffers")
     */
     private $neighbourhood;

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

    /**
     * @return Collection|Neighbourhood[]
     */
    public function getNeighbourhood(): Collection
    {
        return $this->neighbourhood;
    }

    public function addNeighbourhood(Neighbourhood $neighbourhood): self
    {
        if (!$this->neighbourhood->contains($neighbourhood)) {
            $this->neighbourhood[] = $neighbourhood;
        }

        return $this;
    }

    public function removeNeighbourhood(Neighbourhood $neighbourhood): self
    {
        if ($this->neighbourhood->contains($neighbourhood)) {
            $this->neighbourhood->removeElement($neighbourhood);
       }

       return $this;
   }
}

It is related to the neighbourhood class :

/**
 * @ORM\Entity(repositoryClass="App\Repository\NeighbourhoodRepository")
 */
class Neighbourhood implements ResourceInterface
{
    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\SpecialOffer", mappedBy="neighbourhood")
     */
    private $specialOffers;

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

        /**
     * @return Collection|SpecialOffer[]
     */
    public function getSpecialOffers(): Collection
    {
        return $this->specialOffers;
    }

    public function addSpecialOffer(SpecialOffer $specialOffer): self
    {
        if (!$this->specialOffers->contains($specialOffer)) {
            $this->specialOffers[] = $specialOffer;
            $specialOffer->addNeighbourhood($this);
        }

        return $this;
    }

    public function removeSpecialOffer(SpecialOffer $specialOffer): self
    {
         if ($this->specialOffers->contains($specialOffer)) {
            $this->specialOffers->removeElement($specialOffer);
            $specialOffer->removeNeighbourhood($this);
        }

        return $this;
    }
}

And finally the form is

class SpecialOfferType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add(
                'neighbourhood',
                EntityType::class,
                [
                    'class' => Neighbourhood::class,
                    'label' => 'form.neighbourhood.label',
                    'translation_domain' => 'Default',
                    'required' => false,
                    'placeholder' => 'form.neighbourhood.all'
                ]
             );
        }
   }

But when I don't select a specific neighbourhood for the Special offer in my form I get the following error : Could not determine access type for property "neighbourhood" in class "App\Entity\SpecialOffer": The property "neighbourhood" in class "App\Entity\SpecialOffer" can be defined with the methods "addNeighbourhood()", "removeNeighbourhood()" but the new value must be an array or an instance of \Traversable, "NULL" given.

Is there anyway I can make it so that my special offer either contains and array of neighbourhoods or just null ?

I feel like I'm overlooking something really obvious, any help would be greatly appreciated

Upvotes: 2

Views: 1426

Answers (2)

Heyden7611
Heyden7611

Reputation: 174

Test =>

$builder
            ->add(
                'neighbourhood',
                EntityType::class,
                [
                    'class' => Neighbourhood::class,
                    'label' => 'form.neighbourhood.label',
                    'translation_domain' => 'Default',
                    'required' => false,
                    'multiple' => true,
                    'placeholder' => 'form.neighbourhood.all'
                ]
             );

Upvotes: 5

Jakumi
Jakumi

Reputation: 8374

Since your fields on the entities are both many-to-many, thus expecting an array (or similar) and the form field is of EntityType, which will return one Entity of the expected type or null, I feel like there is some form of asymmetry.

I would consider using the CollectionType from the start or at least setting the multiple option on the form to true, so that the return value is an array.

Another option would be to add a DataTransformer to the form field, which turns null into an empty array and one entity into an array of one entity, and vice-versa.

Upvotes: 1

Related Questions