2

ZendフレームワークとDoctrine2をORMとして使用するPHPを使用するアプリケーションがあります。私の質問は、コントローラーが基礎となるモデルと永続層についてどれだけ知っておくべきかに関するものです。理想的には、これは自分自身では「何もない」と言えます。コントローラーは、エンティティが永続化される方法とタイミングについて何も知らないはずです。しかし、これが常に最善の解決策であるとは限らないと思います(?)。

私は「関心の分離」の設計ガイドラインに従おうとしました。これは、モデルで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.
}

ご覧のとおり、コントローラーは永続性について何も知りませんが、この結果を得るには、サービスレイヤーのcreateXXX()またはupdateXXX()メソッドへのすべての呼び出し内でpersist()とflush()の両方を実行する必要があります。私はむしろこのようなことをしたかった:

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に順序付けられた方法でこれを実行するように指示できる場合はdunno ??)。特権は、まだ永続化されていないセクションのIDを誤って取得します。

とにかく、ここでの大きな問題は、すべてのオブジェクトが作成され、リレーションが設定されるまで、フラッシュを延期する必要があるかどうかです。目標は、データベースへのすべての書き込みを行う1つのトランザクションを持つことです。これは、コントローラーによってトリガーされる必要があります(これは、オブジェクトとリレーションの構築が行われることを知っている唯一のトランザクションであるため)。これにより、コントローラーを「汚染」します。永続性レイヤー?

4

2 に答える 2

0

Antony は、EntityManager の __destruct() にフックするようなものを提案しています。ただし、エンティティが頻繁に変更されたかどうかがわからないため、読み取り専用のシナリオしかない場合でも、毎回フラッシュを呼び出したくありません。

したがって、サービス層はフラッシュするべきではありませんが、コントローラは、Doctrine EventManager を使用して、各サービス層アクションがイベント "requireFlush" をディスパッチするように簡単に設定できます。

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

おそらく、これにはある種の便利な関数を作成する必要があります。

次に、独自のイベント リスナーを作成します。

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();
于 2010-11-25T11:28:58.330 に答える
0

Zend、PHP、またはDoctrine2についてまったく何も知らないことをすぐに認めます...

しかし、これはUnit of Workパターンの実装が必要なように思えます。私は ASP.NET と C# を使用して MVC を使用しており、それを行うものがあります。

私のコントローラーはサービスレイヤーを呼び出すだけで、トランザクションが永続ストア(私の場合はデータベース)にいつコミットされるかを制御するのはサービスレイヤー次第だと言いました

于 2010-11-25T10:47:12.447 に答える