Reputation: 281
I`m a little bit confused with how to update related collection using doctrine events.
One entity is related with another through ManyToMany. For better understanding u can see examples below (User with Jobs). The goal is: when User`s status is going to be changed then his jobs should also be changed from event listener.
I have already tried preUpdate
event:
public function preUpdate(PreUpdateEventArgs $args)
{
$entity = $args->getObject();
foreach ($entity->getJobs() as $job) {
$entity->getJobs()->removeElement($job);
}
}
And also onFlush
event:
public function onFlush(OnFlushEventArgs $eventArgs)
{
$em = $eventArgs->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityUpdates() as $entity) {
foreach ($entity->getJobs() as $job) {
$entity->getJobs()->removeElement($job);
}
$em->persist($entity);
$uow->recomputeSingleEntityChangeSet(
$em->getClassMetadata(Job::class),
$entity
);
}
}
But in case when only status was changed outside the listener - user still have the same jobs(jobs were not changed from listener):
$user->setStatus('some-other-status');
But in case when collection was changed outside the listener - it works(jobs were changed from listener):
$collection = ... // some new collection of jobs
$user->setJobs(collection);
Any help is welcome.
Here are code snippets of entities:
User.php
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @ORM\Entity()
* @ORM\Table()
*/
class User
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue
*/
protected $id;
/**
* @ORM\Column(type="string")
*/
protected $status;
/**
* @ORM\ManyToMany(targetEntity="Job")
* @ORM\JoinTable(name="_user_x_job",
* joinColumns={@ORM\JoinColumn(name="user", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={@ORM\JoinColumn(name="job", referencedColumnName="id", onDelete="CASCADE")}
* )
*/
protected $jobs;
public function __construct()
{
$this->jobs = new ArrayCollection();
}
...
}
Job.php:
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @ORM\Entity()
* @ORM\Table()
*/
class Job
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue
*/
protected $id;
/**
* @ORM\Column(type="string")
*/
protected $name;
...
}
Upvotes: 2
Views: 1388
Reputation: 1943
I would recommend to avoid Doctrine Event Listeners at all for this task. Instead the whole behavior should be modeled directly in the User Entity: The setState() method can also hold the behavior to remove the job(s).
Then look out for removeOrphans=true
doctrine mapping option:
Basically it works by removing the Job
from the collection AND set its User
to null. Doctrine will detect your orphaned jobs from the unit of work and automatically deletes them from the database when calling persist($user); flush();
Upvotes: 3