Создание сервиса состоит из двух этапов — создание PHP-класса, а также, объявление данного класса в *.services.yml файле модуля, который данный класс и предоставляет.
¶Создание сервиса
Для примера возьмем следующий класс (modules/custom/mymodule/src/MyObject.php
).
<?php
namespace Drupal\mymodule;
/**
* Provides my special service.
*/
class MyObject {
/**
* Gets hello world message.
*
* @return string
* The message.
*/
protected function helloWorld() {
return 'Hello World!';
}
}
Данный класс не имеет никаких зависимостей, это просто PHP-класс, который можно инициализировать напрямую.
Для того чтобы превратить данный класс в сервис, необходимо объявить его в качестве сервиса в mymodule.services.yml.
services:
mymodule.awesome_service:
class: Drupal\mymodule\MyObject
Таким образом, мы создали сервис mymodule.awesome_service
, при обращении, к которому, мы получим экземпляр MyObject
и сможем вызывать его методы.
¶Структура *.services.yml
Пример example.services.yml
:
# Раздел, в котором можно зарегистрировать статичные параметры, которые можно
# использовать для передачи в качестве аргументов сервису.
parameters:
example.parameter: 'Hello World!'
# Раздел, в котором объявляются сервисы.
services:
# Простой пример, регистрирующий класс в качестве сервиса.
example.simple:
class: Drupal\example\Simple
# Пример регистрации класса в качестве сервиса, с передачей в качестве
# аргументов другого сервиса и параметра.
example.complex:
class: Drupal\example\Complex
arguments: ['@example.simple', '%example.parameter%']
Файл *.services.yml
состоит из двух разделов, parameters
и services
. Оба раздела являются опциональными, вы сами решаете что вам нужно и описывать в соответствии со структурой.
Раздел parameters
отвечает за объявление статичных переменных, которые могут быть переданы в сервисы в качестве аргументов. Это необходимо в тех случаях, когда ваш сервис требует какое-то значение по умолчанию, но вы хотите предусмотреть возможность переопределения данного значения сторонними разработчиками. Так как при вызове сервиса, разработчики не могут передавать никаких аргументов и входных данных, это позволяет решить данный недостаток.
Раздел services
отвечает за непосредственное объявление сервисов. В данном разделе вы описываете название сервиса, а также его свойства.
¶Свойства сервисов
¶abstract
Позволяет объявить сервис в качестве абстрактного сервиса. Это означает, что данные сервис должен использовать как parent
у других сервисов. При значении true
, сервис станет абстрактным, при значение false
(по умолчанию), сервис будет инициализирован.
Все свойства данного сервиса, будут применены на сервисы, указавшие текущий в качестве parent
.
¶alias
Позволяет задать синоним сервису.
Например:
services:
example.simple:
class: Drupal\example\Simple
alias: foo_bar
Из примера выше, сервис можно будет вызвать как по example.simple
, так и по foo_bar
.
Объявление сервиса также можно сделать в другом формате.
services:
example.simple:
class: Drupal\example\Simple
foo_bar: '@example.simple'
Это аналогичная запись первому варианту, отличие лишь в том, что второй способ позволяет задать алиас для стороннего сервиса, тогда как первый, задается непосредственно при объявлении сервиса.
¶arguments
Аргументы — массив аргументов, которые будут переданы либо непосредственно в класс class
сервиса, либо в его фабрику factory
.
Аргументы могут быть:
- Другим сервисом. Для этого название сервиса, который необходимо передать как аргумент, записывается с префиксом
@
. Например@service.to.inject
. - Параметром из
parameters
раздела. Переменные объявленные в данном разделе, являются глобальным, таким образом, вам доступны параметры объявленные в других*.services.yml
файлах. Для передачи параметра в качестве аргумента, его название оборачивается в%
. Например%example.parameter%
. - Примитивным типом данных поддерживаемым YAML: строкой, числом, логическим значением. Например
'just string'
.
¶calls
Позволяет задать метод сервиса, который будет вызван после успешной инициализации объекта данного сервиса. Вы также можете передавать аргументы для данного метода.
<?php
namespace Drupal\mymodule;
/**
* Provides my special service.
*/
class MyObject {
/**
* The foo object.
*
* @var Drupal\foo\FooInterface
*/
protected $foo;
/**
* Sets foo object.
*
* @param Drupal\foo\FooInterface $foo
* The foo object.
*/
protected function setFoo(FooInterface $foo) {
$this->foo = $foo;
}
}
services:
my_service:
class: Drupal\mymodule\MyObject
calls:
- [setFoo, ['@foo.service']]
Вы можете указывать сколько угодно вызовов.
¶class
Класс, который будет инициализирован при обращении к сервису. Должен быть указан неоходимый класс, включая пространство имён.
¶configurator
configuratior
- это сервис с методом, который будут вызваны после успешной инициализации объекта.
services:
my_service:
class: Drupal\mymodule\MyObject
configurator: ['@my_service_configurator', configure]
В примере выше, будет вызван метод ::configure()
у сервиса my_service_configurator
.
То что возвращает данные метод, будет передано в качестве конструктора текущего сервиса.
¶decorates, decoration_priority и decoration_inner_name
Свойство decorated
позволяет заменить существующий сервис своим собственным. Таким образом, вы можете переопределять сервисы, не хакая их исходный код.
Для корректной замены, вы должны принимать заменяемый сервис в качестве аргумента вашего.
Замененные сервисы получают название [origina_service_name].inner
по умолчанию. Вы можете влиять на название при помощи decoration_inner_name
.
services:
example.simple:
class: Drupal\example\Simple
example.complex:
class: Drupal\example\Complex
decorates: example.simple
arguments: ['@example.simple.inner']
Таким образом, при вызове сервиса example.simple
будет вызван example.complex
, без необходимости менять код, где использовался старый сервис.
Если для одного сервиса объявляется несколько декораторов, то в данном случае может помочь свойство decoration_priority
, которое позволяет контролировать, в каком порядке будут вызваны декораторы. Декораторы вызываются в порядке убывания приоритета.
Например:
services:
example.first:
class: Drupal\example\First
example.second:
class: Drupal\example\Second
decorates: example.first
decoration_priority: 1
arguments: ['@example.simple.inner']
example.third:
class: Drupal\example\Third
decorates: example.first
decoration_priority: 5
arguments: ['@example.simple.inner']
Таким образом, примерный вызов будет выглядеть следующим образом:
$service = new Second(new Third(new First()));
¶factory
В factory
указывается класс, который будет использован для инициализации объекта сервиса. Иными словами, в нём описывается то, как именно будет создаваться объект сервиса с полным контролем и поведением.
В качестве фабрики может выступать либо класс, либо другой сервис. Он должен вернуть объект, который был инициализирован. Для сервисов с factory
, class
обычно содержит интерфейс, который должны реализовывать объекты, которые инициализируются фабрикой.
¶parent
parent
позволяет объявить сервис, в качестве наследника от другого сервиса со свойством abstract
. Данный сервис унаследует всей свойства своего родителя, кроме shared
, abstract
и tags
.
services:
example.first:
abstract: true
arguments: ['@foo', '@bar']
example.second:
parent: example.first
example.third:
parent: example.first
# Данный аргумент будет добавлен в качестве третьего.
arguments: ['@baz']
¶properties
Позволяет внедрять зависимости непосредственно в свойства класса.
<?php
namespace Drupal\mymodule;
/**
* Provides my special service.
*/
class MyObject {
/**
* The foo object.
*
* @var Drupal\foo\FooInterface
*/
protected $foo;
}
services:
my_service:
class: Drupal\mymodule\MyObject
properties:
foo: '@foo'
¶public
При объявлении сервиса, вы можете сделать его как публичным, так и приватным. По умолчанию, все сервисы являются публичным.
Публичные сервисы можно вызывать из контейнера, как это обычно и происходит. В случае с приватными сервисами (public: false
), они могут быть вызваны только в качестве Dependency Injection.
¶shared
Позволяет определить, как долго будет жить объект данного сервиса:
- true: (по умолчанию) Экземпляр создаётся только при первом обращении к сервису. Все последующие обращения возвращают уже инициализированный объект.
- false: Каждое обращение к сервису, будет инициализировать новый объект.
¶synthetic
При помощи свойства synthetic
со значением true
, вы можете указать, что данный сервис внедряется в сам Service Container, а не создается им.
¶tags
Теги позволяют определять группу сервисов, относящихся к одной и той же задаче. Данные сервисы также называются сервисами с метками.
Подобные сервисы собираются так называемыми сервис коллекторами (сборщиками), они обнаруживают все сервисы с необходимыми им метками и инициализируют их с какой-то конкретной целью.
¶Ссылки
- Drupal 8: Services, Niklan, 2017
- Drupal 8: Сервисы с метками, Niklan, 2019