将配置从 service.yaml 移动到 Symfony 5 中的另一个文件

Move configuration from service.yaml to another file in Symfony5

我的 config/service.yaml 包含几个具有巨大配置的服务。现在,我想将该服务的配置移动到一个单独的文件中。

我试过这样做:

service.yaml结束时:

imports:
   - { resource: 'packages/custom_service.yaml' }

config/packages/custom_service.yaml:

services:
    App\Service\CustomService:
        arguments:
            $forms:
                - location: 'room1'
                  id: 43543
                - location: 'room2'
                  id: 6476546
                - location: 'room3'
                  id: 121231
        ...

src/Service/CustomService.php:

    /**
     * @var array
     */
    protected $forms;

    public function __construct(array $forms)
    {
        $this->forms = $forms;
    }

但是当我尝试在某些控制器中自动装配时,出现此错误:

Cannot resolve argument $customService of "App\Controller\CustomController::customAction()": Cannot autowire service "App\Service\CustomService": argument "$forms" of method "__construct()" is type-hinted "array", you should configure its value explicitly.

但是如果我删除类型提示,则会出现此错误:

Cannot resolve argument $customService of "App\Controller\CustomController::customAction()": Cannot autowire service "App\Service\CustomService": argument "$forms" of method "__construct()" has no type-hint, you should configure its value explicitly.

自动装配有文件作用域。

你也需要在config/packages/custom_service.yaml中设置它。

修改该文件如下

services:
  _defaults:
    autowire: true
  
  App\Service\CustomService:
    [...]

如果需要,您还应该添加 autoconfigure and/or 可见性(public 关键字)。

很遗憾,我无法添加评论,所以我必须使用答案。

我必须做出以下假设,我相信您的 services.yaml 中有以下代码段:

App\:
    resource: '../src/'
    exclude:
        - '../src/DependencyInjection/'
        - '../src/Entity/'
        - '../src/Kernel.php'
        - '../src/Tests/'

已经包含了你想要的 class App\Service\CustomService 所以 Symfony 会在查看 imports 之前尝试自动装配它,这会导致错误,因为你有 class 在 import 中定义,仅当根 services.yaml 尚未找到 class.

时才会进行评估

下面的示例将起作用。但是,您将无法自动发现 App 命名空间内的 classes,不推荐这样做。

services.yaml:

parameters:

imports:
    - { resource: 'custom_services.yaml' }

services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true
        autoconfigure: true

    App\Controller\:
        resource: '../src/Controller/'
        tags: ['controller.service_arguments']

custom_services.yaml:

services:
    _defaults:
        autowire: true
        autoconfigure: true

    App\Service\SomeService:
        arguments:
            $forms:
                - location: 'room1'
                  id: 43543
                - location: 'room2'
                  id: 6476546
                - location: 'room3'
                  id: 121231

src/Service/SomeService.php:

<?php

declare(strict_types=1);

namespace App\Service;

class SomeService
{
    protected array $forms;

    public function __construct(array $forms)
    {
        $this->forms = $forms;
    }

    public function getForms()
    {
        return $this->forms;
    }
}

src/Controller/HomeController:

<?php

declare(strict_types=1);

namespace App\Controller;

use App\Service\SomeService;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;

class HomeController
{
    #[Route('/', 'home')]
    public function __invoke(SomeService $someService)
    {
        return new JsonResponse($someService->getForms());
    }
}

您可以做的是将文件导入内核而不是 yaml 文件,这将覆盖服务定义。

因此删除 services.yaml 中的导入部分并确保将文件导入 src/Kernel.php:

protected function configureContainer(ContainerConfigurator $container): void
{
    $container->import('../config/{packages}/*.yaml');
    $container->import('../config/{packages}/'.$this->environment.'/*.yaml');

    if (is_file(\dirname(__DIR__).'/config/services.yaml')) {
        $container->import('../config/services.yaml');
        
        // Import your custom service files after services.yaml to overwrite already existing definitions
        $container->import('../config/custom_services.yaml');


        $container->import('../config/{services}_'.$this->environment.'.yaml');
    } elseif (is_file($path = \dirname(__DIR__).'/config/services.php')) {
        (require $path)($container->withPath($path), $this);
    }
}

其他两个答案部分正确,但并未涵盖所有内容。

首先,无论何时您决定需要多个自动装配的服务文件,您都必须注意给定服务仅由一个文件自动装配。否则 Symfony 将多次尝试定义服务并且通常会失败。有多种方法可以实现这一点,但最直接的方法是明确排除您的 config/services.yaml 文件中的任何自定义服务:

# config/services.yaml
parameters:

imports:
    - { resource: 'services/custom_service.yaml' }

services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.

    # makes classes in src/ available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    App\:
        resource: '../src/'
        exclude:
            - '../src/Service/CustomService.php' # ADD THIS
            - '../src/DependencyInjection/'
            - # the rest of the default excludes

这样就可以跳过主配置文件中的 CustomService.php。

其次,请注意在上面的文件中,我从 config/services 而不是 config/packages 加载了 custom_service.yaml 文件。这些包实际上是为捆绑包特定配置保留的。默认情况下会加载其中的每个文件。将服务文件放在那里可能会或可能不会起作用,但它肯定会改变事物的加载顺序并可能导致问题。因此,为自定义服务创建一个目录并使用它。

您还需要在每个服务文件中重复 _defaults 部分,因为 _defaults 被认为是文件特定的。也许有点痛苦,但事实就是这样:

# config/services/custom_service.yaml

services:
  _defaults:
    autowire: true
    autoconfigure: true

  App\Service\CustomService:
    arguments:
      $forms:
        -
            location: 'room1'
            id: 43543
        -
            location: 'room2'
            id: 6476546
        -
            location: 'room3'
            id: 121231

另请注意,您的 yaml 数组语法一团糟。可能只是一个 copy/paste 问题。

这样就可以了。