I'm trying to get some simple CRUD done with doctrine 2 but when it's time to update a record with one property set as an array collection I don't seem to get removeElement() to work as it's supposed to. I even tried doing it in this ridiculously ugly way:
foreach($entity->getCountries() as $c) {
$entity->getCountries()->removeElement($c);
$this->em->persist($entity);
$this->em->flush();
}
and it didn't work... Anyone knows how to handle this? I've asked for a solution to this in many different forms and haven't got a good response so far... seems there's lack of good examples of Doctrine 2 CRUD handling. I'll post more code at request.
Edit
//in user entity
/**
*
* @param \Doctring\Common\Collections\Collection $property
* @OneToMany(targetEntity="Countries",mappedBy="user", cascade={"persist", "remove"})
*/
private $countries;
//in countries entity
/**
*
* @var User
* @ManyToOne(targetEntity="User", inversedBy="id")
* @JoinColumns({
* @JoinColumn(name="user_id", referencedColumnName="id")
* })
*/
private $user;
I do something similar in a project with Events which have participants not unlike your User/Country relationship. I will just lay out the process and you can see if there's anything you are doing differently.
On the Participant
entity
/**
* @ManyToOne(targetEntity="Event", inversedBy="participants", fetch="LAZY")
* @JoinColumn(name="event_id", referencedColumnName="id", nullable="TRUE")
* @var Event
*/
protected $event;
On the Event
entity:
/**
* @OneToMany(targetEntity="Participant", mappedBy="event")
* @var \Doctrine\Common\Collections\ArrayCollection
*/
protected $participants;
Also in Event#__constructor
I initialize like this:
$this->participants = new \Doctrine\Common\Collections\ArrayCollection();
Here is how I update an event:
public function update(Event $event, Event $changes)
{
// Remove participants
$removed = array();
foreach($event->participants as $participant)
{
if(!$changes->isAttending($participant->person))
{
$removed[] = $participant;
}
}
foreach($removed as $participant)
{
$event->removeParticipant($participant);
$this->em->remove($participant);
}
// Add new participants
foreach($changes->participants as $participant)
{
if(!$event->isAttending($participant->person))
{
$event->addParticipant($participant);
$this->em->perist($participant);
}
}
$event->copyFrom($changes);
$event->setUpdated();
$this->em->flush();
}
The methods on the Event
entity are:
public function removeParticipant(Participant $participant)
{
$this->participants->removeElement($participant);
$participant->unsetEvent();
}
public function addParticipant(Participant $participant)
{
$participant->setEvent($this);
$this->participants[] = $participant;
}
The methods on the Participant
entity are:
public function setEvent(Event $event)
{
$this->event = $event;
}
public function unsetEvent()
{
$this->event = null;
}
UPDATE: isAttending method
/**
* Checks if the given person is a
* participant of the event
*
* @param Person $person
* @return boolean
*/
public function isAttending(Person $person)
{
foreach($this->participants as $participant)
{
if($participant->person->id == $person->id)
return true;
}
return false;
}
New answer
In your countries entity, should you not have:
@ManyToOne(targetEntity="User", inversedBy="countries")
instead of inversedBy="id"?
Initial answer
You need to set the countries field in your entity as remove cascade. For example, on a bidirectional one to many relationship:
class Entity
{
/**
*
* @OneToMany(targetEntity="Country", mappedBy="entity", cascade={"remove"})
*/
private $countries;
}
This way, when saving your entity, doctrine will also save changes in collections attached to your entity (such as countries). Otherwise you have to explicitly remove the countries you want to remove before flushing, e.g.
$this->em()->remove($aCountry);
This is also valid for persist, merge and detach operations. More information here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With