Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Separating business logic from PHP Doctrine 2

I use symfony 2.3 and php doctrine 2.

The program has the following models:

  • entity Order - a typical customer order
  • entity BadOrderEntry(fields: id, order - unidirectional one-to-one relationship with Order, createdAt)
  • factory BadOrderEntryFactory for creation entity BadOrderEntry
  • repository BadOrderEntryRepository for search methods of entity BadOrderEntry
  • manager BadOrderEntryManager for save/edit/delete methods of entity BadOrderEntry
  • 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?

like image 256
stalxed Avatar asked Jan 04 '15 14:01

stalxed


2 Answers

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

like image 59
Ziumin Avatar answered Nov 17 '22 12:11

Ziumin


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")
like image 39
lsroudi Avatar answered Nov 17 '22 12:11

lsroudi