如何在 Zend Framework 2 中注册自定义水化器?
How to register a custom hydrator in Zend Framework 2?
在 Apigility 驱动的 ZF2 应用程序中,我想使用自定义 Hydrator
。
Module
class
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',
),
...
),
),
...
当检索到集合时,它会被忽略,但这是另一个问题 (s. here)。当需要单个实体时,hydratin 机制启动,但它不使用我的工厂,以创建实例。
经过一番调试,我来到了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
并尝试直接实例化.
所以,我想,我必须注册我的定制水瓶。 Where/how我可以这样做吗?
编辑:
我将工厂代码从 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
class):
module.config.php
return array(
...
'hydrators' => array(
'factories' => array(
'MyNamespace\Hydrator\ProjectHydrator' => 'MyNamespace\Hydrator\ProjectHydratorFactory',
),
),
);
但它以错误结尾:
Zend\ServiceManager\Exception\ServiceNotFoundException
File:
/var/www/my-project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:550
Message: Zend\Stdlib\Hydrator\HydratorPluginManager::get was unable to
fetch or create an instance for Portfolio\V2\Rest\ImageService
快速响应。将 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
的实例。调用方法$services->getServiceLocator()
可以获得通用的ServiceLocator
您在使用 getHydratorConfig
进行的编辑中所做的是正确的,您看到的错误消息是由于在您的工厂方法中您试图从 hydrator 插件管理器获取图像服务而引起的。
解决方案很简单,与其他插件管理器一样,您需要在 hydrator 管理器实例上调用 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
中被覆盖并且只提供水化器。尽管如此,它的父级 class 实现了 Zend\ServiceManager\ServiceLocatorAwareInterface
,因此我们可以访问其内部 ServiceLocator
并通过 ServiceLocator
访问整个可用服务集。
顺便说一句,我通常更喜欢在工厂方法中命名 serviceLocator 变量(在您的代码中为 $serviceManager
),以便它反映实际使用的插件管理器,因此对于水龙头工厂,$hydrators
,对于表单工厂,$formElemements
,等等。我保留使用 $services
仅指代主要服务经理。我发现这样做是一个有用的提醒,即对于不在该特定管理器中的任何依赖项,getServiceLocator()
调用是必需的。
在 Apigility 驱动的 ZF2 应用程序中,我想使用自定义 Hydrator
。
Module
class
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',
),
...
),
),
...
当检索到集合时,它会被忽略,但这是另一个问题 (s. here)。当需要单个实体时,hydratin 机制启动,但它不使用我的工厂,以创建实例。
经过一番调试,我来到了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
并尝试直接实例化.
所以,我想,我必须注册我的定制水瓶。 Where/how我可以这样做吗?
编辑:
我将工厂代码从 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
class):
module.config.php
return array(
...
'hydrators' => array(
'factories' => array(
'MyNamespace\Hydrator\ProjectHydrator' => 'MyNamespace\Hydrator\ProjectHydratorFactory',
),
),
);
但它以错误结尾:
Zend\ServiceManager\Exception\ServiceNotFoundException
File: /var/www/my-project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:550 Message: Zend\Stdlib\Hydrator\HydratorPluginManager::get was unable to fetch or create an instance for Portfolio\V2\Rest\ImageService
快速响应。将 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
的实例。调用方法$services->getServiceLocator()
您在使用 getHydratorConfig
进行的编辑中所做的是正确的,您看到的错误消息是由于在您的工厂方法中您试图从 hydrator 插件管理器获取图像服务而引起的。
解决方案很简单,与其他插件管理器一样,您需要在 hydrator 管理器实例上调用 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
中被覆盖并且只提供水化器。尽管如此,它的父级 class 实现了 Zend\ServiceManager\ServiceLocatorAwareInterface
,因此我们可以访问其内部 ServiceLocator
并通过 ServiceLocator
访问整个可用服务集。
顺便说一句,我通常更喜欢在工厂方法中命名 serviceLocator 变量(在您的代码中为 $serviceManager
),以便它反映实际使用的插件管理器,因此对于水龙头工厂,$hydrators
,对于表单工厂,$formElemements
,等等。我保留使用 $services
仅指代主要服务经理。我发现这样做是一个有用的提醒,即对于不在该特定管理器中的任何依赖项,getServiceLocator()
调用是必需的。