Визначення сервісів
Конфігурація – це місце, де ми розміщуємо визначення
користувацьких сервісів. Робиться це в секції services
.
Наприклад, ось як ми створюємо сервіс з іменем database
, який буде
екземпляром класу PDO
:
services:
database: PDO('sqlite::memory:')
Іменування служб використовується для того, щоб ми могли посилатися на них. Якщо на сервіс не посилаються, немає необхідності давати йому ім'я. Тому замість імені ми просто використовуємо двокрапку:
services:
- PDO('sqlite::memory:') # анонімний сервіс
Однорядковий запис може бути розбитий на кілька рядків, щоб можна
було додати додаткові ключі, наприклад setup. Псевдонім для
ключа create:
– factory:
.
services:
database:
create: PDO('sqlite::memory:')
setup: ...
Потім ми отримуємо сервіс із контейнера DI, використовуючи метод
getService()
за ім'ям, або, що ще краще, метод getByType()
за типом:
$database = $container->getService('database');
$database = $container->getByType(PDO::class);
Створення сервісу
Найчастіше ми створюємо сервіс, просто створюючи екземпляр класу:
services:
database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
Що створить фабричний метод у DI-контейнері:
public function createServiceDatabase(): PDO
{
return new PDO('mysql:host=127.0.0.1;dbname=test', 'root', 'secret');
}
Альтернативно, ключ arguments
може бути використаний для передачі
аргументів:
services:
database:
create: PDO
arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]
Статичний метод також може створити сервіс:
services:
database: My\Database::create(root, secret)
Це еквівалентно коду PHP:
public function createServiceDatabase(): PDO
{
return My\Database::create('root', 'secret');
}
Передбачається, що статичний метод My\Database::create()
має певне
значення, що повертається, яке повинен знати контейнер DI. Якщо у нього
його немає, ми записуємо тип у конфігурацію:
services:
database:
create: My\Database::create(root, secret)
type: PDO
Nette DI надає вам надзвичайно потужні засоби вираження, що дозволяють
написати практично все, що завгодно. Наприклад, щоб звернутися до іншої служби і викликати її метод.
Для простоти замість ->
використовується ::
.
services:
routerFactory: App\Router\Factory
router: @routerFactory::create()
Це еквівалентно коду PHP:
public function createServiceRouterFactory(): App\Router\Factory
{
return new App\Router\Factory;
}
public function createServiceRouter(): Router
{
return $this->getService('routerFactory')->create();
}
Виклики методів можна об'єднувати в ланцюжки, як у PHP:
services:
foo: FooFactory::build()::get()
Це еквівалентно коду PHP:
public function createServiceFoo()
{
return FooFactory::build()->get();
}
Аргументи
Іменовані параметри також можуть використовуватися для передачі аргументів:
services:
database: PDO(
'mysql:host=127.0.0.1;dbname=test' # позиційний
username: root # іменований
password: secret # іменований
)
Використання ком необов'язкове при розбитті аргументів на кілька рядків.
Звичайно, ми також можемо використовувати інші сервіси або параметри як аргументи:
services:
- Foo(@anotherService, %appDir%)
Відповідає коду PHP:
public function createService01(): Foo
{
return new Foo($this->getService('anotherService'), '...');
}
Якщо перший аргумент – автомонтований, а ви хочете
вказати другий, опустіть перший за допомогою символу _
, например
Foo(_, %appDir%)
. Або, що ще краще, передавайте тільки другий аргумент
як іменований параметр, наприклад: Foo(path: %appDir%)
.
Nette DI і формат NEON дають вам надзвичайно потужні виразні засоби, що дозволяють написати практично все, що завгодно. Таким чином, аргументом може бути новостворений об'єкт, ви можете викликати статичні методи, методи інших сервісів або навіть глобальні функції, використовуючи спеціальну нотацію:
services:
analyser: My\Analyser(
FilesystemIterator(%appDir%) # створюємо об'єкт
DateTime::createFromFormat('Y-m-d') # викликаємо статичний метод
@anotherService # передаємо інший сервіс
@http.request::getRemoteAddress() # викликаємо метод іншого сервісу
::getenv(NetteMode) # викликаємо глобальну функцію
)
Відповідає коду PHP:
public function createServiceAnalyser(): My\Analyser
{
return new My\Analyser(
new FilesystemIterator('...'),
DateTime::createFromFormat('Y-m-d'),
$this->getService('anotherService'),
$this->getService('http.request')->getRemoteAddress(),
getenv('NetteMode')
);
}
Спеціальні функції
Ви також можете використовувати спеціальні функції в аргументах для приведення або заперечення значень:
not(%arg%)
запереченняbool(%arg%)
приведення до bool без втратint(%arg%)
приведення до int без втратfloat(%arg%)
приведення до плаваючого стану без втратstring(%arg%)
приведення до рядка без втрат
services:
- Foo(
id: int(::getenv('ProjectId'))
productionMode: not(%debugMode%)
)
Переписування без втрат відрізняється від звичайного переписування
в PHP, наприклад використовуючи (int)
, у тому, що воно викидає
виняток для нечислових значень.
Як аргументи можна передавати кілька сервісів. Масив усіх сервісів
певного типу (тобто класу або інтерфейсу) створюється функцією
typed()
. Функція буде опускати сервіси, у яких відключено
автопідключення, при цьому можна вказати кілька типів,
розділених комою.
services:
- BarsDependent( typed(Bar) )
Ви також можете передати масив сервісів автоматично, використовуючи автозв'язування.
Масив усіх сервісів із певним тегом створюється
функцією tagged()
. Можна вказати кілька тегів, розділених комою.
services:
- LoggersDependent( tagged(logger) )
Посилання на сервіси
Посилання на окремі сервіси використовуються за допомогою символу
@
и имени, например @database
:
services:
- create: Foo(@database)
setup:
- setCacheStorage(@cache.storage)
Відповідає коду PHP:
public function createService01(): Foo
{
$service = new Foo($this->getService('database'));
$service->setCacheStorage($this->getService('cache.storage'));
return $service;
}
Навіть на анонімні сервіси можна посилатися за допомогою зворотного виклику, просто вкажіть їхній тип (клас або інтерфейс) замість імені. Однак зазвичай у цьому немає потреби через автозв'язування.
services:
- create: Foo(@Nette\Database\Connection) # или @\PDO
setup:
- setCacheStorage(@cache.storage)
Setup
У секції setup
ми перераховуємо методи, які будуть викликатися
під час створення сервісу:
services:
database:
create: PDO(%dsn%, %user%, %password%)
setup:
- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
Відповідає коду PHP:
public function createServiceDatabase(): PDO
{
$service = new PDO('...', '...', '...');
$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $service;
}
Також можна встановити властивості. Додавання елемента в масив також підтримується, і його слід писати в лапках, щоб не конфліктувати із синтаксисом NEON:
services:
foo:
create: Foo
setup:
- $value = 123
- '$onClick[]' = [@bar, clickHandler]
Відповідає коду PHP:
public function createServiceFoo(): Foo
{
$service = new Foo;
$service->value = 123;
$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
return $service;
}
Однак статичні методи або методи інших сервісів також можуть бути
викликані в налаштуванні. Ми передаємо їм фактичний сервіс як
@self
:
services:
foo:
create: Foo
setup:
- My\Helpers::initializeFoo(@self)
- @anotherService::setFoo(@self)
Відповідає коду PHP:
public function createServiceFoo(): Foo
{
$service = new Foo;
My\Helpers::initializeFoo($service);
$this->getService('anotherService')->setFoo($service);
return $service;
}
Автозв'язування
Ключ autowired
можна використовувати для виключення сервісу з
автозв'язування або для впливу на його поведінку. Детальнішу
інформацію див. у розділі Автозв'язування.
services:
foo:
create: Foo
autowired: false # foo видаляється з автозв'язування
Теги
Інформація про користувача може бути додана до окремих сервісів у вигляді тегів:
services:
foo:
create: Foo
tags:
- cached
Теги також можуть мати значення:
services:
foo:
create: Foo
tags:
logger: monolog.logger.event
Масив сервісів з певними тегами може бути переданий як аргумент за
допомогою функції tagged()
. Можна також вказати кілька тегів,
розділених комою.
services:
- LoggersDependent( tagged(logger) )
Імена служб можна отримати з контейнера DI за допомогою методу
findByTag()
:
$names = $container->findByTag('logger');
// $names - масив, що містить ім'я сервісу і значення тега
// наприклад ['foo' => 'monolog.logger.event', ...]
Режим впровадження
Прапор inject: true
використовується для активації передавання
залежностей через публічні змінні за допомогою анотації inject і методів inject*().
services:
articles:
create: App\Model\Articles
inject: true
За замовчуванням inject
активується тільки для презентерів.
Модифікація сервісів
У контейнері DI є низка сервісів, які були додані вбудованим або вашим розширенням. Визначення цих сервісів можуть бути
змінені в конфігурації. Наприклад, для сервісу application.application
,
який за замовчуванням є об'єктом Nette\Application\Application
, ми можемо
змінити клас:
services:
application.application:
create: MyApplication
alteration: true
Прапор alteration
є інформативним і говорить про те, що ми просто
змінюємо існуючий сервіс.
Ми також можемо додати setup
:
services:
application.application:
create: MyApplication
alteration: true
setup:
- '$onStartup[]' = [@resource, init]
Під час перезапису сервісу ми можемо видалити вихідні аргументи,
елементи setup
або теги, для яких reset
є:
services:
application.application:
create: MyApplication
alteration: true
reset:
- arguments
- setup
- tags
Сервіс, доданий розширенням, також може бути видалено з контейнера:
services:
cache.journal: false