Використання фабрики для створення сервісів

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

Використання фабрики для створення сервісів

Сервіс-контейнер Symfony надає декілька функцій для контролювання створення об'єктів, які дозволяють вам вказувати аргументи, передані конструктору, а також викликати методи та встановлювати параметри.

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

Статичні фабрики

Уявіть, що у вас є фабрика, яка конфігурує та повертає новий об'єкт NewsletterManager, викликаючи статичний метод createNewsletterManager():

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

// ...

class NewsletterManagerStaticFactory
{
    public static function createNewsletterManager(): NewsletterManager
    {
        $newsletterManager = new NewsletterManager();

        // ...

        return $newsletterManager;
    }
}

Щоб зробити об'єкт NewsletterManager доступним у якості сервісу, використайте опцію factory, щоб визначити, який метод якого класу має бути викликаний для створення його об'єкта:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
# config/services.yaml
services:
    # ...

    App\Email\NewsletterManager:
        # перший аргумент - це клас, а другий - статичний метод
        factory: ['App\Email\NewsletterManagerStaticFactory', 'createNewsletterManager']

Note

При використанні фабрики для створення сервісів, обране для класу значення не має ніякого ефекту на кінцевий сервіс. Справжнє ім'я класу залежить лише від об'єкта, який повертається фабрикою. Однак, сконфігуроване ім'я класу може бути використано пропусками компілятора, і тому має бути встановлене у розумних значеннях.

Нестатичні фабрики

Якщо ваша фабрика використовує регулярний метод, а не статичний, щоб сконфігурувати та створити ваш сервіс, ви можете існтанціювати саму фабрику як сервіс. Конфігурація сервіс- контейнера тоді виглядає наступним чином:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
# config/services.yaml
services:
    # ...

    # по-перше, створіть сервіс для фабрики
    App\Email\NewsletterManagerFactory: ~

    # по-друге, використайте сервіс фабрики як перший аргумент опції
    # 'factory' і метод фабрики як другий аргумент
    App\Email\NewsletterManager:
        factory: ['@App\Email\NewsletterManagerFactory', 'createNewsletterManager']

Викличні фабрики

Уявіть, що тепер ви змінили метод вашої фабрики на __invoke(), щоб ваш сервіс фабрики міг бути використаний у якості зворотного виклику:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/Email/InvokableNewsletterManagerFactory.php
namespace App\Email;

// ...
class InvokableNewsletterManagerFactory
{
    public function __invoke(): NewsletterManager
    {
        $newsletterManager = new NewsletterManager();

        // ...

        return $newsletterManager;
    }
}

Сервіси можуть бути створені та сконфігуровані через викликані фабрики, опускаючи ім'я методу:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
# config/services.yaml
services:
    # ...

    App\Email\NewsletterManager:
        class:   App\Email\NewsletterManager
        factory: '@App\Email\NewsletterManagerFactory'

Використання виразів у фабриках сервісів

6.1

Використання виразів як фабрик було представлено в Symfony 6.1.

Замість використання PHP-класів як фабррики, ви також можете використати вирази. Це дозволяє вам, наприклад, змінити сервіс, зановуючись на параметрі:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
# config/services.yaml
services:
    App\Email\NewsletterManagerInterface:
        # використати сервіс "tracable_newsletter", якщо включено налагодження, і "newsletter" - якщо ні.
        # "@=" вказує, що це є виразом
        factory: '@=parameter("kernel.debug") ? service("tracable_newsletter") : service("newsletter")'

    # ви можете використати функцію arg(), щоб отримати аргумент з визначення
    App\Email\NewsletterManagerInterface:
        factory: "@=arg(0).createNewsletterManager() ?: service("default_newsletter_manager")"
        arguments:
            - '@App\Email\NewsletterManagerFactory'

Передача аргументів методу фабрики

Tip

Аргументи у вашому методі фабрики автомонтуються , якщо це включено у вашому сервісі.

Якщо вам потрібно передати аргументи методу фабрики, ви можете використати опцію arguments. Наприклад, уявіть, що метод createNewsletterManager() у попередньому прикладі, бере сервіс templating в якості аргументу:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
# config/services.yaml
services:
    # ...

    App\Email\NewsletterManager:
        factory:   ['@App\Email\NewsletterManagerFactory', createNewsletterManager]
        arguments: ['@templating']