php - 如何在 Zend Framework 2 中注册自定义水化器?

标签 php zend-framework2 laminas-api-tools hal

在 Apigility 驱动的 ZF2 应用程序中,我想使用自定义 Hydrator

模块

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 直接创建。 (在我的例子中,它会导致 fatal error ,因为它随后尝试在 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

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

最佳答案

您在使用 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\HydratorPluginManagerHydratorPluginManager 继承自 Zend\ServiceManager\AbstractPluginManager,它是 Zend\ServiceManager\ServiceManager 的子项。但是 ServiceManager#get(...) 方法在 HydratorPluginManager 中被逻辑覆盖并且只提供水合器。尽管如此,它的父类实现了 Zend\ServiceManager\ServiceLocatorAwareInterface,因此我们可以访问其内部的 ServiceLocator 并通过 ServiceLocator 访问整个集合可用服务。

顺便说一句,我通常更喜欢在工厂方法中命名 serviceLocator 变量($serviceManager 在您的代码中),以便它反射(reflect)实际使用的插件管理器,因此对于水龙头工厂, $hydrators,用于表单工厂,$formElements,等等。我保留使用 $services 仅指代主要服务管理器。我发现这样做是一个有用的提醒,即 getServiceLocator() 调用对于不在该特定管理器中的任何依赖项都是必需的。

关于php - 如何在 Zend Framework 2 中注册自定义水化器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29012817/

相关文章:

zend-framework2 - 未找到名称为 "course"的路由,zend Framework 2

php - 将 ZF2 服务管理器转换为 ZF3

mongodb - 如何配置灵活性以使用 Doctrine 的 MongoDB ODM?

php - 单独的 php Web 应用程序之间的自动登录。安全问题?

php - 更新不同的数据库和表

PHP 使用数组进行双重排序或使用两个 mysql 查询?

php - Apigility 中的水化器设置被忽略

PHP - 动态引用对象属性

php - 在 Zend Framework 2 中创建下拉列表

php - Apigility Rest 服务 - 如何使用连接的数据库按非唯一列进行过滤