Как реализовать Декоратор Сервиса в сервис-контейнере Symfony?

Декоратор — это структурный паттерн проектирования, который позволяет динамически добавлять объектам новую функциональность, оборачивая их в полезные «обёртки».

При повторном переопределении существующего определения сервиса, для сервис-контейнера Symfony, то исходный сервис будет теряться:

# config/services.yaml
services:
    App\Mailer: ~

    # this replaces the old App\Mailer definition with the new one, the
    # old definition is lost
    App\Mailer:
        class: App\NewMailer

В большинстве случаев это именно то, что вы хотите сделать, например для разных сред окружения (test, dev и т.д.).

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

Конфигурация ниже заменяет сервис App\Mailer на новый, но сохраняет ссылку на старый как App\DecoratingMailer.inner:

# config/services.yaml
services:
    App\Mailer: ~

    App\DecoratingMailer:
        # overrides the App\Mailer service
        # but that service is still available as App\DecoratingMailer.inner
        decorates: App\Mailer

Опция decorates сообщает сервис-контейнеру Symfony, что сервис App\DecoratingMailer заменяет сервис App\Mailer. Если вы используете конфигурацию services.yaml по умолчанию, то декорируемый сервис автоматически внедряется, когда у конструктора декорирующего сервиса есть один аргумент, с тайп-хинтом декорированного класса сервиса.

Если вы не используете autowiring или у сервиса декоратора есть несколько аргументов конструктора, с тайп-хинтом декорированного класса сервиса, то вы должны явно добавить декорированный сервис (идентификатор декорированного сервиса автоматически изменяется на decorating_service_id + ‘.inner’) :

# config/services.yaml
services:
    App\Mailer: ~

    App\DecoratingMailer:
        decorates: App\Mailer
        # pass the old service as an argument
        arguments: ['@App\DecoratingMailer.inner']

Видимость декорируемого сервиса App\Mailer (который является алиасом для нового сервиса) будет по-прежнему такой же, как и исходная видимость App\Mailer.

Сгенерированный внутренний идентификатор основан на идентификаторе сервиса-декоратора (здесь App\DecoratingMailer), а не на декорированном сервисе (здесь App\Mailer). Вы можете контролировать внутреннее имя сервиса через опцию decoration_inner_name:

# config/services.yaml
services:
    App\DecoratingMailer:
        # ...
        decoration_inner_name: App\DecoratingMailer.wooz
        arguments: ['@App\DecoratingMailer.wooz']

Приоритет декораторов в сервис-контейнере.

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

# config/services.yaml
Foo: ~

Bar:
    public: false
    decorates: Foo
    decoration_priority: 5
    arguments: ['@Bar.inner']

Baz:
    public: false
    decorates: Foo
    decoration_priority: 1
    arguments: ['@Baz.inner']

Сгенерированный код будет следующим:

$this->services[Foo::class] = new Baz(new Bar(new Foo()));

Добавить комментарий

Ваш адрес email не будет опубликован.