将配置从 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 问题。
这样就可以了。
我的 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 问题。
这样就可以了。