如何在Zend Framework 2中注册自定义水合器?

aut*_*tix 2 php hal zend-framework2 apigility

在Apigility驱动的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('Portfolio\V2\Rest\ImageService'));
                    return $projectHydrator;
                }
            ),
            ...
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

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',
        ),
        ...
    ),
),
...
Run Code Online (Sandbox Code Playgroud)

当一个集合被检索时,它被忽略了,但它是另一个问题(s.here).当需要单个实体时,waterratin机制启动,但它不使用我的工厂,以创建实例.

经过一些调试后,我来到了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
    }
}
Run Code Online (Sandbox Code Playgroud)

自定义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;
            }
        ),
    );
}
Run Code Online (Sandbox Code Playgroud)

在module.config.php(需要一个Factory类)的配置数组中相同:

module.config.php

return array(
    ...
    'hydrators' => array(
        'factories' => array(
            'MyNamespace\\Hydrator\\ProjectHydrator' => 'MyNamespace\\Hydrator\\ProjectHydratorFactory',
        ), 
    ),
);
Run Code Online (Sandbox Code Playgroud)

但它以错误结束:

Zend的\的ServiceManager \异常\ ServiceNotFoundException的

文件:/var/www/my-project/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:550消息:Zend\Stdlib\Hydrator\HydratorPluginManager :: get无法获取或创建Portfolio的实例\ V2 \休息\ ImageService

Cri*_*isp 5

您在编辑中使用的操作getHydratorConfig是正确的,您看到的错误消息是由于您在工厂方法中尝试从保护器插件管理器获取图像服务.

解决方案很简单,与其他插件管理器一样,您需要调用getServiceLocator()hydrator管理器实例以获取主服务定位器(也称为服务管理器)

因此,对您的工厂方法进行一些小改动应该可以解决问题...

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;
            }
        ),
    );
}
Run Code Online (Sandbox Code Playgroud)

工作原理:作为参数传递给闭包的实例是a 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()对于不在该特定管理器中的任何依赖项,必须进行调用.