-
-
Notifications
You must be signed in to change notification settings - Fork 282
/
autowiring.texy
260 lines (186 loc) · 9.4 KB
/
autowiring.texy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
Cablare automată
****************
.[perex]
Autowiring este o caracteristică grozavă care poate trece automat servicii către constructor și alte metode, astfel încât nu trebuie să le scriem deloc. Aceasta vă economisește mult timp.
Acest lucru ne permite să sărim peste marea majoritate a argumentelor atunci când scriem definițiile serviciilor. În loc de:
```neon
services:
articles: Model\ArticleRepository(@database, @cache.storage)
```
Scrieți doar:
```neon
services:
articles: Model\ArticleRepository
```
Cablarea automată este determinată de tipuri, astfel încât clasa `ArticleRepository` trebuie să fie definită după cum urmează:
```php
namespace Model;
class ArticleRepository
{
public function __construct(\PDO $db, \Nette\Caching\Storage $storage)
{}
}
```
Pentru a utiliza autowiring, trebuie să existe **un singur serviciu** pentru fiecare tip din container. Dacă ar exista mai multe, autowiring nu ar ști pe care să îl treacă și ar arunca o excepție:
```neon
services:
mainDb: PDO(%dsn%, %user%, %password%)
tempDb: PDO('sqlite::memory:')
articles: Model\ArticleRepository # THROWS EXCEPTION, atât mainDb cât și tempDb se potrivesc
```
Soluția ar fi fie să se ocolească autowiring și să se precizeze explicit numele serviciului (de exemplu, `articles: Model\ArticleRepository(@mainDb)`). Cu toate acestea, este mai convenabil să se [dezactiveze |#Disabled autowiring] autowiring-ul pentru un singur serviciu, sau să se [prefere |#Preferred Autowiring] primul serviciu.
Autowiring dezactivat .[#toc-disabled-autowiring]
-------------------------------------------------
Puteți dezactiva cablarea automată a serviciilor utilizând opțiunea `autowired: no`:
```neon
services:
mainDb: PDO(%dsn%, %user%, %password%)
tempDb:
create: PDO('sqlite::memory:')
autowired: false # elimină tempDb din autowiring
articles: Model\ArticleRepository # prin urmare, trece mainDb la constructor
```
Serviciul `articles` nu aruncă excepția că există două servicii corespunzătoare de tip `PDO` (de exemplu, `mainDb` și `tempDb`) care pot fi transmise constructorului, deoarece acesta vede doar serviciul `mainDb`.
.[note]
Configurarea autowiring-ului în Nette funcționează diferit față de Symfony, unde opțiunea `autowire: false` spune că autowiring-ul nu trebuie folosit pentru argumentele constructorului serviciului.
În Nette, autowiring este întotdeauna folosit, fie pentru argumentele constructorului, fie pentru orice altă metodă. Opțiunea `autowired: false` spune că instanța serviciului nu trebuie să fie transmisă nicăieri folosind autowiring.
Autowiring preferat .[#toc-preferred-autowiring]
------------------------------------------------
În cazul în care avem mai multe servicii de același tip și unul dintre ele are opțiunea `autowired`, acest serviciu devine cel preferat:
```neon
services:
mainDb:
create: PDO(%dsn%, %user%, %password%)
autowired: PDO # îl face preferat
tempDb:
create: PDO('sqlite::memory:')
articles: Model\ArticleRepository
```
Serviciul `articles` nu aruncă excepția că există două servicii `PDO` corespunzătoare (adică `mainDb` și `tempDb`), ci utilizează serviciul preferat, adică `mainDb`.
Colecția de servicii .[#toc-collection-of-services]
---------------------------------------------------
Autowiring poate transmite, de asemenea, o serie de servicii de un anumit tip. Deoarece PHP nu poate nota în mod nativ tipul de elemente ale tabloului, pe lângă tipul `array`, trebuie adăugat un comentariu phpDoc cu tipul de element, cum ar fi `ClassName[]`:
```php
namespace Model;
class ShipManager
{
/**
* @param Shipper[] $shippers
*/
public function __construct(array $shippers)
{}
}
```
Recipientul DI transmite apoi automat un array de servicii care corespund tipului dat. Acesta va omite serviciile care au cablarea automată dezactivată.
Tipul din comentariu poate fi, de asemenea, de forma `array<int, Class>` sau `list<Class>`. Dacă nu puteți controla forma comentariului phpDoc, puteți trece o matrice de servicii direct în configurație folosind [`typed()` |services#Special Functions].
Argumente scalare .[#toc-scalar-arguments]
------------------------------------------
Autowiring poate transmite numai obiecte și array-uri de obiecte. Argumentele scalare (de exemplu, șiruri de caractere, numere, booleeni) se [scriu în configurare |services#Arguments].
O alternativă este crearea unui [obiect de configurare |best-practices:passing-settings-to-presenters] care încapsulează o valoare scalară (sau mai multe valori) sub forma unui obiect, care poate fi apoi transmis din nou cu ajutorul autowiring-ului.
```php
class MySettings
{
public function __construct(
// readonly poate fi utilizat începând cu PHP 8.1
public readonly bool $value,
)
{}
}
```
Creați un serviciu adăugându-l la configurație:
```neon
services:
- MySettings('any value')
```
Toate clasele îl vor solicita apoi prin cablare automată.
Restrângerea cablării automate .[#toc-narrowing-of-autowiring]
--------------------------------------------------------------
Pentru servicii individuale, autocablarea poate fi restrânsă la anumite clase sau interfețe.
În mod normal, autowiring-ul trece serviciul la fiecare parametru al metodei căruia îi corespunde tipul serviciului. Restrângerea înseamnă că se specifică condițiile pe care tipurile specificate pentru parametrii metodei trebuie să le îndeplinească pentru ca serviciul să le fie transmis.
Să luăm un exemplu:
```php
class ParentClass
{}
class ChildClass extends ParentClass
{}
class ParentDependent
{
function __construct(ParentClass $obj)
{}
}
class ChildDependent
{
function __construct(ChildClass $obj)
{}
}
```
Dacă le-am înregistrat pe toate ca servicii, cablarea automată ar eșua:
```neon
services:
parent: ParentClass
child: ChildClass
parentDep: ParentDependent # EXCEPȚIE DE ÎNTÂRZIERE, atât părintele cât și copilul se potrivesc
childDep: ChildDependent # trece serviciul "child" în constructor
```
Serviciul `parentDep` aruncă excepția `Multiple services of type ParentClass found: parent, child`, deoarece atât `parent`, cât și `child` se potrivesc în constructorul său, iar autowiring nu poate lua o decizie cu privire la care să o aleagă.
Prin urmare, pentru serviciul `child`, putem restrânge cablarea automată la `ChildClass`:
```neon
services:
parent: ParentClass
child:
create: ChildClass
autowired: ChildClass # alternative: 'autowired: self'
parentDep: ParentDependent # Se produce o EXCEPȚIE, 'child' nu poate fi autowired.
childDep: ChildDependent # transmite serviciul "child" la constructor
```
Serviciul `parentDep` este transmis acum constructorului serviciului `parentDep`, deoarece este acum singurul obiect corespunzător. Serviciul `child` nu mai este transmis prin autocablare. Da, serviciul `child` este în continuare de tipul `ParentClass`, dar condiția de restrângere dată pentru tipul de parametru nu se mai aplică, adică nu mai este adevărat că `ParentClass` *este un supratip* al `ChildClass`.
În cazul `child`, `autowired: ChildClass` ar putea fi scris ca `autowired: self`, deoarece `self` înseamnă tipul de serviciu curent.
Cheia `autowired` poate include mai multe clase și interfețe ca matrice:
```neon
autowired: [BarClass, FooInterface]
```
Să încercăm să adăugăm interfețe la exemplu:
```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)
{}
}
```
Atunci când nu limităm serviciul `child`, acesta se va potrivi în constructorii tuturor claselor `FooDependent`, `BarDependent`, `ParentDependent` și `ChildDependent` și autowiring îl va trece acolo.
Cu toate acestea, dacă limităm autowiring-ul la `ChildClass` folosind `autowired: ChildClass` (sau `self`), autowiring-ul îl trece doar la constructorul `ChildDependent`, deoarece necesită un argument de tip `ChildClass` și `ChildClass` *este de tip* `ChildClass`. Niciun alt tip specificat pentru ceilalți parametri nu este un supraansamblu al `ChildClass`, astfel încât serviciul nu este transmis.
Dacă îl restricționăm la `ParentClass` folosind `autowired: ParentClass`, autowiring îl va trece din nou la constructorul `ChildDependent` (deoarece tipul necesar `ChildClass` este un supraansamblu al `ParentClass`) și la constructorul `ParentDependent` de asemenea, deoarece tipul necesar al `ParentClass` este de asemenea corespunzător.
Dacă îl restricționăm la `FooInterface`, acesta va trece în continuare automat la `ParentDependent` (tipul necesar `ParentClass` este un supratip al `FooInterface`) și la `ChildDependent`, dar și la constructorul `FooDependent`, dar nu și la `BarDependent`, deoarece `BarInterface` nu este un supratip al `FooInterface`.
```neon
services:
child:
create: ChildClass
autowired: FooInterface
fooDep: FooDependent # transmite copilul serviciului către constructor
barDep: BarDependent # EXCEPȚIE, niciun serviciu nu ar trece
parentDep: ParentDependent # trece serviciul copil la constructor
childDep: ChildDependent # trece serviciul copil la constructor
```