Definizioni del servizio
La configurazione è il luogo in cui si istruisce il contenitore DI su come assemblare i singoli servizi e come collegarli con altre dipendenze. Nette fornisce un modo molto chiaro ed elegante per raggiungere questo obiettivo.
La sezione services
del file di configurazione di NEON è il luogo in cui si definiscono i servizi personalizzati
e le loro configurazioni. Vediamo un semplice esempio di definizione di un servizio chiamato database
, che
rappresenta un'istanza della classe PDO
:
services:
database: PDO('sqlite::memory:')
Questa configurazione dà luogo al seguente metodo factory nel contenitore DI:
public function createServiceDatabase(): PDO
{
return new PDO('sqlite::memory:');
}
I nomi dei servizi consentono di fare riferimento ad essi in altre parti del file di configurazione, utilizzando il formato
@serviceName
. Se non è necessario dare un nome al servizio, si può usare semplicemente un punto:
services:
- PDO('sqlite::memory:')
Per recuperare un servizio dal contenitore DI, si può usare il metodo getService()
con il nome del servizio come
parametro, oppure il metodo getByType()
con il tipo di servizio:
$database = $container->getService('database');
$database = $container->getByType(PDO::class);
Creazione di servizi
Più comunemente, si crea un servizio semplicemente istanziando una classe specifica. Ad esempio:
services:
database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
Se abbiamo bisogno di espandere la configurazione con ulteriori chiavi, la definizione può essere espansa in più righe:
services:
database:
create: PDO('sqlite::memory:')
setup: ...
La chiave create
ha un alias factory
, entrambe le versioni sono comuni nella pratica. Tuttavia, si
consiglia di utilizzare create
.
Gli argomenti del costruttore o il metodo di creazione possono essere scritti in alternativa nella chiave
arguments
:
services:
database:
create: PDO
arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]
I servizi non devono essere creati solo tramite la semplice istanziazione di una classe; possono anche derivare dalla chiamata di metodi statici o di altri servizi:
services:
database: DatabaseFactory::create()
router: @routerFactory::create()
Si noti che per semplicità, al posto di ->
, si usa ::
, vedi espressione significa. Questi metodi di fabbrica sono generati:
public function createServiceDatabase(): PDO
{
return DatabaseFactory::create();
}
public function createServiceRouter(): RouteList
{
return $this->getService('routerFactory')->create();
}
Il contenitore DI deve conoscere il tipo del servizio creato. Se si crea un servizio usando un metodo che non ha un tipo di ritorno specificato, si deve menzionare esplicitamente questo tipo nella configurazione:
services:
database:
create: DatabaseFactory::create()
type: PDO
Argomenti
Passiamo gli argomenti ai costruttori e ai metodi in modo molto simile al normale PHP:
services:
database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
Per una migliore leggibilità, possiamo elencare gli argomenti su righe separate. In questo formato, l'uso delle virgole è facoltativo:
services:
database: PDO(
'mysql:host=127.0.0.1;dbname=test'
root
secret
)
È possibile anche dare un nome agli argomenti, in modo da non preoccuparsi del loro ordine:
services:
database: PDO(
username: root
password: secret
dsn: 'mysql:host=127.0.0.1;dbname=test'
)
Se si desidera omettere alcuni argomenti e utilizzare i loro valori predefiniti o inserire un servizio tramite autocablaggio, utilizzare un trattino basso:
services:
foo: Foo(_, %appDir%)
Gli argomenti possono essere servizi, parametri e molto altro, vedere i mezzi di espressione.
Impostazione
Nella sezione setup
, si definiscono i metodi che devono essere richiamati durante la creazione del servizio.
services:
database:
create: PDO(%dsn%, %user%, %password%)
setup:
- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
In PHP, questo sarebbe simile a:
public function createServiceDatabase(): PDO
{
$service = new PDO('...', '...', '...');
$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $service;
}
Oltre alle chiamate di metodo, è possibile passare valori alle proprietà. È supportata anche l'aggiunta di un elemento a un array, ma è necessario racchiuderlo tra virgolette per evitare di scontrarsi con la sintassi NEON:
services:
foo:
create: Foo
setup:
- $value = 123
- '$onClick[]' = [@bar, clickHandler]
In PHP, questo si tradurrebbe in:
public function createServiceFoo(): Foo
{
$service = new Foo;
$service->value = 123;
$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
return $service;
}
Nella configurazione, si possono anche chiamare metodi statici o metodi di altri servizi. Se è necessario passare il servizio
corrente come argomento, utilizzare @self
:
services:
foo:
create: Foo
setup:
- My\Helpers::initializeFoo(@self)
- @anotherService::setFoo(@self)
Si noti che per semplicità, al posto di ->
, si usa ::
, vedi espressione significa. Questo genera il seguente metodo di fabbrica:
public function createServiceFoo(): Foo
{
$service = new Foo;
My\Helpers::initializeFoo($service);
$this->getService('anotherService')->setFoo($service);
return $service;
}
Mezzi di espressione
Nette DI ci fornisce capacità di espressione eccezionalmente ricche, che ci permettono di articolare quasi tutto. Nei file di configurazione, possiamo usare dei parametri:
# parametro
%wwwDir%
# valore sotto una chiave di parametro
%mailer.user%
# parametro all'interno di una stringa
'%wwwDir%/images'
Possiamo anche creare oggetti, chiamare metodi e funzioni:
# creare un oggetto
DateTime()
# chiamare un metodo statico
Collator::create(%locale%)
# chiamare una funzione PHP
::getenv(DB_USER)
Fare riferimento ai servizi con il loro nome o con il loro tipo:
# servizio per nome
@database
# servizio per tipo
@Nette\Database\Connection
Usare la sintassi dei callable di prima classe:
# creating a callback, equivalent to [@user, logout]
@user::logout(...)
Utilizzare le costanti:
# costante di classe
FilesystemIterator::SKIP_DOTS
# costante globale ottenuta con la funzione PHP constant()
::constant(PHP_VERSION)
Le chiamate ai metodi possono essere concatenate, proprio come in PHP. Per semplicità, invece di ->
, usiamo
::
:
DateTime()::format('Y-m-d')
# PHP: (new DateTime())->format('Y-m-d')
@http.request::getUrl()::getHost()
# PHP: $this->getService('http.request')->getUrl()->getHost()
Queste espressioni possono essere utilizzate ovunque durante la creazione di servizi, nei parametri, nella sezione di impostazione o nei parametri:
parameters:
ipAddress: @http.request::getRemoteAddress()
services:
database:
create: DatabaseFactory::create( @anotherService::getDsn() )
setup:
- initialize( ::getenv('DB_USER') )
Funzioni speciali
All'interno dei file di configurazione è possibile utilizzare queste funzioni speciali:
not()
per la negazione dei valoribool()
,int()
,float()
,string()
per la fusione dei tipi senza perdita di informazionityped()
per generare un array di tutti i servizi di un tipo specificatotagged()
per creare un array di tutti i servizi con un determinato tag
services:
- Foo(
id: int(::getenv('ProjectId'))
productionMode: not(%debugMode%)
)
Rispetto al typecasting convenzionale in PHP, come (int)
, il type casting lossless lancia un'eccezione per
i valori non numerici.
La funzione typed()
crea un array di tutti i servizi di un particolare tipo (classe o interfaccia). Esclude
i servizi con il cablaggio automatico disattivato. È possibile specificare più tipi, separati da virgole.
services:
- BarsDependent( typed(Bar) )
È anche possibile passare automaticamente un array di servizi di un tipo specifico come argomento utilizzando l'autowiring.
La funzione tagged()
crea un array di tutti i servizi con un tag specificato. È possibile elencare più tag,
separati da virgole.
services:
- LoggersDependent( tagged(logger) )
Cablaggio auto
Il tasto autowired
consente di modificare il comportamento del cablaggio automatico per un particolare servizio.
Per maggiori dettagli, vedere il capitolo sul cablaggio
automatico.
services:
foo:
create: Foo
autowired: false # il servizio pippo è escluso dal cablaggio automatico
Tag
I tag sono utilizzati per aggiungere informazioni supplementari ai servizi. È possibile assegnare uno o più tag a un servizio:
services:
foo:
create: Foo
tags:
- cached
I tag possono anche contenere dei valori:
services:
foo:
create: Foo
tags:
logger: monolog.logger.event
Per recuperare tutti i servizi con tag specifici, è possibile utilizzare la funzione tagged()
:
services:
- LoggersDependent( tagged(logger) )
Nel contenitore DI, è possibile ottenere i nomi di tutti i servizi con un tag specifico utilizzando il metodo
findByTag()
:
$names = $container->findByTag('logger');
// $names è un array contenente il nome del servizio e il valore del tag
// ad esempio ['foo' => 'monolog.logger.event', ...]
Modalità di iniezione
L'uso del flag inject: true
attiva il passaggio delle dipendenze tramite variabili pubbliche con l'annotazione inject e i metodi inject*().
services:
articles:
create: App\Model\Articles
inject: true
Per impostazione predefinita, inject
è attivato solo per i presentatori.
Modifiche del servizio
Il contenitore DI contiene molti servizi aggiunti da estensioni integrate o dall'utente. È possibile modificare le definizioni di questi servizi direttamente nella configurazione. Per
esempio, si può cambiare la classe del servizio application.application
, che è convenzionalmente
Nette\Application\Application
, in un'altra:
services:
application.application:
create: MyApplication
alteration: true
Il flag alteration
è informativo e indica che stiamo semplicemente modificando un servizio esistente.
Possiamo anche completare la configurazione:
services:
application.application:
create: MyApplication
alteration: true
setup:
- '$onStartup[]' = [@resource, init]
Quando si sovrascrive un servizio, si potrebbe voler rimuovere gli argomenti originali, gli elementi di configurazione
o i tag, ed è qui che reset
si rivela utile:
services:
application.application:
create: MyApplication
alteration: true
reset:
- arguments
- setup
- tags
Se si desidera rimuovere un servizio aggiunto da un'estensione, è possibile farlo in questo modo:
services:
cache.journal: false