Определения на услуги
Конфигурацията е мястото, където поставяме дефинициите на
потребителските услуги. Това е направено в раздела services
.
Например, така създаваме услуга с име database
, която ще бъде
инстанция на класа PDO
:
services:
database: PDO('sqlite::memory:')
Наименованията на услугите се използват, за да можем да се обръщаме към тях. Ако услугата не се споменава, не е необходимо да ѝ давате име. Затова просто използваме двоеточие вместо име:
services:
- PDO('sqlite::memory:') # анонимный сервис
Запис на един ред може да бъде разделен на няколко реда, за да се
позволи добавянето на допълнителни клавиши, напр. за настройка. Псевдонимът на ключа 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 # named
password: secret # named
)
Използването на запетаи не е задължително, когато аргументите се разделят на няколко реда.
Разбира се, можем да използваме и други услуги или параметри като аргументи:
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
са изброени методите, които ще бъдат извикани при
създаването на услугата:
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