Разделение проблем - где flush () в структуре MVC (контроллер против уровня обслуживания) - PullRequest
2 голосов
/ 25 ноября 2010

У меня есть приложение, в котором я использую PHP с Zend Framework и Doctrine2 в качестве ORM. Мой вопрос связан с тем, что контролер предпочтительно должен знать о базовой модели и уровне персистентности. В идеале я бы сказал, что это «ничто» сам - контроллер не должен ничего знать о том, как / когда сущности сохраняются. Однако я чувствую, что это не всегда лучшее решение (?).

Я пытался следовать руководству по проектированию «разделения интересов». Я сделал это путем создания сервисного уровня, который выполняет операции CRUD на моих моделях. Смотрите следующий пример:

public function testbuildAction()
{        
    // create section
    $sectionService = new \MyAPP\Model\Service\Acl\SectionService();        
    $sectionA       = $sectionService->createSection('SectionA-NAME');

    // create privilege with the above section
    $privilegeService   = new \MyAPP\Model\Service\Acl\PrivilegeService();
    $privilegeA = $privilegeService->createPrivilege(
                            $sectionA, 
                            \MyAPPFrameWork\Model\Acl\Privilege::PERMISSION_EDIT
                        );

    // create a role with the privilege above. A role must have at least one priv.
    $roleService = new \MyAPP\Model\Service\Acl\RoleService();
    $role        = $roleService->createRole('Role-NAME', $privilegeA); 

    // this loads a managed User object (managed by EntityManager)
    $user = $this->_helper->IdentityLoader(); 
    $user->addRole($role); // add the role to this user
    $userService = new \MyAPP\Model\Service\Core\UserService();        
    $userService->updateUser($user); // persist the updates.
}

Как вы можете видеть, Контроллер ничего не знает о постоянстве, но для получения этого результата мне нужно выполнять оба метода: persist () и flush () внутри каждого вызова методов createXXX () или updateXXX () уровня обслуживания. , Я бы предпочел сделать что-то вроде этого:

public function testbuildAction()
{        
    // create section
    $sectionService = new \MyAPP\Model\Service\Acl\SectionService();        
    $sectionA       = $sectionService->createSection('SectionA-NAME');

    // create privilege with the above section
    $privilegeService   = new \MyAPP\Model\Service\Acl\PrivilegeService();
    $privilegeA = $privilegeService->createPrivilege(
                            $sectionA, 
                            \MyAPPFrameWork\Model\Acl\Privilege::PERMISSION_EDIT
                        );

    // create a role with the privilege above. A role must have at least one priv.
    $roleService = new \MyAPP\Model\Service\Acl\RoleService();
    $role        = $roleService->createRole('Role-NAME', $privilegeA); 

    // this loads a managed User object (managed by EntityManager)
    $user = $this->_helper->IdentityLoader(); 
    $user->addRole($role); // add the role to this user

    // persist it all (all service-classes access the same entitymanager).
    $roleService->flush(); // everything is persisted
}

Но это приводит к сбою Doctrine2, так как он сохраняет объекты в базе данных в неправильном порядке - привилегии сохраняются до разделов (не знаю, могу ли я поручить Doctrine выполнить это упорядоченным образом ??). Привилегии получают неверный идентификатор для разделов, которые еще не сохранены.

В любом случае, большая проблема здесь заключается в том, должен ли я пытаться отложить сброс до тех пор, пока не будут созданы все объекты и не установлены отношения. Цель состоит в том, чтобы иметь ОДНУ транзакцию, которая выполняет всю запись в базу данных - которая, следовательно, должна быть инициирована контроллером (поскольку она единственная, кто знает, когда выполняется объект и построение отношений), тем самым «загрязняя» контроллер знаниями о персистентный слой?

Ответы [ 2 ]

0 голосов
/ 25 ноября 2010

Антоний предлагает что-то вроде подключения к __destruct () EntityManager.Однако, поскольку вы не знаете, изменились ли сущности в ралли, вы не хотите каждый раз вызывать сброс, даже если у вас есть сценарий только для чтения.

Поэтому уровень обслуживания не должен очищаться, а контроллерВы можете легко использовать Doctrine EventManager, чтобы каждое действие сервисного уровня отправляло событие "requireFlush":

$em->getEventManager()->dispatchEvent("requireFlush", new OnFlushEventArgs($em));

Вероятно, вам следует написать для этого некоторую удобную функцию.

Тогда вынапишите свое собственное событие Listener:

class DelayFlushListener
{
    private $requiresFlush = true;
    private $delayFlush = true;

    public function __construct($delayFlush = true) {
       $this->delayFlush = $delayFlush;
    }

    public function requireFlush(EventArgs $args) {
        $this->em = $args->getEntityManager();
        if ($this->delayFlush) {
            $this->requiresFlush = true;
        } else {
            $this->em->flush();
        }
    }

    public function flush() {
         if ($this->requiresFlush) {
             $this->em->flush();
         }
    }
}

Теперь зарегистрируйте этого слушателя в своем загрузчике:

 $listener = new DelayFlushListener();
 $em->getEventManager()->addEventListener(array("requireFlush"), $listener);

И внутри вашего контроллера вы можете запускать сброс при необходимости в обратном вызове postDispatch при каждомодин запрос).

 $listener->flush();
0 голосов
/ 25 ноября 2010

Я с готовностью признаю, что абсолютно ничего не знаю о Zend, PHP или Doctrine2 ...

НО, это звучит так, как будто вам нужна реализация шаблона Unit of Work . Я работаю с MVC, используя ASP.NET и C #, и у меня есть кое-что, что делает это.

Сказав, что мои контроллеры просто вызывают уровень обслуживания, и это зависит от уровня обслуживания, чтобы контролировать, когда транзакция фиксируется в постоянном хранилище (база данных в моем случае)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...