Symfony — Отделение бизнес-логики от PHP Doctrine 2

Я использую Symfony 2.3 и PHP доктрины 2.

Программа имеет следующие модели:

  • Заказ предприятия — типичный заказ клиента
  • Entity BadOrderEntry (поля: id, order — однонаправленное взаимно-однозначное отношение с Order, creationAt)
  • фабрика BadOrderEntryFactory для создания сущности BadOrderEntry
  • репозиторий BadOrderEntryRepository для методов поиска сущности BadOrderEntry
  • менеджер BadOrderEntryManager для сохранения / редактирования / удаления методов сущности BadOrderEntry
  • AND MAIN CLASS BadOrderList — список плохих заказов, код этого класса:

    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);
    }
    }
    

Мне очень нравится дизайн этого класса. Я много думал об этом.
Все замечательно. НО! Есть одна проблема: операции в методах add и remove должны выполняться в транзакциях.

Код транзакции в PHP Docrine 2 выглядит так:

<?php
$em->getConnection()->beginTransaction();
try {
//... do some work
$em->getConnection()->commit();
} catch (Exception $e) {
$em->getConnection()->rollback();
throw $e;
}

Но как я могу вызвать этот код внутри BadOrderList?

Я потратил много времени и удалил в зависимости от базы данных (и соответственно PHP Doctrine 2), и снова его создать?
Теперь зависимость скрыта в классах BadOrderEntryRepository и BadOrderEntryManager.

Как скрыть зависимость от механизма транзакций в классе BadOrderList?

7

Решение

После нашего обсуждения у меня есть ответ на ваш вопрос.
Вопрос действительно не «Как скрыть зависимость от механизма транзакций в классе BadOrderList?», но Как отделить модель от персистентного слоя? (Doctrine2 в этом конкретном случае).

Я попытался проиллюстрировать мое предложение с помощью некоторого кода

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.

Так что, если мы говорим о структуре каталогов, вся ваша модель может быть перемещена из комплекта и использована где угодно. Ваша структура Bundle будет выглядеть так:

BadEntryBundle
|
+ Entity
| |
| --- BadOrderEntryEntity.php
|
+ ORM
| |
| --- ORMBadOrderEntryManager.php

И тогда вы просто добавите ORMBadOrderEntryManager в свой BadOrderEntryList

4

Другие решения

Вы можете преобразовать свой класс как сервис и называть его как хотите после внедрения контейнера вашего сервиса в ваш класс.
Вы можете найти больше информации здесь о внедрение зависимости :

$injectedContainerOfService->get("id_of_your_service")
1