Symfony2動的イベントサブスクリプション

こんにちは、ハラジテル。



少し前まではあまり標準的ではなかったタスクに遭遇したので、このソリューションのバリエーションを共有したり、このトピックに関するスマートソープを学びたいと思います。 誰も気にしない、猫へようこそ。



プロジェクトの構成に関するいくつかの言葉:Symfony 2.3 + doctrine 2の使用



問題の声明


事前に名前がわからないイベントハンドラを実装します。 システムには、データベース内のすべてのイベントのレジストリが含まれています。



ドキュメントを使用して、symfonyイベントディスパッチャーの標準実装でイベントをサブスクライブするための3つのオプションを区別できます。

  1. kernel.event_listenerタグを含むサービスをDIコンテナに追加します
  2. EventSubscriberInterfaceを実装するkernel.event_subscriberタグを使用して、DIコンテナにサービスを追加します
  3. アプリケーションのディスパッチ中にaddListenerメソッド呼び出しを追加する




最初の方法は、イベントの名前が事前にわからないため、私たちには適していません。 2番目のメソッドはまさにあなたが必要とするもののようですが、getSubscribedEventsメソッドは静的でなければなりません。つまり、注入されたサービスと対話できません。



3番目の方法は最も論理的に思えましたが、データベースへの各リクエストに別のリクエストを追加したくないので、イベント名のリストをキャッシュするよりエレガントなソリューションを探し始めました。



このアイデアはコンパイラパスを使用するようになったため、彼女はこの問題を解決しました。 バンドルオブジェクトのインスタンスを作成するとき、DIコンテナのコンパイルに参加するクラスを登録できます。 教義に到達する方法が1つ重要な問題として残っており、ここでコンパイラパスのソートが役立ちます。 コンテナをコンパイルするには5つの手順があります。





初期段階ではサービス定義がコンパイルされ、後期段階では未使用のサービスとプライベートエイリアスが削除されます(ドキュメントから)。



実装


この段階のすべてのサービスはすでに使用可能になっているため、最後の段階は私たちに似ています。 コンパイラーを宣言します。

/** * Builds the bundle. * * It is only ever called once when the cache is empty. * * This method can be overridden to register compilation passes, * other extensions, ... * * @param ContainerBuilder $container A ContainerBuilder instance */ public function build(ContainerBuilder $container) { parent::build($container); $container->addCompilerPass(new EventsCompilerPass(), PassConfig::TYPE_AFTER_REMOVING); }
      
      







実際にはコンパイラコード(データベースからデータを取得するコードは、リポジトリクラスに隠されています)

  public function process(ContainerBuilder $container) { if (!$container->hasDefinition(self::SERVICE_KEY)) { return; } $eventClassName = $container->getParameter(self::EVENT_ENTITY_CLASS_PARAM); $dispatcher = $container->getDefinition(self::DISPATCHER_KEY); $em = $container->get('doctrine.orm.entity_manager'); $eventNames = array(); if ($this->isSchemaSynced($em, $eventClassName) !== false) { $eventNames = $em->getRepository($eventClassName) ->getEventNames(); } foreach ($eventNames as $eventName) { $dispatcher->addMethodCall( 'addListenerService', array($eventName['name'], array(self::SERVICE_KEY, 'process')) ); } }
      
      





ご覧のとおり、すべてのイベント名を選択し、イベントデータハンドラーとしてサービスを登録します。 説明されていない唯一のものはisSchemaSyncedメソッドです。その実装から始めます。

  protected function isSchemaSynced(EntityManager $em, $className) { $tables = $em->getConnection()->getSchemaManager()->listTableNames(); $table = $em->getClassMetadata($className)->getTableName(); return array_search($table, $tables); }
      
      





イベント名を持つテーブルが作成されているかどうかを確認します。 問題は、Doctrine:schema:create serviceコマンドが呼び出されたときにコンテナーが初めてコンパイルされ、DBExceptionが発生する可能性があることです。



ご清聴ありがとうございました。同様の課題に直面した人々の意見を聞いてうれしいです。

これは、プログラミング全般に関する私の最初の投稿であると厳密に判断しないでください。



All Articles