Join table is not updated in ManyToMany association in doctrine 2
I have tow entities Slaplans
and Slaholidays
and a join table slaplans_slaholidays
.
After creating two Slaholidays
objects, I persist them both, add them to the Slaplans
and flush. The problem is that only the slaplans
and slaholidays
tables are updated, but the join table isn't.
Slaplans
Entity :
<?php
namespace ZC\Entity;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Slaplans
*
* @Table(name="slaplans")
* @Entity(repositoryClass="Repositories\Slaplans")
*/
class Slaplans
{
/*
* @ManyToMany(targetEntity="Slaholidays",inversedBy="plans", cascade={"ALL"})
* @JoinTable(name="slaplans_slaholidays",
* joinColumns={@JoinColumn(name="slaplanid" ,referencedColumnName="slaplanid")},
* inverseJoinColumns={@JoinColumn(name="slaholidayid" ,referencedColumnName="slaholidayid")})
* }
*/
private $holidays;
public function __construct()
{
$this->holidays = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getHolidays() {
return $this->holidays;
}
public function setHolidays($holidays)
{
$this->holidays=$holidays;
}
/*public function addHoliday($holiday) {
$this->holidays[]=$holiday;
}*/
}
Slaholidays
Entity:
<?php
namespace ZC\Entity;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Slaholidays
*
* @Table(name="slaholidays")
* @Entity(repositoryClass="Repositories\Slaholidays")
*/
class Slaholidays
{
/**
* @var integer $slaholidayid
*
* @Column(name="slaholidayid", type="integer", nullable=false)
* @Id
* @GeneratedValue(strategy="IDENTITY")
*/
开发者_如何学编程 private $slaholidayid;
/*
* @ManyToMany(targetEntity="Slaplans",mappedBy="holidays", cascade={"ALL"})
*/
private $plans;
/*public function getPlans(){
return $this->plans;
}*/
}
Code to persist the entities:
$allholidays=array();
$holiday=$this->_em->getRepository('ZC\Entity\Slaholidays')->find($value);
$holiday=new ZC\Entity\Slaholidays();
//..sets holiday fields here
$this->_em->persist($holiday);
$allholidays[]=$holiday;
$slaplan->setHolidays($allholidays);
foreach ($slaplan->getHolidays() as $value) {
$this->_em->persist($value);
}
$this->_em->persist($slaplan);
$this->_em->flush();
The are two issues in your code:
The first one: you are persisting each Slaholiday
twice: first with
$this->_em->persist($holiday);
and second with
foreach ($slaplan->getHolidays() as $value) {
$this->_em->persist($value);
}
There is no problem actually, as they are not actually persisted in the db until flush
are called, but anyway, you don't need that foreach
.
The reason why your join table is not updated is in $slaplan->setHolidays
method. You are initializing $slaplan->holidays
with ArrayCollection
(which is right) and in setHolidays
you set it to the input parameter (which is $allholidays
Array
, and this is not right).
So, the correct way to do that is to use add
method of the ArrayCollection
public function setHolidays($holidays)
{
//$this->holidays->clear(); //clears the collection, uncomment if you need it
foreach ($holidays as $holiday){
$this->holidays->add($holiday);
}
}
OR
public function addHolidays(ZC\Entity\Slaholiday $holiday)
{
$this->holidays->add($holiday);
}
public function clearHolidays(){
$this->holidays->clear();
}
//..and in the working script...//
//..the rest of the script
$this->_em->persist($holiday);
//$slaplan->clearHolidays(); //uncomment if you need your collection cleaned
$slaplan->addHOliday($holiday);
Although Doctrine checks the owning side of an association for things that need to be persisted, it's always important to keep both sides of the association in sync.
My advise is to have get, add and remove (no set) methods at both sides, that look like this:
class Slaplans
{
public function getHolidays()
{
return $this->holidays->toArray();
}
public function addHoliday(Slaholiday $holiday)
{
if (!$this->holidays->contains($holiday)) {
$this->holidays->add($holiday);
$holiday->addPlan($this);
}
return $this;
}
public function removeHoliday(Slaholiday $holiday)
{
if ($this->holidays->contains($holiday)) {
$this->holidays->removeElement($holiday);
$holiday->removePlan($this);
}
return $this;
}
}
Do the same in Slaplan
.
Now when you add a Slaholiday to a Slaplan, that Slaplan will also be added to the Slaholiday automatically. The same goes for removing.
So now you can do something like this:
$plan = $em->find('Slaplan', 1);
$holiday = new Slaholiday();
// set data on $holiday
// no need to persist $holiday, because you have a cascade={"ALL"} all on the association
$plan->addHoliday($holiday);
// no need to persist $plan, because it's managed by the entitymanager (unless you don't use change tracking policy "DEFERRED_IMPLICIT" (which is used by default))
$em->flush();
PS: Don't use cascade
on both sides of the association. This will make things slower than necessary, and in some cases can lead to errors. If you create a Slaplan first, then add Slaholidays to it, keep the cascade
in Slaplan and remove it from Slaholiday.
精彩评论