したがって、ブート時または作成時に依存関係を挿入する必要があるエンティティがあります。
<?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クラス内で行われます。
依存性注入アルゴリズムは簡単です。
- DICから特別なタグでマークされたすべてのサービスを取得します
- ロード時に、受信したサービスのいずれかが、ロードされたエンティティのクラスと等しいクラスを持っている場合、すべてのセッター呼び出しを実行します
実装:
<?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]] ... ..
-
entity-object-title
説明のために使用される任意の名前。 -
abstract: true
サービスを直接作成する機能を無効にします。 これらの目的にはpublic: false
を使用できます。 -
calls
-依存関係の注入が行われるセッターの列挙。
Doctrineを使用してエンティティをロードするとき、依存関係は自動的に注入されます。
新しいオブジェクトに依存性注入を実装する必要がある場合は、適切なファクトリまたはファクトリメソッド内でInjectorを使用できます。
PS。 Injectorクラスでは、ContainerBuilderが単純化のために直接使用されますが、実際のプロジェクトではラッパーが使用されます。 これにより、Symfony DICの機能をカプセル化し、必要に応じて他のDIライブラリを使用できます。 さらに重要なことは、「持っていない型をモックしない」という原則を使用できるようにすることです。