I have a Symfony project which uses Doctrine2 as the main ORM. The project has multiple entities that are related to each other. I have the following entities:
Order - order in the shop
Product - products in orders
Addon - addons for products
The objects are linked in the following way:
Order
|- Product
|- Product
|- Addon
|- Addon
The Order and Product entity both have a "Status"-field. The status of Products is related to the status of the Order. The status of Products and Orders can change in a few ways; and they need to react to each other.
I was thinking of using Doctrine EntityListeners to listen to changes in the Entities. So when the status of an Order-entity happens, the Listener can update the status of the Products.
Detecting changes in the Order entity works great. But when I want to change the status of its Products, I will need to flush the changes in Doctrine. When I flush the changes of the Products from within the OrderListener, Symfony crashes.
TL;DR: What is the best way to detect changes in an Entity's field, and update other entities based on these changes.
Thanks in advance,
Wouter
services.yml
order_listener:
class: AppBundle\EntityListener\OrderListener
arguments: [ '@service_container' ]
tags:
- {name: doctrine.event_listener, event: preUpdate}
- {name: doctrine.event_listener, event: postFlush}
product_listener:
class: AppBundle\EntityListener\ProductListener
arguments: [ '@service_container' ]
tags:
- {name: doctrine.event_listener, event: preUpdate}
OrderListener
namespace AppBundle\EntityListener;
use AppBundle\Entity\Product;
use AppBundle\Entity\Order;
use Doctrine\ORM\Event\PostFlushEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Symfony\Component\DependencyInjection\Container;
class OrderListener {
/**
* @var Container
*/
protected $container;
/**
* @var PreUpdateEventArgs[]
*/
protected $changed = [];
/**
* OrderListener constructor.
*
* @param Container $container
*/
public function __construct(Container $container) {
$this->container = $container;
}
public function preUpdate(PreUpdateEventArgs $preUpdateEventArgs){
if(!$preUpdateEventArgs->getEntity() instanceof Order)
return;
$this->changed[] = $preUpdateEventArgs;
}
public function postFlush(PostFlushEventArgs $postFlushEventArgs){
foreach($this->changed as $preUpdateEventArgs){
/** @var Product $product */
$em = $this->container->get('doctrine')->getEntityManager();
foreach($preUpdateEventArgs->getEntity()->getProducts() as $product) {
$product->setStatus(Product::STATUS_COMPLETED);
$em->persist($product);
}
$em->flush();
}
$this->changed = [];
}
}
ProductListener
namespace AppBundle\EntityListener;
use AppBundle\Entity\Product;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Symfony\Component\DependencyInjection\Container;
class ProductListener {
/**
* @var Container
*/
protected $container;
/**
* OrderListener constructor.
*
* @param Container $container
*/
public function __construct(Container $container) {
$this->container = $container;
}
public function preUpdate(PreUpdateEventArgs $eventArgs){
if(!$eventArgs->getEntity() instanceof Product)
return;
dump($eventArgs);
}
}
When flushing an Order Symfony dies with this error:
[Symfony\Component\Process\Exception\RuntimeException]
The process has been signaled with signal "11".