开发者

Join table is not updated in ManyToMany association in doctrine 2

开发者 https://www.devze.com 2023-04-03 21:38 出处:网络
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 Slaplansand flush. The problem is that

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号