Как получить доступ к параметрам приложения из сервиса?

62

Из моих контроллеров я обращаюсь к параметрам приложения (в /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%

или что-то подобное? Как мне получить доступ к моим параметрам приложения из службы?


Этот вопрос кажется таким же, но мой на самом деле отвечает на него (параметры с контроллера), я говорю о доступе от службы.

  • 0
    Возможный дубликат Как я могу прочитать из parameters.yml в контроллере в symfony2?
  • 0
    Мой вопрос на самом деле отвечает на этот (параметры из контроллера), я говорю о доступе из службы здесь
Показать ещё 3 комментария
Теги:
yaml

5 ответов

101
Лучший ответ

Вы можете передать параметры своей службе так же, как и другие сервисы, указав их в определении своей службы. Например, в YAML:

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

где %my_param1% и т.д. соответствует параметру с именем my_param1. Тогда ваш конструктор класса обслуживания может быть следующим:

public function __construct($myParam1, $myParam2)
{
    // ...
}
  • 0
    Достаточно просто. Спасибо
  • 0
    Есть ли способ обработки в случае, если параметр не существует? вместо симфонии исключение МОК.
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
    }
}

Добавьте @service_container в качестве "аргументов" в ваших services.yml

services:
  my_service_id:
    class: ...\MyServiceClass
    arguments: ["@service_container"]  // <- Add this
  • 1
    Именно то, что я искал, поэтому мне нравится внедрение зависимостей :)
  • 32
    -1. Передача контейнера полностью уничтожает цель внедрения зависимости. Вашему классу следует давать только то, что ему действительно нужно для работы, а не весь контейнер.
Показать ещё 5 комментариев
11

Чистый путь 2018

С 2017 года и Symfony 3.4 существует намного более чистый способ - легко настроить и использовать.

Вместо того, чтобы использовать анти-шаблон контейнера и сервиса/параметра, вы можете передавать параметры в класс через конструктор. Не беспокойтесь, это не трудоемкая работа, а настройка времени и забыть.

Как настроить его в 2 шага?

1. config.yml

# 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"
    }
}

Мгновенный апгрейд готов!

Если вы используете более старый подход, вы можете автоматизировать его с помощью ректора.

Прочитайте больше

Это называется инсталляцией конструктора по методу локализации сервисов.

Чтобы узнать больше об этом, проверьте мое сообщение. Как получить параметр в Symfony Controller "Чистый путь".

(Он протестирован, и я обновляю его для новой версии Symfony (5, 6...)).

  • 1
    В качестве примера кода я бы взял что-то еще, кроме класса контроллера, так как OP хочет внедрить параметры в любой сервис, а автоматическое подключение по умолчанию включено в контроллерах SF3.
  • 0
    Спасибо за ваш комментарий. Конфигурация выше работает для любого сервиса, контроллера, хранилища или собственного сервиса. Нет никакой разницы.
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;
    // ...
}
  • 0
    На момент написания этого комментария, к сожалению, в Symfony вложение параметров невозможно, см. Документы: symfony.com/doc/current/service_container/…
0

Symfony 3.4 здесь.

После некоторых исследований я не думаю, что передавать параметры классу/службе через конструктор, это всегда хорошая идея. Представьте себе, если вам нужно передать контроллеру/службе еще несколько параметров, чем 2 или 3. Что тогда? Было бы смешно проходить, допустим, до 10 параметров.

Вместо этого используйте класс ParameterBag как зависимость, объявляя службу в yml, а затем используйте столько параметров, сколько хотите.

Конкретный пример, скажем, у вас есть служба почтовой рассылки, такая как PHPMailer, и вы хотите иметь параметры подключения PHPMailer в файле paramters.yml:

#parameters.yml
parameters:
    mail_admin: [email protected]
    mail_host: mail.abc.com
    mail_username: [email protected]
    mail_password: pass
    mail_from: [email protected]
    mail_from_name: [email protected]
    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()
{
    //...
}

Я думаю, что это лучший способ.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню