Декоратор — это структурный паттерн проектирования, который позволяет динамически добавлять объектам новую функциональность, оборачивая их в полезные «обёртки».
При повторном переопределении существующего определения сервиса, для сервис-контейнера 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()));