Использование фабрики для созданя сервисов

Сервис-контейнер Symfony предоставляет мощный способ контролировать создание объектов, позволяет вам указывать, какие аргументы передаются в конструктор, а также вызывать методы и устанавливать параметры. Однако, иногда, это не даст вам всё необходимое для создания ваших объектов. Для такой ситуации вы можете использовать фабрику для создания объекта и сказать сервис-контейнеру вызвать метод в фабрике, вместо того, чтобы напрямую инстанциировать класс.

Представьте, что у вас есть фабрика, которая конфигурирует и возвращает новый объект NewsletterManager, вызывая статический метод createNewsletterManager():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class NewsletterManagerStaticFactory
{
    public static function createNewsletterManager()
    {
        $newsletterManager = new NewsletterManager();

        // ...

        return $newsletterManager;
    }
}

Чтобы сделать объект NewsletterManager доступным в качестве сервиса, вы можете скофигурировать сервис-контейнер так, чтобы он использовал метод фабрики NewsletterManagerStaticFactory::createNewsletterManager():

  • YAML
    1
    2
    3
    4
    5
    6
    7
    # app/config/services.yml
    services:
        # ...
    
        AppBundle\Email\NewsletterManager:
            # вызов статического метода
            factory: ['AppBundle\Email\NewsletterManagerStaticFactory', createNewsletterManager]
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    <!-- app/config/services.xml -->
    
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <services>
            <service id="AppBundle\Email\NewsletterManager">
                <!-- вызов статического метода -->
                <factory class="AppBundle\Email\NewsletterManagerStaticFactory" method="createNewsletterManager" />
            </service>
        </services>
    </container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    // app/config/services.php
    
    use AppBundle\Email\NewsletterManager;
    use AppBundle\NumberGenerator;
    use AppBundle\Email\NewsletterManagerStaticFactory;
    // ...
    
    $container->register(NumberGenerator::class)
        // вызов статического метода
        ->setFactory(array(NewsletterManagerStaticFactory::class, 'createNewsletterManager'));
    

Note

При использовании фабрики для создания сервису, выбранное для класса значение не имеет никакого эффекта на итоговый сервис. Настоящее имя класса зависит только от объекта, возвращаемого фабрикой. Однако, сконфигурированное имя класса может быть использовано пропусками компилятора, и поэтому должно быть установлено в разумных значениях.

Если ваша фабрика не использует статическую функцию, чтобы сконфигурировать и создать ваш сервис, а использует обычный метод, то вы можете инстанциировать саму фабрику как сервис. Позже, в разделе "Передача аргументов методу фабрики", вы узнаете, как вы можете внедрять аргументы в этом методе.

Тогда конфигурация сервис-контейнера выглядит следующим образом:

  • YAML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    # app/config/services.yml
    
    services:
        # ...
    
        AppBundle\Email\NewsletterManagerFactory: ~
    
        AppBundle\Email\NewsletterManager:
            # вызвать метод указанного сервиса фабрики
            factory: 'AppBundle\Email\NewsletterManagerFactory:createNewsletterManager'
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <!-- app/config/services.xml -->
    
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <services>
            <service id="AppBundle\Email\NewsletterManagerFactory" />
    
            <service id="AppBundle\Email\NewsletterManager">
                <!-- вызвать метод указанного сервиса фабрики -->
                <factory service="AppBundle\Email\NewsletterManagerFactory"
                    method="createNewsletterManager"
                />
            </service>
        </services>
    </container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    // app/config/services.php
    
    use AppBundle\Email\NewsletterManager;
    use AppBundle\Email\NewsletterManagerFactory;
    // ...
    
    $container->register(NewsletterManagerFactory::class);
    
    $container->register(NewsletterManager::class)
        // вызвать метод указанного сервиса фабрики
        ->setFactory(array(
            new Reference(NewsletterManagerFactory::class),
            'createNewsletterManager',
        ));
    

Note

Традиционный синтаксис конфигурации в YAML-файлах использовал массив для определения сервиса фабрики и имени метода:

1
2
3
4
5
6
7
# app/config/services.yml

app.newsletter_manager:
    # новый синтаксис
    factory: 'AppBundle\Email\NewsletterManagerFactory:createNewsletterManager'
    # старый синтаксис
    factory: ['@AppBundle\Email\NewsletterManagerFactory', createNewsletterManager]

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

Tip

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

Если вам над передать аргументы методу фабрики, вы можете использовать опции arguments. Например, представьте, что метод createNewsletterManager() в предыдущем примере берёт сервис templating в качестве аргумента:

  • YAML
    1
    2
    3
    4
    5
    6
    7
    8
    # app/config/services.yml
    
    services:
        # ...
    
        AppBundle\Email\NewsletterManager:
            factory:   'AppBundle\Email\NewsletterManagerFactory:createNewsletterManager'
            arguments: ['@templating']
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    <!-- app/config/services.xml -->
    
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <services>
            <!-- ... -->
    
            <service id="AppBundle\Email\NewsletterManager">
                <factory service="AppBundle\Email\NewsletterManagerFactory" method="createNewsletterManager"/>
                <argument type="service" id="templating"/>
            </service>
        </services>
    </container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    // app/config/services.php
    
    use AppBundle\Email\NewsletterManager;
    use AppBundle\Email\NewsletterManagerFactory;
    use Symfony\Component\DependencyInjection\Reference;
    
    // ...
    $container->register(NewsletterManager::class)
        ->addArgument(new Reference('templating'))
        ->setFactory(array(
            new Reference(NewsletterManagerFactory::class),
            'createNewsletterManager',
        ));
    

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.