Question

J'ai une application où j'utilise PHP avec Zend Framework et Doctrine2 comme ORM. Ma question est liée à combien le contrôleur doit de préférence savoir sur la couche de modèle et de la persistance sous-jacente. Idéalement, je dirais que c'est moi-même « rien » - le contrôleur ne doit pas rien savoir comment / quand les entités sont conservées. Cependant, je pense que ce n'est pas toujours la meilleure solution (?).

J'ai essayé de suivre la «séparation des préoccupations régle de conception. Je l'ai fait en créant une couche de service qui effectue des opérations sur CRUD mes modèles. Voir l'exemple suivant:

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.
}

Comme vous pouvez le voir le contrôleur ne sait rien au sujet de la persistance, mais pour obtenir ce résultat j'ai besoin d'effectuer à la fois persist () et flush () à l'intérieur de chaque appel à des méthodes createXXX () ou updateXXX () de la couche de service . Je préfère avoir fait quelque chose comme ceci:

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
}

Mais cela provoque Doctrine2 à l'échec car il ne persiste les objets de la base de données dans le mauvais ordre - les privilèges sont conservés avant les articles (je sais pas si je peux demander à la doctrine d'exécuter cette manière ordonnée ??). Les privilèges obtient ID erroné pour les sections qui ne persistent pas encore.

Quoi qu'il en soit, la grande question est ici de savoir si je devrais essayer de reporter le rinçage jusqu'à ce que tous les objets ont été créés et les relations ont été définies. Le but étant d'avoir une transaction qui fait tout écrit à la base de données - qui doit par conséquent être déclenché par le contrôleur (car il est le seul à savoir lors de la construction de l'objet et la relation est fait), ce qui « contaminer » le contrôleur avec la connaissance du couche de persistance?

Était-ce utile?

La solution

Antony suggère quelque chose comme crochetage dans __destruct () du EntityManager. Cependant, puisque vous ne savez pas si les entités raelly changé, vous ne voulez pas appeler flush chaque fois, même si vous avez seulement un scénario de lecture seule.

Par conséquent, la couche de service ne doit pas rincer mais le contrôleur, vous pouvez facilement utiliser la doctrine EventManager d'avoir chaque action de la couche de service Transmet un événement « requireFlush »:

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

Vous devriez probablement écrire une sorte de fonction de commodité pour cela.

Ensuite, vous écrivez votre propre événement 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();
         }
    }
}

Inscrivez-vous maintenant que l'auditeur dans votre fichier de démarrage:

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

Et à l'intérieur de votre contrôleur, vous pouvez déclencher la chasse d'eau de retard si nécessaire dans un rappel postDispatch à chaque demande).

 $listener->flush();

Autres conseils

Je reconnais volontiers que je ne sais absolument rien sur Zend, PHP ou Doctrine2 ...

Mais, cela ne sonne comme vous avez besoin d'une application de la Unité de travail modèle. Je travaille avec MVC en utilisant ASP.NET et C # et quelque chose qui le fait.

Après avoir dit que mes contrôleurs appellent simplement la couche de service et il est à la couche de service de contrôle lorsque la transaction est validée au magasin de persistance (base de données dans mon cas)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top