Внедрение зависимостей в фикстуры
Класс является фикстурой, если он реализует интерфейс \Doctrine\Fixture\Fixture. В интерфейсе определены два метода: import и purge. При использовании фикстур в реальных условиях зачастую возникает потребность использовать различные компоненты из приложения. Например, в фикстурах может потребоваться ObjectManager Doctrine2 или сервис для загрузки данных из XML-формата. Далее будет описываться способ внедрения зависимостей в фикстуры.
Внедрение зависимостей через FixtureInitializer
Быстрый старт
- Создать Aware-интерфейс. Если фикстура реализует данный интерфейс, то значит, что в нее требуется внедрить зависимость:
- Aware-интерфейс должен определять setter'ы, позволяющие передать зависимость.
interface DependencyServiceAwareInterface
{
function setDependencyService(DependencyService $dependencyService);
}
- Создать FixtureInitializer:
- Создать класс, реализующий интерфейс Nnx\DoctrineFixtureModule\FixtureInitializer\FixtureInitializerInterface;
- В качестве аргументов конструктора указать сервисы, которые необходимо внедрить в фикстуру;
- В классе реализовать метод getSubscribedEvents, возвращающий массив с двумя значениями: "import" и "purge";
- Реализовать в классе два метода: import и purge;
- В методах import и purge проверяется, что если фикстура реализует заданный Aware-интерфейс, то в нее устанавливается требуемая зависимость.
use Doctrine\Fixture\Event\ImportFixtureEventListener;
use Doctrine\Fixture\Event\PurgeFixtureEventListener;
use Nnx\DoctrineFixtureModule\FixtureInitializer\FixtureInitializerInterface;
class MyFixtureInitializer implements
FixtureInitializerInterface,
ImportFixtureEventListener,
PurgeFixtureEventListener
{
/**
* Constructor.
*
* @param DependencyService $dependencyService
*/
public function __construct(DependencyService $dependencyService)
{
$this->dependencyService = $dependencyService;
}
/**
* {@inheritdoc}
*/
public function getSubscribedEvents()
{
return array(
ImportFixtureEventListener::IMPORT,
PurgeFixtureEventListener::PURGE,
);
}
/**
* {@inheritdoc}
*/
public function purge(FixtureEvent $event)
{
if ( ! ($fixture instanceof MyServiceAwareInterface)) {
return;
}
$fixture->setDependencyService($this->dependencyService);
}
/**
* {@inheritdoc}
*/
public function import(FixtureEvent $event)
{
if ( ! ($fixture instanceof MyServiceAwareInterface)) {
return;
}
$fixture->setDependencyService($this->dependencyService);
}
}
- Создать фабрику для FixtureInitializer:
- Создать стандартную фабрику, которая реализует интерфейс \Zend\ServiceManager\FactoryInterface;
- Из ServiceLocator получить необходимые зависимости. Необходимо помнить что ServiceLocator в фабриках, используемых для менеджеров плагинов, является самим менеджером плагинов;
- Создать FixtureInitializer и передать в конструктор необходимые зависимости.
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class MyFixtureInitializerFactory implements FactoryInterface
{
/**
* @inheritDoc
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$appServiceLocator = $serviceLocator instanceof AbstractPluginManager ? $serviceLocator->getServiceLocator() : $serviceLocator;
/** @var DependencyService $dependencyService */
$dependencyService = $appServiceLocator->get(DependencyService::class);
return new MyFixtureInitializer($dependencyService);
}
}
- Зарегестрировать FixtureInitializer в менеджере плагинов \Nnx\DoctrineFixtureModule\Executor\FixtureExecutorManagerInterface:
- В секцию ['nnx_fixture_initializer']['factories'] конфигурационных файлов приложения, добавить созданный FixtureInitializer
return [
'nnx_fixture_initializer' => [
'factories' => [
MyFixtureInitializer::class => MyFixtureInitializerFactory::class,
]
],
];
- Добавить FixtureInitializer в список тех FixtureInitializer, которые запускаются для фикстур
- Добавить в секцию ['nnx_doctrine_fixture_module']['fixtureInitializer'] имя FixtureInitializer
return [
'nnx_doctrine_fixture_module' => [
'fixtureInitializer' => [
MyFixtureInitializer::class
]
],
];
Описание FixtureInitializer
Для внедрения зависимостей используется модификация паттерна "Method Injection", а именно – "Interface Injection". Непосредственно после создания фикстуры запускаются FixtureInitializer.
FixtureInitializer – это класс, реализующий \Nnx\DoctrineFixtureModule\FixtureInitializer\FixtureInitializerInterface. Интерфейс \Nnx\DoctrineFixtureModule\FixtureInitializer\FixtureInitializerInterface расширяет \Doctrine\Common\EventSubscriber.
Таким образом, в классе FixtureInitializer необходимо реализовать метод getSubscribedEvents, в котором нужно вернуть массив с именами событий, на которые подписываемся.
Для реализации FixtureInitializer необходимо подписаться на два события:
- import - событие, бросаемое при запуске процесса импорта;
- purge - событие, бросаемое, при запуске процесса удаления данных.
Пример FixtureInitializer:
use Doctrine\Fixture\Event\ImportFixtureEventListener;
use Doctrine\Fixture\Event\PurgeFixtureEventListener;
use Nnx\DoctrineFixtureModule\FixtureInitializer\FixtureInitializerInterface;
class MyFixtureInitializer implements
FixtureInitializerInterface,
ImportFixtureEventListener,
PurgeFixtureEventListener
{
/**
* Constructor.
*
* @param DependencyService $dependencyService
*/
public function __construct(DependencyService $dependencyService)
{
$this->dependencyService = $dependencyService;
}
/**
* {@inheritdoc}
*/
public function getSubscribedEvents()
{
return array(
ImportFixtureEventListener::IMPORT,
PurgeFixtureEventListener::PURGE,
);
}
/**
* {@inheritdoc}
*/
public function purge(FixtureEvent $event)
{
if ( ! ($fixture instanceof MyServiceAwareInterface)) {
return;
}
$fixture->setDependencyService($this->dependencyService);
}
/**
* {@inheritdoc}
*/
public function import(FixtureEvent $event)
{
if ( ! ($fixture instanceof MyServiceAwareInterface)) {
return;
}
$fixture->setDependencyService($this->dependencyService);
}
}
В фикстуре необходимо реализовать два метода: import и purge. В методах обычно происходит проверка того, что фикстура реализует заданный интерфейс. В случае если фикстура интерфейс реализует, то происходит внедрение соответствующей зависимости в фикстуру.
Регистрация FixtureInitializer
Для работы с FixtureInitializer используется специальный менеджер плагинов \Nnx\DoctrineFixtureModule\FixtureInitializer\FixtureInitializerManagerInterface. Для регистрации FixtureInitializer в менеджере плагинов необходимо добавить соответствующий плагин в секцию nnx_fixture_initializer.
return [
'nnx_fixture_initializer' => [
'invokables' => [
],
'factories' => [
MyFixtureInitializer::class => MyFixtureInitializerFactory::class,
],
'abstract_factories' => [
],
'shared' => [
]
],
];
FixtureInitializer бывают двух типов:
- Initializer для контекста. Такие FixtureInitializer создаются и подписываются на события import и purge. В качестве аргумента передаются данные, переданные в аргументах import и purge объекта имплементирующего, \Nnx\DoctrineFixtureModule\Executor\ExecutorInterface
- Статические Initializer. Такие FixtureInitializer не создаются каждый раз заново, также они подписываются единожды на события import и purge всех FixtureInitializer
Добавление Initializer для контекста
Для добавления Initializer для контекста необходимо в секции ['nnx_doctrine_fixture_module']['contextInitializer'] добавить имя Initializera'a (по этому имени он будет получен из \Nnx\DoctrineFixtureModule\Executor\FixtureExecutorManagerInterface)
Добавление статического Initializer
Для добавления Initializer для контекста необходимо в секции ['nnx_doctrine_fixture_module']['fixtureInitializer'] добавить имя Initializera'a (по этому имени он будет получен из \Nnx\DoctrineFixtureModule\Executor\FixtureExecutorManagerInterface)