Як отримати доступ до параметрів програми із сервісу?


81

З моїх контролерів я отримую доступ до параметрів програми (тих, що знаходяться /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%

чи щось подібне? Як мені отримати доступ до параметрів моєї програми зі служби?


Це питання здається однаковим, але мій насправді відповідає на нього (параметри від контролера), я кажу про доступ із сервісу.



Моє запитання насправді відповідає на це (параметри від контролера), я говорю про доступ із сервісу тут
П’єр де ЛЕСПІН

Я не впевнений, що я вас розумію. Чи погоджуєтесь ви з дублікатами? Контролери - це послуги в Symfony сьогодні.
Томаш Вотруба 03.03.18

Я не згоден з дублікатом. Інше питання - це конкретно прохання до контролерів, за допомогою яких легко отримувати параметри $this->getParameter().
Pierre de LESPINAY

Це правда, я згоден. І це ще можливо. Також є тенденція відходити від контейнера, який впорскується куди завгодно, і переходити до інжектора конструктора. Завдяки автоматичному виявленню служби PSR-4 та прив'язуванню параметрів: symfony.com/blog/new-in-symfony-3-4-local-service-binding , він чистий і набагато коротший для роботи.
Томаш Вотруба

Відповіді:


123

Ви можете передавати параметри своїй службі так само, як вводите інші служби, вказуючи їх у своєму визначенні послуги. Наприклад, у YAML:

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

де %my_param1%etc відповідає іменованому параметру my_param1. Тоді конструктором класу сервісів тоді може бути:

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

чи існує спосіб обробки у випадку, якщо параметр не існує? istead виключення symfony IOC.
Мохаммед Ясін ЧАБЛІ

і звідки береться цінність my_param1?
Sliq

@Sliq, ти визначаєш це в параметрах .yml
Графік

35

Чистий шлях 2018

З 2018 року та Symfony 3.4 існує набагато чистіший спосіб - простий у налаштуванні та використанні.

Замість використання анти-шаблону контейнера та служби / локатора параметрів, ви можете передавати параметри класу через його конструктор . Не хвилюйтеся, це не вимагає часу робота, а швидше налаштуйте один раз і забудьте про підхід.

Як налаштувати його в 2 кроки?

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

Миттєве оновлення готове!

Якщо ви використовуєте старіший підхід, ви можете автоматизувати його за допомогою Ректора .

Читати далі

Це називається підходом конструктора до підходу до локатора послуг .

Щоб прочитати більше про це, перегляньте мій допис Як отримати параметр у контролері Symfony чистим способом .

(Перевірено, і я постійно оновлюю його для нової основної версії Symfony (5, 6 ...)).


1
Я б взяв щось інше, ніж клас контролера, як приклад коду, оскільки OP хоче вводити параметри в будь-яку службу, а автоматичне підключення за замовчуванням увімкнено в контролерах
SF3

Дякуємо за ваш коментар. Наведена вище конфігурація працює для будь-якої служби, контролера, сховища або власної служби. Різниці немає.
Томаш Вотруба

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
Саме те, що я шукав, тому мені подобається введення залежності :)
klimpond

44
-1. Передача контейнера в цілому перемагає мету введення залежності. Вашому класу слід давати лише те, що йому насправді потрібно для роботи, а не весь контейнер.
Річмадж

@richsage, чи існує альтернатива для досягнення подібних результатів - тож декларація служби не оновлюється для кожного параметра? Це також виглядає дещо акуратніше, ніж введення параметрів один за одним.
Батандва

1
Передати цілий контейнер службі - це дійсно погана ідея. Як каже @richsage, це не відповідає цілям введення залежності. Якщо ви не хочете використовувати ін'єкцію залежностей, тоді не використовуйте Symfony2 :)
tersakyan

2
@tersakyan, а як тоді контролери? за замовчуванням усі контролери мають доступ до контролера. Тоді чи не слід нам також використовувати контролери? :)
Алекс Жека

9

Існує дуже чистий новий спосіб досягти цього, починаючи з 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:
    array_param_name:
        param_name_1:   "value"
        param_name_2:   "value"

послуги.имм

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;
    // ...
}

На момент написання цього коментаря, на жаль, вкладання параметрів у Symfony неможливо, див. Документи: symfony.com/doc/current/service_container/…
Томаш Вотруба

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.?), але для мого Symfony 4.x він не працював. Отже, для Symfony 4.

у файлі services.yaml

parameters:
    param1: 'hello'

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

у вашому файлі класу serviceCheck.php виконайте конструктор так

private $toBechecked;

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

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

Готово.


Ви можете пояснити це далі? Що саме не працювало з іншим рішенням - чи є повідомлення про помилки?
Ніко Хаасе,

Він використовував ParameterBagInterface $ params у своєму конструкторі, але для повного використання конфігурації параметрів у services.yaml я використовував ін'єкцію залежностей.
Гній

Ви можете пояснити це далі? Відповідь richsage містить не той ParameterBagInterface, а список параметрів, які потрібно вводити, як і ваш код
Ніко Хаасе,

Моя відповідь була опублікована в 2012 році, коли екосистемою був лише Symfony 2. Я більше не використовую Symfony, тому не оновлювався для наступних версій.
Річседж

-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 належним чином - введення цілого контейнера вважається поганим стилем, оскільки ви приховуєте реальні залежності
Ніко Хаасе

Я думаю, ви помиляєтесь з поняттями, у цьому прикладі я наводжу лише загальний випадок. Якщо ви сумніваєтесь, перед тим, як голосувати, ознайомтеся з офіційною документацією до Symfony. symfony.com/doc/current/components/dependency_injection.html
відтінки3002

Ви можете пояснити це далі? У зв’язаній документації чітко зазначено, що впорскувати контейнер - це не гарна ідея і не показує жодного прикладу, який використовує цей тип впорскування - так само очевидно, що ви не вводите залежності, коли
Ніко Хаазе
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.