Nette Documentation Preview

syntax
Autowiring
**********

.[perex]
L'Autowiring è una funzionalità fantastica che può passare automaticamente i servizi richiesti al costruttore e ad altri metodi, quindi non dobbiamo scriverli affatto. Ti fa risparmiare un sacco di tempo.

Grazie a questo, possiamo omettere la stragrande maggioranza degli argomenti quando scriviamo le definizioni dei servizi. Invece di:

```neon
services:
	articles: Model\ArticleRepository(@database, @cache.storage)
```

Basta scrivere:

```neon
services:
	articles: Model\ArticleRepository
```

L'Autowiring si basa sui tipi, quindi affinché funzioni, la classe `ArticleRepository` deve essere definita più o meno così:

```php
namespace Model;

class ArticleRepository
{
	public function __construct(\PDO $db, \Nette\Caching\Storage $storage)
	{}
}
```

Per poter utilizzare l'autowiring, deve esserci **esattamente un servizio** per ogni tipo nel container. Se ce ne fossero di più, l'autowiring non saprebbe quale passare e lancerebbe un'eccezione:

```neon
services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # LANCIA ECCEZIONE, soddisfano sia mainDb che tempDb
```

La soluzione sarebbe bypassare l'autowiring e specificare esplicitamente il nome del servizio (cioè `articles: Model\ArticleRepository(@mainDb)`). Ma è più intelligente [disattivare |#Disattivazione dell autowiring] l'autowiring per uno dei servizi, o [dare la preferenza |#Preferenza dell autowiring] al primo servizio.


Disattivazione dell'autowiring
------------------------------

Possiamo disattivare l'autowiring di un servizio usando l'opzione `autowired: no`:

```neon
services:
	mainDb: PDO(%dsn%, %user%, %password%)

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # il servizio tempDb è escluso dall'autowiring

	articles: Model\ArticleRepository  # quindi passa mainDb al costruttore
```

Il servizio `articles` non lancerà un'eccezione perché esistono due servizi compatibili di tipo `PDO` (cioè `mainDb` e `tempDb`) che possono essere passati al costruttore, perché vede solo il servizio `mainDb`.

.[note]
La configurazione dell'autowiring in Nette funziona diversamente rispetto a Symfony, dove l'opzione `autowire: false` indica che l'autowiring non deve essere utilizzato per gli argomenti del costruttore del servizio specificato. In Nette, l'autowiring viene sempre utilizzato, sia per gli argomenti del costruttore che per qualsiasi altro metodo. L'opzione `autowired: false` indica che l'istanza del servizio specificato non deve essere passata da nessuna parte tramite autowiring.


Preferenza dell'autowiring
--------------------------

Se abbiamo più servizi dello stesso tipo e per uno di essi specifichiamo l'opzione `autowired`, questo servizio diventa preferito:

```neon
services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO    # diventa preferito

	tempDb:
		create: PDO('sqlite::memory:')

	articles: Model\ArticleRepository
```

Il servizio `articles` non lancerà un'eccezione perché esistono due servizi compatibili di tipo `PDO` (cioè `mainDb` e `tempDb`), ma utilizzerà il servizio preferito, ovvero `mainDb`.


Array di servizi
----------------

L'Autowiring può anche passare array di servizi di un certo tipo. Poiché in PHP non è possibile scrivere nativamente il tipo degli elementi dell'array, è necessario aggiungere, oltre al tipo `array`, anche un commento phpDoc con il tipo dell'elemento nella forma `ClassName[]`:

```php
namespace Model;

class ShipManager
{
	/**
	 * @param Shipper[] $shippers
	 */
	public function __construct(array $shippers)
	{}
}
```

Il container DI passerà quindi automaticamente un array di servizi corrispondenti al tipo specificato. Ometterà i servizi che hanno l'autowiring disattivato.

Il tipo nel commento può anche essere nella forma `array<int, Class>` o `list<Class>`. Se non puoi influenzare la forma del commento phpDoc, puoi passare l'array di servizi direttamente nella configurazione usando [`typed()` |services#Funzioni speciali].


Argomenti scalari
-----------------

L'Autowiring può fornire solo oggetti e array di oggetti. Gli argomenti scalari (ad es. stringhe, numeri, booleani) [li scriviamo nella configurazione |services#Argomenti]. Un'alternativa è creare un [oggetto-impostazioni |best-practices:passing-settings-to-presenters], che incapsula il valore scalare (o più valori) in un oggetto, che può poi essere nuovamente passato tramite autowiring.

```php
class MySettings
{
	public function __construct(
		// readonly può essere usato da PHP 8.1
		public readonly bool $value,
	)
	{}
}
```

Lo trasformi in un servizio aggiungendolo alla configurazione:

```neon
services:
	- MySettings('any value')
```

Tutte le classi lo richiederanno quindi tramite autowiring.


Restrizione dell'autowiring
---------------------------

Per singoli servizi, l'autowiring può essere ristretto a determinate classi o interfacce.

Normalmente, l'autowiring passa un servizio a ogni parametro di metodo il cui tipo corrisponde al servizio. La restrizione significa che stabiliamo condizioni che i tipi specificati nei parametri dei metodi devono soddisfare affinché il servizio venga loro passato.

Lo mostreremo con un esempio:

```php
class ParentClass
{}

class ChildClass extends ParentClass
{}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}
```

Se li registrassimo tutti come servizi, l'autowiring fallirebbe:

```neon
services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # LANCIA ECCEZIONE, soddisfano i servizi parent e child
	childDep: ChildDependent    # l'autowiring passa il servizio child al costruttore
```

Il servizio `parentDep` lancerà l'eccezione `Multiple services of type ParentClass found: parent, child`, perché entrambi i servizi `parent` e `child` corrispondono al suo costruttore, e l'autowiring non può decidere quale scegliere.

Per il servizio `child`, possiamo quindi restringere il suo autowiring al tipo `ChildClass`:

```neon
services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # si può anche scrivere 'autowired: self'

	parentDep: ParentDependent  # l'autowiring passa il servizio parent al costruttore
	childDep: ChildDependent    # l'autowiring passa il servizio child al costruttore
```

Ora, al costruttore del servizio `parentDep` viene passato il servizio `parent`, perché ora è l'unico oggetto compatibile. L'autowiring non passerà più il servizio `child` lì. Sì, il servizio `child` è ancora di tipo `ParentClass`, ma la condizione restrittiva data per il tipo del parametro non è più valida, cioè non è vero che `ParentClass` *è un supertipo di* `ChildClass`.

Per il servizio `child`, `autowired: ChildClass` potrebbe anche essere scritto come `autowired: self`, poiché `self` è un segnaposto per la classe del servizio corrente.

Nella chiave `autowired` è possibile specificare anche più classi o interfacce come array:

```neon
autowired: [BarClass, FooInterface]
```

Proviamo a completare l'esempio con un'interfaccia:

```php
interface FooInterface
{}

interface BarInterface
{}

class ParentClass implements FooInterface
{}

class ChildClass extends ParentClass implements BarInterface
{}

class FooDependent
{
	function __construct(FooInterface $obj)
	{}
}

class BarDependent
{
	function __construct(BarInterface $obj)
	{}
}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}
```

Se non limitiamo in alcun modo il servizio `child`, corrisponderà ai costruttori di tutte le classi `FooDependent`, `BarDependent`, `ParentDependent` e `ChildDependent` e l'autowiring lo passerà lì.

Ma se restringiamo il suo autowiring a `ChildClass` usando `autowired: ChildClass` (o `self`), l'autowiring lo passerà solo al costruttore di `ChildDependent`, perché richiede un argomento di tipo `ChildClass` ed è vero che `ChildClass` *è di tipo* `ChildClass`. Nessun altro tipo specificato negli altri parametri è un supertipo di `ChildClass`, quindi il servizio non viene passato.

Se lo limitiamo a `ParentClass` usando `autowired: ParentClass`, l'autowiring lo passerà di nuovo al costruttore di `ChildDependent` (perché il `ChildClass` richiesto è un supertipo di `ParentClass`) e ora anche al costruttore di `ParentDependent`, perché anche il tipo `ParentClass` richiesto è compatibile.

Se lo limitiamo a `FooInterface`, sarà ancora autowired in `ParentDependent` (il `ParentClass` richiesto è un supertipo di `FooInterface`) e `ChildDependent`, ma inoltre anche nel costruttore di `FooDependent`, ma non in `BarDependent`, perché `BarInterface` non è un supertipo di `FooInterface`.

```neon
services:
	child:
		create: ChildClass
		autowired: FooInterface

	fooDep: FooDependent        # l'autowiring passa child al costruttore
	barDep: BarDependent        # LANCIA ECCEZIONE, nessun servizio corrisponde
	parentDep: ParentDependent  # l'autowiring passa child al costruttore
	childDep: ChildDependent    # l'autowiring passa child al costruttore
```

Autowiring

L'Autowiring è una funzionalità fantastica che può passare automaticamente i servizi richiesti al costruttore e ad altri metodi, quindi non dobbiamo scriverli affatto. Ti fa risparmiare un sacco di tempo.

Grazie a questo, possiamo omettere la stragrande maggioranza degli argomenti quando scriviamo le definizioni dei servizi. Invece di:

services:
	articles: Model\ArticleRepository(@database, @cache.storage)

Basta scrivere:

services:
	articles: Model\ArticleRepository

L'Autowiring si basa sui tipi, quindi affinché funzioni, la classe ArticleRepository deve essere definita più o meno così:

namespace Model;

class ArticleRepository
{
	public function __construct(\PDO $db, \Nette\Caching\Storage $storage)
	{}
}

Per poter utilizzare l'autowiring, deve esserci esattamente un servizio per ogni tipo nel container. Se ce ne fossero di più, l'autowiring non saprebbe quale passare e lancerebbe un'eccezione:

services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # LANCIA ECCEZIONE, soddisfano sia mainDb che tempDb

La soluzione sarebbe bypassare l'autowiring e specificare esplicitamente il nome del servizio (cioè articles: Model\ArticleRepository(@mainDb)). Ma è più intelligente disattivare l'autowiring per uno dei servizi, o dare la preferenza al primo servizio.

Disattivazione dell'autowiring

Possiamo disattivare l'autowiring di un servizio usando l'opzione autowired: no:

services:
	mainDb: PDO(%dsn%, %user%, %password%)

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # il servizio tempDb è escluso dall'autowiring

	articles: Model\ArticleRepository  # quindi passa mainDb al costruttore

Il servizio articles non lancerà un'eccezione perché esistono due servizi compatibili di tipo PDO (cioè mainDb e tempDb) che possono essere passati al costruttore, perché vede solo il servizio mainDb.

La configurazione dell'autowiring in Nette funziona diversamente rispetto a Symfony, dove l'opzione autowire: false indica che l'autowiring non deve essere utilizzato per gli argomenti del costruttore del servizio specificato. In Nette, l'autowiring viene sempre utilizzato, sia per gli argomenti del costruttore che per qualsiasi altro metodo. L'opzione autowired: false indica che l'istanza del servizio specificato non deve essere passata da nessuna parte tramite autowiring.

Preferenza dell'autowiring

Se abbiamo più servizi dello stesso tipo e per uno di essi specifichiamo l'opzione autowired, questo servizio diventa preferito:

services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO    # diventa preferito

	tempDb:
		create: PDO('sqlite::memory:')

	articles: Model\ArticleRepository

Il servizio articles non lancerà un'eccezione perché esistono due servizi compatibili di tipo PDO (cioè mainDb e tempDb), ma utilizzerà il servizio preferito, ovvero mainDb.

Array di servizi

L'Autowiring può anche passare array di servizi di un certo tipo. Poiché in PHP non è possibile scrivere nativamente il tipo degli elementi dell'array, è necessario aggiungere, oltre al tipo array, anche un commento phpDoc con il tipo dell'elemento nella forma ClassName[]:

namespace Model;

class ShipManager
{
	/**
	 * @param Shipper[] $shippers
	 */
	public function __construct(array $shippers)
	{}
}

Il container DI passerà quindi automaticamente un array di servizi corrispondenti al tipo specificato. Ometterà i servizi che hanno l'autowiring disattivato.

Il tipo nel commento può anche essere nella forma array<int, Class> o list<Class>. Se non puoi influenzare la forma del commento phpDoc, puoi passare l'array di servizi direttamente nella configurazione usando typed().

Argomenti scalari

L'Autowiring può fornire solo oggetti e array di oggetti. Gli argomenti scalari (ad es. stringhe, numeri, booleani) li scriviamo nella configurazione. Un'alternativa è creare un oggetto-impostazioni, che incapsula il valore scalare (o più valori) in un oggetto, che può poi essere nuovamente passato tramite autowiring.

class MySettings
{
	public function __construct(
		// readonly può essere usato da PHP 8.1
		public readonly bool $value,
	)
	{}
}

Lo trasformi in un servizio aggiungendolo alla configurazione:

services:
	- MySettings('any value')

Tutte le classi lo richiederanno quindi tramite autowiring.

Restrizione dell'autowiring

Per singoli servizi, l'autowiring può essere ristretto a determinate classi o interfacce.

Normalmente, l'autowiring passa un servizio a ogni parametro di metodo il cui tipo corrisponde al servizio. La restrizione significa che stabiliamo condizioni che i tipi specificati nei parametri dei metodi devono soddisfare affinché il servizio venga loro passato.

Lo mostreremo con un esempio:

class ParentClass
{}

class ChildClass extends ParentClass
{}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}

Se li registrassimo tutti come servizi, l'autowiring fallirebbe:

services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # LANCIA ECCEZIONE, soddisfano i servizi parent e child
	childDep: ChildDependent    # l'autowiring passa il servizio child al costruttore

Il servizio parentDep lancerà l'eccezione Multiple services of type ParentClass found: parent, child, perché entrambi i servizi parent e child corrispondono al suo costruttore, e l'autowiring non può decidere quale scegliere.

Per il servizio child, possiamo quindi restringere il suo autowiring al tipo ChildClass:

services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # si può anche scrivere 'autowired: self'

	parentDep: ParentDependent  # l'autowiring passa il servizio parent al costruttore
	childDep: ChildDependent    # l'autowiring passa il servizio child al costruttore

Ora, al costruttore del servizio parentDep viene passato il servizio parent, perché ora è l'unico oggetto compatibile. L'autowiring non passerà più il servizio child lì. Sì, il servizio child è ancora di tipo ParentClass, ma la condizione restrittiva data per il tipo del parametro non è più valida, cioè non è vero che ParentClass è un supertipo di ChildClass.

Per il servizio child, autowired: ChildClass potrebbe anche essere scritto come autowired: self, poiché self è un segnaposto per la classe del servizio corrente.

Nella chiave autowired è possibile specificare anche più classi o interfacce come array:

autowired: [BarClass, FooInterface]

Proviamo a completare l'esempio con un'interfaccia:

interface FooInterface
{}

interface BarInterface
{}

class ParentClass implements FooInterface
{}

class ChildClass extends ParentClass implements BarInterface
{}

class FooDependent
{
	function __construct(FooInterface $obj)
	{}
}

class BarDependent
{
	function __construct(BarInterface $obj)
	{}
}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}

Se non limitiamo in alcun modo il servizio child, corrisponderà ai costruttori di tutte le classi FooDependent, BarDependent, ParentDependent e ChildDependent e l'autowiring lo passerà lì.

Ma se restringiamo il suo autowiring a ChildClass usando autowired: ChildClass (o self), l'autowiring lo passerà solo al costruttore di ChildDependent, perché richiede un argomento di tipo ChildClass ed è vero che ChildClass è di tipo ChildClass. Nessun altro tipo specificato negli altri parametri è un supertipo di ChildClass, quindi il servizio non viene passato.

Se lo limitiamo a ParentClass usando autowired: ParentClass, l'autowiring lo passerà di nuovo al costruttore di ChildDependent (perché il ChildClass richiesto è un supertipo di ParentClass) e ora anche al costruttore di ParentDependent, perché anche il tipo ParentClass richiesto è compatibile.

Se lo limitiamo a FooInterface, sarà ancora autowired in ParentDependent (il ParentClass richiesto è un supertipo di FooInterface) e ChildDependent, ma inoltre anche nel costruttore di FooDependent, ma non in BarDependent, perché BarInterface non è un supertipo di FooInterface.

services:
	child:
		create: ChildClass
		autowired: FooInterface

	fooDep: FooDependent        # l'autowiring passa child al costruttore
	barDep: BarDependent        # LANCIA ECCEZIONE, nessun servizio corrisponde
	parentDep: ParentDependent  # l'autowiring passa child al costruttore
	childDep: ChildDependent    # l'autowiring passa child al costruttore