I use symfony 2.3 and php doctrine 2.
The program has the following models:
AND MAIN CLASS BadOrderList - list of bad orders, code of this class:
private $factory;
private $repository;
private $manager;
public function __construct(
BadOrderEntryFactory $f,
BadOrderEntryRepository $r,
BadOrderEntryManager $m
) {
$this->factory = $f;
$this->repository = $r;
$this->manager = $m;
}
public function has(Order $order)
{
return $this->repository->existsByOrder($order);
}
public function add(Order $order)
{
if (! $this->has($order)) {
$entry = $this->factory->create($order);
$this->manager->save($entry);
}
}
public function remove(Order $order)
{
$entry = $this->repository->findOneByOrder($order);
if ($entry !== null) {
$this->manager->delete($entry);
}
}
I really like the design of this class. I thought a lot about it. Everything is wonderful. BUT! There is one problem: operations in methods add and remove must be performed in the transactions.
Transaction code in PHP Docrine 2 looks like this:
<?php
$em->getConnection()->beginTransaction();
try {
//... do some work
$em->getConnection()->commit();
} catch (Exception $e) {
$em->getConnection()->rollback();
throw $e;
}
But how can I call this code inside BadOrderList?
I spent a lot of time and removed depending on the database(and correspondingly PHP Doctrine 2), and again to create it? Is now dependence is hidden in classes BadOrderEntryRepository and BadOrderEntryManager.
How to hide the dependence on the transaction mechanism in class BadOrderList?
After our discussion I have an answer to your question. The question is really not "How to hide the dependence on the transaction mechanism in class BadOrderList?", but How to decouple a model from a persistence layer? (Doctrine2 in that particular case).
I tried illustrate my suggestion with some code
class BadOrderEntry
// Bad - is too bad word to describe an order here. Why is it bad? Is it Declined? Cancelled?
{
private $order;
// some code
}
class BadOrderEntryFactory
{
// If there is not to much processing to build BadOrderEntry better use factory method like BadOrderEntry::fromOrder($order);
}
class BadOrderEntryRepository
{
// here is some read model
}
class BadOrderEntryManager
// ITS a part of our model and shouldn't be coupled to ORM
{
public function save(BadEntry $be)
{
// some model events, model actions
$this->doSave($be); // here we should hide our storage manipulation
}
protected function doSave($be) // it can be abstract, but may contain some basic storage actions
{
}
// similar code for delete/remove and other model code
}
class ORMBadOrderEntryManager extends BadOrderEntryManager
// IT'S NOT the part of your model. There is no business logic. There is only persistent logic and transaction manipulation
{
protected $entityManager;
// some constructor to inject doctrine entitymanager
protected doSave($be)
{
$em = $this->entityManager;
$em->getConnection()->beginTransaction(); // suspend auto-commit
try {
$em->persist($be);
$em->flush();
$em->getConnection()->commit();
} catch (Exception $e) {
$em->getConnection()->rollback();
throw $e;
}
}
}
// You can also implement ODMBadOrderEntryManager, MemcacheBadOrderEntryManager etc.
So if we talk about directory structure, all your model can be moved out of bundle and used anywhere. Your Bundle structure will be like:
BadEntryBundle
|
+ Entity
| |
| --- BadOrderEntryEntity.php
|
+ ORM
| |
| --- ORMBadOrderEntryManager.php
And then you'll just inject ORMBadOrderEntryManager to your BadOrderEntryList
You can transform your class as a service and call it whatever you want after injecting your service container inside your class. you can find more information here about dependency injection :
$injectedContainerOfService->get("id_of_your_service")
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