I have a strange problem using a many-to-many relation in Symfony (with Doctrine), I've never had before in symfony projects with many-to-many relations and I can't find any difference to the other projects.
I have the two entitys Product and Tag and a many-to-many relation to each other. Unfortunately, if I try to add a product to a tag or vice versa, the error
Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "TestBundle\Entity\Product#$tags", got "TestBundle\Entity\Tag" instead.
appears. The code used to add a tag to a product:
$tag1 = $em->getRepository('TestBundle:Tag')->findOneBy(array(
'tag' => "Bla"
));
$tag1->addProduct($product);
$em->persist($tag1);
$em->persist($product);
$em->flush();
Of course, the variable $tag1 and $product both contain a valid entity. The YAML file for the many-to-many relations (I cut away irrelevant parts):
TestBundle\Entity\Tag:
type: entity
table: tags
repositoryClass: TestBundle\Repository\TagRepository
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
tag:
type: string
length: 255
unique: true
manyToMany:
products:
targetEntity: Product
mappedBy: tags
lifecycleCallbacks: { }
Product:
TestBundle\Entity\Product:
type: entity
table: products
repositoryClass: TestBundle\Repository\ProductRepository
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
name:
type: string
length: 255
unique: true
manyToOne:
manufacturer:
targetEntity: Manufacturer
inversedBy: products
joinColumn:
name: manufacturer_id
referencedColumnName: id
onDelete: CASCADE
manyToMany:
tags:
targetEntity: Product
inversedBy: products
joinTable:
name: tags2products
joinColumns:
tag_id:
referencedColumnName: id
inverseJoinColumns:
product_id:
referencedColumnName: id
lifecycleCallbacks: { }
The setter and getter functions also don't contain any special tricks: The Tag.php entity file contains:
/**
* Constructor
*/
public function __construct()
{
$this->product = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add product
*
* @param \TestBundle\Entity\Product $product
*
* @return Tag
*/
public function addProduct(\TestBundle\Entity\Product $product)
{
$product->addTag($this);
$this->product[] = $product;
return $this;
}
public function removeProduct(\TestBundle\Entity\Product $product)
{
$this->product->removeElement($product);
}
/**
* Get products
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
While the Product.php contains:
/**
* Add tag
*
* @param \TestBundle\Entity\Tag $tag
*
* @return Product
*/
public function addTag(Tag $tag)
{
$this->tags->add($tag);
//$this->tags[] = $tag;
return $this;
}
/**
* Remove tag
*
* @param \TestBundle\Entity\Tag $webpage
*/
public function removeTag(Tag $tag)
{
$this->tags->removeElement($tag) ;
}
/**
* Get webpages
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getTags()
{
return $this->tags;
}
I also tried to add a $this->tags = new ArrayCollection(); to the constructor of the product, but it didnt change anything.
Also, there is no problem adding, reading and persisting tags to products. The error is thrown as soon as I call $em->flush().
Does anybody know why my Product entity expects a array collection? I never told him to expect one! Thank you very much in advance!
The error is telling you that the property "#tags" of the entity TestBundle\Entity\Product that you are trying to flush, contains an object of type TestBundle\Entity\Tag instead of a collection of this object. Doctrine expects this collection/array because the metadata for that property states that TestBundle\Entity\Product is in a many-yo-many with TestBundle\Entity\Tag and the relation is done via the property "#tags". This should happen if:
//You did this
$this->tags = $tag;
//instead of what you actually did which is correct
$this->tags->add($tag);
//Or
$this->tags[] = $tag;
But the code that you posted here should not produce that exception.
Are you sure there is no other place where an accessor method is called that changes the tags property of TestBundle\Entity\Product? Something like and event listener?