'Where should EntityManager::persist() and EntityManager::flush() be called
I'm developing a medium scale application using Symfony2 and Doctrine2. I'm trying to structure my code according to the SOLID principles as much as possible. Now here is the question:
For creating new Entities, I use Symfony Forms with proxy objects i.e: I don't bind the form directly to my Entity, but to some other class that will passed to some service which will take the needed action based on the received data, i.e: the proxy class serves as a DTO to that service which I will call the Handler. Now considering the Handler doesn't have a dependency on the EntityManager, where should I do calls to EntityManager::persist() and EntityManager::flush()? I am usually comfortable with putting flush in the controller but I'm not so sure about persist since the controller shouldn't assume anything about what the Handler does, and maybe Handler::handle (the method that the form data is passed to) does more than just persist a new Entity to the database. One Idea is to create some interfaces to encapsulate flush and persist and pass them around, which will act as wrappers around EntityManager::flush() and EntityManager::persist(), but I'm not so sure about it since EntityManager::flush() might create unwanted consequences. So Maybe I should just create an interface around persist.
So My question is where and how to make the call to persist and flush, in order to get the most Solid code? Or am I just overcomplicating things in my quest of best practices?
Solution 1:[1]
If you have a service that will handle tasks upon your entities, to me, the right way is to inject EntityManager into your service definition and do persist and flush operation inside it.
Another way to proceed, if you want to keep separate that logic, is to create an EventSubscriber and raise a custom event from your "entity service" when you're ready to do persist and flush operations
Solution 2:[2]
My 2 cents:
- about
flush, as it calls the DB, doing it like you already do when needed in your controllers sounds good to me. - about
presist, it should be called in yourHandlerwhen your entity is in a "ready to be flushed" state. APersisterinterface with only thepersistmethod as a dependency of yourHandlers, and aDoctrinePersisterimplementation injected in them looks OK.
Solution 3:[3]
Another option here - you can implement save() method in your entity repository class and make persistence there. Inject your entity repository as dependency into your Handler class.
Solution 4:[4]
If you don't want to couple your service and business logic to the EntityManager (good job), SOLID provides a perfect solution to separate it from your database logic.
//This class is responsible for business logic.
//It knows nothing about databases
abstract class CancelOrder
{
//If you need something from the database in your business logic,
//create a function that returns the object you want.
//This gets implemented in the inherited class
abstract protected function getOrderStatusCancelled();
public function cancel($order)
{
$order->setOrderStatus($this->getOrderStatusCancelled());
$order->setSubmittedTime(new DateTime());
//and other business logic not involving database operations
}
}
//This class is responsible for database logic. You can create a new class for any related CRUD operations.
class CancelOrderManager extends CancelOrder
{
public function __construct($entityManager, $orderStatusRepository)...
public function getOrderStatusCancelled()
{
return $this->orderStatusRepository->findByCode('cancelled');
}
public function cancel($order)
{
parent::cancel($order);
$this->entityManager->flush();
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Yassine Guedidi |
| Solution 3 | Serhii Smirnov |
| Solution 4 | Tek |
