mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 06:52:09 +01:00
Compare commits
1052 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69958152e6 | ||
|
|
cf8f5f9f93 | ||
|
|
60c245413d | ||
|
|
7c9b74221f | ||
|
|
c7e5605d11 | ||
|
|
c19afa1529 | ||
|
|
516b593193 | ||
|
|
e2b971d7c5 | ||
|
|
19d9244a88 | ||
|
|
10a5a3ff73 | ||
|
|
f5fb400d0f | ||
|
|
a321331c89 | ||
|
|
522863116a | ||
|
|
5bfb744967 | ||
|
|
8ed6c2234a | ||
|
|
ff612b9678 | ||
|
|
ee0d7197dd | ||
|
|
39d2136f46 | ||
|
|
c223b8f635 | ||
|
|
bea454eefc | ||
|
|
14f2572e4e | ||
|
|
c2c500077b | ||
|
|
6281c2b79f | ||
|
|
bac1c17eab | ||
|
|
b6137c8911 | ||
|
|
da7854f586 | ||
|
|
5f4ecfd1d8 | ||
|
|
51be1b1d52 | ||
|
|
5f39343bfd | ||
|
|
7ef1f0a379 | ||
|
|
488a5dd3bf | ||
|
|
30795559dc | ||
|
|
f71725575c | ||
|
|
4a3c7f05bf | ||
|
|
896c65504d | ||
|
|
16a8f10fd2 | ||
|
|
498de4c564 | ||
|
|
d80a831157 | ||
|
|
52660297ab | ||
|
|
b44774285b | ||
|
|
58287bb731 | ||
|
|
bc37f75b41 | ||
|
|
8a25b264f7 | ||
|
|
0e48b19cd3 | ||
|
|
d2978303f0 | ||
|
|
109042e5af | ||
|
|
08328adc6c | ||
|
|
191a5366b1 | ||
|
|
65806884b0 | ||
|
|
ad80e8281a | ||
|
|
44dddb2eee | ||
|
|
0c0c61c51b | ||
|
|
cc28fed9f5 | ||
|
|
2245149588 | ||
|
|
b13564c6c0 | ||
|
|
91709c1275 | ||
|
|
434b7cee2a | ||
|
|
7f0a181e39 | ||
|
|
d18126aac5 | ||
|
|
b7fd8241cf | ||
|
|
2432939e4f | ||
|
|
93ce84fa6e | ||
|
|
1bf4603422 | ||
|
|
e6961bd968 | ||
|
|
25d5bc5b46 | ||
|
|
5724e6279e | ||
|
|
cfc0655a1c | ||
|
|
6cde337777 | ||
|
|
c6b2d89748 | ||
|
|
e1dc94d1c2 | ||
|
|
74ef28295a | ||
|
|
831a1eb7d2 | ||
|
|
3a82b153f3 | ||
|
|
168ac31084 | ||
|
|
6f93cebe6e | ||
|
|
8c582a49d3 | ||
|
|
5f1fe1587c | ||
|
|
fe4a2e83cf | ||
|
|
205b2f5f20 | ||
|
|
3f550c19e3 | ||
|
|
8ac6a13ca0 | ||
|
|
2707b09a07 | ||
|
|
121158f92c | ||
|
|
56cd688c4a | ||
|
|
96546caceb | ||
|
|
57247ed6ca | ||
|
|
12817076c3 | ||
|
|
4c2f104d42 | ||
|
|
ef64cf7c33 | ||
|
|
0983d3a4af | ||
|
|
51ad860a25 | ||
|
|
9bd51aaeb6 | ||
|
|
1fe1a6a048 | ||
|
|
c37b115450 | ||
|
|
19129e9f8a | ||
|
|
efe62e3f0b | ||
|
|
7d01f19667 | ||
|
|
722cea6536 | ||
|
|
c1bb2ccf4b | ||
|
|
e3d7c6076c | ||
|
|
ce7d93f14d | ||
|
|
a139a1b63c | ||
|
|
1153b9468c | ||
|
|
40f299f1eb | ||
|
|
d0e9177121 | ||
|
|
428032ca7c | ||
|
|
68af854f46 | ||
|
|
77467cd824 | ||
|
|
f666aa641e | ||
|
|
ca3319c2f6 | ||
|
|
c06f6b9376 | ||
|
|
802f20b8e7 | ||
|
|
96d13ac62a | ||
|
|
ed53defaa1 | ||
|
|
2ea6a1a5fb | ||
|
|
41cb5fbbbf | ||
|
|
83851a9716 | ||
|
|
066ec1ac81 | ||
|
|
68744489f0 | ||
|
|
cc2ad1993c | ||
|
|
e4d46c4276 | ||
|
|
858a1adc3b | ||
|
|
3b499132d9 | ||
|
|
39153fd88a | ||
|
|
bdc9679e37 | ||
|
|
87a8ee21c9 | ||
|
|
59c8bc09ab | ||
|
|
3a7d7c9f57 | ||
|
|
06eca40134 | ||
|
|
23b35e9554 | ||
|
|
e063926cbd | ||
|
|
4a01a76a17 | ||
|
|
93c2dd9d4b | ||
|
|
75bc22980e | ||
|
|
9696c3434d | ||
|
|
9d4f54b9a4 | ||
|
|
37946d3a21 | ||
|
|
baf96cdad4 | ||
|
|
ce09c96427 | ||
|
|
ae659fe650 | ||
|
|
0a177d5074 | ||
|
|
dbfe47b07b | ||
|
|
bf3e082c00 | ||
|
|
d31aabb40c | ||
|
|
22b1f52c1c | ||
|
|
d66884403f | ||
|
|
a90ee5c495 | ||
|
|
11270425e5 | ||
|
|
552eae37a3 | ||
|
|
ee4b03aa78 | ||
|
|
f1246d57c2 | ||
|
|
a14ef7c279 | ||
|
|
54c29140fa | ||
|
|
eb49f66926 | ||
|
|
daa99f197b | ||
|
|
2b04cc2e3f | ||
|
|
3d9af3187f | ||
|
|
e83d8a80ba | ||
|
|
c5291b4de8 | ||
|
|
029ca611f0 | ||
|
|
831d86548c | ||
|
|
f26b3b9cf9 | ||
|
|
9e7715f678 | ||
|
|
9ab84f7478 | ||
|
|
e6bb4ef20e | ||
|
|
0e26e3ed50 | ||
|
|
63315c8e4a | ||
|
|
8ca99fdfdc | ||
|
|
8b6a58fa0e | ||
|
|
2d8e466636 | ||
|
|
94986af284 | ||
|
|
ad5c8e4bdc | ||
|
|
c363f55ad1 | ||
|
|
c973a62272 | ||
|
|
8d3446015a | ||
|
|
4e335f4044 | ||
|
|
bb36d49b38 | ||
|
|
2b81a8e260 | ||
|
|
7d3b3f28e9 | ||
|
|
cbec236e8b | ||
|
|
306963fe79 | ||
|
|
fb4578406f | ||
|
|
bdc41e2b5e | ||
|
|
90376a6431 | ||
|
|
97634ae6a1 | ||
|
|
b725908c83 | ||
|
|
f79d166a4e | ||
|
|
9c22814cfa | ||
|
|
b274893486 | ||
|
|
e0e55dc9c5 | ||
|
|
010b1e0886 | ||
|
|
93eb8a1bcb | ||
|
|
1464827220 | ||
|
|
8709fb38b0 | ||
|
|
cbb6c897de | ||
|
|
e9e60f2fbc | ||
|
|
5f3c1dbab8 | ||
|
|
753bc16c0b | ||
|
|
6090141e0b | ||
|
|
e4a6c041b5 | ||
|
|
be307edba8 | ||
|
|
c54c557e02 | ||
|
|
46d0865339 | ||
|
|
4672d284ff | ||
|
|
9c56071392 | ||
|
|
0a1988b349 | ||
|
|
1a5a4c674a | ||
|
|
95795c87a8 | ||
|
|
083f642cfa | ||
|
|
db6e702088 | ||
|
|
4175edf311 | ||
|
|
716da7e538 | ||
|
|
67ac5a82da | ||
|
|
e384978e0b | ||
|
|
5ccbc201bf | ||
|
|
d15624f72f | ||
|
|
9d1a4973ae | ||
|
|
55c4845d57 | ||
|
|
a38f473a92 | ||
|
|
bcdc5bdaf4 | ||
|
|
40a0964f06 | ||
|
|
08a9e60ed0 | ||
|
|
3e3c023c95 | ||
|
|
5e6d5c06a9 | ||
|
|
1622b7877d | ||
|
|
80aae2796d | ||
|
|
528ef40fc4 | ||
|
|
4b4b9b7b6f | ||
|
|
ae842259f5 | ||
|
|
69f51cc794 | ||
|
|
7178b9d6b7 | ||
|
|
8a14eee67a | ||
|
|
f9331ee2b9 | ||
|
|
c5315f86fb | ||
|
|
5820bb8f49 | ||
|
|
80278c545e | ||
|
|
cb05f1aadf | ||
|
|
ab616f1a1d | ||
|
|
820a0da4c1 | ||
|
|
fcd02b1ee2 | ||
|
|
90962f060a | ||
|
|
758f0d7605 | ||
|
|
eb8510ff5c | ||
|
|
d5fdd676f4 | ||
|
|
b0d07ffaba | ||
|
|
196d3a6996 | ||
|
|
a3e3a3bbf3 | ||
|
|
abcad6fa45 | ||
|
|
1b6cf58a1a | ||
|
|
6501890ab5 | ||
|
|
e399d21fb3 | ||
|
|
16f355f0cc | ||
|
|
7d1444e5b6 | ||
|
|
25d5936337 | ||
|
|
68f9bf5dfa | ||
|
|
94d45a036f | ||
|
|
9acca2252f | ||
|
|
716fc97b70 | ||
|
|
a809a71aa6 | ||
|
|
4617a5e310 | ||
|
|
e77c5a3a5e | ||
|
|
c3cc0fdd8c | ||
|
|
bd4449c462 | ||
|
|
e3e96745cc | ||
|
|
12e0cefba1 | ||
|
|
21221f73cc | ||
|
|
ab5e9e393b | ||
|
|
507c73c073 | ||
|
|
ba0ea8953b | ||
|
|
e62571c8f4 | ||
|
|
53763d432b | ||
|
|
154920a0b3 | ||
|
|
b8d0a85017 | ||
|
|
98f9de2af6 | ||
|
|
52a6a21387 | ||
|
|
cb497826be | ||
|
|
ba0d3842a9 | ||
|
|
bf49055a1f | ||
|
|
29e1935c65 | ||
|
|
694413a888 | ||
|
|
33e02b2796 | ||
|
|
26f7588479 | ||
|
|
20a6efdff6 | ||
|
|
83c81f6c41 | ||
|
|
4fc8629414 | ||
|
|
791667a9e4 | ||
|
|
95da667862 | ||
|
|
feb27f00c1 | ||
|
|
c02ddd692f | ||
|
|
151a3fba9d | ||
|
|
b187bc8588 | ||
|
|
1e056842fe | ||
|
|
ebb0c67ecc | ||
|
|
abd9186d00 | ||
|
|
719d007a81 | ||
|
|
08d3f72755 | ||
|
|
3f7a3333ad | ||
|
|
2a8802af12 | ||
|
|
9cc11d2541 | ||
|
|
ee5b2ce5b0 | ||
|
|
d54c9678d0 | ||
|
|
859e6af972 | ||
|
|
8c3c9f115d | ||
|
|
3907872046 | ||
|
|
779781173a | ||
|
|
54cd70002c | ||
|
|
76c4539ffa | ||
|
|
0f8d193512 | ||
|
|
cc314d0fb7 | ||
|
|
2df4d75565 | ||
|
|
dc21ab63ac | ||
|
|
2a250b5814 | ||
|
|
c9c493b2fe | ||
|
|
e6eef1a97d | ||
|
|
8d4718f875 | ||
|
|
44fa5d340a | ||
|
|
708146bbbc | ||
|
|
a5bf9bb96a | ||
|
|
3eace16e85 | ||
|
|
e4c27092cd | ||
|
|
adadf1fb90 | ||
|
|
380b5b62ef | ||
|
|
a0e7a59572 | ||
|
|
fb6c0c1d8b | ||
|
|
fcf1116e33 | ||
|
|
78dc63df27 | ||
|
|
bc5efd4bfe | ||
|
|
c0dfba2ef3 | ||
|
|
0efac09141 | ||
|
|
b6b4cbcb93 | ||
|
|
b1f553eba3 | ||
|
|
0c4aac5a35 | ||
|
|
e0081b59be | ||
|
|
4bd574daee | ||
|
|
efb6cebd41 | ||
|
|
e4769d3191 | ||
|
|
cf408ad9ae | ||
|
|
7c29078051 | ||
|
|
d5ba106803 | ||
|
|
b59189ab48 | ||
|
|
f9a4adc8ab | ||
|
|
401a0c4fe9 | ||
|
|
dba9d72b2d | ||
|
|
fe0647053a | ||
|
|
7b3db4a037 | ||
|
|
6672aaf165 | ||
|
|
aa3b331cae | ||
|
|
e5e3166747 | ||
|
|
3918dcfb42 | ||
|
|
6290747bf9 | ||
|
|
b6f4220493 | ||
|
|
bfb033fe3c | ||
|
|
afbf293c94 | ||
|
|
bf86155dc2 | ||
|
|
1d218bae30 | ||
|
|
9acc70d5b8 | ||
|
|
b7860c782b | ||
|
|
7baef1e120 | ||
|
|
9a24ce5fad | ||
|
|
9fcb8f1305 | ||
|
|
5a40b99e11 | ||
|
|
5049b615c5 | ||
|
|
94144e1227 | ||
|
|
599dd58fe1 | ||
|
|
aff543a4ff | ||
|
|
1854ce2d32 | ||
|
|
b00f0c258e | ||
|
|
13a79b068c | ||
|
|
27c9e9cab3 | ||
|
|
1051817d92 | ||
|
|
40fbbf4429 | ||
|
|
00ed2ca991 | ||
|
|
54b7ad2073 | ||
|
|
517d038e5b | ||
|
|
3db79ebbf3 | ||
|
|
6f98147d09 | ||
|
|
a2faeb9a26 | ||
|
|
3764ebf7a3 | ||
|
|
a7d5adb3ce | ||
|
|
6f507c322a | ||
|
|
54013671a7 | ||
|
|
f5dea25b6c | ||
|
|
7527b788de | ||
|
|
cfadb5499d | ||
|
|
e52bc846f0 | ||
|
|
9ce9ae2818 | ||
|
|
f259754b7c | ||
|
|
3bc2cb6b15 | ||
|
|
fdb9d44538 | ||
|
|
a9fcaf1d18 | ||
|
|
5b8b5f28f5 | ||
|
|
2e155e98a7 | ||
|
|
c7a91a459c | ||
|
|
1df03f21e6 | ||
|
|
09f0966ad6 | ||
|
|
da6c6f7045 | ||
|
|
fd8d981f30 | ||
|
|
db4d00a58f | ||
|
|
1a5942a4d9 | ||
|
|
282b8fbfe8 | ||
|
|
537a27d277 | ||
|
|
cf7757e090 | ||
|
|
69cc78c259 | ||
|
|
2893394673 | ||
|
|
8aa6a5f401 | ||
|
|
588d29d789 | ||
|
|
0a3fbac8af | ||
|
|
1556a1ff7a | ||
|
|
64e4a2129c | ||
|
|
7361d3e57d | ||
|
|
28d03e41f7 | ||
|
|
f8ced51687 | ||
|
|
2c06ffa4a7 | ||
|
|
9a281f54de | ||
|
|
b73d44b46b | ||
|
|
b971cbeebb | ||
|
|
fcba06f64c | ||
|
|
7974a92bf9 | ||
|
|
124593383b | ||
|
|
56df970d8b | ||
|
|
21466a0524 | ||
|
|
010cd948c9 | ||
|
|
30626ac6bb | ||
|
|
1f62233bf6 | ||
|
|
ffbe56779d | ||
|
|
bdc039fe31 | ||
|
|
73288bc472 | ||
|
|
8ab0af3d30 | ||
|
|
82f4a177b2 | ||
|
|
1fef3196d9 | ||
|
|
ad519a5c6f | ||
|
|
4444065531 | ||
|
|
e280b2df26 | ||
|
|
3c34265666 | ||
|
|
8693fa790c | ||
|
|
e997c6688c | ||
|
|
03148b1300 | ||
|
|
f47d68c826 | ||
|
|
06eb00d687 | ||
|
|
7827453b94 | ||
|
|
6f3140a09e | ||
|
|
2d31d71961 | ||
|
|
2db1f76dee | ||
|
|
6e61571edc | ||
|
|
22e7c02cf7 | ||
|
|
4a5f48f7ff | ||
|
|
5534db1810 | ||
|
|
18897fa7d5 | ||
|
|
689da1f251 | ||
|
|
5c250063b8 | ||
|
|
99fe3b5c5b | ||
|
|
af5c250cc8 | ||
|
|
1fe0910d6b | ||
|
|
2cf0c59f7a | ||
|
|
7e51a09347 | ||
|
|
906e91cbb4 | ||
|
|
2aaec3a79a | ||
|
|
4799c418c9 | ||
|
|
a1d7de6b66 | ||
|
|
83d46d7cf9 | ||
|
|
f3b6c4b356 | ||
|
|
343afda275 | ||
|
|
27009986d7 | ||
|
|
e71868fe8b | ||
|
|
795e7735d8 | ||
|
|
46ef989f38 | ||
|
|
8a4d35d5e7 | ||
|
|
faec95fed2 | ||
|
|
c69044bc9b | ||
|
|
9a1085d72f | ||
|
|
501226b90b | ||
|
|
2c39b3f118 | ||
|
|
436f0467ec | ||
|
|
394f6fe1b4 | ||
|
|
7d9a2c0a71 | ||
|
|
2dc19e6919 | ||
|
|
b68cab1b3d | ||
|
|
476b68cd1f | ||
|
|
3ffd3f6509 | ||
|
|
1733bf8678 | ||
|
|
84a87a6ac8 | ||
|
|
6fa95b93df | ||
|
|
2004761475 | ||
|
|
3ad9c43a96 | ||
|
|
1bd3066b9f | ||
|
|
663763ab1a | ||
|
|
2a8f22c521 | ||
|
|
efd429dd24 | ||
|
|
716139c168 | ||
|
|
492635a8e8 | ||
|
|
0dad2daf2c | ||
|
|
e0b72408a1 | ||
|
|
575d0c03ee | ||
|
|
a2a4f73b30 | ||
|
|
f1d6ba8919 | ||
|
|
0872d1429d | ||
|
|
96f5f84e8b | ||
|
|
0945f60484 | ||
|
|
f38922944e | ||
|
|
63e9ad9e9a | ||
|
|
46ff264502 | ||
|
|
8b1ce48e79 | ||
|
|
07674b1887 | ||
|
|
ec43884612 | ||
|
|
ee62934911 | ||
|
|
8a6dbb4ae2 | ||
|
|
2a8a886bbe | ||
|
|
2b03858717 | ||
|
|
497faffe0b | ||
|
|
8a762f04f1 | ||
|
|
bbc56aaec6 | ||
|
|
647712649b | ||
|
|
61f2c4d53b | ||
|
|
ca8ee91467 | ||
|
|
2987e9956f | ||
|
|
917e0cff39 | ||
|
|
6ce0cf4a3d | ||
|
|
d4915a8b3d | ||
|
|
749947efe7 | ||
|
|
535d869835 | ||
|
|
ea23628010 | ||
|
|
10fa1e07b2 | ||
|
|
c32efc2588 | ||
|
|
6032a2ccf7 | ||
|
|
1f9d1bf4cb | ||
|
|
dff40cf351 | ||
|
|
a55c72b47d | ||
|
|
f03db50be1 | ||
|
|
2a77776c91 | ||
|
|
c51c84fc47 | ||
|
|
00beecce74 | ||
|
|
9f60fdc288 | ||
|
|
3b88e4844a | ||
|
|
63561fc382 | ||
|
|
caa8041963 | ||
|
|
1c8b5181b5 | ||
|
|
e5eaf9b855 | ||
|
|
380f8a42f3 | ||
|
|
05b5a64cb9 | ||
|
|
05678dcd97 | ||
|
|
a9c80c2293 | ||
|
|
e18b80c1bf | ||
|
|
3ddb2a845c | ||
|
|
9baf213673 | ||
|
|
f874bc85f1 | ||
|
|
d075f5d5f8 | ||
|
|
fac8d84fdb | ||
|
|
09680b18ee | ||
|
|
caf8d876da | ||
|
|
629c600b10 | ||
|
|
7f46fad0f0 | ||
|
|
559e1d139a | ||
|
|
bd9cd989a0 | ||
|
|
f7e49a53be | ||
|
|
ade5559ed5 | ||
|
|
c019f8649b | ||
|
|
68571184ca | ||
|
|
f171d49dc0 | ||
|
|
ae88844353 | ||
|
|
8d1d32ce81 | ||
|
|
01aa9a5870 | ||
|
|
bf7ffa94cf | ||
|
|
672ff01f44 | ||
|
|
fd64e137c8 | ||
|
|
49cc83d273 | ||
|
|
ada109bc90 | ||
|
|
303baeea33 | ||
|
|
eeae5734a5 | ||
|
|
b6860328c0 | ||
|
|
3e04516740 | ||
|
|
ec9e79a35b | ||
|
|
2d3e89e2cb | ||
|
|
727a9914f2 | ||
|
|
5aebfefc45 | ||
|
|
118eb87032 | ||
|
|
7df7503af6 | ||
|
|
c49bba7a5e | ||
|
|
2acb298e74 | ||
|
|
eaac5cd3dc | ||
|
|
14d615201e | ||
|
|
545070f879 | ||
|
|
9d3ae4a55a | ||
|
|
c213974234 | ||
|
|
e22592f06b | ||
|
|
e4a7403ead | ||
|
|
175cdefd67 | ||
|
|
e8376b3995 | ||
|
|
b87f86e6f0 | ||
|
|
df7a5bc712 | ||
|
|
fc314a2abf | ||
|
|
ebfab93d15 | ||
|
|
13d8695780 | ||
|
|
cc72796fff | ||
|
|
35a29731bb | ||
|
|
7568328a6d | ||
|
|
b904f44c6c | ||
|
|
46ab755728 | ||
|
|
476ffd5cd8 | ||
|
|
59fb8ca571 | ||
|
|
c44e19a5e7 | ||
|
|
b69e8e0267 | ||
|
|
4444a29c14 | ||
|
|
9c2c1178e2 | ||
|
|
f98b33f764 | ||
|
|
759ae5678f | ||
|
|
6b61e52baa | ||
|
|
21c3f4d826 | ||
|
|
7a7464e6d3 | ||
|
|
0f77181a2c | ||
|
|
f86fa175df | ||
|
|
f5e7ddb21c | ||
|
|
7e2eb61deb | ||
|
|
dfcb20ea1e | ||
|
|
ce03a61b7a | ||
|
|
0f25ae4b83 | ||
|
|
4fe224a320 | ||
|
|
703648580a | ||
|
|
09caeb2753 | ||
|
|
7f9827df4f | ||
|
|
941292fe85 | ||
|
|
de8b444938 | ||
|
|
e8c1e6c1d3 | ||
|
|
4ad077b3d7 | ||
|
|
9415275195 | ||
|
|
5c34678580 | ||
|
|
642a20bcff | ||
|
|
4d8cf2698e | ||
|
|
5233a139bb | ||
|
|
fbcf91d074 | ||
|
|
3d1f172788 | ||
|
|
6ea4411789 | ||
|
|
4357161c3d | ||
|
|
3b5bd316e6 | ||
|
|
2d92c09101 | ||
|
|
4c1e520bd3 | ||
|
|
7a08253f18 | ||
|
|
e6382d393e | ||
|
|
3cd65b12a8 | ||
|
|
d4c53a8885 | ||
|
|
c8b0931f9c | ||
|
|
c226b951b3 | ||
|
|
91b662edb6 | ||
|
|
c313440962 | ||
|
|
a86d5a799d | ||
|
|
9b1f397ab2 | ||
|
|
d83b1e338c | ||
|
|
07937b5b9a | ||
|
|
a3db423eab | ||
|
|
2b2f4e8acc | ||
|
|
84e78b8200 | ||
|
|
601948baaa | ||
|
|
19168ca8d6 | ||
|
|
5e828e20cf | ||
|
|
3a10714d6f | ||
|
|
7e379f6872 | ||
|
|
8b1c353650 | ||
|
|
75bff1d983 | ||
|
|
8b35dae5a9 | ||
|
|
9062af45ca | ||
|
|
57f256912a | ||
|
|
b31afda33b | ||
|
|
ccb9ef7246 | ||
|
|
bdb3fc72da | ||
|
|
8aa05b89a0 | ||
|
|
d8a2e329b6 | ||
|
|
ad58c6a6e2 | ||
|
|
8f360f08c5 | ||
|
|
2e2bca8cf8 | ||
|
|
32eae38f07 | ||
|
|
3a2bfce92c | ||
|
|
57e18d0b1b | ||
|
|
b3f9acd1bf | ||
|
|
cdc7feccc8 | ||
|
|
275fe31c04 | ||
|
|
8e9ea9e9e0 | ||
|
|
92a8453b92 | ||
|
|
7fd49b7547 | ||
|
|
8212ba8803 | ||
|
|
2bfcb6e5ab | ||
|
|
9d8cadf1fc | ||
|
|
a17809c3b9 | ||
|
|
9dbe3d003a | ||
|
|
38c476f704 | ||
|
|
90efde936a | ||
|
|
caaf1509bf | ||
|
|
9e578a4f0d | ||
|
|
f1c180ba50 | ||
|
|
8c732120bc | ||
|
|
6cf69ec6d1 | ||
|
|
67f876372c | ||
|
|
f4d658ab7e | ||
|
|
7433c3ad47 | ||
|
|
4d73e3ce78 | ||
|
|
5afa36097a | ||
|
|
afe010ed0f | ||
|
|
02f3a93b71 | ||
|
|
e9f3a43f37 | ||
|
|
8c7386fea0 | ||
|
|
d6b9da008b | ||
|
|
436142a1d8 | ||
|
|
a8445c9661 | ||
|
|
bfb9e16fcf | ||
|
|
3d729a768f | ||
|
|
2f1ed28f09 | ||
|
|
ee1b78441c | ||
|
|
2cbd674141 | ||
|
|
3182cceda4 | ||
|
|
71bcd64fb5 | ||
|
|
c2fa7282ca | ||
|
|
373fd32b0e | ||
|
|
3865835070 | ||
|
|
4be38e2dda | ||
|
|
ba286de3cb | ||
|
|
31db15f501 | ||
|
|
1f15e3b198 | ||
|
|
189883e267 | ||
|
|
ae6524bb2f | ||
|
|
ef4543df33 | ||
|
|
d46f67bf95 | ||
|
|
bbdbfb807c | ||
|
|
29aee06e76 | ||
|
|
f8587c4ed7 | ||
|
|
9c7f575569 | ||
|
|
adceff29f1 | ||
|
|
59b7fa2ebc | ||
|
|
94e3772221 | ||
|
|
8efcaf97eb | ||
|
|
eda69c23bd | ||
|
|
8c00c88818 | ||
|
|
99e07eb11c | ||
|
|
5367ce843e | ||
|
|
55d58ddb60 | ||
|
|
dc2bd9e412 | ||
|
|
7c17bba801 | ||
|
|
0c6d3ec6e0 | ||
|
|
813e807ea6 | ||
|
|
cfefa1e9b7 | ||
|
|
426ab21b69 | ||
|
|
f310e1f5ba | ||
|
|
41fd9816e8 | ||
|
|
5abe6a5875 | ||
|
|
e308b0aee1 | ||
|
|
38b1fee344 | ||
|
|
75a7cc983c | ||
|
|
d62359a009 | ||
|
|
745250942f | ||
|
|
f32ae29385 | ||
|
|
ffd47ce2dd | ||
|
|
a3ec3f368f | ||
|
|
57054caca5 | ||
|
|
965de8b742 | ||
|
|
1479848714 | ||
|
|
5fe78f7892 | ||
|
|
cdd79bc764 | ||
|
|
52b96538b7 | ||
|
|
4aa3c630df | ||
|
|
883c8c443f | ||
|
|
1d3c6d6eb8 | ||
|
|
4dcaa2c23b | ||
|
|
21e142b9a5 | ||
|
|
3cd2562626 | ||
|
|
28b5dfdb84 | ||
|
|
41bca04103 | ||
|
|
7abeb9bf00 | ||
|
|
7c4442561f | ||
|
|
7e45006300 | ||
|
|
c39cbf15be | ||
|
|
9e304fbe69 | ||
|
|
f9257cdf78 | ||
|
|
0e074e407a | ||
|
|
71cba3d1fe | ||
|
|
23f5c016cf | ||
|
|
1a82e4a294 | ||
|
|
a0be22b65f | ||
|
|
6e5626bbff | ||
|
|
6e6b81dcdf | ||
|
|
3c19eb6de2 | ||
|
|
309e48ad54 | ||
|
|
ef82567730 | ||
|
|
61cc67e4b7 | ||
|
|
0c2d1df424 | ||
|
|
35afdb1c84 | ||
|
|
a3a8caae51 | ||
|
|
5830736537 | ||
|
|
0bfdbe1e2c | ||
|
|
d41c4c6cc6 | ||
|
|
2550417804 | ||
|
|
c44db2cc35 | ||
|
|
660cb3b6ac | ||
|
|
ad12df8fcc | ||
|
|
b7ff742f84 | ||
|
|
220bfa1da5 | ||
|
|
80188edbdd | ||
|
|
0f98b3d9f2 | ||
|
|
0b9fb97557 | ||
|
|
214a912b37 | ||
|
|
990a1fe952 | ||
|
|
1ac9d52332 | ||
|
|
1fa1e7072b | ||
|
|
68dc5fe901 | ||
|
|
db9c43feea | ||
|
|
9e0c7de24e | ||
|
|
07fa660a47 | ||
|
|
734a1c63a2 | ||
|
|
e22f02f198 | ||
|
|
318e6ec067 | ||
|
|
af1303ec96 | ||
|
|
5ecf9e9202 | ||
|
|
d52f9ee87e | ||
|
|
78c9490212 | ||
|
|
2d230ce033 | ||
|
|
894dc4fc8f | ||
|
|
ec6ada4935 | ||
|
|
646cbf6d30 | ||
|
|
47d9d46304 | ||
|
|
2f16062f78 | ||
|
|
e22bfec6b6 | ||
|
|
bf24177bfd | ||
|
|
62eb3bd96c | ||
|
|
e1ebd77811 | ||
|
|
69253bc083 | ||
|
|
36b35f99a4 | ||
|
|
a91d208d10 | ||
|
|
89dd9dd9ed | ||
|
|
068da9d89f | ||
|
|
53661fefc9 | ||
|
|
d462ed3b4d | ||
|
|
4a02a79abb | ||
|
|
2ee4482d00 | ||
|
|
9f4339e7dc | ||
|
|
7e20e97ee0 | ||
|
|
86d23f42db | ||
|
|
b8299feae0 | ||
|
|
ec03eb3451 | ||
|
|
4117209b69 | ||
|
|
a870290575 | ||
|
|
e7a466b708 | ||
|
|
66ae30085b | ||
|
|
c49636c923 | ||
|
|
2f6303faef | ||
|
|
8d4c3d3a34 | ||
|
|
d212e52a20 | ||
|
|
301892bcff | ||
|
|
62892dc4e4 | ||
|
|
f08aa92da0 | ||
|
|
e5fe46a051 | ||
|
|
0c8808a57a | ||
|
|
9384ca8e20 | ||
|
|
5e536ed412 | ||
|
|
bb1fdcfa77 | ||
|
|
b91825bc73 | ||
|
|
e879ce0b12 | ||
|
|
c8735de2c1 | ||
|
|
98cf5836fd | ||
|
|
72810e9c05 | ||
|
|
3404ac174e | ||
|
|
60f59b29ed | ||
|
|
0f7ae0a660 | ||
|
|
d294e1d453 | ||
|
|
1c97f970eb | ||
|
|
8117f42634 | ||
|
|
e83404e57d | ||
|
|
499409dbc1 | ||
|
|
3da3becc24 | ||
|
|
a711fad058 | ||
|
|
2e856599b3 | ||
|
|
0119d8cbbc | ||
|
|
d8159b8be2 | ||
|
|
10442a7b4e | ||
|
|
413e2a79fb | ||
|
|
cfb0f7ffa7 | ||
|
|
7e9ef10f67 | ||
|
|
38f3d71a2f | ||
|
|
9eba533f7c | ||
|
|
9acdac02cf | ||
|
|
dfb7c1cc38 | ||
|
|
702b491981 | ||
|
|
44207c1628 | ||
|
|
85cfd10cae | ||
|
|
6ead3a5cbb | ||
|
|
e1586ea6cb | ||
|
|
72d88a2d69 | ||
|
|
3f5a1054a8 | ||
|
|
134f12f419 | ||
|
|
f3113f1a2f | ||
|
|
5d860bfc9b | ||
|
|
aa8c2937d7 | ||
|
|
38e954248c | ||
|
|
6c64f7db34 | ||
|
|
d4a9015c2b | ||
|
|
0c745d9021 | ||
|
|
6ef4b5f396 | ||
|
|
ee83302751 | ||
|
|
7e83c9231d | ||
|
|
f5e922ed92 | ||
|
|
3c41e421de | ||
|
|
4c2950a9da | ||
|
|
b8d3929dce | ||
|
|
a8b4f05418 | ||
|
|
f671fa8477 | ||
|
|
53e8674899 | ||
|
|
11049b441e | ||
|
|
6fe69f16e2 | ||
|
|
8353e981be | ||
|
|
ae76d91437 | ||
|
|
04830b787c | ||
|
|
fa844b1274 | ||
|
|
3043dcc823 | ||
|
|
7f2dd9bea1 | ||
|
|
01edf115db | ||
|
|
aebd3f30cb | ||
|
|
801a7504a6 | ||
|
|
a5ad24c0fd | ||
|
|
682b4e39fb | ||
|
|
046683ab60 | ||
|
|
29a3495c8b | ||
|
|
b2ca75e074 | ||
|
|
16748bdee9 | ||
|
|
7ef5f7004d | ||
|
|
b7ff9db6e7 | ||
|
|
953c63392c | ||
|
|
476a6ba935 | ||
|
|
6e65db5d2d | ||
|
|
30591eaf56 | ||
|
|
435587c260 | ||
|
|
6a15af599c | ||
|
|
6ba0455129 | ||
|
|
793a3e3b75 | ||
|
|
a02dad303c | ||
|
|
fc7e1c4e39 | ||
|
|
032dd3289a | ||
|
|
ab991de8a2 | ||
|
|
f14f5dc72c | ||
|
|
35615ef60e | ||
|
|
7f51f6d6ad | ||
|
|
9ddd82bc3c | ||
|
|
4292d8e0e6 | ||
|
|
5ebe984194 | ||
|
|
1712e3cbd0 | ||
|
|
87b894ea22 | ||
|
|
597460d5ea | ||
|
|
d739e44ee6 | ||
|
|
6b33fa6ddd | ||
|
|
3a9cc3d258 | ||
|
|
dd4a38ce9f | ||
|
|
4158915af3 | ||
|
|
954439af64 | ||
|
|
a74807aa86 | ||
|
|
0d4281ddfa | ||
|
|
32214ddaa6 | ||
|
|
3a43f924c6 | ||
|
|
d13d0f5848 | ||
|
|
fd83543026 | ||
|
|
607911ff27 | ||
|
|
e6f07fa0b1 | ||
|
|
1c67f4274b | ||
|
|
5efdff2a4f | ||
|
|
b15b95ee8f | ||
|
|
b0a351f55a | ||
|
|
1ea83e5774 | ||
|
|
4244dfe0a2 | ||
|
|
e7e9db8cee | ||
|
|
6b53ac6790 | ||
|
|
9872d1c997 | ||
|
|
31b7d78516 | ||
|
|
36dcf15ebc | ||
|
|
56021c1af9 | ||
|
|
1d8dd53995 | ||
|
|
7375c8a058 | ||
|
|
f61d5c24ac | ||
|
|
7c17c11a04 | ||
|
|
d4f5db4b8a | ||
|
|
be4f88c811 | ||
|
|
6f150a343b | ||
|
|
30ea3a26d0 | ||
|
|
cb3f554e38 | ||
|
|
3999837279 | ||
|
|
9d3ad08a82 | ||
|
|
a21843f974 | ||
|
|
fd2bab9b11 | ||
|
|
7d8134ca36 | ||
|
|
fc287b2943 | ||
|
|
4b1a419883 | ||
|
|
002587824e | ||
|
|
96241294bb | ||
|
|
9f85e2769c | ||
|
|
cf62f931d9 | ||
|
|
1af202bf10 | ||
|
|
98331a662f | ||
|
|
2dce2d0d63 | ||
|
|
80bca72915 | ||
|
|
2a0c73d0e5 | ||
|
|
ea97ea4c47 | ||
|
|
081f3e43c0 | ||
|
|
9b3d89db04 | ||
|
|
f151daa2f9 | ||
|
|
4b062a4010 | ||
|
|
d7817793c3 | ||
|
|
3f2cc10bfe | ||
|
|
9c07649287 | ||
|
|
47f931c8b2 | ||
|
|
61e2caf818 | ||
|
|
ad97969e93 | ||
|
|
25ca8dc835 | ||
|
|
a41c6d32c3 | ||
|
|
f2e34bd172 | ||
|
|
5bbd1d20cf | ||
|
|
ff44938f9f | ||
|
|
414aae50c2 | ||
|
|
36160a81fb | ||
|
|
36a4f662b2 | ||
|
|
3d406e5213 | ||
|
|
61237d4c08 | ||
|
|
341c6b2929 | ||
|
|
4f7a2dfb50 | ||
|
|
0542c12aae | ||
|
|
203cd6ecf2 | ||
|
|
2adc193421 | ||
|
|
fe2ebeef25 | ||
|
|
e9cd90f52e | ||
|
|
d973fa129b | ||
|
|
4fa65f3853 | ||
|
|
f478fee5fb | ||
|
|
4b4022358a | ||
|
|
413c33274d | ||
|
|
e13714e400 | ||
|
|
bcd6ac885d | ||
|
|
512dfd6aaf | ||
|
|
d95f304073 | ||
|
|
18c153ef8a | ||
|
|
bb60e06b88 | ||
|
|
0ae2a2ded8 | ||
|
|
811f1b19bd | ||
|
|
a0b739c259 | ||
|
|
cbda203f23 | ||
|
|
d7a5eab7d5 | ||
|
|
e51f0cec94 | ||
|
|
c6f8dc2482 | ||
|
|
e3387386e0 | ||
|
|
520bfa5c42 | ||
|
|
77c73e8801 | ||
|
|
0a46111bb2 | ||
|
|
551018136b | ||
|
|
48be0c1bd0 | ||
|
|
1c983629b5 | ||
|
|
65160f626d | ||
|
|
1445a655fe | ||
|
|
cf8adfe5d3 | ||
|
|
cf624ebb7b | ||
|
|
c9a81e5241 | ||
|
|
d7e47f413f | ||
|
|
28b62cfb71 |
@@ -6,22 +6,64 @@
|
||||
"docsSlug": "doctrine-orm",
|
||||
"versions": [
|
||||
{
|
||||
"name": "3.0",
|
||||
"branchName": "3.0.x",
|
||||
"name": "4.0",
|
||||
"branchName": "4.0.x",
|
||||
"slug": "latest",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "3.3",
|
||||
"branchName": "3.3.x",
|
||||
"slug": "3.3",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "3.2",
|
||||
"branchName": "3.2.x",
|
||||
"slug": "3.2",
|
||||
"current": true
|
||||
},
|
||||
{
|
||||
"name": "3.1",
|
||||
"branchName": "3.1.x",
|
||||
"slug": "3.1",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "3.0",
|
||||
"branchName": "3.0.x",
|
||||
"slug": "3.0",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.21",
|
||||
"branchName": "2.21.x",
|
||||
"slug": "2.21",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.20",
|
||||
"branchName": "2.20.x",
|
||||
"slug": "2.20",
|
||||
"maintained": true
|
||||
},
|
||||
{
|
||||
"name": "2.19",
|
||||
"branchName": "2.19.x",
|
||||
"slug": "2.19",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.18",
|
||||
"branchName": "2.18.x",
|
||||
"slug": "2.18",
|
||||
"upcoming": true
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.17",
|
||||
"branchName": "2.17.x",
|
||||
"slug": "2.17",
|
||||
"current": true
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.16",
|
||||
@@ -64,42 +106,6 @@
|
||||
"branchName": "2.10.x",
|
||||
"slug": "2.10",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.9",
|
||||
"branchName": "2.9.x",
|
||||
"slug": "2.9",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.8",
|
||||
"branchName": "2.8.x",
|
||||
"slug": "2.8",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.7",
|
||||
"branchName": "2.7",
|
||||
"slug": "2.7",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.6",
|
||||
"branchName": "2.6",
|
||||
"slug": "2.6",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.5",
|
||||
"branchName": "2.5",
|
||||
"slug": "2.5",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.4",
|
||||
"branchName": "2.4",
|
||||
"slug": "2.4",
|
||||
"maintained": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
9
.github/dependabot.yml
vendored
Normal file
9
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "CI"
|
||||
target-branch: "2.19.x"
|
||||
2
.github/workflows/coding-standards.yml
vendored
2
.github/workflows/coding-standards.yml
vendored
@@ -24,4 +24,4 @@ on:
|
||||
|
||||
jobs:
|
||||
coding-standards:
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@3.0.0"
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.1.0"
|
||||
|
||||
122
.github/workflows/continuous-integration.yml
vendored
122
.github/workflows/continuous-integration.yml
vendored
@@ -33,32 +33,28 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.2"
|
||||
- "7.3"
|
||||
- "7.4"
|
||||
- "8.0"
|
||||
- "8.1"
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3.7"
|
||||
extension:
|
||||
- "sqlite3"
|
||||
- "pdo_sqlite"
|
||||
proxy:
|
||||
- "common"
|
||||
deps:
|
||||
- "highest"
|
||||
include:
|
||||
- php-version: "8.0"
|
||||
dbal-version: "2.13"
|
||||
- php-version: "8.2"
|
||||
dbal-version: "4@dev"
|
||||
extension: "pdo_sqlite"
|
||||
- php-version: "8.2"
|
||||
dbal-version: "3@dev"
|
||||
extension: "pdo_sqlite"
|
||||
- php-version: "8.2"
|
||||
dbal-version: "default"
|
||||
dbal-version: "4@dev"
|
||||
extension: "sqlite3"
|
||||
- php-version: "8.1"
|
||||
dbal-version: "default"
|
||||
proxy: "lazy-ghost"
|
||||
deps: "lowest"
|
||||
extension: "pdo_sqlite"
|
||||
|
||||
steps:
|
||||
@@ -80,26 +76,25 @@ jobs:
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
dependency-versions: "${{ matrix.deps }}"
|
||||
|
||||
- name: "Run PHPUnit"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage-no-cache.xml"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 0
|
||||
ORM_PROXY_IMPLEMENTATION: "${{ matrix.proxy }}"
|
||||
|
||||
- name: "Run PHPUnit with Second Level Cache"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --exclude-group performance,non-cacheable,locking_functional --coverage-clover=coverage-cache.xml"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 1
|
||||
ORM_PROXY_IMPLEMENTATION: "${{ matrix.proxy }}"
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v3"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
with:
|
||||
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
|
||||
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.deps }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
|
||||
|
||||
@@ -113,21 +108,22 @@ jobs:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3@dev"
|
||||
- "3.7"
|
||||
postgres-version:
|
||||
- "15"
|
||||
extension:
|
||||
- pdo_pgsql
|
||||
- pgsql
|
||||
include:
|
||||
- php-version: "8.0"
|
||||
dbal-version: "2.13"
|
||||
- php-version: "8.2"
|
||||
dbal-version: "4@dev"
|
||||
postgres-version: "14"
|
||||
extension: pdo_pgsql
|
||||
- php-version: "8.2"
|
||||
dbal-version: "default"
|
||||
dbal-version: "3.7"
|
||||
postgres-version: "9.6"
|
||||
extension: pdo_pgsql
|
||||
|
||||
@@ -162,7 +158,7 @@ jobs:
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
@@ -170,9 +166,9 @@ jobs:
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/pdo_pgsql.xml --coverage-clover=coverage.xml"
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v3"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
|
||||
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.extension }}-coverage"
|
||||
path: "coverage.xml"
|
||||
|
||||
|
||||
@@ -186,29 +182,26 @@ jobs:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3@dev"
|
||||
- "3.7"
|
||||
- "4@dev"
|
||||
mariadb-version:
|
||||
- "10.9"
|
||||
- "11.4"
|
||||
extension:
|
||||
- "mysqli"
|
||||
- "pdo_mysql"
|
||||
include:
|
||||
- php-version: "8.0"
|
||||
dbal-version: "2.13"
|
||||
mariadb-version: "10.6"
|
||||
extension: "pdo_mysql"
|
||||
|
||||
services:
|
||||
mariadb:
|
||||
image: "mariadb:${{ matrix.mariadb-version }}"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
MYSQL_DATABASE: "doctrine_tests"
|
||||
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: yes
|
||||
MARIADB_DATABASE: "doctrine_tests"
|
||||
|
||||
options: >-
|
||||
--health-cmd "mysqladmin ping --silent"
|
||||
--health-cmd "healthcheck.sh --connect --innodb_initialized"
|
||||
|
||||
ports:
|
||||
- "3306:3306"
|
||||
@@ -232,7 +225,7 @@ jobs:
|
||||
extensions: "${{ matrix.extension }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
@@ -240,7 +233,7 @@ jobs:
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage.xml"
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v3"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.mariadb-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
|
||||
path: "coverage.xml"
|
||||
@@ -256,9 +249,10 @@ jobs:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3@dev"
|
||||
- "3.7"
|
||||
mysql-version:
|
||||
- "5.7"
|
||||
- "8.0"
|
||||
@@ -266,8 +260,12 @@ jobs:
|
||||
- "mysqli"
|
||||
- "pdo_mysql"
|
||||
include:
|
||||
- php-version: "8.0"
|
||||
dbal-version: "2.13"
|
||||
- php-version: "8.2"
|
||||
dbal-version: "4@dev"
|
||||
mysql-version: "8.0"
|
||||
extension: "mysqli"
|
||||
- php-version: "8.2"
|
||||
dbal-version: "4@dev"
|
||||
mysql-version: "8.0"
|
||||
extension: "pdo_mysql"
|
||||
|
||||
@@ -302,7 +300,7 @@ jobs:
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
@@ -317,45 +315,11 @@ jobs:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 1
|
||||
|
||||
- name: "Upload coverage files"
|
||||
uses: "actions/upload-artifact@v3"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.mysql-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
|
||||
|
||||
phpunit-lower-php-versions:
|
||||
name: "PHPUnit with SQLite"
|
||||
runs-on: "ubuntu-22.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.1"
|
||||
deps:
|
||||
- "highest"
|
||||
- "lowest"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
ini-values: "zend.assertions=1, apc.enable_cli=1"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
with:
|
||||
dependency-versions: "${{ matrix.deps }}"
|
||||
|
||||
- name: "Run PHPUnit"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/pdo_sqlite.xml"
|
||||
|
||||
|
||||
upload_coverage:
|
||||
name: "Upload coverage to Codecov"
|
||||
runs-on: "ubuntu-22.04"
|
||||
@@ -372,11 +336,13 @@ jobs:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Download coverage files"
|
||||
uses: "actions/download-artifact@v3"
|
||||
uses: "actions/download-artifact@v4"
|
||||
with:
|
||||
path: "reports"
|
||||
|
||||
- name: "Upload to Codecov"
|
||||
uses: "codecov/codecov-action@v3"
|
||||
uses: "codecov/codecov-action@v4"
|
||||
with:
|
||||
directory: reports
|
||||
env:
|
||||
CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}"
|
||||
|
||||
42
.github/workflows/documentation.yml
vendored
42
.github/workflows/documentation.yml
vendored
@@ -5,44 +5,16 @@ on:
|
||||
branches:
|
||||
- "*.x"
|
||||
paths:
|
||||
- .github/workflows/documentation.yml
|
||||
- docs/**
|
||||
- ".github/workflows/documentation.yml"
|
||||
- "docs/**"
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
paths:
|
||||
- .github/workflows/documentation.yml
|
||||
- docs/**
|
||||
- ".github/workflows/documentation.yml"
|
||||
- "docs/**"
|
||||
|
||||
jobs:
|
||||
validate-with-guides:
|
||||
name: "Validate documentation with phpDocumentor/guides"
|
||||
runs-on: "ubuntu-22.04"
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "8.2"
|
||||
|
||||
- name: "Remove existing composer file"
|
||||
run: "rm composer.json"
|
||||
|
||||
- name: "Require phpdocumentor/guides-cli"
|
||||
run: "composer require --dev phpdocumentor/guides-cli --no-update"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
- name: "Add dummy title to the sidebar"
|
||||
run: |
|
||||
printf '%s\n%s\n\n%s\n' "Dummy title" "===========" "$(cat docs/en/sidebar.rst)" > docs/en/sidebar.rst
|
||||
|
||||
- name: "Run guides-cli"
|
||||
run: "vendor/bin/guides -vvv --no-progress docs/en 2>&1 | grep -v 'Unknown directive' | ( ! grep WARNING )"
|
||||
documentation:
|
||||
name: "Documentation"
|
||||
uses: "doctrine/.github/.github/workflows/documentation.yml@5.1.0"
|
||||
|
||||
13
.github/workflows/phpbench.yml
vendored
13
.github/workflows/phpbench.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
- "8.1"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
@@ -47,15 +47,8 @@ jobs:
|
||||
coverage: "pcov"
|
||||
ini-values: "zend.assertions=1, apc.enable_cli=1"
|
||||
|
||||
- name: "Cache dependencies installed with composer"
|
||||
uses: "actions/cache@v3"
|
||||
with:
|
||||
path: "~/.composer/cache"
|
||||
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
|
||||
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
|
||||
|
||||
- name: "Install dependencies with composer"
|
||||
run: "composer update --no-interaction --no-progress"
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
|
||||
- name: "Run PHPBench"
|
||||
run: "vendor/bin/phpbench run --report=default"
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@3.0.0"
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@5.1.0"
|
||||
secrets:
|
||||
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
|
||||
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}
|
||||
|
||||
86
.github/workflows/static-analysis.yml
vendored
86
.github/workflows/static-analysis.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
- src/**
|
||||
- phpstan*
|
||||
- psalm*
|
||||
- tests/Doctrine/StaticAnalysis/**
|
||||
- tests/StaticAnalysis/**
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
@@ -20,84 +20,70 @@ on:
|
||||
- src/**
|
||||
- phpstan*
|
||||
- psalm*
|
||||
- tests/Doctrine/StaticAnalysis/**
|
||||
- tests/StaticAnalysis/**
|
||||
|
||||
jobs:
|
||||
static-analysis-phpstan:
|
||||
name: "Static Analysis with PHPStan"
|
||||
runs-on: "ubuntu-22.04"
|
||||
name: Static Analysis with PHPStan
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
dbal-version:
|
||||
- "default"
|
||||
persistence-version:
|
||||
- "default"
|
||||
include:
|
||||
- dbal-version: "2.13"
|
||||
persistence-version: "default"
|
||||
- dbal-version: "default"
|
||||
persistence-version: "2.5"
|
||||
- dbal-version: default
|
||||
config: phpstan.neon
|
||||
- dbal-version: 3.8.2
|
||||
config: phpstan-dbal3.neon
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
- name: Install PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "8.2"
|
||||
coverage: none
|
||||
php-version: "8.3"
|
||||
tools: cs2pr
|
||||
|
||||
- name: "Require specific DBAL version"
|
||||
- name: Require specific DBAL version
|
||||
run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update"
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Require specific persistence version"
|
||||
run: "composer require doctrine/persistence ^$([ ${{ matrix.persistence-version }} = default ] && echo '3.1' || echo ${{ matrix.persistence-version }}) --no-update"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
- name: Install dependencies with Composer
|
||||
uses: ramsey/composer-install@v2
|
||||
|
||||
- name: "Run a static analysis with phpstan/phpstan"
|
||||
run: "vendor/bin/phpstan analyse"
|
||||
if: "${{ matrix.dbal-version == 'default' && matrix.persistence-version == 'default'}}"
|
||||
|
||||
- name: "Run a static analysis with phpstan/phpstan"
|
||||
run: "vendor/bin/phpstan analyse -c phpstan-dbal2.neon"
|
||||
if: "${{ matrix.dbal-version == '2.13' }}"
|
||||
|
||||
- name: "Run a static analysis with phpstan/phpstan"
|
||||
run: "vendor/bin/phpstan analyse -c phpstan-persistence2.neon"
|
||||
if: "${{ matrix.dbal-version == 'default' && matrix.persistence-version != 'default'}}"
|
||||
- name: Run static analysis with phpstan/phpstan
|
||||
run: "vendor/bin/phpstan analyse -c ${{ matrix.config }} --error-format=checkstyle | cs2pr"
|
||||
|
||||
static-analysis-psalm:
|
||||
name: "Static Analysis with Psalm"
|
||||
runs-on: "ubuntu-22.04"
|
||||
name: Static Analysis with Psalm
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
dbal-version:
|
||||
- default
|
||||
- 3.8.2
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
- name: Install PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "8.2"
|
||||
coverage: none
|
||||
php-version: "8.3"
|
||||
tools: cs2pr
|
||||
|
||||
- name: "Require specific persistence version"
|
||||
run: "composer require doctrine/persistence ^3.1 --no-update"
|
||||
- name: Require specific DBAL version
|
||||
run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update"
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
- name: Install dependencies with Composer
|
||||
uses: ramsey/composer-install@v3
|
||||
|
||||
- name: "Run a static analysis with vimeo/psalm"
|
||||
run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)"
|
||||
- name: Run static analysis with Vimeo Psalm
|
||||
run: vendor/bin/psalm --shepherd
|
||||
|
||||
@@ -57,7 +57,7 @@ sqlite database.
|
||||
Tips for creating unit tests:
|
||||
|
||||
1. If you put a test into the `Ticket` namespace as described above, put the testcase and all entities into the same class.
|
||||
See `https://github.com/doctrine/orm/tree/2.8.x/tests/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an
|
||||
See `https://github.com/doctrine/orm/tree/3.0.x/tests/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an
|
||||
example.
|
||||
|
||||
## Getting merged
|
||||
|
||||
42
README.md
42
README.md
@@ -1,11 +1,11 @@
|
||||
| [3.0.x][3.0] | [2.18.x][2.18] | [2.17.x][2.17] |
|
||||
|:----------------:|:----------------:|:----------:|
|
||||
| [![Build status][3.0 image]][3.0] | [![Build status][2.18 image]][2.18] | [![Build status][2.17 image]][2.17] |
|
||||
| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.18 coverage image]][2.18 coverage] | [![Coverage Status][2.17 coverage image]][2.17 coverage] |
|
||||
| [4.0.x][4.0] | [3.3.x][3.3] | [3.2.x][3.2] | [2.20.x][2.20] | [2.19.x][2.19] |
|
||||
|:------------------------------------------------------:|:------------------------------------------------------:|:------------------------------------------------------:|:--------------------------------------------------------:|:--------------------------------------------------------:|
|
||||
| [![Build status][4.0 image]][4.0] | [![Build status][3.3 image]][3.3] | [![Build status][3.2 image]][3.2] | [![Build status][2.20 image]][2.20] | [![Build status][2.19 image]][2.19] |
|
||||
| [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.3 coverage image]][3.3 coverage] | [![Coverage Status][3.2 coverage image]][3.2 coverage] | [![Coverage Status][2.20 coverage image]][2.20 coverage] | [![Coverage Status][2.19 coverage image]][2.19 coverage] |
|
||||
|
||||
[<h1 align="center">🇺🇦 UKRAINE NEEDS YOUR HELP NOW!</h1>](https://www.doctrine-project.org/stop-war.html)
|
||||
|
||||
Doctrine ORM is an object-relational mapper for PHP 7.1+ that provides transparent persistence
|
||||
Doctrine ORM is an object-relational mapper for PHP 8.1+ that provides transparent persistence
|
||||
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
|
||||
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
|
||||
inspired by Hibernate's HQL. This provides developers with a powerful alternative to SQL that maintains flexibility
|
||||
@@ -18,15 +18,23 @@ without requiring unnecessary code duplication.
|
||||
* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/stable/index.html)
|
||||
|
||||
|
||||
[3.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.0.x
|
||||
[3.0]: https://github.com/doctrine/orm/tree/3.0.x
|
||||
[3.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.0.x/graph/badge.svg
|
||||
[3.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.0.x
|
||||
[2.18 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.18.x
|
||||
[2.18]: https://github.com/doctrine/orm/tree/2.18.x
|
||||
[2.18 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.18.x/graph/badge.svg
|
||||
[2.18 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.18.x
|
||||
[2.17 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.17.x
|
||||
[2.17]: https://github.com/doctrine/orm/tree/2.17.x
|
||||
[2.17 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.17.x/graph/badge.svg
|
||||
[2.17 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.17.x
|
||||
[4.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=4.0.x
|
||||
[4.0]: https://github.com/doctrine/orm/tree/4.0.x
|
||||
[4.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/4.0.x/graph/badge.svg
|
||||
[4.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/4.0.x
|
||||
[3.3 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.3.x
|
||||
[3.3]: https://github.com/doctrine/orm/tree/3.3.x
|
||||
[3.3 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.3.x/graph/badge.svg
|
||||
[3.3 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.3.x
|
||||
[3.2 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.2.x
|
||||
[3.2]: https://github.com/doctrine/orm/tree/3.2.x
|
||||
[3.2 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.2.x/graph/badge.svg
|
||||
[3.2 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.2.x
|
||||
[2.20 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.20.x
|
||||
[2.20]: https://github.com/doctrine/orm/tree/2.20.x
|
||||
[2.20 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.20.x/graph/badge.svg
|
||||
[2.20 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.20.x
|
||||
[2.19 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.19.x
|
||||
[2.19]: https://github.com/doctrine/orm/tree/2.19.x
|
||||
[2.19 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.19.x/graph/badge.svg
|
||||
[2.19 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.19.x
|
||||
|
||||
786
UPGRADE.md
786
UPGRADE.md
@@ -1,3 +1,789 @@
|
||||
# Upgrade to 3.3
|
||||
|
||||
## Deprecate `DatabaseDriver`
|
||||
|
||||
The class `Doctrine\ORM\Mapping\Driver\DatabaseDriver` is deprecated without replacement.
|
||||
|
||||
# Upgrade to 3.2
|
||||
|
||||
## Deprecate the `NotSupported` exception
|
||||
|
||||
The class `Doctrine\ORM\Exception\NotSupported` is deprecated without replacement.
|
||||
|
||||
## Deprecate remaining `Serializable` implementation
|
||||
|
||||
Relying on `SequenceGenerator` implementing the `Serializable` is deprecated
|
||||
because that interface won't be implemented in ORM 4 anymore.
|
||||
|
||||
The following methods are deprecated:
|
||||
|
||||
* `SequenceGenerator::serialize()`
|
||||
* `SequenceGenerator::unserialize()`
|
||||
|
||||
## `orm:schema-tool:update` option `--complete` is deprecated
|
||||
|
||||
That option behaves as a no-op, and is deprecated. It will be removed in 4.0.
|
||||
|
||||
## Deprecate properties `$indexes` and `$uniqueConstraints` of `Doctrine\ORM\Mapping\Table`
|
||||
|
||||
The properties `$indexes` and `$uniqueConstraints` have been deprecated since they had no effect at all.
|
||||
The preferred way of defining indices and unique constraints is by
|
||||
using the `\Doctrine\ORM\Mapping\UniqueConstraint` and `\Doctrine\ORM\Mapping\Index` attributes.
|
||||
|
||||
# Upgrade to 3.1
|
||||
|
||||
## Deprecate `Doctrine\ORM\Mapping\ReflectionEnumProperty`
|
||||
|
||||
This class is deprecated and will be removed in 4.0.
|
||||
Instead, use `Doctrine\Persistence\Reflection\EnumReflectionProperty` from
|
||||
`doctrine/persistence`.
|
||||
|
||||
## Deprecate passing null to `ClassMetadata::fullyQualifiedClassName()`
|
||||
|
||||
Passing `null` to `Doctrine\ORM\ClassMetadata::fullyQualifiedClassName()` is
|
||||
deprecated and will no longer be possible in 4.0.
|
||||
|
||||
## Deprecate array access
|
||||
|
||||
Using array access on instances of the following classes is deprecated:
|
||||
|
||||
- `Doctrine\ORM\Mapping\DiscriminatorColumnMapping`
|
||||
- `Doctrine\ORM\Mapping\EmbedClassMapping`
|
||||
- `Doctrine\ORM\Mapping\FieldMapping`
|
||||
- `Doctrine\ORM\Mapping\JoinColumnMapping`
|
||||
- `Doctrine\ORM\Mapping\JoinTableMapping`
|
||||
|
||||
# Upgrade to 3.0
|
||||
|
||||
## BC BREAK: Calling `ClassMetadata::getAssociationMappedByTargetField()` with the owning side of an association now throws an exception
|
||||
|
||||
Previously, calling
|
||||
`Doctrine\ORM\Mapping\ClassMetadata::getAssociationMappedByTargetField()` with
|
||||
the owning side of an association returned `null`, which was undocumented, and
|
||||
wrong according to the phpdoc of the parent method.
|
||||
|
||||
If you do not know whether you are on the owning or inverse side of an association,
|
||||
you can use `Doctrine\ORM\Mapping\ClassMetadata::isAssociationInverseSide()`
|
||||
to find out.
|
||||
|
||||
## BC BREAK: `Doctrine\ORM\Proxy\Autoloader` no longer extends `Doctrine\Common\Proxy\Autoloader`
|
||||
|
||||
Make sure to use the former when writing a type declaration or an `instanceof` check.
|
||||
|
||||
## Minor BC BREAK: Changed order of arguments passed to `OneToOne`, `ManyToOne` and `Index` mapping PHP attributes
|
||||
|
||||
To keep PHP mapping attributes consistent, order of arguments passed to above attributes has been changed
|
||||
so `$targetEntity` is a first argument now. This change affects only non-named arguments usage.
|
||||
|
||||
## BC BREAK: AUTO keyword for identity generation defaults to IDENTITY for PostgreSQL when using `doctrine/dbal` 4
|
||||
|
||||
When using the `AUTO` strategy to let Doctrine determine the identity generation mechanism for
|
||||
an entity, and when using `doctrine/dbal` 4, PostgreSQL now uses `IDENTITY`
|
||||
instead of `SEQUENCE` or `SERIAL`.
|
||||
* If you want to upgrade your existing tables to identity columns, you will need to follow [migration to identity columns on PostgreSQL](https://www.doctrine-project.org/projects/doctrine-dbal/en/4.0/how-to/postgresql-identity-migration.html)
|
||||
* If you want to keep using SQL sequences, you need to configure the ORM this way:
|
||||
```php
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
use Doctrine\ORM\Configuration;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
assert($configuration instanceof Configuration);
|
||||
$configuration->setIdentityGenerationPreferences([
|
||||
PostgreSQLPlatform::CLASS => ClassMetadata::GENERATOR_TYPE_SEQUENCE,
|
||||
]);
|
||||
```
|
||||
|
||||
## BC BREAK: Throw exceptions when using illegal attributes on Embeddable
|
||||
|
||||
There are only a few attributes allowed on an embeddable such as `#[Column]` or
|
||||
`#[Embedded]`. Previously all others that target entity classes where ignored,
|
||||
now they throw an exception.
|
||||
|
||||
## BC BREAK: Partial objects are removed
|
||||
|
||||
WARNING: This was relaxed in ORM 3.2 when partial was re-allowed for array-hydration.
|
||||
|
||||
- The `PARTIAL` keyword in DQL no longer exists (reintroduced in ORM 3.2)
|
||||
- `Doctrine\ORM\Query\AST\PartialObjectExpression` is removed. (reintroduced in ORM 3.2)
|
||||
- `Doctrine\ORM\Query\SqlWalker::HINT_PARTIAL` (reintroduced in ORM 3.2) and
|
||||
`Doctrine\ORM\Query::HINT_FORCE_PARTIAL_LOAD` are removed.
|
||||
- `Doctrine\ORM\EntityManager*::getPartialReference()` is removed.
|
||||
|
||||
## BC BREAK: `Doctrine\ORM\Persister\Entity\EntityPersister::executeInserts()` return type changed to `void`
|
||||
|
||||
Implementors should adapt to the new signature, and should call
|
||||
`UnitOfWork::assignPostInsertId()` for each entry in the previously returned
|
||||
array.
|
||||
|
||||
## BC BREAK: `Doctrine\ORM\Proxy\ProxyFactory` no longer extends abstract factory from `doctrine/common`
|
||||
|
||||
It is no longer possible to call methods, constants or properties inherited
|
||||
from that class on a `ProxyFactory` instance.
|
||||
|
||||
`Doctrine\ORM\Proxy\ProxyFactory::createProxyDefinition()` and
|
||||
`Doctrine\ORM\Proxy\ProxyFactory::resetUninitializedProxy()` are removed as well.
|
||||
|
||||
## BC BREAK: lazy ghosts are enabled unconditionally
|
||||
|
||||
`Doctrine\ORM\Configuration::setLazyGhostObjectEnabled()` and
|
||||
`Doctrine\ORM\Configuration::isLazyGhostObjectEnabled()` are now no-ops and
|
||||
will be deprecated in 3.1.0
|
||||
|
||||
## BC BREAK: collisions in identity map are unconditionally rejected
|
||||
|
||||
`Doctrine\ORM\Configuration::setRejectIdCollisionInIdentityMap()` and
|
||||
`Doctrine\ORM\Configuration::isRejectIdCollisionInIdentityMapEnabled()` are now
|
||||
no-ops and will be deprecated in 3.1.0.
|
||||
|
||||
## BC BREAK: Lifecycle callback mapping on embedded classes is now explicitly forbidden
|
||||
|
||||
Lifecycle callback mapping on embedded classes produced no effect, and is now
|
||||
explicitly forbidden to point out mistakes.
|
||||
|
||||
## BC BREAK: The `NOTIFY` change tracking policy is removed
|
||||
|
||||
You should use `DEFERRED_EXPLICIT` instead.
|
||||
|
||||
## BC BREAK: `Mapping\Driver\XmlDriver::__construct()` third argument is now enabled by default
|
||||
|
||||
The third argument to
|
||||
`Doctrine\ORM\Mapping\Driver\XmlDriver::__construct()` was introduced to
|
||||
let users opt-in to XML validation, that is now always enabled by default.
|
||||
|
||||
As a consequence, the same goes for
|
||||
`Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver`, and for
|
||||
`Doctrine\ORM\ORMSetup::createXMLMetadataConfiguration()`.
|
||||
|
||||
## BC BREAK: `Mapping\Driver\AttributeDriver::__construct()` second argument is now a no-op
|
||||
|
||||
The second argument to
|
||||
`Doctrine\ORM\Mapping\Driver\AttributeDriver::__construct()` was introduced to
|
||||
let users opt-in to a new behavior, that is now always enforced, regardless of
|
||||
the value of that argument.
|
||||
|
||||
## BC BREAK: `Query::setDQL()` and `Query::setFirstResult()` no longer accept `null`
|
||||
|
||||
The `$dqlQuery` argument of `Doctrine\ORM\Query::setDQL()` must always be a
|
||||
string.
|
||||
|
||||
The `$firstResult` argument of `Doctrine\ORM\Query::setFirstResult()` must
|
||||
always be an integer.
|
||||
|
||||
## BC BREAK: `orm:schema-tool:update` option `--complete` is now a no-op
|
||||
|
||||
`orm:schema-tool:update` now behaves as if `--complete` was provided,
|
||||
regardless of whether it is provided or not.
|
||||
|
||||
## BC BREAK: Removed `Doctrine\ORM\Proxy\Proxy` interface.
|
||||
|
||||
Use `Doctrine\Persistence\Proxy` instead to check whether proxies are initialized.
|
||||
|
||||
## BC BREAK: Overriding fields or associations declared in other than mapped superclasses
|
||||
|
||||
As stated in the documentation, fields and associations may only be overridden when being inherited
|
||||
from mapped superclasses. Overriding them for parent entity classes now throws a `MappingException`.
|
||||
|
||||
## BC BREAK: Undeclared entity inheritance now throws a `MappingException`
|
||||
|
||||
As soon as an entity class inherits from another entity class, inheritance has to
|
||||
be declared by adding the appropriate configuration for the root entity.
|
||||
|
||||
## Removed `getEntityManager()` in `Doctrine\ORM\Event\OnClearEventArgs` and `Doctrine\ORM\Event\*FlushEventArgs`
|
||||
|
||||
Use `getObjectManager()` instead.
|
||||
|
||||
## BC BREAK: Removed `Doctrine\ORM\Mapping\ClassMetadataInfo` class
|
||||
|
||||
Use `Doctrine\ORM\Mapping\ClassMetadata` instead.
|
||||
|
||||
## BC BREAK: Removed `Doctrine\ORM\Event\LifecycleEventArgs` class.
|
||||
|
||||
Use one of the dedicated event classes instead:
|
||||
|
||||
* `Doctrine\ORM\Event\PrePersistEventArgs`
|
||||
* `Doctrine\ORM\Event\PreUpdateEventArgs`
|
||||
* `Doctrine\ORM\Event\PreRemoveEventArgs`
|
||||
* `Doctrine\ORM\Event\PostPersistEventArgs`
|
||||
* `Doctrine\ORM\Event\PostUpdateEventArgs`
|
||||
* `Doctrine\ORM\Event\PostRemoveEventArgs`
|
||||
* `Doctrine\ORM\Event\PostLoadEventArgs`
|
||||
|
||||
## BC BREAK: Removed `AttributeDriver::$entityAnnotationClasses` and `AttributeDriver::getReader()`
|
||||
|
||||
* If you need to change the behavior of `AttributeDriver::isTransient()`,
|
||||
override that method instead.
|
||||
* The attribute reader is internal to the driver and should not be accessed from outside.
|
||||
|
||||
## BC BREAK: Removed `Doctrine\ORM\Query\AST\InExpression`
|
||||
|
||||
The AST parser will create a `InListExpression` or a `InSubselectExpression` when
|
||||
encountering an `IN ()` DQL expression instead of a generic `InExpression`.
|
||||
|
||||
As a consequence, `SqlWalker::walkInExpression()` has been replaced by
|
||||
`SqlWalker::walkInListExpression()` and `SqlWalker::walkInSubselectExpression()`.
|
||||
|
||||
## BC BREAK: Changed `EntityManagerInterface#refresh($entity)`, `EntityManagerDecorator#refresh($entity)` and `UnitOfWork#refresh($entity)` signatures
|
||||
|
||||
The new signatures of these methods add an optional `LockMode|int|null $lockMode`
|
||||
param with default `null` value (no lock).
|
||||
|
||||
## BC Break: Removed AnnotationDriver
|
||||
|
||||
The annotation driver and anything related to annotation has been removed.
|
||||
Please migrate to another mapping driver.
|
||||
|
||||
The `Doctrine\ORM\Mapping\Annotation` maker interface has been removed in favor of the new
|
||||
`Doctrine\ORM\Mapping\MappingAttribute` interface.
|
||||
|
||||
## BC BREAK: Removed `EntityManager::create()`
|
||||
|
||||
The constructor of `EntityManager` is now public and must be used instead of the `create()` method.
|
||||
However, the constructor expects a `Connection` while `create()` accepted an array with connection parameters.
|
||||
You can pass that array to DBAL's `Doctrine\DBAL\DriverManager::getConnection()` method to bootstrap the
|
||||
connection.
|
||||
|
||||
## BC BREAK: Removed `QueryBuilder` methods and constants.
|
||||
|
||||
The following `QueryBuilder` constants and methods have been removed:
|
||||
|
||||
1. `SELECT`,
|
||||
2. `DELETE`,
|
||||
3. `UPDATE`,
|
||||
4. `STATE_DIRTY`,
|
||||
5. `STATE_CLEAN`,
|
||||
6. `getState()`,
|
||||
7. `getType()`.
|
||||
|
||||
## BC BREAK: Omitting only the alias argument for `QueryBuilder::update` and `QueryBuilder::delete` is not supported anymore
|
||||
|
||||
When building an UPDATE or DELETE query and when passing a class/type to the function, the alias argument must not be omitted.
|
||||
|
||||
### Before
|
||||
|
||||
```php
|
||||
$qb = $em->createQueryBuilder()
|
||||
->delete('User u')
|
||||
->where('u.id = :user_id')
|
||||
->setParameter('user_id', 1);
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```php
|
||||
$qb = $em->createQueryBuilder()
|
||||
->delete('User', 'u')
|
||||
->where('u.id = :user_id')
|
||||
->setParameter('user_id', 1);
|
||||
```
|
||||
|
||||
## BC BREAK: Split output walkers and tree walkers
|
||||
|
||||
`SqlWalker` and its child classes don't implement the `TreeWalker` interface
|
||||
anymore.
|
||||
|
||||
The following methods have been removed from the `TreeWalker` interface and
|
||||
from the `TreeWalkerAdapter` and `TreeWalkerChain` classes:
|
||||
|
||||
* `setQueryComponent()`
|
||||
* `walkSelectClause()`
|
||||
* `walkFromClause()`
|
||||
* `walkFunction()`
|
||||
* `walkOrderByClause()`
|
||||
* `walkOrderByItem()`
|
||||
* `walkHavingClause()`
|
||||
* `walkJoin()`
|
||||
* `walkSelectExpression()`
|
||||
* `walkQuantifiedExpression()`
|
||||
* `walkSubselect()`
|
||||
* `walkSubselectFromClause()`
|
||||
* `walkSimpleSelectClause()`
|
||||
* `walkSimpleSelectExpression()`
|
||||
* `walkAggregateExpression()`
|
||||
* `walkGroupByClause()`
|
||||
* `walkGroupByItem()`
|
||||
* `walkDeleteClause()`
|
||||
* `walkUpdateClause()`
|
||||
* `walkUpdateItem()`
|
||||
* `walkWhereClause()`
|
||||
* `walkConditionalExpression()`
|
||||
* `walkConditionalTerm()`
|
||||
* `walkConditionalFactor()`
|
||||
* `walkConditionalPrimary()`
|
||||
* `walkExistsExpression()`
|
||||
* `walkCollectionMemberExpression()`
|
||||
* `walkEmptyCollectionComparisonExpression()`
|
||||
* `walkNullComparisonExpression()`
|
||||
* `walkInExpression()`
|
||||
* `walkInstanceOfExpression()`
|
||||
* `walkLiteral()`
|
||||
* `walkBetweenExpression()`
|
||||
* `walkLikeExpression()`
|
||||
* `walkStateFieldPathExpression()`
|
||||
* `walkComparisonExpression()`
|
||||
* `walkInputParameter()`
|
||||
* `walkArithmeticExpression()`
|
||||
* `walkArithmeticTerm()`
|
||||
* `walkStringPrimary()`
|
||||
* `walkArithmeticFactor()`
|
||||
* `walkSimpleArithmeticExpression()`
|
||||
* `walkPathExpression()`
|
||||
* `walkResultVariable()`
|
||||
* `getExecutor()`
|
||||
|
||||
The following changes have been made to the abstract `TreeWalkerAdapter` class:
|
||||
|
||||
* The method `setQueryComponent()` is now protected.
|
||||
* The method `_getQueryComponents()` has been removed in favor of
|
||||
`getQueryComponents()`.
|
||||
|
||||
## BC BREAK: Removed identity columns emulation through sequences
|
||||
|
||||
If the platform you are using does not support identity columns, you should
|
||||
switch to the `SEQUENCE` strategy.
|
||||
|
||||
## BC BREAK: Made setters parameters mandatory
|
||||
|
||||
The following methods require an argument when being called. Pass `null`
|
||||
instead of omitting the argument.
|
||||
|
||||
* `Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs::setFoundMetadata()`
|
||||
* `Doctrine\ORM\AbstractQuery::setHydrationCacheProfile()`
|
||||
* `Doctrine\ORM\AbstractQuery::setResultCache()`
|
||||
* `Doctrine\ORM\AbstractQuery::setResultCacheProfile()`
|
||||
|
||||
## BC BREAK: New argument to `NamingStrategy::joinColumnName()`
|
||||
|
||||
### Before
|
||||
|
||||
```php
|
||||
<?php
|
||||
class MyStrategy implements NamingStrategy
|
||||
{
|
||||
/**
|
||||
* @param string $propertyName A property name.
|
||||
*/
|
||||
public function joinColumnName($propertyName): string
|
||||
{
|
||||
// …
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
The `class-string` type for `$className` can be inherited from the signature of
|
||||
the interface.
|
||||
|
||||
```php
|
||||
<?php
|
||||
class MyStrategy implements NamingStrategy
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function joinColumnName(string $propertyName, string $className): string
|
||||
{
|
||||
// …
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## BC BREAK: Remove `StaticPHPDriver` and `DriverChain`
|
||||
|
||||
Use `Doctrine\Persistence\Mapping\Driver\StaticPHPDriver` and
|
||||
`Doctrine\Persistence\Mapping\Driver\MappingDriverChain` from
|
||||
`doctrine/persistence` instead.
|
||||
|
||||
## BC BREAK: `UnderscoreNamingStrategy` is number aware only
|
||||
|
||||
The second argument to `UnderscoreNamingStrategy::__construct()` was dropped,
|
||||
the strategy can no longer be unaware of numbers.
|
||||
|
||||
## BC BREAK: Remove `Doctrine\ORM\Tools\DisconnectedClassMetadataFactory`
|
||||
|
||||
No replacement is provided.
|
||||
|
||||
## BC BREAK: Remove support for `Type::canRequireSQLConversion()`
|
||||
|
||||
This feature was deprecated in DBAL 3.3.0 and will be removed in DBAL 4.0.
|
||||
The value conversion methods are now called regardless of the type.
|
||||
|
||||
The `MappingException::sqlConversionNotAllowedForIdentifiers()` method has been removed
|
||||
as no longer relevant.
|
||||
|
||||
## BC Break: Removed the `doctrine` binary.
|
||||
|
||||
The documentation explains how the console tools can be bootstrapped for
|
||||
standalone usage:
|
||||
|
||||
https://www.doctrine-project.org/projects/doctrine-orm/en/stable/reference/tools.html
|
||||
|
||||
The method `ConsoleRunner::printCliConfigTemplate()` has been removed as well
|
||||
because it was only useful in the context of the `doctrine` binary.
|
||||
|
||||
## BC Break: Removed `EntityManagerHelper` and related logic
|
||||
|
||||
All console commands require a `$entityManagerProvider` to be passed via the
|
||||
constructor. Commands won't try to get the entity manager from a previously
|
||||
registered `em` console helper.
|
||||
|
||||
The following classes have been removed:
|
||||
|
||||
* `Doctrine\ORM\Tools\Console\EntityManagerProvider\HelperSetManagerProvider`
|
||||
* `Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper`
|
||||
|
||||
The following breaking changes have been applied to `Doctrine\ORM\Tools\Console\ConsoleRunner`:
|
||||
|
||||
* The method `createHelperSet()` has been removed.
|
||||
* The methods `run()` and `createApplication()` don't accept an instance of
|
||||
`HelperSet` as first argument anymore.
|
||||
* The method `addCommands()` requires an instance of `EntityManagerProvider`
|
||||
as second argument now.
|
||||
|
||||
## BC Break: `Exception\ORMException` is no longer a class, but an interface
|
||||
|
||||
All methods in `Doctrine\ORM\ORMException` have been extracted to dedicated exceptions.
|
||||
|
||||
* `missingMappingDriverImpl()` => `Exception\MissingMappingDriverImplementation::create()`
|
||||
* `unrecognizedField()` => `Persisters\Exception\UnrecognizedField::byName()`
|
||||
* `unexpectedAssociationValue()` => `Exception\UnexpectedAssociationValue::create()`
|
||||
* `invalidOrientation()` => `Persisters\Exception\InvalidOrientation::fromClassNameAndField()`
|
||||
* `entityManagerClosed()` => `Exception\EntityManagerClosed::create()`
|
||||
* `invalidHydrationMode()` => `Exception\InvalidHydrationMode::fromMode()`
|
||||
* `mismatchedEventManager()` => `Exception\MismatchedEventManager::create()`
|
||||
* `findByRequiresParameter()` => `Repository\Exception\InvalidMagicMethodCall::onMissingParameter()`
|
||||
* `invalidMagicCall()` => `Repository\Exception\InvalidMagicMethodCall::becauseFieldNotFoundIn()`
|
||||
* `invalidFindByInverseAssociation()` => `Repository\Exception\InvalidFindByCall::fromInverseSideUsage()`
|
||||
* `invalidResultCacheDriver()` => `Cache\Exception\InvalidResultCacheDriver::create()`
|
||||
* `notSupported()` => `Exception\NotSupported::create()`
|
||||
* `queryCacheNotConfigured()` => `QueryCacheNotConfigured::create()`
|
||||
* `metadataCacheNotConfigured()` => `Cache\Exception\MetadataCacheNotConfigured::create()`
|
||||
* `queryCacheUsesNonPersistentCache()` => `Cache\Exception\QueryCacheUsesNonPersistentCache::fromDriver()`
|
||||
* `metadataCacheUsesNonPersistentCache()` => `Cache\Exception\MetadataCacheUsesNonPersistentCache::fromDriver()`
|
||||
* `proxyClassesAlwaysRegenerating()` => `Exception\ProxyClassesAlwaysRegenerating::create()`
|
||||
* `invalidEntityRepository()` => `Exception\InvalidEntityRepository::fromClassName()`
|
||||
* `missingIdentifierField()` => `Exception\MissingIdentifierField::fromFieldAndClass()`
|
||||
* `unrecognizedIdentifierFields()` => `Exception\UnrecognizedIdentifierFields::fromClassAndFieldNames()`
|
||||
* `cantUseInOperatorOnCompositeKeys()` => `Persisters\Exception\CantUseInOperatorOnCompositeKeys::create()`
|
||||
|
||||
## BC Break: `CacheException` is no longer a class, but an interface
|
||||
|
||||
All methods in `Doctrine\ORM\Cache\CacheException` have been extracted to dedicated exceptions.
|
||||
|
||||
* `updateReadOnlyCollection()` => `Cache\Exception\CannotUpdateReadOnlyCollection::fromEntityAndField()`
|
||||
* `updateReadOnlyEntity()` => `Cache\Exception\CannotUpdateReadOnlyEntity::fromEntity()`
|
||||
* `nonCacheableEntity()` => `Cache\Exception\NonCacheableEntity::fromEntity()`
|
||||
* `nonCacheableEntityAssociation()` => `Cache\Exception\NonCacheableEntityAssociation::fromEntityAndField()`
|
||||
|
||||
|
||||
## BC Break: Missing type declaration added for identifier generators
|
||||
|
||||
Although undocumented, it was possible to configure a custom repository
|
||||
class that implements `ObjectRepository` but does not extend the
|
||||
`EntityRepository` base class. Repository classes have to extend
|
||||
`EntityRepository` now.
|
||||
|
||||
## BC BREAK: Removed support for entity namespace alias
|
||||
|
||||
- `EntityManager::getRepository()` no longer accepts the entity namespace alias
|
||||
notation.
|
||||
- `Configuration::addEntityNamespace()` and
|
||||
`Configuration::getEntityNamespace()` have been removed.
|
||||
|
||||
## BC BREAK: Remove helper methods from `AbstractCollectionPersister`
|
||||
|
||||
The following protected methods of
|
||||
`Doctrine\ORM\Cache\Persister\Collection\AbstractCollectionPersister`
|
||||
have been removed.
|
||||
|
||||
* `evictCollectionCache()`
|
||||
* `evictElementCache()`
|
||||
|
||||
## BC BREAK: `Doctrine\ORM\Query\TreeWalkerChainIterator`
|
||||
|
||||
This class has been removed without replacement.
|
||||
|
||||
## BC BREAK: Remove quoting methods from `ClassMetadata`
|
||||
|
||||
The following methods have been removed from the class metadata because
|
||||
quoting is handled by implementations of `Doctrine\ORM\Mapping\QuoteStrategy`:
|
||||
|
||||
* `getQuotedIdentifierColumnNames()`
|
||||
* `getQuotedColumnName()`
|
||||
* `getQuotedTableName()`
|
||||
* `getQuotedJoinTableName()`
|
||||
|
||||
## BC BREAK: Remove ability to merge detached entities
|
||||
|
||||
Merge semantics was a poor fit for the PHP "share-nothing" architecture.
|
||||
In addition to that, merging caused multiple issues with data integrity
|
||||
in the managed entity graph, which was constantly spawning more edge-case
|
||||
bugs/scenarios.
|
||||
|
||||
The method `UnitOfWork::merge()` has been removed. The method
|
||||
`EntityManager::merge()` will throw an exception on each call.
|
||||
|
||||
## BC BREAK: Removed ability to partially flush/commit entity manager and unit of work
|
||||
|
||||
The following methods don't accept a single entity or an array of entities anymore:
|
||||
|
||||
* `Doctrine\ORM\EntityManager::flush()`
|
||||
* `Doctrine\ORM\Decorator\EntityManagerDecorator::flush()`
|
||||
* `Doctrine\ORM\UnitOfWork::commit()`
|
||||
|
||||
The semantics of `flush()` and `commit()` will remain the same, but the change
|
||||
tracking will be performed on all entities managed by the unit of work, and not
|
||||
just on the provided entities, as the parameter is now completely ignored.
|
||||
|
||||
## BC BREAK: Removed ability to partially clear entity manager and unit of work
|
||||
|
||||
* Passing an argument other than `null` to `EntityManager::clear()` will raise
|
||||
an exception.
|
||||
* The unit of work cannot be cleared partially anymore. Passing an argument to
|
||||
`UnitOfWork::clear()` does not have any effect anymore; the unit of work is
|
||||
cleared completely.
|
||||
* The method `EntityRepository::clear()` has been removed.
|
||||
* The methods `getEntityClass()` and `clearsAllEntities()` have been removed
|
||||
from `OnClearEventArgs`.
|
||||
|
||||
## BC BREAK: Remove support for Doctrine Cache
|
||||
|
||||
The Doctrine Cache library is not supported anymore. The following methods
|
||||
have been removed from `Doctrine\ORM\Configuration`:
|
||||
|
||||
* `getQueryCacheImpl()`
|
||||
* `setQueryCacheImpl()`
|
||||
* `getHydrationCacheImpl()`
|
||||
* `setHydrationCacheImpl()`
|
||||
* `getMetadataCacheImpl()`
|
||||
* `setMetadataCacheImpl()`
|
||||
|
||||
The methods have been replaced by PSR-6 compatible counterparts
|
||||
(just strip the `Impl` suffix from the old name to get the new one).
|
||||
|
||||
## BC BREAK: Remove `Doctrine\ORM\Configuration::newDefaultAnnotationDriver`
|
||||
|
||||
This functionality has been moved to the new `ORMSetup` class. Call
|
||||
`Doctrine\ORM\ORMSetup::createDefaultAnnotationDriver()` to create
|
||||
a new annotation driver.
|
||||
|
||||
## BC BREAK: Remove `Doctrine\ORM\Tools\Setup`
|
||||
|
||||
In our effort to migrate from Doctrine Cache to PSR-6, the `Setup` class which
|
||||
accepted a Doctrine Cache instance in each method has been removed.
|
||||
|
||||
The replacement is `Doctrine\ORM\ORMSetup` which accepts a PSR-6
|
||||
cache instead.
|
||||
|
||||
## BC BREAK: Removed named queries
|
||||
|
||||
All APIs related to named queries have been removed.
|
||||
|
||||
## BC BREAK: Remove old cache accessors and mutators from query classes
|
||||
|
||||
The following methods have been removed from `AbstractQuery`:
|
||||
|
||||
* `setResultCacheDriver()`
|
||||
* `getResultCacheDriver()`
|
||||
* `useResultCache()`
|
||||
* `getResultCacheLifetime()`
|
||||
* `getResultCacheId()`
|
||||
|
||||
The following methods have been removed from `Query`:
|
||||
|
||||
* `setQueryCacheDriver()`
|
||||
* `getQueryCacheDriver()`
|
||||
|
||||
## BC BREAK: Remove `Doctrine\ORM\Cache\MultiGetRegion`
|
||||
|
||||
The interface has been merged into `Doctrine\ORM\Cache\Region`.
|
||||
|
||||
## BC BREAK: Rename `AbstractIdGenerator::generate()` to `generateId()`
|
||||
|
||||
* Implementations of `AbstractIdGenerator` have to implement the method
|
||||
`generateId()`.
|
||||
* The method `generate()` has been removed from `AbstractIdGenerator`.
|
||||
|
||||
## BC BREAK: Remove cache settings inspection
|
||||
|
||||
Doctrine does not provide its own cache implementation anymore and relies on
|
||||
the PSR-6 standard instead. As a consequence, we cannot determine anymore
|
||||
whether a given cache adapter is suitable for a production environment.
|
||||
Because of that, functionality that aims to do so has been removed:
|
||||
|
||||
* `Configuration::ensureProductionSettings()`
|
||||
* the `orm:ensure-production-settings` console command
|
||||
|
||||
## BC BREAK: PSR-6-based second level cache
|
||||
|
||||
The second level cache has been reworked to consume a PSR-6 cache. Using a
|
||||
Doctrine Cache instance is not supported anymore.
|
||||
|
||||
* `DefaultCacheFactory`: The constructor expects a PSR-6 cache item pool as
|
||||
second argument now.
|
||||
* `DefaultMultiGetRegion`: This class has been removed.
|
||||
* `DefaultRegion`:
|
||||
* The constructor expects a PSR-6 cache item pool as second argument now.
|
||||
* The protected `$cache` property is removed.
|
||||
* The properties `$name` and `$lifetime` as well as the constant
|
||||
`REGION_KEY_SEPARATOR` and the method `getCacheEntryKey()` are
|
||||
`private` now.
|
||||
* The method `getCache()` has been removed.
|
||||
|
||||
|
||||
## BC Break: Remove `Doctrine\ORM\Mapping\Driver\PHPDriver`
|
||||
|
||||
Use `StaticPHPDriver` instead when you want to programmatically configure
|
||||
entity metadata.
|
||||
|
||||
## BC BREAK: Remove `Doctrine\ORM\EntityManagerInterface#transactional()`
|
||||
|
||||
This method has been replaced by `Doctrine\ORM\EntityManagerInterface#wrapInTransaction()`.
|
||||
|
||||
## BC BREAK: Removed support for schema emulation.
|
||||
|
||||
The ORM no longer attempts to emulate schemas on SQLite.
|
||||
|
||||
## BC BREAK: Remove `Setup::registerAutoloadDirectory()`
|
||||
|
||||
Use Composer's autoloader instead.
|
||||
|
||||
## BC BREAK: Remove YAML mapping drivers.
|
||||
|
||||
If your code relies on `YamlDriver` or `SimpleYamlDriver`, you **MUST** migrate to
|
||||
attribute, annotation or XML drivers instead.
|
||||
|
||||
You can use the `orm:convert-mapping` command to convert your metadata mapping to XML
|
||||
_before_ upgrading to 3.0:
|
||||
|
||||
```sh
|
||||
php doctrine orm:convert-mapping xml /path/to/mapping-path-converted-to-xml
|
||||
```
|
||||
|
||||
## BC BREAK: Remove code generators and related console commands
|
||||
|
||||
These console commands have been removed:
|
||||
|
||||
* `orm:convert-d1-schema`
|
||||
* `orm:convert-mapping`
|
||||
* `orm:generate:entities`
|
||||
* `orm:generate-repositories`
|
||||
|
||||
These classes have been deprecated:
|
||||
|
||||
* `Doctrine\ORM\Tools\ConvertDoctrine1Schema`
|
||||
* `Doctrine\ORM\Tools\EntityGenerator`
|
||||
* `Doctrine\ORM\Tools\EntityRepositoryGenerator`
|
||||
|
||||
The entire `Doctrine\ORM\Tools\Export` namespace has been removed as well.
|
||||
|
||||
## BC BREAK: Removed `Doctrine\ORM\Version`
|
||||
|
||||
Use Composer's runtime API if you _really_ need to check the version of the ORM package at runtime.
|
||||
|
||||
## BC BREAK: EntityRepository::count() signature change
|
||||
|
||||
The argument `$criteria` of `Doctrine\ORM\EntityRepository::count()` is now
|
||||
optional. Overrides in child classes should be made compatible.
|
||||
|
||||
## BC BREAK: changes in exception hierarchy
|
||||
|
||||
- `Doctrine\ORM\ORMException` has been removed
|
||||
- `Doctrine\ORM\Exception\ORMException` is now an interface
|
||||
|
||||
## Variadic methods now use native variadics
|
||||
The following methods were using `func_get_args()` to simulate a variadic argument:
|
||||
- `Doctrine\ORM\Query\Expr#andX()`
|
||||
- `Doctrine\ORM\Query\Expr#orX()`
|
||||
- `Doctrine\ORM\QueryBuilder#select()`
|
||||
- `Doctrine\ORM\QueryBuilder#addSelect()`
|
||||
- `Doctrine\ORM\QueryBuilder#where()`
|
||||
- `Doctrine\ORM\QueryBuilder#andWhere()`
|
||||
- `Doctrine\ORM\QueryBuilder#orWhere()`
|
||||
- `Doctrine\ORM\QueryBuilder#groupBy()`
|
||||
- `Doctrine\ORM\QueryBuilder#andGroupBy()`
|
||||
- `Doctrine\ORM\QueryBuilder#having()`
|
||||
- `Doctrine\ORM\QueryBuilder#andHaving()`
|
||||
- `Doctrine\ORM\QueryBuilder#orHaving()`
|
||||
A variadic argument is now actually used in their signatures signature (`...$x`).
|
||||
Signatures of overridden methods should be changed accordingly
|
||||
|
||||
## Minor BC BREAK: removed `Doctrine\ORM\EntityManagerInterface#copy()`
|
||||
|
||||
Method `Doctrine\ORM\EntityManagerInterface#copy()` never got its implementation and is removed in 3.0.
|
||||
|
||||
## BC BREAK: Removed classes related to UUID and TABLE generator strategies
|
||||
|
||||
The following classes have been removed:
|
||||
- `Doctrine\ORM\Id\TableGenerator`
|
||||
- `Doctrine\ORM\Id\UuidGenerator`
|
||||
|
||||
Using the `UUID` strategy for generating identifiers is not supported anymore.
|
||||
|
||||
## BC BREAK: Removed `Query::iterate()`
|
||||
|
||||
The deprecated method `Query::iterate()` has been removed along with the
|
||||
following classes and methods:
|
||||
|
||||
- `AbstractHydrator::iterate()`
|
||||
- `AbstractHydrator::hydrateRow()`
|
||||
- `IterableResult`
|
||||
|
||||
Use `toIterable()` instead.
|
||||
|
||||
# Upgrade to 2.20
|
||||
|
||||
## Add `Doctrine\ORM\Query\OutputWalker` interface, deprecate `Doctrine\ORM\Query\SqlWalker::getExecutor()`
|
||||
|
||||
Output walkers should implement the new `\Doctrine\ORM\Query\OutputWalker` interface and create
|
||||
`Doctrine\ORM\Query\Exec\SqlFinalizer` instances instead of `Doctrine\ORM\Query\Exec\AbstractSqlExecutor`s.
|
||||
The output walker must not base its workings on the query `firstResult`/`maxResult` values, so that the
|
||||
`SqlFinalizer` can be kept in the query cache and used regardless of the actual `firstResult`/`maxResult` values.
|
||||
Any operation dependent on `firstResult`/`maxResult` should take place within the `SqlFinalizer::createExecutor()`
|
||||
method. Details can be found at https://github.com/doctrine/orm/pull/11188.
|
||||
|
||||
## Explictly forbid property hooks
|
||||
|
||||
Property hooks are not supported yet by Doctrine ORM. Until support is added,
|
||||
they are explicitly forbidden because the support would result in a breaking
|
||||
change in behavior.
|
||||
|
||||
Progress on this is tracked at https://github.com/doctrine/orm/issues/11624 .
|
||||
|
||||
## PARTIAL DQL syntax is undeprecated
|
||||
|
||||
Use of the PARTIAL keyword is not deprecated anymore in DQL, because we will be
|
||||
able to support PARTIAL objects with PHP 8.4 Lazy Objects and
|
||||
Symfony/VarExporter in a better way. When we decided to remove this feature
|
||||
these two abstractions did not exist yet.
|
||||
|
||||
WARNING: If you want to upgrade to 3.x and still use PARTIAL keyword in DQL
|
||||
with array or object hydrators, then you have to directly migrate to ORM 3.3.x or higher.
|
||||
PARTIAL keyword in DQL is not available in 3.0, 3.1 and 3.2 of ORM.
|
||||
|
||||
## Deprecate `\Doctrine\ORM\Query\Parser::setCustomOutputTreeWalker()`
|
||||
|
||||
Use the `\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER` query hint to set the output walker
|
||||
class instead of setting it through the `\Doctrine\ORM\Query\Parser::setCustomOutputTreeWalker()` method
|
||||
on the parser instance.
|
||||
|
||||
# Upgrade to 2.19
|
||||
|
||||
## Deprecate calling `ClassMetadata::getAssociationMappedByTargetField()` with the owning side of an association
|
||||
|
||||
Calling
|
||||
`Doctrine\ORM\Mapping\ClassMetadata::getAssociationMappedByTargetField()` with
|
||||
the owning side of an association returns `null`, which is undocumented, and
|
||||
wrong according to the phpdoc of the parent method.
|
||||
|
||||
If you do not know whether you are on the owning or inverse side of an association,
|
||||
you can use `Doctrine\ORM\Mapping\ClassMetadata::isAssociationInverseSide()`
|
||||
to find out.
|
||||
|
||||
## Deprecate `Doctrine\ORM\Query\Lexer::T_*` constants
|
||||
|
||||
Use `Doctrine\ORM\Query\TokenType::T_*` instead.
|
||||
|
||||
# Upgrade to 2.17
|
||||
|
||||
## Deprecate annotations classes for named queries
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
include(__DIR__ . '/doctrine.php');
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
fwrite(
|
||||
STDERR,
|
||||
'[Warning] The use of this script is discouraged. See'
|
||||
. ' https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/tools.html#doctrine-console'
|
||||
. ' for instructions on bootstrapping the console runner.'
|
||||
. PHP_EOL
|
||||
);
|
||||
|
||||
echo PHP_EOL . PHP_EOL;
|
||||
|
||||
require_once 'Doctrine/Common/ClassLoader.php';
|
||||
|
||||
$classLoader = new \Doctrine\Common\ClassLoader('Doctrine');
|
||||
$classLoader->register();
|
||||
|
||||
$classLoader = new \Doctrine\Common\ClassLoader('Symfony');
|
||||
$classLoader->register();
|
||||
|
||||
$configFile = getcwd() . DIRECTORY_SEPARATOR . 'cli-config.php';
|
||||
|
||||
$helperSet = null;
|
||||
if (file_exists($configFile)) {
|
||||
if ( ! is_readable($configFile)) {
|
||||
trigger_error(
|
||||
'Configuration file [' . $configFile . '] does not have read permission.', E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
require $configFile;
|
||||
|
||||
foreach ($GLOBALS as $helperSetCandidate) {
|
||||
if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) {
|
||||
$helperSet = $helperSetCandidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet();
|
||||
|
||||
\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet);
|
||||
@@ -1,9 +0,0 @@
|
||||
@echo off
|
||||
|
||||
if "%PHPBIN%" == "" set PHPBIN=@php_bin@
|
||||
if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH
|
||||
GOTO RUN
|
||||
:USE_PEAR_PATH
|
||||
set PHPBIN=%PHP_PEAR_PHP_BIN%
|
||||
:RUN
|
||||
"%PHPBIN%" "@bin_dir@\doctrine" %*
|
||||
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
||||
|
||||
fwrite(
|
||||
STDERR,
|
||||
'[Warning] The use of this script is discouraged. See'
|
||||
. ' https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/tools.html#doctrine-console'
|
||||
. ' for instructions on bootstrapping the console runner.'
|
||||
. PHP_EOL
|
||||
);
|
||||
|
||||
echo PHP_EOL . PHP_EOL;
|
||||
|
||||
$autoloadFiles = [
|
||||
__DIR__ . '/../vendor/autoload.php',
|
||||
__DIR__ . '/../../../autoload.php'
|
||||
];
|
||||
|
||||
foreach ($autoloadFiles as $autoloadFile) {
|
||||
if (file_exists($autoloadFile)) {
|
||||
require_once $autoloadFile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$directories = [getcwd(), getcwd() . DIRECTORY_SEPARATOR . 'config'];
|
||||
|
||||
$configFile = null;
|
||||
foreach ($directories as $directory) {
|
||||
$configFile = $directory . DIRECTORY_SEPARATOR . 'cli-config.php';
|
||||
|
||||
if (file_exists($configFile)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! file_exists($configFile)) {
|
||||
ConsoleRunner::printCliConfigTemplate();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( ! is_readable($configFile)) {
|
||||
echo 'Configuration file [' . $configFile . '] does not have read permission.' . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$commands = [];
|
||||
|
||||
$helperSet = require $configFile;
|
||||
|
||||
if ( ! ($helperSet instanceof HelperSet)) {
|
||||
foreach ($GLOBALS as $helperSetCandidate) {
|
||||
if ($helperSetCandidate instanceof HelperSet) {
|
||||
$helperSet = $helperSetCandidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleRunner::run($helperSet, $commands);
|
||||
@@ -3,9 +3,8 @@
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
@@ -14,6 +13,9 @@
|
||||
<var name="db_port" value="3306"/>
|
||||
<var name="db_user" value="root" />
|
||||
<var name="db_dbname" value="doctrine_tests" />
|
||||
<var name="db_default_table_option_charset" value="utf8mb4" />
|
||||
<var name="db_default_table_option_collation" value="utf8mb4_unicode_ci" />
|
||||
<var name="db_default_table_option_engine" value="InnoDB" />
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
@@ -25,11 +27,11 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">../../../src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</include>
|
||||
</source>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
@@ -14,6 +13,9 @@
|
||||
<var name="db_port" value="3306"/>
|
||||
<var name="db_user" value="root" />
|
||||
<var name="db_dbname" value="doctrine_tests" />
|
||||
<var name="db_default_table_option_charset" value="utf8mb4" />
|
||||
<var name="db_default_table_option_collation" value="utf8mb4_unicode_ci" />
|
||||
<var name="db_default_table_option_engine" value="InnoDB" />
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
@@ -25,12 +27,11 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">../../../src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
</include>
|
||||
</source>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
@@ -25,11 +24,11 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">../../../src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</include>
|
||||
</source>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
@@ -23,11 +22,11 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">../../../src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</include>
|
||||
</source>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
@@ -25,11 +24,11 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">../../../src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</include>
|
||||
</source>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
@@ -23,11 +22,11 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">../../../src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</include>
|
||||
</source>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
|
||||
@@ -15,49 +15,43 @@
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"phpstan/extension-installer": true
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0",
|
||||
"php": "^8.1",
|
||||
"composer-runtime-api": "^2",
|
||||
"ext-ctype": "*",
|
||||
"doctrine/cache": "^1.12.1 || ^2.1.1",
|
||||
"doctrine/collections": "^1.5 || ^2.1",
|
||||
"doctrine/common": "^3.0.3",
|
||||
"doctrine/dbal": "^2.13.1 || ^3.2",
|
||||
"doctrine/collections": "^2.2",
|
||||
"doctrine/dbal": "^3.8.2 || ^4",
|
||||
"doctrine/deprecations": "^0.5.3 || ^1",
|
||||
"doctrine/event-manager": "^1.2 || ^2",
|
||||
"doctrine/inflector": "^1.4 || ^2.0",
|
||||
"doctrine/instantiator": "^1.3 || ^2",
|
||||
"doctrine/lexer": "^2 || ^3",
|
||||
"doctrine/persistence": "^2.4 || ^3",
|
||||
"doctrine/lexer": "^3",
|
||||
"doctrine/persistence": "^3.3.1",
|
||||
"psr/cache": "^1 || ^2 || ^3",
|
||||
"symfony/console": "^4.2 || ^5.0 || ^6.0 || ^7.0",
|
||||
"symfony/polyfill-php72": "^1.23",
|
||||
"symfony/polyfill-php80": "^1.16"
|
||||
"symfony/console": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/var-exporter": "^6.3.9 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/annotations": "^1.13 || ^2",
|
||||
"doctrine/coding-standard": "^9.0.2 || ^12.0",
|
||||
"phpbench/phpbench": "^0.16.10 || ^1.0",
|
||||
"phpstan/phpstan": "~1.4.10 || 1.10.35",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6",
|
||||
"doctrine/coding-standard": "^12.0",
|
||||
"phpbench/phpbench": "^1.0",
|
||||
"phpdocumentor/guides-cli": "^1.4",
|
||||
"phpstan/extension-installer": "^1.4",
|
||||
"phpstan/phpstan": "1.12.6",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.2",
|
||||
"phpunit/phpunit": "^10.4.0",
|
||||
"psr/log": "^1 || ^2 || ^3",
|
||||
"squizlabs/php_codesniffer": "3.7.2",
|
||||
"symfony/cache": "^4.4 || ^5.4 || ^6.4 || ^7.0",
|
||||
"symfony/var-exporter": "^4.4 || ^5.4 || ^6.2 || ^7.0",
|
||||
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0",
|
||||
"vimeo/psalm": "4.30.0 || 5.16.0"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/annotations": "<1.13 || >= 3.0"
|
||||
"symfony/cache": "^5.4 || ^6.2 || ^7.0",
|
||||
"vimeo/psalm": "5.24.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-dom": "Provides support for XSD validation for XML mapping files",
|
||||
"symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0",
|
||||
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
|
||||
"symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Doctrine\\ORM\\": "src" }
|
||||
@@ -69,7 +63,6 @@
|
||||
"Doctrine\\Performance\\": "tests/Performance"
|
||||
}
|
||||
},
|
||||
"bin": ["bin/doctrine"],
|
||||
"archive": {
|
||||
"exclude": ["!vendor", "tests", "*phpunit.xml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"]
|
||||
}
|
||||
|
||||
4
docs/.gitignore
vendored
4
docs/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
en/_exts/configurationblock.pyc
|
||||
build
|
||||
en/_build
|
||||
.idea
|
||||
3
docs/.gitmodules
vendored
3
docs/.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "en/_theme"]
|
||||
path = en/_theme
|
||||
url = https://github.com/doctrine/doctrine-sphinx-theme.git
|
||||
@@ -35,14 +35,12 @@ class ConfigurationBlock(Directive):
|
||||
'html': 'HTML',
|
||||
'xml': 'XML',
|
||||
'php': 'PHP',
|
||||
'yaml': 'YAML',
|
||||
'jinja': 'Twig',
|
||||
'html+jinja': 'Twig',
|
||||
'jinja+html': 'Twig',
|
||||
'php+html': 'PHP',
|
||||
'html+php': 'PHP',
|
||||
'ini': 'INI',
|
||||
'php-annotations': 'Annotations',
|
||||
}
|
||||
|
||||
def run(self):
|
||||
|
||||
Submodule docs/en/_theme deleted from 6f1bc8bead
@@ -140,11 +140,6 @@ Now we're going to create the ``point`` type and implement all required methods.
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function canRequireSQLConversion()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function convertToPHPValueSQL($sqlExpr, AbstractPlatform $platform)
|
||||
{
|
||||
return sprintf('AsText(%s)', $sqlExpr);
|
||||
|
||||
@@ -99,12 +99,12 @@ discuss it step by step:
|
||||
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER); // (2)
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS); // (3)
|
||||
$parser->match(TokenType::T_IDENTIFIER); // (2)
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS); // (3)
|
||||
$this->firstDateExpression = $parser->ArithmeticPrimary(); // (4)
|
||||
$parser->match(Lexer::T_COMMA); // (5)
|
||||
$parser->match(TokenType::T_COMMA); // (5)
|
||||
$this->secondDateExpression = $parser->ArithmeticPrimary(); // (6)
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS); // (3)
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS); // (3)
|
||||
}
|
||||
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
@@ -183,23 +183,23 @@ I'll skip the blah and show the code for this function:
|
||||
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->firstDateExpression = $parser->ArithmeticPrimary();
|
||||
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
|
||||
$this->intervalExpression = $parser->ArithmeticPrimary();
|
||||
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
|
||||
/** @var Lexer $lexer */
|
||||
$lexer = $parser->getLexer();
|
||||
$this->unit = $lexer->token['value'];
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
@@ -232,6 +232,33 @@ vendors SQL parser to show us further errors in the parsing
|
||||
process, for example if the Unit would not be one of the supported
|
||||
values by MySql.
|
||||
|
||||
Typed functions
|
||||
---------------
|
||||
By default, result of custom functions is fetched as-is from the database driver.
|
||||
If you want to be sure that the type is always the same, then your custom function needs to
|
||||
implement ``Doctrine\ORM\Query\AST\TypedExpression``. Then, the result is wired
|
||||
through ``Doctrine\DBAL\Types\Type::convertToPhpValue()`` of the ``Type`` returned in ``getReturnType()``.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
||||
use Doctrine\ORM\Query\AST\TypedExpression;
|
||||
|
||||
class DateDiff extends FunctionNode implements TypedExpression
|
||||
{
|
||||
// ...
|
||||
|
||||
public function getReturnType(): Type
|
||||
{
|
||||
return Type::getType(Types::INTEGER);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Conclusion
|
||||
----------
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Implementing ArrayAccess for Domain Objects
|
||||
===========================================
|
||||
|
||||
.. sectionauthor:: Roman Borschel (roman@code-factory.org)
|
||||
.. sectionauthor:: Roman Borschel <roman@code-factory.org>
|
||||
|
||||
This recipe will show you how to implement ArrayAccess for your
|
||||
domain objects in order to allow more uniform access, for example
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
Implementing the Notify ChangeTracking Policy
|
||||
=============================================
|
||||
|
||||
.. sectionauthor:: Roman Borschel (roman@code-factory.org)
|
||||
|
||||
The NOTIFY change-tracking policy is the most effective
|
||||
change-tracking policy provided by Doctrine but it requires some
|
||||
boilerplate code. This recipe will show you how this boilerplate
|
||||
code should look like. We will implement it on a
|
||||
`Layer Supertype <https://martinfowler.com/eaaCatalog/layerSupertype.html>`_
|
||||
for all our domain objects.
|
||||
|
||||
.. note::
|
||||
|
||||
The notify change tracking policy is deprecated and will be removed in ORM 3.0.
|
||||
(\ `Details <https://github.com/doctrine/orm/issues/8383>`_)
|
||||
|
||||
Implementing NotifyPropertyChanged
|
||||
----------------------------------
|
||||
|
||||
The NOTIFY policy is based on the assumption that the entities
|
||||
notify interested listeners of changes to their properties. For
|
||||
that purpose, a class that wants to use this policy needs to
|
||||
implement the ``NotifyPropertyChanged`` interface from the
|
||||
``Doctrine\Common`` namespace.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Persistence\NotifyPropertyChanged;
|
||||
use Doctrine\Persistence\PropertyChangedListener;
|
||||
|
||||
abstract class DomainObject implements NotifyPropertyChanged
|
||||
{
|
||||
private $listeners = array();
|
||||
|
||||
public function addPropertyChangedListener(PropertyChangedListener $listener) {
|
||||
$this->listeners[] = $listener;
|
||||
}
|
||||
|
||||
/** Notifies listeners of a change. */
|
||||
protected function onPropertyChanged($propName, $oldValue, $newValue) {
|
||||
if ($this->listeners) {
|
||||
foreach ($this->listeners as $listener) {
|
||||
$listener->propertyChanged($this, $propName, $oldValue, $newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Then, in each property setter of concrete, derived domain classes,
|
||||
you need to invoke onPropertyChanged as follows to notify
|
||||
listeners:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// Mapping not shown, either in attributes, annotations, xml or yaml as usual
|
||||
class MyEntity extends DomainObject
|
||||
{
|
||||
private $data;
|
||||
// ... other fields as usual
|
||||
|
||||
public function setData($data) {
|
||||
if ($data != $this->data) { // check: is it actually modified?
|
||||
$this->onPropertyChanged('data', $this->data, $data);
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
The check whether the new value is different from the old one is
|
||||
not mandatory but recommended. That way you can avoid unnecessary
|
||||
updates and also have full control over when you consider a
|
||||
property changed.
|
||||
@@ -43,13 +43,13 @@ entities:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
#[Entity]
|
||||
class Article
|
||||
{
|
||||
const STATUS_VISIBLE = 'visible';
|
||||
const STATUS_INVISIBLE = 'invisible';
|
||||
|
||||
/** @Column(type="string") */
|
||||
#[Column(type: "string")]
|
||||
private $status;
|
||||
|
||||
public function setStatus($status)
|
||||
@@ -67,10 +67,10 @@ the **columnDefinition** attribute.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
#[Entity]
|
||||
class Article
|
||||
{
|
||||
/** @Column(type="string", columnDefinition="ENUM('visible', 'invisible')") */
|
||||
#[Column(type: "string", columnDefinition: "ENUM('visible', 'invisible')")]
|
||||
private $status;
|
||||
}
|
||||
|
||||
@@ -131,10 +131,10 @@ Then in your entity you can just use this type:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
#[Entity]
|
||||
class Article
|
||||
{
|
||||
/** @Column(type="enumvisibility") */
|
||||
#[Column(type: "enumvisibility")]
|
||||
private $status;
|
||||
}
|
||||
|
||||
@@ -196,4 +196,3 @@ With this base class you can define an enum as easily as:
|
||||
protected $name = 'enumvisibility';
|
||||
protected $values = array('visible', 'invisible');
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ As you can see, we have a method "setBlockEntity" which ties a potential strateg
|
||||
* that is used for this blockitem. (This string (!) value will be persisted by Doctrine ORM)
|
||||
*
|
||||
* This is a doctrine field, so make sure that you use a
|
||||
#[Column] attribute or setup your yaml or xml files correctly
|
||||
#[Column] attribute or setup your xml files correctly
|
||||
* @var string
|
||||
*/
|
||||
protected $strategyClassName;
|
||||
|
||||
@@ -11,7 +11,7 @@ What we offer are hooks to execute any kind of validation.
|
||||
.. note::
|
||||
|
||||
You don't need to validate your entities in the lifecycle
|
||||
events. Its only one of many options. Of course you can also
|
||||
events. It is only one of many options. Of course you can also
|
||||
perform validations in value setters or any other method of your
|
||||
entities that are used in your code.
|
||||
|
||||
@@ -71,23 +71,6 @@ First Attributes:
|
||||
public function assertCustomerAllowedBuying() {}
|
||||
}
|
||||
|
||||
As Annotations:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @HasLifecycleCallbacks
|
||||
*/
|
||||
class Order
|
||||
{
|
||||
/**
|
||||
* @PrePersist @PreUpdate
|
||||
*/
|
||||
public function assertCustomerAllowedBuying() {}
|
||||
}
|
||||
|
||||
In XML Mappings:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
@@ -162,15 +162,13 @@ requiring timezoned datetimes:
|
||||
<?php
|
||||
namespace Shipping;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
#[Entity]
|
||||
class Event
|
||||
{
|
||||
/** @Column(type="datetime") */
|
||||
#[Column(type: 'datetime')]
|
||||
private $created;
|
||||
|
||||
/** @Column(type="string") */
|
||||
#[Column(type: 'string')]
|
||||
private $timezone;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Welcome to Doctrine 2 ORM's documentation!
|
||||
Welcome to Doctrine ORM's documentation!
|
||||
==========================================
|
||||
|
||||
The Doctrine documentation is comprised of tutorials, a reference section and
|
||||
@@ -39,10 +39,8 @@ Mapping Objects onto a Database
|
||||
:doc:`Inheritance <reference/inheritance-mapping>`
|
||||
|
||||
* **Drivers**:
|
||||
:doc:`Docblock Annotations <reference/annotations-reference>` \|
|
||||
:doc:`Attributes <reference/attributes-reference>` \|
|
||||
:doc:`XML <reference/xml-mapping>` \|
|
||||
:doc:`YAML <reference/yaml-mapping>` \|
|
||||
:doc:`PHP <reference/php-mapping>`
|
||||
|
||||
Working with Objects
|
||||
@@ -75,6 +73,7 @@ Advanced Topics
|
||||
* :doc:`TypedFieldMapper <reference/typedfieldmapper>`
|
||||
* :doc:`Improving Performance <reference/improving-performance>`
|
||||
* :doc:`Caching <reference/caching>`
|
||||
* :doc:`Partial Hydration <reference/partial-hydration>`
|
||||
* :doc:`Partial Objects <reference/partial-objects>`
|
||||
* :doc:`Change Tracking Policies <reference/change-tracking-policies>`
|
||||
* :doc:`Best Practices <reference/best-practices>`
|
||||
@@ -96,7 +95,7 @@ Tutorials
|
||||
Changelogs
|
||||
----------
|
||||
|
||||
* `Upgrade <https://github.com/doctrine/doctrine2/blob/master/UPGRADE.md>`_
|
||||
* `Upgrade <https://github.com/doctrine/orm/blob/HEAD/UPGRADE.md>`_
|
||||
|
||||
Cookbook
|
||||
--------
|
||||
@@ -112,7 +111,6 @@ Cookbook
|
||||
|
||||
* **Implementation**:
|
||||
:doc:`Array Access <cookbook/implementing-arrayaccess-for-domain-objects>` \|
|
||||
:doc:`Notify ChangeTracking Example <cookbook/implementing-the-notify-changetracking-policy>` \|
|
||||
:doc:`Working with DateTime <cookbook/working-with-datetime>` \|
|
||||
:doc:`Validation <cookbook/validation-of-entities>` \|
|
||||
:doc:`Entities in the Session <cookbook/entities-in-session>` \|
|
||||
|
||||
@@ -111,21 +111,16 @@ Gets or sets the metadata driver implementation that is used by
|
||||
Doctrine to acquire the object-relational metadata for your
|
||||
classes.
|
||||
|
||||
There are currently 5 available implementations:
|
||||
There are currently 3 available implementations:
|
||||
|
||||
|
||||
- ``Doctrine\ORM\Mapping\Driver\AttributeDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\XmlDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\DriverChain``
|
||||
- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver`` (deprecated and will
|
||||
be removed in ``doctrine/orm`` 3.0)
|
||||
- ``Doctrine\ORM\Mapping\Driver\YamlDriver`` (deprecated and will be
|
||||
removed in ``doctrine/orm`` 3.0)
|
||||
|
||||
Throughout the most part of this manual the AttributeDriver is
|
||||
used in the examples. For information on the usage of the
|
||||
AnnotationDriver, XmlDriver or YamlDriver please refer to the dedicated
|
||||
chapters ``Annotation Reference``, ``XML Mapping`` and ``YAML Mapping``.
|
||||
XmlDriver please refer to the dedicated chapter ``XML Mapping``.
|
||||
|
||||
The attribute driver can be injected in the ``Doctrine\ORM\Configuration``:
|
||||
|
||||
@@ -155,9 +150,9 @@ Metadata Cache (***RECOMMENDED***)
|
||||
|
||||
Gets or sets the cache adapter to use for caching metadata
|
||||
information, that is, all the information you supply via attributes,
|
||||
annotations, xml or yaml, so that they do not need to be parsed and
|
||||
loaded from scratch on every single request which is a waste of
|
||||
resources. The cache implementation must implement the PSR-6
|
||||
xml, so that they do not need to be parsed and loaded from scratch on
|
||||
every single request which is a waste of resources. The cache
|
||||
implementation must implement the PSR-6
|
||||
``Psr\Cache\CacheItemPoolInterface`` interface.
|
||||
|
||||
Usage of a metadata cache is highly recommended.
|
||||
@@ -425,7 +420,7 @@ Multiple Metadata Sources
|
||||
|
||||
When using different components using Doctrine ORM you may end up
|
||||
with them using two different metadata drivers, for example XML and
|
||||
YAML. You can use the MappingDriverChain Metadata implementations to
|
||||
PHP. You can use the MappingDriverChain Metadata implementations to
|
||||
aggregate these drivers based on namespaces:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -435,7 +430,7 @@ aggregate these drivers based on namespaces:
|
||||
|
||||
$chain = new MappingDriverChain();
|
||||
$chain->addDriver($xmlDriver, 'Doctrine\Tests\Models\Company');
|
||||
$chain->addDriver($yamlDriver, 'Doctrine\Tests\ORM\Mapping');
|
||||
$chain->addDriver($phpDriver, 'Doctrine\Tests\ORM\Mapping');
|
||||
|
||||
Based on the namespace of the entity the loading of entities is
|
||||
delegated to the appropriate driver. The chain semantics come from
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ well.
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Doctrine ORM requires a minimum of PHP 7.1. For greatly improved
|
||||
Doctrine ORM requires a minimum of PHP 8.1. For greatly improved
|
||||
performance it is also recommended that you use APC with PHP.
|
||||
|
||||
Doctrine ORM Packages
|
||||
|
||||
@@ -56,27 +56,6 @@ A many-to-one association is the most common association between objects. Exampl
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Address")
|
||||
* @JoinColumn(name="address_id", referencedColumnName="id")
|
||||
*/
|
||||
private Address|null $address = null;
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class Address
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -87,18 +66,6 @@ A many-to-one association is the most common association between objects. Exampl
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
manyToOne:
|
||||
address:
|
||||
targetEntity: Address
|
||||
joinColumn:
|
||||
name: address_id
|
||||
referencedColumnName: id
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
The above ``#[JoinColumn]`` is optional as it would default
|
||||
@@ -154,30 +121,6 @@ references one ``Shipment`` entity.
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
class Product
|
||||
{
|
||||
// ...
|
||||
|
||||
/**
|
||||
* One Product has One Shipment.
|
||||
* @OneToOne(targetEntity="Shipment")
|
||||
* @JoinColumn(name="shipment_id", referencedColumnName="id")
|
||||
*/
|
||||
private Shipment|null $shipment = null;
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class Shipment
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -188,17 +131,6 @@ references one ``Shipment`` entity.
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Product:
|
||||
type: entity
|
||||
oneToOne:
|
||||
shipment:
|
||||
targetEntity: Shipment
|
||||
joinColumn:
|
||||
name: shipment_id
|
||||
referencedColumnName: id
|
||||
|
||||
Note that the ``#[JoinColumn]`` is not really necessary in this example,
|
||||
as the defaults would be the same.
|
||||
|
||||
@@ -259,38 +191,6 @@ object.
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
class Customer
|
||||
{
|
||||
// ...
|
||||
|
||||
/**
|
||||
* One Customer has One Cart.
|
||||
* @OneToOne(targetEntity="Cart", mappedBy="customer")
|
||||
*/
|
||||
private Cart|null $cart = null;
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class Cart
|
||||
{
|
||||
// ...
|
||||
|
||||
/**
|
||||
* One Cart has One Customer.
|
||||
* @OneToOne(targetEntity="Customer", inversedBy="cart")
|
||||
* @JoinColumn(name="customer_id", referencedColumnName="id")
|
||||
*/
|
||||
private Customer|null $customer = null;
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -304,22 +204,6 @@ object.
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Customer:
|
||||
oneToOne:
|
||||
cart:
|
||||
targetEntity: Cart
|
||||
mappedBy: customer
|
||||
Cart:
|
||||
oneToOne:
|
||||
customer:
|
||||
targetEntity: Customer
|
||||
inversedBy: cart
|
||||
joinColumn:
|
||||
name: customer_id
|
||||
referencedColumnName: id
|
||||
|
||||
Note that the @JoinColumn is not really necessary in this example,
|
||||
as the defaults would be the same.
|
||||
|
||||
@@ -428,41 +312,6 @@ bidirectional many-to-one.
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
/** @Entity */
|
||||
class Product
|
||||
{
|
||||
// ...
|
||||
/**
|
||||
* One product has many features. This is the inverse side.
|
||||
* @var Collection<int, Feature>
|
||||
* @OneToMany(targetEntity="Feature", mappedBy="product")
|
||||
*/
|
||||
private Collection $features;
|
||||
// ...
|
||||
|
||||
public function __construct() {
|
||||
$this->features = new ArrayCollection();
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class Feature
|
||||
{
|
||||
// ...
|
||||
/**
|
||||
* Many features have one product. This is the owning side.
|
||||
* @ManyToOne(targetEntity="Product", inversedBy="features")
|
||||
* @JoinColumn(name="product_id", referencedColumnName="id")
|
||||
*/
|
||||
private Product|null $product = null;
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -476,24 +325,6 @@ bidirectional many-to-one.
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Product:
|
||||
type: entity
|
||||
oneToMany:
|
||||
features:
|
||||
targetEntity: Feature
|
||||
mappedBy: product
|
||||
Feature:
|
||||
type: entity
|
||||
manyToOne:
|
||||
product:
|
||||
targetEntity: Product
|
||||
inversedBy: features
|
||||
joinColumn:
|
||||
name: product_id
|
||||
referencedColumnName: id
|
||||
|
||||
Note that the @JoinColumn is not really necessary in this example,
|
||||
as the defaults would be the same.
|
||||
|
||||
@@ -556,39 +387,6 @@ The following example sets up such a unidirectional one-to-many association:
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
|
||||
/**
|
||||
* Many Users have Many Phonenumbers.
|
||||
* @ManyToMany(targetEntity="Phonenumber")
|
||||
* @JoinTable(name="users_phonenumbers",
|
||||
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns={@JoinColumn(name="phonenumber_id", referencedColumnName="id", unique=true)}
|
||||
* )
|
||||
* @var Collection<int, Phonenumber>
|
||||
*/
|
||||
private Collection $phonenumbers;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->phonenumbers = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class Phonenumber
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -606,24 +404,6 @@ The following example sets up such a unidirectional one-to-many association:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
manyToMany:
|
||||
phonenumbers:
|
||||
targetEntity: Phonenumber
|
||||
joinTable:
|
||||
name: users_phonenumbers
|
||||
joinColumns:
|
||||
user_id:
|
||||
referencedColumnName: id
|
||||
inverseJoinColumns:
|
||||
phonenumber_id:
|
||||
referencedColumnName: id
|
||||
unique: true
|
||||
|
||||
|
||||
Generates the following MySQL Schema:
|
||||
|
||||
.. code-block:: sql
|
||||
@@ -684,33 +464,6 @@ database perspective is known as an adjacency list approach.
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
class Category
|
||||
{
|
||||
// ...
|
||||
/**
|
||||
* One Category has Many Categories.
|
||||
* @OneToMany(targetEntity="Category", mappedBy="parent")
|
||||
* @var Collection<int, Category>
|
||||
*/
|
||||
private Collection $children;
|
||||
|
||||
/**
|
||||
* Many Categories have One Category.
|
||||
* @ManyToOne(targetEntity="Category", inversedBy="children")
|
||||
* @JoinColumn(name="parent_id", referencedColumnName="id")
|
||||
*/
|
||||
private Category|null $parent = null;
|
||||
// ...
|
||||
|
||||
public function __construct() {
|
||||
$this->children = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -720,19 +473,6 @@ database perspective is known as an adjacency list approach.
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Category:
|
||||
type: entity
|
||||
oneToMany:
|
||||
children:
|
||||
targetEntity: Category
|
||||
mappedBy: parent
|
||||
manyToOne:
|
||||
parent:
|
||||
targetEntity: Category
|
||||
inversedBy: children
|
||||
|
||||
Note that the @JoinColumn is not really necessary in this example,
|
||||
as the defaults would be the same.
|
||||
|
||||
@@ -787,38 +527,6 @@ entities:
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
|
||||
/**
|
||||
* Many Users have Many Groups.
|
||||
* @ManyToMany(targetEntity="Group")
|
||||
* @JoinTable(name="users_groups",
|
||||
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")}
|
||||
* )
|
||||
* @var Collection<int, Group>
|
||||
*/
|
||||
private Collection $groups;
|
||||
|
||||
// ...
|
||||
|
||||
public function __construct() {
|
||||
$this->groups = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class Group
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -836,22 +544,6 @@ entities:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
manyToMany:
|
||||
groups:
|
||||
targetEntity: Group
|
||||
joinTable:
|
||||
name: users_groups
|
||||
joinColumns:
|
||||
user_id:
|
||||
referencedColumnName: id
|
||||
inverseJoinColumns:
|
||||
group_id:
|
||||
referencedColumnName: id
|
||||
|
||||
Generated MySQL Schema:
|
||||
|
||||
.. code-block:: sql
|
||||
@@ -939,47 +631,6 @@ one is bidirectional.
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
|
||||
/**
|
||||
* Many Users have Many Groups.
|
||||
* @ManyToMany(targetEntity="Group", inversedBy="users")
|
||||
* @JoinTable(name="users_groups")
|
||||
* @var Collection<int, Group>
|
||||
*/
|
||||
private Collection $groups;
|
||||
|
||||
public function __construct() {
|
||||
$this->groups = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class Group
|
||||
{
|
||||
// ...
|
||||
/**
|
||||
* Many Groups have Many Users.
|
||||
* @ManyToMany(targetEntity="User", mappedBy="groups")
|
||||
* @var Collection<int, User>
|
||||
*/
|
||||
private Collection $users;
|
||||
|
||||
public function __construct() {
|
||||
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -1001,30 +652,6 @@ one is bidirectional.
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
manyToMany:
|
||||
groups:
|
||||
targetEntity: Group
|
||||
inversedBy: users
|
||||
joinTable:
|
||||
name: users_groups
|
||||
joinColumns:
|
||||
user_id:
|
||||
referencedColumnName: id
|
||||
inverseJoinColumns:
|
||||
group_id:
|
||||
referencedColumnName: id
|
||||
|
||||
Group:
|
||||
type: entity
|
||||
manyToMany:
|
||||
users:
|
||||
targetEntity: User
|
||||
mappedBy: groups
|
||||
|
||||
The MySQL schema is exactly the same as for the Many-To-Many
|
||||
uni-directional case above.
|
||||
|
||||
@@ -1172,12 +799,6 @@ As an example, consider this mapping:
|
||||
#[OneToOne(targetEntity: Shipment::class)]
|
||||
private Shipment|null $shipment = null;
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/** @OneToOne(targetEntity="Shipment") */
|
||||
private Shipment|null $shipment = null;
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -1186,14 +807,6 @@ As an example, consider this mapping:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Product:
|
||||
type: entity
|
||||
oneToOne:
|
||||
shipment:
|
||||
targetEntity: Shipment
|
||||
|
||||
This is essentially the same as the following, more verbose,
|
||||
mapping:
|
||||
|
||||
@@ -1207,16 +820,6 @@ mapping:
|
||||
#[JoinColumn(name: 'shipment_id', referencedColumnName: 'id')]
|
||||
private Shipment|null $shipment = null;
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/**
|
||||
* One Product has One Shipment.
|
||||
* @OneToOne(targetEntity="Shipment")
|
||||
* @JoinColumn(name="shipment_id", referencedColumnName="id")
|
||||
*/
|
||||
private Shipment|null $shipment = null;
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -1227,17 +830,6 @@ mapping:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Product:
|
||||
type: entity
|
||||
oneToOne:
|
||||
shipment:
|
||||
targetEntity: Shipment
|
||||
joinColumn:
|
||||
name: shipment_id
|
||||
referencedColumnName: id
|
||||
|
||||
The @JoinTable definition used for many-to-many mappings has
|
||||
similar defaults. As an example, consider this mapping:
|
||||
|
||||
@@ -1255,20 +847,6 @@ similar defaults. As an example, consider this mapping:
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
/**
|
||||
* @ManyToMany(targetEntity="Group")
|
||||
* @var Collection<int, Group>
|
||||
*/
|
||||
private Collection $groups;
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -1277,14 +855,6 @@ similar defaults. As an example, consider this mapping:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
manyToMany:
|
||||
groups:
|
||||
targetEntity: Group
|
||||
|
||||
This is essentially the same as the following, more verbose, mapping:
|
||||
|
||||
.. configuration-block::
|
||||
@@ -1300,32 +870,13 @@ This is essentially the same as the following, more verbose, mapping:
|
||||
* @var Collection<int, Group>
|
||||
*/
|
||||
#[JoinTable(name: 'User_Group')]
|
||||
#[JoinColumn(name: 'User_id', referencedColumnName: 'id')]
|
||||
#[InverseJoinColumn(name: 'Group_id', referencedColumnName: 'id')]
|
||||
#[JoinColumn(name: 'user_id', referencedColumnName: 'id')]
|
||||
#[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')]
|
||||
#[ManyToMany(targetEntity: Group::class)]
|
||||
private Collection $groups;
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
/**
|
||||
* Many Users have Many Groups.
|
||||
* @ManyToMany(targetEntity="Group")
|
||||
* @JoinTable(name="User_Group",
|
||||
* joinColumns={@JoinColumn(name="User_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns={@JoinColumn(name="Group_id", referencedColumnName="id")}
|
||||
* )
|
||||
* @var Collection<int, Group>
|
||||
*/
|
||||
private Collection $groups;
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -1333,32 +884,16 @@ This is essentially the same as the following, more verbose, mapping:
|
||||
<many-to-many field="groups" target-entity="Group">
|
||||
<join-table name="User_Group">
|
||||
<join-columns>
|
||||
<join-column id="User_id" referenced-column-name="id" />
|
||||
<join-column id="user_id" referenced-column-name="id" />
|
||||
</join-columns>
|
||||
<inverse-join-columns>
|
||||
<join-column id="Group_id" referenced-column-name="id" />
|
||||
<join-column id="group_id" referenced-column-name="id" />
|
||||
</inverse-join-columns>
|
||||
</join-table>
|
||||
</many-to-many>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
manyToMany:
|
||||
groups:
|
||||
targetEntity: Group
|
||||
joinTable:
|
||||
name: User_Group
|
||||
joinColumns:
|
||||
User_id:
|
||||
referencedColumnName: id
|
||||
inverseJoinColumns:
|
||||
Group_id:
|
||||
referencedColumnName: id
|
||||
|
||||
In that case, the name of the join table defaults to a combination
|
||||
of the simple, unqualified class names of the participating
|
||||
classes, separated by an underscore character. The names of the
|
||||
@@ -1379,12 +914,6 @@ attribute on ``JoinColumn`` will be inherited from PHP type. So that:
|
||||
#[OneToOne]
|
||||
private Shipment $shipment;
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/** @OneToOne */
|
||||
private Shipment $shipment;
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -1393,13 +922,6 @@ attribute on ``JoinColumn`` will be inherited from PHP type. So that:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Product:
|
||||
type: entity
|
||||
oneToOne:
|
||||
shipment: ~
|
||||
|
||||
Is essentially the same as following:
|
||||
|
||||
.. configuration-block::
|
||||
@@ -1432,18 +954,6 @@ Is essentially the same as following:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Product:
|
||||
type: entity
|
||||
oneToOne:
|
||||
shipment:
|
||||
targetEntity: Shipment
|
||||
joinColumn:
|
||||
name: shipment_id
|
||||
referencedColumnName: id
|
||||
nullable: false
|
||||
|
||||
If you accept these defaults, you can reduce the mapping code to a
|
||||
minimum.
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@ Attributes Reference
|
||||
PHP 8 adds native support for metadata with its "Attributes" feature.
|
||||
Doctrine ORM provides support for mapping metadata using PHP attributes as of version 2.9.
|
||||
|
||||
The attributes metadata support is closely modelled after the already existing
|
||||
annotation metadata supported since the first version 2.0.
|
||||
The attributes metadata support is closely modelled after the already
|
||||
existing and now removed annotation metadata supported since the first
|
||||
version 2.0.
|
||||
|
||||
Index
|
||||
-----
|
||||
@@ -14,7 +15,7 @@ Index
|
||||
- :ref:`#[AttributeOverride] <attrref_attributeoverride>`
|
||||
- :ref:`#[Column] <attrref_column>`
|
||||
- :ref:`#[Cache] <attrref_cache>`
|
||||
- :ref:`#[ChangeTrackingPolicy <attrref_changetrackingpolicy>`
|
||||
- :ref:`#[ChangeTrackingPolicy] <attrref_changetrackingpolicy>`
|
||||
- :ref:`#[CustomIdGenerator] <attrref_customidgenerator>`
|
||||
- :ref:`#[DiscriminatorColumn] <attrref_discriminatorcolumn>`
|
||||
- :ref:`#[DiscriminatorMap] <attrref_discriminatormap>`
|
||||
@@ -310,7 +311,6 @@ Example:
|
||||
Entity,
|
||||
ChangeTrackingPolicy("DEFERRED_IMPLICIT"),
|
||||
ChangeTrackingPolicy("DEFERRED_EXPLICIT"),
|
||||
ChangeTrackingPolicy("NOTIFY")
|
||||
]
|
||||
class User {}
|
||||
|
||||
@@ -485,9 +485,8 @@ used as default.
|
||||
Optional parameters:
|
||||
|
||||
- **strategy**: Set the name of the identifier generation strategy.
|
||||
Valid values are ``AUTO``, ``SEQUENCE``, ``IDENTITY``, ``UUID``
|
||||
(deprecated), ``CUSTOM`` and ``NONE``.
|
||||
If not specified, the default value is ``AUTO``.
|
||||
Valid values are ``AUTO``, ``SEQUENCE``, ``IDENTITY``, ``CUSTOM`` and
|
||||
``NONE``. If not specified, the default value is ``AUTO``.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -712,10 +711,6 @@ details of the database join table. If you do not specify
|
||||
``#[JoinTable]`` on these relations reasonable mapping defaults apply
|
||||
using the affected table and the column names.
|
||||
|
||||
A notable difference to the annotation metadata support, ``#[JoinColumn]``
|
||||
and ``#[InverseJoinColumn]`` can be specified at the property level and are not
|
||||
nested within the ``#[JoinTable]`` attribute.
|
||||
|
||||
Required attribute:
|
||||
|
||||
- **name**: Database name of the join-table
|
||||
@@ -931,7 +926,7 @@ Example:
|
||||
#[OneToMany(
|
||||
targetEntity: "Phonenumber",
|
||||
mappedBy: "user",
|
||||
cascade: ["persist", "remove", "merge"],
|
||||
cascade: ["persist", "remove"],
|
||||
orphanRemoval: true)
|
||||
]
|
||||
public $phonenumbers;
|
||||
|
||||
@@ -47,17 +47,14 @@ mapping metadata:
|
||||
- :doc:`Attributes <attributes-reference>`
|
||||
- :doc:`XML <xml-mapping>`
|
||||
- :doc:`PHP code <php-mapping>`
|
||||
- :doc:`Docblock Annotations <annotations-reference>` (deprecated and will be removed in ``doctrine/orm`` 3.0)
|
||||
- :doc:`YAML <yaml-mapping>` (deprecated and will be removed in ``doctrine/orm`` 3.0.)
|
||||
|
||||
This manual will usually show mapping metadata via attributes, though
|
||||
many examples also show the equivalent configuration in annotations,
|
||||
YAML and XML.
|
||||
many examples also show the equivalent configuration in XML.
|
||||
|
||||
.. note::
|
||||
|
||||
All metadata drivers perform equally. Once the metadata of a class has been
|
||||
read from the source (attributes, annotations, XML, etc.) it is stored in an instance
|
||||
read from the source (attributes, XML, etc.) it is stored in an instance
|
||||
of the ``Doctrine\ORM\Mapping\ClassMetadata`` class which are
|
||||
stored in the metadata cache. If you're not using a metadata cache (not
|
||||
recommended!) then the XML driver is the fastest.
|
||||
@@ -77,17 +74,6 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward:
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
|
||||
/** @Entity */
|
||||
class Message
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -96,12 +82,6 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Message:
|
||||
type: entity
|
||||
# ...
|
||||
|
||||
With no additional information, Doctrine expects the entity to be saved
|
||||
into a table with the same name as the class in our case ``Message``.
|
||||
You can change this by configuring information about the table:
|
||||
@@ -121,21 +101,6 @@ You can change this by configuring information about the table:
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\Table;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="message")
|
||||
*/
|
||||
class Message
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -144,13 +109,6 @@ You can change this by configuring information about the table:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Message:
|
||||
type: entity
|
||||
table: message
|
||||
# ...
|
||||
|
||||
Now the class ``Message`` will be saved and fetched from the table ``message``.
|
||||
|
||||
Property Mapping
|
||||
@@ -182,23 +140,6 @@ specified, ``string`` is used as the default.
|
||||
private $postedAt;
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
|
||||
/** @Entity */
|
||||
class Message
|
||||
{
|
||||
/** @Column(type="integer") */
|
||||
private $id;
|
||||
/** @Column(length=140) */
|
||||
private $text;
|
||||
/** @Column(type="datetime", name="posted_at") */
|
||||
private $postedAt;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -209,19 +150,6 @@ specified, ``string`` is used as the default.
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Message:
|
||||
type: entity
|
||||
fields:
|
||||
id:
|
||||
type: integer
|
||||
text:
|
||||
length: 140
|
||||
postedAt:
|
||||
type: datetime
|
||||
column: posted_at
|
||||
|
||||
When we don't explicitly specify a column name via the ``name`` option, Doctrine
|
||||
assumes the field name is also the column name. So in this example:
|
||||
|
||||
@@ -300,50 +228,12 @@ and a custom ``Doctrine\ORM\Mapping\TypedFieldMapper`` implementation.
|
||||
Doctrine Mapping Types
|
||||
----------------------
|
||||
|
||||
The ``type`` option used in the ``@Column`` accepts any of the existing
|
||||
Doctrine types or even your own custom types. A Doctrine type defines
|
||||
The ``type`` option used in the ``@Column`` accepts any of the
|
||||
`existing Doctrine DBAL types <https://docs.doctrine-project.org/projects/doctrine-dbal/en/stable/reference/types.html#reference>`_
|
||||
or :doc:`your own custom mapping types
|
||||
<../cookbook/custom-mapping-types>`. A Doctrine type defines
|
||||
the conversion between PHP and SQL types, independent from the database vendor
|
||||
you are using. All Mapping Types that ship with Doctrine are fully portable
|
||||
between the supported database systems.
|
||||
|
||||
As an example, the Doctrine Mapping Type ``string`` defines the
|
||||
mapping from a PHP string to a SQL VARCHAR (or VARCHAR2 etc.
|
||||
depending on the RDBMS brand). Here is a quick overview of the
|
||||
built-in mapping types:
|
||||
|
||||
- ``string``: Type that maps a SQL VARCHAR to a PHP string.
|
||||
- ``integer``: Type that maps a SQL INT to a PHP integer.
|
||||
- ``smallint``: Type that maps a database SMALLINT to a PHP
|
||||
integer.
|
||||
- ``bigint``: Type that maps a database BIGINT to a PHP string.
|
||||
- ``boolean``: Type that maps a SQL boolean or equivalent (TINYINT) to a PHP boolean.
|
||||
- ``decimal``: Type that maps a SQL DECIMAL to a PHP string.
|
||||
- ``date``: Type that maps a SQL DATETIME to a PHP DateTime
|
||||
object.
|
||||
- ``time``: Type that maps a SQL TIME to a PHP DateTime object.
|
||||
- ``datetime``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP
|
||||
DateTime object.
|
||||
- ``datetimetz``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP
|
||||
DateTime object with timezone.
|
||||
- ``text``: Type that maps a SQL CLOB to a PHP string.
|
||||
- ``object``: Type that maps a SQL CLOB to a PHP object using
|
||||
``serialize()`` and ``unserialize()``
|
||||
- ``array``: Type that maps a SQL CLOB to a PHP array using
|
||||
``serialize()`` and ``unserialize()``
|
||||
- ``simple_array``: Type that maps a SQL CLOB to a PHP array using
|
||||
``implode()`` and ``explode()``, with a comma as delimiter. *IMPORTANT*
|
||||
Only use this type if you are sure that your values cannot contain a ",".
|
||||
- ``json_array``: Type that maps a SQL CLOB to a PHP array using
|
||||
``json_encode()`` and ``json_decode()``
|
||||
- ``float``: Type that maps a SQL Float (Double Precision) to a
|
||||
PHP double. *IMPORTANT*: Works only with locale settings that use
|
||||
decimal points as separator.
|
||||
- ``guid``: Type that maps a database GUID/UUID to a PHP string. Defaults to
|
||||
varchar but uses a specific type if the platform supports it.
|
||||
- ``blob``: Type that maps a SQL BLOB to a PHP resource stream
|
||||
|
||||
A cookbook article shows how to define :doc:`your own custom mapping types
|
||||
<../cookbook/custom-mapping-types>`.
|
||||
you are using.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -384,20 +274,6 @@ the field that serves as the identifier with the ``#[Id]`` attribute.
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
class Message
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue
|
||||
*/
|
||||
private int|null $id = null;
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -409,24 +285,27 @@ the field that serves as the identifier with the ``#[Id]`` attribute.
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Message:
|
||||
type: entity
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
# fields here
|
||||
|
||||
In most cases using the automatic generator strategy (``#[GeneratedValue]``) is
|
||||
what you want, but for backwards-compatibility reasons it might not. It
|
||||
defaults to the identifier generation mechanism your current database
|
||||
vendor preferred at the time that strategy was introduced:
|
||||
``AUTO_INCREMENT`` with MySQL, sequences with PostgreSQL and Oracle and
|
||||
so on.
|
||||
If you are using `doctrine/dbal` 4, we now recommend using ``IDENTITY``
|
||||
for PostgreSQL, and ``AUTO`` resolves to it because of that.
|
||||
You can stick with ``SEQUENCE`` while still using the ``AUTO``
|
||||
strategy, by configuring what it defaults to.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
use Doctrine\ORM\Configuration;
|
||||
|
||||
$config = new Configuration();
|
||||
$config->setIdentityGenerationPreferences([
|
||||
PostgreSQLPlatform::class => ClassMetadata::GENERATOR_TYPE_SEQUENCE,
|
||||
]);
|
||||
|
||||
.. _identifier-generation-strategies:
|
||||
|
||||
@@ -455,14 +334,12 @@ Here is the list of possible generation strategies:
|
||||
generation. This strategy does currently not provide full
|
||||
portability. Sequences are supported by Oracle, PostgreSql and
|
||||
SQL Anywhere.
|
||||
- ``UUID`` (deprecated): Tells Doctrine to use the built-in Universally
|
||||
Unique Identifier generator. This strategy provides full portability.
|
||||
- ``NONE``: Tells Doctrine that the identifiers are assigned (and
|
||||
thus generated) by your code. The assignment must take place before
|
||||
a new entity is passed to ``EntityManager#persist``. NONE is the
|
||||
same as leaving off the ``#[GeneratedValue]`` entirely.
|
||||
- ``CUSTOM``: With this option, you can use the ``#[CustomIdGenerator]`` attribute.
|
||||
It will allow you to pass a :ref:`class of your own to generate the identifiers.<annref_customidgenerator>`
|
||||
It will allow you to pass a :ref:`class of your own to generate the identifiers. <attrref_customidgenerator>`
|
||||
|
||||
Sequence Generator
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
@@ -485,20 +362,6 @@ besides specifying the sequence's name:
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
class Message
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @GeneratedValue(strategy="SEQUENCE")
|
||||
* @SequenceGenerator(sequenceName="message_seq", initialValue=1, allocationSize=100)
|
||||
*/
|
||||
protected int|null $id = null;
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -510,20 +373,6 @@ besides specifying the sequence's name:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Message:
|
||||
type: entity
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: SEQUENCE
|
||||
sequenceGenerator:
|
||||
sequenceName: message_seq
|
||||
allocationSize: 100
|
||||
initialValue: 1
|
||||
|
||||
The initial value specifies at which value the sequence should
|
||||
start.
|
||||
|
||||
|
||||
@@ -18,14 +18,20 @@ especially what the strategies presented here provide help with.
|
||||
|
||||
.. note::
|
||||
|
||||
Having an SQL logger enabled when processing batches can have a serious impact on performance and resource usage.
|
||||
To avoid that you should remove the corresponding middleware.
|
||||
To remove all middlewares, you can use this line:
|
||||
Having an SQL logger enabled when processing batches can have a
|
||||
serious impact on performance and resource usage.
|
||||
To avoid that, you should use a PSR logger implementation that can be
|
||||
disabled at runtime.
|
||||
For example, with Monolog, you can use ``Logger::pushHandler()``
|
||||
to push a ``NullHandler`` to the logger instance, and then pop it
|
||||
when you need to enable logging again.
|
||||
|
||||
With DBAL 2, you can disable the SQL logger like below:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em->getConnection()->getConfiguration()->setMiddlewares([]); // DBAL 3
|
||||
$em->getConnection()->getConfiguration()->setSQLLogger(null); // DBAL 2
|
||||
$em->getConnection()->getConfiguration()->setSQLLogger(null);
|
||||
|
||||
Bulk Inserts
|
||||
------------
|
||||
@@ -188,6 +194,3 @@ problems using the following approach:
|
||||
Iterating results is not possible with queries that
|
||||
fetch-join a collection-valued association. The nature of such SQL
|
||||
result sets is not suitable for incremental hydration.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ should use events judiciously.
|
||||
Use cascades judiciously
|
||||
------------------------
|
||||
|
||||
Automatic cascades of the persist/remove/merge/etc. operations are
|
||||
Automatic cascades of the persist/remove/etc. operations are
|
||||
very handy but should be used wisely. Do NOT simply add all
|
||||
cascades to all associations. Think about which cascades actually
|
||||
do make sense for you for a particular association, given the
|
||||
|
||||
@@ -109,7 +109,7 @@ Metadata Cache
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Your class metadata can be parsed from a few different sources like
|
||||
YAML, XML, Attributes, Annotations etc. Instead of parsing this
|
||||
XML, Attributes, etc. Instead of parsing this
|
||||
information on each request we should cache it using one of the cache
|
||||
drivers.
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Change tracking is the process of determining what has changed in
|
||||
managed entities since the last time they were synchronized with
|
||||
the database.
|
||||
|
||||
Doctrine provides 3 different change tracking policies, each having
|
||||
Doctrine provides 2 different change tracking policies, each having
|
||||
its particular advantages and disadvantages. The change tracking
|
||||
policy can be defined on a per-class basis (or more precisely,
|
||||
per-hierarchy).
|
||||
@@ -56,122 +56,3 @@ This policy can be configured as follows:
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
Notify
|
||||
~~~~~~
|
||||
|
||||
.. note::
|
||||
|
||||
The notify change tracking policy is deprecated and will be removed in ORM 3.0.
|
||||
(\ `Details <https://github.com/doctrine/orm/issues/8383>`_)
|
||||
|
||||
This policy is based on the assumption that the entities notify
|
||||
interested listeners of changes to their properties. For that
|
||||
purpose, a class that wants to use this policy needs to implement
|
||||
the ``NotifyPropertyChanged`` interface from the Doctrine
|
||||
namespace. As a guideline, such an implementation can look as
|
||||
follows:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Persistence\NotifyPropertyChanged,
|
||||
Doctrine\Persistence\PropertyChangedListener;
|
||||
|
||||
#[Entity]
|
||||
#[ChangeTrackingPolicy('NOTIFY')]
|
||||
class MyEntity implements NotifyPropertyChanged
|
||||
{
|
||||
// ...
|
||||
|
||||
private array $_listeners = array();
|
||||
|
||||
public function addPropertyChangedListener(PropertyChangedListener $listener): void
|
||||
{
|
||||
$this->_listeners[] = $listener;
|
||||
}
|
||||
}
|
||||
|
||||
Then, in each property setter of this class or derived classes, you
|
||||
need to notify all the ``PropertyChangedListener`` instances. As an
|
||||
example we add a convenience method on ``MyEntity`` that shows this
|
||||
behaviour:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// ...
|
||||
|
||||
class MyEntity implements NotifyPropertyChanged
|
||||
{
|
||||
// ...
|
||||
|
||||
protected function _onPropertyChanged($propName, $oldValue, $newValue): void
|
||||
{
|
||||
if ($this->_listeners) {
|
||||
foreach ($this->_listeners as $listener) {
|
||||
$listener->propertyChanged($this, $propName, $oldValue, $newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setData($data): void
|
||||
{
|
||||
if ($data != $this->data) {
|
||||
$this->_onPropertyChanged('data', $this->data, $data);
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
You have to invoke ``_onPropertyChanged`` inside every method that
|
||||
changes the persistent state of ``MyEntity``.
|
||||
|
||||
The check whether the new value is different from the old one is
|
||||
not mandatory but recommended. That way you also have full control
|
||||
over when you consider a property changed.
|
||||
|
||||
If your entity contains an embeddable, you will need to notify
|
||||
separately for each property in the embeddable when it changes
|
||||
for example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// ...
|
||||
|
||||
class MyEntity implements NotifyPropertyChanged
|
||||
{
|
||||
public function setEmbeddable(MyValueObject $embeddable): void
|
||||
{
|
||||
if (!$embeddable->equals($this->embeddable)) {
|
||||
// notice the entityField.embeddableField notation for referencing the property
|
||||
$this->_onPropertyChanged('embeddable.prop1', $this->embeddable->getProp1(), $embeddable->getProp1());
|
||||
$this->_onPropertyChanged('embeddable.prop2', $this->embeddable->getProp2(), $embeddable->getProp2());
|
||||
$this->embeddable = $embeddable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
This would update all the fields of the embeddable, you may wish to
|
||||
implement a diff method on your embedded object which returns only
|
||||
the changed fields.
|
||||
|
||||
The negative point of this policy is obvious: You need implement an
|
||||
interface and write some plumbing code. But also note that we tried
|
||||
hard to keep this notification functionality abstract. Strictly
|
||||
speaking, it has nothing to do with the persistence layer and the
|
||||
Doctrine ORM or DBAL. You may find that property notification
|
||||
events come in handy in many other scenarios as well. As mentioned
|
||||
earlier, the ``Doctrine\Common`` namespace is not that evil and
|
||||
consists solely of very small classes and interfaces that have
|
||||
almost no external dependencies (none to the DBAL and none to the
|
||||
ORM) and that you can easily take with you should you want to swap
|
||||
out the persistence layer. This change tracking policy does not
|
||||
introduce a dependency on the Doctrine DBAL/ORM or the persistence
|
||||
layer.
|
||||
|
||||
The positive point and main advantage of this policy is its
|
||||
effectiveness. It has the best performance characteristics of the 3
|
||||
policies with larger units of work and a flush() operation is very
|
||||
cheap when nothing has changed.
|
||||
|
||||
@@ -60,11 +60,6 @@ access point to ORM functionality provided by Doctrine.
|
||||
$connection = DriverManager::getConnection($dbParams, $config);
|
||||
$entityManager = new EntityManager($connection, $config);
|
||||
|
||||
.. note::
|
||||
|
||||
The ``ORMSetup`` class has been introduced with ORM 2.12. It's predecessor ``Setup`` is deprecated and will
|
||||
be removed in version 3.0.
|
||||
|
||||
Or if you prefer XML:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -75,23 +70,6 @@ Or if you prefer XML:
|
||||
$connection = DriverManager::getConnection($dbParams, $config);
|
||||
$entityManager = new EntityManager($connection, $config);
|
||||
|
||||
Or if you prefer YAML:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$paths = ['/path/to/yml-mappings'];
|
||||
$config = ORMSetup::createYAMLMetadataConfiguration($paths, $isDevMode);
|
||||
$connection = DriverManager::getConnection($dbParams, $config);
|
||||
$entityManager = new EntityManager($connection, $config);
|
||||
|
||||
.. note::
|
||||
If you want to use yml mapping you should add yaml dependency to your `composer.json`:
|
||||
|
||||
::
|
||||
|
||||
"symfony/yaml": "*"
|
||||
|
||||
Inside the ``ORMSetup`` methods several assumptions are made:
|
||||
|
||||
- If ``$isDevMode`` is true caching is done in memory with the ``ArrayAdapter``. Proxy objects are recreated on every request.
|
||||
|
||||
@@ -524,8 +524,8 @@ when the DQL is switched to an arbitrary join.
|
||||
aggregation (GROUP BY)
|
||||
|
||||
|
||||
Partial Object Syntax
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
Partial Hydration Syntax
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
By default when you run a DQL query in Doctrine and select only a
|
||||
subset of the fields for a given entity, you do not receive objects
|
||||
@@ -533,7 +533,7 @@ back. Instead, you receive only arrays as a flat rectangular result
|
||||
set, similar to how you would if you were just using SQL directly
|
||||
and joining some data.
|
||||
|
||||
If you want to select partial objects you can use the ``partial``
|
||||
If you want to select partial objects or fields in array hydration you can use the ``partial``
|
||||
DQL keyword:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -542,12 +542,13 @@ DQL keyword:
|
||||
$query = $em->createQuery('SELECT partial u.{id, username} FROM CmsUser u');
|
||||
$users = $query->getResult(); // array of partially loaded CmsUser objects
|
||||
|
||||
You use the partial syntax when joining as well:
|
||||
You can use the partial syntax when joining as well:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT partial u.{id, username}, partial a.{id, name} FROM CmsUser u JOIN u.articles a');
|
||||
$usersArray = $query->getArrayResult(); // array of partially loaded CmsUser and CmsArticle fields
|
||||
$users = $query->getResult(); // array of partially loaded CmsUser objects
|
||||
|
||||
"NEW" Operator Syntax
|
||||
@@ -587,7 +588,91 @@ And then use the ``NEW`` DQL keyword :
|
||||
$query = $em->createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city, SUM(o.value)) FROM Customer c JOIN c.email e JOIN c.address a JOIN c.orders o GROUP BY c');
|
||||
$users = $query->getResult(); // array of CustomerDTO
|
||||
|
||||
Note that you can only pass scalar expressions to the constructor.
|
||||
You can also nest several DTO :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class CustomerDTO
|
||||
{
|
||||
public function __construct(string $name, string $email, AddressDTO $address, string|null $value = null)
|
||||
{
|
||||
// Bind values to the object properties.
|
||||
}
|
||||
}
|
||||
|
||||
class AddressDTO
|
||||
{
|
||||
public function __construct(string $street, string $city, string $zip)
|
||||
{
|
||||
// Bind values to the object properties.
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT NEW CustomerDTO(c.name, e.email, NEW AddressDTO(a.street, a.city, a.zip)) FROM Customer c JOIN c.email e JOIN c.address a');
|
||||
$users = $query->getResult(); // array of CustomerDTO
|
||||
|
||||
Note that you can only pass scalar expressions or other Data Transfer Objects to the constructor.
|
||||
|
||||
If you use your data transfer objects for multiple queries, and you would rather not have to
|
||||
specify arguments that precede the ones you are really interested in, you can use named arguments.
|
||||
|
||||
Consider the following DTO, which uses optional arguments:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
class CustomerDTO
|
||||
{
|
||||
public function __construct(
|
||||
public string|null $name = null,
|
||||
public string|null $email = null,
|
||||
public string|null $city = null,
|
||||
public mixed|null $value = null,
|
||||
public AddressDTO|null $address = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
You can specify arbitrary arguments in an arbitrary order by using the named argument syntax, and the ORM will try to match argument names with the selected column names.
|
||||
The syntax relies on the NAMED keyword, like so:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT NEW NAMED CustomerDTO(a.city, c.name) FROM Customer c JOIN c.address a');
|
||||
$users = $query->getResult(); // array of CustomerDTO
|
||||
|
||||
// CustomerDTO => {name : 'SMITH', email: null, city: 'London', value: null}
|
||||
|
||||
ORM will also give precedence to column aliases over column names :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT NEW NAMED CustomerDTO(c.name, CONCAT(a.city, ' ' , a.zip) AS value) FROM Customer c JOIN c.address a');
|
||||
$users = $query->getResult(); // array of CustomerDTO
|
||||
|
||||
// CustomerDTO => {name : 'DOE', email: null, city: null, value: 'New York 10011'}
|
||||
|
||||
To define a custom name for a DTO constructor argument, you can either alias the column with the ``AS`` keyword.
|
||||
|
||||
The ``NAMED`` keyword must precede all DTO you want to instantiate :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT NEW NAMED CustomerDTO(c.name, NEW NAMED AddressDTO(a.street, a.city, a.zip) AS address) FROM Customer c JOIN c.address a');
|
||||
$users = $query->getResult(); // array of CustomerDTO
|
||||
|
||||
// CustomerDTO => {name : 'DOE', email: null, city: null, value: 'New York 10011'}
|
||||
|
||||
If two arguments have the same name, a ``DuplicateFieldException`` is thrown.
|
||||
If a field cannot be matched with a property name, a ``NoMatchingPropertyException`` is thrown. This typically happens when using functions without aliasing them.
|
||||
|
||||
Using INDEX BY
|
||||
~~~~~~~~~~~~~~
|
||||
@@ -812,7 +897,7 @@ classes have to implement the base class :
|
||||
namespace MyProject\Query\AST;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
class MysqlFloor extends FunctionNode
|
||||
{
|
||||
@@ -827,12 +912,12 @@ classes have to implement the base class :
|
||||
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1003,7 +1088,7 @@ The Query class
|
||||
---------------
|
||||
|
||||
An instance of the ``Doctrine\ORM\Query`` class represents a DQL
|
||||
query. You create a Query instance be calling
|
||||
query. You create a Query instance by calling
|
||||
``EntityManager#createQuery($dql)``, passing the DQL query string.
|
||||
Alternatively you can create an empty ``Query`` instance and invoke
|
||||
``Query#setDQL($dql)`` afterwards. Here are some examples:
|
||||
@@ -1020,58 +1105,146 @@ Alternatively you can create an empty ``Query`` instance and invoke
|
||||
$q = $em->createQuery();
|
||||
$q->setDQL('select u from MyProject\Model\User u');
|
||||
|
||||
Query Result Formats
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
Query Result Formats (Hydration Modes)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The format in which the result of a DQL SELECT query is returned
|
||||
can be influenced by a so-called ``hydration mode``. A hydration
|
||||
mode specifies a particular way in which a SQL result set is
|
||||
transformed. Each hydration mode has its own dedicated method on
|
||||
the Query class. Here they are:
|
||||
The way in which the SQL result set of a DQL SELECT query is transformed
|
||||
to PHP is determined by the so-called "hydration mode".
|
||||
|
||||
``getResult()``
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- ``Query#getResult()``: Retrieves a collection of objects. The
|
||||
result is either a plain collection of objects (pure) or an array
|
||||
where the objects are nested in the result rows (mixed).
|
||||
- ``Query#getSingleResult()``: Retrieves a single object. If the
|
||||
result contains more than one object, an ``NonUniqueResultException``
|
||||
is thrown. If the result contains no objects, an ``NoResultException``
|
||||
is thrown. The pure/mixed distinction does not apply.
|
||||
- ``Query#getOneOrNullResult()``: Retrieve a single object. If the
|
||||
result contains more than one object, a ``NonUniqueResultException``
|
||||
is thrown. If no object is found null will be returned.
|
||||
- ``Query#getArrayResult()``: Retrieves an array graph (a nested
|
||||
array) that is largely interchangeable with the object graph
|
||||
generated by ``Query#getResult()`` for read-only purposes.
|
||||
Retrieves a collection of objects. The result is either a plain collection of objects (pure) or an array
|
||||
where the objects are nested in the result rows (mixed):
|
||||
|
||||
.. note::
|
||||
.. code-block:: php
|
||||
|
||||
An array graph can differ from the corresponding object
|
||||
graph in certain scenarios due to the difference of the identity
|
||||
semantics between arrays and objects.
|
||||
<?php
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
|
||||
$query = $em->createQuery('SELECT u FROM User u');
|
||||
$users = $query->getResult();
|
||||
// same as:
|
||||
$users = $query->getResult(AbstractQuery::HYDRATE_OBJECT);
|
||||
|
||||
- Objects fetched in a FROM clause are returned as a Set, that means every
|
||||
object is only ever included in the resulting array once. This is the case
|
||||
even when using JOIN or GROUP BY in ways that return the same row for an
|
||||
object multiple times. If the hydrator sees the same object multiple times,
|
||||
then it makes sure it is only returned once.
|
||||
|
||||
- ``Query#getScalarResult()``: Retrieves a flat/rectangular result
|
||||
set of scalar values that can contain duplicate data. The
|
||||
pure/mixed distinction does not apply.
|
||||
- ``Query#getSingleScalarResult()``: Retrieves a single scalar
|
||||
value from the result returned by the dbms. If the result contains
|
||||
more than a single scalar value, an exception is thrown. The
|
||||
pure/mixed distinction does not apply.
|
||||
- If an object is already in memory from a previous query of any kind, then
|
||||
then the previous object is used, even if the database may contain more
|
||||
recent data. This even happens if the previous object is still an unloaded proxy.
|
||||
|
||||
Instead of using these methods, you can alternatively use the
|
||||
general-purpose method
|
||||
``Query#execute(array $params = [], $hydrationMode = Query::HYDRATE_OBJECT)``.
|
||||
Using this method you can directly supply the hydration mode as the
|
||||
second parameter via one of the Query constants. In fact, the
|
||||
methods mentioned earlier are just convenient shortcuts for the
|
||||
execute method. For example, the method ``Query#getResult()``
|
||||
internally invokes execute, passing in ``Query::HYDRATE_OBJECT`` as
|
||||
the hydration mode.
|
||||
``getArrayResult()``
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The use of the methods mentioned earlier is generally preferred as
|
||||
it leads to more concise code.
|
||||
Retrieves an array graph (a nested array) for read-only purposes.
|
||||
|
||||
.. note::
|
||||
|
||||
An array graph can differ from the corresponding object
|
||||
graph in certain scenarios due to the difference of the identity
|
||||
semantics between arrays and objects.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$users = $query->getArrayResult();
|
||||
// same as:
|
||||
$users = $query->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||
|
||||
``getScalarResult()``
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Retrieves a flat/rectangular result set of scalar values that can contain duplicate data. The
|
||||
pure/mixed distinction does not apply.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$users = $query->getScalarResult();
|
||||
// same as:
|
||||
$users = $query->getResult(AbstractQuery::HYDRATE_SCALAR);
|
||||
|
||||
Fields from classes are prefixed by the DQL alias in the result.
|
||||
A query of the kind `SELECT u.name ...` returns a key `u_name` in the result rows.
|
||||
|
||||
``getSingleScalarResult()``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Retrieves a single scalar value from the result returned by the database. If the result contains
|
||||
more than a single scalar value, a ``NonUniqueResultException`` is thrown. The pure/mixed distinction does not apply.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT COUNT(u.id) FROM User u');
|
||||
$numUsers = $query->getSingleScalarResult();
|
||||
// same as:
|
||||
$numUsers = $query->getResult(AbstractQuery::HYDRATE_SINGLE_SCALAR);
|
||||
|
||||
``getSingleColumnResult()``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Retrieves an array from a one-dimensional array of scalar values:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT a.id FROM User u');
|
||||
$ids = $query->getSingleColumnResult();
|
||||
// same as:
|
||||
$ids = $query->getResult(AbstractQuery::HYDRATE_SCALAR_COLUMN);
|
||||
|
||||
``getSingleResult()``
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Retrieves a single object. If the result contains more than one object, a ``NonUniqueResultException``
|
||||
is thrown. If the result contains no objects, a ``NoResultException`` is thrown. The pure/mixed distinction does not apply.
|
||||
|
||||
``getOneOrNullResult()``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Retrieves a single object. If the result contains more than one object, a ``NonUniqueResultException``
|
||||
is thrown. If no object is found, ``null`` will be returned.
|
||||
|
||||
Custom Hydration Modes
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can easily add your own custom hydration modes by first
|
||||
creating a class which extends ``AbstractHydrator``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace MyProject\Hydrators;
|
||||
|
||||
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
|
||||
|
||||
class CustomHydrator extends AbstractHydrator
|
||||
{
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
return $this->_stmt->fetchAllAssociative();
|
||||
}
|
||||
}
|
||||
|
||||
Next you just need to add the class to the ORM configuration:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em->getConfiguration()->addCustomHydrationMode('CustomHydrator', 'MyProject\Hydrators\CustomHydrator');
|
||||
|
||||
Now the hydrator is ready to be used in your queries:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$results = $query->getResult('CustomHydrator');
|
||||
|
||||
Pure and Mixed Results
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -1175,165 +1348,6 @@ will return the rows iterating the different top-level entities.
|
||||
[2] => Object (User)
|
||||
[3] => Object (Group)
|
||||
|
||||
|
||||
Hydration Modes
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Each of the Hydration Modes makes assumptions about how the result
|
||||
is returned to user land. You should know about all the details to
|
||||
make best use of the different result formats:
|
||||
|
||||
The constants for the different hydration modes are:
|
||||
|
||||
|
||||
- ``Query::HYDRATE_OBJECT``
|
||||
- ``Query::HYDRATE_ARRAY``
|
||||
- ``Query::HYDRATE_SCALAR``
|
||||
- ``Query::HYDRATE_SINGLE_SCALAR``
|
||||
- ``Query::HYDRATE_SCALAR_COLUMN``
|
||||
|
||||
Object Hydration
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Object hydration hydrates the result set into the object graph:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$users = $query->getResult(Query::HYDRATE_OBJECT);
|
||||
|
||||
Sometimes the behavior in the object hydrator can be confusing, which is
|
||||
why we are listing as many of the assumptions here for reference:
|
||||
|
||||
- Objects fetched in a FROM clause are returned as a Set, that means every
|
||||
object is only ever included in the resulting array once. This is the case
|
||||
even when using JOIN or GROUP BY in ways that return the same row for an
|
||||
object multiple times. If the hydrator sees the same object multiple times,
|
||||
then it makes sure it is only returned once.
|
||||
|
||||
- If an object is already in memory from a previous query of any kind, then
|
||||
then the previous object is used, even if the database may contain more
|
||||
recent data. Data from the database is discarded. This even happens if the
|
||||
previous object is still an unloaded proxy.
|
||||
|
||||
This list might be incomplete.
|
||||
|
||||
Array Hydration
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
You can run the same query with array hydration and the result set
|
||||
is hydrated into an array that represents the object graph:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$users = $query->getResult(Query::HYDRATE_ARRAY);
|
||||
|
||||
You can use the ``getArrayResult()`` shortcut as well:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$users = $query->getArrayResult();
|
||||
|
||||
Scalar Hydration
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
If you want to return a flat rectangular result set instead of an
|
||||
object graph you can use scalar hydration:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$users = $query->getResult(Query::HYDRATE_SCALAR);
|
||||
echo $users[0]['u_id'];
|
||||
|
||||
The following assumptions are made about selected fields using
|
||||
Scalar Hydration:
|
||||
|
||||
|
||||
1. Fields from classes are prefixed by the DQL alias in the result.
|
||||
A query of the kind 'SELECT u.name ..' returns a key 'u_name' in
|
||||
the result rows.
|
||||
|
||||
Single Scalar Hydration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you have a query which returns just a single scalar value you can use
|
||||
single scalar hydration:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT COUNT(a.id) FROM CmsUser u LEFT JOIN u.articles a WHERE u.username = ?1 GROUP BY u.id');
|
||||
$query->setParameter(1, 'jwage');
|
||||
$numArticles = $query->getResult(Query::HYDRATE_SINGLE_SCALAR);
|
||||
|
||||
You can use the ``getSingleScalarResult()`` shortcut as well:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$numArticles = $query->getSingleScalarResult();
|
||||
|
||||
Scalar Column Hydration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you have a query which returns a one-dimensional array of scalar values
|
||||
you can use scalar column hydration:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT a.id FROM CmsUser u');
|
||||
$ids = $query->getResult(Query::HYDRATE_SCALAR_COLUMN);
|
||||
|
||||
You can use the ``getSingleColumnResult()`` shortcut as well:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$ids = $query->getSingleColumnResult();
|
||||
|
||||
Custom Hydration Modes
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can easily add your own custom hydration modes by first
|
||||
creating a class which extends ``AbstractHydrator``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace MyProject\Hydrators;
|
||||
|
||||
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
|
||||
|
||||
class CustomHydrator extends AbstractHydrator
|
||||
{
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
return $this->_stmt->fetchAllAssociative();
|
||||
}
|
||||
}
|
||||
|
||||
Next you just need to add the class to the ORM configuration:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em->getConfiguration()->addCustomHydrationMode('CustomHydrator', 'MyProject\Hydrators\CustomHydrator');
|
||||
|
||||
Now the hydrator is ready to be used in your queries:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$results = $query->getResult('CustomHydrator');
|
||||
|
||||
Iterating Large Result Sets
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1587,8 +1601,8 @@ Identifiers
|
||||
/* Alias Identification declaration (the "u" of "FROM User u") */
|
||||
AliasIdentificationVariable :: = identifier
|
||||
|
||||
/* identifier that must be a class name (the "User" of "FROM User u"), possibly as a fully qualified class name or namespace-aliased */
|
||||
AbstractSchemaName ::= fully_qualified_name | aliased_name | identifier
|
||||
/* identifier that must be a class name (the "User" of "FROM User u"), possibly as a fully qualified class name */
|
||||
AbstractSchemaName ::= fully_qualified_name | identifier
|
||||
|
||||
/* Alias ResultVariable declaration (the "total" of "COUNT(*) AS total") */
|
||||
AliasResultVariable = identifier
|
||||
@@ -1688,7 +1702,7 @@ Select Expressions
|
||||
PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet
|
||||
PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
|
||||
NewObjectExpression ::= "NEW" AbstractSchemaName "(" NewObjectArg {"," NewObjectArg}* ")"
|
||||
NewObjectArg ::= ScalarExpression | "(" Subselect ")"
|
||||
NewObjectArg ::= (ScalarExpression | "(" Subselect ")" | NewObjectExpression) ["AS" AliasResultVariable]
|
||||
|
||||
Conditional Expressions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -131,47 +131,47 @@ There are two ways to set up an event handler:
|
||||
* For *all events* you can create a Lifecycle Event Listener or Subscriber class and register
|
||||
it by calling ``$eventManager->addEventListener()`` or ``eventManager->addEventSubscriber()``,
|
||||
see
|
||||
:ref:`Listening and subscribing to Lifecycle Events<listening-and-subscribing-to-lifecycle-events>`
|
||||
:ref:`Listening and subscribing to Lifecycle Events <listening-and-subscribing-to-lifecycle-events>`
|
||||
* For *some events* (see table below), you can create a *Lifecycle Callback* method in the
|
||||
entity, see :ref:`Lifecycle Callbacks<lifecycle-callbacks>`.
|
||||
entity, see :ref:`Lifecycle Callbacks <lifecycle-callbacks>`.
|
||||
|
||||
.. _reference-events-lifecycle-events:
|
||||
|
||||
Events Overview
|
||||
---------------
|
||||
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| Event | Dispatched by | Lifecycle | Passed |
|
||||
| | | Callback | Argument |
|
||||
+=================================================================+=======================+===========+=====================================+
|
||||
| :ref:`preRemove<reference-events-pre-remove>` | ``$em->remove()`` | Yes | `PreRemoveEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postRemove<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `PostRemoveEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`prePersist<reference-events-pre-persist>` | ``$em->persist()`` | Yes | `PrePersistEventArgs`_ |
|
||||
| | on *initial* persist | | |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postPersist<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `PostPersistEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`preUpdate<reference-events-pre-update>` | ``$em->flush()`` | Yes | `PreUpdateEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postUpdate<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `PostUpdateEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postLoad<reference-events-post-load>` | Loading from database | Yes | `PostLoadEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`loadClassMetadata<reference-events-load-class-metadata>` | Loading of mapping | No | `LoadClassMetadataEventArgs`_ |
|
||||
| | metadata | | |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| ``onClassMetadataNotFound`` | ``MappingException`` | No | `OnClassMetadataNotFoundEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`preFlush<reference-events-pre-flush>` | ``$em->flush()`` | Yes | `PreFlushEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`onFlush<reference-events-on-flush>` | ``$em->flush()`` | No | `OnFlushEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postFlush<reference-events-post-flush>` | ``$em->flush()`` | No | `PostFlushEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`onClear<reference-events-on-clear>` | ``$em->clear()`` | No | `OnClearEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| Event | Dispatched by | Lifecycle | Passed |
|
||||
| | | Callback | Argument |
|
||||
+==================================================================+=======================+===========+=====================================+
|
||||
| :ref:`preRemove <reference-events-pre-remove>` | ``$em->remove()`` | Yes | `PreRemoveEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postRemove <reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `PostRemoveEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`prePersist <reference-events-pre-persist>` | ``$em->persist()`` | Yes | `PrePersistEventArgs`_ |
|
||||
| | on *initial* persist | | |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postPersist <reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `PostPersistEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`preUpdate <reference-events-pre-update>` | ``$em->flush()`` | Yes | `PreUpdateEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postUpdate <reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `PostUpdateEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postLoad <reference-events-post-load>` | Loading from database | Yes | `PostLoadEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`loadClassMetadata <reference-events-load-class-metadata>` | Loading of mapping | No | `LoadClassMetadataEventArgs`_ |
|
||||
| | metadata | | |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| ``onClassMetadataNotFound`` | ``MappingException`` | No | `OnClassMetadataNotFoundEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`preFlush <reference-events-pre-flush>` | ``$em->flush()`` | Yes | `PreFlushEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`onFlush <reference-events-on-flush>` | ``$em->flush()`` | No | `OnFlushEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postFlush <reference-events-post-flush>` | ``$em->flush()`` | No | `PostFlushEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`onClear <reference-events-on-clear>` | ``$em->clear()`` | No | `OnClearEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
|
||||
.. warning::
|
||||
|
||||
@@ -260,40 +260,6 @@ specific to a particular entity class's lifecycle.
|
||||
$this->value = 'changed from preUpdate callback!';
|
||||
}
|
||||
}
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Event\PrePersistEventArgs;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @HasLifecycleCallbacks
|
||||
*/
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
|
||||
/** @Column(type="string", length=255) */
|
||||
public $value;
|
||||
|
||||
/** @PrePersist */
|
||||
public function doStuffOnPrePersist(PrePersistEventArgs $eventArgs)
|
||||
{
|
||||
$this->createdAt = date('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
/** @PrePersist */
|
||||
public function doOtherStuffOnPrePersist()
|
||||
{
|
||||
$this->value = 'changed from prePersist callback!';
|
||||
}
|
||||
|
||||
/** @PreUpdate */
|
||||
public function doStuffOnPreUpdate(PreUpdateEventArgs $eventArgs)
|
||||
{
|
||||
$this->value = 'changed from preUpdate callback!';
|
||||
}
|
||||
}
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -311,17 +277,6 @@ specific to a particular entity class's lifecycle.
|
||||
</lifecycle-callbacks>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
fields:
|
||||
# ...
|
||||
value:
|
||||
type: string(255)
|
||||
lifecycleCallbacks:
|
||||
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ]
|
||||
preUpdate: [ doStuffOnPreUpdate ]
|
||||
|
||||
Lifecycle Callbacks Event Argument
|
||||
----------------------------------
|
||||
@@ -358,7 +313,7 @@ behaviors across different entity classes.
|
||||
|
||||
Note that they require much more detailed knowledge about the inner
|
||||
workings of the ``EntityManager`` and ``UnitOfWork`` classes. Please
|
||||
read the :ref:`Implementing Event Listeners<reference-events-implementing-listeners>` section
|
||||
read the :ref:`Implementing Event Listeners <reference-events-implementing-listeners>` section
|
||||
carefully if you are trying to write your own listener.
|
||||
|
||||
For event subscribers, there are no surprises. They declare the
|
||||
@@ -471,11 +426,11 @@ prePersist
|
||||
There are two ways for the ``prePersist`` event to be triggered:
|
||||
|
||||
- One is when you call ``EntityManager::persist()``. The
|
||||
event is also called for all :ref:`cascaded associations<transitive-persistence>`.
|
||||
event is also called for all :ref:`cascaded associations <transitive-persistence>`.
|
||||
- The other is inside the ``flush()`` method when changes to associations are computed and
|
||||
this association is marked as :ref:`cascade: persist<transitive-persistence>`. Any new entity found
|
||||
this association is marked as :ref:`cascade: persist <transitive-persistence>`. Any new entity found
|
||||
during this operation is also persisted and ``prePersist`` called
|
||||
on it. This is called :ref:`persistence by reachability<persistence-by-reachability>`.
|
||||
on it. This is called :ref:`persistence by reachability <persistence-by-reachability>`.
|
||||
|
||||
In both cases you get passed a ``PrePersistEventArgs`` instance
|
||||
which has access to the entity and the entity manager.
|
||||
@@ -499,7 +454,7 @@ preRemove
|
||||
|
||||
The ``preRemove`` event is called on every entity immediately when it is passed
|
||||
to the ``EntityManager::remove()`` method. It is cascaded for all
|
||||
associations that are marked as :ref:`cascade: remove<transitive-persistence>`
|
||||
associations that are marked as :ref:`cascade: remove <transitive-persistence>`
|
||||
|
||||
It is not called for a DQL ``DELETE`` statement.
|
||||
|
||||
@@ -547,7 +502,7 @@ entities and their associations have been computed. This means, the
|
||||
- Collections scheduled for removal
|
||||
|
||||
To make use of the ``onFlush`` event you have to be familiar with the
|
||||
internal :ref:`UnitOfWork<unit-of-work>` API, which grants you access to the previously
|
||||
internal :ref:`UnitOfWork <unit-of-work>` API, which grants you access to the previously
|
||||
mentioned sets. See this example:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -794,16 +749,6 @@ An entity listener is a lifecycle listener class used for an entity.
|
||||
{
|
||||
// ....
|
||||
}
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
namespace MyProject\Entity;
|
||||
|
||||
/** @Entity @EntityListeners({"UserListener"}) */
|
||||
class User
|
||||
{
|
||||
// ....
|
||||
}
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -814,13 +759,6 @@ An entity listener is a lifecycle listener class used for an entity.
|
||||
<!-- .... -->
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
.. code-block:: yaml
|
||||
|
||||
MyProject\Entity\User:
|
||||
type: entity
|
||||
entityListeners:
|
||||
UserListener:
|
||||
# ....
|
||||
|
||||
.. _reference-entity-listeners:
|
||||
|
||||
@@ -893,45 +831,6 @@ you need to map the listener method using the event type mapping:
|
||||
public function postLoadHandler(User $user, PostLoadEventArgs $event): void { // ... }
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Event\PostLoadEventArgs;
|
||||
use Doctrine\ORM\Event\PostPersistEventArgs;
|
||||
use Doctrine\ORM\Event\PostRemoveEventArgs;
|
||||
use Doctrine\ORM\Event\PostUpdateEventArgs;
|
||||
use Doctrine\ORM\Event\PreFlushEventArgs;
|
||||
use Doctrine\ORM\Event\PrePersistEventArgs;
|
||||
use Doctrine\ORM\Event\PreRemoveEventArgs;
|
||||
use Doctrine\ORM\Event\PreUpdateEventArgs;
|
||||
|
||||
class UserListener
|
||||
{
|
||||
/** @PrePersist */
|
||||
public function prePersistHandler(User $user, PrePersistEventArgs $event): void { // ... }
|
||||
|
||||
/** @PostPersist */
|
||||
public function postPersistHandler(User $user, PostPersistEventArgs $event): void { // ... }
|
||||
|
||||
/** @PreUpdate */
|
||||
public function preUpdateHandler(User $user, PreUpdateEventArgs $event): void { // ... }
|
||||
|
||||
/** @PostUpdate */
|
||||
public function postUpdateHandler(User $user, PostUpdateEventArgs $event): void { // ... }
|
||||
|
||||
/** @PostRemove */
|
||||
public function postRemoveHandler(User $user, PostRemoveEventArgs $event): void { // ... }
|
||||
|
||||
/** @PreRemove */
|
||||
public function preRemoveHandler(User $user, PreRemoveEventArgs $event): void { // ... }
|
||||
|
||||
/** @PreFlush */
|
||||
public function preFlushHandler(User $user, PreFlushEventArgs $event): void { // ... }
|
||||
|
||||
/** @PostLoad */
|
||||
public function postLoadHandler(User $user, PostLoadEventArgs $event): void { // ... }
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -954,24 +853,6 @@ you need to map the listener method using the event type mapping:
|
||||
<!-- .... -->
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
.. code-block:: yaml
|
||||
|
||||
MyProject\Entity\User:
|
||||
type: entity
|
||||
entityListeners:
|
||||
UserListener:
|
||||
preFlush: [preFlushHandler]
|
||||
postLoad: [postLoadHandler]
|
||||
|
||||
postPersist: [postPersistHandler]
|
||||
prePersist: [prePersistHandler]
|
||||
|
||||
postUpdate: [postUpdateHandler]
|
||||
preUpdate: [preUpdateHandler]
|
||||
|
||||
postRemove: [postRemoveHandler]
|
||||
preRemove: [preRemoveHandler]
|
||||
# ....
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -994,7 +875,8 @@ Specifying an entity listener instance :
|
||||
|
||||
// User.php
|
||||
|
||||
/** @Entity @EntityListeners({"UserListener"}) */
|
||||
#[Entity]
|
||||
#[EntityListeners(["UserListener"])
|
||||
class User
|
||||
{
|
||||
// ....
|
||||
@@ -1052,7 +934,7 @@ Load ClassMetadata Event
|
||||
|
||||
``loadClassMetadata`` - The ``loadClassMetadata`` event occurs after the
|
||||
mapping metadata for a class has been loaded from a mapping source
|
||||
(attributes/annotations/xml/yaml) in to a ``Doctrine\ORM\Mapping\ClassMetadata`` instance.
|
||||
(attributes/xml) in to a ``Doctrine\ORM\Mapping\ClassMetadata`` instance.
|
||||
You can hook in to this process and manipulate the instance.
|
||||
This event is not a lifecycle callback.
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ The many-to-many association is only supporting foreign keys in the table defini
|
||||
To work with many-to-many tables containing extra columns you have to use the
|
||||
foreign keys as primary keys feature of Doctrine ORM.
|
||||
|
||||
See :doc:`the tutorial on composite primary keys for more information<../tutorials/composite-primary-keys>`.
|
||||
See :doc:`the tutorial on composite primary keys for more information <../tutorials/composite-primary-keys>`.
|
||||
|
||||
|
||||
How can i paginate fetch-joined collections?
|
||||
|
||||
@@ -208,44 +208,6 @@ Example:
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
namespace MyProject\Model;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("SINGLE_TABLE")
|
||||
* @DiscriminatorColumn(name="discr", type="string")
|
||||
* @DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
|
||||
*/
|
||||
class Person
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Employee extends Person
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
MyProject\Model\Person:
|
||||
type: entity
|
||||
inheritanceType: SINGLE_TABLE
|
||||
discriminatorColumn:
|
||||
name: discr
|
||||
type: string
|
||||
discriminatorMap:
|
||||
person: Person
|
||||
employee: Employee
|
||||
|
||||
MyProject\Model\Employee:
|
||||
type: entity
|
||||
|
||||
In this example, the ``#[DiscriminatorMap]`` specifies that in the
|
||||
discriminator column, a value of "person" identifies a row as being of type
|
||||
@@ -380,7 +342,7 @@ It is not supported to use overrides in entity inheritance scenarios.
|
||||
.. note::
|
||||
|
||||
When using traits, make sure not to miss the warnings given in the
|
||||
:doc:`Limitations and Known Issues</reference/limitations-and-known-issues>` chapter.
|
||||
:doc:`Limitations and Known Issues </reference/limitations-and-known-issues>` chapter.
|
||||
|
||||
|
||||
Association Override
|
||||
@@ -439,58 +401,6 @@ Example:
|
||||
{
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
// user mapping
|
||||
namespace MyProject\Model;
|
||||
/**
|
||||
* @MappedSuperclass
|
||||
*/
|
||||
class User
|
||||
{
|
||||
// other fields mapping
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity="Group", inversedBy="users")
|
||||
* @JoinTable(name="users_groups",
|
||||
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")}
|
||||
* )
|
||||
* @var Collection<int, Group>
|
||||
*/
|
||||
protected Collection $groups;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Address")
|
||||
* @JoinColumn(name="address_id", referencedColumnName="id")
|
||||
*/
|
||||
protected Address|null $address = null;
|
||||
}
|
||||
|
||||
// admin mapping
|
||||
namespace MyProject\Model;
|
||||
/**
|
||||
* @Entity
|
||||
* @AssociationOverrides({
|
||||
* @AssociationOverride(name="groups",
|
||||
* joinTable=@JoinTable(
|
||||
* name="users_admingroups",
|
||||
* joinColumns=@JoinColumn(name="adminuser_id"),
|
||||
* inverseJoinColumns=@JoinColumn(name="admingroup_id")
|
||||
* )
|
||||
* ),
|
||||
* @AssociationOverride(name="address",
|
||||
* joinColumns=@JoinColumn(
|
||||
* name="adminaddress_id", referencedColumnName="id"
|
||||
* )
|
||||
* )
|
||||
* })
|
||||
*/
|
||||
class Admin extends User
|
||||
{
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<!-- user mapping -->
|
||||
@@ -500,7 +410,6 @@ Example:
|
||||
<many-to-many field="groups" target-entity="Group" inversed-by="users">
|
||||
<cascade>
|
||||
<cascade-persist/>
|
||||
<cascade-merge/>
|
||||
<cascade-detach/>
|
||||
</cascade>
|
||||
<join-table name="users_groups">
|
||||
@@ -537,51 +446,6 @@ Example:
|
||||
</association-overrides>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
.. code-block:: yaml
|
||||
|
||||
# user mapping
|
||||
MyProject\Model\User:
|
||||
type: mappedSuperclass
|
||||
# other fields mapping
|
||||
manyToOne:
|
||||
address:
|
||||
targetEntity: Address
|
||||
joinColumn:
|
||||
name: address_id
|
||||
referencedColumnName: id
|
||||
cascade: [ persist, merge ]
|
||||
manyToMany:
|
||||
groups:
|
||||
targetEntity: Group
|
||||
joinTable:
|
||||
name: users_groups
|
||||
joinColumns:
|
||||
user_id:
|
||||
referencedColumnName: id
|
||||
inverseJoinColumns:
|
||||
group_id:
|
||||
referencedColumnName: id
|
||||
cascade: [ persist, merge, detach ]
|
||||
|
||||
# admin mapping
|
||||
MyProject\Model\Admin:
|
||||
type: entity
|
||||
associationOverride:
|
||||
address:
|
||||
joinColumn:
|
||||
adminaddress_id:
|
||||
name: adminaddress_id
|
||||
referencedColumnName: id
|
||||
groups:
|
||||
joinTable:
|
||||
name: users_admingroups
|
||||
joinColumns:
|
||||
adminuser_id:
|
||||
referencedColumnName: id
|
||||
inverseJoinColumns:
|
||||
admingroup_id:
|
||||
referencedColumnName: id
|
||||
|
||||
|
||||
Things to note:
|
||||
|
||||
@@ -645,51 +509,6 @@ Could be used by an entity that extends a mapped superclass to override a field
|
||||
{
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
// user mapping
|
||||
namespace MyProject\Model;
|
||||
/**
|
||||
* @MappedSuperclass
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/** @Id @GeneratedValue @Column(type="integer", name="user_id", length=150) */
|
||||
protected int|null $id = null;
|
||||
|
||||
/** @Column(name="user_name", nullable=true, unique=false, length=250) */
|
||||
protected string $name;
|
||||
|
||||
// other fields mapping
|
||||
}
|
||||
|
||||
// guest mapping
|
||||
namespace MyProject\Model;
|
||||
/**
|
||||
* @Entity
|
||||
* @AttributeOverrides({
|
||||
* @AttributeOverride(name="id",
|
||||
* column=@Column(
|
||||
* name = "guest_id",
|
||||
* type = "integer",
|
||||
* length = 140
|
||||
* )
|
||||
* ),
|
||||
* @AttributeOverride(name="name",
|
||||
* column=@Column(
|
||||
* name = "guest_name",
|
||||
* nullable = false,
|
||||
* unique = true,
|
||||
* length = 240
|
||||
* )
|
||||
* )
|
||||
* })
|
||||
*/
|
||||
class Guest extends User
|
||||
{
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<!-- user mapping -->
|
||||
@@ -702,7 +521,6 @@ Could be used by an entity that extends a mapped superclass to override a field
|
||||
<many-to-one field="address" target-entity="Address">
|
||||
<cascade>
|
||||
<cascade-persist/>
|
||||
<cascade-merge/>
|
||||
</cascade>
|
||||
<join-column name="address_id" referenced-column-name="id"/>
|
||||
</many-to-one>
|
||||
@@ -723,42 +541,6 @@ Could be used by an entity that extends a mapped superclass to override a field
|
||||
</attribute-overrides>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
.. code-block:: yaml
|
||||
|
||||
# user mapping
|
||||
MyProject\Model\User:
|
||||
type: mappedSuperclass
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
column: user_id
|
||||
length: 150
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
name:
|
||||
type: string
|
||||
column: user_name
|
||||
length: 250
|
||||
nullable: true
|
||||
unique: false
|
||||
#other fields mapping
|
||||
|
||||
|
||||
# guest mapping
|
||||
MyProject\Model\Guest:
|
||||
type: entity
|
||||
attributeOverride:
|
||||
id:
|
||||
column: guest_id
|
||||
type: integer
|
||||
length: 140
|
||||
name:
|
||||
column: guest_name
|
||||
type: string
|
||||
length: 240
|
||||
nullable: false
|
||||
unique: true
|
||||
|
||||
Things to note:
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
:orphan:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
|
||||
@@ -65,15 +65,6 @@ Where the ``attribute_name`` column contains the key and
|
||||
The feature request for persistence of primitive value arrays
|
||||
`is described in the DDC-298 ticket <https://github.com/doctrine/orm/issues/3743>`_.
|
||||
|
||||
Cascade Merge with Bi-directional Associations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are two bugs now that concern the use of cascade merge in combination with bi-directional associations.
|
||||
Make sure to study the behavior of cascade merge if you are using it:
|
||||
|
||||
- `DDC-875 <https://github.com/doctrine/orm/issues/5398>`_ Merge can sometimes add the same entity twice into a collection
|
||||
- `DDC-763 <https://github.com/doctrine/orm/issues/5277>`_ Cascade merge on associated entities can insert too many rows through "Persistence by Reachability"
|
||||
|
||||
Custom Persisters
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -16,13 +16,6 @@ metadata:
|
||||
- **Attributes** (AttributeDriver)
|
||||
- **PHP Code in files or static functions** (PhpDriver)
|
||||
|
||||
There are also two deprecated ways to do this:
|
||||
|
||||
- **Class DocBlock Annotations** (AnnotationDriver)
|
||||
- **YAML files** (YamlDriver)
|
||||
|
||||
They will be removed in 3.0, make sure to avoid them.
|
||||
|
||||
Something important to note about the above drivers is they are all
|
||||
an intermediate step to the same end result. The mapping
|
||||
information is populated to ``Doctrine\ORM\Mapping\ClassMetadata``
|
||||
@@ -44,11 +37,7 @@ an entity.
|
||||
$em->getConfiguration()->setMetadataCacheImpl(new ApcuCache());
|
||||
|
||||
|
||||
If you want to use one of the included core metadata drivers you need to
|
||||
configure it. If you pick the annotation driver despite it being
|
||||
deprecated, you will additionally need to install
|
||||
``doctrine/annotations``. All the drivers are in the
|
||||
``Doctrine\ORM\Mapping\Driver`` namespace:
|
||||
All the drivers are in the ``Doctrine\ORM\Mapping\Driver`` namespace:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -81,8 +70,8 @@ implements the ``MappingDriver`` interface:
|
||||
/**
|
||||
* Loads the metadata for the specified class into the provided container.
|
||||
*
|
||||
* @psalm-param class-string<T> $className
|
||||
* @psalm-param ClassMetadata<T> $metadata
|
||||
* @param class-string<T> $className
|
||||
* @param ClassMetadata<T> $metadata
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
@@ -93,8 +82,7 @@ implements the ``MappingDriver`` interface:
|
||||
/**
|
||||
* Gets the names of all mapped classes known to this driver.
|
||||
*
|
||||
* @return array<int, string> The names of all mapped classes known to this driver.
|
||||
* @psalm-return list<class-string>
|
||||
* @return list<class-string> The names of all mapped classes known to this driver.
|
||||
*/
|
||||
public function getAllClassNames();
|
||||
|
||||
@@ -102,7 +90,7 @@ implements the ``MappingDriver`` interface:
|
||||
* Returns whether the class with the specified name should have its metadata loaded.
|
||||
* This is only the case if it is either mapped as an Entity or a MappedSuperclass.
|
||||
*
|
||||
* @psalm-param class-string $className
|
||||
* @param class-string $className
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
@@ -110,28 +110,28 @@ You need to create a class which implements ``Doctrine\ORM\Mapping\NamingStrateg
|
||||
<?php
|
||||
class MyAppNamingStrategy implements NamingStrategy
|
||||
{
|
||||
public function classToTableName($className)
|
||||
public function classToTableName(string $className): string
|
||||
{
|
||||
return 'MyApp_' . substr($className, strrpos($className, '\\') + 1);
|
||||
}
|
||||
public function propertyToColumnName($propertyName)
|
||||
public function propertyToColumnName(string $propertyName): string
|
||||
{
|
||||
return $propertyName;
|
||||
}
|
||||
public function referenceColumnName()
|
||||
public function referenceColumnName(): string
|
||||
{
|
||||
return 'id';
|
||||
}
|
||||
public function joinColumnName($propertyName, $className = null)
|
||||
public function joinColumnName(string $propertyName, ?string $className = null): string
|
||||
{
|
||||
return $propertyName . '_' . $this->referenceColumnName();
|
||||
}
|
||||
public function joinTableName($sourceEntity, $targetEntity, $propertyName = null)
|
||||
public function joinTableName(string $sourceEntity, string $targetEntity, string $propertyName): string
|
||||
{
|
||||
return strtolower($this->classToTableName($sourceEntity) . '_' .
|
||||
$this->classToTableName($targetEntity));
|
||||
}
|
||||
public function joinKeyColumnName($entityName, $referencedColumnName = null)
|
||||
public function joinKeyColumnName(string $entityName, ?string $referencedColumnName): string
|
||||
{
|
||||
return strtolower($this->classToTableName($entityName) . '_' .
|
||||
($referencedColumnName ?: $this->referenceColumnName()));
|
||||
|
||||
@@ -465,477 +465,3 @@ above would result in partial objects if any objects in the result
|
||||
are actually a subtype of User. When using DQL, Doctrine
|
||||
automatically includes the necessary joins for this mapping
|
||||
strategy but with native SQL it is your responsibility.
|
||||
|
||||
Named Native Query
|
||||
------------------
|
||||
|
||||
.. note::
|
||||
|
||||
Named Native Queries are deprecated as of version 2.9 and will be removed in ORM 3.0
|
||||
|
||||
You can also map a native query using a named native query mapping.
|
||||
|
||||
To achieve that, you must describe the SQL resultset structure
|
||||
using named native query (and sql resultset mappings if is a several resultset mappings).
|
||||
|
||||
Like named query, a named native query can be defined at class level or in a XML or YAML file.
|
||||
|
||||
|
||||
A resultSetMapping parameter is defined in @NamedNativeQuery,
|
||||
it represents the name of a defined @SqlResultSetMapping.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace MyProject\Model;
|
||||
/**
|
||||
* @NamedNativeQueries({
|
||||
* @NamedNativeQuery(
|
||||
* name = "fetchMultipleJoinsEntityResults",
|
||||
* resultSetMapping= "mappingMultipleJoinsEntityResults",
|
||||
* query = "SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM users u INNER JOIN addresses a ON u.id = a.user_id INNER JOIN phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username"
|
||||
* ),
|
||||
* })
|
||||
* @SqlResultSetMappings({
|
||||
* @SqlResultSetMapping(
|
||||
* name = "mappingMultipleJoinsEntityResults",
|
||||
* entities= {
|
||||
* @EntityResult(
|
||||
* entityClass = "__CLASS__",
|
||||
* fields = {
|
||||
* @FieldResult(name = "id", column="u_id"),
|
||||
* @FieldResult(name = "name", column="u_name"),
|
||||
* @FieldResult(name = "status", column="u_status"),
|
||||
* }
|
||||
* ),
|
||||
* @EntityResult(
|
||||
* entityClass = "Address",
|
||||
* fields = {
|
||||
* @FieldResult(name = "id", column="a_id"),
|
||||
* @FieldResult(name = "zip", column="a_zip"),
|
||||
* @FieldResult(name = "country", column="a_country"),
|
||||
* }
|
||||
* )
|
||||
* },
|
||||
* columns = {
|
||||
* @ColumnResult("numphones")
|
||||
* }
|
||||
* )
|
||||
*})
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
|
||||
/** @Column(type="string", length=50, nullable=true) */
|
||||
public $status;
|
||||
|
||||
/** @Column(type="string", length=255, unique=true) */
|
||||
public $username;
|
||||
|
||||
/** @Column(type="string", length=255) */
|
||||
public $name;
|
||||
|
||||
/** @OneToMany(targetEntity="Phonenumber") */
|
||||
public $phonenumbers;
|
||||
|
||||
/** @OneToOne(targetEntity="Address") */
|
||||
public $address;
|
||||
|
||||
// ....
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="MyProject\Model\User">
|
||||
<named-native-queries>
|
||||
<named-native-query name="fetchMultipleJoinsEntityResults" result-set-mapping="mappingMultipleJoinsEntityResults">
|
||||
<query>SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM users u INNER JOIN addresses a ON u.id = a.user_id INNER JOIN phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username</query>
|
||||
</named-native-query>
|
||||
</named-native-queries>
|
||||
<sql-result-set-mappings>
|
||||
<sql-result-set-mapping name="mappingMultipleJoinsEntityResults">
|
||||
<entity-result entity-class="__CLASS__">
|
||||
<field-result name="id" column="u_id"/>
|
||||
<field-result name="name" column="u_name"/>
|
||||
<field-result name="status" column="u_status"/>
|
||||
</entity-result>
|
||||
<entity-result entity-class="Address">
|
||||
<field-result name="id" column="a_id"/>
|
||||
<field-result name="zip" column="a_zip"/>
|
||||
<field-result name="country" column="a_country"/>
|
||||
</entity-result>
|
||||
<column-result name="numphones"/>
|
||||
</sql-result-set-mapping>
|
||||
</sql-result-set-mappings>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
.. code-block:: yaml
|
||||
|
||||
MyProject\Model\User:
|
||||
type: entity
|
||||
namedNativeQueries:
|
||||
fetchMultipleJoinsEntityResults:
|
||||
name: fetchMultipleJoinsEntityResults
|
||||
resultSetMapping: mappingMultipleJoinsEntityResults
|
||||
query: SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM users u INNER JOIN addresses a ON u.id = a.user_id INNER JOIN phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username
|
||||
sqlResultSetMappings:
|
||||
mappingMultipleJoinsEntityResults:
|
||||
name: mappingMultipleJoinsEntityResults
|
||||
columnResult:
|
||||
0:
|
||||
name: numphones
|
||||
entityResult:
|
||||
0:
|
||||
entityClass: __CLASS__
|
||||
fieldResult:
|
||||
0:
|
||||
name: id
|
||||
column: u_id
|
||||
1:
|
||||
name: name
|
||||
column: u_name
|
||||
2:
|
||||
name: status
|
||||
column: u_status
|
||||
1:
|
||||
entityClass: Address
|
||||
fieldResult:
|
||||
0:
|
||||
name: id
|
||||
column: a_id
|
||||
1:
|
||||
name: zip
|
||||
column: a_zip
|
||||
2:
|
||||
name: country
|
||||
column: a_country
|
||||
|
||||
|
||||
Things to note:
|
||||
- The resultset mapping declares the entities retrieved by this native query.
|
||||
- Each field of the entity is bound to a SQL alias (or column name).
|
||||
- All fields of the entity including the ones of subclasses
|
||||
and the foreign key columns of related entities have to be present in the SQL query.
|
||||
- Field definitions are optional provided that they map to the same
|
||||
column name as the one declared on the class property.
|
||||
- ``__CLASS__`` is an alias for the mapped class
|
||||
|
||||
|
||||
In the above example,
|
||||
the ``fetchJoinedAddress`` named query use the joinMapping result set mapping.
|
||||
This mapping returns 2 entities, User and Address, each property is declared and associated to a column name,
|
||||
actually the column name retrieved by the query.
|
||||
|
||||
Let's now see an implicit declaration of the property / column.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace MyProject\Model;
|
||||
/**
|
||||
* @NamedNativeQueries({
|
||||
* @NamedNativeQuery(
|
||||
* name = "findAll",
|
||||
* resultSetMapping = "mappingFindAll",
|
||||
* query = "SELECT * FROM addresses"
|
||||
* ),
|
||||
* })
|
||||
* @SqlResultSetMappings({
|
||||
* @SqlResultSetMapping(
|
||||
* name = "mappingFindAll",
|
||||
* entities= {
|
||||
* @EntityResult(
|
||||
* entityClass = "Address"
|
||||
* )
|
||||
* }
|
||||
* )
|
||||
* })
|
||||
*/
|
||||
class Address
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
|
||||
/** @Column() */
|
||||
public $country;
|
||||
|
||||
/** @Column() */
|
||||
public $zip;
|
||||
|
||||
/** @Column()*/
|
||||
public $city;
|
||||
|
||||
// ....
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="MyProject\Model\Address">
|
||||
<named-native-queries>
|
||||
<named-native-query name="findAll" result-set-mapping="mappingFindAll">
|
||||
<query>SELECT * FROM addresses</query>
|
||||
</named-native-query>
|
||||
</named-native-queries>
|
||||
<sql-result-set-mappings>
|
||||
<sql-result-set-mapping name="mappingFindAll">
|
||||
<entity-result entity-class="Address"/>
|
||||
</sql-result-set-mapping>
|
||||
</sql-result-set-mappings>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
.. code-block:: yaml
|
||||
|
||||
MyProject\Model\Address:
|
||||
type: entity
|
||||
namedNativeQueries:
|
||||
findAll:
|
||||
resultSetMapping: mappingFindAll
|
||||
query: SELECT * FROM addresses
|
||||
sqlResultSetMappings:
|
||||
mappingFindAll:
|
||||
name: mappingFindAll
|
||||
entityResult:
|
||||
address:
|
||||
entityClass: Address
|
||||
|
||||
|
||||
In this example, we only describe the entity member of the result set mapping.
|
||||
The property / column mappings is done using the entity mapping values.
|
||||
In this case the model property is bound to the model_txt column.
|
||||
If the association to a related entity involve a composite primary key,
|
||||
a @FieldResult element should be used for each foreign key column.
|
||||
The @FieldResult name is composed of the property name for the relationship,
|
||||
followed by a dot ("."), followed by the name or the field or property of the primary key.
|
||||
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace MyProject\Model;
|
||||
/**
|
||||
* @NamedNativeQueries({
|
||||
* @NamedNativeQuery(
|
||||
* name = "fetchJoinedAddress",
|
||||
* resultSetMapping= "mappingJoinedAddress",
|
||||
* query = "SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM users u INNER JOIN addresses a ON u.id = a.user_id WHERE u.username = ?"
|
||||
* ),
|
||||
* })
|
||||
* @SqlResultSetMappings({
|
||||
* @SqlResultSetMapping(
|
||||
* name = "mappingJoinedAddress",
|
||||
* entities= {
|
||||
* @EntityResult(
|
||||
* entityClass = "__CLASS__",
|
||||
* fields = {
|
||||
* @FieldResult(name = "id"),
|
||||
* @FieldResult(name = "name"),
|
||||
* @FieldResult(name = "status"),
|
||||
* @FieldResult(name = "address.id", column = "a_id"),
|
||||
* @FieldResult(name = "address.zip", column = "a_zip"),
|
||||
* @FieldResult(name = "address.city", column = "a_city"),
|
||||
* @FieldResult(name = "address.country", column = "a_country"),
|
||||
* }
|
||||
* )
|
||||
* }
|
||||
* )
|
||||
* })
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
|
||||
/** @Column(type="string", length=50, nullable=true) */
|
||||
public $status;
|
||||
|
||||
/** @Column(type="string", length=255, unique=true) */
|
||||
public $username;
|
||||
|
||||
/** @Column(type="string", length=255) */
|
||||
public $name;
|
||||
|
||||
/** @OneToOne(targetEntity="Address") */
|
||||
public $address;
|
||||
|
||||
// ....
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="MyProject\Model\User">
|
||||
<named-native-queries>
|
||||
<named-native-query name="fetchJoinedAddress" result-set-mapping="mappingJoinedAddress">
|
||||
<query>SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM users u INNER JOIN addresses a ON u.id = a.user_id WHERE u.username = ?</query>
|
||||
</named-native-query>
|
||||
</named-native-queries>
|
||||
<sql-result-set-mappings>
|
||||
<sql-result-set-mapping name="mappingJoinedAddress">
|
||||
<entity-result entity-class="__CLASS__">
|
||||
<field-result name="id"/>
|
||||
<field-result name="name"/>
|
||||
<field-result name="status"/>
|
||||
<field-result name="address.id" column="a_id"/>
|
||||
<field-result name="address.zip" column="a_zip"/>
|
||||
<field-result name="address.city" column="a_city"/>
|
||||
<field-result name="address.country" column="a_country"/>
|
||||
</entity-result>
|
||||
</sql-result-set-mapping>
|
||||
</sql-result-set-mappings>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
.. code-block:: yaml
|
||||
|
||||
MyProject\Model\User:
|
||||
type: entity
|
||||
namedNativeQueries:
|
||||
fetchJoinedAddress:
|
||||
name: fetchJoinedAddress
|
||||
resultSetMapping: mappingJoinedAddress
|
||||
query: SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM users u INNER JOIN addresses a ON u.id = a.user_id WHERE u.username = ?
|
||||
sqlResultSetMappings:
|
||||
mappingJoinedAddress:
|
||||
entityResult:
|
||||
0:
|
||||
entityClass: __CLASS__
|
||||
fieldResult:
|
||||
0:
|
||||
name: id
|
||||
1:
|
||||
name: name
|
||||
2:
|
||||
name: status
|
||||
3:
|
||||
name: address.id
|
||||
column: a_id
|
||||
4:
|
||||
name: address.zip
|
||||
column: a_zip
|
||||
5:
|
||||
name: address.city
|
||||
column: a_city
|
||||
6:
|
||||
name: address.country
|
||||
column: a_country
|
||||
|
||||
|
||||
|
||||
If you retrieve a single entity and if you use the default mapping,
|
||||
you can use the resultClass attribute instead of resultSetMapping:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace MyProject\Model;
|
||||
/**
|
||||
* @NamedNativeQueries({
|
||||
* @NamedNativeQuery(
|
||||
* name = "find-by-id",
|
||||
* resultClass = "Address",
|
||||
* query = "SELECT * FROM addresses"
|
||||
* ),
|
||||
* })
|
||||
*/
|
||||
class Address
|
||||
{
|
||||
// ....
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="MyProject\Model\Address">
|
||||
<named-native-queries>
|
||||
<named-native-query name="find-by-id" result-class="Address">
|
||||
<query>SELECT * FROM addresses WHERE id = ?</query>
|
||||
</named-native-query>
|
||||
</named-native-queries>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
.. code-block:: yaml
|
||||
|
||||
MyProject\Model\Address:
|
||||
type: entity
|
||||
namedNativeQueries:
|
||||
findAll:
|
||||
name: findAll
|
||||
resultClass: Address
|
||||
query: SELECT * FROM addresses
|
||||
|
||||
|
||||
In some of your native queries, you'll have to return scalar values,
|
||||
for example when building report queries.
|
||||
You can map them in the @SqlResultsetMapping through @ColumnResult.
|
||||
You actually can even mix, entities and scalar returns in the same native query (this is probably not that common though).
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace MyProject\Model;
|
||||
/**
|
||||
* @NamedNativeQueries({
|
||||
* @NamedNativeQuery(
|
||||
* name = "count",
|
||||
* resultSetMapping= "mappingCount",
|
||||
* query = "SELECT COUNT(*) AS count FROM addresses"
|
||||
* )
|
||||
* })
|
||||
* @SqlResultSetMappings({
|
||||
* @SqlResultSetMapping(
|
||||
* name = "mappingCount",
|
||||
* columns = {
|
||||
* @ColumnResult(
|
||||
* name = "count"
|
||||
* )
|
||||
* }
|
||||
* )
|
||||
* })
|
||||
*/
|
||||
class Address
|
||||
{
|
||||
// ....
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="MyProject\Model\Address">
|
||||
<named-native-query name="count" result-set-mapping="mappingCount">
|
||||
<query>SELECT COUNT(*) AS count FROM addresses</query>
|
||||
</named-native-query>
|
||||
<sql-result-set-mappings>
|
||||
<sql-result-set-mapping name="mappingCount">
|
||||
<column-result name="count"/>
|
||||
</sql-result-set-mapping>
|
||||
</sql-result-set-mappings>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
.. code-block:: yaml
|
||||
|
||||
MyProject\Model\Address:
|
||||
type: entity
|
||||
namedNativeQueries:
|
||||
count:
|
||||
name: count
|
||||
resultSetMapping: mappingCount
|
||||
query: SELECT COUNT(*) AS count FROM addresses
|
||||
sqlResultSetMappings:
|
||||
mappingCount:
|
||||
name: mappingCount
|
||||
columnResult:
|
||||
count:
|
||||
name: count
|
||||
|
||||
15
docs/en/reference/partial-hydration.rst
Normal file
15
docs/en/reference/partial-hydration.rst
Normal file
@@ -0,0 +1,15 @@
|
||||
Partial Hydration
|
||||
=================
|
||||
|
||||
Partial hydration of entities is allowed in the array hydrator, when
|
||||
only a subset of the fields of an entity are loaded from the database
|
||||
and the nested results are still created based on the entity relationship structure.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$users = $em->createQuery("SELECT PARTIAL u.{id,name}, partial a.{id,street} FROM MyApp\Domain\User u JOIN u.addresses a")
|
||||
->getArrayResult();
|
||||
|
||||
This is a useful optimization when you are not interested in all fields of an entity
|
||||
for performance reasons, for example in use-cases for exporting or rendering lots of data.
|
||||
@@ -1,19 +1,11 @@
|
||||
Partial Objects
|
||||
===============
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Creating Partial Objects through DQL is deprecated and
|
||||
will be removed in the future, use data transfer object
|
||||
support in DQL instead. (\ `Details
|
||||
<https://github.com/doctrine/orm/issues/8471>`_)
|
||||
|
||||
A partial object is an object whose state is not fully initialized
|
||||
after being reconstituted from the database and that is
|
||||
disconnected from the rest of its data. The following section will
|
||||
describe why partial objects are problematic and what the approach
|
||||
of Doctrine2 to this problem is.
|
||||
of Doctrine to this problem is.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -94,5 +86,3 @@ When should I force partial objects?
|
||||
Mainly for optimization purposes, but be careful of premature
|
||||
optimization as partial objects lead to potentially more fragile
|
||||
code.
|
||||
|
||||
|
||||
|
||||
@@ -1,97 +1,20 @@
|
||||
PHP Mapping
|
||||
===========
|
||||
|
||||
Doctrine ORM also allows you to provide the ORM metadata in the form
|
||||
of plain PHP code using the ``ClassMetadata`` API. You can write
|
||||
the code in PHP files or inside of a static function named
|
||||
``loadMetadata($class)`` on the entity class itself.
|
||||
|
||||
PHP Files
|
||||
---------
|
||||
|
||||
.. note::
|
||||
|
||||
PHPDriver is deprecated and will be removed in 3.0, use StaticPHPDriver
|
||||
instead.
|
||||
|
||||
If you wish to write your mapping information inside PHP files that
|
||||
are named after the entity and included to populate the metadata
|
||||
for an entity you can do so by using the ``PHPDriver``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$driver = new PHPDriver('/path/to/php/mapping/files');
|
||||
$em->getConfiguration()->setMetadataDriverImpl($driver);
|
||||
|
||||
Now imagine we had an entity named ``Entities\User`` and we wanted
|
||||
to write a mapping file for it using the above configured
|
||||
``PHPDriver`` instance:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace Entities;
|
||||
|
||||
class User
|
||||
{
|
||||
private $id;
|
||||
private $username;
|
||||
}
|
||||
|
||||
To write the mapping information you just need to create a file
|
||||
named ``Entities.User.php`` inside of the
|
||||
``/path/to/php/mapping/files`` folder:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// /path/to/php/mapping/files/Entities.User.php
|
||||
|
||||
$metadata->mapField(array(
|
||||
'id' => true,
|
||||
'fieldName' => 'id',
|
||||
'type' => 'integer'
|
||||
));
|
||||
|
||||
$metadata->mapField(array(
|
||||
'fieldName' => 'username',
|
||||
'type' => 'string',
|
||||
'options' => array(
|
||||
'fixed' => true,
|
||||
'comment' => "User's login name"
|
||||
)
|
||||
));
|
||||
|
||||
$metadata->mapField(array(
|
||||
'fieldName' => 'login_count',
|
||||
'type' => 'integer',
|
||||
'nullable' => false,
|
||||
'options' => array(
|
||||
'unsigned' => true,
|
||||
'default' => 0
|
||||
)
|
||||
));
|
||||
|
||||
Now we can easily retrieve the populated ``ClassMetadata`` instance
|
||||
where the ``PHPDriver`` includes the file and the
|
||||
``ClassMetadataFactory`` caches it for later retrieval:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$class = $em->getClassMetadata('Entities\User');
|
||||
// or
|
||||
$class = $em->getMetadataFactory()->getMetadataFor('Entities\User');
|
||||
Doctrine ORM also allows you to provide the ORM metadata in the form of plain
|
||||
PHP code using the ``ClassMetadata`` API. You can write the code in inside of a
|
||||
static function named ``loadMetadata($class)`` on the entity class itself.
|
||||
|
||||
Static Function
|
||||
---------------
|
||||
|
||||
In addition to the PHP files you can also specify your mapping
|
||||
information inside of a static function defined on the entity class
|
||||
itself. This is useful for cases where you want to keep your entity
|
||||
and mapping information together but don't want to use attributes or
|
||||
annotations. For this you just need to use the ``StaticPHPDriver``:
|
||||
In addition to other drivers using configuration languages you can also
|
||||
programatically specify your mapping information inside of a static function
|
||||
defined on the entity class itself.
|
||||
|
||||
This is useful for cases where you want to keep your entity and mapping
|
||||
information together but don't want to use attributes. For this you just
|
||||
need to use the ``StaticPHPDriver``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -164,13 +87,11 @@ The API of the ClassMetadataBuilder has the following methods with a fluent inte
|
||||
- ``setTable($name)``
|
||||
- ``addIndex(array $columns, $indexName)``
|
||||
- ``addUniqueConstraint(array $columns, $constraintName)``
|
||||
- ``addNamedQuery($name, $dqlQuery)``
|
||||
- ``setJoinedTableInheritance()``
|
||||
- ``setSingleTableInheritance()``
|
||||
- ``setDiscriminatorColumn($name, $type = 'string', $length = 255, $columnDefinition = null, $enumType = null, $options = [])``
|
||||
- ``addDiscriminatorMapClass($name, $class)``
|
||||
- ``setChangeTrackingPolicyDeferredExplicit()``
|
||||
- ``setChangeTrackingPolicyNotify()``
|
||||
- ``addLifecycleEvent($methodName, $event)``
|
||||
- ``addManyToOne($name, $targetEntity, $inversedBy = null)``
|
||||
- ``addInverseOneToOne($name, $targetEntity, $mappedBy)``
|
||||
@@ -272,7 +193,6 @@ Inheritance Getters
|
||||
- ``isInheritanceTypeNone()``
|
||||
- ``isInheritanceTypeJoined()``
|
||||
- ``isInheritanceTypeSingleTable()``
|
||||
- ``isInheritanceTypeTablePerClass()``
|
||||
- ``isInheritedField($fieldName)``
|
||||
- ``isInheritedAssociation($fieldName)``
|
||||
|
||||
@@ -282,7 +202,6 @@ Change Tracking Getters
|
||||
|
||||
- ``isChangeTrackingDeferredExplicit()``
|
||||
- ``isChangeTrackingDeferredImplicit()``
|
||||
- ``isChangeTrackingNotify()``
|
||||
|
||||
Field & Association Getters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -611,3 +611,21 @@ same query of example 6 written using
|
||||
->add('from', new Expr\From('User', 'u'))
|
||||
->add('where', new Expr\Comparison('u.id', '=', '?1'))
|
||||
->add('orderBy', new Expr\OrderBy('u.name', 'ASC'));
|
||||
|
||||
Binding Parameters to Placeholders
|
||||
----------------------------------
|
||||
|
||||
It is often not necessary to know about the exact placeholder names when
|
||||
building a query. You can use a helper method to bind a value to a placeholder
|
||||
and directly use that placeholder in your query as a return value:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// $qb instanceof QueryBuilder
|
||||
|
||||
$qb->select('u')
|
||||
->from('User', 'u')
|
||||
->where('u.email = ' . $qb->createNamedParameter($userInputEmail))
|
||||
;
|
||||
// SELECT u FROM User u WHERE email = :dcValue1
|
||||
|
||||
@@ -295,30 +295,6 @@ level cache region.
|
||||
// other properties and methods
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Cache("NONSTRICT_READ_WRITE")
|
||||
*/
|
||||
class State
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
* @Column(type="integer")
|
||||
*/
|
||||
protected int|null $id = null;
|
||||
|
||||
/**
|
||||
* @Column(unique=true)
|
||||
*/
|
||||
protected string $name;
|
||||
|
||||
// other properties and methods
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
@@ -335,24 +311,6 @@ level cache region.
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Country:
|
||||
type: entity
|
||||
cache:
|
||||
usage: READ_ONLY
|
||||
region: my_entity_region
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
id: true
|
||||
generator:
|
||||
strategy: IDENTITY
|
||||
fields:
|
||||
name:
|
||||
type: string
|
||||
|
||||
|
||||
Association cache definition
|
||||
----------------------------
|
||||
The most common use case is to cache entities. But we can also cache relationships.
|
||||
@@ -389,44 +347,6 @@ It caches the primary keys of association and cache each element will be cached
|
||||
// other properties and methods
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Cache("NONSTRICT_READ_WRITE")
|
||||
*/
|
||||
class State
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
* @Column(type="integer")
|
||||
*/
|
||||
protected int|null $id = null;
|
||||
|
||||
/**
|
||||
* @Column(unique=true)
|
||||
*/
|
||||
protected string $name;
|
||||
|
||||
/**
|
||||
* @Cache("NONSTRICT_READ_WRITE")
|
||||
* @ManyToOne(targetEntity="Country")
|
||||
* @JoinColumn(name="country_id", referencedColumnName="id")
|
||||
*/
|
||||
protected Country|null $country;
|
||||
|
||||
/**
|
||||
* @Cache("NONSTRICT_READ_WRITE")
|
||||
* @OneToMany(targetEntity="City", mappedBy="state")
|
||||
* @var Collection<int, City>
|
||||
*/
|
||||
protected Collection $cities;
|
||||
|
||||
// other properties and methods
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
@@ -458,38 +378,6 @@ It caches the primary keys of association and cache each element will be cached
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
State:
|
||||
type: entity
|
||||
cache:
|
||||
usage: NONSTRICT_READ_WRITE
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
id: true
|
||||
generator:
|
||||
strategy: IDENTITY
|
||||
fields:
|
||||
name:
|
||||
type: string
|
||||
|
||||
manyToOne:
|
||||
state:
|
||||
targetEntity: Country
|
||||
joinColumns:
|
||||
country_id:
|
||||
referencedColumnName: id
|
||||
cache:
|
||||
usage: NONSTRICT_READ_WRITE
|
||||
|
||||
oneToMany:
|
||||
cities:
|
||||
targetEntity:City
|
||||
mappedBy: state
|
||||
cache:
|
||||
usage: NONSTRICT_READ_WRITE
|
||||
|
||||
.. note::
|
||||
|
||||
for this to work, the target entity must also be marked as cacheable.
|
||||
|
||||
@@ -83,18 +83,8 @@ The following Commands are currently available:
|
||||
cache drivers.
|
||||
- ``orm:clear-cache:result`` Clear result cache of the various
|
||||
cache drivers.
|
||||
- ``orm:convert-d1-schema`` Converts Doctrine 1.X schema into a
|
||||
Doctrine 2.X schema.
|
||||
- ``orm:convert-mapping`` Convert mapping information between
|
||||
supported formats.
|
||||
- ``orm:ensure-production-settings`` Verify that Doctrine is
|
||||
properly configured for a production environment.
|
||||
- ``orm:generate-entities`` Generate entity classes and method
|
||||
stubs from your mapping information.
|
||||
- ``orm:generate-proxies`` Generates proxy classes for entity
|
||||
classes.
|
||||
- ``orm:generate-repositories`` Generate repository classes from
|
||||
your mapping information.
|
||||
- ``orm:run-dql`` Executes arbitrary DQL directly from the command
|
||||
line.
|
||||
- ``orm:schema-tool:create`` Processes the schema and either
|
||||
@@ -107,14 +97,10 @@ The following Commands are currently available:
|
||||
update the database schema of EntityManager Storage Connection or
|
||||
generate the SQL output.
|
||||
|
||||
For these commands are also available aliases:
|
||||
The following alias is defined:
|
||||
|
||||
|
||||
- ``orm:convert:d1-schema`` is alias for ``orm:convert-d1-schema``.
|
||||
- ``orm:convert:mapping`` is alias for ``orm:convert-mapping``.
|
||||
- ``orm:generate:entities`` is alias for ``orm:generate-entities``.
|
||||
- ``orm:generate:proxies`` is alias for ``orm:generate-proxies``.
|
||||
- ``orm:generate:repositories`` is alias for ``orm:generate-repositories``.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -225,163 +211,6 @@ will output the SQL for the ran operation.
|
||||
|
||||
$ php bin/doctrine orm:schema-tool:create --dump-sql
|
||||
|
||||
Entity Generation
|
||||
-----------------
|
||||
|
||||
Generate entity classes and method stubs from your mapping information.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ php bin/doctrine orm:generate-entities
|
||||
$ php bin/doctrine orm:generate-entities --update-entities
|
||||
$ php bin/doctrine orm:generate-entities --regenerate-entities
|
||||
|
||||
This command is not suited for constant usage. It is a little helper and does
|
||||
not support all the mapping edge cases very well. You still have to put work
|
||||
in your entities after using this command.
|
||||
|
||||
It is possible to use the EntityGenerator on code that you have already written. It will
|
||||
not be lost. The EntityGenerator will only append new code to your
|
||||
file and will not delete the old code. However this approach may still be prone
|
||||
to error and we suggest you use code repositories such as GIT or SVN to make
|
||||
backups of your code.
|
||||
|
||||
It makes sense to generate the entity code if you are using entities as Data
|
||||
Access Objects only and don't put much additional logic on them. If you are
|
||||
however putting much more logic on the entities you should refrain from using
|
||||
the entity-generator and code your entities manually.
|
||||
|
||||
.. note::
|
||||
|
||||
Even if you specified Inheritance options in your
|
||||
XML or YAML Mapping files the generator cannot generate the base and
|
||||
child classes for you correctly, because it doesn't know which
|
||||
class is supposed to extend which. You have to adjust the entity
|
||||
code manually for inheritance to work!
|
||||
|
||||
|
||||
Convert Mapping Information
|
||||
---------------------------
|
||||
|
||||
Convert mapping information between supported formats.
|
||||
|
||||
This is an **execute one-time** command. It should not be necessary for
|
||||
you to call this method multiple times, especially when using the ``--from-database``
|
||||
flag.
|
||||
|
||||
Converting an existing database schema into mapping files only solves about 70-80%
|
||||
of the necessary mapping information. Additionally the detection from an existing
|
||||
database cannot detect inverse associations, inheritance types,
|
||||
entities with foreign keys as primary keys and many of the
|
||||
semantical operations on associations such as cascade.
|
||||
|
||||
.. note::
|
||||
|
||||
There is no need to convert YAML or XML mapping files to annotations
|
||||
every time you make changes. All mapping drivers are first class citizens
|
||||
in Doctrine 2 and can be used as runtime mapping for the ORM. See the
|
||||
docs on XML and YAML Mapping for an example how to register this metadata
|
||||
drivers as primary mapping source.
|
||||
|
||||
To convert some mapping information between the various supported
|
||||
formats you can use the ``ClassMetadataExporter`` to get exporter
|
||||
instances for the different formats:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$cme = new \Doctrine\ORM\Tools\Export\ClassMetadataExporter();
|
||||
|
||||
Once you have a instance you can use it to get an exporter. For
|
||||
example, the yml exporter:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$exporter = $cme->getExporter('yml', '/path/to/export/yml');
|
||||
|
||||
Now you can export some ``ClassMetadata`` instances:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$classes = array(
|
||||
$em->getClassMetadata('Entities\User'),
|
||||
$em->getClassMetadata('Entities\Profile')
|
||||
);
|
||||
$exporter->setMetadata($classes);
|
||||
$exporter->export();
|
||||
|
||||
This functionality is also available from the command line to
|
||||
convert your loaded mapping information to another format. The
|
||||
``orm:convert-mapping`` command accepts two arguments, the type to
|
||||
convert to and the path to generate it:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ php bin/doctrine orm:convert-mapping xml /path/to/mapping-path-converted-to-xml
|
||||
|
||||
Reverse Engineering
|
||||
-------------------
|
||||
|
||||
You can use the ``DatabaseDriver`` to reverse engineer a database to an
|
||||
array of ``ClassMetadata`` instances and generate YAML, XML, etc. from
|
||||
them.
|
||||
|
||||
.. note::
|
||||
|
||||
Reverse Engineering is a **one-time** process that can get you started with a project.
|
||||
Converting an existing database schema into mapping files only detects about 70-80%
|
||||
of the necessary mapping information. Additionally the detection from an existing
|
||||
database cannot detect inverse associations, inheritance types,
|
||||
entities with foreign keys as primary keys and many of the
|
||||
semantical operations on associations such as cascade.
|
||||
|
||||
First you need to retrieve the metadata instances with the
|
||||
``DatabaseDriver``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em->getConfiguration()->setMetadataDriverImpl(
|
||||
new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
|
||||
$em->getConnection()->getSchemaManager()
|
||||
)
|
||||
);
|
||||
|
||||
$cmf = new \Doctrine\ORM\Tools\DisconnectedClassMetadataFactory();
|
||||
$cmf->setEntityManager($em);
|
||||
$metadata = $cmf->getAllMetadata();
|
||||
|
||||
Now you can get an exporter instance and export the loaded metadata
|
||||
to yml:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$cme = new \Doctrine\ORM\Tools\Export\ClassMetadataExporter();
|
||||
$exporter = $cme->getExporter('yml', '/path/to/export/yml');
|
||||
$exporter->setMetadata($metadata);
|
||||
$exporter->export();
|
||||
|
||||
You can also reverse engineer a database using the
|
||||
``orm:convert-mapping`` command:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ php bin/doctrine orm:convert-mapping --from-database yml /path/to/mapping-path-converted-to-yml
|
||||
|
||||
.. note::
|
||||
|
||||
Reverse Engineering is not always working perfectly
|
||||
depending on special cases. It will only detect Many-To-One
|
||||
relations (even if they are One-To-One) and will try to create
|
||||
entities from Many-To-Many tables. It also has problems with naming
|
||||
of foreign keys that have multiple column names. Any Reverse
|
||||
Engineered Database-Schema needs considerable manual work to become
|
||||
a useful domain model.
|
||||
|
||||
|
||||
Runtime vs Development Mapping Validation
|
||||
-----------------------------------------
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ requirement.
|
||||
|
||||
A more convenient alternative for explicit transaction demarcation is the use
|
||||
of provided control abstractions in the form of
|
||||
``Connection#transactional($func)`` and ``EntityManager#transactional($func)``.
|
||||
``Connection#transactional($func)`` and ``EntityManager#wrapInTransaction($func)``.
|
||||
When used, these control abstractions ensure that you never forget to rollback
|
||||
the transaction, in addition to the obvious code reduction. An example that is
|
||||
functionally equivalent to the previously shown code looks as follows:
|
||||
@@ -96,21 +96,23 @@ functionally equivalent to the previously shown code looks as follows:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// transactional with Connection instance
|
||||
// $conn instanceof Connection
|
||||
$conn->transactional(function($conn) {
|
||||
// ... do some work
|
||||
$user = new User;
|
||||
$user->setName('George');
|
||||
});
|
||||
|
||||
// transactional with EntityManager instance
|
||||
// $em instanceof EntityManager
|
||||
$em->transactional(function($em) {
|
||||
$em->wrapInTransaction(function($em) {
|
||||
// ... do some work
|
||||
$user = new User;
|
||||
$user->setName('George');
|
||||
$em->persist($user);
|
||||
});
|
||||
|
||||
.. warning::
|
||||
|
||||
For historical reasons, ``EntityManager#transactional($func)`` will return
|
||||
``true`` whenever the return value of ``$func`` is loosely false.
|
||||
Some examples of this include ``array()``, ``"0"``, ``""``, ``0``, and
|
||||
``null``.
|
||||
|
||||
The difference between ``Connection#transactional($func)`` and
|
||||
``EntityManager#transactional($func)`` is that the latter
|
||||
abstraction flushes the ``EntityManager`` prior to transaction
|
||||
@@ -200,17 +202,6 @@ example we'll use an integer.
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
/** @Version @Column(type="integer") */
|
||||
private int $version;
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -219,15 +210,6 @@ example we'll use an integer.
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
fields:
|
||||
version:
|
||||
type: integer
|
||||
version: true
|
||||
|
||||
Alternatively a datetime type can be used (which maps to a SQL
|
||||
timestamp or datetime):
|
||||
|
||||
@@ -244,17 +226,6 @@ timestamp or datetime):
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
/** @Version @Column(type="datetime") */
|
||||
private DateTime $version;
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -263,15 +234,6 @@ timestamp or datetime):
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
fields:
|
||||
version:
|
||||
type: datetime
|
||||
version: true
|
||||
|
||||
Version numbers (not timestamps) should however be preferred as
|
||||
they can not potentially conflict in a highly concurrent
|
||||
environment, unlike timestamps where this is a possibility,
|
||||
|
||||
@@ -47,21 +47,6 @@ Then, an entity using the ``CustomIdObject`` typed field will be correctly assig
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="cms_users_typed_with_custom_typed_field")
|
||||
*/
|
||||
class UserTypedWithCustomTypedField
|
||||
{
|
||||
/** @Column */
|
||||
public CustomIdObject $customId;
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -176,4 +161,4 @@ You need to create a class which implements ``Doctrine\ORM\Mapping\TypedFieldMap
|
||||
|
||||
Note that this case checks whether the mapping is already assigned, and if yes, it skips it. This is up to your
|
||||
implementation. You can make a "greedy" mapper which will always override the mapping with its own type, or one
|
||||
that behaves like the ``DefaultTypedFieldMapper`` and does not modify the type once its set prior in the chain.
|
||||
that behaves like the ``DefaultTypedFieldMapper`` and does not modify the type once its set prior in the chain.
|
||||
|
||||
@@ -129,16 +129,10 @@ optimize the performance of the Flush Operation:
|
||||
- Temporarily mark entities as read only. If you have a very large UnitOfWork
|
||||
but know that a large set of entities has not changed, just mark them as read
|
||||
only with ``$entityManager->getUnitOfWork()->markReadOnly($entity)``.
|
||||
- Flush only a single entity with ``$entityManager->flush($entity)``.
|
||||
- Use :doc:`Change Tracking Policies <change-tracking-policies>` to use more
|
||||
explicit strategies of notifying the UnitOfWork what objects/properties
|
||||
changed.
|
||||
|
||||
.. note::
|
||||
|
||||
Flush only a single entity with ``$entityManager->flush($entity)`` is deprecated and will be removed in ORM 3.0.
|
||||
(\ `Details <https://github.com/doctrine/orm/issues/8459>`_)
|
||||
|
||||
Query Internals
|
||||
---------------
|
||||
|
||||
|
||||
@@ -415,7 +415,7 @@ Transitive persistence / Cascade Operations
|
||||
Doctrine ORM provides a mechanism for transitive persistence through cascading of certain operations.
|
||||
Each association to another entity or a collection of
|
||||
entities can be configured to automatically cascade the following operations to the associated entities:
|
||||
``persist``, ``remove``, ``merge``, ``detach``, ``refresh`` or ``all``.
|
||||
``persist``, ``remove``, ``detach``, ``refresh`` or ``all``.
|
||||
|
||||
The main use case for ``cascade: persist`` is to avoid "exposing" associated entities to your PHP application.
|
||||
Continuing with the User-Comment example of this chapter, this is how the creation of a new user and a new
|
||||
|
||||
@@ -338,10 +338,11 @@ Performance of different deletion strategies
|
||||
Deleting an object with all its associated objects can be achieved
|
||||
in multiple ways with very different performance impacts.
|
||||
|
||||
1. If an association is marked as ``CASCADE=REMOVE`` Doctrine ORM
|
||||
will fetch this association. If its a Single association it will
|
||||
pass this entity to
|
||||
``EntityManager#remove()``. If the association is a collection, Doctrine will loop over all its elements and pass them to``EntityManager#remove()``.
|
||||
1. If an association is marked as ``CASCADE=REMOVE`` Doctrine ORM will
|
||||
fetch this association. If it's a Single association it will pass
|
||||
this entity to ``EntityManager#remove()``. If the association is a
|
||||
collection, Doctrine will loop over all its elements and pass them to
|
||||
``EntityManager#remove()``.
|
||||
In both cases the cascade remove semantics are applied recursively.
|
||||
For large object graphs this removal strategy can be very costly.
|
||||
2. Using a DQL ``DELETE`` statement allows you to delete multiple
|
||||
@@ -413,77 +414,6 @@ automatically without invoking the ``detach`` method:
|
||||
The ``detach`` operation is usually not as frequently needed and
|
||||
used as ``persist`` and ``remove``.
|
||||
|
||||
Merging entities
|
||||
----------------
|
||||
|
||||
Merging entities refers to the merging of (usually detached)
|
||||
entities into the context of an EntityManager so that they become
|
||||
managed again. To merge the state of an entity into an
|
||||
EntityManager use the ``EntityManager#merge($entity)`` method. The
|
||||
state of the passed entity will be merged into a managed copy of
|
||||
this entity and this copy will subsequently be returned.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$detachedEntity = unserialize($serializedEntity); // some detached entity
|
||||
$entity = $em->merge($detachedEntity);
|
||||
// $entity now refers to the fully managed copy returned by the merge operation.
|
||||
// The EntityManager $em now manages the persistence of $entity as usual.
|
||||
|
||||
|
||||
The semantics of the merge operation, applied to an entity X, are
|
||||
as follows:
|
||||
|
||||
|
||||
- If X is a detached entity, the state of X is copied onto a
|
||||
pre-existing managed entity instance X' of the same identity.
|
||||
- If X is a new entity instance, a new managed copy X' will be
|
||||
created and the state of X is copied onto this managed instance.
|
||||
- If X is a removed entity instance, an InvalidArgumentException
|
||||
will be thrown.
|
||||
- If X is a managed entity, it is ignored by the merge operation,
|
||||
however, the merge operation is cascaded to entities referenced by
|
||||
relationships from X if these relationships have been mapped with
|
||||
the cascade element value MERGE or ALL (see ":ref:`transitive-persistence`").
|
||||
- For all entities Y referenced by relationships from X having the
|
||||
cascade element value MERGE or ALL, Y is merged recursively as Y'.
|
||||
For all such Y referenced by X, X' is set to reference Y'. (Note
|
||||
that if X is managed then X is the same object as X'.)
|
||||
- If X is an entity merged to X', with a reference to another
|
||||
entity Y, where cascade=MERGE or cascade=ALL is not specified, then
|
||||
navigation of the same association from X' yields a reference to a
|
||||
managed object Y' with the same persistent identity as Y.
|
||||
|
||||
The ``merge`` operation will throw an ``OptimisticLockException``
|
||||
if the entity being merged uses optimistic locking through a
|
||||
version field and the versions of the entity being merged and the
|
||||
managed copy don't match. This usually means that the entity has
|
||||
been modified while being detached.
|
||||
|
||||
The ``merge`` operation is usually not as frequently needed and
|
||||
used as ``persist`` and ``remove``. The most common scenario for
|
||||
the ``merge`` operation is to reattach entities to an EntityManager
|
||||
that come from some cache (and are therefore detached) and you want
|
||||
to modify and persist such an entity.
|
||||
|
||||
.. warning::
|
||||
|
||||
If you need to perform multiple merges of entities that share certain subparts
|
||||
of their object-graphs and cascade merge, then you have to call ``EntityManager#clear()`` between the
|
||||
successive calls to ``EntityManager#merge()``. Otherwise you might end up with
|
||||
multiple copies of the "same" object in the database, however with different ids.
|
||||
|
||||
.. note::
|
||||
|
||||
If you load some detached entities from a cache and you do
|
||||
not need to persist or delete them or otherwise make use of them
|
||||
without the need for persistence services there is no need to use
|
||||
``merge``. I.e. you can simply pass detached objects from a cache
|
||||
directly to the view.
|
||||
|
||||
|
||||
Synchronization with the Database
|
||||
---------------------------------
|
||||
@@ -594,7 +524,7 @@ during development.
|
||||
.. note::
|
||||
|
||||
Do not invoke ``flush`` after every change to an entity
|
||||
or every single invocation of persist/remove/merge/... This is an
|
||||
or every single invocation of persist/remove/... This is an
|
||||
anti-pattern and unnecessarily reduces the performance of your
|
||||
application. Instead, form units of work that operate on your
|
||||
objects and call ``flush`` when you are done. While serving a
|
||||
@@ -862,7 +792,7 @@ By default the EntityManager returns a default implementation of
|
||||
``Doctrine\ORM\EntityRepository`` when you call
|
||||
``EntityManager#getRepository($entityClass)``. You can overwrite
|
||||
this behaviour by specifying the class name of your own Entity
|
||||
Repository in the Attribute, Annotation, XML or YAML metadata. In large
|
||||
Repository in the Attribute or XML metadata. In large
|
||||
applications that require lots of specialized DQL queries using a
|
||||
custom repository is one recommended way of grouping these queries
|
||||
in a central location.
|
||||
|
||||
@@ -2,7 +2,8 @@ XML Mapping
|
||||
===========
|
||||
|
||||
The XML mapping driver enables you to provide the ORM metadata in
|
||||
form of XML documents.
|
||||
form of XML documents. It requires the ``dom`` extension in order to be
|
||||
able to validate your mapping documents against its XML Schema.
|
||||
|
||||
The XML driver is backed by an XML Schema document that describes
|
||||
the structure of a mapping document. The most recent version of the
|
||||
@@ -691,7 +692,6 @@ specified by their respective tags:
|
||||
|
||||
|
||||
- ``<cascade-persist />``
|
||||
- ``<cascade-merge />``
|
||||
- ``<cascade-remove />``
|
||||
- ``<cascade-refresh />``
|
||||
- ``<cascade-detach />``
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
YAML Mapping
|
||||
============
|
||||
|
||||
.. warning::
|
||||
The YAML driver is deprecated and will be removed in version 3.0.
|
||||
It is strongly recommended to switch to one of the other mappings.
|
||||
|
||||
The YAML mapping driver enables you to provide the ORM metadata in
|
||||
form of YAML documents.
|
||||
|
||||
The YAML mapping document of a class is loaded on-demand the first
|
||||
time it is requested and subsequently stored in the metadata cache.
|
||||
In order to work, this requires certain conventions:
|
||||
|
||||
|
||||
- Each entity/mapped superclass must get its own dedicated YAML
|
||||
mapping document.
|
||||
- The name of the mapping document must consist of the fully
|
||||
qualified name of the class, where namespace separators are
|
||||
replaced by dots (.).
|
||||
- All mapping documents should get the extension ".dcm.yml" to
|
||||
identify it as a Doctrine mapping file. This is more of a
|
||||
convention and you are not forced to do this. You can change the
|
||||
file extension easily enough.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$driver->setFileExtension('.yml');
|
||||
|
||||
It is recommended to put all YAML mapping documents in a single
|
||||
folder but you can spread the documents over several folders if you
|
||||
want to. In order to tell the YamlDriver where to look for your
|
||||
mapping documents, supply an array of paths as the first argument
|
||||
of the constructor, like this:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Driver\YamlDriver;
|
||||
|
||||
// $config instanceof Doctrine\ORM\Configuration
|
||||
$driver = new YamlDriver(array('/path/to/files'));
|
||||
$config->setMetadataDriverImpl($driver);
|
||||
|
||||
Simplified YAML Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The Symfony project sponsored a driver that simplifies usage of the YAML Driver.
|
||||
The changes between the original driver are:
|
||||
|
||||
- File Extension is .orm.yml
|
||||
- Filenames are shortened, "MyProject\\Entities\\User" will become User.orm.yml
|
||||
- You can add a global file and add multiple entities in this file.
|
||||
|
||||
Configuration of this client works a little bit different:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$namespaces = array(
|
||||
'/path/to/files1' => 'MyProject\Entities',
|
||||
'/path/to/files2' => 'OtherProject\Entities'
|
||||
);
|
||||
$driver = new \Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver($namespaces);
|
||||
$driver->setGlobalBasename('global'); // global.orm.yml
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
As a quick start, here is a small example document that makes use
|
||||
of several common elements:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# Doctrine.Tests.ORM.Mapping.User.dcm.yml
|
||||
Doctrine\Tests\ORM\Mapping\User:
|
||||
type: entity
|
||||
repositoryClass: Doctrine\Tests\ORM\Mapping\UserRepository
|
||||
table: cms_users
|
||||
schema: schema_name # The schema the table lies in, for platforms that support schemas (Optional, >= 2.5)
|
||||
readOnly: true
|
||||
indexes:
|
||||
name_index:
|
||||
columns: [ name ]
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
name:
|
||||
type: string
|
||||
length: 50
|
||||
email:
|
||||
type: string
|
||||
length: 32
|
||||
column: user_email
|
||||
unique: true
|
||||
options:
|
||||
fixed: true
|
||||
comment: User's email address
|
||||
loginCount:
|
||||
type: integer
|
||||
column: login_count
|
||||
nullable: false
|
||||
options:
|
||||
unsigned: true
|
||||
default: 0
|
||||
oneToOne:
|
||||
address:
|
||||
targetEntity: Address
|
||||
joinColumn:
|
||||
name: address_id
|
||||
referencedColumnName: id
|
||||
onDelete: CASCADE
|
||||
oneToMany:
|
||||
phonenumbers:
|
||||
targetEntity: Phonenumber
|
||||
mappedBy: user
|
||||
cascade: ["persist", "merge"]
|
||||
manyToMany:
|
||||
groups:
|
||||
targetEntity: Group
|
||||
joinTable:
|
||||
name: cms_users_groups
|
||||
joinColumns:
|
||||
user_id:
|
||||
referencedColumnName: id
|
||||
inverseJoinColumns:
|
||||
group_id:
|
||||
referencedColumnName: id
|
||||
lifecycleCallbacks:
|
||||
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ]
|
||||
postPersist: [ doStuffOnPostPersist ]
|
||||
|
||||
Be aware that class-names specified in the YAML files should be
|
||||
fully qualified.
|
||||
|
||||
Reference
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Unique Constraints
|
||||
------------------
|
||||
|
||||
It is possible to define unique constraints by the following declaration:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# ECommerceProduct.orm.yml
|
||||
ECommerceProduct:
|
||||
type: entity
|
||||
fields:
|
||||
# definition of some fields
|
||||
uniqueConstraints:
|
||||
search_idx:
|
||||
columns: [ name, email ]
|
||||
|
||||
@@ -1,83 +1,75 @@
|
||||
.. toc::
|
||||
:orphan:
|
||||
|
||||
.. tocheader:: Tutorials
|
||||
.. toctree::
|
||||
:caption: Tutorials
|
||||
:depth: 3
|
||||
|
||||
.. toctree::
|
||||
:depth: 3
|
||||
tutorials/getting-started
|
||||
tutorials/getting-started-database
|
||||
tutorials/getting-started-models
|
||||
tutorials/working-with-indexed-associations
|
||||
tutorials/extra-lazy-associations
|
||||
tutorials/composite-primary-keys
|
||||
tutorials/ordered-associations
|
||||
tutorials/override-field-association-mappings-in-subclasses
|
||||
tutorials/pagination
|
||||
tutorials/embeddables
|
||||
|
||||
tutorials/getting-started
|
||||
tutorials/getting-started-database
|
||||
tutorials/getting-started-models
|
||||
tutorials/working-with-indexed-associations
|
||||
tutorials/extra-lazy-associations
|
||||
tutorials/composite-primary-keys
|
||||
tutorials/ordered-associations
|
||||
tutorials/override-field-association-mappings-in-subclasses
|
||||
tutorials/pagination
|
||||
tutorials/embeddables
|
||||
.. toctree::
|
||||
:caption: Reference
|
||||
:depth: 3
|
||||
|
||||
.. toc::
|
||||
reference/architecture
|
||||
reference/configuration
|
||||
reference/faq
|
||||
reference/basic-mapping
|
||||
reference/association-mapping
|
||||
reference/inheritance-mapping
|
||||
reference/working-with-objects
|
||||
reference/working-with-associations
|
||||
reference/typedfieldmapper
|
||||
reference/events
|
||||
reference/unitofwork
|
||||
reference/unitofwork-associations
|
||||
reference/transactions-and-concurrency
|
||||
reference/batch-processing
|
||||
reference/dql-doctrine-query-language
|
||||
reference/query-builder
|
||||
reference/native-sql
|
||||
reference/change-tracking-policies
|
||||
reference/partial-hydration
|
||||
reference/partial-objects
|
||||
reference/attributes-reference
|
||||
reference/xml-mapping
|
||||
reference/php-mapping
|
||||
reference/caching
|
||||
reference/improving-performance
|
||||
reference/tools
|
||||
reference/metadata-drivers
|
||||
reference/best-practices
|
||||
reference/limitations-and-known-issues
|
||||
tutorials/pagination
|
||||
reference/filters
|
||||
reference/namingstrategy
|
||||
reference/advanced-configuration
|
||||
reference/second-level-cache
|
||||
reference/security
|
||||
|
||||
.. tocheader:: Reference
|
||||
.. toctree::
|
||||
:caption: Cookbook
|
||||
:depth: 3
|
||||
|
||||
.. toctree::
|
||||
:depth: 3
|
||||
|
||||
reference/architecture
|
||||
reference/configuration
|
||||
reference/faq
|
||||
reference/basic-mapping
|
||||
reference/association-mapping
|
||||
reference/inheritance-mapping
|
||||
reference/working-with-objects
|
||||
reference/working-with-associations
|
||||
reference/events
|
||||
reference/unitofwork
|
||||
reference/unitofwork-associations
|
||||
reference/transactions-and-concurrency
|
||||
reference/batch-processing
|
||||
reference/dql-doctrine-query-language
|
||||
reference/query-builder
|
||||
reference/native-sql
|
||||
reference/change-tracking-policies
|
||||
reference/partial-objects
|
||||
reference/annotations-reference
|
||||
reference/attributes-reference
|
||||
reference/xml-mapping
|
||||
reference/yaml-mapping
|
||||
reference/php-mapping
|
||||
reference/caching
|
||||
reference/improving-performance
|
||||
reference/tools
|
||||
reference/metadata-drivers
|
||||
reference/best-practices
|
||||
reference/limitations-and-known-issues
|
||||
tutorials/pagination
|
||||
reference/filters
|
||||
reference/namingstrategy
|
||||
reference/advanced-configuration
|
||||
reference/second-level-cache
|
||||
reference/security
|
||||
|
||||
.. toc::
|
||||
|
||||
.. tocheader:: Cookbook
|
||||
|
||||
.. toctree::
|
||||
:depth: 3
|
||||
|
||||
cookbook/aggregate-fields
|
||||
cookbook/custom-mapping-types
|
||||
cookbook/decorator-pattern
|
||||
cookbook/dql-custom-walkers
|
||||
cookbook/dql-user-defined-functions
|
||||
cookbook/implementing-arrayaccess-for-domain-objects
|
||||
cookbook/implementing-the-notify-changetracking-policy
|
||||
cookbook/resolve-target-entity-listener
|
||||
cookbook/sql-table-prefixes
|
||||
cookbook/strategy-cookbook-introduction
|
||||
cookbook/validation-of-entities
|
||||
cookbook/working-with-datetime
|
||||
cookbook/mysql-enums
|
||||
cookbook/advanced-field-value-conversion-using-custom-mapping-types
|
||||
cookbook/entities-in-session
|
||||
cookbook/aggregate-fields
|
||||
cookbook/custom-mapping-types
|
||||
cookbook/decorator-pattern
|
||||
cookbook/dql-custom-walkers
|
||||
cookbook/dql-user-defined-functions
|
||||
cookbook/implementing-arrayaccess-for-domain-objects
|
||||
cookbook/resolve-target-entity-listener
|
||||
cookbook/sql-table-prefixes
|
||||
cookbook/strategy-cookbook-introduction
|
||||
cookbook/validation-of-entities
|
||||
cookbook/working-with-datetime
|
||||
cookbook/mysql-enums
|
||||
cookbook/advanced-field-value-conversion-using-custom-mapping-types
|
||||
cookbook/entities-in-session
|
||||
|
||||
@@ -50,38 +50,6 @@ and year of production as primary keys:
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
namespace VehicleCatalogue\Model;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Car
|
||||
{
|
||||
/** @Id @Column(type="string") */
|
||||
private string $name;
|
||||
/** @Id @Column(type="integer") */
|
||||
private int $year;
|
||||
|
||||
public function __construct($name, $year)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->year = $year;
|
||||
}
|
||||
|
||||
public function getModelName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getYearOfProduction(): int
|
||||
{
|
||||
return $this->year;
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -96,16 +64,6 @@ and year of production as primary keys:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
VehicleCatalogue\Model\Car:
|
||||
type: entity
|
||||
id:
|
||||
name:
|
||||
type: string
|
||||
year:
|
||||
type: integer
|
||||
|
||||
Now you can use this entity:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -160,7 +118,6 @@ The semantics of mapping identity through foreign entities are easy:
|
||||
- Only allowed on Many-To-One or One-To-One associations.
|
||||
- Plug an ``#[Id]`` attribute onto every association.
|
||||
- Set an attribute ``association-key`` with the field name of the association in XML.
|
||||
- Set a key ``associationKey:`` with the field name of the association in YAML.
|
||||
|
||||
Use-Case 1: Dynamic Attributes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -188,7 +145,7 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
|
||||
#[OneToMany(targetEntity: ArticleAttribute::class, mappedBy: 'article', cascade: ['ALL'], indexBy: 'attribute')]
|
||||
private Collection $attributes;
|
||||
|
||||
public function addAttribute(string $name, ArticleAttribute $value): void
|
||||
public function addAttribute(string $name, string $value): void
|
||||
{
|
||||
$this->attributes[$name] = new ArticleAttribute($name, $value, $this);
|
||||
}
|
||||
@@ -214,57 +171,6 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
namespace Application\Model;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private int|null $id = null;
|
||||
/** @Column(type="string") */
|
||||
private string $title;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="ArticleAttribute", mappedBy="article", cascade={"ALL"}, indexBy="attribute")
|
||||
* @var Collection<int, ArticleAttribute>
|
||||
*/
|
||||
private Collection $attributes;
|
||||
|
||||
public function addAttribute($name, $value): void
|
||||
{
|
||||
$this->attributes[$name] = new ArticleAttribute($name, $value, $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class ArticleAttribute
|
||||
{
|
||||
/** @Id @ManyToOne(targetEntity="Article", inversedBy="attributes") */
|
||||
private Article|null $article;
|
||||
|
||||
/** @Id @Column(type="string") */
|
||||
private string $attribute;
|
||||
|
||||
/** @Column(type="string") */
|
||||
private string $value;
|
||||
|
||||
public function __construct($name, $value, $article)
|
||||
{
|
||||
$this->attribute = $name;
|
||||
$this->value = $value;
|
||||
$this->article = $article;
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
@@ -283,24 +189,6 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
|
||||
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Application\Model\ArticleAttribute:
|
||||
type: entity
|
||||
id:
|
||||
article:
|
||||
associationKey: true
|
||||
attribute:
|
||||
type: string
|
||||
fields:
|
||||
value:
|
||||
type: string
|
||||
manyToOne:
|
||||
article:
|
||||
targetEntity: Article
|
||||
inversedBy: attributes
|
||||
|
||||
|
||||
Use-Case 2: Simple Derived Identity
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -328,26 +216,6 @@ One good example for this is a user-address relationship:
|
||||
private User|null $user = null;
|
||||
}
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
|
||||
Address:
|
||||
type: entity
|
||||
id:
|
||||
user:
|
||||
associationKey: true
|
||||
oneToOne:
|
||||
user:
|
||||
targetEntity: User
|
||||
|
||||
|
||||
Use-Case 3: Join-Table with Metadata
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -44,33 +44,6 @@ instead of simply adding the respective columns to the ``User`` class.
|
||||
private string $country;
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
/** @Embedded(class = "Address") */
|
||||
private Address $address;
|
||||
}
|
||||
|
||||
/** @Embeddable */
|
||||
class Address
|
||||
{
|
||||
/** @Column(type = "string") */
|
||||
private string $street;
|
||||
|
||||
/** @Column(type = "string") */
|
||||
private string $postalCode;
|
||||
|
||||
/** @Column(type = "string") */
|
||||
private string $city;
|
||||
|
||||
/** @Column(type = "string") */
|
||||
private string $country;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -86,22 +59,6 @@ instead of simply adding the respective columns to the ``User`` class.
|
||||
</embeddable>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
embedded:
|
||||
address:
|
||||
class: Address
|
||||
|
||||
Address:
|
||||
type: embeddable
|
||||
fields:
|
||||
street: { type: string }
|
||||
postalCode: { type: string }
|
||||
city: { type: string }
|
||||
country: { type: string }
|
||||
|
||||
In terms of your database schema, Doctrine will automatically inline all
|
||||
columns from the ``Address`` class into the table of the ``User`` class,
|
||||
just as if you had declared them directly there.
|
||||
@@ -147,32 +104,12 @@ The following example shows you how to set your prefix to ``myPrefix_``:
|
||||
private Address $address;
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
/** @Embedded(class = "Address", columnPrefix = "myPrefix_") */
|
||||
private $address;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<entity name="User">
|
||||
<embedded name="address" class="Address" column-prefix="myPrefix_" />
|
||||
</entity>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
embedded:
|
||||
address:
|
||||
class: Address
|
||||
columnPrefix: myPrefix_
|
||||
|
||||
To have Doctrine drop the prefix and use the value object's property name
|
||||
directly, set ``columnPrefix=false`` (``use-column-prefix="false"`` for XML):
|
||||
|
||||
@@ -189,32 +126,12 @@ directly, set ``columnPrefix=false`` (``use-column-prefix="false"`` for XML):
|
||||
private Address $address;
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
/** @Embedded(class = "Address", columnPrefix = false) */
|
||||
private Address $address;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<entity name="User">
|
||||
<embedded name="address" class="Address" use-column-prefix="false" />
|
||||
</entity>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
embedded:
|
||||
address:
|
||||
class: Address
|
||||
columnPrefix: false
|
||||
|
||||
|
||||
DQL
|
||||
---
|
||||
|
||||
@@ -65,23 +65,6 @@ switch to extra lazy as shown in these examples:
|
||||
public Collection $users;
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
namespace Doctrine\Tests\Models\CMS;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class CmsGroup
|
||||
{
|
||||
/**
|
||||
* @ManyToMany(targetEntity="CmsUser", mappedBy="groups", fetch="EXTRA_LAZY")
|
||||
* @var Collection<int, CmsUser>
|
||||
*/
|
||||
public Collection $users;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -95,14 +78,3 @@ switch to extra lazy as shown in these examples:
|
||||
<many-to-many field="users" target-entity="CmsUser" mapped-by="groups" fetch="EXTRA_LAZY" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Doctrine\Tests\Models\CMS\CmsGroup:
|
||||
type: entity
|
||||
# ...
|
||||
manyToMany:
|
||||
users:
|
||||
targetEntity: CmsUser
|
||||
mappedBy: groups
|
||||
fetch: EXTRA_LAZY
|
||||
|
||||
@@ -27,7 +27,7 @@ What is Doctrine?
|
||||
-----------------
|
||||
|
||||
Doctrine ORM is an `object-relational mapper (ORM) <https://en.wikipedia.org/wiki/Object-relational_mapping>`_
|
||||
for PHP 7.1+ that provides transparent persistence for PHP objects. It uses the Data Mapper
|
||||
for PHP that provides transparent persistence for PHP objects. It uses the Data Mapper
|
||||
pattern at the heart, aiming for a complete separation of your domain/business
|
||||
logic from the persistence in a relational database management system.
|
||||
|
||||
@@ -82,10 +82,9 @@ that directory with the following contents:
|
||||
|
||||
{
|
||||
"require": {
|
||||
"doctrine/orm": "^2.11.0",
|
||||
"doctrine/dbal": "^3.2",
|
||||
"symfony/yaml": "^5.4",
|
||||
"symfony/cache": "^5.4"
|
||||
"doctrine/orm": "^3",
|
||||
"doctrine/dbal": "^4",
|
||||
"symfony/cache": "^7"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {"": "src/"}
|
||||
@@ -107,12 +106,8 @@ Add the following directories::
|
||||
doctrine2-tutorial
|
||||
|-- config
|
||||
| `-- xml
|
||||
| `-- yaml
|
||||
`-- src
|
||||
|
||||
.. note::
|
||||
The YAML driver is deprecated and will be removed in version 3.0.
|
||||
It is strongly recommended to switch to one of the other mappings.
|
||||
.. note::
|
||||
It is strongly recommended that you require ``doctrine/dbal`` in your
|
||||
``composer.json`` as well, because using the ORM means mapping objects
|
||||
@@ -144,22 +139,14 @@ step:
|
||||
|
||||
// Create a simple "default" Doctrine ORM configuration for Attributes
|
||||
$config = ORMSetup::createAttributeMetadataConfiguration(
|
||||
paths: array(__DIR__."/src"),
|
||||
paths: [__DIR__ . '/src'],
|
||||
isDevMode: true,
|
||||
);
|
||||
// or if you prefer annotation, YAML or XML
|
||||
// $config = ORMSetup::createAnnotationMetadataConfiguration(
|
||||
// paths: array(__DIR__."/src"),
|
||||
// isDevMode: true,
|
||||
// );
|
||||
// or if you prefer XML
|
||||
// $config = ORMSetup::createXMLMetadataConfiguration(
|
||||
// paths: array(__DIR__."/config/xml"),
|
||||
// paths: [__DIR__ . '/config/xml'],
|
||||
// isDevMode: true,
|
||||
//);
|
||||
// $config = ORMSetup::createYAMLMetadataConfiguration(
|
||||
// paths: array(__DIR__."/config/yaml"),
|
||||
// isDevMode: true,
|
||||
// );
|
||||
|
||||
// configuring the database connection
|
||||
$connection = DriverManager::getConnection([
|
||||
@@ -170,10 +157,6 @@ step:
|
||||
// obtaining the entity manager
|
||||
$entityManager = new EntityManager($connection, $config);
|
||||
|
||||
.. note::
|
||||
The YAML driver is deprecated and will be removed in version 3.0.
|
||||
It is strongly recommended to switch to one of the other mappings.
|
||||
|
||||
The ``require_once`` statement sets up the class autoloading for Doctrine and
|
||||
its dependencies using Composer's autoloader.
|
||||
|
||||
@@ -500,8 +483,8 @@ language describes how entities, their properties and references should be
|
||||
persisted and what constraints should be applied to them.
|
||||
|
||||
Metadata for an Entity can be configured using attributes directly in
|
||||
the Entity class itself, or in an external XML or YAML file. This
|
||||
Getting Started guide will demonstrate metadata mappings using all three
|
||||
the Entity class itself, or in an external XML file. This
|
||||
Getting Started guide will demonstrate metadata mappings using both
|
||||
methods, but you only need to choose one.
|
||||
|
||||
.. configuration-block::
|
||||
@@ -527,33 +510,6 @@ methods, but you only need to choose one.
|
||||
// .. (other code)
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
// src/Product.php
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="products")
|
||||
*/
|
||||
class Product
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
private int|null $id = null;
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
private string $name;
|
||||
|
||||
// .. (other code)
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<!-- config/xml/Product.dcm.xml -->
|
||||
@@ -571,25 +527,6 @@ methods, but you only need to choose one.
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. note::
|
||||
The YAML driver is deprecated and will be removed in version 3.0.
|
||||
It is strongly recommended to switch to one of the other mappings.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# config/yaml/Product.dcm.yml
|
||||
Product:
|
||||
type: entity
|
||||
table: products
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
name:
|
||||
type: string
|
||||
|
||||
The top-level ``entity`` definition specifies information about
|
||||
the class and table name. The primitive type ``Product#name`` is
|
||||
defined as a ``field`` attribute. The ``id`` property is defined with
|
||||
@@ -1082,59 +1019,6 @@ the ``Product`` before:
|
||||
// ... (other code)
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
// src/Bug.php
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="bugs")
|
||||
*/
|
||||
class Bug
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
private int|null $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
private string $description;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime")
|
||||
*/
|
||||
private DateTime $created;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
private string $status;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="User", inversedBy="assignedBugs")
|
||||
*/
|
||||
private User|null $engineer;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="User", inversedBy="reportedBugs")
|
||||
*/
|
||||
private User|null $reporter;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToMany(targetEntity="Product")
|
||||
*/
|
||||
private Collection $products;
|
||||
|
||||
// ... (other code)
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<!-- config/xml/Bug.dcm.xml -->
|
||||
@@ -1159,40 +1043,6 @@ the ``Product`` before:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. note::
|
||||
The YAML driver is deprecated and will be removed in version 3.0.
|
||||
It is strongly recommended to switch to one of the other mappings.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# config/yaml/Bug.dcm.yml
|
||||
Bug:
|
||||
type: entity
|
||||
table: bugs
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
description:
|
||||
type: text
|
||||
created:
|
||||
type: datetime
|
||||
status:
|
||||
type: string
|
||||
manyToOne:
|
||||
reporter:
|
||||
targetEntity: User
|
||||
inversedBy: reportedBugs
|
||||
engineer:
|
||||
targetEntity: User
|
||||
inversedBy: assignedBugs
|
||||
manyToMany:
|
||||
products:
|
||||
targetEntity: Product
|
||||
|
||||
|
||||
Here we have the entity, id and primitive type definitions.
|
||||
For the "created" field we have used the ``datetime`` type,
|
||||
which translates the YYYY-mm-dd HH:mm:ss database format
|
||||
@@ -1249,47 +1099,6 @@ Finally, we'll add metadata mappings for the ``User`` entity.
|
||||
// .. (other code)
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
// src/User.php
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="users")
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
* @var int
|
||||
*/
|
||||
private int|null $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
* @var string
|
||||
*/
|
||||
private string $name;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="Bug", mappedBy="reporter")
|
||||
* @var Collection<int, Bug> An ArrayCollection of Bug objects.
|
||||
*/
|
||||
private Collection $reportedBugs;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="Bug", mappedBy="engineer")
|
||||
* @var Collection<int, Bug> An ArrayCollection of Bug objects.
|
||||
*/
|
||||
private Collection $assignedBugs;
|
||||
|
||||
// .. (other code)
|
||||
}
|
||||
.. code-block:: xml
|
||||
|
||||
<!-- config/xml/User.dcm.xml -->
|
||||
@@ -1310,33 +1119,7 @@ Finally, we'll add metadata mappings for the ``User`` entity.
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. note::
|
||||
The YAML driver is deprecated and will be removed in version 3.0.
|
||||
It is strongly recommended to switch to one of the other mappings.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# config/yaml/User.dcm.yml
|
||||
User:
|
||||
type: entity
|
||||
table: users
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
name:
|
||||
type: string
|
||||
oneToMany:
|
||||
reportedBugs:
|
||||
targetEntity: Bug
|
||||
mappedBy: reporter
|
||||
assignedBugs:
|
||||
targetEntity: Bug
|
||||
mappedBy: engineer
|
||||
|
||||
Here are some new things to mention about the ``OneToMany`` attribute.
|
||||
Here are some new things to mention about the ``one-to-many`` tags.
|
||||
Remember that we discussed about the inverse and owning side. Now
|
||||
both reportedBugs and assignedBugs are inverse relations, which
|
||||
means the join details have already been defined on the owning
|
||||
@@ -1800,21 +1583,6 @@ we have to adjust the metadata slightly.
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass="BugRepository")
|
||||
* @ORM\Table(name="bugs")
|
||||
*/
|
||||
class Bug
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
@@ -1827,16 +1595,6 @@ we have to adjust the metadata slightly.
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. note::
|
||||
The YAML driver is deprecated and will be removed in version 3.0.
|
||||
It is strongly recommended to switch to one of the other mappings.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Bug:
|
||||
type: entity
|
||||
repositoryClass: BugRepository
|
||||
|
||||
Now we can remove our query logic in all the places and instead use them through the EntityRepository.
|
||||
As an example here is the code of the first use case "List of Bugs":
|
||||
|
||||
|
||||
@@ -27,22 +27,6 @@ can specify the ``#[OrderBy]`` in the following way:
|
||||
private Collection $groups;
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
/** @Entity **/
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity="Group")
|
||||
* @OrderBy({"name" = "ASC"})
|
||||
* @var Collection<int, Group>
|
||||
*/
|
||||
private Collection $groups;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
@@ -55,23 +39,6 @@ can specify the ``#[OrderBy]`` in the following way:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
manyToMany:
|
||||
groups:
|
||||
orderBy: { 'name': 'ASC' }
|
||||
targetEntity: Group
|
||||
joinTable:
|
||||
name: users_groups
|
||||
joinColumns:
|
||||
user_id:
|
||||
referencedColumnName: id
|
||||
inverseJoinColumns:
|
||||
group_id:
|
||||
referencedColumnName: id
|
||||
|
||||
The DQL Snippet in OrderBy is only allowed to consist of
|
||||
unqualified, unquoted field names and of an optional ASC/DESC
|
||||
positional statement. Multiple Fields are separated by a comma (,).
|
||||
|
||||
@@ -66,7 +66,7 @@ which has mapping metadata that is overridden by the attribute above:
|
||||
#[Column(name: 'trait_foo', type: 'integer', length: 100, nullable: true, unique: true)]
|
||||
protected int $foo;
|
||||
|
||||
#[OneToOne(targetEntity: Bar::class, cascade: ['persist', 'merge'])]
|
||||
#[OneToOne(targetEntity: Bar::class, cascade: ['persist'])]
|
||||
#[JoinColumn(name: 'example_trait_bar_id', referencedColumnName: 'id')]
|
||||
protected Bar|null $bar = null;
|
||||
}
|
||||
@@ -81,4 +81,4 @@ The case for just extending a class would be just the same but:
|
||||
// ...
|
||||
}
|
||||
|
||||
Overriding is also supported via XML and YAML (:ref:`examples <inheritence_mapping_overrides>`).
|
||||
Overriding is also supported via XML (:ref:`examples <inheritence_mapping_overrides>`).
|
||||
|
||||
@@ -24,9 +24,7 @@ Mapping Indexed Associations
|
||||
You can map indexed associations by adding:
|
||||
|
||||
* ``indexBy`` argument to any ``#[OneToMany]`` or ``#[ManyToMany]`` attribute.
|
||||
* ``indexBy`` attribute to any ``@OneToMany`` or ``@ManyToMany`` annotation.
|
||||
* ``index-by`` attribute to any ``<one-to-many />`` or ``<many-to-many />`` xml element.
|
||||
* ``indexBy:`` key-value pair to any association defined in ``manyToMany:`` or ``oneToMany:`` YAML mapping files.
|
||||
|
||||
The code and mappings for the Market entity looks like this:
|
||||
|
||||
@@ -90,74 +88,6 @@ The code and mappings for the Market entity looks like this:
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
namespace Doctrine\Tests\Models\StockExchange;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="exchange_markets")
|
||||
*/
|
||||
class Market
|
||||
{
|
||||
/**
|
||||
* @Id @Column(type="integer") @GeneratedValue
|
||||
* @var int
|
||||
*/
|
||||
private int|null $id = null;
|
||||
|
||||
/**
|
||||
* @Column(type="string")
|
||||
* @var string
|
||||
*/
|
||||
private string $name;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="Stock", mappedBy="market", indexBy="symbol")
|
||||
* @var Collection<int, Stock>
|
||||
*/
|
||||
private Collection $stocks;
|
||||
|
||||
public function __construct($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->stocks = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): int|null
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function addStock(Stock $stock): void
|
||||
{
|
||||
$this->stocks[$stock->getSymbol()] = $stock;
|
||||
}
|
||||
|
||||
public function getStock($symbol): Stock
|
||||
{
|
||||
if (!isset($this->stocks[$symbol])) {
|
||||
throw new \InvalidArgumentException("Symbol is not traded on this market.");
|
||||
}
|
||||
|
||||
return $this->stocks[$symbol];
|
||||
}
|
||||
|
||||
/** @return array<string, Stock> */
|
||||
public function getStocks(): array
|
||||
{
|
||||
return $this->stocks->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -177,24 +107,6 @@ The code and mappings for the Market entity looks like this:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Doctrine\Tests\Models\StockExchange\Market:
|
||||
type: entity
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
name:
|
||||
type:string
|
||||
oneToMany:
|
||||
stocks:
|
||||
targetEntity: Stock
|
||||
mappedBy: market
|
||||
indexBy: symbol
|
||||
|
||||
Inside the ``addStock()`` method you can see how we directly set the key of the association to the symbol,
|
||||
so that we can work with the indexed association directly after invoking ``addStock()``. Inside ``getStock($symbol)``
|
||||
we pick a stock traded on the particular market by symbol. If this stock doesn't exist an exception is thrown.
|
||||
@@ -234,47 +146,6 @@ here are the code and mappings for it:
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
namespace Doctrine\Tests\Models\StockExchange;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="exchange_stocks")
|
||||
*/
|
||||
class Stock
|
||||
{
|
||||
/**
|
||||
* @Id @GeneratedValue @Column(type="integer")
|
||||
* @var int
|
||||
*/
|
||||
private int|null $id = null;
|
||||
|
||||
/**
|
||||
* @Column(type="string", unique=true)
|
||||
*/
|
||||
private string $symbol;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Market", inversedBy="stocks")
|
||||
* @var Market
|
||||
*/
|
||||
private Market|null $market = null;
|
||||
|
||||
public function __construct($symbol, Market $market)
|
||||
{
|
||||
$this->symbol = $symbol;
|
||||
$this->market = $market;
|
||||
$market->addStock($this);
|
||||
}
|
||||
|
||||
public function getSymbol(): string
|
||||
{
|
||||
return $this->symbol;
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -293,23 +164,6 @@ here are the code and mappings for it:
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Doctrine\Tests\Models\StockExchange\Stock:
|
||||
type: entity
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
symbol:
|
||||
type: string
|
||||
manyToOne:
|
||||
market:
|
||||
targetEntity: Market
|
||||
inversedBy: stocks
|
||||
|
||||
Querying indexed associations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="cascade-all" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-persist" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-merge" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-remove" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-refresh" type="orm:emptyType" minOccurs="0"/>
|
||||
<xs:element name="cascade-detach" type="orm:emptyType" minOccurs="0"/>
|
||||
@@ -82,36 +81,6 @@
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="named-query">
|
||||
<xs:attribute name="name" type="xs:string" use="required" />
|
||||
<xs:attribute name="query" type="xs:string" use="required" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="named-queries">
|
||||
<xs:sequence>
|
||||
<xs:element name="named-query" type="orm:named-query" minOccurs="1" maxOccurs="unbounded" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="named-native-query">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="query" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="name" type="xs:string" use="required" />
|
||||
<xs:attribute name="result-class" type="orm:fqcn" />
|
||||
<xs:attribute name="result-set-mapping" type="xs:string" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="named-native-queries">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="named-native-query" type="orm:named-native-query" minOccurs="1" maxOccurs="unbounded" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="entity-listener">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="lifecycle-callback" type="orm:lifecycle-callback" minOccurs="0" maxOccurs="unbounded"/>
|
||||
@@ -143,23 +112,6 @@
|
||||
<xs:attribute name="discriminator-column" type="xs:string" use="optional" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="sql-result-set-mapping">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="entity-result" type="orm:entity-result"/>
|
||||
<xs:element name="column-result" type="orm:column-result"/>
|
||||
</xs:choice>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="name" type="xs:string" use="required" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="sql-result-set-mappings">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="sql-result-set-mapping" type="orm:sql-result-set-mapping" minOccurs="1" maxOccurs="unbounded" />
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="cache">
|
||||
<xs:attribute name="usage" type="orm:cache-usage-type" />
|
||||
<xs:attribute name="region" type="xs:string" />
|
||||
@@ -175,9 +127,6 @@
|
||||
<xs:element name="discriminator-map" type="orm:discriminator-map" minOccurs="0"/>
|
||||
<xs:element name="lifecycle-callbacks" type="orm:lifecycle-callbacks" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="entity-listeners" type="orm:entity-listeners" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="named-queries" type="orm:named-queries" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="named-native-queries" type="orm:named-native-queries" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="sql-result-set-mappings" type="orm:sql-result-set-mappings" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="field" type="orm:field" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="embedded" type="orm:embedded" minOccurs="0" maxOccurs="unbounded"/>
|
||||
@@ -239,7 +188,6 @@
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="DEFERRED_IMPLICIT"/>
|
||||
<xs:enumeration value="DEFERRED_EXPLICIT"/>
|
||||
<xs:enumeration value="NOTIFY"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
@@ -247,7 +195,6 @@
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="SINGLE_TABLE"/>
|
||||
<xs:enumeration value="JOINED"/>
|
||||
<xs:enumeration value="TABLE_PER_CLASS"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
@@ -257,7 +204,6 @@
|
||||
<xs:enumeration value="SEQUENCE"/>
|
||||
<xs:enumeration value="IDENTITY"/>
|
||||
<xs:enumeration value="AUTO"/>
|
||||
<xs:enumeration value="UUID"/>
|
||||
<xs:enumeration value="CUSTOM" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
@@ -9,48 +9,45 @@
|
||||
<!-- Ignore warnings, show progress of the run and show sniff names -->
|
||||
<arg value="nps"/>
|
||||
|
||||
<config name="php_version" value="70100"/>
|
||||
<config name="php_version" value="80100"/>
|
||||
|
||||
<file>src</file>
|
||||
<file>tests</file>
|
||||
|
||||
<exclude-pattern>*/src/Mapping/InverseJoinColumn.php</exclude-pattern>
|
||||
<exclude-pattern>*/tests/Tests/Proxies/__CG__*</exclude-pattern>
|
||||
<exclude-pattern>*/tests/Tests/ORM/Tools/Export/export/*</exclude-pattern>
|
||||
|
||||
<rule ref="Doctrine">
|
||||
<exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingAnyTypeHint" />
|
||||
<exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint"/>
|
||||
<exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint" />
|
||||
<exclude name="SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly.ReferencedGeneralException"/>
|
||||
<exclude name="SlevomatCodingStandard.ControlStructures.EarlyExit"/>
|
||||
<exclude name="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming"/>
|
||||
<exclude name="SlevomatCodingStandard.Classes.SuperfluousExceptionNaming"/>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.Commenting.RequireOneLineDocComment.MultiLineDocComment">
|
||||
<!-- Remove when dropping PHPUnit 7 -->
|
||||
<exclude-pattern>*/tests/*</exclude-pattern>
|
||||
<exclude name="SlevomatCodingStandard.Classes.ModernClassNameReference.ClassNameReferencedViaFunctionCall"/>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint">
|
||||
<exclude-pattern>*/src/*</exclude-pattern>
|
||||
<!--
|
||||
that class extends another one inside src/ and can therefore not
|
||||
have more native typehints since its parent cannot have them: that
|
||||
would break signature compatibility.
|
||||
-->
|
||||
<exclude-pattern>tests/Tests/Mocks/HydratorMockStatement.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Tests/Models/Cache/ComplexAction.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Tests/Models/DDC117/DDC117ArticleDetails.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Tests/Models/DDC117/DDC117Translation.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Tests/ORM/Functional/Ticket/DDC2579Test.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Tests/ORM/Functional/ValueObjectsTest.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint">
|
||||
<exclude-pattern>*/src/*</exclude-pattern>
|
||||
<rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingAnyTypeHint">
|
||||
<exclude-pattern>tests/*</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint">
|
||||
<exclude-pattern>tests/*</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
|
||||
<exclude-pattern>src/Mapping/Driver/CompatibilityAnnotationDriver.php</exclude-pattern>
|
||||
<exclude-pattern>src/Tools/Console/CommandCompatibility.php</exclude-pattern>
|
||||
<exclude-pattern>src/Tools/Console/Helper/EntityManagerHelper.php</exclude-pattern>
|
||||
<exclude-pattern>tests/*</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
@@ -69,14 +66,6 @@
|
||||
<exclude-pattern>src/Tools/ToolEvents.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingTraversableTypeHintSpecification">
|
||||
<!-- https://github.com/doctrine/annotations/issues/129 -->
|
||||
<exclude-pattern>src/Mapping/Column.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/Index.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/Table.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/UniqueConstraint.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator">
|
||||
<exclude-pattern>src/Internal/Hydration/AbstractHydrator.php</exclude-pattern>
|
||||
</rule>
|
||||
@@ -93,7 +82,6 @@
|
||||
<exclude-pattern>src/Mapping/Cache.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/ChangeTrackingPolicy.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/Column.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/ColumnResult.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/CustomIdGenerator.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/DiscriminatorColumn.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/DiscriminatorMap.php</exclude-pattern>
|
||||
@@ -101,8 +89,6 @@
|
||||
<exclude-pattern>src/Mapping/Embedded.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/Entity.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/EntityListeners.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/EntityResult.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/FieldResult.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/GeneratedValue.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/HasLifecycleCallbacks.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/Id.php</exclude-pattern>
|
||||
@@ -114,10 +100,6 @@
|
||||
<exclude-pattern>src/Mapping/ManyToMany.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/ManyToOne.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/MappedSuperclass.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/NamedNativeQueries.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/NamedNativeQuery.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/NamedQueries.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/NamedQuery.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/OneToMany.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/OneToOne.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/OrderBy.php</exclude-pattern>
|
||||
@@ -130,8 +112,6 @@
|
||||
<exclude-pattern>src/Mapping/PreRemove.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/PreUpdate.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/SequenceGenerator.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/SqlResultSetMapping.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/SqlResultSetMappings.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/Table.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/UniqueConstraint.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/Version.php</exclude-pattern>
|
||||
@@ -141,6 +121,36 @@
|
||||
<exclude-pattern>src/Cache/DefaultQueryCache.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.Commenting.ForbiddenAnnotations">
|
||||
<properties>
|
||||
<property name="forbiddenAnnotations" type="array">
|
||||
<!--
|
||||
From Doctrine Coding Standard:
|
||||
Forbid useless annotations - Git and LICENCE file provide more accurate information
|
||||
-->
|
||||
<element value="@api"/>
|
||||
<element value="@author"/>
|
||||
<element value="@category"/>
|
||||
<element value="@copyright"/>
|
||||
<element value="@created"/>
|
||||
<element value="@license"/>
|
||||
<element value="@package"/>
|
||||
<element value="@since"/>
|
||||
<element value="@subpackage"/>
|
||||
<element value="@version"/>
|
||||
|
||||
<!-- Additionally forbid oldschool PHPUnit annotations to force the usage of attributes -->
|
||||
<element value="@covers"/>
|
||||
<element value="@depends"/>
|
||||
<element value="@dataProvider"/>
|
||||
<element value="@group"/>
|
||||
<element value="@requires"/>
|
||||
<element value="@test"/>
|
||||
<element value="@testWith"/>
|
||||
</property>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming">
|
||||
<exclude-pattern>src/EntityManagerInterface.php</exclude-pattern>
|
||||
</rule>
|
||||
@@ -196,9 +206,6 @@
|
||||
<exclude-pattern>tests/Tests/Models/DDC1590/DDC1590User.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.Commenting.ForbiddenAnnotations.AnnotationForbidden">
|
||||
<exclude-pattern>tests/Tests/ORM/Functional/Ticket/DDC832Test.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="Squiz.Classes.ValidClassName.NotCamelCaps">
|
||||
<!-- we need to test what happens with an stdClass proxy -->
|
||||
@@ -223,34 +230,20 @@
|
||||
|
||||
<rule ref="PSR2.Methods.MethodDeclaration.Underscore">
|
||||
<exclude-pattern>src/AbstractQuery.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/ClassMetadataInfo.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/ClassMetadata.php</exclude-pattern>
|
||||
<exclude-pattern>src/NativeQuery.php</exclude-pattern>
|
||||
<exclude-pattern>src/Query.php</exclude-pattern>
|
||||
<exclude-pattern>src/Query/TreeWalkerAdapter.php</exclude-pattern>
|
||||
<exclude-pattern>src/Tools/Export/Driver/AbstractExporter.php</exclude-pattern>
|
||||
<exclude-pattern>src/Tools/Export/Driver/AnnotationExporter.php</exclude-pattern>
|
||||
<exclude-pattern>src/Tools/Export/Driver/PhpExporter.php</exclude-pattern>
|
||||
<!-- extending a class from another package -->
|
||||
<exclude-pattern>tests/Tests/Mocks/DatabasePlatformMock.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Tests/Mocks/SchemaManagerMock.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Tests/ORM/AbstractQueryTest.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Tests/ORM/Functional/Ticket/DDC3634Test.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="Squiz.NamingConventions.ValidVariableName.PublicHasUnderscore">
|
||||
<exclude-pattern>src/AbstractQuery.php</exclude-pattern>
|
||||
<exclude-pattern>src/Configuration.php</exclude-pattern>
|
||||
<exclude-pattern>src/EntityRepository.php</exclude-pattern>
|
||||
<exclude-pattern>src/Internal/Hydration/AbstractHydrator.php</exclude-pattern>
|
||||
<exclude-pattern>src/Query/Exec/AbstractSqlExecutor.php</exclude-pattern>
|
||||
<exclude-pattern>src/Query/Exec/AbstractSqlExecutor.php</exclude-pattern>
|
||||
<exclude-pattern>src/Query/Printer.php</exclude-pattern>
|
||||
<exclude-pattern>src/Tools/EntityRepositoryGenerator.php</exclude-pattern>
|
||||
<exclude-pattern>src/Tools/Console/Helper/EntityManagerHelper.php</exclude-pattern>
|
||||
<exclude-pattern>src/Tools/Export/Driver/AbstractExporter.php</exclude-pattern>
|
||||
<exclude-pattern>src/Tools/Export/Driver/AnnotationExporter.php</exclude-pattern>
|
||||
<exclude-pattern>src/Tools/Export/Driver/PhpExporter.php</exclude-pattern>
|
||||
<exclude-pattern>src/Tools/Export/Driver/XmlExporter.php</exclude-pattern>
|
||||
<exclude-pattern>src/Tools/Export/Driver/YamlExporter.php</exclude-pattern>
|
||||
<!-- the impact of changing this would be too big -->
|
||||
<exclude-pattern>tests/Tests/OrmFunctionalTestCase.php</exclude-pattern>
|
||||
</rule>
|
||||
@@ -265,6 +258,7 @@
|
||||
<!-- Using @group and Group entity in the same file -->
|
||||
<exclude-pattern>tests/Tests/ORM/Functional/Ticket/DDC1885Test.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Tests/ORM/Functional/Ticket/DDC1843Test.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Tests/ORM/Mapping/ClassMetadataFactoryTest.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="Generic.CodeAnalysis.EmptyStatement.DetectedElse">
|
||||
|
||||
@@ -40,11 +40,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Cache/Persister/Collection/AbstractCollectionPersister.php
|
||||
|
||||
-
|
||||
message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#"
|
||||
count: 1
|
||||
path: src/Cache/Persister/Collection/AbstractCollectionPersister.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$key of method Doctrine\\\\ORM\\\\Cache\\\\EntityHydrator\\:\\:buildCacheEntry\\(\\) expects Doctrine\\\\ORM\\\\Cache\\\\EntityCacheKey, Doctrine\\\\ORM\\\\Cache\\\\CacheKey given\\.$#"
|
||||
count: 1
|
||||
@@ -95,48 +90,28 @@ parameters:
|
||||
count: 1
|
||||
path: src/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Cache\\\\Region\\\\DefaultRegion\\:\\:getCache\\(\\) should return Doctrine\\\\Common\\\\Cache\\\\CacheProvider but returns Doctrine\\\\Common\\\\Cache\\\\Cache\\.$#"
|
||||
count: 1
|
||||
path: src/Cache/Region/DefaultRegion.php
|
||||
|
||||
-
|
||||
message: "#^Access to an undefined property Doctrine\\\\ORM\\\\Cache\\\\CacheEntry\\:\\:\\$time\\.$#"
|
||||
count: 1
|
||||
path: src/Cache/TimestampQueryCacheValidator.php
|
||||
|
||||
-
|
||||
message: "#^Class Doctrine\\\\Common\\\\Cache\\\\ArrayCache not found\\.$#"
|
||||
count: 2
|
||||
path: src/Configuration.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:find\\(\\) invoked with 4 parameters, 2 required\\.$#"
|
||||
message: "#^Return type \\(Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\) of method Doctrine\\\\ORM\\\\Decorator\\\\EntityManagerDecorator\\:\\:getMetadataFactory\\(\\) should be compatible with return type \\(Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory\\<Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\<object\\>\\>\\) of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getMetadataFactory\\(\\)$#"
|
||||
count: 1
|
||||
path: src/Decorator/EntityManagerDecorator.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:flush\\(\\) invoked with 1 parameter, 0 required\\.$#"
|
||||
message: "#^Return type \\(Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\) of method Doctrine\\\\ORM\\\\Decorator\\\\EntityManagerDecorator\\:\\:getMetadataFactory\\(\\) should be compatible with return type \\(Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory\\<Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\<object\\>\\>\\) of method Doctrine\\\\Persistence\\\\ObjectManagerDecorator\\<Doctrine\\\\ORM\\\\EntityManagerInterface\\>\\:\\:getMetadataFactory\\(\\)$#"
|
||||
count: 1
|
||||
path: src/Decorator/EntityManagerDecorator.php
|
||||
|
||||
-
|
||||
message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#"
|
||||
count: 1
|
||||
path: src/EntityManager.php
|
||||
|
||||
-
|
||||
message: "#^Result of && is always false\\.$#"
|
||||
count: 1
|
||||
path: src/EntityManager.php
|
||||
|
||||
-
|
||||
message: "#^Return type \\(Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\) of method Doctrine\\\\ORM\\\\EntityManager\\:\\:getMetadataFactory\\(\\) should be compatible with return type \\(Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory\\<Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\<object\\>\\>\\) of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getMetadataFactory\\(\\)$#"
|
||||
count: 2
|
||||
count: 1
|
||||
path: src/EntityManager.php
|
||||
|
||||
-
|
||||
message: "#^Template type T of method Doctrine\\\\ORM\\\\EntityManagerInterface\\:\\:getClassMetadata\\(\\) is not referenced in a parameter\\.$#"
|
||||
message: "#^Return type \\(Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataFactory\\) of method Doctrine\\\\ORM\\\\EntityManagerInterface\\:\\:getMetadataFactory\\(\\) should be compatible with return type \\(Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory\\<Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\<object\\>\\>\\) of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getMetadataFactory\\(\\)$#"
|
||||
count: 1
|
||||
path: src/EntityManagerInterface.php
|
||||
|
||||
@@ -145,100 +120,20 @@ parameters:
|
||||
count: 1
|
||||
path: src/EntityRepository.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:find\\(\\) invoked with 4 parameters, 2 required\\.$#"
|
||||
count: 1
|
||||
path: src/EntityRepository.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getTableHiLoCurrentValSql\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/Id/TableGenerator.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getTableHiLoUpdateNextValSql\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/Id/TableGenerator.php
|
||||
|
||||
-
|
||||
message: "#^If condition is always true\\.$#"
|
||||
count: 2
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadataFactory.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$class of method Doctrine\\\\ORM\\\\Mapping\\\\QuoteStrategy\\:\\:getSequenceName\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata, Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo given\\.$#"
|
||||
count: 2
|
||||
path: src/Mapping/ClassMetadataFactory.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\:\\:fullyQualifiedClassName\\(\\) should return class\\-string\\|null but returns string\\|null\\.$#"
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\ToOneOwningSideMapping\\:\\:fromMappingArray\\(\\) should return static\\(Doctrine\\\\ORM\\\\Mapping\\\\ToOneOwningSideMapping\\) but returns Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadataInfo.php
|
||||
path: src/Mapping/ToOneOwningSideMapping.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\NamingStrategy\\:\\:joinColumnName\\(\\) invoked with 2 parameters, 1 required\\.$#"
|
||||
count: 2
|
||||
path: src/Mapping/ClassMetadataInfo.php
|
||||
|
||||
-
|
||||
message: "#^Negated boolean expression is always false\\.$#"
|
||||
count: 2
|
||||
path: src/Mapping/ClassMetadataInfo.php
|
||||
|
||||
-
|
||||
message: "#^Result of && is always false\\.$#"
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\ToOneOwningSideMapping\\:\\:fromMappingArrayAndName\\(\\) should return static\\(Doctrine\\\\ORM\\\\Mapping\\\\ToOneOwningSideMapping\\) but returns Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadataInfo.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:mapEmbedded\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/Driver/AnnotationDriver.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:mapManyToMany\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/Driver/AnnotationDriver.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:mapManyToOne\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/Driver/AnnotationDriver.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:mapOneToMany\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/Driver/AnnotationDriver.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:mapOneToOne\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/Driver/AnnotationDriver.php
|
||||
|
||||
-
|
||||
message: "#^Expression on left side of \\?\\? is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/Driver/AnnotationDriver.php
|
||||
|
||||
-
|
||||
message: "#^Expression on left side of \\?\\? is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#4 \\.\\.\\.\\$args of static method Doctrine\\\\Deprecations\\\\Deprecation\\:\\:trigger\\(\\) expects float\\|int\\|string, false given\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: "#^Empty array passed to foreach\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/Driver/XmlDriver.php
|
||||
|
||||
-
|
||||
message: "#^Offset 'version' on \\*NEVER\\* in isset\\(\\) always exists and is always null\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/Driver/XmlDriver.php
|
||||
path: src/Mapping/ToOneOwningSideMapping.php
|
||||
|
||||
-
|
||||
message: "#^Call to function is_int\\(\\) with string will always evaluate to false\\.$#"
|
||||
@@ -250,11 +145,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/NativeQuery.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Persisters\\\\Collection\\\\OneToManyPersister\\:\\:delete\\(\\) should return int\\|null but empty return statement found\\.$#"
|
||||
count: 1
|
||||
path: src/Persisters/Collection/OneToManyPersister.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$hints of method Doctrine\\\\ORM\\\\Internal\\\\Hydration\\\\AbstractHydrator\\:\\:hydrateAll\\(\\) expects array\\<string, string\\>, array\\<string, Doctrine\\\\ORM\\\\PersistentCollection\\|true\\> given\\.$#"
|
||||
count: 1
|
||||
@@ -270,11 +160,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: "#^Property Doctrine\\\\ORM\\\\Persisters\\\\Entity\\\\CachedPersisterContext\\:\\:\\$class \\(Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\) does not accept Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\.$#"
|
||||
count: 1
|
||||
path: src/Persisters/Entity/CachedPersisterContext.php
|
||||
|
||||
-
|
||||
message: "#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$isEmbeddedClass\\.$#"
|
||||
count: 1
|
||||
@@ -285,11 +170,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method Doctrine\\\\Common\\\\Proxy\\\\Proxy\\:\\:__wakeup\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined static method Doctrine\\\\ORM\\\\Proxy\\\\ProxyFactory\\:\\:createLazyGhost\\(\\)\\.$#"
|
||||
count: 1
|
||||
@@ -307,7 +187,7 @@ parameters:
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$class of method Doctrine\\\\ORM\\\\Utility\\\\IdentifierFlattener\\:\\:flattenIdentifier\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata, Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata given\\.$#"
|
||||
count: 3
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
@@ -330,31 +210,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Query/AST/Functions/DateSubFunction.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$simpleArithmeticExpr of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkSimpleArithmeticExpression\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\SimpleArithmeticExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#"
|
||||
count: 1
|
||||
path: src/Query/AST/Functions/LengthFunction.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$simpleArithmeticExpr of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkSimpleArithmeticExpression\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\SimpleArithmeticExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#"
|
||||
count: 1
|
||||
path: src/Query/AST/Functions/LowerFunction.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$simpleArithmeticExpr of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkSimpleArithmeticExpression\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\SimpleArithmeticExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#"
|
||||
count: 1
|
||||
path: src/Query/AST/Functions/UpperFunction.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\AST\\\\IndexBy\\:\\:dispatch\\(\\) should return string but returns void\\.$#"
|
||||
count: 1
|
||||
path: src/Query/AST/IndexBy.php
|
||||
|
||||
-
|
||||
message: "#^Result of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkIndexBy\\(\\) \\(void\\) is used\\.$#"
|
||||
count: 1
|
||||
path: src/Query/AST/IndexBy.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkJoinPathExpression\\(\\)\\.$#"
|
||||
count: 1
|
||||
@@ -375,11 +230,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Query/AST/WhenClause.php
|
||||
|
||||
-
|
||||
message: "#^Property Doctrine\\\\ORM\\\\Query\\\\Exec\\\\AbstractSqlExecutor\\:\\:\\$queryCacheProfile \\(Doctrine\\\\DBAL\\\\Cache\\\\QueryCacheProfile\\) does not accept null\\.$#"
|
||||
count: 1
|
||||
path: src/Query/Exec/AbstractSqlExecutor.php
|
||||
|
||||
-
|
||||
message: "#^PHPDoc type array\\<string\\> of property Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Andx\\:\\:\\$allowedClasses is not covariant with PHPDoc type array\\<int, class\\-string\\> of overridden property Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Base\\:\\:\\$allowedClasses\\.$#"
|
||||
count: 1
|
||||
@@ -395,63 +245,18 @@ parameters:
|
||||
count: 1
|
||||
path: src/Query/Expr/Select.php
|
||||
|
||||
-
|
||||
message: "#^Comparison operation \"\\<\" between null and 102 is always true\\.$#"
|
||||
count: 1
|
||||
path: src/Query/Parser.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\Parser\\:\\:ArithmeticFactor\\(\\) should return Doctrine\\\\ORM\\\\Query\\\\AST\\\\ArithmeticFactor but returns Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string\\.$#"
|
||||
count: 1
|
||||
path: src/Query/Parser.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\Parser\\:\\:ArithmeticTerm\\(\\) should return Doctrine\\\\ORM\\\\Query\\\\AST\\\\ArithmeticTerm but returns Doctrine\\\\ORM\\\\Query\\\\AST\\\\ArithmeticFactor\\|string\\.$#"
|
||||
count: 1
|
||||
path: src/Query/Parser.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$stringPattern of class Doctrine\\\\ORM\\\\Query\\\\AST\\\\LikeExpression constructor expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\FunctionNode\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\InputParameter\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\Literal\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\PathExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#"
|
||||
count: 1
|
||||
path: src/Query/Parser.php
|
||||
|
||||
-
|
||||
message: "#^Result of && is always true\\.$#"
|
||||
count: 1
|
||||
path: src/Query/Parser.php
|
||||
|
||||
-
|
||||
message: "#^Unreachable statement \\- code above always terminates\\.$#"
|
||||
count: 4
|
||||
count: 2
|
||||
path: src/Query/Parser.php
|
||||
|
||||
-
|
||||
message: "#^Offset 'columns' on array\\{name\\: string, entities\\: array, columns\\: array\\} in isset\\(\\) always exists and is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Query/ResultSetMappingBuilder.php
|
||||
|
||||
-
|
||||
message: "#^Offset 'entities' on array\\{name\\: string, entities\\: array, columns\\: array\\} in isset\\(\\) always exists and is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Query/ResultSetMappingBuilder.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$class of static method Doctrine\\\\ORM\\\\Utility\\\\PersisterHelper\\:\\:getTypeOfColumn\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata, Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo given\\.$#"
|
||||
count: 1
|
||||
path: src/Query/ResultSetMappingBuilder.php
|
||||
|
||||
-
|
||||
message: "#^Call to function is_string\\(\\) with Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node will always evaluate to false\\.$#"
|
||||
count: 1
|
||||
path: src/Query/SqlWalker.php
|
||||
|
||||
-
|
||||
message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#"
|
||||
count: 1
|
||||
path: src/Query/SqlWalker.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkConditionalPrimary\\(\\) should return string but return statement is missing\\.$#"
|
||||
message: "#^Match arm is unreachable because previous comparison is always true\\.$#"
|
||||
count: 1
|
||||
path: src/Query/SqlWalker.php
|
||||
|
||||
@@ -460,161 +265,21 @@ parameters:
|
||||
count: 1
|
||||
path: src/Query/SqlWalker.php
|
||||
|
||||
-
|
||||
message: "#^Result of && is always false\\.$#"
|
||||
count: 1
|
||||
path: src/Query/SqlWalker.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\TreeWalkerAdapter\\:\\:getExecutor\\(\\) should return Doctrine\\\\ORM\\\\Query\\\\Exec\\\\AbstractSqlExecutor but returns null\\.$#"
|
||||
count: 1
|
||||
path: src/Query/TreeWalkerAdapter.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\TreeWalkerChain\\:\\:getExecutor\\(\\) should return Doctrine\\\\ORM\\\\Query\\\\Exec\\\\AbstractSqlExecutor but returns null\\.$#"
|
||||
count: 1
|
||||
path: src/Query/TreeWalkerChain.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$value \\(string\\) of method Doctrine\\\\ORM\\\\Query\\\\TreeWalkerChainIterator\\:\\:offsetSet\\(\\) should be compatible with parameter \\$value \\(Doctrine\\\\ORM\\\\Query\\\\TreeWalker\\) of method ArrayAccess\\<int,Doctrine\\\\ORM\\\\Query\\\\TreeWalker\\>\\:\\:offsetSet\\(\\)$#"
|
||||
count: 1
|
||||
path: src/Query/TreeWalkerChainIterator.php
|
||||
|
||||
-
|
||||
message: "#^PHPDoc tag @param references unknown parameter\\: \\$where$#"
|
||||
count: 2
|
||||
path: src/QueryBuilder.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$dqlPart of method Doctrine\\\\ORM\\\\QueryBuilder\\:\\:add\\(\\) expects array\\<'join'\\|int, array\\<int\\|string, object\\>\\|string\\>\\|object\\|string, non\\-empty\\-array\\<string, Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Join\\> given\\.$#"
|
||||
count: 2
|
||||
path: src/QueryBuilder.php
|
||||
|
||||
-
|
||||
message: "#^Class Doctrine\\\\Common\\\\Cache\\\\ApcCache not found\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/ClearCache/QueryCommand.php
|
||||
|
||||
-
|
||||
message: "#^Class Doctrine\\\\Common\\\\Cache\\\\XcacheCache not found\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/ClearCache/QueryCommand.php
|
||||
|
||||
-
|
||||
message: "#^Class Doctrine\\\\Common\\\\Cache\\\\ApcCache not found\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/ClearCache/ResultCommand.php
|
||||
|
||||
-
|
||||
message: "#^Class Doctrine\\\\Common\\\\Cache\\\\XcacheCache not found\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/ClearCache/ResultCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$metadata of method Doctrine\\\\ORM\\\\Tools\\\\Export\\\\Driver\\\\AbstractExporter\\:\\:setMetadata\\(\\) expects array\\<int, Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\>, array\\<int, Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\> given\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php
|
||||
|
||||
-
|
||||
message: "#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$name\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/ConvertMappingCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$metadata of method Doctrine\\\\ORM\\\\Tools\\\\Export\\\\Driver\\\\AbstractExporter\\:\\:setMetadata\\(\\) expects array\\<int, Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\>, array\\<Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\> given\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/ConvertMappingCommand.php
|
||||
|
||||
-
|
||||
message: "#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$name\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateEntitiesCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$metadatas of method Doctrine\\\\ORM\\\\Tools\\\\EntityGenerator\\:\\:generate\\(\\) expects array\\<int, Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\>, array\\<Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\> given\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateEntitiesCommand.php
|
||||
|
||||
-
|
||||
message: "#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$name\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateProxiesCommand.php
|
||||
|
||||
-
|
||||
message: "#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$customRepositoryClassName\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateRepositoriesCommand.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$entityListeners of method Doctrine\\\\ORM\\\\Tools\\\\Console\\\\Command\\\\MappingDescribeCommand\\:\\:formatEntityListeners\\(\\) expects array\\<int, object\\>, array\\<string, array\\<int, array\\<string, string\\>\\>\\> given\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/MappingDescribeCommand.php
|
||||
|
||||
-
|
||||
message: "#^Offset 'allocationSize' on array\\{sequenceName\\: string, allocationSize\\: string, initialValue\\: string, quoted\\?\\: mixed\\} in isset\\(\\) always exists and is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/EntityGenerator.php
|
||||
|
||||
-
|
||||
message: "#^Offset 'initialValue' on array\\{sequenceName\\: string, allocationSize\\: string, initialValue\\: string, quoted\\?\\: mixed\\} in isset\\(\\) always exists and is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/EntityGenerator.php
|
||||
|
||||
-
|
||||
message: "#^Offset 'name' on array\\{name\\: string, schema\\?\\: string, indexes\\?\\: array, uniqueConstraints\\?\\: array, options\\?\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/EntityGenerator.php
|
||||
|
||||
-
|
||||
message: "#^Offset 'sequenceName' on array\\{sequenceName\\: string, allocationSize\\: string, initialValue\\: string, quoted\\?\\: mixed\\} in isset\\(\\) always exists and is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/EntityGenerator.php
|
||||
|
||||
-
|
||||
message: "#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\<object\\>\\:\\:\\$lifecycleCallbacks \\(array\\<string, array\\<int, string\\>\\>\\) in isset\\(\\) is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/EntityGenerator.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Tools\\\\Export\\\\Driver\\\\AbstractExporter\\:\\:_getIdGeneratorTypeString\\(\\) should return string but return statement is missing\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Export/Driver/AbstractExporter.php
|
||||
|
||||
-
|
||||
message: "#^If condition is always true\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Export/Driver/PhpExporter.php
|
||||
|
||||
-
|
||||
message: "#^Offset 'name' on array\\{name\\: string, schema\\?\\: string, indexes\\?\\: array, uniqueConstraints\\?\\: array, options\\?\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Export/Driver/XmlExporter.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$policy of method Doctrine\\\\ORM\\\\Tools\\\\Export\\\\Driver\\\\AbstractExporter\\:\\:_getChangeTrackingPolicyString\\(\\) expects 1\\|2\\|3, int given\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Export/Driver/XmlExporter.php
|
||||
|
||||
-
|
||||
message: "#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\<object\\>\\:\\:\\$lifecycleCallbacks \\(array\\<string, array\\<int, string\\>\\>\\) in isset\\(\\) is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Export/Driver/XmlExporter.php
|
||||
|
||||
-
|
||||
message: "#^Right side of && is always true\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Export/Driver/XmlExporter.php
|
||||
|
||||
-
|
||||
message: "#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\<object\\>\\:\\:\\$lifecycleCallbacks \\(array\\<string, array\\<int, string\\>\\>\\) in isset\\(\\) is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Export/Driver/YamlExporter.php
|
||||
|
||||
-
|
||||
message: "#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\<object\\>\\:\\:\\$table \\(array\\<string, array\\|bool\\|string\\>\\) on left side of \\?\\? is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Tools/Export/Driver/YamlExporter.php
|
||||
|
||||
-
|
||||
message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#"
|
||||
count: 1
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
includes:
|
||||
- phpstan-baseline.neon
|
||||
- phpstan-params.neon
|
||||
|
||||
parameters:
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
|
||||
ignoreErrors:
|
||||
# PHPStan doesn't understand our method_exists() safeguards.
|
||||
- '/Call to an undefined method Doctrine\\DBAL\\Connection::createSchemaManager\(\)\./'
|
||||
# Class name will change in DBAL 3.
|
||||
- '/^Class Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform not found\.$/'
|
||||
- '/^Class Doctrine\\DBAL\\Platforms\\AbstractMySQLPlatform not found\.$/'
|
||||
- '/^Class Doctrine\\DBAL\\Platforms\\MySQLPlatform not found\.$/'
|
||||
-
|
||||
message: '/Doctrine\\DBAL\\Platforms\\MyS(ql|QL)Platform/'
|
||||
path: src/Mapping/ClassMetadataFactory.php
|
||||
|
||||
# Forward compatibility for DBAL 3.5
|
||||
- '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getAlterSchemaSQL\(\).$/'
|
||||
|
||||
# Forward compatibility for DBAL 3.4
|
||||
- '/^Call to an undefined method Doctrine\\DBAL\\Cache\\QueryCacheProfile::[gs]etResultCache\(\)\.$/'
|
||||
-
|
||||
message: '/^Call to an undefined static method Doctrine\\DBAL\\Configuration::[gs]etResultCache\(\)\.$/'
|
||||
path: src/Configuration.php
|
||||
-
|
||||
message: '/^Parameter #3 \$resultCache of class Doctrine\\DBAL\\Cache\\QueryCacheProfile constructor/'
|
||||
path: src/AbstractQuery.php
|
||||
-
|
||||
message: '/^Parameter #2 \$\w+ of method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getDateAdd\w+Expression\(\) expects int, string given\.$/'
|
||||
path: src/Query/AST/Functions/DateAddFunction.php
|
||||
-
|
||||
message: '/^Parameter #2 \$\w+ of method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getDateSub\w+Expression\(\) expects int, string given\.$/'
|
||||
path: src/Query/AST/Functions/DateSubFunction.php
|
||||
|
||||
# False positive
|
||||
-
|
||||
message: '/^Call to an undefined method Doctrine\\Common\\Cache\\Cache::deleteAll\(\)\.$/'
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/ClearCache/ResultCommand.php
|
||||
# See https://github.com/doctrine/dbal/pull/5129
|
||||
-
|
||||
message: '/^Parameter #3 \$startPos of method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getLocateExpression\(\) expects int\|false, string given\.$/'
|
||||
count: 1
|
||||
path: src/Query/AST/Functions/LocateFunction.php
|
||||
|
||||
# Won't get fixed in DBAL 2
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$start of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getSubstringExpression\\(\\) expects int, string given\\.$#"
|
||||
count: 1
|
||||
path: src/Query/AST/Functions/SubstringFunction.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$length of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getSubstringExpression\\(\\) expects int\\|null, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: src/Query/AST/Functions/SubstringFunction.php
|
||||
|
||||
-
|
||||
message: '#^Class Doctrine\\DBAL\\Platforms\\MySQLPlatform not found\.$#'
|
||||
count: 2
|
||||
path: src/Mapping/ClassMetadataFactory.php
|
||||
|
||||
# Symfony cache supports passing a key prefix to the clear method.
|
||||
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
|
||||
|
||||
# Persistence 2 support
|
||||
-
|
||||
message: '/clear.*invoked with 1 parameter/'
|
||||
path: src/EntityRepository.php
|
||||
-
|
||||
message: '#^Class Doctrine\\Persistence\\ObjectManagerAware not found\.$#'
|
||||
path: src/UnitOfWork.php
|
||||
-
|
||||
message: '#^Call to method injectObjectManager\(\) on an unknown class Doctrine\\Persistence\\ObjectManagerAware\.$#'
|
||||
path: src/UnitOfWork.php
|
||||
36
phpstan-dbal3.neon
Normal file
36
phpstan-dbal3.neon
Normal file
@@ -0,0 +1,36 @@
|
||||
includes:
|
||||
- phpstan-baseline.neon
|
||||
- phpstan-params.neon
|
||||
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
# Symfony cache supports passing a key prefix to the clear method.
|
||||
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
|
||||
|
||||
# We can be certain that those values are not matched.
|
||||
-
|
||||
message: '~^Match expression does not handle remaining values:~'
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
# DBAL 4 compatibility
|
||||
-
|
||||
message: '~^Method Doctrine\\ORM\\Query\\AST\\Functions\\TrimFunction::getTrimMode\(\) never returns .* so it can be removed from the return type\.$~'
|
||||
path: src/Query/AST/Functions/TrimFunction.php
|
||||
-
|
||||
message: '~^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getArrayBindingType\(\) never returns .* so it can be removed from the return type\.$~'
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
- '~^Class Doctrine\\DBAL\\Platforms\\SQLitePlatform not found\.$~'
|
||||
|
||||
# To be removed in 4.0
|
||||
-
|
||||
message: '#Negated boolean expression is always false\.#'
|
||||
paths:
|
||||
- src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
-
|
||||
message: '~^Call to deprecated method getEventManager\(\) of class Doctrine\\DBAL\\Connection\.$~'
|
||||
path: src/EntityManager.php
|
||||
-
|
||||
message: '~deprecated class Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand\:~'
|
||||
path: src/Tools/Console/ConsoleRunner.php
|
||||
@@ -9,7 +9,3 @@ parameters:
|
||||
Doctrine\ORM\Query\Parser:
|
||||
- syntaxError
|
||||
phpVersion: 80200
|
||||
|
||||
ignoreErrors:
|
||||
# Remove on 3.0.x
|
||||
- '~^Default value of the parameter #2 \$value \(array\{\}\) of method Doctrine\\ORM\\Query\\AST\\InstanceOfExpression\:\:__construct\(\) is incompatible with type non\-empty\-array<int, Doctrine\\ORM\\Query\\AST\\InputParameter\|string>\.$~'
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
includes:
|
||||
- phpstan-baseline.neon
|
||||
- phpstan-params.neon
|
||||
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
# deprecations from doctrine/dbal:3.x
|
||||
- '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getGuidExpression\(\).$/'
|
||||
|
||||
# Fallback logic for DBAL 2
|
||||
-
|
||||
message: '/Application::add\(\) expects Symfony\\Component\\Console\\Command\\Command/'
|
||||
path: src/Tools/Console/ConsoleRunner.php
|
||||
|
||||
- '/^Class Doctrine\\DBAL\\Platforms\\SQLAnywherePlatform not found\.$/'
|
||||
- '/^Call to method \w+\(\) on an unknown class Doctrine\\DBAL\\Platforms\\SQLAnywherePlatform\.$/'
|
||||
|
||||
-
|
||||
message: '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getSQLResultCasing\(\)\.$/'
|
||||
path: src/Internal/SQLResultCasing.php
|
||||
-
|
||||
message: '/^Parameter \$stmt of method .* has invalid type Doctrine\\DBAL\\Driver\\ResultStatement\.$/'
|
||||
path: src/Internal/Hydration/AbstractHydrator.php
|
||||
-
|
||||
message: '/^Class Doctrine\\DBAL\\Driver\\ResultStatement not found\.$/'
|
||||
path: src/Internal/Hydration/AbstractHydrator.php
|
||||
-
|
||||
message: '/^Call to static method ensure\(\) on an unknown class Doctrine\\DBAL\\ForwardCompatibility\\Result\.$/'
|
||||
path: src/Internal/Hydration/AbstractHydrator.php
|
||||
-
|
||||
message: '/^Instanceof between Doctrine\\DBAL\\Platforms\\AbstractPlatform and Doctrine\\DBAL\\Platforms\\MySQLPlatform will always evaluate to false\.$/'
|
||||
path: src/Utility/LockSqlHelper.php
|
||||
|
||||
# False positive
|
||||
-
|
||||
message: '/^Call to an undefined method Doctrine\\Common\\Cache\\Cache::deleteAll\(\)\.$/'
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/ClearCache/ResultCommand.php
|
||||
|
||||
# Symfony cache supports passing a key prefix to the clear method.
|
||||
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
|
||||
71
phpstan.neon
71
phpstan.neon
@@ -4,49 +4,44 @@ includes:
|
||||
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
# deprecations from doctrine/dbal:3.x
|
||||
- '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getGuidExpression\(\).$/'
|
||||
|
||||
# Fallback logic for DBAL 2
|
||||
-
|
||||
message: '/Application::add\(\) expects Symfony\\Component\\Console\\Command\\Command/'
|
||||
path: src/Tools/Console/ConsoleRunner.php
|
||||
|
||||
- '/^Class Doctrine\\DBAL\\Platforms\\SQLAnywherePlatform not found\.$/'
|
||||
- '/^Call to method \w+\(\) on an unknown class Doctrine\\DBAL\\Platforms\\SQLAnywherePlatform\.$/'
|
||||
|
||||
-
|
||||
message: '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getSQLResultCasing\(\)\.$/'
|
||||
path: src/Internal/SQLResultCasing.php
|
||||
-
|
||||
message: '/^Parameter \$stmt of method .* has invalid type Doctrine\\DBAL\\Driver\\ResultStatement\.$/'
|
||||
path: src/Internal/Hydration/AbstractHydrator.php
|
||||
-
|
||||
message: '/^Class Doctrine\\DBAL\\Driver\\ResultStatement not found\.$/'
|
||||
path: src/Internal/Hydration/AbstractHydrator.php
|
||||
-
|
||||
message: '/^Call to static method ensure\(\) on an unknown class Doctrine\\DBAL\\ForwardCompatibility\\Result\.$/'
|
||||
path: src/Internal/Hydration/AbstractHydrator.php
|
||||
-
|
||||
message: '/^Instanceof between Doctrine\\DBAL\\Platforms\\AbstractPlatform and Doctrine\\DBAL\\Platforms\\MySQLPlatform will always evaluate to false\.$/'
|
||||
path: src/Utility/LockSqlHelper.php
|
||||
|
||||
# False positive
|
||||
-
|
||||
message: '/^Call to an undefined method Doctrine\\Common\\Cache\\Cache::deleteAll\(\)\.$/'
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/ClearCache/ResultCommand.php
|
||||
|
||||
# Symfony cache supports passing a key prefix to the clear method.
|
||||
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
|
||||
|
||||
# Persistence 2 support
|
||||
# We can be certain that those values are not matched.
|
||||
-
|
||||
message: '/clear.*invoked with 1 parameter/'
|
||||
path: src/EntityRepository.php
|
||||
message: '~^Match expression does not handle remaining values:~'
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
# DBAL 4 compatibility
|
||||
-
|
||||
message: '#^Class Doctrine\\Persistence\\ObjectManagerAware not found\.$#'
|
||||
message: '~^Method Doctrine\\ORM\\Query\\AST\\Functions\\TrimFunction::getTrimMode\(\) never returns .* so it can be removed from the return type\.$~'
|
||||
path: src/Query/AST/Functions/TrimFunction.php
|
||||
-
|
||||
message: '~^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getArrayBindingType\(\) never returns .* so it can be removed from the return type\.$~'
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
# Compatibility with DBAL 3
|
||||
# See https://github.com/doctrine/dbal/pull/3480
|
||||
-
|
||||
message: '~^Result of method Doctrine\\DBAL\\Connection::commit\(\) \(void\) is used\.$~'
|
||||
path: src/UnitOfWork.php
|
||||
-
|
||||
message: '#^Call to method injectObjectManager\(\) on an unknown class Doctrine\\Persistence\\ObjectManagerAware\.$#'
|
||||
message: '~^Strict comparison using === between null and false will always evaluate to false\.$~'
|
||||
path: src/UnitOfWork.php
|
||||
-
|
||||
message: '~^Variable \$e on left side of \?\? always exists and is not nullable\.$~'
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '~^Parameter #1 \$command of method Symfony\\Component\\Console\\Application::add\(\) expects Symfony\\Component\\Console\\Command\\Command, Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand given\.$~'
|
||||
path: src/Tools/Console/ConsoleRunner.php
|
||||
|
||||
-
|
||||
message: '~Strict comparison using \=\=\= between callable\(\)\: mixed and null will always evaluate to false\.~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
# To be removed in 4.0
|
||||
-
|
||||
message: '#Negated boolean expression is always false\.#'
|
||||
paths:
|
||||
- src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
@@ -14,10 +14,13 @@
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
verbose="false"
|
||||
displayDetailsOnTestsThatTriggerNotices="true"
|
||||
displayDetailsOnTestsThatTriggerWarnings="true"
|
||||
failOnNotice="true"
|
||||
failOnWarning="true"
|
||||
failOnRisky="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
bootstrap="./tests/Tests/TestInit.php"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Doctrine ORM Test Suite">
|
||||
@@ -31,42 +34,38 @@
|
||||
<group>locking_functional</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1"/>
|
||||
<!-- "Real" test database -->
|
||||
<var name="db_driver" value="pdo_sqlite"/>
|
||||
<var name="db_memory" value="true"/>
|
||||
<!-- to use another database driver / credentials, provide them like so:
|
||||
<var name="db_driver" value="pdo_mysql"/>
|
||||
<var name="db_host" value="localhost" />
|
||||
<var name="db_user" value="root" />
|
||||
<var name="db_password" value="" />
|
||||
<var name="db_dbname" value="doctrine_tests" />
|
||||
<var name="db_port" value="3306"/>-->
|
||||
<!--<var name="db_event_subscribers" value="Doctrine\DBAL\Event\Listeners\OracleSessionInit">-->
|
||||
<!--
|
||||
At the start of each test run, we will drop and recreate the test database.
|
||||
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
<!-- "Real" test database -->
|
||||
<var name="db_driver" value="pdo_sqlite"/>
|
||||
<var name="db_memory" value="true"/>
|
||||
<!-- to use another database driver / credentials, provide them like so:
|
||||
<var name="db_driver" value="pdo_mysql"/>
|
||||
<var name="db_host" value="localhost" />
|
||||
<var name="db_user" value="root" />
|
||||
<var name="db_password" value="" />
|
||||
<var name="db_dbname" value="doctrine_tests" />
|
||||
<var name="db_port" value="3306"/>-->
|
||||
<!--<var name="db_event_subscribers" value="Doctrine\DBAL\Event\Listeners\OracleSessionInit">-->
|
||||
By default we assume that the `db_` config above has unrestricted access to the provided database
|
||||
platform.
|
||||
|
||||
<!--
|
||||
At the start of each test run, we will drop and recreate the test database.
|
||||
If you prefer, you can provide a restricted user above and a separate `privileged_db` config
|
||||
block to provide details of a privileged connection to use for the setup / teardown actions.
|
||||
|
||||
By default we assume that the `db_` config above has unrestricted access to the provided database
|
||||
platform.
|
||||
|
||||
If you prefer, you can provide a restricted user above and a separate `privileged_db` config
|
||||
block to provide details of a privileged connection to use for the setup / teardown actions.
|
||||
|
||||
Note that these configurations are not merged - if you specify a `privileged_db_driver` then
|
||||
you must also specify all the other options that your driver requires.
|
||||
|
||||
<var name="privileged_db_driver" value="pdo_mysql"/>
|
||||
<var name="privileged_db_host" value="localhost" />
|
||||
<var name="privileged_db_user" value="root" />
|
||||
<var name="privileged_db_password" value="" />
|
||||
<var name="privileged_db_dbname" value="doctrine_tests_tmp" />
|
||||
<var name="privileged_db_port" value="3306"/>
|
||||
-->
|
||||
|
||||
<env name="COLUMNS" value="120"/>
|
||||
</php>
|
||||
Note that these configurations are not merged - if you specify a `privileged_db_driver` then
|
||||
you must also specify all the other options that your driver requires.
|
||||
|
||||
<var name="privileged_db_driver" value="pdo_mysql"/>
|
||||
<var name="privileged_db_host" value="localhost" />
|
||||
<var name="privileged_db_user" value="root" />
|
||||
<var name="privileged_db_password" value="" />
|
||||
<var name="privileged_db_dbname" value="doctrine_tests_tmp" />
|
||||
<var name="privileged_db_port" value="3306"/>
|
||||
-->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
|
||||
2428
psalm-baseline.xml
2428
psalm-baseline.xml
File diff suppressed because it is too large
Load Diff
204
psalm.xml
204
psalm.xml
@@ -24,16 +24,9 @@
|
||||
<issueHandlers>
|
||||
<DeprecatedClass>
|
||||
<errorLevel type="suppress">
|
||||
<!-- DBAL 2 compatibility -->
|
||||
<referencedClass name="Doctrine\DBAL\Tools\Console\Command\ImportCommand"/>
|
||||
<referencedClass name="Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper"/>
|
||||
<!-- The exception is thrown by a deprecated method. -->
|
||||
<referencedClass name="Doctrine\ORM\Cache\Exception\InvalidResultCacheDriver"/>
|
||||
<!-- We wire the command as long as DBAL ships it -->
|
||||
<referencedClass name="Doctrine\DBAL\Tools\Console\Command\ReservedWordsCommand" />
|
||||
<!-- Remove on 3.0.x -->
|
||||
<referencedClass name="Doctrine\Common\Persistence\PersistentObject"/>
|
||||
<referencedClass name="Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets"/>
|
||||
<referencedClass name="Doctrine\ORM\Event\LifecycleEventArgs"/>
|
||||
<referencedClass name="Doctrine\ORM\Exception\UnknownEntityNamespace"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\Driver\AnnotationDriver"/>
|
||||
@@ -52,67 +45,14 @@
|
||||
<referencedClass name="Doctrine\ORM\Tools\Console\EntityManagerProvider\HelperSetManagerProvider"/>
|
||||
</errorLevel>
|
||||
</DeprecatedClass>
|
||||
<DeprecatedConstant>
|
||||
<errorLevel type="suppress">
|
||||
<file name="src/Configuration.php"/>
|
||||
<file name="src/Query/Lexer.php"/>
|
||||
<file name="src/Query/Parser.php"/>
|
||||
<file name="src/QueryBuilder.php"/>
|
||||
<file name="src/Tools/EntityGenerator.php"/>
|
||||
<file name="src/Mapping/ClassMetadataInfo.php" />
|
||||
<file name="src/Tools/EntityGenerator.php" />
|
||||
<file name="src/Tools/Export/Driver/AbstractExporter.php" />
|
||||
<file name="src/Tools/SchemaTool.php" />
|
||||
</errorLevel>
|
||||
</DeprecatedConstant>
|
||||
<DeprecatedInterface>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Remove on 3.0.x -->
|
||||
<referencedClass name="Doctrine\ORM\Cache\MultiGetRegion"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\Annotation"/>
|
||||
</errorLevel>
|
||||
</DeprecatedInterface>
|
||||
<DeprecatedMethod>
|
||||
<errorLevel type="suppress">
|
||||
<!-- We're calling the deprecated method for BC here. -->
|
||||
<file name="src/Internal/SQLResultCasing.php"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Platforms\AbstractPlatform::getIdentitySequenceName"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Platforms\AbstractPlatform::usesSequenceEmulatedIdentityColumns"/>
|
||||
<!-- We need to keep the calls for DBAL 2.13 compatibility. -->
|
||||
<referencedMethod name="Doctrine\DBAL\Cache\QueryCacheProfile::getResultCacheDriver"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Cache\QueryCacheProfile::setResultCacheDriver"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Configuration::getResultCacheImpl"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Configuration::setResultCacheImpl"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Connection::getSchemaManager"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Platforms\AbstractPlatform::getGuidExpression"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Platforms\AbstractPlatform::supportsForeignKeyConstraints"/>
|
||||
<!-- Remove on 3.0.x -->
|
||||
<!-- Compatibility with DBAL 3 -->
|
||||
<referencedMethod name="Doctrine\DBAL\Connection::getEventManager"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Platforms\AbstractPlatform::getReadLockSQL"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Platforms\AbstractPlatform::getWriteLockSQL"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Schema\Schema::visit"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Schema\SchemaDiff::toSaveSql"/>
|
||||
<referencedMethod name="Doctrine\DBAL\Schema\SchemaDiff::toSql"/>
|
||||
<referencedMethod name="Doctrine\ORM\Internal\Hydration\AbstractHydrator::hydrateRow"/>
|
||||
<referencedMethod name="Doctrine\ORM\Configuration::ensureProductionSettings"/>
|
||||
<referencedMethod name="Doctrine\ORM\Configuration::newDefaultAnnotationDriver"/>
|
||||
<referencedMethod name="Doctrine\ORM\EntityManager::createConnection"/>
|
||||
<referencedMethod name="Doctrine\ORM\EntityManagerInterface::getPartialReference"/>
|
||||
<referencedMethod name="Doctrine\ORM\Id\AbstractIdGenerator::generate"/>
|
||||
<referencedMethod name="Doctrine\ORM\ORMInvalidArgumentException::invalidEntityName"/>
|
||||
<referencedMethod name="Doctrine\ORM\ORMSetup::createDefaultAnnotationDriver"/>
|
||||
<referencedMethod name="Doctrine\ORM\Query\SqlWalker::walkInExpression"/>
|
||||
<referencedMethod name="Doctrine\ORM\Query\TreeWalkerAdapter::_getQueryComponents"/>
|
||||
<referencedMethod name="Doctrine\ORM\Mapping\ClassMetadataInfo::isInheritanceTypeTablePerClass"/>
|
||||
<file name="src/Query/TreeWalkerChain.php"/>
|
||||
</errorLevel>
|
||||
</DeprecatedMethod>
|
||||
<DeprecatedProperty>
|
||||
<errorLevel type="suppress">
|
||||
<referencedProperty name="Doctrine\ORM\Cache\Region\DefaultRegion::$cache"/>
|
||||
<referencedProperty name="Doctrine\ORM\Mapping\Driver\AttributeDriver::$entityAnnotationClasses"/>
|
||||
</errorLevel>
|
||||
</DeprecatedProperty>
|
||||
<DocblockTypeContradiction>
|
||||
<errorLevel type="suppress">
|
||||
<!-- We're catching invalid input here. -->
|
||||
@@ -123,14 +63,12 @@
|
||||
<file name="src/Tools/Pagination/LimitSubqueryOutputWalker.php"/>
|
||||
<!-- https://github.com/vimeo/psalm/issues/8520 -->
|
||||
<file name="src/PersistentCollection.php"/>
|
||||
<!-- Remove on 4.0.x -->
|
||||
<file name="src/Mapping/Driver/AttributeDriver.php"/>
|
||||
<file name="src/Mapping/Driver/XmlDriver.php"/>
|
||||
<file name="src/ORMSetup.php"/>
|
||||
</errorLevel>
|
||||
</DocblockTypeContradiction>
|
||||
<DuplicateClass>
|
||||
<errorLevel type="suppress">
|
||||
<file name="src/Tools/Console/CommandCompatibility.php"/>
|
||||
<file name="src/Tools/Console/Helper/EntityManagerHelper.php"/>
|
||||
</errorLevel>
|
||||
</DuplicateClass>
|
||||
<ForbiddenCode>
|
||||
<errorLevel type="suppress">
|
||||
<file name="src/Tools/Debug.php"/>
|
||||
@@ -138,9 +76,6 @@
|
||||
</ForbiddenCode>
|
||||
<InvalidArgument>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Argument type changes in DBAL 3.2 -->
|
||||
<referencedFunction name="Doctrine\DBAL\Cache\QueryCacheProfile::__construct"/>
|
||||
<!-- https://github.com/vimeo/psalm/issues/9155 -->
|
||||
<referencedFunction name="Doctrine\ORM\Mapping\ClassMetadata::addInheritedAssociationMapping"/>
|
||||
</errorLevel>
|
||||
</InvalidArgument>
|
||||
@@ -156,13 +91,24 @@
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
</errorLevel>
|
||||
</InvalidArrayAssignment>
|
||||
<InvalidClass>
|
||||
<LessSpecificReturnStatement>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Class name changes in DBAL 3. -->
|
||||
<referencedClass name="Doctrine\DBAL\Platforms\PostgreSQLPlatform" />
|
||||
<referencedClass name="Doctrine\DBAL\Platforms\MySQLPlatform" />
|
||||
<!-- In DBAL 4, column precision is nullable. See https://github.com/doctrine/dbal/pull/3511 -->
|
||||
<file name="src/Mapping/Driver/DatabaseDriver.php"/>
|
||||
</errorLevel>
|
||||
</InvalidClass>
|
||||
</LessSpecificReturnStatement>
|
||||
<MoreSpecificReturnType>
|
||||
<errorLevel type="suppress">
|
||||
<!-- In DBAL 4, the default column value is mixed. See https://github.com/doctrine/dbal/pull/3511 -->
|
||||
<file name="src/Mapping/Driver/DatabaseDriver.php"/>
|
||||
</errorLevel>
|
||||
</MoreSpecificReturnType>
|
||||
<InvalidReturnType>
|
||||
<errorLevel type="suppress">
|
||||
<!-- https://github.com/vimeo/psalm/issues/8819 -->
|
||||
<file name="src/Internal/Hydration/AbstractHydrator.php"/>
|
||||
</errorLevel>
|
||||
</InvalidReturnType>
|
||||
<InvalidParamDefault>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Remove on 3.0.x -->
|
||||
@@ -181,47 +127,34 @@
|
||||
<file name="src/Mapping/ReflectionReadonlyProperty.php"/>
|
||||
</errorLevel>
|
||||
</MethodSignatureMismatch>
|
||||
<MissingDependency>
|
||||
<errorLevel type="suppress">
|
||||
<!-- DBAL 3.2 forward compatibility -->
|
||||
<file name="src/Internal/SQLResultCasing.php"/>
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
<file name="src/Tools/Pagination/LimitSubqueryOutputWalker.php"/>
|
||||
</errorLevel>
|
||||
</MissingDependency>
|
||||
<MissingParamType>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Persistence 2 compatibility -->
|
||||
<file name="src/EntityManager.php"/>
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
<file name="src/Mapping/ClassMetadataInfo.php"/>
|
||||
<file name="src/Mapping/ClassMetadata.php"/>
|
||||
</errorLevel>
|
||||
</MissingParamType>
|
||||
<NonInvariantDocblockPropertyType>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Remove on 3.0.x -->
|
||||
<file name="src/Query/AST/InListExpression.php"/>
|
||||
<file name="src/Query/AST/InSubselectExpression.php"/>
|
||||
</errorLevel>
|
||||
</NonInvariantDocblockPropertyType>
|
||||
<PossiblyInvalidArgument>
|
||||
<errorLevel type="suppress">
|
||||
<!-- https://github.com/vimeo/psalm/issues/9155 -->
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
</errorLevel>
|
||||
</PossiblyInvalidArgument>
|
||||
<PropertyNotSetInConstructor>
|
||||
<PossiblyNullArrayOffset>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Remove on 3.0.x -->
|
||||
<referencedProperty name="Doctrine\ORM\Cache\CacheKey::$hash"/>
|
||||
<!-- https://github.com/vimeo/psalm/issues/7878 -->
|
||||
<file name="src/Persisters/Collection/ManyToManyPersister.php"/>
|
||||
</errorLevel>
|
||||
</PossiblyNullArrayOffset>
|
||||
<PropertyNotSetInConstructor>
|
||||
<errorLevel type="suppress">
|
||||
<directory name="src/Query/AST" />
|
||||
</errorLevel>
|
||||
</PropertyNotSetInConstructor>
|
||||
<PropertyTypeCoercion>
|
||||
<errorLevel type="suppress">
|
||||
<file name="src/Mapping/ClassMetadataInfo.php"/>
|
||||
<file name="src/Mapping/ClassMetadata.php"/>
|
||||
</errorLevel>
|
||||
</PropertyTypeCoercion>
|
||||
<RedundantCastGivenDocblockType>
|
||||
@@ -231,18 +164,18 @@
|
||||
<file name="src/QueryBuilder.php"/>
|
||||
</errorLevel>
|
||||
</RedundantCastGivenDocblockType>
|
||||
<RedundantCondition>
|
||||
<errorLevel type="suppress">
|
||||
<!-- The SQLAnywherePlatform class may or may not exist depending on the DBAL version -->
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
</errorLevel>
|
||||
</RedundantCondition>
|
||||
<ReferenceConstraintViolation>
|
||||
<errorLevel type="suppress">
|
||||
<!-- https://github.com/vimeo/psalm/issues/9155 -->
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
</errorLevel>
|
||||
</ReferenceConstraintViolation>
|
||||
<RiskyTruthyFalsyComparison>
|
||||
<!-- TODO: Enable this new rule on higher branches. -->
|
||||
<errorLevel type="suppress">
|
||||
<directory name="src" />
|
||||
</errorLevel>
|
||||
</RiskyTruthyFalsyComparison>
|
||||
<TooManyArguments>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Symfony cache supports passing a key prefix to the clear method. -->
|
||||
@@ -251,55 +184,62 @@
|
||||
<!-- Persistence 2 compatibility -->
|
||||
<referencedFunction name="Doctrine\Persistence\ObjectManager::clear"/>
|
||||
|
||||
<!-- Remove on 3.0.x -->
|
||||
<referencedFunction name="Doctrine\Persistence\ObjectManager::refresh"/>
|
||||
<!-- See https://github.com/doctrine/orm/issues/8850 -->
|
||||
<referencedFunction name="Doctrine\DBAL\Connection::lastInsertId"/>
|
||||
|
||||
<!-- FIXME -->
|
||||
<referencedFunction name="Doctrine\DBAL\DriverManager::getConnection"/>
|
||||
</errorLevel>
|
||||
</TooManyArguments>
|
||||
<TypeDoesNotContainNull>
|
||||
<errorLevel type="suppress">
|
||||
<!-- DBAL 3 compatibility -->
|
||||
<file name="src/Tools/SchemaTool.php"/>
|
||||
</errorLevel>
|
||||
</TypeDoesNotContainNull>
|
||||
<TypeDoesNotContainType>
|
||||
<errorLevel type="suppress">
|
||||
<file name="src/Internal/SQLResultCasing.php"/>
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
<!-- DBAL 3 compatibility -->
|
||||
<file name="src/UnitOfWork.php"/>
|
||||
<file name="src/Utility/LockSqlHelper.php"/>
|
||||
</errorLevel>
|
||||
</TypeDoesNotContainType>
|
||||
<UndefinedClass>
|
||||
<errorLevel type="suppress">
|
||||
<referencedClass name="Doctrine\Common\Cache\ApcCache"/>
|
||||
<referencedClass name="Doctrine\Common\Cache\ArrayCache"/>
|
||||
<referencedClass name="Doctrine\Common\Cache\XcacheCache"/>
|
||||
|
||||
<!-- DBAL 2 compatibility -->
|
||||
<referencedClass name="Doctrine\DBAL\Driver\ResultStatement"/>
|
||||
<referencedClass name="Doctrine\DBAL\ForwardCompatibility\Result"/>
|
||||
<referencedClass name="Doctrine\DBAL\Platforms\SQLAnywherePlatform"/>
|
||||
|
||||
<!-- DBAL 3.2 forward compatibility -->
|
||||
<referencedClass name="Doctrine\DBAL\Platforms\PostgreSQLPlatform"/>
|
||||
<referencedClass name="Doctrine\DBAL\Platforms\SQLServerPlatform"/>
|
||||
|
||||
<!-- Persistence 2 compatibility -->
|
||||
<referencedClass name="Doctrine\Persistence\ObjectManagerAware"/>
|
||||
|
||||
<!-- Annotations 1 compatibility -->
|
||||
<referencedClass name="Doctrine\Common\Annotations\CachedReader"/>
|
||||
<!-- Compatibility with DBAL 3 -->
|
||||
<referencedClass name="Doctrine\DBAL\Platforms\SQLitePlatform"/>
|
||||
</errorLevel>
|
||||
</UndefinedClass>
|
||||
<UndefinedDocblockClass>
|
||||
<errorLevel type="suppress">
|
||||
<!-- DBAL 2 compatibility -->
|
||||
<referencedClass name="Doctrine\DBAL\Driver\ResultStatement"/>
|
||||
</errorLevel>
|
||||
</UndefinedDocblockClass>
|
||||
<UndefinedMethod>
|
||||
<errorLevel type="suppress">
|
||||
<!-- See https://github.com/doctrine/orm/issues/8884 -->
|
||||
<referencedMethod name="Doctrine\DBAL\Platforms\AbstractPlatform::getGuidExpression"/>
|
||||
<!-- Compatibility with DBAL 3 -->
|
||||
<referencedMethod name="Doctrine\DBAL\Connection::getEventManager"/>
|
||||
<!-- FIXME -->
|
||||
<referencedMethod name="Doctrine\DBAL\Schema\SchemaDiff::toSaveSql"/>
|
||||
</errorLevel>
|
||||
</UndefinedMethod>
|
||||
<MissingClosureReturnType>
|
||||
<UndefinedPropertyFetch>
|
||||
<errorLevel type="suppress">
|
||||
<file name="src/Tools/Pagination/Paginator.php"/>
|
||||
<!-- https://github.com/vimeo/psalm/issues/7878 -->
|
||||
<file name="src/Persisters/Collection/ManyToManyPersister.php"/>
|
||||
<file name="src/PersistentCollection.php"/>
|
||||
<file name="src/Utility/PersisterHelper.php"/>
|
||||
<file name="src/Tools/SchemaValidator.php"/>
|
||||
</errorLevel>
|
||||
</MissingClosureReturnType>
|
||||
</UndefinedPropertyFetch>
|
||||
<UnhandledMatchCondition>
|
||||
<errorLevel type="suppress">
|
||||
<!-- We can be certain that those values are not matched. -->
|
||||
<file name="src/Persisters/Entity/BasicEntityPersister.php"/>
|
||||
</errorLevel>
|
||||
</UnhandledMatchCondition>
|
||||
<ArgumentTypeCoercion>
|
||||
<errorLevel type="suppress">
|
||||
<!-- See https://github.com/JetBrains/phpstorm-stubs/pull/1383 -->
|
||||
<file name="src/Mapping/ClassMetadata.php"/>
|
||||
</errorLevel>
|
||||
</ArgumentTypeCoercion>
|
||||
</issueHandlers>
|
||||
</psalm>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,127 +38,69 @@ interface Cache
|
||||
*/
|
||||
public const MODE_REFRESH = 4;
|
||||
|
||||
/**
|
||||
* @param string $className The entity class.
|
||||
*
|
||||
* @return Region|null
|
||||
*/
|
||||
public function getEntityCacheRegion($className);
|
||||
public function getEntityCacheRegion(string $className): Region|null;
|
||||
|
||||
/**
|
||||
* @param string $className The entity class.
|
||||
* @param string $association The field name that represents the association.
|
||||
*
|
||||
* @return Region|null
|
||||
*/
|
||||
public function getCollectionCacheRegion($className, $association);
|
||||
public function getCollectionCacheRegion(string $className, string $association): Region|null;
|
||||
|
||||
/**
|
||||
* Determine whether the cache contains data for the given entity "instance".
|
||||
*
|
||||
* @param string $className The entity class.
|
||||
* @param mixed $identifier The entity identifier
|
||||
*
|
||||
* @return bool true if the underlying cache contains corresponding data; false otherwise.
|
||||
*/
|
||||
public function containsEntity($className, $identifier);
|
||||
public function containsEntity(string $className, mixed $identifier): bool;
|
||||
|
||||
/**
|
||||
* Evicts the entity data for a particular entity "instance".
|
||||
*
|
||||
* @param string $className The entity class.
|
||||
* @param mixed $identifier The entity identifier.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function evictEntity($className, $identifier);
|
||||
public function evictEntity(string $className, mixed $identifier): void;
|
||||
|
||||
/**
|
||||
* Evicts all entity data from the given region.
|
||||
*
|
||||
* @param string $className The entity metadata.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function evictEntityRegion($className);
|
||||
public function evictEntityRegion(string $className): void;
|
||||
|
||||
/**
|
||||
* Evict data from all entity regions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function evictEntityRegions();
|
||||
public function evictEntityRegions(): void;
|
||||
|
||||
/**
|
||||
* Determine whether the cache contains data for the given collection.
|
||||
*
|
||||
* @param string $className The entity class.
|
||||
* @param string $association The field name that represents the association.
|
||||
* @param mixed $ownerIdentifier The identifier of the owning entity.
|
||||
*
|
||||
* @return bool true if the underlying cache contains corresponding data; false otherwise.
|
||||
*/
|
||||
public function containsCollection($className, $association, $ownerIdentifier);
|
||||
public function containsCollection(string $className, string $association, mixed $ownerIdentifier): bool;
|
||||
|
||||
/**
|
||||
* Evicts the cache data for the given identified collection instance.
|
||||
*
|
||||
* @param string $className The entity class.
|
||||
* @param string $association The field name that represents the association.
|
||||
* @param mixed $ownerIdentifier The identifier of the owning entity.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function evictCollection($className, $association, $ownerIdentifier);
|
||||
public function evictCollection(string $className, string $association, mixed $ownerIdentifier): void;
|
||||
|
||||
/**
|
||||
* Evicts all entity data from the given region.
|
||||
*
|
||||
* @param string $className The entity class.
|
||||
* @param string $association The field name that represents the association.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function evictCollectionRegion($className, $association);
|
||||
public function evictCollectionRegion(string $className, string $association): void;
|
||||
|
||||
/**
|
||||
* Evict data from all collection regions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function evictCollectionRegions();
|
||||
public function evictCollectionRegions(): void;
|
||||
|
||||
/**
|
||||
* Determine whether the cache contains data for the given query.
|
||||
*
|
||||
* @param string $regionName The cache name given to the query.
|
||||
*
|
||||
* @return bool true if the underlying cache contains corresponding data; false otherwise.
|
||||
*/
|
||||
public function containsQuery($regionName);
|
||||
public function containsQuery(string $regionName): bool;
|
||||
|
||||
/**
|
||||
* Evicts all cached query results under the given name, or default query cache if the region name is NULL.
|
||||
*
|
||||
* @param string|null $regionName The cache name associated to the queries being cached.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function evictQueryRegion($regionName = null);
|
||||
public function evictQueryRegion(string|null $regionName = null): void;
|
||||
|
||||
/**
|
||||
* Evict data from all query regions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function evictQueryRegions();
|
||||
public function evictQueryRegions(): void;
|
||||
|
||||
/**
|
||||
* Get query cache by region name or create a new one if none exist.
|
||||
*
|
||||
* @param string|null $regionName Query cache region name, or default query cache if the region name is NULL.
|
||||
*
|
||||
* @return QueryCache The Query Cache associated with the region name.
|
||||
*/
|
||||
public function getQueryCache($regionName = null);
|
||||
public function getQueryCache(string|null $regionName = null): QueryCache;
|
||||
}
|
||||
|
||||
@@ -4,37 +4,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
/**
|
||||
* Association cache entry
|
||||
*/
|
||||
class AssociationCacheEntry implements CacheEntry
|
||||
{
|
||||
/**
|
||||
* The entity identifier
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public $identifier;
|
||||
|
||||
/**
|
||||
* The entity class name
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var string
|
||||
* @psalm-var class-string
|
||||
*/
|
||||
public $class;
|
||||
|
||||
/**
|
||||
* @param string $class The entity class.
|
||||
* @param array<string, mixed> $identifier The entity identifier.
|
||||
* @psalm-param class-string $class
|
||||
* @param class-string $class The entity class name
|
||||
*/
|
||||
public function __construct($class, array $identifier)
|
||||
{
|
||||
$this->class = $class;
|
||||
$this->identifier = $identifier;
|
||||
public function __construct(
|
||||
public readonly string $class,
|
||||
public readonly array $identifier,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,10 +22,8 @@ class AssociationCacheEntry implements CacheEntry
|
||||
* This method allow Doctrine\Common\Cache\PhpFileCache compatibility
|
||||
*
|
||||
* @param array<string, mixed> $values array containing property values
|
||||
*
|
||||
* @return AssociationCacheEntry
|
||||
*/
|
||||
public static function __set_state(array $values)
|
||||
public static function __set_state(array $values): self
|
||||
{
|
||||
return new self($values['class'], $values['identifier']);
|
||||
}
|
||||
|
||||
@@ -11,72 +11,49 @@ use Doctrine\ORM\Cache\Logging\CacheLogger;
|
||||
*/
|
||||
class CacheConfiguration
|
||||
{
|
||||
/** @var CacheFactory|null */
|
||||
private $cacheFactory;
|
||||
private CacheFactory|null $cacheFactory = null;
|
||||
private RegionsConfiguration|null $regionsConfig = null;
|
||||
private CacheLogger|null $cacheLogger = null;
|
||||
private QueryCacheValidator|null $queryValidator = null;
|
||||
|
||||
/** @var RegionsConfiguration|null */
|
||||
private $regionsConfig;
|
||||
|
||||
/** @var CacheLogger|null */
|
||||
private $cacheLogger;
|
||||
|
||||
/** @var QueryCacheValidator|null */
|
||||
private $queryValidator;
|
||||
|
||||
/** @return CacheFactory|null */
|
||||
public function getCacheFactory()
|
||||
public function getCacheFactory(): CacheFactory|null
|
||||
{
|
||||
return $this->cacheFactory;
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function setCacheFactory(CacheFactory $factory)
|
||||
public function setCacheFactory(CacheFactory $factory): void
|
||||
{
|
||||
$this->cacheFactory = $factory;
|
||||
}
|
||||
|
||||
/** @return CacheLogger|null */
|
||||
public function getCacheLogger()
|
||||
public function getCacheLogger(): CacheLogger|null
|
||||
{
|
||||
return $this->cacheLogger;
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function setCacheLogger(CacheLogger $logger)
|
||||
public function setCacheLogger(CacheLogger $logger): void
|
||||
{
|
||||
$this->cacheLogger = $logger;
|
||||
}
|
||||
|
||||
/** @return RegionsConfiguration */
|
||||
public function getRegionsConfiguration()
|
||||
public function getRegionsConfiguration(): RegionsConfiguration
|
||||
{
|
||||
if ($this->regionsConfig === null) {
|
||||
$this->regionsConfig = new RegionsConfiguration();
|
||||
}
|
||||
|
||||
return $this->regionsConfig;
|
||||
return $this->regionsConfig ??= new RegionsConfiguration();
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function setRegionsConfiguration(RegionsConfiguration $regionsConfig)
|
||||
public function setRegionsConfiguration(RegionsConfiguration $regionsConfig): void
|
||||
{
|
||||
$this->regionsConfig = $regionsConfig;
|
||||
}
|
||||
|
||||
/** @return QueryCacheValidator */
|
||||
public function getQueryValidator()
|
||||
public function getQueryValidator(): QueryCacheValidator
|
||||
{
|
||||
if ($this->queryValidator === null) {
|
||||
$this->queryValidator = new TimestampQueryCacheValidator(
|
||||
$this->cacheFactory->getTimestampRegion()
|
||||
);
|
||||
}
|
||||
|
||||
return $this->queryValidator;
|
||||
return $this->queryValidator ??= new TimestampQueryCacheValidator(
|
||||
$this->cacheFactory->getTimestampRegion(),
|
||||
);
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function setQueryValidator(QueryCacheValidator $validator)
|
||||
public function setQueryValidator(QueryCacheValidator $validator): void
|
||||
{
|
||||
$this->queryValidator = $validator;
|
||||
}
|
||||
|
||||
@@ -5,57 +5,22 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
use Doctrine\ORM\Exception\ORMException;
|
||||
use LogicException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Exception for cache.
|
||||
*/
|
||||
class CacheException extends ORMException
|
||||
class CacheException extends LogicException implements ORMException
|
||||
{
|
||||
/**
|
||||
* @param string $sourceEntity
|
||||
* @param string $fieldName
|
||||
*
|
||||
* @return CacheException
|
||||
*/
|
||||
public static function updateReadOnlyCollection($sourceEntity, $fieldName)
|
||||
public static function updateReadOnlyCollection(string $sourceEntity, string $fieldName): self
|
||||
{
|
||||
return new self(sprintf('Cannot update a readonly collection "%s#%s"', $sourceEntity, $fieldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method is not used anymore.
|
||||
*
|
||||
* @param string $entityName
|
||||
*
|
||||
* @return CacheException
|
||||
*/
|
||||
public static function updateReadOnlyEntity($entityName)
|
||||
{
|
||||
return new self(sprintf('Cannot update a readonly entity "%s"', $entityName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entityName
|
||||
*
|
||||
* @return CacheException
|
||||
*/
|
||||
public static function nonCacheableEntity($entityName)
|
||||
public static function nonCacheableEntity(string $entityName): self
|
||||
{
|
||||
return new self(sprintf('Entity "%s" not configured as part of the second-level cache.', $entityName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method is not used anymore.
|
||||
*
|
||||
* @param string $entityName
|
||||
* @param string $field
|
||||
*
|
||||
* @return CacheException
|
||||
*/
|
||||
public static function nonCacheableEntityAssociation($entityName, $field)
|
||||
{
|
||||
return new self(sprintf('Entity association field "%s#%s" not configured as part of the second-level cache.', $entityName, $field));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,82 +8,57 @@ use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Cache\Persister\Collection\CachedCollectionPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
|
||||
/**
|
||||
* Contract for building second level cache regions components.
|
||||
*
|
||||
* @psalm-import-type AssociationMapping from ClassMetadata
|
||||
*/
|
||||
interface CacheFactory
|
||||
{
|
||||
/**
|
||||
* Build an entity persister for the given entity metadata.
|
||||
*
|
||||
* @param EntityManagerInterface $em The entity manager.
|
||||
* @param EntityPersister $persister The entity persister that will be cached.
|
||||
* @param ClassMetadata $metadata The entity metadata.
|
||||
*
|
||||
* @return CachedEntityPersister
|
||||
*/
|
||||
public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata);
|
||||
public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata): CachedEntityPersister;
|
||||
|
||||
/**
|
||||
* Build a collection persister for the given relation mapping.
|
||||
*
|
||||
* @param AssociationMapping $mapping The association mapping.
|
||||
*
|
||||
* @return CachedCollectionPersister
|
||||
*/
|
||||
public function buildCachedCollectionPersister(EntityManagerInterface $em, CollectionPersister $persister, array $mapping);
|
||||
/** Build a collection persister for the given relation mapping. */
|
||||
public function buildCachedCollectionPersister(
|
||||
EntityManagerInterface $em,
|
||||
CollectionPersister $persister,
|
||||
AssociationMapping $mapping,
|
||||
): CachedCollectionPersister;
|
||||
|
||||
/**
|
||||
* Build a query cache based on the given region name
|
||||
*
|
||||
* @param string|null $regionName The region name.
|
||||
*
|
||||
* @return QueryCache The built query cache.
|
||||
*/
|
||||
public function buildQueryCache(EntityManagerInterface $em, $regionName = null);
|
||||
public function buildQueryCache(EntityManagerInterface $em, string|null $regionName = null): QueryCache;
|
||||
|
||||
/**
|
||||
* Build an entity hydrator
|
||||
*
|
||||
* @return EntityHydrator The built entity hydrator.
|
||||
*/
|
||||
public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata);
|
||||
public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata): EntityHydrator;
|
||||
|
||||
/**
|
||||
* Build a collection hydrator
|
||||
*
|
||||
* @param mixed[] $mapping The association mapping.
|
||||
*
|
||||
* @return CollectionHydrator The built collection hydrator.
|
||||
*/
|
||||
public function buildCollectionHydrator(EntityManagerInterface $em, array $mapping);
|
||||
public function buildCollectionHydrator(EntityManagerInterface $em, AssociationMapping $mapping): CollectionHydrator;
|
||||
|
||||
/**
|
||||
* Build a cache region
|
||||
*
|
||||
* @param array<string,mixed> $cache The cache configuration.
|
||||
*
|
||||
* @return Region The cache region.
|
||||
*/
|
||||
public function getRegion(array $cache);
|
||||
public function getRegion(array $cache): Region;
|
||||
|
||||
/**
|
||||
* Build timestamp cache region
|
||||
*
|
||||
* @return TimestampRegion The timestamp region.
|
||||
*/
|
||||
public function getTimestampRegion();
|
||||
public function getTimestampRegion(): TimestampRegion;
|
||||
|
||||
/**
|
||||
* Build \Doctrine\ORM\Cache
|
||||
*
|
||||
* @return Cache
|
||||
*/
|
||||
public function createCache(EntityManagerInterface $entityManager);
|
||||
public function createCache(EntityManagerInterface $entityManager): Cache;
|
||||
}
|
||||
|
||||
@@ -4,33 +4,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
|
||||
/**
|
||||
* Defines entity / collection / query key to be stored in the cache region.
|
||||
* Allows multiple roles to be stored in the same cache region.
|
||||
*/
|
||||
abstract class CacheKey
|
||||
{
|
||||
/**
|
||||
* Unique identifier
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var string
|
||||
*/
|
||||
public $hash;
|
||||
|
||||
public function __construct(?string $hash = null)
|
||||
public function __construct(public readonly string $hash)
|
||||
{
|
||||
if ($hash === null) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/10212',
|
||||
'Calling %s() without providing a value for the $hash parameter is deprecated.',
|
||||
__METHOD__
|
||||
);
|
||||
} else {
|
||||
$this->hash = $hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,23 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
/**
|
||||
* Collection cache entry
|
||||
*/
|
||||
class CollectionCacheEntry implements CacheEntry
|
||||
{
|
||||
/**
|
||||
* The list of entity identifiers hold by the collection
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var CacheKey[]
|
||||
*/
|
||||
public $identifiers;
|
||||
|
||||
/** @param CacheKey[] $identifiers List of entity identifiers hold by the collection */
|
||||
public function __construct(array $identifiers)
|
||||
public function __construct(public readonly array $identifiers)
|
||||
{
|
||||
$this->identifiers = $identifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,10 +17,8 @@ class CollectionCacheEntry implements CacheEntry
|
||||
* This method allows for Doctrine\Common\Cache\PhpFileCache compatibility
|
||||
*
|
||||
* @param array<string, mixed> $values array containing property values
|
||||
*
|
||||
* @return CollectionCacheEntry
|
||||
*/
|
||||
public static function __set_state(array $values)
|
||||
public static function __set_state(array $values): CollectionCacheEntry
|
||||
{
|
||||
return new self($values['identifiers']);
|
||||
}
|
||||
|
||||
@@ -17,41 +17,22 @@ class CollectionCacheKey extends CacheKey
|
||||
/**
|
||||
* The owner entity identifier
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
public $ownerIdentifier;
|
||||
public readonly array $ownerIdentifier;
|
||||
|
||||
/**
|
||||
* The owner entity class
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var string
|
||||
* @psalm-var class-string
|
||||
*/
|
||||
public $entityClass;
|
||||
|
||||
/**
|
||||
* The association name
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var string
|
||||
*/
|
||||
public $association;
|
||||
|
||||
/**
|
||||
* @param string $entityClass The entity class.
|
||||
* @param string $association The field name that represents the association.
|
||||
* @param class-string $entityClass The owner entity class.
|
||||
* @param array<string, mixed> $ownerIdentifier The identifier of the owning entity.
|
||||
* @psalm-param class-string $entityClass
|
||||
*/
|
||||
public function __construct($entityClass, $association, array $ownerIdentifier)
|
||||
{
|
||||
public function __construct(
|
||||
public readonly string $entityClass,
|
||||
public readonly string $association,
|
||||
array $ownerIdentifier,
|
||||
) {
|
||||
ksort($ownerIdentifier);
|
||||
|
||||
$this->ownerIdentifier = $ownerIdentifier;
|
||||
$this->entityClass = (string) $entityClass;
|
||||
$this->association = (string) $association;
|
||||
|
||||
parent::__construct(str_replace('\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association);
|
||||
}
|
||||
|
||||
@@ -13,13 +13,9 @@ use Doctrine\ORM\PersistentCollection;
|
||||
*/
|
||||
interface CollectionHydrator
|
||||
{
|
||||
/**
|
||||
* @param array|mixed[]|Collection $collection The collection.
|
||||
*
|
||||
* @return CollectionCacheEntry
|
||||
*/
|
||||
public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, $collection);
|
||||
/** @param mixed[]|Collection $collection The collection. */
|
||||
public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, array|Collection $collection): CollectionCacheEntry;
|
||||
|
||||
/** @return mixed[]|null */
|
||||
public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection);
|
||||
public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection): array|null;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ interface ConcurrentRegion extends Region
|
||||
*
|
||||
* @throws LockException Indicates a problem accessing the region.
|
||||
*/
|
||||
public function lock(CacheKey $key);
|
||||
public function lock(CacheKey $key): Lock|null;
|
||||
|
||||
/**
|
||||
* Attempts to read unlock the mapping for the given key.
|
||||
@@ -30,9 +30,7 @@ interface ConcurrentRegion extends Region
|
||||
* @param CacheKey $key The key of the item to unlock.
|
||||
* @param Lock $lock The lock previously obtained from {@link readLock}
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws LockException Indicates a problem accessing the region.
|
||||
*/
|
||||
public function unlock(CacheKey $key, Lock $lock);
|
||||
public function unlock(CacheKey $key, Lock $lock): bool;
|
||||
}
|
||||
|
||||
@@ -20,37 +20,27 @@ use function is_object;
|
||||
*/
|
||||
class DefaultCache implements Cache
|
||||
{
|
||||
/** @var EntityManagerInterface */
|
||||
private $em;
|
||||
|
||||
/** @var UnitOfWork */
|
||||
private $uow;
|
||||
|
||||
/** @var CacheFactory */
|
||||
private $cacheFactory;
|
||||
private readonly UnitOfWork $uow;
|
||||
private readonly CacheFactory $cacheFactory;
|
||||
|
||||
/**
|
||||
* @var QueryCache[]
|
||||
* @psalm-var array<string, QueryCache>
|
||||
*/
|
||||
private $queryCaches = [];
|
||||
private array $queryCaches = [];
|
||||
|
||||
/** @var QueryCache|null */
|
||||
private $defaultQueryCache;
|
||||
private QueryCache|null $defaultQueryCache = null;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $em,
|
||||
) {
|
||||
$this->uow = $em->getUnitOfWork();
|
||||
$this->cacheFactory = $em->getConfiguration()
|
||||
->getSecondLevelCacheConfiguration()
|
||||
->getCacheFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getEntityCacheRegion($className)
|
||||
public function getEntityCacheRegion(string $className): Region|null
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
|
||||
@@ -62,10 +52,7 @@ class DefaultCache implements Cache
|
||||
return $persister->getCacheRegion();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getCollectionCacheRegion($className, $association)
|
||||
public function getCollectionCacheRegion(string $className, string $association): Region|null
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
$persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));
|
||||
@@ -77,10 +64,7 @@ class DefaultCache implements Cache
|
||||
return $persister->getCacheRegion();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function containsEntity($className, $identifier)
|
||||
public function containsEntity(string $className, mixed $identifier): bool
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
|
||||
@@ -92,10 +76,7 @@ class DefaultCache implements Cache
|
||||
return $persister->getCacheRegion()->contains($this->buildEntityCacheKey($metadata, $identifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function evictEntity($className, $identifier)
|
||||
public function evictEntity(string $className, mixed $identifier): void
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
|
||||
@@ -107,10 +88,7 @@ class DefaultCache implements Cache
|
||||
$persister->getCacheRegion()->evict($this->buildEntityCacheKey($metadata, $identifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function evictEntityRegion($className)
|
||||
public function evictEntityRegion(string $className): void
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
|
||||
@@ -122,10 +100,7 @@ class DefaultCache implements Cache
|
||||
$persister->getCacheRegion()->evictAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function evictEntityRegions()
|
||||
public function evictEntityRegions(): void
|
||||
{
|
||||
$metadatas = $this->em->getMetadataFactory()->getAllMetadata();
|
||||
|
||||
@@ -140,10 +115,7 @@ class DefaultCache implements Cache
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function containsCollection($className, $association, $ownerIdentifier)
|
||||
public function containsCollection(string $className, string $association, mixed $ownerIdentifier): bool
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
$persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));
|
||||
@@ -155,10 +127,7 @@ class DefaultCache implements Cache
|
||||
return $persister->getCacheRegion()->contains($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function evictCollection($className, $association, $ownerIdentifier)
|
||||
public function evictCollection(string $className, string $association, mixed $ownerIdentifier): void
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
$persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));
|
||||
@@ -170,10 +139,7 @@ class DefaultCache implements Cache
|
||||
$persister->getCacheRegion()->evict($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function evictCollectionRegion($className, $association)
|
||||
public function evictCollectionRegion(string $className, string $association): void
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
$persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));
|
||||
@@ -185,16 +151,13 @@ class DefaultCache implements Cache
|
||||
$persister->getCacheRegion()->evictAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function evictCollectionRegions()
|
||||
public function evictCollectionRegions(): void
|
||||
{
|
||||
$metadatas = $this->em->getMetadataFactory()->getAllMetadata();
|
||||
|
||||
foreach ($metadatas as $metadata) {
|
||||
foreach ($metadata->associationMappings as $association) {
|
||||
if (! $association['type'] & ClassMetadata::TO_MANY) {
|
||||
if (! $association->isToMany()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -209,18 +172,12 @@ class DefaultCache implements Cache
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function containsQuery($regionName)
|
||||
public function containsQuery(string $regionName): bool
|
||||
{
|
||||
return isset($this->queryCaches[$regionName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function evictQueryRegion($regionName = null)
|
||||
public function evictQueryRegion(string|null $regionName = null): void
|
||||
{
|
||||
if ($regionName === null && $this->defaultQueryCache !== null) {
|
||||
$this->defaultQueryCache->clear();
|
||||
@@ -233,10 +190,7 @@ class DefaultCache implements Cache
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function evictQueryRegions()
|
||||
public function evictQueryRegions(): void
|
||||
{
|
||||
$this->getQueryCache()->clear();
|
||||
|
||||
@@ -245,25 +199,16 @@ class DefaultCache implements Cache
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getQueryCache($regionName = null)
|
||||
public function getQueryCache(string|null $regionName = null): QueryCache
|
||||
{
|
||||
if ($regionName === null) {
|
||||
return $this->defaultQueryCache ?:
|
||||
$this->defaultQueryCache = $this->cacheFactory->buildQueryCache($this->em);
|
||||
return $this->defaultQueryCache ??= $this->cacheFactory->buildQueryCache($this->em);
|
||||
}
|
||||
|
||||
if (! isset($this->queryCaches[$regionName])) {
|
||||
$this->queryCaches[$regionName] = $this->cacheFactory->buildQueryCache($this->em, $regionName);
|
||||
}
|
||||
|
||||
return $this->queryCaches[$regionName];
|
||||
return $this->queryCaches[$regionName] ??= $this->cacheFactory->buildQueryCache($this->em, $regionName);
|
||||
}
|
||||
|
||||
/** @param mixed $identifier The entity identifier. */
|
||||
private function buildEntityCacheKey(ClassMetadata $metadata, $identifier): EntityCacheKey
|
||||
private function buildEntityCacheKey(ClassMetadata $metadata, mixed $identifier): EntityCacheKey
|
||||
{
|
||||
if (! is_array($identifier)) {
|
||||
$identifier = $this->toIdentifierArray($metadata, $identifier);
|
||||
@@ -272,11 +217,10 @@ class DefaultCache implements Cache
|
||||
return new EntityCacheKey($metadata->rootEntityName, $identifier);
|
||||
}
|
||||
|
||||
/** @param mixed $ownerIdentifier The identifier of the owning entity. */
|
||||
private function buildCollectionCacheKey(
|
||||
ClassMetadata $metadata,
|
||||
string $association,
|
||||
$ownerIdentifier
|
||||
mixed $ownerIdentifier,
|
||||
): CollectionCacheKey {
|
||||
if (! is_array($ownerIdentifier)) {
|
||||
$ownerIdentifier = $this->toIdentifierArray($metadata, $ownerIdentifier);
|
||||
@@ -285,21 +229,14 @@ class DefaultCache implements Cache
|
||||
return new CollectionCacheKey($metadata->rootEntityName, $association, $ownerIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $identifier The entity identifier.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function toIdentifierArray(ClassMetadata $metadata, $identifier): array
|
||||
/** @return array<string, mixed> */
|
||||
private function toIdentifierArray(ClassMetadata $metadata, mixed $identifier): array
|
||||
{
|
||||
if (is_object($identifier)) {
|
||||
$class = DefaultProxyClassNameResolver::getClass($identifier);
|
||||
if ($this->em->getMetadataFactory()->hasMetadataFor($class)) {
|
||||
$identifier = $this->uow->getSingleIdentifierValue($identifier);
|
||||
|
||||
if ($identifier === null) {
|
||||
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($class);
|
||||
}
|
||||
$identifier = $this->uow->getSingleIdentifierValue($identifier)
|
||||
?? throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
use Doctrine\Common\Cache\Cache as LegacyCache;
|
||||
use Doctrine\Common\Cache\Psr6\CacheAdapter;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Cache\Persister\Collection\CachedCollectionPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Collection\NonStrictReadWriteCachedCollectionPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Collection\ReadOnlyCachedCollectionPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Collection\ReadWriteCachedCollectionPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\NonStrictReadWriteCachedEntityPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\ReadOnlyCachedEntityPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\ReadWriteCachedEntityPersister;
|
||||
@@ -18,97 +17,53 @@ use Doctrine\ORM\Cache\Region\DefaultRegion;
|
||||
use Doctrine\ORM\Cache\Region\FileLockRegion;
|
||||
use Doctrine\ORM\Cache\Region\UpdateTimestampCache;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use TypeError;
|
||||
|
||||
use function assert;
|
||||
use function get_debug_type;
|
||||
use function sprintf;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
class DefaultCacheFactory implements CacheFactory
|
||||
{
|
||||
/** @var CacheItemPoolInterface */
|
||||
private $cacheItemPool;
|
||||
|
||||
/** @var RegionsConfiguration */
|
||||
private $regionsConfig;
|
||||
|
||||
/** @var TimestampRegion|null */
|
||||
private $timestampRegion;
|
||||
private TimestampRegion|null $timestampRegion = null;
|
||||
|
||||
/** @var Region[] */
|
||||
private $regions = [];
|
||||
private array $regions = [];
|
||||
|
||||
/** @var string|null */
|
||||
private $fileLockRegionDirectory;
|
||||
private string|null $fileLockRegionDirectory = null;
|
||||
|
||||
/** @param CacheItemPoolInterface $cacheItemPool */
|
||||
public function __construct(RegionsConfiguration $cacheConfig, $cacheItemPool)
|
||||
public function __construct(private readonly RegionsConfiguration $regionsConfig, private readonly CacheItemPoolInterface $cacheItemPool)
|
||||
{
|
||||
if ($cacheItemPool instanceof LegacyCache) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/9322',
|
||||
'Passing an instance of %s to %s is deprecated, pass a %s instead.',
|
||||
get_debug_type($cacheItemPool),
|
||||
__METHOD__,
|
||||
CacheItemPoolInterface::class
|
||||
);
|
||||
|
||||
$this->cacheItemPool = CacheAdapter::wrap($cacheItemPool);
|
||||
} elseif (! $cacheItemPool instanceof CacheItemPoolInterface) {
|
||||
throw new TypeError(sprintf(
|
||||
'%s: Parameter #2 is expected to be an instance of %s, got %s.',
|
||||
__METHOD__,
|
||||
CacheItemPoolInterface::class,
|
||||
get_debug_type($cacheItemPool)
|
||||
));
|
||||
} else {
|
||||
$this->cacheItemPool = $cacheItemPool;
|
||||
}
|
||||
|
||||
$this->regionsConfig = $cacheConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileLockRegionDirectory
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setFileLockRegionDirectory($fileLockRegionDirectory)
|
||||
public function setFileLockRegionDirectory(string $fileLockRegionDirectory): void
|
||||
{
|
||||
$this->fileLockRegionDirectory = (string) $fileLockRegionDirectory;
|
||||
$this->fileLockRegionDirectory = $fileLockRegionDirectory;
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getFileLockRegionDirectory()
|
||||
public function getFileLockRegionDirectory(): string|null
|
||||
{
|
||||
return $this->fileLockRegionDirectory;
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function setRegion(Region $region)
|
||||
public function setRegion(Region $region): void
|
||||
{
|
||||
$this->regions[$region->getName()] = $region;
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function setTimestampRegion(TimestampRegion $region)
|
||||
public function setTimestampRegion(TimestampRegion $region): void
|
||||
{
|
||||
$this->timestampRegion = $region;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata)
|
||||
public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata): CachedEntityPersister
|
||||
{
|
||||
assert($metadata->cache !== null);
|
||||
$region = $this->getRegion($metadata->cache);
|
||||
@@ -133,14 +88,14 @@ class DefaultCacheFactory implements CacheFactory
|
||||
throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildCachedCollectionPersister(EntityManagerInterface $em, CollectionPersister $persister, array $mapping)
|
||||
{
|
||||
assert(isset($mapping['cache']));
|
||||
$usage = $mapping['cache']['usage'];
|
||||
$region = $this->getRegion($mapping['cache']);
|
||||
public function buildCachedCollectionPersister(
|
||||
EntityManagerInterface $em,
|
||||
CollectionPersister $persister,
|
||||
AssociationMapping $mapping,
|
||||
): CachedCollectionPersister {
|
||||
assert(isset($mapping->cache));
|
||||
$usage = $mapping->cache['usage'];
|
||||
$region = $this->getRegion($mapping->cache);
|
||||
|
||||
if ($usage === ClassMetadata::CACHE_USAGE_READ_ONLY) {
|
||||
return new ReadOnlyCachedCollectionPersister($persister, $region, $em, $mapping);
|
||||
@@ -161,10 +116,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildQueryCache(EntityManagerInterface $em, $regionName = null)
|
||||
public function buildQueryCache(EntityManagerInterface $em, string|null $regionName = null): QueryCache
|
||||
{
|
||||
return new DefaultQueryCache(
|
||||
$em,
|
||||
@@ -172,23 +124,17 @@ class DefaultCacheFactory implements CacheFactory
|
||||
[
|
||||
'region' => $regionName ?: Cache::DEFAULT_QUERY_REGION_NAME,
|
||||
'usage' => ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE,
|
||||
]
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildCollectionHydrator(EntityManagerInterface $em, array $mapping)
|
||||
public function buildCollectionHydrator(EntityManagerInterface $em, AssociationMapping $mapping): CollectionHydrator
|
||||
{
|
||||
return new DefaultCollectionHydrator($em);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata)
|
||||
public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata): EntityHydrator
|
||||
{
|
||||
return new DefaultEntityHydrator($em);
|
||||
}
|
||||
@@ -196,7 +142,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getRegion(array $cache)
|
||||
public function getRegion(array $cache): Region
|
||||
{
|
||||
if (isset($this->regions[$cache['region']])) {
|
||||
return $this->regions[$cache['region']];
|
||||
@@ -213,7 +159,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
) {
|
||||
throw new LogicException(
|
||||
'If you want to use a "READ_WRITE" cache an implementation of "Doctrine\ORM\Cache\ConcurrentRegion" is required, ' .
|
||||
'The default implementation provided by doctrine is "Doctrine\ORM\Cache\Region\FileLockRegion" if you want to use it please provide a valid directory, DefaultCacheFactory#setFileLockRegionDirectory(). '
|
||||
'The default implementation provided by doctrine is "Doctrine\ORM\Cache\Region\FileLockRegion" if you want to use it please provide a valid directory, DefaultCacheFactory#setFileLockRegionDirectory(). ',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -224,10 +170,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
return $this->regions[$cache['region']] = $region;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getTimestampRegion()
|
||||
public function getTimestampRegion(): TimestampRegion
|
||||
{
|
||||
if ($this->timestampRegion === null) {
|
||||
$name = Cache::DEFAULT_TIMESTAMP_REGION_NAME;
|
||||
@@ -239,10 +182,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
return $this->timestampRegion;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function createCache(EntityManagerInterface $entityManager)
|
||||
public function createCache(EntityManagerInterface $entityManager): Cache
|
||||
{
|
||||
return new DefaultCache($entityManager);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user