Как зарегистрировать пользовательский гидратор в Zend Framework 2?
В приложении Apigility driven ZF2 я хочу использовать пользовательский Hydrator
.
Module
класс
class Module implements ApigilityProviderInterface {
...
public function getServiceConfig() {
return array(
'factories' => array(
...
'MyNamespace\Hydrator\ProjectHydrator' => function(ServiceManager $serviceManager) {
$projectHydrator = new ProjectHydrator();
$projectHydrator->setImageService($serviceManager->get('PortfolioV2RestImageService'));
return $projectHydrator;
}
),
...
);
}
}
module.config.php
...
'zf-hal' => array(
'metadata_map' => array(
...
'Portfolio\V2\Rest\Project\ProjectEntity' => array(
'entity_identifier_name' => 'id',
'route_name' => 'portfolio.rest.project',
'route_identifier_name' => 'id',
// 'hydrator' => 'Zend\Stdlib\Hydrator\ClassMethods',
'hydrator' => 'MyNamespace\Hydrator\ProjectHydrator',
),
...
),
),
...
Это игнорируется, когда коллекция извлекается, но это другая проблема (s. здесь ). Когда требуется один объект, запускается механизм гидратина, но он не использует мою фабрику, чтобы создать экземпляр.
После некоторой отладки я пришел к этому месту в коде ZFHalMetadataMetadata#setHydrator(...)
:
if (is_string($hydrator)) {
if (null !== $this->hydrators
&& $this->hydrators->has($hydrator)
) {
$hydrator = $this->hydrators->get($hydrator);
} elseif (class_exists($hydrator)) {
$hydrator = new $hydrator(); // <-- here
}
}
В custom Hydrator
создается непосредственно. (В моем случае это приводит к фатальной ошибке, так как он пытается выполнить метод на ProjectHydrator#imageService
, который не установлен). Я взглянул на Metadata#hydrators
(типа ZendStdlibHydratorHydratorPluginManager
) и нашел только четыре дефолта invocables
, поэтому null !== $this->hydrators && $this->hydrators->has($hydrator)
является false
и прямой экземпляр пробуется.
Так что, я думаю, мне нужно зарегистрировать свой собственный гидратор. Где/как я могу это сделать?
EDIT:
Я перемещаю заводской код из Module#getServiceConfig()
в Module#getHydratorConfig()
:
public function getHydratorConfig() {
return array(
'factories' => array(
// V2
'MyNamespace\Hydrator\ProjectHydrator' => function(ServiceManager $serviceManager) {
$projectHydrator = new ProjectHydrator();
$projectHydrator->setImageService($serviceManager->get('PortfolioV2RestImageService'));
return $projectHydrator;
}
),
);
}
То же самое над конфигурационным массивом в module.config.php
(нужен класс Factory
):
module.config.php
return array(
...
'hydrators' => array(
'factories' => array(
'MyNamespace\Hydrator\ProjectHydrator' => 'MyNamespace\Hydrator\ProjectHydratorFactory',
),
),
);
Но заканчивается она ошибкой:
ZendServiceManagerExceptionServiceNotFoundException
Файл: в /var/www в/мой проект/поставщика/на базе ZendFramework/на базе ZendFramework/библиотека/Зенд/элемент ServiceManager/элемент ServiceManager.php: 550 Сообщение: ZendStdlibHydratorHydratorPluginManager:: получить не удалось извлечение или создание экземпляра для PortfolioV2RestImageService
2 ответа:
То, что вы сделали в своем редактировании с помощью
getHydratorConfig
, было правильно, сообщение об ошибке, которое вы видите, вызвано тем, что в вашем фабричном методе вы пытаетесь получить свой сервис изображений из менеджера плагинов hydrator.Решение простое, как и в случае с другими менеджерами плагинов, вам нужно вызвать
getServiceLocator()
на экземпляре менеджера hydrator, чтобы получить главный сервис locator (он же service manager)Таким образом, небольшое изменение в вашем Заводском методе должно устранить проблему. ...
public function getHydratorConfig() { return array( 'factories' => array( // V2 'MyNamespace\\Hydrator\\ProjectHydrator' => function(ServiceManager $serviceManager) { $projectHydrator = new ProjectHydrator(); // get the image service from the main service locator $imageService = $serviceManager->getServiceLocator()->get('Portfolio\V2\Rest\ImageService'); $projectHydrator->setImageService($imageService); return $projectHydrator; } ), ); }
Как это работает: экземпляр, переданный замыканию в качестве аргумента, является
Zend\Stdlib\Hydrator\HydratorPluginManager
.HydratorPluginManager
наследует отZend\ServiceManager\AbstractPluginManager
, то есть является потомкомZend\ServiceManager\ServiceManager
. Но методServiceManager#get(...)
логически переопределяется вHydratorPluginManager
и предоставляет только гидраторы. Тем не менее, родительский класс реализуетZend\ServiceManager\ServiceLocatorAwareInterface
, поэтому мы обращаемся к его внутреннемуServiceLocator
и черезServiceLocator
ко всему набору доступных сервисов.В качестве отступления я обычно предпочитаю называть переменную serviceLocator на заводе метод (
$serviceManager
в вашем коде) так, чтобы он отражал фактический менеджер плагинов в использовании, поэтому для фабрики гидраторов,$hydrators
, для фабрики форм,$formElemements
и так далее. Я оставляю за собой использование$services
только для обращения к главному менеджеру службы. Я считаю, что это полезное напоминание о том, что вызовgetServiceLocator()
необходим для любых зависимостей, не расположенных в этом конкретном менеджере.
Быстрый ответ. Замените
HydratorManager
пользовательским.В вашей глобальной конфигурации:
return array( 'service_manager' => array( 'factories' => ( 'HydratorManager' => 'MyNamespace\Hydrator\HydratorManagerFactory', ), ), );
MyNamespace \ Hydrator\HydratorManagerFactory.php:
<?php namespace MyNamespace\Hydrator; use Zend\Mvc\Service\HydratorManagerFactory as BaseHydratorManagerFactory; class HydratorManagerFactory extends BaseHydratorManagerFactory { const PLUGIN_MANAGER_CLASS = 'MyNamespace\Hydrator\HydratorPluginManager'; }
MyNamespace\Hydrator\HydratorPluginManager.php
<?php namespace MyNamespace\Hydrator; use Zend\Stdlib\Hydrator\HydratorPluginManager as BaseHydratorPluginManager; class HydratorPluginManager extends BaseHydratorPluginManager { protected $factories = array( 'mynamespacehydratorprojecthydrator' => 'MyNamespace\\Hydrator\\ProjectHydratorFactory', ); }
Примечание фабрика получит экземпляр
Zend\ServiceManager\AbstractPluginManager
. Вы можете получить общий ServiceLocator, вызывающий метод$services->getServiceLocator()