Reputation: 7311
Using Symfony 2.5, the Doctrine documentation says that the preUpdate
event listener is the most restrictive. You can't just make a change to the entity, instead you have to use the special function:
$entity->setNewValue($fieldName, $value)
However, I have an entity $view
which has an one to many associated entity $routing
, and I have a special setter in my $view
object that adds multiple routes:
class View {
protected $routing;
public function getRouting() {
return $this->routing;
}
public function addRouting(\Gutensite\CmsBundle\Entity\Routing\Routing $routing)
{
// Maintain Relationship
$routing->setView($this);
$this->routing[] = $routing;
return $this;
}
}
I do not have a setRouting()
function in that entity, because instead I have an addRouting() because it is a multiple oneToMany
association.
So... in my preUpdate event listener, how am I supposed to create a new "default" route, and add it to view, since the $entity->setNewValue()
function won't exactly work on a collection, and evidently I can't just call $view->addRouting($route)
(I tried that but nothing gets persisted to the database).
Note: since 2.4 we should use the LifeCycleEventArgs instead of PreUpdateEventArgs (evidently).
public function preUpdate(LifeCycleEventArgs $eventArgs) {
$entity = $eventArgs->getEntity();
$em = $eventArgs->getEntityManager();
if($entity instanceof View) {
if(!$entity->getRouting() || !$entity->getRouting()[0]) {
$routing = new Routing();
// simplified code to Set Friendly URL
$routing->setFurl('Unknown-Friendly-URL-'.$entity->getId());
// Set Default Values for Route
$routing->setFlagPrimary(1);
$routing->setTime(time());
$routing->setTimeMod(time());
$routing->setStatus(1);
$routing->setView($entity);
// Add the route to the View Entity
$entity->addRouting($routing);
// Recalculate Entity Changes (not working)
$uow = $em->getUnitOfWork();
// This doesn't work whether I compute the changesets for $entity (which is $view)
// or if I calculate changes for $entity->getRouting() (which is the new route I'm adding).
$classMetadata = $em->getClassMetadata(get_class($entity));
// Recomputer Entire Change Set Because it's a New Record (I'm not sure this is necessary or not)
$uow->computeChangeSet($classMetadata, $entity);
// This doesn't work either
$uow->recomputeSingleEntityChangeSet($classMetadata, $entity);
}
}
}
Upvotes: 1
Views: 979
Reputation: 7311
I ended up just implementing this in an onFlush
event. The preUpdate
is too restrictive for my use case. So that begs the question: why use preUpdate when onFlush is more flexible?
preUpdate
Insert a simple value to an entity, e.g.
$eventArgs->setNewValue('myField', $value);
You also have access to the entities old and new values (which is super useful for easily comparing WHICH fields changed, something I have needed before).
This event has a powerful feature however, it is executed with a PreUpdateEventArgs instance, which contains a reference to the computed change-set of this entity. This means you have access to all the fields that have changed for this entity with their old and new value. --reference
NOTE: If you do use preUpdate, remember that you must use the setNewValue()
syntax, not the normal setter for your entity. There is no need to recalculate the change set, and you also should NOT call persist:
Any calls to EntityManager#persist() or EntityManager#remove(), even in combination with the UnitOfWork API are strongly discouraged and don’t work as expected outside the flush operation.
preUpdate
Changes to associations of the updated entity are never allowed in this event, since Doctrine cannot guarantee to correctly handle referential integrity at this point of the flush operation. --reference
So if you need to update a child or the parent value (e.g. update the modification time of the parent), you can't do that within preUpdate because the setNewValue
method is called on the eventArgs for the current entity, not on a specific entity.
Or if you need to add a collection, as in my question, you can't call custom setters like addRouting
(for the reasons I mentioned). I suspect that means@RomaKliuchko's suggestion would not work.
Please add other advantages and disadvantages.
Upvotes: 4
Reputation: 499
Have you tried to implement setRouting(ArrayCollection $routing)
method, which will override existing collection and do like this:
$routings = $entity->getRouting();
// add entities to routing collection if needed
$eventArgs->setNewValue('routing', $routings);
Upvotes: 0