В приложении ZF2, управляемом Apigility, я хочу использовать пользовательский 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('Portfolio\V2\Rest\ImageService'));
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',
),
...
),
),
...
Это игнорируется, когда коллекция извлекается, но это другая проблема. Вот). Когда требуется одна сущность, запускается гидратиновый механизм, но он не использует мою фабрику для создания экземпляра.
После некоторой отладки я пришел в это место в ZF\Hal\Metadata\Metadata#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
}
}
Обычай Hydrator
создается напрямую. (В моем случае это приводит к фатальной ошибке, так как он пытается затем выполнить метод на ProjectHydrator#imageService
, это не установлено). Я посмотрел на Metadata#hydrators
(типа Zend\Stdlib\Hydrator\HydratorPluginManager
) и нашел только четыре по умолчанию invocables
, вот почему null !== $this->hydrators && $this->hydrators->has($hydrator)
является false
и прямой экземпляр судят.
Итак, я думаю, я должен зарегистрировать свой гидратор. Где / как я могу это сделать?
РЕДАКТИРОВАТЬ:
Я перемещаю заводской код из 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('Portfolio\V2\Rest\ImageService'));
return $projectHydrator;
}
),
);
}
То же самое над массивом конфигурации в module.config.php
(нужен Factory
учебный класс):
module.config.php
return array(
...
'hydrators' => array(
'factories' => array(
'MyNamespace\\Hydrator\\ProjectHydrator' => 'MyNamespace\\Hydrator\\ProjectHydratorFactory',
),
),
);
Но это заканчивается ошибкой:
Zend \ ServiceManager \ Exception \ ServiceNotFoundException
Файл:
/var/www/my-project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:550
Сообщение: Zend \ Stdlib \ Hydrator \ HydratorPluginManager :: get не удалось
получить или создать экземпляр для портфолио \ V2 \ Rest \ ImageService
Что вы сделали в своем редактировании, используя getHydratorConfig
было правильно, сообщение об ошибке, которое вы видите, вызвано тем, что в вашем заводском методе вы пытаетесь получить службу изображений из диспетчера подключаемых модулей гидратора.
Решение простое, так как с другими менеджерами плагинов вам нужно позвонить getServiceLocator()
на экземпляре диспетчера гидраторов, чтобы получить основной локатор сервиса (он же менеджер сервиса)
Итак, небольшое изменение в вашем заводском методе должно решить проблему …
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()