如何从服务访问应用程序参数?


80

从我的控制器中,我使用来访问应用程序参数(中的/app/config

$this->container->getParameter('my_param')

但是我不知道如何从服务访问它(我想我的服务类不应该扩展Symfony\Bundle\FrameworkBundle\Controller\Controller)。

我是否应该像这样将所需的参数映射到我的服务注册中:

#src/Me/MyBundle/Service/my_service/service.yml
parameters:
    my_param1: %my_param1%
    my_param2: %my_param2%
    my_param3: %my_param3%

或类似的东西?我应该如何从服务访问我的应用程序参数?


这个问题似乎是一样的,但我的回答是实际的(来自控制器的参数),我说的是从服务访问。



我的问题实际上回答了这个问题(来自控制器的参数),我说的是这里的服务访问
Pierre de LESPINAY,

我不确定我是否理解你。您是否同意重复?控制器是当今Symfony中的服务。
托马什Votruba

我不同意重复的说法。另一个问题是专门询问Controller,该控制器很容易使用来获取参数$this->getParameter()
Pierre de LESPINAY,

是的,我同意。而且仍然是可能的。还有一种趋势,就是从任何地方注入容器,而转向构造函数注入。借助PSR-4服务的自动发现和参数绑定功能:symfony.com/blog/new-in-symfony-3-4-local-service-binding,它很干净而且使用起来更短。
托马什Votruba

Answers:


121

您可以通过在服务定义中指定参数,以与注入其他服务相同的方式将参数传递给服务。例如,在YAML中:

services:
    my_service:
        class:  My\Bundle\Service\MyService
        arguments: [%my_param1%, %my_param2%]

其中%my_param1%etc对应于名为的参数my_param1。然后,您的服务类构造函数可以是:

public function __construct($myParam1, $myParam2)
{
    // ...
}

如果参数不存在,有没有办法处理?代替symfony例外IOC。
Mohammed Yassine CHABLI

的价值my_param1从何而来?
Sliq

34

清洁之路2018

自2017年和Symfony 3.4起,存在更简洁的方法-易于设置和使用。

您可以通过其构造函数将参数传递给类,而不是使用容器和服务/参数定位符反模式。不用担心,这不是耗时的工作,而是只需设置一次,然后忘记方法。

如何分两步设置?

1。 config.yml

# config.yml
parameters:
    api_pass: 'secret_password'
    api_user: 'my_name'

services:
    _defaults:
        autowire: true
        bind:
            $apiPass: '%api_pass%'
            $apiUser: '%api_user%'

    App\:
        resource: ..

2.任何 Controller

<?php declare(strict_types=1);

final class ApiController extends SymfonyController
{
    /**
     * @var string 
     */
    private $apiPass;

    /**
     * @var string
     */
    private $apiUser;

    public function __construct(string $apiPass, string $apiUser)
    {
        $this->apiPass = $apiPass;
        $this->apiUser = $apiUser;
    }

    public function registerAction(): void
    {
        var_dump($this->apiPass); // "secret_password"
        var_dump($this->apiUser); // "my_name"
    }
}

即时升级就绪!

如果您使用较旧的方法,则可以使用Rector使其自动化

阅读更多

这称为服务定位器方法上的构造函数注入

要了解有关此内容的更多信息,请查看我的文章如何以简洁的方式在Symfony Controller中获取参数

(已经过测试,我会针对新的Symfony主版本(5、6 ...)保持更新)。


1
我本来会以控制器类之外的其他方式作为代码示例,因为OP希望在任何服务中注入参数,并且默认情况下在SF3控制器中启用了自动装配功能
alpadev

谢谢你的评论。上面的配置适用于任何服务,控制器,存储库或自己的服务。没有区别。
托马什Votruba

18

为什么不让服务直接访问容器,而不是一一对应地映射所需的参数?这样做,如果添加了新参数(与您的服务有关),则不必更新映射。

为此:

对您的服务等级进行以下更改

use Symfony\Component\DependencyInjection\ContainerInterface; // <- Add this

class MyServiceClass
{
    private $container; // <- Add this
    public function __construct(ContainerInterface $container) // <- Add this
    {
        $this->container = $container;
    }
    public function doSomething()
    {
        $this->container->getParameter('param_name_1'); // <- Access your param
    }
}

在您的services.yml中将@service_container添加为“参数”

services:
  my_service_id:
    class: ...\MyServiceClass
    arguments: ["@service_container"]  // <- Add this

1
正是我在寻找什么,这就是为什么我喜欢依赖注入:)
klimpond

43
-1。整个传递容器会破坏依赖项注入的目的。应该只给您的班实际需要的东西,而不是整个容器。
richsage 2015年

@richsage,是否有其他方法可以达到类似的结果-因此不会为每个参数更新服务声明?与逐个注入参数相比,这看起来也更整洁。
Batandwa 2015年

1
将整个容器传递给服务是一个非常糟糕的主意。正如@richsage所说,它不适合依赖注入的目的。如果您不想使用依赖注入,请不要使用Symfony2 :)
tersakyan

2
@tersakyan,但是控制器呢?默认情况下,所有控制器都可以访问控制器。那我们不应该也使用控制器吗?:)
Alex Zheka's

8

从symfony 4.1开始,有一种非常干净的新方法可以实现它

<?php
// src/Service/MessageGeneratorService.php

use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

class MessageGeneratorService
{
 private $params;
 public function __construct(ParameterBagInterface $params)
 {
      $this->params = $params;
 }
 public function someMethod()
 {
     $parameterValue = $this->params->get('parameter_name');
...
 }
}

来源:https : //symfony.com/blog/new-in-symfony-4-1-getting-container-parameters-as-a-service


6

为了解决上述问题,我定义了一个数组参数,然后注入了它。以后添加新参数只需要添加参数数组,而无需更改service_container参数或构造。

因此,扩展@richsage答案:

parameters.yml

parameters:
    array_param_name:
        param_name_1:   "value"
        param_name_2:   "value"

services.yml

services:
    my_service:
        class:  My\Bundle\Service\MyService
        arguments: [%array_param_name%]

然后进入课堂

public function __construct($params)
{
    $this->param1 = array_key_exists('param_name_1',$params)
        ? $params['param_name_1'] : null;
    // ...
}

在写这篇评论的时候,不幸的是,则params的筑巢的Symfony是不可能的,看文档:symfony.com/doc/current/service_container/...
托马什Votruba

5

使用Symfony 4.1,解决方案非常简单。

这是原始文章的摘录:

// src/Service/MessageGenerator.php
// ...

use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

class MessageGenerator
{
    private $params;

    public function __construct(ParameterBagInterface $params)
    {
        $this->params = $params;
    }

    public function someMethod()
    {
        $parameterValue = $this->params->get('parameter_name');
        // ...
    }
}

链接到原始帖子:https : //symfony.com/blog/new-in-symfony-4-1-getting-container-parameters-as-a-service


0

@richsage是正确的(对于Symfony 3.?),但不适用于我的Symfony4.x。这就是Symfony 4。

在services.yaml文件中

parameters:
    param1: 'hello'

Services:
    App\Service\routineCheck:
            arguments:
                $toBechecked: '%param1%'  # argument must match in class constructor

在您的服务类的tinetageCheck.php文件中,像这样进行构造

private $toBechecked;

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

public function echoSomething()
{
    echo $this->toBechecked;
}

做完了


您能进一步解释吗?到底什么与其他解决方案不兼容-是否给出任何错误消息?
Nico Haase

他在构造函数中使用了ParameterBagInterface $ params,但是为了充分利用services.yaml中的参数配置,我使用了依赖注入。

您能进一步解释吗?richsage的答案不包含ParameterBagInterface,而是要注入的参数列表,就像您的代码一样
Nico Haase

我的答案发布于2012年,当时的生态系统仅为Symfony2。我不再使用Symfony,因此没有为后续版本进行更新。
richsage

-1

Symfony 3.4在这里。

经过一些研究,我认为通过参数的构造函数将参数传递给类/服务始终不是一个好主意。想象一下,如果您需要传递给控制器​​/服务的参数多于2或3,那么该怎么办?假设最多传递10个参数,这将是荒谬的。

相反,ParameterBag在yml中声明服务时,使用该类作为依赖项,然后根据需要使用尽可能多的参数。

一个具体的例子,假设您有一个邮件服务,例如PHPMailer,并且您想要在paramters.yml文件中包含PHPMailer连接参数:

#parameters.yml
parameters:
    mail_admin: abc@abc.abc
    mail_host: mail.abc.com
    mail_username: noreply@abc.com
    mail_password: pass
    mail_from: contact@abc.com
    mail_from_name: contact@abc.com
    mail_smtp_secure: 'ssl'
    mail_port: 465

#services.yml
services:
    app.php_mailer:
        class: AppBundle\Services\PHPMailerService
        arguments: ['@assetic.parameter_bag'] #here one could have other services to be injected
        public: true

# AppBundle\Services\PHPMailerService.php
...
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
...
class PHPMailerService
{
    private $parameterBag;
    private $mailAdmin;
    private $mailHost;
    private $mailUsername;
    private $mailPassword;
    private $mailFrom;
    private $mailFromName;
    private $mailSMTPSecure;
    private $mailPort;
}
public function __construct(ParameterBag $parameterBag)
{
    $this->parameterBag = $parameterBag;

    $this->mailAdmin      = $this->parameterBag->get('mail_admin');
    $this->mailHost       = $this->parameterBag->get('mail_host');
    $this->mailUsername   = $this->parameterBag->get('mail_username');
    $this->mailPassword   = $this->parameterBag->get('mail_password');
    $this->mailFrom       = $this->parameterBag->get('mail_from');
    $this->mailFromName   = $this->parameterBag->get('mail_from_name');
    $this->mailSMTPSecure = $this->parameterBag->get('mail_smtp_secure');
    $this->mailPort       = $this->parameterBag->get('mail_port');
}
public function sendEmail()
{
    //...
}

我认为这是一种更好的方法。


-1

在symfony 4中,我们可以通过依赖注入来访问参数:

服务:

   use Symfony\Component\DependencyInjection\ContainerInterface as Container;

   MyServices {

         protected $container;
         protected $path;

         public function __construct(Container $container)
         {
             $this->container = $container;
             $this->path = $this->container->getParameter('upload_directory');
         }
    }

parameters.yml:

parameters:
     upload_directory: '%kernel.project_dir%/public/uploads'

提供的代码未正确使用DI-注入整个容器被认为是不良样式,因为您隐藏了真正的依赖项
Nico Haase

我认为您会误解这些概念,在示例中,我仅显示一个一般情况。如有疑问,请在投票前查阅symfony的官方文档。symfony.com/doc/current/components/dependency_injection.html
shades3002

您能进一步解释吗?链接的文件明确指出,注入容器是不是一个好主意,不表明使用这种类型的注射任何例子-因为很显然,你是不是注入依赖当你注入整个容器
尼科·哈泽
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.