我正在将Symfony 3.2项目移至Symfony 3.3,我想使用DI new features。我有read the docs但到目前为止我可以让它发挥作用。请参阅以下类定义:
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":参数" $ api_count_url"方法 " __构建体()"必须有一个类型提示或明确给出一个值。
当然我在这里遗漏了一些东西,或者仅仅这是不可能的,这导致我接下来的问题:这是一个糟糕的OOP设计还是它缺少Symfony 3.3 DI功能的功能?
当然,我不想让AParent
类成为一个接口,因为我不想重新定义实现这种接口的类的方法。
此外,我不想重复自己,并在孩子们身上复制/粘贴相同的功能。
想法?线索?建议吗?这可能吗?
更新
阅读" How to Manage Common Dependencies with Parent Services"我在我的场景中尝试了以下内容:
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%'
但错误变成了:
属性" autowire"在服务" CommonBundle \ API \ AChildren"不可能是 继承自" _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说明,我做了一些mods(参见上面的代码,看下面的定义),现在对象来了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
?
答案 0 :(得分:3)
有趣。您似乎希望autowire了解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'
最后一个略微偏离主题的说明:没有必要公开:假,除非你愚弄自动配置的东西。默认情况下,除非您明确声明它们是公共的,否则所有服务都被定义为私有。
更新 - 评论提到有关对象为空的内容。不完全确定这意味着什么,但我去了我的测试类添加了一个记录器。所以:
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版本发布,我就会复活。
答案 1 :(得分:1)
嗯,我遇到了同样的问题。出于某些原因,我不得不创建一个 Symfony 3.3.17 项目作为开始,因为在我的公司中,我不得不使用仍然捆绑在SF 3.3中的两个捆绑软件(我知道你会说什么关于此问题,但我打算很快将所有这些都升级。
我的问题有点不同,这就是为什么Cerad's solution在我的情况下使用起来有点复杂。但是我也可以(可能)为问题中提到的问题提供解决方案。就我而言,我正在使用Symfony Flex来管理我的应用程序,即使它是SF 3.3。因此,了解flex的人都知道 config.yml 不再存在,而是在应用程序的根目录下有一个 config 文件夹。在其中,您只有一个 services.yaml 文件。因此,如果要添加 services2.yaml 文件,除非将其重命名为 services_2.yaml ,否则将不会检测到该文件。但是在这种情况下,这意味着您拥有另一个名为 2 的环境,例如 dev 或 test 。不好吧?
我从xabbuh's answer in this issue找到了解决方案。
因此,为了能够在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 来设置公共,自动装配和自动配置。您的服务声明中的“ strong>”键。将子服务中的 autoconfigure 值设置为 false 很重要,因为如果未设置,您将拥有您需要更加明确
服务“ CommonBundle \ API \ AChildren”不能具有“父”,也不能具有“自动配置”。实际上有道理...
最后一件事,如果问题与Symfony Console命令有关(例如我的情况),请不要忘记使用
tags:
- { name: console.command }
为您的孩子服务,因为自动配置设置为否。
答案 2 :(得分:0)
我认为答案就在错误信息中,与使用抽象类无关。 您是否以这样的方式启用自动装配,即AChildren将在没有您明确指示的情况下接通电源?如果是这样,您可能需要为每个子类指定构造函数参数。 有关可能的帮助,请参阅https://symfony.com/doc/current/service_container/parent_services.html
答案 3 :(得分:0)
似乎自动装配在Symfony 3.3.8中不适用于抽象服务。我为此创建了an issue on Github。
与此同时,我个人删除了abstract
选项,它运行正常。