Сервіс-контейнер

Дата оновлення перекладу 2022-12-23

Сервіс-контейнер

Screencast

Ви віддаєте перевагу відео-урокам? Подивіться Symfony Fundamentals screencast series.

Ваш застосунок повний корисних об'єктів: об'єкт "Mailer" може допомогти вам відправляти електронні листи, в той час як інший об'єкт може допомогти вам зберігати дані у базу даних. Майже все, що "робить" ваш застосунок, насправді виконується одним з цих об'єктів. І кожний раз, коли ви встановлюєте новий пакет, ви отримуєте доступ до нових об'єктів!

У Symfony, ці корисні об'єкти називаються сервісами, і кожний сервіс живе всередині дуже особливого об'єкта під назвою сервіс-контейнер. Контейнер дозволяє вам централізувати те, як створюються об'єкти. Він робить ваше життя легшим, пропагує сильну архітектуру, а також дуже швидкий!

Отримання та використання сервісів

В той момент, коли ви запускаєте застосунок Symfony, ваш контейнер вже містить багато сервісів. Вони дуже схожі на інструменти: чекають, поки ви скористаєтеся ними. У вашому контролері ви можете "запитати" сервіс з контейнера, шляхом додавання підказки аргументу з класом сервісу або іменем інтерфейса. Хочете записати лог чогось? Не проблема:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/Controller/ProductController.php
namespace App\Controller;

use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ProductController extends AbstractController
{
    #[Route('/products')]
    public function list(LoggerInterface $logger): Response
    {
        $logger->info('Look, I just used a service!');

        // ...
    }
}

Які ще сервіси існують? Дізнайтеся, запустивши:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ php bin/console debug:autowiring

  # це лише *маленький* приклад виведення...

  Автомонтовані типи
  ==================

   Наступні класи та інтерфейси можуть бути використані як підказки під час автомонтування:

   Описати екземпляр логера.
   Psr\Log\LoggerInterface (monolog.logger)

   Запросити стек, що контролює життєвий цикл запитів.
   Symfony\Component\HttpFoundation\RequestStack (request_stack)

   RouterInterface - це інтерфейс, який мають реалізовувати усі класи Router.
   Symfony\Component\Routing\RouterInterface (router.default)

  [...]

Коли ви використовуєте ці підказки у своїх методах контролера або всередині ваших власних сервісів , Symfony буде автоматично передавати вам об'єкт сервісу, відповідний до цього типу.

Читаючи документи, ви побачите як використовувати багато різних сервісів, які живуть у контейнері.

Tip

Насправді існує багато інших сервісів у контейнері, і кожний сервіс має у ньому унікальний id, наприклад, request_stack або router.default. Щоб побачити повний список, ви можете виконати php bin/console debug:container. Але у більшості випадків вам не треба буде про це турбуватися. Див. . Див. Як налагоджувати сервіс-контейнер та список сервісів.

Створення/конфігурація сервісів у контейнері

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/Service/MessageGenerator.php
namespace App\Service;

class MessageGenerator
{
    public function getHappyMessage(): string
    {
        $messages = [
            'You did it! You updated the system! Amazing!',
            'That was one of the coolest updates I\'ve seen all day!',
            'Great work! Keep going!',
        ];

        $index = array_rand($messages);

        return $messages[$index];
    }
}

Вітаємо! Ви щойно створили ваш перший клас сервісів! Ви можете використати його одразу ж у вашому контролері:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// src/Controller/ProductController.php
use App\Service\MessageGenerator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ProductController extends AbstractController
{
    #[Route('/products/new')]
    public function new(MessageGenerator $messageGenerator): Response
    {
    // завдяки підказці, контейнер викличе
    // новий MessageGenerator і передасть його вам!
    // ...

    $message = $messageGenerator->getHappyMessage();
    $this->addFlash('success', $message);
    // ...
}

Коли ви запитаєте сервіс MessageGenerator, контейнер створить новий об'єкт MessageGenerator та поверне його (дивіться пояснення нижче). Але якщо ви ніколи не запитаєте цей сервіс, він ніколи не буде створений: економія пам'яті та швидкості. В якості бонусу, сервіс MessageGenerator створюється лише один раз: кожний раз, коли ви запитуєте його, вам повертається один і той самий екземпляр.

Документація припускає, що ви використовуєте наступну конфігурацію сервісу, яка є конфігурацією за замовчуванням для нового проекту:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# config/services.yaml
services:
    # конфігурація дл сервісів за замовчуванням у *цьому* файлі
    _defaults:
        autowire: true      # Автоматично впровпаджує залежності у ваші сервіси.
        autoconfigure: true # Автоматично реєструє ваші сервіси як команди, підписники подій і т.д.

    # робить класи в src/ доступними для використання в якості сервісів
    # створює по сервісу у класі, чий id є повним іменем класу
    App\:
        resource: '../src/'
        exclude:
            - '../src/DependencyInjection/'
            - '../src/Entity/'
            - '../src/Kernel.php'

    # порядок у цьому файлі важливий, тому що визначення сервісів завжди
    # *заміняють* попередні; додайте вашу власну конфігурацію сервісу нижче

    # ...

Tip

Значення опцій resource і exclude може бути будь-яким валідним глобальним патерном. Значення опції exclude також може бути масивом глобальних патернів.

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

Якщо ви віддаєте перевагу створенню сервісу вручну, це також можливо: див. .

Обмеження сервісів для конкретного середовища Symfony

Якщо ви використовуєте PHP 8.0 або новіше, ви можете використати PHP-атрибут #[When], щоб зареєструвати клас в якості сервісу лише у деяких середовищах:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Symfony\Component\DependencyInjection\Attribute\When;

// SomeClass реєструється лише у середовищі "dev"

#[When(env: 'dev')]
class SomeClass
{
    // ...
}

// ви також можете застосувати більше одного атрибута When до одного класу

#[When(env: 'dev')]
#[When(env: 'test')]
class AnotherClass
{
    // ...
}

Впровпадження сервісів/конфігурації у сервіс

Що, якщо вам потрібно отримати доступ до сервісу logger з MessageGenerator? Не проблема! Створіть метод __construct() з аргументом $logger, який має підказку LoggerInterface. Встановіть це у новій властивості $logger та використайте його пізніше:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// src/Service/MessageGenerator.php
namespace App\Service;

use Psr\Log\LoggerInterface;

class MessageGenerator
{
    private $logger;

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

    public function getHappyMessage(): string
    {
        $this->logger->info('About to find a happy message!');
        // ...
    }
}

Ось і все! Контейнер автоматично знатиме, що йому потрібно передати сервіс logger при завантаженні MessageGenerator. Звідки він це знає? Автомонтування . Ключом є підказка LoggerInterface у вашому методі __construct() і конфігурація autowire: true у services.yml. Коли ви додаєте підказку до аргументу, контейнер автоматично шукатиме відповідний сервіс. Якщо це не вдасться, ви побчите чітке виключення з пропозицією допомоги.

До речі, цей метод додавання залежностей у ваш метод __construct(), називається впровадженням залежностей.

Як вам знати, що слід використати LoggerInterface для підказки? Ви можете або прочитати документи для тієї функцій, яку ви використовуєте, або отримати список автоматичного додавання підказок, викликавши:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ php bin/console debug:autowiring

  # це лише *маленький* приклад виведення...

  Describes a logger instance.
  Psr\Log\LoggerInterface (monolog.logger)

  Request stack that controls the lifecycle of requests.
  Symfony\Component\HttpFoundation\RequestStack (request_stack)

  RouterInterface is the interface that all Router classes must implement.
  Symfony\Component\Routing\RouterInterface (router.default)

  [...]

Управління багатьма сервісами

Припустимо, що ви також хочете писати email адміністратору кожний раз, коли сайт оновлюється. Щоб зробити це, створіть новий клас:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// src/Service/SiteUpdateManager.php
namespace App\Service;

use App\Service\MessageGenerator;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;

class SiteUpdateManager
{
    private $messageGenerator;
    private $mailer;

    public function __construct(MessageGenerator $messageGenerator, MailerInterface $mailer)
    {
        $this->messageGenerator = $messageGenerator;
        $this->mailer = $mailer;
    }

    public function notifyOfSiteUpdate(): bool
    {
        $happyMessage = $this->messageGenerator->getHappyMessage();

        $email = (new Email())
            ->from('admin@example.com')
            ->to('manager@example.com')
            ->subject('Site update just happened!')
            ->text('Someone just updated the site. We told them: '.$happyMessage);

        $this->mailer->send($email);

        // ...

        return true;
    }
}

Це використовує сервіси MessageGenerator і Mailer. Це не проблема, ми запитуємо їх використовуючи підказки їх класів та імен інтерфейсів! Тепер цей новий сервіс готовий до використання. У контролері, наприклад, ви можете використовувати підказки нового класу SiteUpdateManager та використати його:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// src/Controller/SiteController.php
namespace App\Controller;

use App\Service\SiteUpdateManager;
// ...

class SiteController extends AbstractController
{
    public function new(SiteUpdateManager $siteUpdateManager)
    {
        // ...

        if ($siteUpdateManager->notifyOfSiteUpdate()) {
            $this->addFlash('success', 'Notification mail was sent successfully.');
        }

        // ...
    }
}

Завдяки автомонтуванню та підказкам у __construct(), контейнер створює об'єкт SiteUpdateManager і передає йому правильний аргумент. У більшості випадків це працює ідеально.

Підключення аргументів вручу

Але існують деякі випадки, коли аргумент не може бути автоматично підключений до сервісу. Наприклад, уявіть, що ви хочете зробити email адміну конфігурованим:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// src/Service/SiteUpdateManager.php
  // ...

  class SiteUpdateManager
  {
      // ...
+    private $adminEmail;

-    public function __construct(MessageGenerator $messageGenerator, MailerInterface $mailer)
+    public function __construct(MessageGenerator $messageGenerator, MailerInterface $mailer, string $adminEmail)
      {
          // ...
+        $this->adminEmail = $adminEmail;
      }

      public function notifyOfSiteUpdate(): bool
      {
          // ...

          $email = (new Email())
              // ...
-            ->to('manager@example.com')
+            ->to($this->adminEmail)
              // ...
          ;
          // ...
      }
  }

Якщо ви внесети ці зміни та оновите його, ви побачите помилку:

Cannot autowire service "AppBundleUpdatesSiteUpdateManager": argument "$adminEmail" of method "__construct()" must have a type-hint or be given a value explicitly. (Неможливо автоматично підключити сервіс "AppBundleUpdatesSiteUpdateManager": аргумент "$adminEmail" методу "__construct()" повинен мати підказку або точно задане значення).

Це має сенс! Не може бути так, щоб контейнер знав, яке значення ви хочете тут передати. Не проблема! У вашій конфігурації ви можете точно встановити цей аргумент:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
# config/services.yaml
services:
    # ... те ж саме, що і раніше

    # те ж саме, що і раніше
    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Tests,Kernel.php}'

    # чітко сконфігуруйте сервіс
    App\Service\SiteUpdateManager:
        arguments:
            $adminEmail: 'manager@example.com'

Завдяки цьому, контейнер передаватиме manager@example.com аргументу $adminEmail в __construct при створенні сервісу SiteUpdateManager. Інші аргументи всеодно будуть автоматизовані.

Але хіба це не крихко? На щастя - ні! Якщо ви перейменуєте аргумент $adminEmail на щось інше, наприклад, $mainEmail, ви отримаєте чітке виключення при перезавантаженні наступной сторінки (навіть якщо сторінки не використовують цей сервіс).

Параметри сервісу

На додаток до об'єктів сервісу, контейнер також містить конфігурацію під назвою параметри. Основна стаття про конфігурацію Symfony детально пояснює параметри конфігурації та демонструє всі їхні типи (параметри рядку, булевого значення, масиву, бінарного значення та PHP-констант).

Однак існує інший тип параметра, пов'язаний з сервісами. У конфігурації YAML, будь-який рядок, що починається з @, вважається ID сервісу, а не звичайним рядком. У конфігурації XML використовуйте тип type="service" для параметра, а у конфігурації PHP - функцію service():

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
# config/services.yaml
services:
    App\Service\MessageGenerator:
        arguments:
            # це не рядок, а посилання на сервіс під назвою 'logger'
            - '@logger'

            # якщо значення аргументу рядку починається з '@', вам потрібно екранувати його,
            # додавши ще один '@', щоб Symfony не вважала її сервісом
            # наступний приклад буде проаналізований як рядок '@securepassword'
            # - '@@securepassword'

Робота з параметрами контейнера пряма при використанні методів доступу контейнера для параметрів:

1
2
3
4
5
6
7
8
// перевіряє, чи визначено параметр (імена параметрів чутливі до регістру)
$container->hasParameter('mailer.transport');

// отримує значення параметра
$container->getParameter('mailer.transport');

// додає новий параметр
$container->setParameter('mailer.transport', 'sendmail');

Caution

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

Note

Ви можете встановити параметр лише до компіляції контейнера, а не під час його роботи. Щоб дізнатися більше про компіляцію контейнера, див. Компіляція контейнера.

Вибір конкретного сервісу

Сервіс MessageGenerator, створений раніше, вимагає аргументу LoggerInterface:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/AppBundle/Service/MessageGenerator.php
// ...

use Psr\Log\LoggerInterface;

class MessageGenerator
{
    private $logger;

    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }
    // ...
}

Однак, існує багато сервісів у контейнері, які реалізують LoggerInterface, наприклад, logger, monolog.logger.request, monolog.logger.php, та ін. Як контейнер знає, які використовувати?

У таких ситуаціях, контейнер зазвичай сконфігурований так, щоб автоматично обирати один з сервісів - у цьому випадку logger (дізнайтеся більше, чому в ). Але ви можете контролювати це і передати інший логер:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
# app/config/services.yml
services:
    # ... той же код, що і раніше

    # чітко сконфігуруйте сервіс
    AppBundle\Service\MessageGenerator:
        arguments:
            $logger: '@monolog.logger.request'

Це повідомляє контейнеру, що аргумент $logger для __construct має використовувати сервіс, id якого monolog.logger.request.

Щоб побачити повний список усіх можливих сервісів у контейнері, виконайте:

1
$ php bin/console debug:container

Зв'язування аргументів за іменем або типом

Ви також можете використати ключове слово bind, щоб зв'язати конкретні аргументи за іменем або типом:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# config/services.yaml
services:
    _defaults:
        bind:
            # передайте це значення будь-якому аргументу $adminEmail для будь-якого сервісу,
            # визначеного в цьому файлі (включно з аргументами контролера)
            $adminEmail: 'manager@example.com'

            # передайте цей сервіс будь-якому аргументу $requestLogger для будь-якого сервісу,
            # визначеного в цьому файлі
            $requestLogger: '@monolog.logger.request'

            # передайте цей сервіс будь-якій підказці LoggerInterface для будь-якого сервісу,
            # визначеного в цьому файлі
            Psr\Log\LoggerInterface: '@monolog.logger.request'

            # за бажанням ви можете винзачити ім'я та тип аргументу для співставлення
            string $adminEmail: 'manager@example.com'
            Psr\Log\LoggerInterface $requestLogger: '@monolog.logger.request'
            iterable $rules: !tagged_iterator app.foo.rule

    # ...

Помістивши ключ bind під _defaults, ви можете вказати значення будь-якого аргументу для будь-якого сервісу, визначеного у цьому файлі! Ви можете зв'язати аргументи за іменем (наприклад, $adminEmail), за типом (наприклад, Psr\Log\LoggerInterface) або і за тим, і за тим (наприклад, Psr\Log\LoggerInterface $requestLogger).

Конфігурація bind також може бути застосована до конкретних сервісів або при завантаженні багатьох сервісів одночасно (тобто ).

Опція автомонтування

Вище, файд services.yml має autowire: true у розділі _defaults, так що це відноситься до усіх сервісів, визначених у цьому файлі. З цим налаштуванням ви можете додавати підказки до аругментів у методі __construct() ваших сервісів, і контейнер автоматично передаватиме вам правильні аргументи. Весь цей запис було написано без автомонтування.

Щоб дізнатися більше про автомонтування, див. Автоматичне визначення залежностей сервісу (автомонтування).

Опція автоконфігурації

Вище, файд services.yml має autowire: true у розділі _defaults, так що це відноситься до усіх сервісів, визначених у цьому файлі. З цим налаштуванням контейнер автоматично застосовуватиме визначену конфігурацію до ваших сервісів, засновуючись на класі вашого сервісу. Це частіше за все використовується для автотегування ваших сервісів.

Наприклад, щоб створити розширення Twig, вам потрібно створити клас, зареєструвати його в якості сервісу, та тегувати його twig.extension:

Однак з autoconfigure: true, вам не потрібний тег. Насправді, якщо ви використовуєте конфігурацію Symfony Standard Edition services.yml , вам не потрібно нічого робити: сервіс буде завантажено автоматично. Потім, autoconfigure додасть тег twig.extension за вас, так як ваш клас реалізує Twig_ExtensionInterface. І завдякиautowire ви навіть можете додати аргументи-конструктори без будь-якої конфігурції.

Перевірка дотримання стандартів кодування визначень сервісу

Команда lint:container перевіряє, щоб аргументи, впроваджені у сервіси, відповідали їх типам оголошень. Корисно виконувати її до розгортання вашого додатку у виробництво (наприклад, у вашому постійному сервері інтеграції):

1
$ php bin/console lint:container

Перевірка всіх типів всіх аргументів сервісу кожний раз при компіляції контейнера може зашкодити продуктивності. Тому така перевірка реалізується у пропуску компілятора під назвою CheckTypeDeclarationsPass, який вимкнено за замовчуванням, і який вмикається лише при виконанні команди lint:container. Якщо вам не лякая втрата продуктивності, увімкніть пропуск комплілятора у вашому додатку.

Публічні сервіси проти приватних

Кожний сервіс визначено приватним за замовчуванням. Коли сервіс приватний, ви не можете отримати до нього доступ напряму з контейнера, використовуючи $container->get(). Кращою практикою буде створення лише приватних сервісів, і вам варто отримувати сервіси, використовуючи впровадження залежностей, а не $container->get().

Якщо вам потрібно отримувати сервіси ліниво, замість використання публічних сервісів, вам варто розглянути використання локатора сервісів .

Але якщо вам потрібно зробити сервіс публічним, перевизначіть налаштування public:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
# config/services.yaml
services:
    # ... той же код, що і раніше

    # чітко сконфігуруйте сервіс
    App\Service\PublicService:
        public: true

Одночасний імпорт багатьох сервісів за допомогою джерела

Ви вже бачили, що ви можете імпортувати багато сервісів одночасно, використовуючи клюс resource. Наприклад, конфігурація Symfony за замовчуванням містить наступне:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
# config/services.yaml
services:
    # ... те ж саме, що і раніше

    # робить класи в src/ доступними для використання в якості сервісів
    # створює по сервісу в класі, чий id є повним іменем класу
    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Tests,Kernel.php}'

Tip

Значення опцій resource та exclude може бути будь-яким валідним глобальним патерном.

Це може бути використано для того, щоб швидко зробити багато класів доступними в якості сервісів, та застосувати якусь конфігурацію за замовчуванням. Id кожного сервісу - це його повністю кваліфіковане ім'я класу. Ви також можете переписати будь-який сервіс, який було імпортовано, використовуючи його id (ім'я класу) нижче (наприклад, див. ). Якщо ви перепишете сервіс, жодні з опцій (наприклад, public) не будуть унаслілудвані з імпорту (але переписаний сервіс наслідує з _defaults).

Ви також можете exclude (виключити) певні шляхи. Це не обов'язково, але трохи покращить продуктивніть середовища dev: виключені шляхи не відстежуються, так що їх зміна не призведе до перебудови контейнера.

Note

Чекайте, це що, означає, що кожний клас у src/AppBundle зареєстрований як сервіс? Навіть модель або класи сутностей? Насправді, ні. Якщо у вас є public: false у вашому ключі _defaults (або ви можете додати його у конкретному імпорті), всі імпортовані сервіси є приватними. Завдяки цьому, всі класи в src/AppBundle, які не чітко використовуються як сервіси, автоматично видаляються з фінального контейнера. У дійсності, імпорт просто означає, що всі класи доступні для використання в якості сервісів, без необхіжності ручноїконфігурації.

Визначення багатьох сервісів, що використовують один простір імен

Якщо ви визначаєте сервіси, використовуючи формат конфігурації YAML, простір імен PHP використовується в якості ключа кожної конфігурації, тому ви не можете визначити різні конфігурації сервісів для класів під одним простором імен:

  • YAML
  • XML
  • PHP
1
2
3
4
5
# config/services.yaml
services:
    App\Domain\:
        resource: '../src/Domain/*'
        # ...

Для того, щоб мати багато визначень, додайте опцію namespace та використайте будь-який унікальний рядок в якості ключа кожної конфігурації сервісу:

1
2
3
4
5
6
7
8
9
10
11
# config/services.yaml
services:
    command_handlers:
        namespace: App\Domain\
        resource: '../src/Domain/*/CommandHandler'
        tags: [command_handler]

    event_subscribers:
        namespace: App\Domain\
        resource: '../src/Domain/*/EventSubscriber'
        tags: [event_subscriber]

Чітка конфігурація сервісів та аргументів

До появи Symfony 3.3, всі сервіси та (зазвичай) аргументи, були чітко сконфігуровані: було неможливо завантажити сервіси автоматично , а автомонтування було набагато менше поширене.

Обидві ці функції необов'язкові. І навіть якщо ви використовуєте їх, можуть бути деякі випадки, коли ви захочете вручну підключити сервіс. Наприклад, уявіть, що ви хочете зареєструвати 2 сервіси для класу SiteUpdateManager - кожний з різними email адміна. У цьому випадку, кожний повинен мати унікальний id сервісу:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# config/services.yaml
services:
    # ...

    # це id сервісу
    site_update_manager.superadmin:
        class: App\Service\SiteUpdateManager
        # ви все ще МОЖЕТЕ використати автомонтування: ми просто хочемо показати, як це виглядає без нього
        autowire: false
        # вручну підключіть всі аргументи
        arguments:
            - '@App\Service\MessageGenerator'
            - '@mailer'
            - 'superadmin@example.com'

    site_update_manager.normal_users:
        class: App\Service\SiteUpdateManager
        autowire: false
        arguments:
            - '@App\Service\MessageGenerator'
            - '@mailer'
            - 'contact@example.com'

    # Створіть псевдонім, щоб за замовчуванням, якщо ви використовуєте підказку SiteUpdateManager,
    # використовувався site_update_manager.superadmin
    App\Service\SiteUpdateManager: '@site_update_manager.superadmin'

У цьому випадку, зареєстровані два сервіси: site_update_manager.superadmin і site_update_manager.normal_users. Завдяки псевдоніми, якщо ви додасте підказку SiteUpdateManager, буде передано перше (site_update_manager.superadmin). Якщо ви хочете передати друге, то вам потрібно вручну підключити сервіс .

Caution

Якщо ви не створите псевдонім і завантажуєте всі свої сервіси з src/AppBundle , тоді три сервіси будуть створені (автоматичний сервіс + два ваших), та автоматично завантажений сервіс буде передано - за замовчуванням - коли ви додасте підказку SiteUpdateManager. Тому створення псевдоніму - гарна ідея.

Дізнайтеся більше