Symfony Dependency Injection Componentを使用したDoctrineエンティティへの依存性注入

本質的に依存関係の実装はDDDの観点からは悪い習慣と見なされているという事実にもかかわらず、非常に便利な状況があります。 このアプローチを使用することの正当性、および代替手段(二重ディスパッチ、イベント)との比較は、この記事のトピックではありません。 技術的な実装-Symfony Dependency Injection Component(以降DIC)とDoctrineの統合により、依存関係をロード可能なエンティティに自動的に注入することについてお話します。 SymfonyとDoctrineの使用バージョンは2です。





したがって、ブート時または作成時に依存関係を挿入する必要があるエンティティがあります。



<?php namespace Domain; /** * @Entity */ class SomeEntity { …... private $someService; private $anotherService; …... public function setSomeService(SomeService $someService) { $this->someService = $someService; } public function setAnotherService2(AnotherService $anotherService) { $this->anotherService = $anotherService; } }
      
      







残念ながら、Doctrineを使用してコンストラクターに依存性注入を実装する簡単な方法は見つかりませんでした-オブジェクトの作成は、作業単位とClassMetadataの奥深くにあります。 したがって、実装はセッターを使用して実行されます。



統合のために、DoctrineイベントとDICタグが使用されます。 DIC configsの形式はyamlですが、好みのものを使用できます。



エンティティをロードした後、依存関係を埋め込みたいです。 これを行うには、postLoadイベントを自由に使用できます。



このイベントに応答するEventSubscriberを実装します。



 <?php namespace Persistence; use Doctrine\ORM\Events; use Doctrine\ORM\Event\LifecycleEventArgs; use Doctrine\Common\EventSubscriber; use DependencyInjection\Injector; class EntityConfigurator implements EventSubscriber { private $injector; public function __construct(Injector $injector) { $this->injector = $injector;; } public function getSubscribedEvents() { return [Events::postLoad]; } public function postLoad(LifecycleEventArgs $args) { $entity = $args->getEntity(); $this->injector->injectSevicesTo($entity); } }
      
      







そして、作成後にエンティティマネージャに接続します



 $entityManager->getEventManager()->addEventSubscriber($entityConfigurator);
      
      







すべての実装作業は、Injectorクラス内で行われます。



依存性注入アルゴリズムは簡単です。





実装:

 <?php namespace DependencyInjection; use Symfony\Component\DependencyInjection\ContainerBuilder; class Injector { const DOCTRINE_ENTITY_TAG = 'doctrine-entity'; /** *@var \Symfony\Component\DependencyInjection\ContainerBuilder */ private $container; private $configurableClasses = []; public function __construct(ContainerBuilder $container) { $this->container = $container; $this->prepareConfigurableClasses(); } private function prepareConfigurableClasses() { //       foreach($this->container->findTaggedServiceIds(self::DOCTRINE_ENTITY_TAG) as $id => $tag) { //    $definition = $this->container->findDefinition($id); //    setter  $this->configurableClasses[$definition->getClass()] = $definition->getMethodCalls(); } } public function injectSevicesTo($object) { if(!is_object($object) || !array_key_exists(get_class($object), $this->configurableClasses)) { return; } //       DIC $parameter_bag = $this->container->getParameterBag(); $calls = $this->configurableClasses[get_class($object)]; foreach($calls as $call) { //   $parametrized_references = $parameter_bag->resolveValue($call[1]); call_user_func_array(array($object, $call[0]), $this->container->resolveServices($parametrized_references)); } } }
      
      





必要に応じて、DOCTRINE_ENTITY_TAG定数を異なるタグの配列に置き換えることができます。



Injectorが依存関係を注入するには、DIC(yaml)構成でエンティティを記述する必要があります。



サービス:
    ... ....
   エンティティオブジェクトタイトル:
        クラス: 'Domain \ SomeEntity'
        タグ:[{name: "doctrine-entity"}]
        抽象:true
        呼び出し:
           -[setSomeService、[@ some-service]]
           -[setSomeService2、[@ some-service2]]
    ... ..








Doctrineを使用してエンティティをロードするとき、依存関係は自動的に注入されます。



新しいオブジェクトに依存性注入を実装する必要がある場合は、適切なファクトリまたはファクトリメソッド内でInjectorを使用できます。



PS。 Injectorクラスでは、ContainerBuilderが単純化のために直接使用されますが、実際のプロジェクトではラッパーが使用されます。 これにより、Symfony DICの機能をカプセル化し、必要に応じて他のDIライブラリを使用できます。 さらに重要なことは、「持っていない型をモックしない」という原則を使用できるようにすることです。



All Articles