6

我正在将 Symfony 3.2 项目迁移到 Symfony 3.3,并且我想使用DI 新功能。我已经阅读了文档,但到目前为止我可以让它工作。请参阅以下类定义:

use Http\Adapter\Guzzle6\Client;
use Http\Message\MessageFactory;

abstract class AParent
{
    protected $message;
    protected $client;
    protected $api_count_url;

    public function __construct(MessageFactory $message, Client $client, string $api_count_url)
    {
        $this->message       = $message;
        $this->client        = $client;
        $this->api_count_url = $api_count_url;
    }

    public function getCount(string $source, string $object, MessageFactory $messageFactory, Client $client): ?array
    {
        // .....
    }

    abstract public function execute(string $source, string $object, int $qty, int $company_id): array;
    abstract protected function processDataFromApi(array $entities, int $company_id): array;
    abstract protected function executeResponse(array $rows = [], int $company_id): array;
}

class AChildren extends AParent
{
    protected $qty;

    public function execute(string $source, string $object, int $qty, int $company_id): array
    {
        $url      = $this->api_count_url . "src={$source}&obj={$object}";
        $request  = $this->message->createRequest('GET', $url);
        $response = $this->client->sendRequest($request);
    }

    protected function processDataFromApi(array $entities, int $company_id): array
    {
        // ....
    }

    protected function executeResponse(array $rows = [], int $company_id): array
    {
        // ....
    }
}

这是我的app/config/services.yml文件的样子:

parameters:
    serv_api_base_url: 'https://url.com/api/'

services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false

    CommonBundle\:
        resource: '../../src/CommonBundle/*'
        exclude: '../../src/CommonBundle/{Entity,Repository}'

    CommonBundle\Controller\:
        resource: '../../src/CommonBundle/Controller'
        public: true
        tags: ['controller.service_arguments']

    # Services that need manually wiring: API related
    CommonBundle\API\AParent:
        arguments:
            $api_count_url: '%serv_api_base_url%'

但我收到以下错误:

AutowiringFailedException 无法自动装配服务“CommonBundle\API\AChildren”:方法“__construct()”的参数“$api_count_url”必须具有类型提示或显式指定值。

当然,我在这里遗漏了一些东西,或者根本不可能,这导致我想到下一个问题:这是一个糟糕的 OOP 设计还是 Symfony 3.3 DI 特性中缺少的功能?

当然,我不想让AParent类成为接口,因为我不想重新定义实现此类接口的类的方法。

此外,我不想重复自己并将相同的功能复制/粘贴到孩子身上。

想法?线索?建议?这可能吗?

更新

阅读“如何使用父服务管理公共依赖项”后,我在我的场景中尝试了以下内容:

CommonBundle\API\AParent:
    abstract: true
    arguments:
        $api_count_url: '%serv_api_base_url%'

CommonBundle\API\AChildren:
    parent: CommonBundle\API\AParent
    arguments:
        $base_url: '%serv_api_base_url%'
        $base_response_url: '%serv_api_base_response_url%' 

但是错误变成了:

当设置了“父级”时,服务“CommonBundle\API\AChildren”的属性“autowire”不能从“_defaults”继承。将您的子定义移动到单独的文件或在 /var/www/html/oneview_symfony/app/config/services.yml 中明确定义此属性(从“/var/www/html/oneview_symfony/app/config/ config.yml”)。

但是我可以使它与以下设置一起工作:

CommonBundle\API\AParent:
    arguments:
        $api_count_url: '%serv_api_base_url%'

CommonBundle\API\AChildren:
    arguments:
        $api_count_url: '%serv_api_base_url%'
        $base_url: '%serv_api_base_url%'
        $base_response_url: '%serv_api_base_response_url%'

这是正确的方法吗?这有意义吗?

更新#2

按照@Cerad 的说明,我做了一些模组(见上面的代码,见下面的定义),现在对象来了NULL?任何想法为什么会这样?

// services.yml
services:
    CommonBundle\EventListener\EntitySuscriber:
        tags:
            - { name: doctrine.event_subscriber, connection: default}

    CommonBundle\API\AParent:
        abstract: true
        arguments:
            - '@httplug.message_factory'
            - '@httplug.client.myclient'
            - '%ser_api_base_url%'

// services_api.yml
services:
    CommonBundle\API\AChildren:
        parent: CommonBundle\API\AParent
        arguments:
            $base_url: '%serv_api_base_url%'
            $base_response_url: '%serv_api_base_response_url%'

// config.yml
imports:
    - { resource: parameters.yml }
    - { resource: security.yml }
    - { resource: services.yml }
    - { resource: services_api.yml }

为什么对象NULL在子类中?

4

4 回答 4

3

有趣的。似乎您希望自动装配了解 AChild 扩展 AParent,然后使用 AParent 服务定义。我不知道这种行为是故意忽略还是设计不支持。autowire 仍处于起步阶段并正在大力开发。

我建议前往 di github 存储库,检查问题,然后在适用的情况下打开一个。开发人员会让您知道这是否是设计使然。

同时,如果您将子定义移动到不同的服务文件,则可以使用父服务功能。它将起作用,因为 _defaults 内容仅适用于当前服务文件。

# services.yml
AppBundle\Service\AParent:
    abstract: true
    arguments:
        $api_count_url: '%serv_api_base_url%'

# services2.yml NOTE: different file, add to config.yml
AppBundle\Service\AChild:
    parent: AppBundle\Service\AParent
    arguments:
        $base_url: 'base url'

最后一个稍微偏离主题的注释:不需要 public: false ,除非你用自动配置的东西鬼混。默认情况下,所有服务都被定义为私有,除非您特别声明它们是公共的。

更新 - 一条评论提到了一些关于对象为空的事情。不完全确定这意味着什么,但我去我的测试类中添加了一个记录器。所以:

use Psr\Log\LoggerInterface;

abstract class AParent
{
    protected $api_count_url;

    public function __construct(
        LoggerInterface $logger, 
        string $api_count_url)
    {
        $this->api_count_url = $api_count_url;
    }
}    
class AChild extends AParent
{
    public function __construct(LoggerInterface $logger, 
        string $api_count_url, string $base_url)
    {
        parent::__construct($logger,$api_count_url);
    }

而且由于只有一个 psr7 记录器实现,记录器是自动装配和注入的,而无需更改服务定义。

更新 2 我更新到 S3.3.8 并开始获得:

[Symfony\Component\DependencyInjection\Exception\RuntimeException]                                                                         
Invalid constructor argument 2 for service "AppBundle\Service\AParent": argument 1 must be defined before. Check your service definition.  

Autowire 仍在大力开发中。在这一点上不会花费精力来找出原因。与参数的顺序有关。一旦 LTS 版本发布,我会重新修改。

于 2017-08-25T19:41:00.997 回答
1

好吧,我经历了同样的问题。由于某些原因,我不得不创建一个Symfony 3.3.17项目来启动,因为在我的公司中,我不得不使用我们的 2 个捆绑包,它们仍然停留在 SF 3.3 中(我知道你会说什么,但我打算尽快升级所有这些)。

我的问题有点不同,这就是为什么Cerad 的解决方案在我的情况下使用起来有点复杂。但我也可以(也许)为问题中提到的问题提供解决方案。就我而言,我使用Symfony Flex来管理我的应用程序,即使它是 SF 3.3。因此,了解 flex 的人都知道config.yml不再存在,而是在应用程序的根目录下有一个config文件夹。在其中,您只有一个services.yaml文件。所以,如果你想添加一个services2.yaml文件,你会看到它没有被检测到,除非你把它重命名为services_2.yaml。但是在这种情况下,这意味着您有另一个环境称为2喜欢devtest。不好,对吧?

我从xabbuh 在本期的回答中找到了解决方案。

因此,为了能够在 Symfony 3.3 中通过 DI 在抽象类中使用自动装配,您仍然可以将服务定义保存在一个文件中:

CommonBundle\API\AParent:
    abstract: true
    autoconfigure: false
    arguments:
        $api_count_url: '%serv_api_base_url%'

CommonBundle\API\AChildren:
    parent: CommonBundle\API\AParent
    autoconfigure: false
    autowire: true
    public: false
    arguments:
        $base_url: '%serv_api_base_url%'
        $base_response_url: '%serv_api_base_response_url%'

这里的重点是

你需要更明确一点

正如xabbuh所说。如果您在服务声明中使用键,则不能从_default继承来设置publicautowireautoconfigure 。将子服务中的autoconfigure值设置为false很重要,因为如果未设置,您将拥有

服务“CommonBundle\API\AChildren”不能有“父”,也不能有“自动配置”。其实有道理...

最后一件事,如果问题与 Symfony 控制台命令有关(就像我的情况一样),不要忘记使用

tags:
    - { name: console.command }

为您的子服务,因为autoconfigure设置为false

于 2018-07-24T09:53:14.533 回答
0

我认为答案就在错误消息中,与使用抽象类无关。您是否设置了自动连线,以使 AChildren 在没有您明确指示的情况下连线?如果是这样,您可能需要为每个子类指定构造函数参数。有关可能的帮助,请参阅https://symfony.com/doc/current/service_container/parent_services.html

于 2017-08-25T19:07:38.480 回答
0

似乎自动装配不适用于 Symfony 3.3.8 中的抽象服务。为此,我在 Github 上创建了一个问题

与此同时,我个人删除了该abstract选项,它工作正常。

于 2017-09-13T09:18:15.440 回答