我必须在不同的环境中将具有不同值的参数绑定在一起,并且遇到问题。
我正在尝试:
# config/services.yaml
services:
_defaults:
bind:
$param: 'param for PROD'
# config/services_dev.yaml
services:
_defaults:
bind:
$param: 'param for DEV'
# src/Controller/SomeController.php
class MyController extends AbstractController
{
public function example($param)
{
echo $param;
}
}
但是,这迫使我必须在 services.yaml 和 services_dev.yaml 文件中都定义所有服务,否则它将无法正常工作。
我想为任何环境共享一个 services.yaml ,并且仅覆盖自定义服务/绑定等,不具有两个相同的文件,其中列出了用于更改一个绑定值的所有服务
真正的问题是我必须创建两个具有相同接口的http客户端(真实和虚拟),在生产中加载真实的一个,在开发中加载虚拟,Symfony 4-s自动装配允许我注入接口在控制器中,然后选择要在绑定中使用的客户端:
# config/services.yaml
services:
_defaults:
bind:
'ClientInterface': '@real_client'
# More services here...
# config/services_dev.yaml
services:
_defaults:
bind:
'ClientInterface': '@dummy_client'
# Here I don't want to have another copy of the services,
# but it does not work without them
# Controller
public function someMethod(ClientInterface $client)
{
// ...
}
在Symfony 2中,我能够扩展services.yml,而在services_dev.yml中仅定义要覆盖/添加的特定值,但是在Symfony 4中, services_dev.yaml 无法使用以下服务: services.yaml ,我必须在两个不同的文件中保持相同的服务,这很痛苦。
有建议吗?
谢谢。
我将用一个真实的例子再次更新帖子:
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
locale: 'en'
app.access_token: '%env(string:APP_ACCESS_TOKEN)%'
app.aws_version: '%env(string:AWS_VERSION)%'
app.aws_profile: '%env(string:AWS_PROFILE)%'
app.aws_region: '%env(string:AWS_REGION)%'
app.aws_queue_url_creation: '%env(string:AWS_QUEUE_URL_CAMPAIGN_CREATION)%'
app.aws_queue_url_edition: '%env(string:AWS_QUEUE_URL_CAMPAIGN_EDITION)%'
app.redis_host: '%env(string:REDIS_HOST)%'
app.redis_port: '%env(string:REDIS_PORT)%'
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.
public: false # Allows optimizing the container by removing unused services; this also means
# fetching services directly from the container via $container->get() won't work.
# The best practice is to be explicit about your dependencies anyway.
bind:
App\Service\MessageSenderServiceInterface: '@App\Service\MessageSenderSqsService'
# 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/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
# Authenticators
App\Security\ApiKeyAuthenticator:
arguments:
- "%app.access_token%"
# Clients
App\Client\AwsSqsClient:
arguments:
- "%app.aws_version%"
- "%app.aws_profile%"
- "%app.aws_region%"
App\Client\RedisClient:
arguments:
- "%app.redis_host%"
- "%app.redis_port%"
# Services
App\Service\MessageSenderSqsService:
arguments:
- '@App\Client\AwsSqsClient'
- '@App\Client\RedisClient'
- "%app.aws_queue_url_creation%"
- "%app.aws_queue_url_edition%"
App\Service\MessageSenderRedisService:
arguments:
- '@App\Client\RedisClient'
imports:
- { resource: services.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.
public: false # Allows optimizing the container by removing unused services; this also means
# fetching services directly from the container via $container->get() won't work.
# The best practice is to be explicit about your dependencies anyway.
bind:
App\Service\MessageSenderServiceInterface: '@App\Service\MessageSenderRedisService'
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class TestController extends AbstractController
{
/**
* @Route("/api/dummy")
*/
public function dummyEndpoint(MessageSenderServiceInterface $messageSender)
{
echo get_class($messageSender); exit;
}
}
这两个环境(prod和dev)从控制器的回波是
App\Service\MessageSenderSqsService
但是如果我将整个节点“ services”从services.yaml复制到services_dev.yaml并仅更改绑定配置,它会正常工作,并说注入的类是:
App\Service\MessageSenderRedisService
我刚刚注意到,如果我不触摸“ _defaults”节点,它会按预期工作,那么只有当我想覆盖服务的“ _defaults”节点时,问题才会开始...
答案 0 :(得分:1)
最后,问题仅在于覆盖“ _defaults”节点(为了使项目中具有不同的“绑定”配置,我正在触摸该节点)。
在不覆盖 _defaults 的情况下扩展services.yaml,一切正常。解决方案是对服务进行不同的配置(按环境绑定),并且仅在services.yaml中使用“ _defaults”。
如果我们在其他文件中覆盖“ _defaults”,则也必须重新定义所有服务。
感谢大家的帮助。
答案 1 :(得分:0)
您可以在parameters
的{{1}}部分中定义参数,并在config.yml
中覆盖此参数。
config_dev.yml
此参数可以在# config.yml
imports:
# ...
parameters:
parameter_1: value 1
parameter_2: value 2
# ...
framework:
# ...
# config_dev.yml
imports:
# ...
parameters:
parameter_1: dev value 1
# ...
framework:
# ...
中用作:
service.yml
答案 2 :(得分:0)
您有一些选择:
1。请勿使用bind
并为不同的环境编写不同的服务配置
# services.yaml
App\Controller:
arguments:
- "@client"
# services_dev.yaml
App\Controller:
arguments:
- "@dummy_client"
2。使用bind
并在每个环境的services.yaml
中创建服务别名:
# services.yaml
services:
some.client:
alias: "@client"
# services_dev.yaml
services:
some.client:
alias: "@dummy_client"
3。只需为每个环境配置一项ClientInterface
服务:
# services.yaml
App\ClientInterface:
class: App\RealClient
# services_dev.yaml
App\ClientInterface:
class: App\DummyClient
4。使用工厂来创建此客户端取决于环境(但这对我而言不是很好的做法)
# services.yaml
App\ClientInterface:
factory: ["@App\ClientFactory", create]
arguments:
- '%kernel.environment%'
class ClientFactory
{
public function create(string $env): ClientInterface
{
if ($env === 'dev') {
return new DummyClient();
} else {
return new Client();
}
}
}
5。在您的情况下,如果您有这么多服务,并且想在所有服务中注入相同的服务,则可以使用选项#3,也可以为所有服务创建一个接口并使用_instanceof
:
# services.yaml
_instanceof:
App\SomeCommonInterface:
calls:
- method: setSomeService # interface's method
arguments:
- '@service'
# services_dev.yaml
_instanceof:
App\SomeCommonInterface:
calls:
- method: setSomeService
arguments:
- '@dummy_service'