Определения на услуги
Конфигурацията е мястото, където даваме указания на контейнера DI как да сглобява отделните услуги и как да ги свързва с други зависимости. Nette предоставя много ясен и елегантен начин за постигане на това.
Разделът services
в конфигурационния файл NEON е мястото, където
дефинираме нашите потребителски услуги и техните конфигурации. Нека
разгледаме прост пример за дефиниране на услуга с име database
,
която представлява инстанция на класа PDO
:
services:
database: PDO('sqlite::memory:')
Тази конфигурация води до следния фабричен метод в контейнера DI:
public function createServiceDatabase(): PDO
{
return new PDO('sqlite::memory:');
}
Имената на услугите ни позволяват да се позоваваме на тях в други
части на конфигурационния файл, като използваме формата @serviceName
.
Ако няма нужда да даваме име на услугата, можем просто да
използваме точка:
services:
- PDO('sqlite::memory:')
За да извлечем услуга от контейнера 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)
Ако трябва да разширим конфигурацията с допълнителни ключове, дефиницията може да се разшири на няколко реда:
services:
database:
create: PDO('sqlite::memory:')
setup: ...
Ключът create
има псевдоним factory
, като и двете версии са
често срещани в практиката. Въпреки това препоръчваме да използвате
create
.
Аргументите на конструктора или методът за създаване могат
алтернативно да бъдат записани в ключа arguments
:
services:
database:
create: PDO
arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]
Не е задължително услугите да се създават само чрез просто инстанциране на клас; те могат да се създават и в резултат на извикване на статични методи или методи на други услуги:
services:
database: DatabaseFactory::create()
router: @routerFactory::create()
Обърнете внимание, че за опростяване вместо ->
, използваме
::
, вж. изразните средства. Тези фабрични
методи се генерират:
public function createServiceDatabase(): PDO
{
return DatabaseFactory::create();
}
public function createServiceRouter(): RouteList
{
return $this->getService('routerFactory')->create();
}
Контейнерът DI трябва да знае типа на създадената услуга. Ако създаваме услуга с помощта на метод, който няма определен тип на връщане, трябва изрично да споменем този тип в конфигурацията:
services:
database:
create: DatabaseFactory::create()
type: PDO
Аргументи
Предаването на аргументи към конструкторите и методите става по начин, много подобен на този в обикновения PHP:
services:
database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
За по-добра четимост можем да изписваме аргументите на отделни редове. В този формат използването на запетаи не е задължително:
services:
database: PDO(
'mysql:host=127.0.0.1;dbname=test'
root
secret
)
Можете също така да назовете аргументите, което ви позволява да не се притеснявате за реда им:
services:
database: PDO(
username: root
password: secret
dsn: 'mysql:host=127.0.0.1;dbname=test'
)
Ако желаете да пропуснете някои аргументи и да използвате стойностите им по подразбиране или да вмъкнете услуга чрез автоматично свързване, използвайте долна черта:
services:
foo: Foo(_, %appDir%)
Аргументите могат да бъдат услуги, параметри и много други, вижте изразни средства.
Настройка
В раздела 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)
Обърнете внимание, че за опростяване вместо ->
, използваме
::
, вижте изразните средства. Това генерира
следния фабричен метод:
public function createServiceFoo(): Foo
{
$service = new Foo;
My\Helpers::initializeFoo($service);
$this->getService('anotherService')->setFoo($service);
return $service;
}
Средства за изразяване
Nette DI ни предоставя изключително богати възможности за изразяване, което ни позволява да изразим почти всичко. В конфигурационните файлове можем да използваме параметри:
# параметър
%wwwDir%
# стойност под ключ на параметър
%mailer.user%
# параметър в рамките на низ
'%wwwDir%/images'
Можем също така да създаваме обекти, да извикваме методи и функции:
# създаване на обект
DateTime()
# извикване на статичен метод
Collator::create(%locale%)
# извикване на функция на PHP
::getenv(DB_USER)
Услугите се наричат по име или по тип:
# услуга по име
@database
# услуга по тип
@Nette\Database\Connection
Използвайте синтаксис на първокласни извиквания:
# creating a callback, equivalent to [@user, logout]
@user::logout(...)
Използвайте константи:
# клас константа
FilesystemIterator::SKIP_DOTS
# глобална константа, получена чрез PHP функцията constant()
::constant(PHP_VERSION)
Извикванията на методите могат да бъдат верижно свързани, точно
както в PHP. За улеснение, вместо ->
, използваме ::
:
DateTime()::format('Y-m-d')
# PHP: (new DateTime())->format('Y-m-d')
@http.request::getUrl()::getHost()
# PHP: $this->getService('http.request')->getUrl()->getHost()
Тези изрази могат да се използват навсякъде при създаване на услуги, в аргументи, в секцията за настройка или в параметри:
parameters:
ipAddress: @http.request::getRemoteAddress()
services:
database:
create: DatabaseFactory::create( @anotherService::getDsn() )
setup:
- initialize( ::getenv('DB_USER') )
Специални функции
В рамките на конфигурационните файлове можете да използвате тези специални функции:
not()
за отричане на стойностиbool()
,int()
,float()
,string()
за привеждане на типове без загубиtyped()
за генериране на масив от всички услуги от определен типtagged()
за създаване на масив от всички услуги с даден етикет
services:
- Foo(
id: int(::getenv('ProjectId'))
productionMode: not(%debugMode%)
)
В сравнение с конвенционалното типизиране в PHP, като например
(int)
, типизирането без загуби ще хвърля изключение за нецифрови
стойности.
Функцията typed()
създава масив от всички услуги от определен тип
(клас или интерфейс). Тя изключва услуги с изключено автоматично
свързване. Могат да бъдат зададени няколко типа, разделени със
запетаи.
services:
- BarsDependent( typed(Bar) )
Можете също така автоматично да подадете масив от услуги от определен тип като аргумент, като използвате автоподразбиране.
Функцията tagged()
създава масив от всички услуги с определен
етикет. Могат да бъдат изброени няколко тага, разделени със запетаи.
services:
- LoggersDependent( tagged(logger) )
Автоматично окабеляване
Ключът 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
е информативен и показва, че просто модифицираме
съществуваща услуга.
Можем също така да допълним настройката:
services:
application.application:
create: MyApplication
alteration: true
setup:
- '$onStartup[]' = [@resource, init]
Когато презаписвате услуга, може да искате да премахнете
оригиналните аргументи, елементи на настройката или тагове, което е
мястото, където reset
е полезен:
services:
application.application:
create: MyApplication
alteration: true
reset:
- arguments
- setup
- tags
Ако искате да премахнете услуга, добавена от разширение, можете да го направите по следния начин:
services:
cache.journal: false