I'm trying to create a ManyToMany relation beetwin services of a company. Each service had N parents services and N children services.
I looked at the doctrine documentation here : Many-To-Many, Self-referencing and I implemented it as followed :
Here is my service entity :
<?
namespace AppBundle\Entity;
class Service
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\ManyToMany(targetEntity="AppBundle\Entity\Service", mappedBy="enfants", cascade={"persist"})
*/
private $parents;
/**
* @ORM\ManyToMany(targetEntity="AppBundle\Entity\Service", inversedBy="parents")
* @ORM\JoinTable(name="app_services_hierarchy",
* joinColumns={@ORM\JoinColumn(name="parent_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="enfant_id", referencedColumnName="id")}
* )
*/
private $enfants;
public function __construct()
{
$this->enfants = new ArrayCollection();
$this->parents = new ArrayCollection();
}
public function getId(){
return $this->id;
}
//--------------------------------------------------Enfants
public function getEnfants(){
return $this->enfants;
}
public function setEnfants($enfant){
$this->enfants = $enfant;
}
public function addEnfant(Service $s){
$this->enfants[] = $s;
return $this;
}
public function removeEnfant(Service $s){
$this->enfants->removeElement($s);
}
//--------------------------------------------------Parents
public function getParents(){
return $this->parents;
}
public function setParents($parents){
$this->parents = $parents;
}
public function addParent(Service $s){
$this->parents[] = $s;
return $this;
}
public function removeParent(Service $s){
$this->parents->removeElement($s);
}
}
And here is my edit function( Controller.php) :
public function editAction(Request $request, $id)
{
$service = $this->getDoctrine()->getRepository(Service::class)->find($id);
$form = $this->createForm(ServiceType::class, $service);
$form ->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager ->persist($service);
dump($service);
$entityManager ->flush();
}
return $this->render('AppBundle:Service:edit.html.twig', array(
'form' => $form->createView(),
));
}
And the generated form looks like :
PROBLEM :
My problem is that the childrens are updated but not the parents. I can see the parents in the $service
variable when I dump()
it in my controler but the only ones updated in my database table (app_services_hierarchie) are the children.
The difference between $parents
and $enfants
in your code is that the service you are looking at is the Owning side in case of your $enfants
mapping, but not in the case of your $parents
mapping.
Doctrine will not store the $parents
unless you tell it to do so via cascade={"persist"}
.
/**
* @ORM\ManyToMany(targetEntity="AppBundle\Entity\Service", mappedBy="enfants", cascade={"persist"})
*/
This is basically the same anwer given in the post linked by @GregoireDucharme.
Edit: after some research, apparently this problem cannot be solved using cascade. According to the Doctrine documentation:
Doctrine will only check the owning side of an association for changes.
So what you have to do is tell your $parents
to also update the $children
property.
public function addParent(Service $s){
$this->parents[] = $s;
$s->addEnfant($this);
return $this;
}
public function removeParent(Service $s){
$this->parents->removeElement($s);
$s->removeEnfant($this);
}
In your form, make sure to specify the following:
->add('parents', 'collection', array(
'by_reference' => false,
//...
))
(I haven't spellchecked any of the code above, so tread carefully.)
If 'by_reference'
is set to true
, addParent
and removeParent
will not be called.
Credit goes to this blog post by Anny Filina.
It also states that you can remove the cascade
option from your $parents
property, but you probably should add cascade={"persist","remove"}
to your $enfants
property.