mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 15:02:22 +01:00
Compare commits
985 Commits
3.3.0
...
old-protot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f7a7a58ae | ||
|
|
51063f502f | ||
|
|
e8c4d7b177 | ||
|
|
2a98a98cd3 | ||
|
|
01de90c141 | ||
|
|
dddd899322 | ||
|
|
72bc09926d | ||
|
|
49dd4c81d0 | ||
|
|
3460805efe | ||
|
|
722f4a86b3 | ||
|
|
1af6270095 | ||
|
|
9a8e1fb477 | ||
|
|
426ddb87a5 | ||
|
|
e7d2ac5277 | ||
|
|
d1302eabad | ||
|
|
5592535a56 | ||
|
|
a3e53b6a23 | ||
|
|
8c259ea5cb | ||
|
|
7de263c740 | ||
|
|
c7faafa192 | ||
|
|
f9a01ae9c4 | ||
|
|
c673a3daf2 | ||
|
|
e77091399a | ||
|
|
5b3f9ee3c7 | ||
|
|
eae20efb24 | ||
|
|
8d0c6a8746 | ||
|
|
ff65cae170 | ||
|
|
662a71b08a | ||
|
|
b06efe3408 | ||
|
|
f12d24cce7 | ||
|
|
6c7bb5a273 | ||
|
|
027344969e | ||
|
|
9190b73d72 | ||
|
|
df7e000ab0 | ||
|
|
533d09467f | ||
|
|
c9fc364c2c | ||
|
|
f2624e953e | ||
|
|
d528f70568 | ||
|
|
ccbe5ded06 | ||
|
|
21bc805dc9 | ||
|
|
b07393ece8 | ||
|
|
1488955ee3 | ||
|
|
5afceb9f10 | ||
|
|
4bfa22353a | ||
|
|
8c125b1065 | ||
|
|
0187cf53a2 | ||
|
|
05d54788fd | ||
|
|
a896421be2 | ||
|
|
4993c4ae9b | ||
|
|
376c5e1628 | ||
|
|
8601d9e525 | ||
|
|
4e988bb103 | ||
|
|
d606941284 | ||
|
|
fa7802b1c7 | ||
|
|
4f8027aa62 | ||
|
|
8220bb3ced | ||
|
|
57b72b1390 | ||
|
|
dabbfc70a9 | ||
|
|
9906068426 | ||
|
|
b115284932 | ||
|
|
6971e74b4a | ||
|
|
99b7d1e9d4 | ||
|
|
906c1465c2 | ||
|
|
04762015c6 | ||
|
|
f185a6f30f | ||
|
|
c35142f0ae | ||
|
|
3cda3f1ae3 | ||
|
|
9b8b5d9bf6 | ||
|
|
e1bb9e005d | ||
|
|
b9880bb30f | ||
|
|
becf73050e | ||
|
|
27f431c628 | ||
|
|
5b15a614a7 | ||
|
|
e747f7b9c4 | ||
|
|
2b0e49d173 | ||
|
|
b2f27f6310 | ||
|
|
3af277ca55 | ||
|
|
fb50004d58 | ||
|
|
6bf8d61ffc | ||
|
|
057a97d93c | ||
|
|
b5c4c7aaec | ||
|
|
9bd6c59285 | ||
|
|
a988d5808e | ||
|
|
8c451a6a66 | ||
|
|
96d22a6ca8 | ||
|
|
cc57d33e65 | ||
|
|
55b06ea6e1 | ||
|
|
35a9f20cc4 | ||
|
|
93a18c4b2e | ||
|
|
cdd64e7bcb | ||
|
|
6f07bbf482 | ||
|
|
31d9626b61 | ||
|
|
84170a735d | ||
|
|
b58a73aed3 | ||
|
|
d7a385105e | ||
|
|
de18565fdc | ||
|
|
cecd03d920 | ||
|
|
ce9d4c739d | ||
|
|
4ce24a4d66 | ||
|
|
d964eb6d50 | ||
|
|
4885e1c7fe | ||
|
|
42f08f6e3e | ||
|
|
23a0608c47 | ||
|
|
2b734405d2 | ||
|
|
0a34e4cef6 | ||
|
|
b8ebde9d07 | ||
|
|
a6a9455bdd | ||
|
|
6a6370b843 | ||
|
|
706de46ef1 | ||
|
|
b4338ad0d8 | ||
|
|
b762189c97 | ||
|
|
398ddb426f | ||
|
|
8bb91280ed | ||
|
|
87a6eef400 | ||
|
|
bf14cca3f3 | ||
|
|
5ef41cc923 | ||
|
|
01a4292b0d | ||
|
|
ebb53acda9 | ||
|
|
d1bcb1f0a0 | ||
|
|
04036f2244 | ||
|
|
6c0537325c | ||
|
|
1ab9d0b194 | ||
|
|
01f26bba53 | ||
|
|
d8f67108c9 | ||
|
|
2167536792 | ||
|
|
34933416d6 | ||
|
|
93d55d7fbc | ||
|
|
e8eaf8386d | ||
|
|
97e275ff31 | ||
|
|
5e3db26738 | ||
|
|
71393f7d5a | ||
|
|
ed61f0e647 | ||
|
|
11cfaf1233 | ||
|
|
08d34341c5 | ||
|
|
5ac805fc4d | ||
|
|
6615520bf7 | ||
|
|
541fb5968d | ||
|
|
22bf778711 | ||
|
|
322516337e | ||
|
|
d00453dc7e | ||
|
|
0cfc82ddae | ||
|
|
e2189307f4 | ||
|
|
b5ba7cddd5 | ||
|
|
2269510e8a | ||
|
|
4bc261aa46 | ||
|
|
d20dfcfa8d | ||
|
|
8744d1936f | ||
|
|
a5ed7ed89c | ||
|
|
2e9f533746 | ||
|
|
3e0b70b4dc | ||
|
|
1feac20c72 | ||
|
|
3cef55ec39 | ||
|
|
cd57cafe47 | ||
|
|
fa4d3bf799 | ||
|
|
7c9ab76896 | ||
|
|
c4d335ed10 | ||
|
|
5d6b5a706f | ||
|
|
62de42cfa1 | ||
|
|
f4cd3df403 | ||
|
|
929bcaf704 | ||
|
|
4594380bf4 | ||
|
|
d250ee79c5 | ||
|
|
4d7ffa0e4f | ||
|
|
65c13cf15b | ||
|
|
fc62b8f997 | ||
|
|
4d49b7778c | ||
|
|
c780f014b9 | ||
|
|
97248866ce | ||
|
|
dd8fcfd851 | ||
|
|
54beb95f68 | ||
|
|
d0b47f3aa9 | ||
|
|
8a79638faa | ||
|
|
6dc46e78cc | ||
|
|
0c2ccec915 | ||
|
|
72971fd438 | ||
|
|
cf94ec8136 | ||
|
|
d81284b506 | ||
|
|
b0825e490b | ||
|
|
91346c41e7 | ||
|
|
1760b4f655 | ||
|
|
e6a79ef9b2 | ||
|
|
15e49aff35 | ||
|
|
5839553356 | ||
|
|
5a9fe624b6 | ||
|
|
b40c2c6b78 | ||
|
|
36aeed46f8 | ||
|
|
ce76688865 | ||
|
|
2f520244df | ||
|
|
a59ae2e5e3 | ||
|
|
2431bc4076 | ||
|
|
6219f72dc6 | ||
|
|
6ff677d2ab | ||
|
|
730543f17e | ||
|
|
1157a845ec | ||
|
|
f62cccbfd1 | ||
|
|
a20373449c | ||
|
|
362ccb4d24 | ||
|
|
2df74442a0 | ||
|
|
b9feae09c0 | ||
|
|
5c773554dd | ||
|
|
bfa65207b0 | ||
|
|
53b7f9d6f8 | ||
|
|
afd79c189f | ||
|
|
8d83dfa05c | ||
|
|
d311888ef9 | ||
|
|
46a880ac0f | ||
|
|
f4c84897ba | ||
|
|
890d544161 | ||
|
|
87ac04d10e | ||
|
|
7b261676d2 | ||
|
|
da69537408 | ||
|
|
d6ec5a3861 | ||
|
|
82972f697a | ||
|
|
0538f93913 | ||
|
|
0977c8926a | ||
|
|
2500025ee0 | ||
|
|
5e57fcf9ee | ||
|
|
7d34b64885 | ||
|
|
2245ad2751 | ||
|
|
68635faf83 | ||
|
|
6f3ca36b08 | ||
|
|
cb3d4caec2 | ||
|
|
863dabe73f | ||
|
|
5ac6a83560 | ||
|
|
7ad0dcbd88 | ||
|
|
1d649ad952 | ||
|
|
c4c489e7d9 | ||
|
|
f319f10d2e | ||
|
|
1b5bd54290 | ||
|
|
847d5bc666 | ||
|
|
e540c4a8c4 | ||
|
|
b361aa438e | ||
|
|
396b7f6718 | ||
|
|
39c5dcb79d | ||
|
|
c17339e64e | ||
|
|
4179a9676f | ||
|
|
e16500ae72 | ||
|
|
f7309d898f | ||
|
|
28f78a0e60 | ||
|
|
8bcd13f5c7 | ||
|
|
079f398bd2 | ||
|
|
035d9edf95 | ||
|
|
984641d20f | ||
|
|
f584bae121 | ||
|
|
9ce1f0d13f | ||
|
|
0cbc87a888 | ||
|
|
f4ed9534cc | ||
|
|
497bbfcec8 | ||
|
|
2d194dee53 | ||
|
|
76bc652d88 | ||
|
|
d98d8bb0c2 | ||
|
|
55f238f790 | ||
|
|
2726636e55 | ||
|
|
305308278a | ||
|
|
c5191fa3f9 | ||
|
|
e5b88b3fe5 | ||
|
|
fae518aaef | ||
|
|
5756a7fc73 | ||
|
|
3d9a4b8ed8 | ||
|
|
12b28571f5 | ||
|
|
ec573d1b75 | ||
|
|
347e33e345 | ||
|
|
6ad04f7d51 | ||
|
|
f6cc1290d2 | ||
|
|
bbd2460e06 | ||
|
|
9019297c55 | ||
|
|
1ed8bc705f | ||
|
|
063c402dd5 | ||
|
|
46b695d67c | ||
|
|
b881f14b4d | ||
|
|
e2ea883786 | ||
|
|
77e3e5c96c | ||
|
|
c7718efff8 | ||
|
|
42dc7830d7 | ||
|
|
10aac31e8d | ||
|
|
16a1877d58 | ||
|
|
6d282cdc43 | ||
|
|
491a382f91 | ||
|
|
563833340a | ||
|
|
b128753960 | ||
|
|
79f17d02f7 | ||
|
|
28e7e5392c | ||
|
|
46d342d0cf | ||
|
|
3fd15f0cc7 | ||
|
|
8c16a20fd2 | ||
|
|
6ec3ff02b7 | ||
|
|
39634ad91e | ||
|
|
1e60554556 | ||
|
|
ed62eb8672 | ||
|
|
0998a46480 | ||
|
|
50b35fc6aa | ||
|
|
9f4a6922a4 | ||
|
|
9d30fdf9c7 | ||
|
|
d6da264404 | ||
|
|
fbca71af39 | ||
|
|
25b5aa6570 | ||
|
|
c030a213f5 | ||
|
|
2f0b5a503a | ||
|
|
cf13e0d10f | ||
|
|
c2c6c623f3 | ||
|
|
5db00b15d2 | ||
|
|
d2a679a780 | ||
|
|
398e1128f2 | ||
|
|
4132f408df | ||
|
|
c0453603e1 | ||
|
|
d9b0ab7b41 | ||
|
|
119610b81e | ||
|
|
59b5e5ae6c | ||
|
|
b7a2434ef7 | ||
|
|
82796e8711 | ||
|
|
145d5139cb | ||
|
|
ea12b6f82f | ||
|
|
ae166d773b | ||
|
|
de31b2abc9 | ||
|
|
b1bec0ee6c | ||
|
|
9b8f9786d9 | ||
|
|
fdfd2de658 | ||
|
|
fcf012075a | ||
|
|
2ac4142e5f | ||
|
|
967d34473e | ||
|
|
dd3c5c8dc3 | ||
|
|
c4c59df1b4 | ||
|
|
ac92e9f8d3 | ||
|
|
54833ec31b | ||
|
|
1b6a1ee315 | ||
|
|
6fb6b9d32d | ||
|
|
1f0660dcfd | ||
|
|
1afbf141fc | ||
|
|
d372416350 | ||
|
|
f6581657de | ||
|
|
935f3606a2 | ||
|
|
cf8f71e56e | ||
|
|
b01bb234f1 | ||
|
|
0bb3624404 | ||
|
|
5f6d758ddc | ||
|
|
88f6485dea | ||
|
|
15a149c9f6 | ||
|
|
d199c92c95 | ||
|
|
c98f40251a | ||
|
|
41292e85df | ||
|
|
dfd9d1b9e7 | ||
|
|
3570057358 | ||
|
|
7eb6e00940 | ||
|
|
f5a0b568f3 | ||
|
|
64dffe0ede | ||
|
|
8836954787 | ||
|
|
3ce3aa978f | ||
|
|
31e19239d1 | ||
|
|
86dc7f46a1 | ||
|
|
042cff9f22 | ||
|
|
5f7210b441 | ||
|
|
3ac8930613 | ||
|
|
29c86ff5e9 | ||
|
|
43c252458b | ||
|
|
a754acb44a | ||
|
|
1a14c85c54 | ||
|
|
9f572d4a4a | ||
|
|
1bbdc5b597 | ||
|
|
1b845c7c84 | ||
|
|
f3c72dc2d1 | ||
|
|
e5ef92555b | ||
|
|
2be99346b1 | ||
|
|
ec508aff81 | ||
|
|
9f8af8ee4c | ||
|
|
a0cd82b9bc | ||
|
|
89e39b8192 | ||
|
|
554489c7e8 | ||
|
|
bb1fe1d6c9 | ||
|
|
cf548e9b02 | ||
|
|
70d6150412 | ||
|
|
09d3226db9 | ||
|
|
449a6ead69 | ||
|
|
99c6a510e6 | ||
|
|
d00cabafbf | ||
|
|
61778f95d4 | ||
|
|
f100bcec5f | ||
|
|
f4ef0a1d5f | ||
|
|
bbc1ed93b1 | ||
|
|
2343a58ffe | ||
|
|
bd6ae78940 | ||
|
|
a0e414ba57 | ||
|
|
d952c1247e | ||
|
|
07a5861503 | ||
|
|
45c3805edf | ||
|
|
aadf295b9a | ||
|
|
91acb40433 | ||
|
|
5fde371852 | ||
|
|
67fe52f36c | ||
|
|
849d0ac3cc | ||
|
|
3e1d124f23 | ||
|
|
408704b845 | ||
|
|
f6fde34297 | ||
|
|
b0cade71ba | ||
|
|
46ea9d1b52 | ||
|
|
9e57633204 | ||
|
|
faab3225bd | ||
|
|
86f3b6afe7 | ||
|
|
3d3f8b7a8b | ||
|
|
98b9a7dd34 | ||
|
|
6e4daa3997 | ||
|
|
9179aeb343 | ||
|
|
0b04aa9000 | ||
|
|
e6fa8622d7 | ||
|
|
df794b648f | ||
|
|
695edb9fdc | ||
|
|
d376afb53c | ||
|
|
0822f8a456 | ||
|
|
bbf0c0355d | ||
|
|
cbfd52e2fe | ||
|
|
6cc43d884b | ||
|
|
e7e4247395 | ||
|
|
3035470d06 | ||
|
|
92445d095e | ||
|
|
4e2a2975c2 | ||
|
|
e479e0daf4 | ||
|
|
f41c6083c3 | ||
|
|
ab03f16e03 | ||
|
|
f351f5ab3b | ||
|
|
6aabdec97b | ||
|
|
df0cc3452b | ||
|
|
65ebadec49 | ||
|
|
38032c166e | ||
|
|
35d8c87fad | ||
|
|
ec265bf3a3 | ||
|
|
9f7df926cf | ||
|
|
09266d5361 | ||
|
|
46e27d0cde | ||
|
|
a31ce59097 | ||
|
|
0a00a3f044 | ||
|
|
e64820f21e | ||
|
|
0b7d878cd3 | ||
|
|
0151657ea1 | ||
|
|
06ffd851c7 | ||
|
|
97534219db | ||
|
|
df94e79ece | ||
|
|
4509c770da | ||
|
|
bc0c0986c5 | ||
|
|
e0ee4b1ad7 | ||
|
|
bcc9da2f4a | ||
|
|
e351954ec6 | ||
|
|
1a435b2de4 | ||
|
|
8b66f88661 | ||
|
|
1c7770794b | ||
|
|
692a6d941f | ||
|
|
af0b0dbdc3 | ||
|
|
193563e3ed | ||
|
|
eef6308b19 | ||
|
|
1751560970 | ||
|
|
bc44d8bceb | ||
|
|
9d27f22ee6 | ||
|
|
e2d077c62d | ||
|
|
7d37fe0060 | ||
|
|
8cc949808a | ||
|
|
7d93958cba | ||
|
|
43eebb270f | ||
|
|
52769aa118 | ||
|
|
762fdd166e | ||
|
|
2de6c807c8 | ||
|
|
c181836233 | ||
|
|
a346c3f84e | ||
|
|
93558d4786 | ||
|
|
579f084202 | ||
|
|
79282317d1 | ||
|
|
da7288e541 | ||
|
|
c14dbc2409 | ||
|
|
e559f823ea | ||
|
|
63d5a07f06 | ||
|
|
d72936caab | ||
|
|
e98654342f | ||
|
|
217d28f767 | ||
|
|
e6557cbee3 | ||
|
|
b44ba04d1f | ||
|
|
ee4e267595 | ||
|
|
13f173a126 | ||
|
|
7fc285dd32 | ||
|
|
346d2380bd | ||
|
|
d791f7f6ca | ||
|
|
d455a023f8 | ||
|
|
2ccf239628 | ||
|
|
d559c933e1 | ||
|
|
45ea294233 | ||
|
|
d4e02aadc0 | ||
|
|
4476ec467d | ||
|
|
ddb3cd17bd | ||
|
|
3e8aa71554 | ||
|
|
82c91a2606 | ||
|
|
2cd3f1f909 | ||
|
|
7968dfae76 | ||
|
|
577a590987 | ||
|
|
9e122e7a89 | ||
|
|
9855f56bd5 | ||
|
|
ea95507da4 | ||
|
|
98f51794d8 | ||
|
|
e3360573ab | ||
|
|
8a75d3d6f6 | ||
|
|
142fe6a01d | ||
|
|
872571f4a5 | ||
|
|
63cb8018dd | ||
|
|
a0071b17de | ||
|
|
f2fab1ac36 | ||
|
|
e3936d0411 | ||
|
|
37d4927118 | ||
|
|
dc7fca409c | ||
|
|
cb590fc17e | ||
|
|
ccdf44d9f1 | ||
|
|
1fcb7b5005 | ||
|
|
f567970a85 | ||
|
|
386fc0b010 | ||
|
|
51300d63f4 | ||
|
|
0d1684873b | ||
|
|
066d135b21 | ||
|
|
0cef113a5b | ||
|
|
45a8a0c412 | ||
|
|
afa2768f28 | ||
|
|
34ca9fc86e | ||
|
|
75e25c1e7c | ||
|
|
4952f82e0b | ||
|
|
2bc1778f80 | ||
|
|
bacb18bd42 | ||
|
|
0c7745a0a6 | ||
|
|
f94e5eff85 | ||
|
|
aef6f17d9c | ||
|
|
654f3e30ce | ||
|
|
5221e999c2 | ||
|
|
66cf6d9f58 | ||
|
|
4108b05401 | ||
|
|
b227daa982 | ||
|
|
f138011843 | ||
|
|
0ec1e1ceb2 | ||
|
|
bd8dd42336 | ||
|
|
0c0b3e9682 | ||
|
|
557d7600b2 | ||
|
|
37db536a66 | ||
|
|
05e7049c44 | ||
|
|
b9c3d3dfcf | ||
|
|
b497a079d3 | ||
|
|
0b4276f738 | ||
|
|
c236f3e846 | ||
|
|
3dab1396c5 | ||
|
|
17c06f5d04 | ||
|
|
3f3996cc47 | ||
|
|
a1037fee9b | ||
|
|
b7776e1ca3 | ||
|
|
34b8b4f086 | ||
|
|
3c9172bad8 | ||
|
|
d6332695fd | ||
|
|
8796f460a7 | ||
|
|
36c4e7df43 | ||
|
|
67d275f451 | ||
|
|
088d3b0217 | ||
|
|
96ed87b2a5 | ||
|
|
a8235bcfc3 | ||
|
|
d2a70d030e | ||
|
|
0bd7ffc266 | ||
|
|
5411108c83 | ||
|
|
7681b89137 | ||
|
|
e42a888dad | ||
|
|
8be1e3f1d3 | ||
|
|
9452f024c7 | ||
|
|
f5b434af25 | ||
|
|
81b1f277a0 | ||
|
|
dcdc8a5a45 | ||
|
|
53ace82169 | ||
|
|
83a312187b | ||
|
|
e48c033839 | ||
|
|
f53b0399f3 | ||
|
|
a064d9fd17 | ||
|
|
21d521e187 | ||
|
|
a67b533d00 | ||
|
|
ff6541503e | ||
|
|
5f6cdb150f | ||
|
|
2537c7f52b | ||
|
|
a0d8a05a7b | ||
|
|
d1fedbf05d | ||
|
|
3102d97173 | ||
|
|
77edd5310c | ||
|
|
24114afb14 | ||
|
|
d1cf9a7617 | ||
|
|
9800ca7657 | ||
|
|
cfba9efd4e | ||
|
|
85ab80a039 | ||
|
|
d1e561eb13 | ||
|
|
cd590f2f33 | ||
|
|
d4ed772b07 | ||
|
|
324dfa4e23 | ||
|
|
391e101cba | ||
|
|
63b8c4db3b | ||
|
|
fee52ddcfa | ||
|
|
9ad525a34f | ||
|
|
d0f998ce45 | ||
|
|
1ffde77a87 | ||
|
|
f4c84883ec | ||
|
|
46836d38ac | ||
|
|
57ee603fec | ||
|
|
cebe3005a6 | ||
|
|
111898a269 | ||
|
|
a0342c87dd | ||
|
|
7da3c2d678 | ||
|
|
81ace85f29 | ||
|
|
b01a9a3ea1 | ||
|
|
945e98dcaf | ||
|
|
52a5ac4bb8 | ||
|
|
75c1cfc0f4 | ||
|
|
489eb411a3 | ||
|
|
2bc55103fd | ||
|
|
368e10a4df | ||
|
|
5d496f145e | ||
|
|
b7f2147015 | ||
|
|
2cb9df201f | ||
|
|
fc8942786d | ||
|
|
a580ef9911 | ||
|
|
d9e419f4a2 | ||
|
|
7b574c25d2 | ||
|
|
8d3736ddae | ||
|
|
d05e8e8285 | ||
|
|
e376962ab5 | ||
|
|
5798192e61 | ||
|
|
e4ce75a8dc | ||
|
|
c4eb2b016d | ||
|
|
ede29990e3 | ||
|
|
01bb774273 | ||
|
|
29beae2daa | ||
|
|
28941f37cd | ||
|
|
d1ceb55c68 | ||
|
|
575639cb49 | ||
|
|
55863d6101 | ||
|
|
aa9f11e11d | ||
|
|
cd1d527b57 | ||
|
|
a25fdb56fc | ||
|
|
36e6526cd9 | ||
|
|
059d7ad08d | ||
|
|
9b91c2dc6c | ||
|
|
c0be5ab403 | ||
|
|
83e7e07464 | ||
|
|
0ce63c0160 | ||
|
|
45b7cd0d01 | ||
|
|
67d6da5226 | ||
|
|
4e06a15dc8 | ||
|
|
3411950c6c | ||
|
|
8315460083 | ||
|
|
e7ace6f8a0 | ||
|
|
52cc90b205 | ||
|
|
f00c433746 | ||
|
|
62d664b6b1 | ||
|
|
7f292d3ee0 | ||
|
|
4c0f6fc513 | ||
|
|
5eea2a6944 | ||
|
|
a30a0b9cb6 | ||
|
|
566b777834 | ||
|
|
cae465b850 | ||
|
|
e3592b5ca7 | ||
|
|
08661094fc | ||
|
|
4e7d5c84e3 | ||
|
|
aeff25f7df | ||
|
|
4810a308eb | ||
|
|
092457fb2e | ||
|
|
8af59cd3de | ||
|
|
ded2e05be7 | ||
|
|
07844788ca | ||
|
|
fe0df7f47d | ||
|
|
076c184d7e | ||
|
|
449111cecb | ||
|
|
567879d932 | ||
|
|
58f11eaec7 | ||
|
|
d79c6672c5 | ||
|
|
f2645f0157 | ||
|
|
a28197a321 | ||
|
|
1e40657862 | ||
|
|
d1b2f62b3c | ||
|
|
4e42bfc45f | ||
|
|
245e1dc2ea | ||
|
|
82062b6df3 | ||
|
|
3e3b7cf1f2 | ||
|
|
1e7a0574fe | ||
|
|
df583fa2cb | ||
|
|
c3ffd0cd22 | ||
|
|
a81754342a | ||
|
|
5ae48b3799 | ||
|
|
05463f43ca | ||
|
|
d1cb92e497 | ||
|
|
820ffc5f2a | ||
|
|
e510015692 | ||
|
|
68cf7661ad | ||
|
|
1ccc4ffcfc | ||
|
|
e645a67ab6 | ||
|
|
f26a43c58a | ||
|
|
ee47d37f7c | ||
|
|
d18144bef1 | ||
|
|
f0d7fa5344 | ||
|
|
7e6e89a8f7 | ||
|
|
dd4684d72e | ||
|
|
b33abd3c14 | ||
|
|
3d204ac1f4 | ||
|
|
247e7ec711 | ||
|
|
1cc5b8ee91 | ||
|
|
012a4e2668 | ||
|
|
1308662ce5 | ||
|
|
fa5292bf7c | ||
|
|
310bed63b1 | ||
|
|
d36438b21e | ||
|
|
fea7f52362 | ||
|
|
5ef465820c | ||
|
|
51da2adf74 | ||
|
|
79f51bf51c | ||
|
|
2045869985 | ||
|
|
7141cb7f9e | ||
|
|
662a8e43f0 | ||
|
|
c6bb99113e | ||
|
|
1f0d55686f | ||
|
|
7fab08bc70 | ||
|
|
642574e17d | ||
|
|
bd53f1980c | ||
|
|
291457f1aa | ||
|
|
a68c3b4239 | ||
|
|
b5b3e30d6a | ||
|
|
b45c4fb441 | ||
|
|
3c9737b3d6 | ||
|
|
5f90854ea6 | ||
|
|
3127a2a787 | ||
|
|
8e46fcda86 | ||
|
|
6d4168f189 | ||
|
|
6dabfa3984 | ||
|
|
f5a95710a4 | ||
|
|
5de0435d99 | ||
|
|
a6526abadb | ||
|
|
ee6b58be97 | ||
|
|
37c0225f7d | ||
|
|
4c95f57996 | ||
|
|
e5cb4e5455 | ||
|
|
165f395aef | ||
|
|
911ec07f33 | ||
|
|
784653ad6f | ||
|
|
434bb7f68d | ||
|
|
a13dd43f96 | ||
|
|
30463c452b | ||
|
|
e4c9057a47 | ||
|
|
8f993266fe | ||
|
|
c14bc2f34f | ||
|
|
065fdfdde6 | ||
|
|
854d069627 | ||
|
|
909993ae3e | ||
|
|
3f29ff74ee | ||
|
|
5dfe5069ab | ||
|
|
df397e1d68 | ||
|
|
98d93ad74a | ||
|
|
f26bfadaef | ||
|
|
b80a676e9a | ||
|
|
16a085ff67 | ||
|
|
f6b2e3fe63 | ||
|
|
cf4d4cc260 | ||
|
|
06a94e2688 | ||
|
|
edf3335e60 | ||
|
|
4fb9f48f1a | ||
|
|
8b40c6ef90 | ||
|
|
0f36bc769f | ||
|
|
3a93402720 | ||
|
|
49d553808d | ||
|
|
fa563476a3 | ||
|
|
c6116d4e55 | ||
|
|
f2cf2ea203 | ||
|
|
8be6b5862e | ||
|
|
368a2f26e8 | ||
|
|
eaebc57f7c | ||
|
|
4e2d1809ba | ||
|
|
12d36551c5 | ||
|
|
2417eb8e3f | ||
|
|
1cda08cf11 | ||
|
|
1aeda7ffdb | ||
|
|
5df39072db | ||
|
|
b37e8d6fb7 | ||
|
|
48e8fdeb25 | ||
|
|
0b70c8f239 | ||
|
|
0768fa502f | ||
|
|
0e8fad8fc3 | ||
|
|
7fca3b151b | ||
|
|
9a6eb7ddb8 | ||
|
|
61845d390c | ||
|
|
d248dc4084 | ||
|
|
499147f256 | ||
|
|
cb530da8db | ||
|
|
aa155c8cd4 | ||
|
|
b3a27357d6 | ||
|
|
58f212d85d | ||
|
|
ca83c24d90 | ||
|
|
5b5caf7c58 | ||
|
|
c39a8d2fac | ||
|
|
e9c7a49961 | ||
|
|
e409a16d2c | ||
|
|
37eaea65d9 | ||
|
|
0b33ee53c1 | ||
|
|
a84cac9787 | ||
|
|
53ae602eeb | ||
|
|
4e17974139 | ||
|
|
7b15963025 | ||
|
|
69893797f7 | ||
|
|
1c9221df0d | ||
|
|
fb7f3bc01c | ||
|
|
578b1d3dd2 | ||
|
|
6b7a81690d | ||
|
|
808e07d97c | ||
|
|
d5653b22b9 | ||
|
|
e626d1ca95 | ||
|
|
06d56de0e9 | ||
|
|
88fcda9a02 | ||
|
|
17e7c2a42e | ||
|
|
6a9b817717 | ||
|
|
38c21f361f | ||
|
|
81ced236e5 | ||
|
|
fa3d2f9102 | ||
|
|
2db53bdbfa | ||
|
|
4a427b8b7b | ||
|
|
802c94fa05 | ||
|
|
91ae8e25d3 | ||
|
|
e48f537590 | ||
|
|
b8d7d82b73 | ||
|
|
ff7a18aeb9 | ||
|
|
ea6723eca8 | ||
|
|
f954914db1 | ||
|
|
9c562b24d5 | ||
|
|
f2d1048e15 | ||
|
|
abace42835 | ||
|
|
c0da37443b | ||
|
|
50efd6827b | ||
|
|
f4d031ca4d | ||
|
|
df33284028 | ||
|
|
a5cf75c3ab | ||
|
|
f8061618ef | ||
|
|
c197fb4ffe | ||
|
|
88a3f9bfb9 | ||
|
|
6cf0d72587 | ||
|
|
e0e90ce5c4 | ||
|
|
bd50902da0 | ||
|
|
ef030bbfc2 | ||
|
|
c112f27362 | ||
|
|
6cb027cd4f | ||
|
|
4c63a562be | ||
|
|
af5281b6d7 | ||
|
|
c948b3e2e5 | ||
|
|
e204b96d1d | ||
|
|
ea2ec0740f | ||
|
|
2725e2e705 | ||
|
|
f40ea70ac4 | ||
|
|
2366b43b1e | ||
|
|
6af9320dc8 | ||
|
|
7f1eedaaa6 | ||
|
|
46341a6e17 | ||
|
|
d7fd378b3c | ||
|
|
8688b1626c | ||
|
|
bd1ff948b8 | ||
|
|
1505ef2c99 | ||
|
|
213ea60923 | ||
|
|
e9406531a9 | ||
|
|
8b78a64e8a | ||
|
|
3bc4126246 | ||
|
|
e2c0f2b7c2 | ||
|
|
bd5fb5e7d1 | ||
|
|
fee3cd5367 | ||
|
|
081986eae2 | ||
|
|
941cf83a1a | ||
|
|
656290d156 | ||
|
|
b80b30b10b | ||
|
|
946e35c2ec | ||
|
|
ad8d16a31f | ||
|
|
c404aa66ac | ||
|
|
9110eebadb | ||
|
|
830985e259 | ||
|
|
87f1aa7875 | ||
|
|
41919e69dc | ||
|
|
03927e8cff | ||
|
|
000759790e | ||
|
|
99b92853cf | ||
|
|
e676d6c30e | ||
|
|
6841c919fa | ||
|
|
347e2a55f4 | ||
|
|
607a6691be | ||
|
|
c1a46219ed | ||
|
|
0c16cce8bc | ||
|
|
6286b69fbd | ||
|
|
4d12dbf30e | ||
|
|
045e8b8782 | ||
|
|
61e57aa5f4 | ||
|
|
aad3576953 | ||
|
|
1d3af800df | ||
|
|
51d6ff6c7f | ||
|
|
56e93548f5 | ||
|
|
e28086d23b | ||
|
|
e805498f95 | ||
|
|
f80561edc9 | ||
|
|
c98970b7c3 | ||
|
|
75f5260a89 | ||
|
|
d070fc62a7 | ||
|
|
6b28e558cf | ||
|
|
4bbe058e02 | ||
|
|
242f3f0343 | ||
|
|
edd92d8ae6 | ||
|
|
9205519217 | ||
|
|
a9821eae7b | ||
|
|
97378f7cdc | ||
|
|
377d2dc190 | ||
|
|
7e62f21a23 | ||
|
|
e18d5da193 | ||
|
|
ffbb835b05 | ||
|
|
fbf081814c | ||
|
|
9615382856 | ||
|
|
8e6cf48908 | ||
|
|
15d45cecbd | ||
|
|
7403ca0ce3 | ||
|
|
30ec740011 | ||
|
|
792d9cbe61 | ||
|
|
2ed4447b64 | ||
|
|
ef378ccd94 | ||
|
|
7007d4c0fc | ||
|
|
3cb30021c5 | ||
|
|
169ff83b11 | ||
|
|
5908cac18f | ||
|
|
b7f0567273 | ||
|
|
8a4ca55e8b | ||
|
|
e5b216c523 | ||
|
|
2f7f3fbf59 | ||
|
|
e7aa7d5b39 | ||
|
|
36d8b337af | ||
|
|
13e53d12a0 | ||
|
|
392850cf2f | ||
|
|
47bfece156 | ||
|
|
cda24bf8c3 | ||
|
|
b4df1ec96f | ||
|
|
246349b8e5 | ||
|
|
48da5b8cc6 | ||
|
|
af259157a2 | ||
|
|
0dcb732a3c | ||
|
|
f54f4a5b97 | ||
|
|
1e6d30da83 | ||
|
|
4e2d754ae7 | ||
|
|
e1ee929dad | ||
|
|
6446a9626c | ||
|
|
6fb1bdefec | ||
|
|
bd1c03fb15 | ||
|
|
6aa543978c | ||
|
|
b9d38d94b9 | ||
|
|
7c39836ce6 | ||
|
|
c498805966 | ||
|
|
32c28ffe90 | ||
|
|
a5375e3a1a | ||
|
|
c63a0ab56a | ||
|
|
a77cdaf5eb | ||
|
|
d2835395ad | ||
|
|
917e5ea8c7 | ||
|
|
8b856c799a | ||
|
|
b97f85af46 | ||
|
|
80b05a889a | ||
|
|
53ba16d747 | ||
|
|
1d2b5962b0 | ||
|
|
8d1e7b3fd5 | ||
|
|
975ebef19d | ||
|
|
548865b621 | ||
|
|
db187bb8a9 | ||
|
|
5e29c80ca3 | ||
|
|
08e2f779e3 | ||
|
|
7c4e5444d6 | ||
|
|
169ac6d13d | ||
|
|
fe1e3c0fa9 | ||
|
|
97f077a120 | ||
|
|
0a0203fa43 | ||
|
|
ecc711cb73 | ||
|
|
83b91b81ca | ||
|
|
9f6dc1d8a8 | ||
|
|
ac34415bf3 | ||
|
|
ed40837b45 | ||
|
|
2f69aece64 | ||
|
|
516f2e1083 | ||
|
|
a5d136d5e6 | ||
|
|
db7ecb2721 | ||
|
|
6adcb5ed68 | ||
|
|
ff6d24d104 | ||
|
|
c73f4cff8e | ||
|
|
aac92169b7 | ||
|
|
4f76085494 | ||
|
|
c8ddbddc19 | ||
|
|
e4d363122a | ||
|
|
a568530c6b | ||
|
|
55596e9d07 | ||
|
|
6eb2ca4689 | ||
|
|
8731c086f9 | ||
|
|
2d36a5da6b | ||
|
|
56b245e4a6 |
49
.doctrine-project.json
Normal file
49
.doctrine-project.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"active": true,
|
||||
"name": "Object Relational Mapper",
|
||||
"shortName": "ORM",
|
||||
"slug": "orm",
|
||||
"docsSlug": "doctrine-orm",
|
||||
"versions": [
|
||||
{
|
||||
"name": "3.0",
|
||||
"branchName": "master",
|
||||
"slug": "latest",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.8",
|
||||
"branchName": "2.8.x",
|
||||
"slug": "2.8",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.7",
|
||||
"branchName": "2.7",
|
||||
"slug": "2.7",
|
||||
"current": true,
|
||||
"aliases": [
|
||||
"current",
|
||||
"stable"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -11,3 +11,4 @@ CONTRIBUTING.md export-ignore
|
||||
phpunit.xml.dist export-ignore
|
||||
run-all.sh export-ignore
|
||||
phpcs.xml.dist export-ignore
|
||||
composer.lock export-ignore
|
||||
|
||||
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
patreon: phpdoctrine
|
||||
tidelift: packagist/doctrine/orm
|
||||
custom: https://www.doctrine-project.org/sponsorship.html
|
||||
37
.github/ISSUE_TEMPLATE/BC_Break.md
vendored
Normal file
37
.github/ISSUE_TEMPLATE/BC_Break.md
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: 💥 BC Break
|
||||
about: Have you encountered an issue during upgrade? 💣
|
||||
---
|
||||
|
||||
<!--
|
||||
Before reporting a BC break, please consult the upgrading document to make sure it's not an expected change: https://github.com/doctrine/orm/blob/master/UPGRADE.md
|
||||
-->
|
||||
|
||||
### BC Break Report
|
||||
|
||||
<!-- Fill in the relevant information below to help triage your issue. -->
|
||||
|
||||
| Q | A
|
||||
|------------ | ------
|
||||
| BC Break | yes
|
||||
| Version | x.y.z
|
||||
|
||||
#### Summary
|
||||
|
||||
<!-- Provide a summary describing the problem you are experiencing. -->
|
||||
|
||||
#### Previous behavior
|
||||
|
||||
<!-- What was the previous (working) behavior? -->
|
||||
|
||||
#### Current behavior
|
||||
|
||||
<!-- What is the current (broken) behavior? -->
|
||||
|
||||
#### How to reproduce
|
||||
|
||||
<!--
|
||||
Provide steps to reproduce the BC break.
|
||||
If possible, also add a code snippet with relevant configuration, entity mappings, DQL etc.
|
||||
Adding a failing Unit or Functional Test would help us a lot - you can submit it in a Pull Request separately, referencing this bug report.
|
||||
-->
|
||||
34
.github/ISSUE_TEMPLATE/Bug.md
vendored
Normal file
34
.github/ISSUE_TEMPLATE/Bug.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: 🐞 Bug Report
|
||||
about: Something is broken? 🔨
|
||||
---
|
||||
|
||||
### Bug Report
|
||||
|
||||
<!-- Fill in the relevant information below to help triage your issue. -->
|
||||
|
||||
| Q | A
|
||||
|------------ | ------
|
||||
| BC Break | yes/no
|
||||
| Version | x.y.z
|
||||
|
||||
#### Summary
|
||||
|
||||
<!-- Provide a summary describing the problem you are experiencing. -->
|
||||
|
||||
#### Current behavior
|
||||
|
||||
<!-- What is the current (buggy) behavior? -->
|
||||
|
||||
#### How to reproduce
|
||||
|
||||
<!--
|
||||
Provide steps to reproduce the bug.
|
||||
If possible, also add a code snippet with relevant configuration, entity mappings, DQL etc.
|
||||
Adding a failing Unit or Functional Test would help us a lot - you can submit one in a Pull Request separately, referencing this bug report.
|
||||
-->
|
||||
|
||||
#### Expected behavior
|
||||
|
||||
<!-- What was the expected (correct) behavior? -->
|
||||
|
||||
18
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
Normal file
18
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: 🎉 Feature Request
|
||||
about: You have a neat idea that should be implemented? 🎩
|
||||
---
|
||||
|
||||
### Feature Request
|
||||
|
||||
<!-- Fill in the relevant information below to help triage your issue. -->
|
||||
|
||||
| Q | A
|
||||
|------------ | ------
|
||||
| New Feature | yes
|
||||
| RFC | yes/no
|
||||
| BC Break | yes/no
|
||||
|
||||
#### Summary
|
||||
|
||||
<!-- Provide a summary of the feature you would like to see implemented. -->
|
||||
20
.github/ISSUE_TEMPLATE/Support_Question.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/Support_Question.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: ❓ Support Question
|
||||
about: Have a problem that you can't figure out? 🤔
|
||||
---
|
||||
|
||||
<!-- Fill in the relevant information below to help triage your issue. -->
|
||||
|
||||
| Q | A
|
||||
|------------ | -----
|
||||
| Version | x.y.z
|
||||
|
||||
<!--
|
||||
Before asking question here, please try asking on Gitter or Slack first.
|
||||
Find out more about Doctrine support channels here: https://www.doctrine-project.org/community/
|
||||
Keep in mind that GitHub is primarily an issue tracker.
|
||||
-->
|
||||
|
||||
### Support Question
|
||||
|
||||
<!-- Describe the issue you are facing here. -->
|
||||
19
.github/PULL_REQUEST_TEMPLATE/Failing_Test.md
vendored
Normal file
19
.github/PULL_REQUEST_TEMPLATE/Failing_Test.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: 🐞 Failing Test
|
||||
about: You found a bug and have a failing Unit or Functional test? 🔨
|
||||
---
|
||||
|
||||
### Failing Test
|
||||
|
||||
<!-- Fill in the relevant information below to help triage your issue. -->
|
||||
|
||||
| Q | A
|
||||
|------------ | ------
|
||||
| BC Break | yes/no
|
||||
| Version | x.y.z
|
||||
|
||||
|
||||
#### Summary
|
||||
|
||||
<!-- Provide a summary of the failing scenario. -->
|
||||
|
||||
18
.github/PULL_REQUEST_TEMPLATE/Improvement.md
vendored
Normal file
18
.github/PULL_REQUEST_TEMPLATE/Improvement.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: ⚙ Improvement
|
||||
about: You have some improvement to make Doctrine better? 🎁
|
||||
---
|
||||
|
||||
### Improvement
|
||||
|
||||
<!-- Fill in the relevant information below to help triage your issue. -->
|
||||
|
||||
| Q | A
|
||||
|------------ | ------
|
||||
| New Feature | yes
|
||||
| RFC | yes/no
|
||||
| BC Break | yes/no
|
||||
|
||||
#### Summary
|
||||
|
||||
<!-- Provide a summary of the improvement you are submitting. -->
|
||||
26
.github/PULL_REQUEST_TEMPLATE/New_Feature.md
vendored
Normal file
26
.github/PULL_REQUEST_TEMPLATE/New_Feature.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: 🎉 New Feature
|
||||
about: You have implemented some neat idea that you want to make part of Doctrine? 🎩
|
||||
---
|
||||
|
||||
<!--
|
||||
Thank you for submitting new feature!
|
||||
Pick the target branch based according to these criteria:
|
||||
* submitting a bugfix: target the lowest active stable branch: 2.7
|
||||
* submitting a new feature: target the next minor branch: 2.8.x
|
||||
* submitting a BC-breaking change: target the master branch
|
||||
-->
|
||||
|
||||
### New Feature
|
||||
|
||||
<!-- Fill in the relevant information below to help triage your issue. -->
|
||||
|
||||
| Q | A
|
||||
|------------ | ------
|
||||
| New Feature | yes
|
||||
| RFC | yes/no
|
||||
| BC Break | yes/no
|
||||
|
||||
#### Summary
|
||||
|
||||
<!-- Provide a summary of the feature you have implemented. -->
|
||||
47
.github/workflows/ci.yml
vendored
Normal file
47
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
coding-standards:
|
||||
name: "Coding Standards"
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '7.4'
|
||||
extensions: mbstring
|
||||
tools: composer, cs2pr
|
||||
|
||||
- name: composer install
|
||||
run: "composer install --no-progress --no-suggest --no-interaction --prefer-dist --optimize-autoloader"
|
||||
|
||||
- name: phpcs
|
||||
run: "php vendor/bin/phpcs -q --report=checkstyle --no-colors | cs2pr"
|
||||
|
||||
static-analysis:
|
||||
name: "Static Analysis"
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '7.4'
|
||||
extensions: mbstring
|
||||
tools: composer, cs2pr
|
||||
|
||||
- name: composer install
|
||||
run: "composer install --no-progress --no-suggest --no-interaction --prefer-dist --optimize-autoloader"
|
||||
|
||||
- name: phpstan
|
||||
run: "php vendor/bin/phpstan analyse --error-format=checkstyle --no-progress | cs2pr"
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -7,11 +7,13 @@ lib/api/
|
||||
lib/Doctrine/Common
|
||||
lib/Doctrine/DBAL
|
||||
/.settings/
|
||||
*.iml
|
||||
.buildpath
|
||||
.project
|
||||
.idea
|
||||
*.iml
|
||||
vendor/
|
||||
composer.lock
|
||||
composer.phar
|
||||
/tests/Doctrine/Performance/history.db
|
||||
/.phpcs-cache
|
||||
phpbench.phar
|
||||
phpbench.phar.pubkey
|
||||
|
||||
@@ -3,20 +3,20 @@ build:
|
||||
analysis:
|
||||
environment:
|
||||
php:
|
||||
version: 7.1
|
||||
version: 7.4
|
||||
cache:
|
||||
disabled: false
|
||||
directories:
|
||||
- ~/.composer/cache
|
||||
|
||||
project_setup:
|
||||
override: true
|
||||
tests:
|
||||
override:
|
||||
- php-scrutinizer-run
|
||||
|
||||
before_commands:
|
||||
- "composer install --no-dev --prefer-source"
|
||||
- phpcs-run
|
||||
dependencies:
|
||||
override:
|
||||
- composer install --no-interaction --prefer-dist
|
||||
|
||||
tools:
|
||||
external_code_coverage:
|
||||
@@ -25,10 +25,8 @@ tools:
|
||||
filter:
|
||||
excluded_paths:
|
||||
- docs
|
||||
- tools
|
||||
|
||||
build_failure_conditions:
|
||||
- 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed
|
||||
- 'issues.severity(>= MAJOR).new.exists' # New issues of major or higher severity
|
||||
- 'project.metric_change("scrutinizer.test_coverage", < 0)' # Code Coverage decreased from previous inspection
|
||||
- 'patches.label("Unused Use Statements").new.exists' # No new unused imports patches allowed
|
||||
|
||||
85
.travis.yml
85
.travis.yml
@@ -3,69 +3,60 @@ sudo: false
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 7.1
|
||||
- 7.2
|
||||
- nightly
|
||||
- 7.3
|
||||
- 7.4
|
||||
|
||||
env:
|
||||
- DB=sqlite
|
||||
- DB=mariadb
|
||||
- DB=mysql
|
||||
- DB=pgsql
|
||||
- DB=sqlite
|
||||
|
||||
before_install:
|
||||
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{,.disabled} || echo "xdebug not available"
|
||||
- composer self-update
|
||||
- |
|
||||
if [[ "$COVERAGE" != "1" ]]; then
|
||||
phpenv config-rm ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini || echo "xdebug is not installed"
|
||||
fi
|
||||
- echo "memory_limit=-1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
|
||||
- travis_retry composer self-update
|
||||
|
||||
install: travis_retry composer update --prefer-dist
|
||||
install:
|
||||
- rm composer.lock
|
||||
- travis_retry composer update --no-interaction --prefer-dist --no-suggest --no-progress
|
||||
|
||||
script:
|
||||
- if [[ "$DB" == "mysql" || "$DB" == "mariadb" ]]; then mysql -e "CREATE SCHEMA doctrine_tests; GRANT ALL PRIVILEGES ON doctrine_tests.* to travis@'%'"; fi
|
||||
- |
|
||||
if [[ "$DB" == "mysql" || "$DB" == "mariadb" ]]; then
|
||||
mysql -e "CREATE SCHEMA doctrine_tests; GRANT ALL PRIVILEGES ON doctrine_tests.* to travis@'%'";
|
||||
fi
|
||||
- ENABLE_SECOND_LEVEL_CACHE=0 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml
|
||||
- ENABLE_SECOND_LEVEL_CACHE=1 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml --exclude-group performance,non-cacheable,locking_functional
|
||||
# temporarily disabled
|
||||
#- ENABLE_SECOND_LEVEL_CACHE=1 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml --exclude-group performance,non-cacheable,locking_functional
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: Test
|
||||
env: DB=mariadb
|
||||
addons:
|
||||
mariadb: 10.1
|
||||
|
||||
- stage: Test
|
||||
env: DB=mysql MYSQL_VERSION=5.7
|
||||
php: 7.1
|
||||
before_script:
|
||||
- ./tests/travis/install-mysql-$MYSQL_VERSION.sh
|
||||
sudo: required
|
||||
|
||||
- stage: Test
|
||||
env: DB=mysql MYSQL_VERSION=5.7
|
||||
php: 7.2
|
||||
before_script:
|
||||
- ./tests/travis/install-mysql-$MYSQL_VERSION.sh
|
||||
sudo: required
|
||||
|
||||
- stage: Test
|
||||
env: DB=mysql MYSQL_VERSION=5.7
|
||||
php: nightly
|
||||
before_script:
|
||||
- ./tests/travis/install-mysql-$MYSQL_VERSION.sh
|
||||
sudo: required
|
||||
mariadb: "10.4"
|
||||
|
||||
- stage: Test
|
||||
env: DB=sqlite DEPENDENCIES=low
|
||||
install: travis_retry composer update --prefer-dist --prefer-lowest
|
||||
install:
|
||||
- travis_retry composer update --no-interaction --prefer-dist --no-suggest --no-progress --prefer-lowest
|
||||
|
||||
- stage: Test
|
||||
if: type = cron
|
||||
php: 7.3
|
||||
env: DB=sqlite DEV_DEPENDENCIES
|
||||
install:
|
||||
- rm composer.lock
|
||||
- composer config minimum-stability dev
|
||||
- travis_retry composer update --prefer-dist
|
||||
- travis_retry composer update --no-interaction --prefer-dist --no-suggest --no-progress
|
||||
|
||||
- stage: Test
|
||||
env: DB=sqlite COVERAGE
|
||||
before_script:
|
||||
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{.disabled,}
|
||||
- if [[ ! $(php -m | grep -si xdebug) ]]; then echo "xdebug required for coverage"; exit 1; fi
|
||||
script:
|
||||
- ENABLE_SECOND_LEVEL_CACHE=0 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml --coverage-clover ./build/logs/clover.xml
|
||||
@@ -73,29 +64,19 @@ jobs:
|
||||
- wget https://scrutinizer-ci.com/ocular.phar
|
||||
- php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml
|
||||
|
||||
- stage: Code Quality
|
||||
env: DB=none STATIC_ANALYSIS
|
||||
install: travis_retry composer update --prefer-dist --prefer-stable
|
||||
before_script:
|
||||
- echo "extension=memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
|
||||
- echo "extension=redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
|
||||
- travis_retry composer require --dev --prefer-dist --prefer-stable phpstan/phpstan:^0.9
|
||||
script: vendor/bin/phpstan analyse -l 1 -c phpstan.neon lib
|
||||
|
||||
- stage: Code Quality
|
||||
env: DB=none BENCHMARK
|
||||
before_script: wget https://phpbench.github.io/phpbench/phpbench.phar https://phpbench.github.io/phpbench/phpbench.phar.pubkey
|
||||
script: php phpbench.phar run -l dots --report=default
|
||||
|
||||
- stage: Code Quality
|
||||
env: DB=none CODING_STANDARDS
|
||||
php: nightly
|
||||
before_script:
|
||||
- wget https://phpbench.github.io/phpbench/phpbench.phar https://phpbench.github.io/phpbench/phpbench.phar.pubkey
|
||||
script:
|
||||
- ./vendor/bin/phpcs
|
||||
- php phpbench.phar run --bootstrap=tests/Doctrine/Tests/TestInit.php -l dots --report=default
|
||||
|
||||
allow_failures:
|
||||
- php: nightly
|
||||
# temporarily disabled
|
||||
- env: DB=mysql
|
||||
- env: DB=mariadb
|
||||
- env: DB=pgsql
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
- $HOME/.composer/cache
|
||||
107
CONTRIBUTING.md
107
CONTRIBUTING.md
@@ -1,79 +1,81 @@
|
||||
# Contribute to Doctrine
|
||||
# Contributing to Doctrine ORM
|
||||
|
||||
Thank you for contributing to Doctrine!
|
||||
Thank you for contributing to Doctrine ORM!
|
||||
|
||||
Before we can merge your Pull-Request here are some guidelines that you need to follow.
|
||||
Before we can merge your pull request here are some guidelines that you need to follow.
|
||||
These guidelines exist not to annoy you, but to keep the code base clean,
|
||||
unified and future proof.
|
||||
|
||||
## We only accept PRs to "master"
|
||||
## Obtaining a copy
|
||||
|
||||
Our branching strategy is "everything to master first", even
|
||||
bugfixes and we then merge them into the stable branches. You should only
|
||||
open pull requests against the master branch. Otherwise we cannot accept the PR.
|
||||
In order to submit a pull request, you will need to [fork the project][Fork] and obtain a
|
||||
fresh copy of the source code:
|
||||
|
||||
There is one exception to the rule, when we merged a bug into some stable branches
|
||||
we do occasionally accept pull requests that merge the same bug fix into earlier
|
||||
branches.
|
||||
```sh
|
||||
git clone git@github.com:<your-github-name>/orm.git
|
||||
cd orm
|
||||
```
|
||||
|
||||
Then you will have to run a Composer installation in the project:
|
||||
```sh
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
./composer.phar install
|
||||
```
|
||||
|
||||
## Choosing the branch
|
||||
|
||||
* I am submitting a bugfix for a stable release
|
||||
* Your PR should target the [lowest active stable branch (2.7)][2.7].
|
||||
* I am submitting a new feature
|
||||
* Your PR should target the [master branch (3.0)][Master].
|
||||
* I am submitting a BC-breaking change
|
||||
* Your PR must target the [master branch (3.0)][Master].
|
||||
* Please also try to provide a deprecation path in a PR targeting the [2.8 branch][2.8].
|
||||
|
||||
Please always create a new branch for your changes (i.e. do not commit directly into `master`
|
||||
in your fork), otherwise you would run into troubles with creating multiple pull requests.
|
||||
|
||||
## Coding Standard
|
||||
|
||||
We use PSR-1 and PSR-2:
|
||||
We follow the [Doctrine Coding Standard][CS].
|
||||
Please refer to this repository to learn about the rules your code should follow.
|
||||
You can also use `vendor/bin/phpcs` to validate your changes locally.
|
||||
|
||||
* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md
|
||||
* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
|
||||
## Tests
|
||||
|
||||
with some exceptions/differences:
|
||||
|
||||
* Keep the nesting of control structures per method as small as possible
|
||||
* Align equals (=) signs
|
||||
* Add spaces between assignment, control and return statements
|
||||
* Prefer early exit over nesting conditions
|
||||
* Add spaces around a negation if condition ``if ( ! $cond)``
|
||||
|
||||
## Unit-Tests
|
||||
|
||||
Please try to add a test for your pull-request.
|
||||
Please try to add a test for your pull request.
|
||||
|
||||
* If you want to fix a bug or provide a reproduce case, create a test file in
|
||||
``tests/Doctrine/Tests/ORM/Functional/Ticket`` with the name of the ticket,
|
||||
``DDC1234Test.php`` for example.
|
||||
* If you want to contribute new functionality add unit- or functional tests
|
||||
``tests/Doctrine/Tests/ORM/Functional/Ticket`` with the identifier of the issue,
|
||||
i.e. ``GH1234Test.php`` for an issue with id `#1234`.
|
||||
* If you want to contribute new functionality, add unit or functional tests
|
||||
depending on the scope of the feature.
|
||||
|
||||
You can run the unit-tests by calling ``vendor/bin/phpunit`` from the root of the project.
|
||||
It will run all the tests with an in memory SQLite database.
|
||||
|
||||
In order to do that, you will need a fresh copy of doctrine2, and you
|
||||
will have to run a composer installation in the project:
|
||||
|
||||
```sh
|
||||
git clone git@github.com:doctrine/doctrine2.git
|
||||
cd doctrine2
|
||||
curl -sS https://getcomposer.org/installer | php --
|
||||
./composer.phar install
|
||||
```
|
||||
You can run the tests by calling ``vendor/bin/phpunit`` from the root of the project.
|
||||
It will run all the tests with an in-memory SQLite database.
|
||||
|
||||
To run the testsuite against another database, copy the ``phpunit.xml.dist``
|
||||
to for example ``mysql.phpunit.xml`` and edit the parameters. You can
|
||||
take a look at the ``tests/travis`` folder for some examples. Then run:
|
||||
|
||||
vendor/bin/phpunit -c mysql.phpunit.xml
|
||||
|
||||
If you do not provide these parameters, the test suite will use an in-memory
|
||||
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/doctrine2/tree/master/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an
|
||||
example.
|
||||
1. If you put a test into the `Ticket` namespace as described above, put the testcase
|
||||
and all entities into the same file.
|
||||
See [DDC2306Test][Test Example] for an example.
|
||||
|
||||
## Travis
|
||||
## CI
|
||||
|
||||
We automatically run your pull request through [Travis CI](http://www.travis-ci.org)
|
||||
against SQLite, MySQL and PostgreSQL. If you break the tests, we cannot merge your code,
|
||||
so please make sure that your code is working before opening up a Pull-Request.
|
||||
We automatically run all pull requests through [Travis CI][Travis].
|
||||
|
||||
* The test suite is ran against SQLite, MySQL, MariaDB and PostgreSQL on all supported PHP versions.
|
||||
* The code is validated against our [Coding Standard](#coding-standard).
|
||||
* The code is checked by a static analysis tool.
|
||||
|
||||
If you break the tests, we cannot merge your code,
|
||||
so please make sure that your code is working before opening a pull request.
|
||||
|
||||
## Getting merged
|
||||
|
||||
@@ -82,3 +84,10 @@ everything as fast as possible, but cannot always live up to our own expectation
|
||||
|
||||
Thank you very much again for your contribution!
|
||||
|
||||
[Master]: https://github.com/doctrine/orm/tree/master
|
||||
[2.8]: https://github.com/doctrine/orm/tree/2.8.x
|
||||
[2.7]: https://github.com/doctrine/orm/tree/2.7
|
||||
[CS]: https://github.com/doctrine/coding-standard
|
||||
[Fork]: https://guides.github.com/activities/forking/
|
||||
[Travis]: https://www.travis-ci.org
|
||||
[Test Example]: https://github.com/doctrine/orm/tree/master/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2306Test.php
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2006-2015 Doctrine Project
|
||||
Copyright (c) Doctrine Project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
52
README.md
52
README.md
@@ -1,14 +1,36 @@
|
||||
| [Master][Master] | [2.5][2.5] |
|
||||
|:----------------:|:----------:|
|
||||
| [![Build status][Master image]][Master] | [![Build status][2.5 image]][2.5] |
|
||||
| [![Coverage Status][Master coverage image]][Master coverage] | [![Coverage Status][2.5 coverage image]][2.5 coverage] |
|
||||
[](https://tidelift.com/subscription/pkg/packagist-doctrine-orm?utm_source=packagist-doctrine-orm&utm_medium=referral&utm_campaign=readme)
|
||||
|
||||
Doctrine 2 is an object-relational mapper (ORM) for PHP 7.1+ that provides transparent persistence
|
||||
| [Master][Master] | [2.8][2.8] | [2.7][2.7] |
|
||||
|:----------------:|:----------:|:----------:|
|
||||
| [![Build status][Master image]][Master] | [![Build status][2.8 image]][2.8] | [![Build status][2.7 image]][2.7] |
|
||||
| [![Coverage Status][Master coverage image]][Master coverage] | [![Coverage Status][2.8 coverage image]][2.8 coverage] | [![Coverage Status][2.7 coverage image]][2.7 coverage] |
|
||||
|
||||
##### :warning: You are browsing the code of upcoming Doctrine 3.0.
|
||||
##### Things changed a lot here and major code changes should be expected. If you are rather looking for a stable version, refer to the [2.7 branch][2.7] for the current stable release or [2.8 branch][2.8] for the upcoming release. If you are submitting a pull request, please see the _[Which branch should I choose?](#which-branch-should-i-choose)_ section below.
|
||||
|
||||
-----
|
||||
|
||||
Doctrine 3 is an object-relational mapper (ORM) for PHP 7.2+ 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
|
||||
without requiring unnecessary code duplication.
|
||||
|
||||
-----
|
||||
|
||||
### Which branch should I choose?
|
||||
|
||||
Please see [Choosing the branch](CONTRIBUTING.md#choosing-the-branch) to get more information about which branch
|
||||
you should target your pull request at.
|
||||
|
||||
## Doctrine ORM for enterprise
|
||||
|
||||
Available as part of the Tidelift Subscription.
|
||||
|
||||
The maintainers of Doctrine ORM and thousands of other packages are working with Tidelift to deliver commercial support
|
||||
and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve
|
||||
code health, while paying the maintainers of the exact dependencies you use.
|
||||
[Learn more.](https://tidelift.com/subscription/pkg/packagist-doctrine-orm?utm_source=packagist-doctrine-orm&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
|
||||
|
||||
## More resources:
|
||||
|
||||
@@ -16,11 +38,15 @@ without requiring unnecessary code duplication.
|
||||
* [Documentation](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
|
||||
|
||||
|
||||
[Master image]: https://img.shields.io/travis/doctrine/doctrine2/master.svg?style=flat-square
|
||||
[Master]: https://travis-ci.org/doctrine/doctrine2
|
||||
[Master coverage image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/doctrine2/master.svg?style=flat-square
|
||||
[Master coverage]: https://scrutinizer-ci.com/g/doctrine/doctrine2/?branch=master
|
||||
[2.5 image]: https://img.shields.io/travis/doctrine/doctrine2/2.5.svg?style=flat-square
|
||||
[2.5]: https://github.com/doctrine/doctrine2/tree/2.5
|
||||
[2.5 coverage image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/doctrine2/2.5.svg?style=flat-square
|
||||
[2.5 coverage]: https://scrutinizer-ci.com/g/doctrine/doctrine2/?branch=2.5
|
||||
[Master image]: https://img.shields.io/travis/doctrine/orm/master.svg?style=flat-square
|
||||
[Master]: https://travis-ci.org/doctrine/orm
|
||||
[Master coverage image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/orm/master.svg?style=flat-square
|
||||
[Master coverage]: https://scrutinizer-ci.com/g/doctrine/orm/?branch=master
|
||||
[2.8 image]: https://img.shields.io/travis/doctrine/orm/2.8.x.svg?style=flat-square
|
||||
[2.8]: https://github.com/doctrine/orm/tree/2.8.x
|
||||
[2.8 coverage image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/orm/2.8.x.svg?style=flat-square
|
||||
[2.8 coverage]: https://scrutinizer-ci.com/g/doctrine/orm/?branch=2.8.x
|
||||
[2.7 image]: https://img.shields.io/travis/doctrine/orm/2.7.svg?style=flat-square
|
||||
[2.7]: https://github.com/doctrine/orm/tree/2.7
|
||||
[2.7 coverage image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/orm/2.7.svg?style=flat-square
|
||||
[2.7 coverage]: https://scrutinizer-ci.com/g/doctrine/orm/?branch=2.7
|
||||
|
||||
@@ -11,7 +11,7 @@ Please read the documentation chapter on Security in Doctrine DBAL and ORM to
|
||||
understand the assumptions we make.
|
||||
|
||||
- [DBAL Security Page](https://github.com/doctrine/dbal/blob/master/docs/en/reference/security.rst)
|
||||
- [ORM Security Page](https://github.com/doctrine/doctrine2/blob/master/docs/en/reference/security.rst)
|
||||
- [ORM Security Page](https://github.com/doctrine/orm/blob/master/docs/en/reference/security.rst)
|
||||
|
||||
If you find a Security bug in Doctrine, please report it on Jira and change the
|
||||
Security Level to "Security Issues". It will be visible to Doctrine Core
|
||||
|
||||
372
UPGRADE.md
372
UPGRADE.md
@@ -1,5 +1,343 @@
|
||||
# Upgrade to 3.0
|
||||
|
||||
## BC Break: Removed ability to clear cache via console with some cache drivers
|
||||
|
||||
The console commands `orm:clear-cache:metadata`, `orm:clear-cache:result`,
|
||||
and `orm:clear-cache:query` cannot be used with the `ApcCache`, `ApcuCache`,
|
||||
or `XcacheCache` because the memory is only available to the webserver process.
|
||||
|
||||
## BC Break: `orm:run-dql` command's `$depth` parameter removed
|
||||
|
||||
The `$depth` parameter has been removed, the dumping functionality
|
||||
is now provided by [`symfony/var-dumper`](https://github.com/symfony/var-dumper).
|
||||
|
||||
## BC Break: Dropped `Doctrine\ORM\Tools\Setup::registerAutoloadDirectory()`
|
||||
|
||||
This method used deprecated Doctrine Autoloader and has been removed. Please rely on Composer autoloading instead.
|
||||
|
||||
## BC Break: Dropped automatic discriminator map discovery
|
||||
|
||||
Automatic discriminator map discovery exhibited multiple flaws
|
||||
that can't be reliably addressed and supported:
|
||||
|
||||
* discovered entries are not namespaced which leads to collisions,
|
||||
* the class name is part of the discriminator map, therefore the class
|
||||
must never be renamed.
|
||||
|
||||
As a consequence this feature has been dropped.
|
||||
|
||||
If your code relied on this feature, please build the discriminator map for
|
||||
your inheritance tree manually where each entry is an unqualified lowercase
|
||||
name of the member entities.
|
||||
|
||||
## BC Break: Missing type declaration added for identifier generators
|
||||
|
||||
The interfaces `Doctrine\ORM\Sequencing\Generator` and
|
||||
`Doctrine\ORM\Sequencing\Planning\ValueGenerationPlan` now uses explicit type
|
||||
declaration for parameters and return (as much as possible).
|
||||
|
||||
## BC Break: Removed possibility to extend the doctrine mapping xml schema with anything
|
||||
|
||||
If you want to extend it now you have to provide your own validation schema.
|
||||
|
||||
## BC Break: Entity Listeners no long support naming convention methods
|
||||
|
||||
If you want their behavior to be kept, please add the necessary Annotation methods (in case XML driver is used,
|
||||
no changes are necessary).
|
||||
|
||||
## BC Break: Removed `Doctrine\ORM\Mapping\Exporter\VariableExporter` constants
|
||||
|
||||
This constant has been removed
|
||||
|
||||
* `Doctrine\ORM\Mapping\Exporter\VariableExporter::INDENTATION`
|
||||
|
||||
## BC Break: Removed support for named queries and named native queries
|
||||
|
||||
These classes have been removed:
|
||||
|
||||
* `Doctrine/ORM/Annotation/NamedQueries`
|
||||
* `Doctrine/ORM/Annotation/NamedQuery`
|
||||
* `Doctrine/ORM/Annotation/NamedNativeQueries`
|
||||
* `Doctrine/ORM/Annotation/NamedNativeQuery`
|
||||
* `Doctrine/ORM/Annotation/ColumnResult`
|
||||
* `Doctrine/ORM/Annotation/FieldResult`
|
||||
* `Doctrine/ORM/Annotation/EntityResult`
|
||||
* `Doctrine/ORM/Annotation/SqlResultSetMapping`
|
||||
* `Doctrine/ORM/Annotation/SqlResultSetMappings`
|
||||
|
||||
These methods have been removed:
|
||||
|
||||
* `Doctrine/ORM/Configuration::addNamedQuery()`
|
||||
* `Doctrine/ORM/Configuration::getNamedQuery()`
|
||||
* `Doctrine/ORM/Configuration::addNamedNativeQuery()`
|
||||
* `Doctrine/ORM/Configuration::getNamedNativeQuery()`
|
||||
* `Doctrine/ORM/Decorator/EntityManagerDecorator::createNamedQuery()`
|
||||
* `Doctrine/ORM/Decorator/EntityManagerDecorator::createNamedNativeQuery()`
|
||||
* `Doctrine/ORM/EntityManager::createNamedQuery()`
|
||||
* `Doctrine/ORM/EntityManager::createNamedNativeQuery()`
|
||||
* `Doctrine/ORM/EntityManagerInterface::createNamedQuery()`
|
||||
* `Doctrine/ORM/EntityManagerInterface::createNamedNativeQuery()`
|
||||
* `Doctrine/ORM/EntityRepository::createNamedQuery()`
|
||||
* `Doctrine/ORM/EntityRepository::createNamedNativeQuery()`
|
||||
* `Doctrine/ORM/Mapping/ClassMetadata::getNamedQuery()`
|
||||
* `Doctrine/ORM/Mapping/ClassMetadata::getNamedQueries()`
|
||||
* `Doctrine/ORM/Mapping/ClassMetadata::addNamedQuery()`
|
||||
* `Doctrine/ORM/Mapping/ClassMetadata::hasNamedQuery()`
|
||||
* `Doctrine/ORM/Mapping/ClassMetadata::getNamedNativeQuery()`
|
||||
* `Doctrine/ORM/Mapping/ClassMetadata::getNamedNativeQueries()`
|
||||
* `Doctrine/ORM/Mapping/ClassMetadata::addNamedNativeQuery()`
|
||||
* `Doctrine/ORM/Mapping/ClassMetadata::hasNamedNativeQuery()`
|
||||
* `Doctrine\ORM\Mapping\ClassMetadata::addSqlResultSetMapping()`
|
||||
* `Doctrine\ORM\Mapping\ClassMetadata::getSqlResultSetMapping()`
|
||||
* `Doctrine\ORM\Mapping\ClassMetadata::getSqlResultSetMappings()`
|
||||
* `Doctrine\ORM\Mapping\ClassMetadata::hasSqlResultSetMapping()`
|
||||
|
||||
## BC Break: Removed support for entity namespace aliases
|
||||
|
||||
The support for namespace aliases has been removed.
|
||||
Please migrate to using `::class` for referencing classes.
|
||||
|
||||
These methods have been removed:
|
||||
|
||||
* `Doctrine\ORM\Configuration::addEntityNamespace()`
|
||||
* `Doctrine\ORM\Configuration::getEntityNamespace()`
|
||||
* `Doctrine\ORM\Configuration::setEntityNamespaces()`
|
||||
* `Doctrine\ORM\Configuration::getEntityNamespaces()`
|
||||
* `Doctrine\ORM\Mapping\AbstractClassMetadataFactory::getFqcnFromAlias()`
|
||||
* `Doctrine\ORM\ORMException::unknownEntityNamespace()`
|
||||
|
||||
## BC Break: Removed same-namespace class name resolution
|
||||
|
||||
Support for same-namespace class name resolution in mappings has been removed.
|
||||
If you're using annotation driver, please migrate to references using `::class`.
|
||||
If you're using XML driver, please migrate to fully qualified references.
|
||||
|
||||
These methods have been removed:
|
||||
|
||||
* Doctrine\ORM\Mapping\ClassMetadata::fullyQualifiedClassName()
|
||||
|
||||
## BC Break: Removed code generators and related console commands
|
||||
|
||||
These console commands have been removed:
|
||||
|
||||
* `orm:convert-mapping`
|
||||
* `orm:generate:entities`
|
||||
* `orm:generate-repositories`
|
||||
|
||||
These classes have been removed:
|
||||
|
||||
* `Doctrine\ORM\Tools\EntityGenerator`
|
||||
* `Doctrine\ORM\Tools\EntityRepositoryGenerator`
|
||||
|
||||
The whole Doctrine\ORM\Tools\Export namespace with all its members has been removed as well.
|
||||
|
||||
## BC Break: proxies no longer implement `Doctrine\ORM\Proxy\Proxy`
|
||||
|
||||
Proxy objects no longer implement `Doctrine\ORM\Proxy\Proxy` nor
|
||||
`Doctrine\Common\Persistence\Proxy`: instead, they implement
|
||||
`ProxyManager\Proxy\GhostObjectInterface`.
|
||||
|
||||
These related classes have been removed:
|
||||
|
||||
* `Doctrine\ORM\Proxy\ProxyFactory` - replaced by `Doctrine\ORM\Proxy\Factory\StaticProxyFactory`
|
||||
and `Doctrine\ORM\Proxy\Factory\ProxyFactory`
|
||||
* `Doctrine\ORM\Proxy\Proxy`
|
||||
* `Doctrine\ORM\Proxy\Autoloader` - we suggest using the composer autoloader instead
|
||||
* `Doctrine\ORM\Reflection\RuntimePublicReflectionProperty`
|
||||
|
||||
These methods have been removed:
|
||||
|
||||
* `Doctrine\ORM\Configuration#getProxyDir()`
|
||||
* `Doctrine\ORM\Configuration#getAutoGenerateProxyClasses()`
|
||||
* `Doctrine\ORM\Configuration#getProxyNamespace()`
|
||||
|
||||
Proxy class names change: the generated proxies now follow
|
||||
the [`ClassNameInflector`](https://github.com/Ocramius/ProxyManager/blob/2.1.1/src/ProxyManager/Inflector/ClassNameInflector.php)
|
||||
naming.
|
||||
|
||||
Proxies are also always generated if not found: fatal errors due to missing
|
||||
proxy classes should no longer occur with ORM default settings.
|
||||
|
||||
In addition to that, the following changes affect entity lazy-loading semantics:
|
||||
|
||||
* `final` methods are now allowed
|
||||
* `__clone` is no longer called by the ORM
|
||||
* `__wakeup` is no longer called by the ORM
|
||||
* `serialize($proxy)` will lead to full recursive proxy initialization: please mitigate
|
||||
the recursive initialization by implementing
|
||||
the [`Serializable`](https://secure.php.net/manual/en/class.serializable.php) interface
|
||||
* `clone $proxy` will lead to full initialization of the cloned instance, not the
|
||||
original instance
|
||||
* lazy-loading a detached proxy no longer causes the proxy identifiers to be reset
|
||||
to `null`
|
||||
* identifier properties are always set when the ORM produces a proxy instance
|
||||
* calling a method on a proxy no longer causes proxy lazy-loading if the method does
|
||||
not access any un-initialized proxy state
|
||||
* accessing entity private state, even with reflection, will trigger lazy-loading
|
||||
|
||||
## BC Break: Removed `Doctrine\ORM\Version`
|
||||
|
||||
The `Doctrine\ORM\Version` class is no longer available: please refrain from checking the ORM version at runtime.
|
||||
|
||||
## BC Break: Removed `EntityManager#merge()` and `EntityManager#detach()` methods
|
||||
|
||||
Merge and detach semantics were a poor fit for the PHP "share-nothing" architecture.
|
||||
In addition to that, merging/detaching caused multiple issues with data integrity
|
||||
in the managed entity graph, which was constantly spawning more edge-case bugs/scenarios.
|
||||
|
||||
The following API methods were therefore removed:
|
||||
|
||||
* `EntityManager#merge()`
|
||||
* `EntityManager#detach()`
|
||||
* `UnitOfWork#merge()`
|
||||
* `UnitOfWork#detach()`
|
||||
|
||||
Users are encouraged to migrate `EntityManager#detach()` calls to `EntityManager#clear()`.
|
||||
|
||||
In order to maintain performance on batch processing jobs, it is endorsed to enable
|
||||
the second level cache (http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/second-level-cache.html)
|
||||
on entities that are frequently reused across multiple `EntityManager#clear()` calls.
|
||||
|
||||
An alternative to `EntityManager#merge()` is not provided by ORM 3.0, since the merging
|
||||
semantics should be part of the business domain rather than the persistence domain of an
|
||||
application. If your application relies heavily on CRUD-alike interactions and/or `PATCH`
|
||||
restful operations, you should look at alternatives such as [JMSSerializer](https://github.com/schmittjoh/serializer).
|
||||
|
||||
## BC Break: Added the final keyword for `EntityManager`
|
||||
|
||||
Final keyword has been added to the ``EntityManager::class`` in order to ensure that EntityManager is not used as valid extension point. Valid extension point should be EntityManagerInterface.
|
||||
|
||||
## BC Break: ``EntityManagerInterface`` is now used instead of ``EntityManager`` in typehints
|
||||
|
||||
`Sequencing\Generator#generate()` now takes ``EntityManagerInterface`` as its first argument instead of ``EntityManager``. If you have any custom generators, please update your code accordingly.
|
||||
|
||||
## BC Break: Removed `EntityManager#flush($entity)` and `EntityManager#flush($entities)`
|
||||
|
||||
If your code relies on single entity flushing optimisations via
|
||||
`EntityManager#flush($entity)`, the signature has been changed to
|
||||
`EntityManager#flush()`.
|
||||
|
||||
Said API was affected by multiple data integrity bugs due to the fact
|
||||
that change tracking was being restricted upon a subset of the managed
|
||||
entities. The ORM cannot support committing subsets of the managed
|
||||
entities while also guaranteeing data integrity, therefore this
|
||||
utility was removed.
|
||||
|
||||
The `flush()` semantics 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
|
||||
`$entity` or `$entities`, as the parameter is now completely ignored.
|
||||
|
||||
The same applies to `UnitOfWork#commit($entity)`, which now is simply
|
||||
`UnitOfWork#commit()`.
|
||||
|
||||
If you would still like to perform batching operations over small `UnitOfWork`
|
||||
instances, it is suggested to follow these paths instead:
|
||||
|
||||
* eagerly use `EntityManager#clear()` in conjunction with a specific second level
|
||||
cache configuration (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/second-level-cache.html)
|
||||
* use an explicit change tracking policy (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/change-tracking-policies.html)
|
||||
|
||||
## BC Break: Removed ``YAML`` mapping drivers.
|
||||
|
||||
If your code relies on ``YamlDriver`` or ``SimpleYamlDriver``, you **MUST** change to
|
||||
annotation or XML drivers instead.
|
||||
|
||||
## BC Break: Changed methods in ``ClassMetadata``
|
||||
|
||||
* ``ClassMetadata::addInheritedProperty``
|
||||
* ``ClassMetadata::setDiscriminatorColumn``
|
||||
|
||||
## BC Break: Removed methods in ``ClassMetadata``
|
||||
|
||||
* ``ClassMetadata::getTypeOfField`` (to be removed, part of Common API)
|
||||
|
||||
## BC Break: Removed methods in ``ClassMetadata``
|
||||
|
||||
* ``ClassMetadata::setTableName`` => Use ``ClassMetadata::setPrimaryTable(['name' => ...])``
|
||||
* ``ClassMetadata::getFieldMapping`` => Use ``ClassMetadata::getProperty()`` and its methods
|
||||
* ``ClassMetadata::getQuotedColumnName`` => Use ``ClassMetadata::getProperty()::getQuotedColumnName()``
|
||||
* ``ClassMetadata::getQuotedTableName``
|
||||
* ``ClassMetadata::getQuotedJoinTableName``
|
||||
* ``ClassMetadata::getQuotedIdentifierColumnNames``
|
||||
* ``ClassMetadata::getIdentifierColumnNames`` => Use ``ClassMetadata::getIdentifierColumns($entityManager)``
|
||||
* ``ClassMetadata::setVersionMetadata``
|
||||
* ``ClassMetadata::setVersioned``
|
||||
* ``ClassMetadata::invokeLifecycleCallbacks``
|
||||
* ``ClassMetadata::isInheritedField`` => Use ``ClassMetadata::getProperty()::isInherited()``
|
||||
* ``ClassMetadata::isUniqueField`` => Use ``ClassMetadata::getProperty()::isUnique()``
|
||||
* ``ClassMetadata::isNullable`` => Use ``ClassMetadata::getProperty()::isNullable()``
|
||||
* ``ClassMetadata::getTypeOfColumn()`` => Use ``PersisterHelper::getTypeOfColumn()``
|
||||
|
||||
## BC Break: Removed ``quoted`` index from table, field and sequence mappings
|
||||
|
||||
Quoting is now always called. Implement your own ``Doctrine\ORM\Mapping\NamingStrategy`` to manipulate
|
||||
your schema, tables and column names to your custom desired naming convention.
|
||||
|
||||
## BC Break: Removed ``ClassMetadata::$fieldMappings[$fieldName]['requireSQLConversion']``
|
||||
|
||||
ORM Type SQL conversion is now always being applied, minimizing the risks of error prone code in ORM internals
|
||||
|
||||
## BC Break: Removed ``ClassMetadata::$columnNames``
|
||||
|
||||
If your code relies on this property, you should search/replace from this:
|
||||
|
||||
$metadata->columnNames[$fieldName]
|
||||
|
||||
To this:
|
||||
|
||||
$metadata->getProperty($fieldName)->getColumnName()
|
||||
|
||||
## BC Break: Renamed ``ClassMetadata::setIdentifierValues()`` to ``ClassMetadata::assignIdentifier()``
|
||||
|
||||
Provides a more meaningful name to method.
|
||||
|
||||
## BC Break: Removed ``ClassMetadata::$namespace``
|
||||
|
||||
The namespace property in ClassMetadata was only used when using association
|
||||
classes in the same namespace and it was used to speedup ClassMetadata
|
||||
creation purposes. Namespace could be easily inferred by asking ``\ReflectionClass``
|
||||
which was already stored internally.
|
||||
|
||||
### BC Break: Removed ``ClassMetadata::$isVersioned``
|
||||
|
||||
Switched to a method alternative: ``ClassMetadata::isVersioned()``
|
||||
|
||||
## BC Break: Removed ``Doctrine\ORM\Mapping\ClassMetadataInfo``
|
||||
|
||||
There was no reason to keep a blank class. All references are now pointing
|
||||
to ``Doctrine\ORM\Mapping\ClassMetadata``.
|
||||
|
||||
## BC Break: Annotations classes namespace change
|
||||
|
||||
All Annotations classes got moved from ``Doctrine\ORM\Mapping`` into a more
|
||||
pertinent namespace ``Doctrine\ORM\Annotation``. This change was done to add
|
||||
room for Metadata namespace refactoring.
|
||||
|
||||
## Minor BC break: Mappings now store ``DBAL\Type`` instances instead of strings
|
||||
|
||||
This leads to manual ``ResultSetMapping`` building instances to also hold Types in meta results.
|
||||
Example:
|
||||
|
||||
$rsm->addMetaResult('e ', 'e_discr', 'discr', false, Type::getType('string'));
|
||||
|
||||
## Enhancement: Mappings now store their declaring ``ClassMetadata``
|
||||
|
||||
Every field, association or embedded now contains a pointer to its declaring ``ClassMetadata``.
|
||||
|
||||
## Enhancement: Mappings now store their corresponding table name
|
||||
|
||||
Every field, association join column or inline embedded field/association holds a reference to its owning table name.
|
||||
|
||||
|
||||
# Upgrade to 2.6
|
||||
|
||||
## Added `Doctrine\ORM\EntityRepository::count()` method
|
||||
|
||||
`Doctrine\ORM\EntityRepository::count()` has been added. This new method has different
|
||||
signature than `Countable::count()` (required parameter) and therefore are not compatible.
|
||||
If your repository implemented the `Countable` interface, you will have to use
|
||||
`$repository->count([])` instead and not implement `Countable` interface anymore.
|
||||
|
||||
## Minor BC BREAK: `Doctrine\ORM\Tools\Console\ConsoleRunner` is now final
|
||||
|
||||
Since it's just an utilitarian class and should not be inherited.
|
||||
@@ -13,13 +351,17 @@ now has a required parameter `$pathExpr`.
|
||||
|
||||
Method `Doctrine\ORM\Query\Parser#isInternalFunction()` was removed because
|
||||
the distinction between internal function and user defined DQL was removed.
|
||||
[#6500](https://github.com/doctrine/doctrine2/pull/6500)
|
||||
[#6500](https://github.com/doctrine/orm/pull/6500)
|
||||
|
||||
## Minor BC BREAK: removed `Doctrine\ORM\ORMException#overwriteInternalDQLFunctionNotAllowed()`
|
||||
|
||||
Method `Doctrine\ORM\Query\Parser#overwriteInternalDQLFunctionNotAllowed()` was
|
||||
removed because of the choice to allow users to overwrite internal functions, ie
|
||||
`AVG`, `SUM`, `COUNT`, `MIN` and `MAX`. [#6500](https://github.com/doctrine/doctrine2/pull/6500)
|
||||
`AVG`, `SUM`, `COUNT`, `MIN` and `MAX`. [#6500](https://github.com/doctrine/orm/pull/6500)
|
||||
|
||||
## Minor BC BREAK: removed $className parameter on `AbstractEntityInheritancePersister#getSelectJoinColumnSQL()`
|
||||
|
||||
As `$className` parameter was not used in the method, it was safely removed.
|
||||
|
||||
## PHP 7.1 is now required
|
||||
|
||||
@@ -35,11 +377,7 @@ As a consequence, automatic cache setup in Doctrine\ORM\Tools\Setup::create*Conf
|
||||
## Minor BC BREAK: removed `Doctrine\ORM\Query\SqlWalker#walkCaseExpression()`
|
||||
|
||||
Method `Doctrine\ORM\Query\SqlWalker#walkCaseExpression()` was unused and part
|
||||
of the internal API of the ORM, so it was removed. [#5600](https://github.com/doctrine/doctrine2/pull/5600).
|
||||
|
||||
## Minor BC BREAK: removed $className parameter on `AbstractEntityInheritancePersister#getSelectJoinColumnSQL()`
|
||||
|
||||
As `$className` parameter was not used in the method, it was safely removed.
|
||||
of the internal API of the ORM, so it was removed. [#5600](https://github.com/doctrine/orm/pull/5600).
|
||||
|
||||
## Minor BC BREAK: query cache key time is now a float
|
||||
|
||||
@@ -64,8 +402,8 @@ either:
|
||||
- map those classes as `MappedSuperclass`
|
||||
|
||||
## Minor BC BREAK: ``EntityManagerInterface`` instead of ``EntityManager`` in type-hints
|
||||
|
||||
As of 2.5, classes requiring the ``EntityManager`` in any method signature will now require
|
||||
|
||||
As of 2.5, classes requiring the ``EntityManager`` in any method signature will now require
|
||||
an ``EntityManagerInterface`` instead.
|
||||
If you are extending any of the following classes, then you need to check following
|
||||
signatures:
|
||||
@@ -158,7 +496,7 @@ the `Doctrine\ORM\Repository\DefaultRepositoryFactory`.
|
||||
When executing DQL queries with new object expressions, instead of returning DTOs numerically indexes, it will now respect user provided aliases. Consider the following query:
|
||||
|
||||
SELECT new UserDTO(u.id,u.name) as user,new AddressDTO(a.street,a.postalCode) as address, a.id as addressId FROM User u INNER JOIN u.addresses a WITH a.isPrimary = true
|
||||
|
||||
|
||||
Previously, your result would be similar to this:
|
||||
|
||||
array(
|
||||
@@ -189,6 +527,7 @@ From now on, the resultset will look like this:
|
||||
|
||||
Added way to access the underlying QueryBuilder#from() method's 'indexBy' parameter when using EntityRepository#createQueryBuilder()
|
||||
|
||||
|
||||
# Upgrade to 2.4
|
||||
|
||||
## BC BREAK: Compatibility Bugfix in PersistentCollection#matching()
|
||||
@@ -233,6 +572,7 @@ Now parenthesis are considered, the previous DQL will generate:
|
||||
|
||||
SELECT 100 / (2 * 2) FROM my_entity
|
||||
|
||||
|
||||
# Upgrade to 2.3
|
||||
|
||||
## Auto Discriminator Map breaks userland implementations with Listener
|
||||
@@ -302,6 +642,7 @@ Also, following mapping drivers have been deprecated, please use their replaceme
|
||||
* `Doctrine\ORM\Mapping\Driver\PHPDriver` => `Doctrine\Common\Persistence\Mapping\Driver\PHPDriver`
|
||||
* `Doctrine\ORM\Mapping\Driver\StaticPHPDriver` => `Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver`
|
||||
|
||||
|
||||
# Upgrade to 2.2
|
||||
|
||||
## ResultCache implementation rewritten
|
||||
@@ -385,6 +726,7 @@ Also, Doctrine 2.2 now is around 10-15% faster than 2.1.
|
||||
|
||||
Previously EntityManager#find(null) returned null. It now throws an exception.
|
||||
|
||||
|
||||
# Upgrade to 2.1
|
||||
|
||||
## Interface for EntityRepository
|
||||
@@ -409,6 +751,7 @@ The annotation reader was heavily refactored between 2.0 and 2.1-RC1. In theory
|
||||
|
||||
This is already done inside the ``$config->newDefaultAnnotationDriver``, so everything should automatically work if you are using this method. You can verify if everything still works by executing a console command such as schema-validate that loads all metadata into memory.
|
||||
|
||||
|
||||
# Update from 2.0-BETA3 to 2.0-BETA4
|
||||
|
||||
## XML Driver <change-tracking-policy /> element demoted to attribute
|
||||
@@ -417,6 +760,7 @@ We changed how the XML Driver allows to define the change-tracking-policy. The w
|
||||
|
||||
<entity change-tracking-policy="DEFERRED_IMPLICT" />
|
||||
|
||||
|
||||
# Update from 2.0-BETA2 to 2.0-BETA3
|
||||
|
||||
## Serialization of Uninitialized Proxies
|
||||
@@ -479,10 +823,12 @@ don't loose anything through this.
|
||||
The default allocation size for sequences has been changed from 10 to 1. This step was made
|
||||
to not cause confusion with users and also because it is partly some kind of premature optimization.
|
||||
|
||||
|
||||
# Update from 2.0-BETA1 to 2.0-BETA2
|
||||
|
||||
There are no backwards incompatible changes in this release.
|
||||
|
||||
|
||||
# Upgrade from 2.0-ALPHA4 to 2.0-BETA1
|
||||
|
||||
## EntityRepository deprecates access to protected variables
|
||||
@@ -553,7 +899,6 @@ access all entities.
|
||||
|
||||
Xml and Yaml Drivers work as before!
|
||||
|
||||
|
||||
## New inversedBy attribute
|
||||
|
||||
It is now *mandatory* that the owning side of a bidirectional association specifies the
|
||||
@@ -617,7 +962,7 @@ you need to use the following, explicit syntax:
|
||||
## XML Mapping Driver
|
||||
|
||||
The 'inheritance-type' attribute changed to take last bit of ClassMetadata constant names, i.e.
|
||||
NONE, SINGLE_TABLE, INHERITANCE_TYPE_JOINED
|
||||
NONE, SINGLE_TABLE, JOINED
|
||||
|
||||
## YAML Mapping Driver
|
||||
|
||||
@@ -649,6 +994,7 @@ The Collection interface in the Common package has been updated with some missin
|
||||
that were present only on the default implementation, ArrayCollection. Custom collection
|
||||
implementations need to be updated to adhere to the updated interface.
|
||||
|
||||
|
||||
# Upgrade from 2.0-ALPHA3 to 2.0-ALPHA4
|
||||
|
||||
## CLI Controller changes
|
||||
@@ -685,6 +1031,8 @@ With new required method AbstractTask::buildDocumentation, its implementation de
|
||||
database schema without deleting any unused tables, sequences or foreign keys.
|
||||
* Use "doctrine schema-tool --complete-update" to do a full incremental update of
|
||||
your schema.
|
||||
|
||||
|
||||
# Upgrade from 2.0-ALPHA2 to 2.0-ALPHA3
|
||||
|
||||
This section details the changes made to Doctrine 2.0-ALPHA3 to make it easier for you
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
include('doctrine.php');
|
||||
|
||||
@@ -1,21 +1,7 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once 'Doctrine/Common/ClassLoader.php';
|
||||
|
||||
|
||||
@@ -1,21 +1,6 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Version class and file
|
||||
project.version_class = Doctrine\\ORM\\Version
|
||||
project.version_file = lib/Doctrine/ORM/Version.php
|
||||
|
||||
23
build.xml
23
build.xml
@@ -38,29 +38,6 @@
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="make-release" depends="check-git-checkout-clean,prepare,php">
|
||||
<replace file="${project.version_file}" token="-DEV" value="" failOnNoReplacements="true" />
|
||||
<exec executable="${php_executable}" outputproperty="doctrine.current_version" failonerror="true">
|
||||
<arg value="-r" />
|
||||
<arg value="require_once '${project.version_file}';echo ${project.version_class}::VERSION;" />
|
||||
</exec>
|
||||
<exec executable="${php_executable}" outputproperty="doctrine.next_version" failonerror="true">
|
||||
<arg value="-r" />
|
||||
<arg value="$parts = explode('.', str_ireplace(array('-DEV', '-ALPHA', '-BETA'), '', '${doctrine.current_version}'));
|
||||
if (count($parts) != 3) {
|
||||
throw new \InvalidArgumentException('Version is assumed in format x.y.z, ${doctrine.current_version} given');
|
||||
}
|
||||
$parts[2]++;
|
||||
echo implode('.', $parts);
|
||||
" />
|
||||
</exec>
|
||||
|
||||
<git-commit file="${project.version_file}" message="Release ${doctrine.current_version}" />
|
||||
<git-tag version="${doctrine.current_version}" />
|
||||
<replace file="${project.version_file}" token="${doctrine.current_version}" value="${doctrine.next_version}-DEV" />
|
||||
<git-commit file="${project.version_file}" message="Bump version to ${doctrine.next_version}" />
|
||||
</target>
|
||||
|
||||
<target name="check-git-checkout-clean">
|
||||
<exec executable="git" failonerror="true">
|
||||
<arg value="diff-index" />
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
{
|
||||
"name": "doctrine/orm",
|
||||
"type": "library",
|
||||
"description": "Object-Relational-Mapper for PHP",
|
||||
"keywords": ["orm", "database"],
|
||||
"homepage": "http://www.doctrine-project.org",
|
||||
"description": "PHP object relational mapper (ORM) that 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). This provides developers with a powerful alternative to SQL that maintains flexibility without requiring unnecessary code duplication.",
|
||||
"keywords": [
|
||||
"php",
|
||||
"orm",
|
||||
"mysql",
|
||||
"object",
|
||||
"data",
|
||||
"mapper",
|
||||
"mapping",
|
||||
"query",
|
||||
"dql"
|
||||
],
|
||||
"homepage": "https://www.doctrine-project.org/projects/orm.html",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
|
||||
@@ -12,28 +22,38 @@
|
||||
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
|
||||
{"name": "Marco Pivetta", "email": "ocramius@gmail.com"}
|
||||
],
|
||||
"support": {
|
||||
"chat": "https://www.doctrine-project.org/slack",
|
||||
"docs": "https://www.doctrine-project.org/projects/orm.html",
|
||||
"email": "doctrine-user@googlegroups.com",
|
||||
"issues": "https://github.com/doctrine/orm/issues",
|
||||
"rss": "https://github.com/doctrine/orm/releases.atom",
|
||||
"source": "https://github.com/doctrine/orm"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"ext-pdo": "*",
|
||||
"doctrine/annotations": "~1.5",
|
||||
"php": "^7.3",
|
||||
"ext-ctype": "*",
|
||||
"doctrine/annotations": "~1.7",
|
||||
"doctrine/cache": "~1.6",
|
||||
"doctrine/collections": "^1.4",
|
||||
"doctrine/common": "^2.7.1",
|
||||
"doctrine/dbal": "^2.6",
|
||||
"doctrine/dbal": "dev-missed-commits",
|
||||
"doctrine/event-manager": "^1.0",
|
||||
"doctrine/inflector": "~1.0",
|
||||
"doctrine/instantiator": "~1.1",
|
||||
"symfony/console": "~3.0|~4.0"
|
||||
"doctrine/persistence": "^1.1",
|
||||
"doctrine/reflection": "^1.0",
|
||||
"ocramius/package-versions": "^1.1.2",
|
||||
"ocramius/proxy-manager": "^2.1.1",
|
||||
"symfony/console": "~4.0|~5.0",
|
||||
"symfony/var-dumper": "^4.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^1.0",
|
||||
"phpunit/phpunit": "^6.5",
|
||||
"squizlabs/php_codesniffer": "^3.2",
|
||||
"symfony/yaml": "~3.4|~4.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
|
||||
"doctrine/coding-standard": "^6.0",
|
||||
"phpstan/phpstan": "^0.11",
|
||||
"phpunit/phpunit": "^7.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Doctrine\\ORM\\": "lib/Doctrine/ORM" }
|
||||
@@ -45,11 +65,6 @@
|
||||
}
|
||||
},
|
||||
"bin": ["bin/doctrine"],
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.6.x-dev"
|
||||
}
|
||||
},
|
||||
"archive": {
|
||||
"exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"]
|
||||
}
|
||||
|
||||
4214
composer.lock
generated
Normal file
4214
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
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
|
||||
@@ -1,4 +1,4 @@
|
||||
The Doctrine2 documentation is licensed under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US)
|
||||
The Doctrine2 documentation is licensed under [CC BY-NC-SA 3.0](https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US)
|
||||
|
||||
Creative Commons Legal Code
|
||||
|
||||
@@ -337,7 +337,6 @@ BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
License is not intended to restrict the license of any rights under
|
||||
applicable law.
|
||||
|
||||
|
||||
Creative Commons Notice
|
||||
|
||||
Creative Commons is not a party to this License, and makes no warranty
|
||||
@@ -359,5 +358,4 @@ Creative Commons Notice
|
||||
available upon request from time to time. For the avoidance of doubt,
|
||||
this trademark restriction does not form part of this License.
|
||||
|
||||
Creative Commons may be contacted at http://creativecommons.org/.
|
||||
|
||||
Creative Commons may be contacted at https://creativecommons.org/.
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# Doctrine ORM Documentation
|
||||
|
||||
## How to Generate:
|
||||
Using Ubuntu 14.04 LTS:
|
||||
|
||||
1. Run ./bin/install-dependencies.sh
|
||||
2. Run ./bin/generate-docs.sh
|
||||
|
||||
It will generate the documentation into the build directory of the checkout.
|
||||
|
||||
|
||||
## Theme issues
|
||||
|
||||
If you get a "Theme error", check if the `en/_theme` subdirectory is empty,
|
||||
in which case you will need to run:
|
||||
|
||||
1. git submodule init
|
||||
2. git submodule update
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/bash
|
||||
EXECPATH=`dirname $0`
|
||||
cd $EXECPATH
|
||||
cd ..
|
||||
|
||||
rm build -Rf
|
||||
sphinx-build en build
|
||||
|
||||
sphinx-build -b latex en build/pdf
|
||||
rubber --into build/pdf --pdf build/pdf/Doctrine2ORM.tex
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
sudo apt-get update && sudo apt-get install -y python2.7 python-sphinx python-pygments
|
||||
@@ -1,89 +0,0 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Doctrine2ORM.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Doctrine2ORM.qhc"
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
|
||||
"run these through (pdf)latex."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
@@ -1,93 +0,0 @@
|
||||
#Copyright (c) 2010 Fabien Potencier
|
||||
#
|
||||
#Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
#of this software and associated documentation files (the "Software"), to deal
|
||||
#in the Software without restriction, including without limitation the rights
|
||||
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
#copies of the Software, and to permit persons to whom the Software is furnished
|
||||
#to do so, subject to the following conditions:
|
||||
#
|
||||
#The above copyright notice and this permission notice shall be included in all
|
||||
#copies or substantial portions of the Software.
|
||||
#
|
||||
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
#THE SOFTWARE.
|
||||
|
||||
from docutils.parsers.rst import Directive, directives
|
||||
from docutils import nodes
|
||||
from string import upper
|
||||
|
||||
class configurationblock(nodes.General, nodes.Element):
|
||||
pass
|
||||
|
||||
class ConfigurationBlock(Directive):
|
||||
has_content = True
|
||||
required_arguments = 0
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = True
|
||||
option_spec = {}
|
||||
formats = {
|
||||
'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):
|
||||
env = self.state.document.settings.env
|
||||
|
||||
node = nodes.Element()
|
||||
node.document = self.state.document
|
||||
self.state.nested_parse(self.content, self.content_offset, node)
|
||||
|
||||
entries = []
|
||||
for i, child in enumerate(node):
|
||||
if isinstance(child, nodes.literal_block):
|
||||
# add a title (the language name) before each block
|
||||
#targetid = "configuration-block-%d" % env.new_serialno('configuration-block')
|
||||
#targetnode = nodes.target('', '', ids=[targetid])
|
||||
#targetnode.append(child)
|
||||
|
||||
innernode = nodes.emphasis(self.formats[child['language']], self.formats[child['language']])
|
||||
|
||||
para = nodes.paragraph()
|
||||
para += [innernode, child]
|
||||
|
||||
entry = nodes.list_item('')
|
||||
entry.append(para)
|
||||
entries.append(entry)
|
||||
|
||||
resultnode = configurationblock()
|
||||
resultnode.append(nodes.bullet_list('', *entries))
|
||||
|
||||
return [resultnode]
|
||||
|
||||
def visit_configurationblock_html(self, node):
|
||||
self.body.append(self.starttag(node, 'div', CLASS='configuration-block'))
|
||||
|
||||
def depart_configurationblock_html(self, node):
|
||||
self.body.append('</div>\n')
|
||||
|
||||
def visit_configurationblock_latex(self, node):
|
||||
pass
|
||||
|
||||
def depart_configurationblock_latex(self, node):
|
||||
pass
|
||||
|
||||
def setup(app):
|
||||
app.add_node(configurationblock,
|
||||
html=(visit_configurationblock_html, depart_configurationblock_html),
|
||||
latex=(visit_configurationblock_latex, depart_configurationblock_latex))
|
||||
app.add_directive('configuration-block', ConfigurationBlock)
|
||||
Submodule docs/en/_theme deleted from 6f1bc8bead
@@ -1,711 +0,0 @@
|
||||
What is new in Doctrine ORM 2.5?
|
||||
================================
|
||||
|
||||
This document describes changes between Doctrine ORM 2.4 and 2.5.
|
||||
It contains a description of all the new features and sections about
|
||||
behavioral changes and potential backwards compatibility breaks.
|
||||
Please review this document carefully when updating to Doctrine 2.5.
|
||||
|
||||
First note, that with the ORM 2.5 release we are dropping support
|
||||
for PHP 5.3. We are enforcing this with Composer, servers without
|
||||
at least PHP 5.4 will not allow installing Doctrine 2.5.
|
||||
|
||||
New Features and Improvements
|
||||
-----------------------------
|
||||
|
||||
Events: PostLoad now triggered after associations are loaded
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Before Doctrine 2.5 if you had an entity with a ``@PostLoad`` event
|
||||
defined then Doctrine would trigger listeners after the fields were
|
||||
loaded, but before assocations are available.
|
||||
|
||||
- `DDC-54 <http://doctrine-project.org/jira/browse/DDC-54>`_
|
||||
- `Commit #a90629 <https://github.com/doctrine/doctrine2/commit/a906295c65f1516737458fbee2f6fa96254f27a5>`_
|
||||
|
||||
Events: Add API to programatically add event listeners to Entity
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When developing third party libraries or decoupled applications
|
||||
it can be interesting to develop an entity listener without knowing
|
||||
the entities that require this listener.
|
||||
|
||||
You can now attach entity listeners to entities using the
|
||||
``AttachEntityListenersListener`` class, which is listening to the
|
||||
``loadMetadata`` event that is fired once for every entity during
|
||||
metadata generation:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Tools\AttachEntityListenersListener;
|
||||
use Doctrine\ORM\Events;
|
||||
|
||||
$listener = new AttachEntityListenersListener();
|
||||
$listener->addEntityListener(
|
||||
'MyProject\Entity\User', 'MyProject\Listener\TimestampableListener',
|
||||
Events::prePersist, 'onPrePersist'
|
||||
);
|
||||
|
||||
$evm->addEventListener(Events::loadClassMetadata, $listener);
|
||||
|
||||
class TimestampableListener
|
||||
{
|
||||
public function onPrePersist($event)
|
||||
{
|
||||
$entity = $event->getEntity();
|
||||
$entity->setCreated(new \DateTime('now'));
|
||||
}
|
||||
}
|
||||
|
||||
Embeddable Objects
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Doctrine now supports creating multiple PHP objects from one database table
|
||||
implementing a feature called "Embeddable Objects". Next to an ``@Entity``
|
||||
class you can now define a class that is embeddable into a database table of an
|
||||
entity using the ``@Embeddable`` annotation. Embeddable objects can never be
|
||||
saved, updated or deleted on their own, only as part of an entity (called
|
||||
"root-entity" or "aggregate"). Consequently embeddables don't have a primary
|
||||
key, they are identified only by their values.
|
||||
|
||||
Example of defining and using embeddables classes:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
/** @Entity */
|
||||
class Product
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
|
||||
/** @Embedded(class = "Money") */
|
||||
private $price;
|
||||
}
|
||||
|
||||
/** @Embeddable */
|
||||
class Money
|
||||
{
|
||||
/** @Column(type = "decimal") */
|
||||
private $value;
|
||||
|
||||
/** @Column(type = "string") */
|
||||
private $currency = 'EUR';
|
||||
}
|
||||
|
||||
You can read more on the features of Embeddables objects `in the documentation
|
||||
<http://docs.doctrine-project.org/en/latest/tutorials/embeddables.html>`_.
|
||||
|
||||
This feature was developed by external contributor `Johannes Schmitt
|
||||
<https://twitter.com/schmittjoh>`_
|
||||
|
||||
- `DDC-93 <http://doctrine-project.org/jira/browse/DDC-93>`_
|
||||
- `Pull Request #835 <https://github.com/doctrine/doctrine2/pull/835>`_
|
||||
|
||||
Second-Level-Cache
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Since version 2.0 of Doctrine, fetching the same object twice by primary key
|
||||
would result in just one query. This was achieved by the identity map pattern
|
||||
(first-level-cache) that kept entities in memory.
|
||||
|
||||
The newly introduced second-level-cache works a bit differently. Instead
|
||||
of saving objects in memory, it saves them in a fast in-memory cache such
|
||||
as Memcache, Redis, Riak or MongoDB. Additionally it allows saving the result
|
||||
of more complex queries than by primary key. Summarized this feature works
|
||||
like the existing Query result cache, but it is much more powerful.
|
||||
|
||||
As an example lets cache an entity Country that is a relation to the User
|
||||
entity. We always want to display the country, but avoid the additional
|
||||
query to this table.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Cache(usage="READ_ONLY", region="country_region")
|
||||
*/
|
||||
class Country
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
* @Column(type="integer")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @Column(unique=true)
|
||||
*/
|
||||
protected $name;
|
||||
}
|
||||
|
||||
In this example we have specified a caching region name called
|
||||
``country_region``, which we have to configure now on the EntityManager:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$config = new \Doctrine\ORM\Configuration();
|
||||
$config->setSecondLevelCacheEnabled();
|
||||
|
||||
$cacheConfig = $config->getSecondLevelCacheConfiguration();
|
||||
$regionConfig = $cacheConfig->getRegionsConfiguration();
|
||||
$regionConfig->setLifetime('country_region', 3600);
|
||||
|
||||
Now Doctrine will first check for the data of any country in the cache
|
||||
instead of the database.
|
||||
|
||||
- `Documentation
|
||||
<http://docs.doctrine-project.org/en/latest/reference/second-level-cache.html>`_
|
||||
- `Pull Request #808 <https://github.com/doctrine/doctrine2/pull/808>`_
|
||||
|
||||
Criteria API: Support for ManyToMany assocations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
We introduced support for querying collections using the `Criteria API
|
||||
<http://docs.doctrine-project.org/en/latest/reference/working-with-associations.html#filtering-collections>`_
|
||||
in 2.4. This only worked efficently for One-To-Many assocations, not for
|
||||
Many-To-Many. With the start of 2.5 also Many-To-Many associations get queried
|
||||
instead of loading them into memory.
|
||||
|
||||
Criteria API: Add new contains() expression
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
It is now possible to use the Criteria API to check for string contains needle
|
||||
using ``contains()``. This translates to using a ``column LIKE '%needle%'`` SQL
|
||||
condition.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use \Doctrine\Common\Collections\Criteria;
|
||||
|
||||
$criteria = Criteria::create()
|
||||
->where(Criteria::expr()->contains('name', 'Benjamin'));
|
||||
|
||||
$users = $repository->matching($criteria);
|
||||
|
||||
Criteria API: Support for EXTRA_LAZY
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A collection that is marked as ``fetch="EXTRA_LAZY"`` will now return another
|
||||
lazy collection when using ``Collection::matching($criteria)``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
class Post
|
||||
{
|
||||
/** @OneToMany(targetEntity="Comment", fetch="EXTRA_LAZY") */
|
||||
private $comments;
|
||||
}
|
||||
|
||||
$criteria = Criteria::create()
|
||||
->where(Criteria->expr()->eq("published", 1));
|
||||
|
||||
$publishedComments = $post->getComments()->matching($criteria);
|
||||
|
||||
echo count($publishedComments);
|
||||
|
||||
The lazy criteria currently supports the ``count()`` and ``contains()``
|
||||
functionality lazily. All other operations of the ``Collection`` interface
|
||||
trigger a full load of the collection.
|
||||
|
||||
This feature was contributed by `Michaël Gallego <https://github.com/bakura10>`_.
|
||||
|
||||
- `Pull Request #882 <https://github.com/doctrine/doctrine2/pull/882>`_
|
||||
- `Pull Request #1032 <https://github.com/doctrine/doctrine2/pull/1032>`_
|
||||
|
||||
Mapping: Allow configuring Index flags
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
It is now possible to control the index flags in the DBAL
|
||||
schema abstraction from the ORM using metadata. This was possible
|
||||
only with a schema event listener before.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @Table(name="product", indexes={@Index(columns={"description"},flags={"fulltext"})})
|
||||
*/
|
||||
class Product
|
||||
{
|
||||
private $description;
|
||||
}
|
||||
|
||||
This feature was contributed by `Adrian Olek <https://github.com/adrianolek>`_.
|
||||
|
||||
- `Pull Request #973 <https://github.com/doctrine/doctrine2/pull/973>`_
|
||||
|
||||
SQLFilter API: Check if a parameter is set
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can now check in your SQLFilter if a parameter was set. This allows
|
||||
to more easily control which features of a filter to enable or disable.
|
||||
|
||||
Extending on the locale example of the documentation:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class MyLocaleFilter extends SQLFilter
|
||||
{
|
||||
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
|
||||
{
|
||||
if (!$targetEntity->reflClass->implementsInterface('LocaleAware')) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!$this->hasParameter('locale')) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return $targetTableAlias.'.locale = ' . $this->getParameter('locale');
|
||||
}
|
||||
}
|
||||
|
||||
This feature was contributed by `Miroslav Demovic <https://github.com/mdemo>`_
|
||||
|
||||
- `Pull Request #963 <https://github.com/doctrine/doctrine2/pull/963>`_
|
||||
|
||||
|
||||
EXTRA_LAZY Improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Efficient query when using EXTRA_LAZY and containsKey
|
||||
|
||||
When calling ``Collection::containsKey($key)`` on one-to-many and many-to-many
|
||||
collections using ``indexBy`` and ``EXTRA_LAZY`` a query is now executed to check
|
||||
for the existance for the item. Prevoiusly this operation was performed in memory
|
||||
by loading all entities of the collection.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
class User
|
||||
{
|
||||
/** @OneToMany(targetEntity="Group", indexBy="id") */
|
||||
private $groups;
|
||||
}
|
||||
|
||||
if ($user->getGroups()->containsKey($groupId)) {
|
||||
echo "User is in group $groupId\n";
|
||||
}
|
||||
|
||||
This feature was contributed by `Asmir Mustafic <https://github.com/goetas>`_
|
||||
|
||||
- `Pull Request #937 <https://github.com/doctrine/doctrine2/pull/937>`_
|
||||
|
||||
2. Add EXTRA_LAZY Support for get() for owning and inverse many-to-many
|
||||
|
||||
This was contributed by `Sander Marechal <https://github.com/sandermarechal>`_.
|
||||
|
||||
Improve efficiency of One-To-Many EAGER
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When marking a one-to-many association with ``fetch="EAGER"`` it will now
|
||||
execute one query less than before and work correctly in combination with
|
||||
``indexBy``.
|
||||
|
||||
Better support for EntityManagerInterface
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Many of the locations where previously only the ``Doctrine\ORM\EntityManager``
|
||||
was allowed are now changed to accept the ``EntityManagerInterface`` that was
|
||||
introduced in 2.4. This allows you to more easily use the decorator pattern
|
||||
to extend the EntityManager if you need. It's still not replaced everywhere,
|
||||
so you still have to be careful.
|
||||
|
||||
DQL Improvements
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
1. It is now possible to add functions to the ``ORDER BY`` clause in DQL statements:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$dql = "SELECT u FROM User u ORDER BY CONCAT(u.username, u.name)";
|
||||
|
||||
2. Support for functions in ``IS NULL`` expressions:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$dql = "SELECT u.name FROM User u WHERE MAX(u.name) IS NULL";
|
||||
|
||||
3. A ``LIKE`` expression is now suported in ``HAVING`` clause.
|
||||
|
||||
4. Subselects are now supported inside a ``NEW()`` expression:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$dql = "SELECT new UserDTO(u.name, SELECT count(g.id) FROM Group g WHERE g.id = u.id) FROM User u";
|
||||
|
||||
5. ``MEMBER OF`` expression now allows to filter for more than one result:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$dql = "SELECT u FROM User u WHERE :groups MEMBER OF u.groups";
|
||||
$query = $entityManager->createQuery($dql);
|
||||
$query->setParameter('groups', array(1, 2, 3));
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
6. Expressions inside ``COUNT()`` now allowed
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$dql = "SELECT COUNT(DISTINCT CONCAT(u.name, u.lastname)) FROM User u";
|
||||
|
||||
7. Add support for ``HOUR`` in ``DATE_ADD()``/``DATE_SUB()`` functions
|
||||
|
||||
Custom DQL Functions: Add support for factories
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Previously custom DQL functions could only be provided with their
|
||||
full-qualified class-name, preventing runtime configuration through
|
||||
dependency injection.
|
||||
|
||||
A simplistic approach has been contributed by `Matthieu Napoli
|
||||
<https://github.com/mnapoli>`_ to pass a callback instead that resolves
|
||||
the function:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
$config = new \Doctrine\ORM\Configuration();
|
||||
|
||||
$config->addCustomNumericFunction(
|
||||
'IS_PUBLISHED', function($funcName) use ($currentSiteId) {
|
||||
return new IsPublishedFunction($currentSiteId);
|
||||
}
|
||||
);
|
||||
|
||||
Query API: WHERE IN Query using a Collection as parameter
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When performing a ``WHERE IN`` query for a collection of entities you can
|
||||
now pass the array collection of entities as a parameter value to the query
|
||||
object:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
$categories = $rootCategory->getChildren();
|
||||
|
||||
$queryBuilder
|
||||
->select('p')
|
||||
->from('Product', 'p')
|
||||
->where('p.category IN (:categories)')
|
||||
->setParameter('categories', $categories)
|
||||
;
|
||||
|
||||
This feature was contributed by `Michael Perrin
|
||||
<https://github.com/michaelperrin>`_.
|
||||
|
||||
- `Pull Request #590 <https://github.com/doctrine/doctrine2/pull/590>`_
|
||||
- `DDC-2319 <http://doctrine-project.org/jira/browse/DDC-2319>`_
|
||||
|
||||
Query API: Add support for default Query Hints
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To configure multiple different features such as custom AST Walker, fetch modes,
|
||||
locking and other features affecting DQL generation we have had a feature
|
||||
called "query hints" since version 2.0.
|
||||
|
||||
It is now possible to add query hints that are always enabled for every Query:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
$config = new \Doctrine\ORM\Configuration();
|
||||
$config->setDefaultQueryHints(
|
||||
'doctrine.customOutputWalker' => 'MyProject\CustomOutputWalker'
|
||||
);
|
||||
|
||||
This feature was contributed by `Artur Eshenbrener
|
||||
<https://github.com/Strate>`_.
|
||||
|
||||
- `Pull Request #863 <https://github.com/doctrine/doctrine2/pull/863>`_
|
||||
|
||||
ResultSetMappingBuilder: Add support for Single-Table Inheritance
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Before 2.5 the ResultSetMappingBuilder did not work with entities
|
||||
that are using Single-Table-Inheritance. This restriction was lifted
|
||||
by adding the missing support.
|
||||
|
||||
YAML Mapping: Many-To-Many doesnt require join column definition
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In Annotations and XML it was not necessary using conventions for naming
|
||||
the many-to-many join column names, in YAML it was not possible however.
|
||||
|
||||
A many-to-many definition in YAML is now possible using this minimal
|
||||
definition:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
manyToMany:
|
||||
groups:
|
||||
targetEntity: Group
|
||||
joinTable:
|
||||
name: users_groups
|
||||
|
||||
Schema Validator Command: Allow to skip sub-checks
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The Schema Validator command executes two independent checks
|
||||
for validity of the mappings and if the schema is synchronized
|
||||
correctly. It is now possible to skip any of the two steps
|
||||
when executing the command:
|
||||
|
||||
::
|
||||
|
||||
$ php vendor/bin/doctrine orm:validate-schema --skip-mapping
|
||||
$ php vendor/bin/doctrine orm:validate-schema --skip-sync
|
||||
|
||||
This allows you to write more specialized continuous integration and automation
|
||||
checks. When no changes are found the command returns the exit code 0
|
||||
and 1, 2 or 3 when failing because of mapping, sync or both.
|
||||
|
||||
EntityGenerator Command: Avoid backups
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When calling the EntityGenerator for an existing entity, Doctrine would
|
||||
create a backup file every time to avoid losing changes to the code. You
|
||||
can now skip generating the backup file by passing the ``--no-backup``
|
||||
flag:
|
||||
|
||||
::
|
||||
|
||||
$ php vendor/bin/doctrine orm:generate-entities src/ --no-backup
|
||||
|
||||
Support for Objects as Identifiers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
It is now possible to use Objects as identifiers for Entities
|
||||
as long as they implement the magic method ``__toString()``.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
class UserId
|
||||
{
|
||||
private $value;
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return (string)$this->value;
|
||||
}
|
||||
}
|
||||
|
||||
class User
|
||||
{
|
||||
/** @Id @Column(type="userid") */
|
||||
private $id;
|
||||
|
||||
public function __construct(UserId $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
}
|
||||
|
||||
class UserIdType extends \Doctrine\DBAL\Types\Type
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
Doctrine\DBAL\Types\Type::addType('userid', 'MyProject\UserIdType');
|
||||
|
||||
Behavioral Changes (BC Breaks)
|
||||
------------------------------
|
||||
|
||||
NamingStrategy interface changed
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``Doctrine\ORM\Mapping\NamingStrategyInterface`` changed slightly
|
||||
to pass the Class Name of the entity into the join column name generation:
|
||||
|
||||
::
|
||||
|
||||
- function joinColumnName($propertyName);
|
||||
+ function joinColumnName($propertyName, $className = null);
|
||||
|
||||
It also received a new method for supporting embeddables:
|
||||
|
||||
::
|
||||
|
||||
public function embeddedFieldToColumnName($propertyName, $embeddedColumnName);
|
||||
|
||||
Minor BC BREAK: EntityManagerInterface instead of EntityManager in type-hints
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As of 2.5, classes requiring the ``EntityManager`` in any method signature will now require
|
||||
an ``EntityManagerInterface`` instead.
|
||||
If you are extending any of the following classes, then you need to check following
|
||||
signatures:
|
||||
|
||||
- ``Doctrine\ORM\Tools\DebugUnitOfWorkListener#dumpIdentityMap(EntityManagerInterface $em)``
|
||||
- ``Doctrine\ORM\Mapping\ClassMetadataFactory#setEntityManager(EntityManagerInterface $em)``
|
||||
|
||||
Minor BC BREAK: Custom Hydrators API change
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As of 2.5, ``AbstractHydrator`` does not enforce the usage of cache as part of
|
||||
API, and now provides you a clean API for column information through the method
|
||||
``hydrateColumnInfo($column)``.
|
||||
Cache variable being passed around by reference is no longer needed since
|
||||
Hydrators are per query instantiated since Doctrine 2.4.
|
||||
|
||||
- `DDC-3060 <http://doctrine-project.org/jira/browse/DDC-3060>`_
|
||||
|
||||
Minor BC BREAK: All non-transient classes in an inheritance must be part of the inheritance map
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As of 2.5, classes, if you define an inheritance map for an inheritance tree, you are required
|
||||
to map all non-transient classes in that inheritance, including the root of the inheritance.
|
||||
|
||||
So far, the root of the inheritance was allowed to be skipped in the inheritance map: this is
|
||||
not possible anymore, and if you don't plan to persist instances of that class, then you should
|
||||
either:
|
||||
|
||||
- make that class as ``abstract``
|
||||
- add that class to your inheritance map
|
||||
|
||||
If you fail to do so, then a ``Doctrine\ORM\Mapping\MappingException`` will be thrown.
|
||||
|
||||
|
||||
- `DDC-3300 <http://doctrine-project.org/jira/browse/DDC-3300>`_
|
||||
- `DDC-3503 <http://doctrine-project.org/jira/browse/DDC-3503>`_
|
||||
|
||||
Minor BC BREAK: Entity based EntityManager#clear() calls follow cascade detach
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Whenever ``EntityManager#clear()`` method gets called with a given entity class
|
||||
name, until 2.4, it was only detaching the specific requested entity.
|
||||
As of 2.5, ``EntityManager`` will follow configured cascades, providing a better
|
||||
memory management since associations will be garbage collected, optimizing
|
||||
resources consumption on long running jobs.
|
||||
|
||||
Updates on entities scheduled for deletion are no longer processed
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In Doctrine 2.4, if you modified properties of an entity scheduled for deletion, UnitOfWork would
|
||||
produce an ``UPDATE`` statement to be executed right before the ``DELETE`` statement. The entity in question
|
||||
was therefore present in ``UnitOfWork#entityUpdates``, which means that ``preUpdate`` and ``postUpdate``
|
||||
listeners were (quite pointlessly) called. In ``preFlush`` listeners, it used to be possible to undo
|
||||
the scheduled deletion for updated entities (by calling ``persist()`` if the entity was found in both
|
||||
``entityUpdates`` and ``entityDeletions``). This does not work any longer, because the entire changeset
|
||||
calculation logic is optimized away.
|
||||
|
||||
Minor BC BREAK: Default lock mode changed from LockMode::NONE to null in method signatures
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A misconception concerning default lock mode values in method signatures lead to unexpected behaviour
|
||||
in SQL statements on SQL Server. With a default lock mode of ``LockMode::NONE`` throughout the
|
||||
method signatures in ORM, the table lock hint ``WITH (NOLOCK)`` was appended to all locking related
|
||||
queries by default. This could result in unpredictable results because an explicit ``WITH (NOLOCK)``
|
||||
table hint tells SQL Server to run a specific query in transaction isolation level READ UNCOMMITTED
|
||||
instead of the default READ COMMITTED transaction isolation level.
|
||||
Therefore there now is a distinction between ``LockMode::NONE`` and ``null`` to be able to tell
|
||||
Doctrine whether to add table lock hints to queries by intention or not. To achieve this, the following
|
||||
method signatures have been changed to declare ``$lockMode = null`` instead of ``$lockMode = LockMode::NONE``:
|
||||
|
||||
- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#getSelectSQL()``
|
||||
- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#load()``
|
||||
- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#refresh()``
|
||||
- ``Doctrine\ORM\Decorator\EntityManagerDecorator#find()``
|
||||
- ``Doctrine\ORM\EntityManager#find()``
|
||||
- ``Doctrine\ORM\EntityRepository#find()``
|
||||
- ``Doctrine\ORM\Persisters\BasicEntityPersister#getSelectSQL()``
|
||||
- ``Doctrine\ORM\Persisters\BasicEntityPersister#load()``
|
||||
- ``Doctrine\ORM\Persisters\BasicEntityPersister#refresh()``
|
||||
- ``Doctrine\ORM\Persisters\EntityPersister#getSelectSQL()``
|
||||
- ``Doctrine\ORM\Persisters\EntityPersister#load()``
|
||||
- ``Doctrine\ORM\Persisters\EntityPersister#refresh()``
|
||||
- ``Doctrine\ORM\Persisters\JoinedSubclassPersister#getSelectSQL()``
|
||||
|
||||
You should update signatures for these methods if you have subclassed one of the above classes.
|
||||
Please also check the calling code of these methods in your application and update if necessary.
|
||||
|
||||
.. note::
|
||||
|
||||
This in fact is really a minor BC BREAK and should not have any affect on database vendors
|
||||
other than SQL Server because it is the only one that supports and therefore cares about
|
||||
``LockMode::NONE``. It's really just a FIX for SQL Server environments using ORM.
|
||||
|
||||
Minor BC BREAK: __clone method not called anymore when entities are instantiated via metadata API
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As of PHP 5.6, instantiation of new entities is deferred to the
|
||||
`doctrine/instantiator <https://github.com/doctrine/instantiator>`_ library, which will avoid calling ``__clone``
|
||||
or any public API on instantiated objects.
|
||||
|
||||
BC BREAK: DefaultRepositoryFactory is now final
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Please implement the ``Doctrine\ORM\Repository\RepositoryFactory`` interface instead of extending
|
||||
the ``Doctrine\ORM\Repository\DefaultRepositoryFactory``.
|
||||
|
||||
BC BREAK: New object expression DQL queries now respects user provided aliasing and not return consumed fields
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When executing DQL queries with new object expressions, instead of returning
|
||||
DTOs numerically indexes, it will now respect user provided aliases. Consider
|
||||
the following query:
|
||||
|
||||
::
|
||||
|
||||
SELECT new UserDTO(u.id,u.name) as user,new AddressDTO(a.street,a.postalCode) as address, a.id as addressId
|
||||
FROM User u INNER JOIN u.addresses a WITH a.isPrimary = true
|
||||
|
||||
Previously, your result would be similar to this:
|
||||
|
||||
::
|
||||
|
||||
array(
|
||||
0=>array(
|
||||
0=>{UserDTO object},
|
||||
1=>{AddressDTO object},
|
||||
2=>{u.id scalar},
|
||||
3=>{u.name scalar},
|
||||
4=>{a.street scalar},
|
||||
5=>{a.postalCode scalar},
|
||||
'addressId'=>{a.id scalar},
|
||||
),
|
||||
...
|
||||
)
|
||||
|
||||
From now on, the resultset will look like this:
|
||||
|
||||
::
|
||||
|
||||
array(
|
||||
0=>array(
|
||||
'user'=>{UserDTO object},
|
||||
'address'=>{AddressDTO object},
|
||||
'addressId'=>{a.id scalar}
|
||||
),
|
||||
...
|
||||
)
|
||||
|
||||
201
docs/en/conf.py
201
docs/en/conf.py
@@ -1,201 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Doctrine 2 ORM documentation build configuration file, created by
|
||||
# sphinx-quickstart on Fri Dec 3 18:10:24 2010.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os, datetime
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
sys.path.append(os.path.abspath('_exts'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['configurationblock']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Doctrine 2 ORM'
|
||||
copyright = u'2010-%y, Doctrine Project Team'.format(datetime.date.today)
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '2'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '2'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
language = 'en'
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of documents that shouldn't be included in the build.
|
||||
#unused_docs = []
|
||||
|
||||
# List of directories, relative to source directory, that shouldn't be searched
|
||||
# for source files.
|
||||
exclude_trees = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
show_authors = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
html_theme = 'doctrine'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = ['_theme']
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_use_modindex = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = ''
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Doctrine2ORMdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'Doctrine2ORM.tex', u'Doctrine 2 ORM Documentation',
|
||||
u'Doctrine Project Team', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_use_modindex = True
|
||||
|
||||
primary_domain = "dcorm"
|
||||
|
||||
def linkcode_resolve(domain, info):
|
||||
if domain == 'dcorm':
|
||||
return 'http://'
|
||||
return None
|
||||
@@ -4,18 +4,18 @@ Advanced field value conversion using custom mapping types
|
||||
.. sectionauthor:: Jan Sorgalla <jsorgalla@googlemail.com>
|
||||
|
||||
When creating entities, you sometimes have the need to transform field values
|
||||
before they are saved to the database. In Doctrine you can use Custom Mapping
|
||||
before they are saved to the database. In Doctrine you can use Custom Mapping
|
||||
Types to solve this (see: :ref:`reference-basic-mapping-custom-mapping-types`).
|
||||
|
||||
There are several ways to achieve this: converting the value inside the Type
|
||||
class, converting the value on the database-level or a combination of both.
|
||||
|
||||
This article describes the third way by implementing the MySQL specific column
|
||||
type `Point <http://dev.mysql.com/doc/refman/5.5/en/gis-class-point.html>`_.
|
||||
type `Point <https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html>`_.
|
||||
|
||||
The ``Point`` type is part of the `Spatial extension <http://dev.mysql.com/doc/refman/5.5/en/spatial-extensions.html>`_
|
||||
The ``Point`` type is part of the `Spatial extension <https://dev.mysql.com/doc/refman/8.0/en/spatial-extensions.html>`_
|
||||
of MySQL and enables you to store a single location in a coordinate space by
|
||||
using x and y coordinates. You can use the Point type to store a
|
||||
using x and y coordinates. You can use the Point type to store a
|
||||
longitude/latitude pair to represent a geographic location.
|
||||
|
||||
The entity
|
||||
@@ -29,23 +29,25 @@ The entity class:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
namespace Geo\Entity;
|
||||
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Location
|
||||
{
|
||||
/**
|
||||
* @Column(type="point")
|
||||
* @ORM\Column(type="point")
|
||||
*
|
||||
* @var \Geo\ValueObject\Point
|
||||
*/
|
||||
private $point;
|
||||
|
||||
/**
|
||||
* @Column(type="string")
|
||||
* @ORM\Column(type="string")
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
@@ -84,7 +86,7 @@ The entity class:
|
||||
}
|
||||
}
|
||||
|
||||
We use the custom type ``point`` in the ``@Column`` docblock annotation of the
|
||||
We use the custom type ``point`` in the ``@Column`` docblock annotation of the
|
||||
``$point`` field. We will create this custom mapping type in the next chapter.
|
||||
|
||||
The point class:
|
||||
@@ -92,7 +94,7 @@ The point class:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
namespace Geo\ValueObject;
|
||||
|
||||
class Point
|
||||
@@ -192,11 +194,11 @@ object into a string representation before saving to the database (in the
|
||||
``convertToDatabaseValue`` method) and back into an object after fetching the
|
||||
value from the database (in the ``convertToPHPValue`` method).
|
||||
|
||||
The format of the string representation format is called `Well-known text (WKT)
|
||||
<http://en.wikipedia.org/wiki/Well-known_text>`_. The advantage of this format
|
||||
is, that it is both human readable and parsable by MySQL.
|
||||
The format of the string representation format is called
|
||||
`Well-known text (WKT) <https://en.wikipedia.org/wiki/Well-known_text>`_.
|
||||
The advantage of this format is, that it is both human readable and parsable by MySQL.
|
||||
|
||||
Internally, MySQL stores geometry values in a binary format that is not
|
||||
Internally, MySQL stores geometry values in a binary format that is not
|
||||
identical to the WKT format. So, we need to let MySQL transform the WKT
|
||||
representation into its internal format.
|
||||
|
||||
@@ -204,19 +206,19 @@ This is where the ``convertToPHPValueSQL`` and ``convertToDatabaseValueSQL``
|
||||
methods come into play.
|
||||
|
||||
This methods wrap a sql expression (the WKT representation of the Point) into
|
||||
MySQL functions `PointFromText <http://dev.mysql.com/doc/refman/5.5/en/creating-spatial-values.html#function_pointfromtext>`_
|
||||
and `AsText <http://dev.mysql.com/doc/refman/5.5/en/functions-to-convert-geometries-between-formats.html#function_astext>`_
|
||||
MySQL functions `ST_PointFromText <https://dev.mysql.com/doc/refman/8.0/en/gis-wkt-functions.html#function_st-pointfromtext>`_
|
||||
and `ST_AsText <https://dev.mysql.com/doc/refman/8.0/en/gis-format-conversion-functions.html#function_st-astext>`_
|
||||
which convert WKT strings to and from the internal format of MySQL.
|
||||
|
||||
.. note::
|
||||
|
||||
When using DQL queries, the ``convertToPHPValueSQL`` and
|
||||
When using DQL queries, the ``convertToPHPValueSQL`` and
|
||||
``convertToDatabaseValueSQL`` methods only apply to identification variables
|
||||
and path expressions in SELECT clauses. Expressions in WHERE clauses are
|
||||
and path expressions in SELECT clauses. Expressions in WHERE clauses are
|
||||
**not** wrapped!
|
||||
|
||||
If you want to use Point values in WHERE clauses, you have to implement a
|
||||
:doc:`user defined function <dql-user-defined-functions>` for
|
||||
:doc:`user defined function <dql-user-defined-functions>` for
|
||||
``PointFromText``.
|
||||
|
||||
Example usage
|
||||
@@ -252,5 +254,5 @@ Example usage
|
||||
$query = $em->createQuery("SELECT l FROM Geo\Entity\Location WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'");
|
||||
$location = $query->getSingleResult();
|
||||
|
||||
/* @var Geo\ValueObject\Point */
|
||||
/** @var Geo\ValueObject\Point $point */
|
||||
$point = $location->getPoint();
|
||||
|
||||
@@ -32,29 +32,28 @@ Our entities look like:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
namespace Bank\Entities;
|
||||
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Account
|
||||
{
|
||||
/** @Id @GeneratedValue @Column(type="integer") */
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/** @Column(type="string", unique=true) */
|
||||
|
||||
/** @ORM\Column(type="string", unique=true) */
|
||||
private $no;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"})
|
||||
*/
|
||||
|
||||
/** @ORM\OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"}) */
|
||||
private $entries;
|
||||
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
*/
|
||||
|
||||
/** @ORM\Column(type="integer") */
|
||||
private $maxCredit = 0;
|
||||
|
||||
|
||||
public function __construct($no, $maxCredit = 0)
|
||||
{
|
||||
$this->no = $no;
|
||||
@@ -62,32 +61,28 @@ Our entities look like:
|
||||
$this->entries = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Entry
|
||||
{
|
||||
/** @Id @GeneratedValue @Column(type="integer") */
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Account", inversedBy="entries")
|
||||
*/
|
||||
|
||||
/** @ORM\ManyToOne(targetEntity="Account", inversedBy="entries") */
|
||||
private $account;
|
||||
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
*/
|
||||
|
||||
/** @ORM\Column(type="integer") */
|
||||
private $amount;
|
||||
|
||||
|
||||
public function __construct($account, $amount)
|
||||
{
|
||||
$this->account = $account;
|
||||
$this->amount = $amount;
|
||||
// more stuff here, from/to whom, stated reason, execution date and such
|
||||
}
|
||||
|
||||
|
||||
public function getAmount()
|
||||
{
|
||||
return $this->amount;
|
||||
@@ -178,7 +173,7 @@ relation with this method:
|
||||
public function addEntry($amount)
|
||||
{
|
||||
$this->assertAcceptEntryAllowed($amount);
|
||||
|
||||
|
||||
$e = new Entry($this, $amount);
|
||||
$this->entries[] = $e;
|
||||
return $e;
|
||||
@@ -196,18 +191,18 @@ Now look at the following test-code for our entities:
|
||||
{
|
||||
$account = new Account("123456", $maxCredit = 200);
|
||||
$this->assertEquals(0, $account->getBalance());
|
||||
|
||||
|
||||
$account->addEntry(500);
|
||||
$this->assertEquals(500, $account->getBalance());
|
||||
|
||||
|
||||
$account->addEntry(-700);
|
||||
$this->assertEquals(-200, $account->getBalance());
|
||||
}
|
||||
|
||||
|
||||
public function testExceedMaxLimit()
|
||||
{
|
||||
$account = new Account("123456", $maxCredit = 200);
|
||||
|
||||
|
||||
$this->setExpectedException("Exception");
|
||||
$account->addEntry(-1000);
|
||||
}
|
||||
@@ -266,19 +261,19 @@ entries collection) we want to add an aggregate field called
|
||||
class Account
|
||||
{
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $balance = 0;
|
||||
|
||||
|
||||
public function getBalance()
|
||||
{
|
||||
return $this->balance;
|
||||
}
|
||||
|
||||
|
||||
public function addEntry($amount)
|
||||
{
|
||||
$this->assertAcceptEntryAllowed($amount);
|
||||
|
||||
|
||||
$e = new Entry($this, $amount);
|
||||
$this->entries[] = $e;
|
||||
$this->balance += $amount;
|
||||
@@ -309,20 +304,20 @@ potentially lead to inconsistent state. See this example:
|
||||
// The Account $accId has a balance of 0 and a max credit limit of 200:
|
||||
// request 1 account
|
||||
$account1 = $em->find('Bank\Entities\Account', $accId);
|
||||
|
||||
|
||||
// request 2 account
|
||||
$account2 = $em->find('Bank\Entities\Account', $accId);
|
||||
|
||||
|
||||
$account1->addEntry(-200);
|
||||
$account2->addEntry(-200);
|
||||
|
||||
|
||||
// now request 1 and 2 both flush the changes.
|
||||
|
||||
The aggregate field ``Account::$balance`` is now -200, however the
|
||||
SUM over all entries amounts yields -400. A violation of our max
|
||||
credit rule.
|
||||
|
||||
You can use both optimistic or pessimistic locking to save-guard
|
||||
You can use both optimistic or pessimistic locking to safe-guard
|
||||
your aggregate fields against this kind of race-conditions. Reading
|
||||
Eric Evans DDD carefully he mentions that the "Aggregate Root"
|
||||
(Account in our example) needs a locking mechanism.
|
||||
@@ -334,7 +329,7 @@ Optimistic locking is as easy as adding a version column:
|
||||
<?php
|
||||
class Account
|
||||
{
|
||||
/** @Column(type="integer") @Version */
|
||||
/** @ORM\Column(type="integer") @ORM\Version */
|
||||
private $version;
|
||||
}
|
||||
|
||||
@@ -350,7 +345,7 @@ the database using a FOR UPDATE.
|
||||
|
||||
<?php
|
||||
use Doctrine\DBAL\LockMode;
|
||||
|
||||
|
||||
$account = $em->find('Bank\Entities\Account', $accId, LockMode::PESSIMISTIC_READ);
|
||||
|
||||
Keeping Updates and Deletes in Sync
|
||||
@@ -373,4 +368,3 @@ the related objects that make up an aggregate value. Finally I
|
||||
showed how you can ensure that your aggregate fields do not get out
|
||||
of sync due to race-conditions and concurrent access.
|
||||
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ class name. Now the new type can be used when mapping columns:
|
||||
<?php
|
||||
class MyPersistentClass
|
||||
{
|
||||
/** @Column(type="mytype") */
|
||||
/** @ORM\Column(type="mytype") */
|
||||
private $field;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,45 +3,48 @@ Persisting the Decorator Pattern
|
||||
|
||||
.. sectionauthor:: Chris Woodford <chris.woodford@gmail.com>
|
||||
|
||||
This recipe will show you a simple example of how you can use
|
||||
This recipe will show you a simple example of how you can use
|
||||
Doctrine 2 to persist an implementation of the
|
||||
`Decorator Pattern <http://en.wikipedia.org/wiki/Decorator_pattern>`_
|
||||
`Decorator Pattern <https://en.wikipedia.org/wiki/Decorator_pattern>`_
|
||||
|
||||
Component
|
||||
---------
|
||||
|
||||
The ``Component`` class needs to be persisted, so it's going to
|
||||
be an ``Entity``. As the top of the inheritance hierarchy, it's going
|
||||
to have to define the persistent inheritance. For this example, we
|
||||
will use Single Table Inheritance, but Class Table Inheritance
|
||||
would work as well. In the discriminator map, we will define two
|
||||
concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.
|
||||
The ``Component`` class needs to be persisted, so it's going to
|
||||
be an ``Entity``. As the top of the inheritance hierarchy, it's going
|
||||
to have to define the persistent inheritance. For this example, we
|
||||
will use Single Table Inheritance, but Class Table Inheritance
|
||||
would work as well. In the discriminator map, we will define two
|
||||
concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
namespace Test;
|
||||
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("SINGLE_TABLE")
|
||||
* @DiscriminatorColumn(name="discr", type="string")
|
||||
* @DiscriminatorMap({"cc" = "Test\Component\ConcreteComponent",
|
||||
"cd" = "Test\Decorator\ConcreteDecorator"})
|
||||
* @ORM\Entity
|
||||
* @ORM\InheritanceType("SINGLE_TABLE")
|
||||
* @ORM\DiscriminatorColumn(name="discr", type="string")
|
||||
* @ORM\DiscriminatorMap({
|
||||
* "cc" = "Test\Component\ConcreteComponent",
|
||||
* "cd" = "Test\Decorator\ConcreteDecorator"
|
||||
* })
|
||||
*/
|
||||
abstract class Component
|
||||
{
|
||||
|
||||
/**
|
||||
* @Id @Column(type="integer")
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
* @ORM\Id @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/** @Column(type="string", nullable=true) */
|
||||
|
||||
/** @ORM\Column(type="string", nullable=true) */
|
||||
protected $name;
|
||||
|
||||
|
||||
/**
|
||||
* Get id
|
||||
* @return integer $id
|
||||
@@ -50,7 +53,7 @@ concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set name
|
||||
* @param string $name
|
||||
@@ -59,7 +62,7 @@ concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get name
|
||||
* @return string $name
|
||||
@@ -68,33 +71,34 @@ concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
ConcreteComponent
|
||||
-----------------
|
||||
|
||||
The ``ConcreteComponent`` class is pretty simple and doesn't do much
|
||||
more than extend the abstract ``Component`` class (only for the
|
||||
The ``ConcreteComponent`` class is pretty simple and doesn't do much
|
||||
more than extend the abstract ``Component`` class (only for the
|
||||
purpose of keeping this example simple).
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
namespace Test\Component;
|
||||
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
use Test\Component;
|
||||
|
||||
/** @Entity */
|
||||
|
||||
/** @ORM\Entity */
|
||||
class ConcreteComponent extends Component
|
||||
{}
|
||||
|
||||
|
||||
Decorator
|
||||
---------
|
||||
|
||||
The ``Decorator`` class doesn't need to be persisted, but it does
|
||||
need to define an association with a persisted ``Entity``. We can
|
||||
The ``Decorator`` class doesn't need to be persisted, but it does
|
||||
need to define an association with a persisted ``Entity``. We can
|
||||
use a ``MappedSuperclass`` for this.
|
||||
|
||||
.. code-block:: php
|
||||
@@ -102,17 +106,19 @@ use a ``MappedSuperclass`` for this.
|
||||
<?php
|
||||
|
||||
namespace Test;
|
||||
|
||||
/** @MappedSuperclass */
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\MappedSuperclass */
|
||||
abstract class Decorator extends Component
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @OneToOne(targetEntity="Test\Component", cascade={"all"})
|
||||
* @JoinColumn(name="decorates", referencedColumnName="id")
|
||||
* @ORM\OneToOne(targetEntity="Test\Component", cascade={"all"})
|
||||
* @ORM\JoinColumn(name="decorates", referencedColumnName="id")
|
||||
*/
|
||||
protected $decorates;
|
||||
|
||||
|
||||
/**
|
||||
* initialize the decorator
|
||||
* @param Component $c
|
||||
@@ -121,7 +127,7 @@ use a ``MappedSuperclass`` for this.
|
||||
{
|
||||
$this->setDecorates($c);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see Test.Component::getName()
|
||||
@@ -130,7 +136,7 @@ use a ``MappedSuperclass`` for this.
|
||||
{
|
||||
return 'Decorated ' . $this->getDecorates()->getName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* the component being decorated
|
||||
* @return Component
|
||||
@@ -139,7 +145,7 @@ use a ``MappedSuperclass`` for this.
|
||||
{
|
||||
return $this->decorates;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* sets the component being decorated
|
||||
* @param Component $c
|
||||
@@ -148,52 +154,53 @@ use a ``MappedSuperclass`` for this.
|
||||
{
|
||||
$this->decorates = $c;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
All operations on the ``Decorator`` (i.e. persist, remove, etc) will
|
||||
cascade from the ``Decorator`` to the ``Component``. This means that
|
||||
when we persist a ``Decorator``, Doctrine will take care of
|
||||
persisting the chain of decorated objects for us. A ``Decorator`` can
|
||||
be treated exactly as a ``Component`` when it comes time to
|
||||
All operations on the ``Decorator`` (i.e. persist, remove, etc) will
|
||||
cascade from the ``Decorator`` to the ``Component``. This means that
|
||||
when we persist a ``Decorator``, Doctrine will take care of
|
||||
persisting the chain of decorated objects for us. A ``Decorator`` can
|
||||
be treated exactly as a ``Component`` when it comes time to
|
||||
persisting it.
|
||||
|
||||
The ``Decorator's`` constructor accepts an instance of a
|
||||
``Component``, as defined by the ``Decorator`` pattern. The
|
||||
setDecorates/getDecorates methods have been defined as protected to
|
||||
hide the fact that a ``Decorator`` is decorating a ``Component`` and
|
||||
keeps the ``Component`` interface and the ``Decorator`` interface
|
||||
|
||||
The ``Decorator's`` constructor accepts an instance of a
|
||||
``Component``, as defined by the ``Decorator`` pattern. The
|
||||
setDecorates/getDecorates methods have been defined as protected to
|
||||
hide the fact that a ``Decorator`` is decorating a ``Component`` and
|
||||
keeps the ``Component`` interface and the ``Decorator`` interface
|
||||
identical.
|
||||
|
||||
To illustrate the intended result of the ``Decorator`` pattern, the
|
||||
getName() method has been overridden to append a string to the
|
||||
To illustrate the intended result of the ``Decorator`` pattern, the
|
||||
getName() method has been overridden to append a string to the
|
||||
``Component's`` getName() method.
|
||||
|
||||
ConcreteDecorator
|
||||
-----------------
|
||||
|
||||
The final class required to complete a simple implementation of the
|
||||
Decorator pattern is the ``ConcreteDecorator``. In order to further
|
||||
illustrate how the ``Decorator`` can alter data as it moves through
|
||||
the chain of decoration, a new field, "special", has been added to
|
||||
this class. The getName() has been overridden and appends the value
|
||||
of the getSpecial() method to its return value.
|
||||
The final class required to complete a simple implementation of the
|
||||
Decorator pattern is the ``ConcreteDecorator``. In order to further
|
||||
illustrate how the ``Decorator`` can alter data as it moves through
|
||||
the chain of decoration, a new field, "special", has been added to
|
||||
this class. The getName() has been overridden and appends the value
|
||||
of the getSpecial() method to its return value.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
namespace Test\Decorator;
|
||||
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
use Test\Decorator;
|
||||
|
||||
/** @Entity */
|
||||
|
||||
/** @ORM\Entity */
|
||||
class ConcreteDecorator extends Decorator
|
||||
{
|
||||
|
||||
/** @Column(type="string", nullable=true) */
|
||||
|
||||
/** @ORM\Column(type="string", nullable=true) */
|
||||
protected $special;
|
||||
|
||||
|
||||
/**
|
||||
* Set special
|
||||
* @param string $special
|
||||
@@ -202,7 +209,7 @@ of the getSpecial() method to its return value.
|
||||
{
|
||||
$this->special = $special;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get special
|
||||
* @return string $special
|
||||
@@ -211,7 +218,7 @@ of the getSpecial() method to its return value.
|
||||
{
|
||||
return $this->special;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see Test.Component::getName()
|
||||
@@ -219,55 +226,55 @@ of the getSpecial() method to its return value.
|
||||
public function getName()
|
||||
{
|
||||
return '[' . $this->getSpecial()
|
||||
. '] ' . parent::getName();
|
||||
. '] ' . parent::getName();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Here is an example of how to persist and retrieve your decorated
|
||||
Here is an example of how to persist and retrieve your decorated
|
||||
objects
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
use Test\Component\ConcreteComponent,
|
||||
Test\Decorator\ConcreteDecorator;
|
||||
|
||||
|
||||
// assumes Doctrine 2 is configured and an instance of
|
||||
// an EntityManager is available as $em
|
||||
|
||||
|
||||
// create a new concrete component
|
||||
$c = new ConcreteComponent();
|
||||
$c->setName('Test Component 1');
|
||||
$em->persist($c); // assigned unique ID = 1
|
||||
|
||||
|
||||
// create a new concrete decorator
|
||||
$c = new ConcreteComponent();
|
||||
$c->setName('Test Component 2');
|
||||
|
||||
|
||||
$d = new ConcreteDecorator($c);
|
||||
$d->setSpecial('Really');
|
||||
$em->persist($d);
|
||||
$em->persist($d);
|
||||
// assigns c as unique ID = 2, and d as unique ID = 3
|
||||
|
||||
|
||||
$em->flush();
|
||||
|
||||
$c = $em->find('Test\Component', 1);
|
||||
$d = $em->find('Test\Component', 3);
|
||||
|
||||
|
||||
echo get_class($c);
|
||||
// prints: Test\Component\ConcreteComponent
|
||||
|
||||
|
||||
echo $c->getName();
|
||||
// prints: Test Component 1
|
||||
|
||||
echo get_class($d)
|
||||
// prints: Test Component 1
|
||||
|
||||
echo get_class($d)
|
||||
// prints: Test\Component\ConcreteDecorator
|
||||
|
||||
|
||||
echo $d->getName();
|
||||
// prints: [Really] Decorated Test Component 2
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ In Doctrine 1 the DQL language was not implemented using a real
|
||||
parser. This made modifications of the DQL by the user impossible.
|
||||
Doctrine 2 in contrast has a real parser for the DQL language,
|
||||
which transforms the DQL statement into an
|
||||
`Abstract Syntax Tree <http://en.wikipedia.org/wiki/Abstract_syntax_tree>`_
|
||||
`Abstract Syntax Tree <https://en.wikipedia.org/wiki/Abstract_syntax_tree>`_
|
||||
and generates the appropriate SQL statement for it. Since this
|
||||
process is deterministic Doctrine heavily caches the SQL that is
|
||||
generated from any given DQL query, which reduces the performance
|
||||
@@ -28,7 +28,6 @@ generating the SQL statement.
|
||||
There are two types of custom tree walkers that you can hook into
|
||||
the DQL parser:
|
||||
|
||||
|
||||
- An output walker. This one actually generates the SQL, and there
|
||||
is only ever one of them. We implemented the default SqlWalker
|
||||
implementation for it.
|
||||
@@ -40,7 +39,6 @@ Now this is all awfully technical, so let me come to some use-cases
|
||||
fast to keep you motivated. Using walker implementation you can for
|
||||
example:
|
||||
|
||||
|
||||
- Modify the AST to generate a Count Query to be used with a
|
||||
paginator for any given DQL query.
|
||||
- Modify the Output Walker to generate vendor-specific SQL
|
||||
@@ -88,7 +86,7 @@ API would look for this use-case:
|
||||
$pageNum = 1;
|
||||
$query = $em->createQuery($dql);
|
||||
$query->setFirstResult( ($pageNum-1) * 20)->setMaxResults(20);
|
||||
|
||||
|
||||
$totalResults = Paginate::count($query);
|
||||
$results = $query->getResult();
|
||||
|
||||
@@ -101,12 +99,12 @@ The ``Paginate::count(Query $query)`` looks like:
|
||||
{
|
||||
static public function count(Query $query)
|
||||
{
|
||||
/* @var $countQuery Query */
|
||||
/** @var Query $countQuery */
|
||||
$countQuery = clone $query;
|
||||
|
||||
|
||||
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('DoctrineExtensions\Paginate\CountSqlWalker'));
|
||||
$countQuery->setFirstResult(null)->setMaxResults(null);
|
||||
|
||||
|
||||
return $countQuery->getSingleScalarResult();
|
||||
}
|
||||
}
|
||||
@@ -130,20 +128,20 @@ implementation is:
|
||||
{
|
||||
$parent = null;
|
||||
$parentName = null;
|
||||
foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) {
|
||||
foreach ($this->getQueryComponents() as $dqlAlias => $qComp) {
|
||||
if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
|
||||
$parent = $qComp;
|
||||
$parentName = $dqlAlias;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$pathExpression = new PathExpression(
|
||||
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,
|
||||
$parent['metadata']->getSingleIdentifierFieldName()
|
||||
);
|
||||
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
|
||||
|
||||
|
||||
$AST->selectClause->selectExpressions = array(
|
||||
new SelectExpression(
|
||||
new AggregateExpression('count', $pathExpression, true), null
|
||||
@@ -196,7 +194,7 @@ SQL\_NO\_CACHE on those queries that need it:
|
||||
public function walkSelectClause($selectClause)
|
||||
{
|
||||
$sql = parent::walkSelectClause($selectClause);
|
||||
|
||||
|
||||
if ($this->getQuery()->getHint('mysqlWalker.sqlNoCache') === true) {
|
||||
if ($selectClause->isDistinct) {
|
||||
$sql = str_replace('SELECT DISTINCT', 'SELECT DISTINCT SQL_NO_CACHE', $sql);
|
||||
@@ -204,7 +202,7 @@ SQL\_NO\_CACHE on those queries that need it:
|
||||
$sql = str_replace('SELECT', 'SELECT SQL_NO_CACHE', $sql);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
@@ -214,4 +212,3 @@ understanding of the DQL Parser and Walkers, but may offer your
|
||||
huge benefits with using vendor specific features. This would still
|
||||
allow you write DQL queries instead of NativeQueries to make use of
|
||||
vendor specific features.
|
||||
|
||||
|
||||
@@ -17,11 +17,10 @@ and gain access to vendor specific functionalities using the
|
||||
``EntityManager#createNativeQuery()`` API as described in
|
||||
the :doc:`Native Query <../reference/native-sql>` chapter.
|
||||
|
||||
|
||||
The DQL Parser has hooks to register functions that can then be
|
||||
used in your DQL queries and transformed into SQL, allowing to
|
||||
extend Doctrines Query capabilities to the vendors strength. This
|
||||
post explains the Used-Defined Functions API (UDF) of the Dql
|
||||
post explains the User-Defined Functions API (UDF) of the Dql
|
||||
Parser and shows some examples to give you some hints how you would
|
||||
extend DQL.
|
||||
|
||||
@@ -45,7 +44,7 @@ configuration:
|
||||
$config->addCustomStringFunction($name, $class);
|
||||
$config->addCustomNumericFunction($name, $class);
|
||||
$config->addCustomDatetimeFunction($name, $class);
|
||||
|
||||
|
||||
$em = EntityManager::create($dbParams, $config);
|
||||
|
||||
The ``$name`` is the name the function will be referred to in the
|
||||
@@ -70,7 +69,7 @@ methods, which are quite handy in my opinion:
|
||||
Date Diff
|
||||
---------
|
||||
|
||||
`Mysql's DateDiff function <http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_datediff>`_
|
||||
`Mysql's DateDiff function <https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_datediff>`_
|
||||
takes two dates as argument and calculates the difference in days
|
||||
with ``date1-date2``.
|
||||
|
||||
@@ -96,7 +95,7 @@ discuss it step by step:
|
||||
// (1)
|
||||
public $firstDateExpression = null;
|
||||
public $secondDateExpression = null;
|
||||
|
||||
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER); // (2)
|
||||
@@ -106,7 +105,7 @@ discuss it step by step:
|
||||
$this->secondDateExpression = $parser->ArithmeticPrimary(); // (6)
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS); // (3)
|
||||
}
|
||||
|
||||
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
return 'DATEDIFF(' .
|
||||
@@ -164,7 +163,7 @@ Date Add
|
||||
|
||||
Often useful it the ability to do some simple date calculations in
|
||||
your DQL query using
|
||||
`MySql's DATE\_ADD function <http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date-add>`_.
|
||||
`MySql's DATE_ADD function <https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-add>`_.
|
||||
|
||||
I'll skip the blah and show the code for this function:
|
||||
|
||||
@@ -180,28 +179,28 @@ I'll skip the blah and show the code for this function:
|
||||
public $firstDateExpression = null;
|
||||
public $intervalExpression = null;
|
||||
public $unit = null;
|
||||
|
||||
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
|
||||
$this->firstDateExpression = $parser->ArithmeticPrimary();
|
||||
|
||||
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
|
||||
|
||||
$this->intervalExpression = $parser->ArithmeticPrimary();
|
||||
|
||||
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
|
||||
/* @var $lexer Lexer */
|
||||
|
||||
/** @var Lexer $lexer */
|
||||
$lexer = $parser->getLexer();
|
||||
$this->unit = $lexer->token['value'];
|
||||
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
|
||||
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
return 'DATE_ADD(' .
|
||||
@@ -246,6 +245,4 @@ vendor sql functions and extend the DQL languages scope.
|
||||
|
||||
Code for this Extension to DQL and other Doctrine Extensions can be
|
||||
found
|
||||
`in my Github DoctrineExtensions repository <http://github.com/beberlei/DoctrineExtensions>`_.
|
||||
|
||||
|
||||
`in the GitHub DoctrineExtensions repository <https://github.com/beberlei/DoctrineExtensions>`_.
|
||||
|
||||
@@ -12,12 +12,14 @@ this working.
|
||||
Merging entity into an EntityManager
|
||||
------------------------------------
|
||||
|
||||
In Doctrine an entity objects has to be "managed" by an EntityManager to be
|
||||
updateable. Entities saved into the session are not managed in the next request
|
||||
In Doctrine, an entity objects has to be "managed" by an EntityManager to be
|
||||
updated. Entities saved into the session are not managed in the next request
|
||||
anymore. This means that you have to register these entities with an
|
||||
EntityManager again if you want to change them or use them as part of
|
||||
references between other entities. You can achieve this by calling
|
||||
``EntityManager#merge()``.
|
||||
references between other entities.
|
||||
|
||||
It is a good idea to avoid storing entities in serialized formats such as
|
||||
``$_SESSION``: instead, store the entity identifiers or raw data.
|
||||
|
||||
For a representative User object the code to get turn an instance from
|
||||
the session into a managed Doctrine object looks like this:
|
||||
@@ -26,43 +28,28 @@ the session into a managed Doctrine object looks like this:
|
||||
|
||||
<?php
|
||||
require_once 'bootstrap.php';
|
||||
$em = GetEntityManager(); // creates an EntityManager
|
||||
$em = GetEntityManager(); // creates an EntityManager
|
||||
|
||||
session_start();
|
||||
if (isset($_SESSION['user']) && $_SESSION['user'] instanceof User) {
|
||||
$user = $_SESSION['user'];
|
||||
$user = $em->merge($user);
|
||||
if (isset($_SESSION['user'])) {
|
||||
$user = $em->find(User::class, $_SESSION['user']);
|
||||
|
||||
if (! $user instanceof User) {
|
||||
// user not found in the database
|
||||
$_SESSION['user'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
Serializing entities into the session
|
||||
-------------------------------------
|
||||
|
||||
A frequent mistake is not to get the merged user object from the return
|
||||
value of ``EntityManager#merge()``. The entity object passed to merge is
|
||||
not necessarily the same object that is returned from the method.
|
||||
|
||||
Serializing entity into the session
|
||||
-----------------------------------
|
||||
|
||||
Entities that are serialized into the session normally contain references to
|
||||
other entities as well. Think of the user entity has a reference to his
|
||||
articles, groups, photos or many other different entities. If you serialize
|
||||
this object into the session then you don't want to serialize the related
|
||||
entities as well. This is why you should call ``EntityManager#detach()`` on this
|
||||
object or implement the __sleep() magic method on your entity.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
require_once 'bootstrap.php';
|
||||
$em = GetEntityManager(); // creates an EntityManager
|
||||
|
||||
$user = $em->find("User", 1);
|
||||
$em->detach($user);
|
||||
$_SESSION['user'] = $user;
|
||||
|
||||
.. note::
|
||||
|
||||
When you called detach on your objects they get "unmanaged" with that
|
||||
entity manager. This means you cannot use them as part of write operations
|
||||
during ``EntityManager#flush()`` anymore in this request.
|
||||
Serializing entities in the session means serializing also all associated
|
||||
entities and collections. While this might look like a quick solution in
|
||||
simple applications, you will encounter problems due to the fact that the
|
||||
data in the session is stale.
|
||||
|
||||
In order to prevent working with stale data, try saving only minimal
|
||||
information about your entities in your session, without storing entire
|
||||
entity objects. Should you need the full information of an object, so it
|
||||
is suggested to re-query the database, which is usually the most
|
||||
authoritative source of information in typical PHP applications.
|
||||
|
||||
@@ -6,7 +6,7 @@ Implementing ArrayAccess for Domain Objects
|
||||
This recipe will show you how to implement ArrayAccess for your
|
||||
domain objects in order to allow more uniform access, for example
|
||||
in templates. In these examples we will implement ArrayAccess on a
|
||||
`Layer Supertype <http://martinfowler.com/eaaCatalog/layerSupertype.html>`_
|
||||
`Layer Supertype <https://martinfowler.com/eaaCatalog/layerSupertype.html>`_
|
||||
for all our domain objects.
|
||||
|
||||
Option 1
|
||||
@@ -16,7 +16,6 @@ In this implementation we will make use of PHPs highly dynamic
|
||||
nature to dynamically access properties of a subtype in a supertype
|
||||
at runtime. Note that this implementation has 2 main caveats:
|
||||
|
||||
|
||||
- It will not work with private fields
|
||||
- It will not go through any getters/setters
|
||||
|
||||
@@ -28,15 +27,15 @@ at runtime. Note that this implementation has 2 main caveats:
|
||||
public function offsetExists($offset) {
|
||||
return isset($this->$offset);
|
||||
}
|
||||
|
||||
|
||||
public function offsetSet($offset, $value) {
|
||||
$this->$offset = $value;
|
||||
}
|
||||
|
||||
|
||||
public function offsetGet($offset) {
|
||||
return $this->$offset;
|
||||
}
|
||||
|
||||
|
||||
public function offsetUnset($offset) {
|
||||
$this->$offset = null;
|
||||
}
|
||||
@@ -50,7 +49,6 @@ Again we use PHPs dynamic nature to invoke methods on a subtype
|
||||
from a supertype at runtime. This implementation has the following
|
||||
caveats:
|
||||
|
||||
|
||||
- It relies on a naming convention
|
||||
- The semantics of offsetExists can differ
|
||||
- offsetUnset will not work with typehinted setters
|
||||
@@ -65,15 +63,15 @@ caveats:
|
||||
$value = $this->{"get$offset"}();
|
||||
return $value !== null;
|
||||
}
|
||||
|
||||
|
||||
public function offsetSet($offset, $value) {
|
||||
$this->{"set$offset"}($value);
|
||||
}
|
||||
|
||||
|
||||
public function offsetGet($offset) {
|
||||
return $this->{"get$offset"}();
|
||||
}
|
||||
|
||||
|
||||
public function offsetUnset($offset) {
|
||||
$this->{"set$offset"}(null);
|
||||
}
|
||||
@@ -95,18 +93,17 @@ exception (i.e. BadMethodCallException).
|
||||
public function offsetExists($offset) {
|
||||
// option 1 or option 2
|
||||
}
|
||||
|
||||
|
||||
public function offsetSet($offset, $value) {
|
||||
throw new BadMethodCallException("Array access of class " . get_class($this) . " is read-only!");
|
||||
}
|
||||
|
||||
|
||||
public function offsetGet($offset) {
|
||||
// option 1 or option 2
|
||||
}
|
||||
|
||||
|
||||
public function offsetUnset($offset) {
|
||||
throw new BadMethodCallException("Array access of class " . get_class($this) . " is read-only!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ 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 <http://martinfowler.com/eaaCatalog/layerSupertype.html>`_
|
||||
`Layer Supertype <https://martinfowler.com/eaaCatalog/layerSupertype.html>`_
|
||||
for all our domain objects.
|
||||
|
||||
Implementing NotifyPropertyChanged
|
||||
@@ -24,15 +24,15 @@ implement the ``NotifyPropertyChanged`` interface from the
|
||||
<?php
|
||||
use Doctrine\Common\NotifyPropertyChanged;
|
||||
use Doctrine\Common\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) {
|
||||
@@ -50,12 +50,12 @@ listeners:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// Mapping not shown, either in annotations, xml or yaml as usual
|
||||
// Mapping not shown, either in annotations or xml 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);
|
||||
@@ -69,4 +69,3 @@ not mandatory but recommended. That way you can avoid unnecessary
|
||||
updates and also have full control over when you consider a
|
||||
property changed.
|
||||
|
||||
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
Implementing Wakeup or Clone
|
||||
============================
|
||||
|
||||
.. sectionauthor:: Roman Borschel (roman@code-factory.org)
|
||||
|
||||
As explained in the
|
||||
`restrictions for entity classes in the manual <http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/architecture.html#entities>`_,
|
||||
it is usually not allowed for an entity to implement ``__wakeup``
|
||||
or ``__clone``, because Doctrine makes special use of them.
|
||||
However, it is quite easy to make use of these methods in a safe
|
||||
way by guarding the custom wakeup or clone code with an entity
|
||||
identity check, as demonstrated in the following sections.
|
||||
|
||||
Safely implementing \_\_wakeup
|
||||
------------------------------
|
||||
|
||||
To safely implement ``__wakeup``, simply enclose your
|
||||
implementation code in an identity check as follows:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class MyEntity
|
||||
{
|
||||
private $id; // This is the identifier of the entity.
|
||||
//...
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
// If the entity has an identity, proceed as normal.
|
||||
if ($this->id) {
|
||||
// ... Your code here as normal ...
|
||||
}
|
||||
// otherwise do nothing, do NOT throw an exception!
|
||||
}
|
||||
|
||||
//...
|
||||
}
|
||||
|
||||
Safely implementing \_\_clone
|
||||
-----------------------------
|
||||
|
||||
Safely implementing ``__clone`` is pretty much the same:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class MyEntity
|
||||
{
|
||||
private $id; // This is the identifier of the entity.
|
||||
//...
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
// If the entity has an identity, proceed as normal.
|
||||
if ($this->id) {
|
||||
// ... Your code here as normal ...
|
||||
}
|
||||
// otherwise do nothing, do NOT throw an exception!
|
||||
}
|
||||
|
||||
//...
|
||||
}
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
As you have seen, it is quite easy to safely make use of
|
||||
``__wakeup`` and ``__clone`` in your entities without adding any
|
||||
really Doctrine-specific or Doctrine-dependant code.
|
||||
|
||||
These implementations are possible and safe because when Doctrine
|
||||
invokes these methods, the entities never have an identity (yet).
|
||||
Furthermore, it is possibly a good idea to check for the identity
|
||||
in your code anyway, since it's rarely the case that you want to
|
||||
unserialize or clone an entity with no identity.
|
||||
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
Integrating with CodeIgniter
|
||||
============================
|
||||
|
||||
This is recipe for using Doctrine 2 in your
|
||||
`CodeIgniter <http://www.codeigniter.com>`_ framework.
|
||||
|
||||
.. note::
|
||||
|
||||
This might not work for all CodeIgniter versions and may require
|
||||
slight adjustments.
|
||||
|
||||
|
||||
Here is how to set it up:
|
||||
|
||||
Make a CodeIgniter library that is both a wrapper and a bootstrap
|
||||
for Doctrine 2.
|
||||
|
||||
Setting up the file structure
|
||||
-----------------------------
|
||||
|
||||
Here are the steps:
|
||||
|
||||
|
||||
- Add a php file to your system/application/libraries folder
|
||||
called Doctrine.php. This is going to be your wrapper/bootstrap for
|
||||
the D2 entity manager.
|
||||
- Put the Doctrine folder (the one that contains Common, DBAL, and
|
||||
ORM) inside that same libraries folder.
|
||||
- Your system/application/libraries folder now looks like this:
|
||||
|
||||
system/applications/libraries -Doctrine -Doctrine.php -index.html
|
||||
|
||||
- If you want, open your config/autoload.php file and autoload
|
||||
your Doctrine library.
|
||||
|
||||
<?php $autoload['libraries'] = array('doctrine');
|
||||
|
||||
|
||||
Creating your Doctrine CodeIgniter library
|
||||
------------------------------------------
|
||||
|
||||
Now, here is what your Doctrine.php file should look like.
|
||||
Customize it to your needs.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\ClassLoader,
|
||||
Doctrine\ORM\Configuration,
|
||||
Doctrine\ORM\EntityManager,
|
||||
Doctrine\Common\Cache\ArrayCache,
|
||||
Doctrine\DBAL\Logging\EchoSQLLogger;
|
||||
|
||||
class Doctrine {
|
||||
|
||||
public $em = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// load database configuration from CodeIgniter
|
||||
require_once APPPATH.'config/database.php';
|
||||
|
||||
// Set up class loading. You could use different autoloaders, provided by your favorite framework,
|
||||
// if you want to.
|
||||
require_once APPPATH.'libraries/Doctrine/Common/ClassLoader.php';
|
||||
|
||||
$doctrineClassLoader = new ClassLoader('Doctrine', APPPATH.'libraries');
|
||||
$doctrineClassLoader->register();
|
||||
$entitiesClassLoader = new ClassLoader('models', rtrim(APPPATH, "/" ));
|
||||
$entitiesClassLoader->register();
|
||||
$proxiesClassLoader = new ClassLoader('Proxies', APPPATH.'models/proxies');
|
||||
$proxiesClassLoader->register();
|
||||
|
||||
// Set up caches
|
||||
$config = new Configuration;
|
||||
$cache = new ArrayCache;
|
||||
$config->setMetadataCacheImpl($cache);
|
||||
$driverImpl = $config->newDefaultAnnotationDriver(array(APPPATH.'models/Entities'));
|
||||
$config->setMetadataDriverImpl($driverImpl);
|
||||
$config->setQueryCacheImpl($cache);
|
||||
|
||||
$config->setQueryCacheImpl($cache);
|
||||
|
||||
// Proxy configuration
|
||||
$config->setProxyDir(APPPATH.'/models/proxies');
|
||||
$config->setProxyNamespace('Proxies');
|
||||
|
||||
// Set up logger
|
||||
$logger = new EchoSQLLogger;
|
||||
$config->setSQLLogger($logger);
|
||||
|
||||
$config->setAutoGenerateProxyClasses( TRUE );
|
||||
|
||||
// Database connection information
|
||||
$connectionOptions = array(
|
||||
'driver' => 'pdo_mysql',
|
||||
'user' => $db['default']['username'],
|
||||
'password' => $db['default']['password'],
|
||||
'host' => $db['default']['hostname'],
|
||||
'dbname' => $db['default']['database']
|
||||
);
|
||||
|
||||
// Create EntityManager
|
||||
$this->em = EntityManager::create($connectionOptions, $config);
|
||||
}
|
||||
}
|
||||
|
||||
Please note that this is a development configuration; for a
|
||||
production system you'll want to use a real caching system like
|
||||
APC, get rid of EchoSqlLogger, and turn off
|
||||
autoGenerateProxyClasses.
|
||||
|
||||
For more details, consult the
|
||||
`Doctrine 2 Configuration documentation <http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html>`_.
|
||||
|
||||
Now to use it
|
||||
-------------
|
||||
|
||||
Whenever you need a reference to the entity manager inside one of
|
||||
your controllers, views, or models you can do this:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em = $this->doctrine->em;
|
||||
|
||||
That's all there is to it. Once you get the reference to your
|
||||
EntityManager do your Doctrine 2.0 voodoo as normal.
|
||||
|
||||
Note: If you do not choose to autoload the Doctrine library, you
|
||||
will need to put this line before you get a reference to it:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$this->load->library('doctrine');
|
||||
|
||||
Good luck!
|
||||
|
||||
|
||||
@@ -43,13 +43,16 @@ entities:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\Entity */
|
||||
class Article
|
||||
{
|
||||
const STATUS_VISIBLE = 'visible';
|
||||
const STATUS_INVISIBLE = 'invisible';
|
||||
|
||||
/** @Column(type="string") */
|
||||
/** @ORM\Column(type="string") */
|
||||
private $status;
|
||||
|
||||
public function setStatus($status)
|
||||
@@ -67,10 +70,13 @@ the **columnDefinition** attribute.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\Entity */
|
||||
class Article
|
||||
{
|
||||
/** @Column(type="string", columnDefinition="ENUM('visible', 'invisible')") */
|
||||
/** @ORM\Column(type="string", columnDefinition="ENUM('visible', 'invisible')") */
|
||||
private $status;
|
||||
}
|
||||
|
||||
@@ -131,10 +137,13 @@ Then in your entity you can just use this type:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\Entity */
|
||||
class Article
|
||||
{
|
||||
/** @Column(type="enumvisibility") */
|
||||
/** @ORM\Column(type="enumvisibility") */
|
||||
private $status;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ An Invoice entity
|
||||
|
||||
namespace Acme\InvoiceModule\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping AS ORM;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Acme\InvoiceModule\Model\InvoiceSubjectInterface;
|
||||
|
||||
/**
|
||||
@@ -139,4 +139,3 @@ bundles, keeping them usable by themselves, but still being able to
|
||||
define relationships between different objects. By using this method,
|
||||
I've found my bundles end up being easier to maintain independently.
|
||||
|
||||
|
||||
|
||||
@@ -23,31 +23,32 @@ appropriate autoloaders.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
namespace DoctrineExtensions;
|
||||
use \Doctrine\ORM\Event\LoadClassMetadataEventArgs;
|
||||
|
||||
|
||||
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
|
||||
use Doctrine\ORM\Mapping;
|
||||
|
||||
class TablePrefix
|
||||
{
|
||||
protected $prefix = '';
|
||||
|
||||
|
||||
public function __construct($prefix)
|
||||
{
|
||||
$this->prefix = (string) $prefix;
|
||||
}
|
||||
|
||||
|
||||
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
|
||||
{
|
||||
$classMetadata = $eventArgs->getClassMetadata();
|
||||
|
||||
if (!$classMetadata->isInheritanceTypeSingleTable() || $classMetadata->getName() === $classMetadata->rootEntityName) {
|
||||
$classMetadata->setPrimaryTable([
|
||||
'name' => $this->prefix . $classMetadata->getTableName()
|
||||
]);
|
||||
if ($classMetadata->inheritanceType !== Mapping\InheritanceType::SINGLE_TABLE ||
|
||||
$classMetadata->getName() === $classMetadata->rootEntityName) {
|
||||
$classMetadata->setTableName($this->prefix . $classMetadata->getTableName());
|
||||
}
|
||||
|
||||
foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {
|
||||
if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY && $mapping['isOwningSide']) {
|
||||
foreach ($classMetadata->associationMappings as $fieldName => $mapping) {
|
||||
if ($mapping['type'] == Mapping\ClassMetadata::MANY_TO_MANY && $mapping['isOwningSide']) {
|
||||
$mappedTableName = $mapping['joinTable']['name'];
|
||||
$classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName;
|
||||
}
|
||||
@@ -68,19 +69,17 @@ before the prefix has been set.
|
||||
If you set this listener up, be aware that you will need
|
||||
to clear your caches and drop then recreate your database schema.
|
||||
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
// $connectionOptions and $config set earlier
|
||||
|
||||
|
||||
$evm = new \Doctrine\Common\EventManager;
|
||||
|
||||
|
||||
// Table Prefix
|
||||
$tablePrefix = new \DoctrineExtensions\TablePrefix('prefix_');
|
||||
$evm->addEventListener(\Doctrine\ORM\Events::loadClassMetadata, $tablePrefix);
|
||||
|
||||
|
||||
$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm);
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ Strategy-Pattern
|
||||
|
||||
This recipe will give you a short introduction on how to design
|
||||
similar entities without using expensive (i.e. slow) inheritance
|
||||
but with not more than \* the well-known strategy pattern \* event
|
||||
but with not more than *the well-known strategy pattern* event
|
||||
listeners
|
||||
|
||||
Scenario / Problem
|
||||
@@ -12,7 +12,6 @@ Scenario / Problem
|
||||
Given a Content-Management-System, we probably want to add / edit
|
||||
some so-called "blocks" and "panels". What are they for?
|
||||
|
||||
|
||||
- A block might be a registration form, some text content, a table
|
||||
with information. A good example might also be a small calendar.
|
||||
- A panel is by definition a block that can itself contain blocks.
|
||||
@@ -23,7 +22,6 @@ So, in this scenario, when building your CMS, you will surely add
|
||||
lots of blocks and panels to your pages and you will find yourself
|
||||
highly uncomfortable because of the following:
|
||||
|
||||
|
||||
- Every existing page needs to know about the panels it contains -
|
||||
therefore, you'll have an association to your panels. But if you've
|
||||
got several types of panels - what do you do? Add an association to
|
||||
@@ -58,7 +56,6 @@ the middle of your page, for example).
|
||||
|
||||
Such an interface could look like this:
|
||||
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
@@ -87,12 +84,12 @@ Such an interface could look like this:
|
||||
* @return \Zend_View_Helper_Interface
|
||||
*/
|
||||
public function setView(\Zend_View_Interface $view);
|
||||
|
||||
|
||||
/**
|
||||
* @return \Zend_View_Interface
|
||||
*/
|
||||
public function getView();
|
||||
|
||||
|
||||
/**
|
||||
* Renders this strategy. This method will be called when the user
|
||||
* displays the site.
|
||||
@@ -100,7 +97,7 @@ Such an interface could look like this:
|
||||
* @return string
|
||||
*/
|
||||
public function renderFrontend();
|
||||
|
||||
|
||||
/**
|
||||
* Renders the backend of this block. This method will be called when
|
||||
* a user tries to reconfigure this block instance.
|
||||
@@ -118,21 +115,21 @@ Such an interface could look like this:
|
||||
* @return array
|
||||
*/
|
||||
public function getRequiredPanelTypes();
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether a Block is able to use a given type or not
|
||||
* @param string $typeName The typename
|
||||
* @return boolean
|
||||
*/
|
||||
public function canUsePanelType($typeName);
|
||||
|
||||
|
||||
public function setBlockEntity(AbstractBlock $block);
|
||||
|
||||
public function getBlockEntity();
|
||||
}
|
||||
|
||||
|
||||
As you can see, we have a method "setBlockEntity" which ties a potential strategy to an object of type AbstractBlock. This type will simply define the basic behaviour of our blocks and could potentially look something like this:
|
||||
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
@@ -155,7 +152,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 2)
|
||||
*
|
||||
* This is a doctrine field, so make sure that you use an @column annotation or setup your
|
||||
* yaml or xml files correctly
|
||||
* xml files correctly
|
||||
* @var string
|
||||
*/
|
||||
protected $strategyClassName;
|
||||
@@ -177,7 +174,7 @@ As you can see, we have a method "setBlockEntity" which ties a potential strateg
|
||||
public function getStrategyClassName() {
|
||||
return $this->strategyClassName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the instantiated strategy
|
||||
*
|
||||
@@ -186,7 +183,7 @@ As you can see, we have a method "setBlockEntity" which ties a potential strateg
|
||||
public function getStrategyInstance() {
|
||||
return $this->strategyInstance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the strategy this block / panel should work as. Make sure that you've used
|
||||
* this method before persisting the block!
|
||||
@@ -213,28 +210,28 @@ This might look like this:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use \Doctrine\ORM,
|
||||
\Doctrine\Common;
|
||||
|
||||
use Doctrine\ORM,
|
||||
Doctrine\Common;
|
||||
|
||||
/**
|
||||
* The BlockStrategyEventListener will initialize a strategy after the
|
||||
* block itself was loaded.
|
||||
*/
|
||||
class BlockStrategyEventListener implements Common\EventSubscriber {
|
||||
|
||||
|
||||
protected $view;
|
||||
|
||||
|
||||
public function __construct(\Zend_View_Interface $view) {
|
||||
$this->view = $view;
|
||||
}
|
||||
|
||||
|
||||
public function getSubscribedEvents() {
|
||||
return array(ORM\Events::postLoad);
|
||||
}
|
||||
|
||||
|
||||
public function postLoad(ORM\Event\LifecycleEventArgs $args) {
|
||||
$blockItem = $args->getEntity();
|
||||
|
||||
|
||||
// Both blocks and panels are instances of Block\AbstractBlock
|
||||
if ($blockItem instanceof Block\AbstractBlock) {
|
||||
$strategy = $blockItem->getStrategyClassName();
|
||||
@@ -251,4 +248,3 @@ This might look like this:
|
||||
In this example, even some variables are set - like a view object
|
||||
or a specific configuration object.
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ What we offer are hooks to execute any kind of validation.
|
||||
perform validations in value setters or any other method of your
|
||||
entities that are used in your code.
|
||||
|
||||
|
||||
Entities can register lifecycle event methods with Doctrine that
|
||||
are called on different occasions. For validation we would need to
|
||||
hook into the events called before persisting and updating. Even
|
||||
@@ -36,12 +35,12 @@ is allowed to:
|
||||
public function assertCustomerAllowedBuying()
|
||||
{
|
||||
$orderLimit = $this->customer->getOrderLimit();
|
||||
|
||||
|
||||
$amount = 0;
|
||||
foreach ($this->orderLines as $line) {
|
||||
$amount += $line->getAmount();
|
||||
}
|
||||
|
||||
|
||||
if ($amount > $orderLimit) {
|
||||
throw new CustomerOrderLimitExceededException();
|
||||
}
|
||||
@@ -58,14 +57,17 @@ First Annotations:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @HasLifecycleCallbacks
|
||||
* @ORM\Entity
|
||||
* @ORM\HasLifecycleCallbacks
|
||||
*/
|
||||
class Order
|
||||
{
|
||||
/**
|
||||
* @PrePersist @PreUpdate
|
||||
* @ORM\PrePersist @ORM\PreUpdate
|
||||
*/
|
||||
public function assertCustomerAllowedBuying() {}
|
||||
}
|
||||
@@ -77,15 +79,12 @@ In XML Mappings:
|
||||
<doctrine-mapping>
|
||||
<entity name="Order">
|
||||
<lifecycle-callbacks>
|
||||
<lifecycle-callback type="prePersist" method="assertCustomerallowedBuying" />
|
||||
<lifecycle-callback type="preUpdate" method="assertCustomerallowedBuying" />
|
||||
<lifecycle-callback type="prePersist" method="assertCustomerAllowedBuying" />
|
||||
<lifecycle-callback type="preUpdate" method="assertCustomerAllowedBuying" />
|
||||
</lifecycle-callbacks>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
YAML needs some little change yet, to allow multiple lifecycle
|
||||
events for one method, this will happen before Beta 1 though.
|
||||
|
||||
Now validation is performed whenever you call
|
||||
``EntityManager#persist($order)`` or when you call
|
||||
``EntityManager#flush()`` and an order is about to be updated. Any
|
||||
@@ -99,21 +98,24 @@ validation callbacks.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
class Order
|
||||
{
|
||||
/**
|
||||
* @PrePersist @PreUpdate
|
||||
* @ORM\PrePersist @ORM\PreUpdate
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!($this->plannedShipDate instanceof DateTime)) {
|
||||
throw new ValidateException();
|
||||
}
|
||||
|
||||
|
||||
if ($this->plannedShipDate->format('U') < time()) {
|
||||
throw new ValidateException();
|
||||
}
|
||||
|
||||
|
||||
if ($this->customer == null) {
|
||||
throw new OrderRequiresCustomerException();
|
||||
}
|
||||
@@ -134,4 +136,4 @@ instances. This was already discussed in the previous blog post on
|
||||
the Versionable extension, which requires another type of event
|
||||
called "onFlush".
|
||||
|
||||
Further readings: :doc:`Lifecycle Events <../reference/events>`
|
||||
Further readings: :ref:`reference-events-lifecycle-events`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Working with DateTime Instances
|
||||
===============================
|
||||
|
||||
There are many nitty gritty details when working with PHPs DateTime instances. You have know their inner
|
||||
There are many nitty gritty details when working with PHPs DateTime instances. You have to know their inner
|
||||
workings pretty well not to make mistakes with date handling. This cookbook entry holds several
|
||||
interesting pieces of information on how to work with PHP DateTime instances in Doctrine 2.
|
||||
|
||||
@@ -15,10 +15,13 @@ these comparisons are always made **BY REFERENCE**. That means the following cha
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity */
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\Entity */
|
||||
class Article
|
||||
{
|
||||
/** @Column(type="datetime") */
|
||||
/** @ORM\Column(type="datetime") */
|
||||
private $updated;
|
||||
|
||||
public function setUpdated()
|
||||
@@ -61,7 +64,7 @@ to manage this mess,
|
||||
however let me crush your expectations fast. There is not a single database out there (supported by Doctrine 2)
|
||||
that supports timezones correctly. Correctly here means that you can cover all the use-cases that
|
||||
can come up with timezones. If you don't believe me you should read up on `Storing DateTime
|
||||
in Databases <http://derickrethans.nl/storing-date-time-in-database.html>`_.
|
||||
in Databases <https://derickrethans.nl/storing-date-time-in-database.html>`_.
|
||||
|
||||
The problem is simple. Not a single database vendor saves the timezone, only the differences to UTC.
|
||||
However with frequent daylight saving and political timezone changes you can have a UTC offset that moves
|
||||
@@ -110,7 +113,7 @@ the UTC time at the time of the booking and the timezone the event happened in.
|
||||
$converted = \DateTime::createFromFormat(
|
||||
$platform->getDateTimeFormatString(),
|
||||
$value,
|
||||
self::$utc ? self::$utc : self::$utc = new \DateTimeZone('UTC')
|
||||
self::getUtc()
|
||||
);
|
||||
|
||||
if (! $converted) {
|
||||
@@ -123,6 +126,11 @@ the UTC time at the time of the booking and the timezone the event happened in.
|
||||
|
||||
return $converted;
|
||||
}
|
||||
|
||||
private static function getUtc()
|
||||
{
|
||||
return self::$utc ? self::$utc : self::$utc = new \DateTimeZone('UTC');
|
||||
}
|
||||
}
|
||||
|
||||
This database type makes sure that every DateTime instance is always saved in UTC, relative
|
||||
@@ -141,7 +149,6 @@ code before bootstrapping the ORM:
|
||||
Type::overrideType('datetime', UTCDateTimeType::class);
|
||||
Type::overrideType('datetimetz', UTCDateTimeType::class);
|
||||
|
||||
|
||||
To be able to transform these values
|
||||
back into their real timezone you have to save the timezone in a separate field of the entity
|
||||
requiring timezoned datetimes:
|
||||
@@ -149,17 +156,20 @@ requiring timezoned datetimes:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
namespace Shipping;
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Event
|
||||
{
|
||||
/** @Column(type="datetime") */
|
||||
/** @ORM\Column(type="datetime") */
|
||||
private $created;
|
||||
|
||||
/** @Column(type="string") */
|
||||
/** @ORM\Column(type="string") */
|
||||
private $timezone;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
Welcome to Doctrine 2 ORM's documentation!
|
||||
==========================================
|
||||
ORM Documentation
|
||||
=================
|
||||
|
||||
The Doctrine documentation is comprised of tutorials, a reference section and
|
||||
cookbook articles that explain different parts of the Object Relational mapper.
|
||||
|
||||
Doctrine DBAL and Doctrine Common both have their own documentation.
|
||||
The Doctrine ORM documentation is comprised of tutorials, a reference section and
|
||||
cookbook articles that explain different parts of the Object Relational Mapper.
|
||||
|
||||
Getting Help
|
||||
------------
|
||||
@@ -13,119 +11,14 @@ If this documentation is not helping to answer questions you have about
|
||||
Doctrine ORM don't panic. You can get help from different sources:
|
||||
|
||||
- There is a :doc:`FAQ <reference/faq>` with answers to frequent questions.
|
||||
- The `Doctrine Mailing List <http://groups.google.com/group/doctrine-user>`_
|
||||
- Internet Relay Chat (IRC) in #doctrine on Freenode
|
||||
- Report a bug on `JIRA <http://www.doctrine-project.org/jira>`_.
|
||||
- The `Doctrine Mailing List <https://groups.google.com/group/doctrine-user>`_
|
||||
- Slack chat room `#orm <https://www.doctrine-project.org/slack>`_
|
||||
- Report a bug on `GitHub <https://github.com/doctrine/orm/issues>`_.
|
||||
- On `Twitter <https://twitter.com/search/%23doctrine2>`_ with ``#doctrine2``
|
||||
- On `StackOverflow <http://stackoverflow.com/questions/tagged/doctrine2>`_
|
||||
|
||||
If you need more structure over the different topics you can browse the :doc:`table
|
||||
of contents <toc>`.
|
||||
- On `StackOverflow <https://stackoverflow.com/questions/tagged/doctrine-orm>`_
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
* **Tutorial**:
|
||||
:doc:`Getting Started with Doctrine <tutorials/getting-started>`
|
||||
|
||||
* **Setup**:
|
||||
:doc:`Installation & Configuration <reference/configuration>`
|
||||
|
||||
Mapping Objects onto a Database
|
||||
-------------------------------
|
||||
|
||||
* **Mapping**:
|
||||
:doc:`Objects <reference/basic-mapping>` |
|
||||
:doc:`Associations <reference/association-mapping>` |
|
||||
:doc:`Inheritance <reference/inheritance-mapping>`
|
||||
|
||||
* **Drivers**:
|
||||
:doc:`Docblock Annotations <reference/annotations-reference>` |
|
||||
:doc:`XML <reference/xml-mapping>` |
|
||||
:doc:`YAML <reference/yaml-mapping>` |
|
||||
:doc:`PHP <reference/php-mapping>`
|
||||
|
||||
Working with Objects
|
||||
--------------------
|
||||
|
||||
* **Basic Reference**:
|
||||
:doc:`Entities <reference/working-with-objects>` |
|
||||
:doc:`Associations <reference/working-with-associations>` |
|
||||
:doc:`Events <reference/events>`
|
||||
|
||||
* **Query Reference**:
|
||||
:doc:`DQL <reference/dql-doctrine-query-language>` |
|
||||
:doc:`QueryBuilder <reference/query-builder>` |
|
||||
:doc:`Native SQL <reference/native-sql>`
|
||||
|
||||
* **Internals**:
|
||||
:doc:`Internals explained <reference/unitofwork>` |
|
||||
:doc:`Associations <reference/unitofwork-associations>`
|
||||
|
||||
Advanced Topics
|
||||
---------------
|
||||
|
||||
* :doc:`Architecture <reference/architecture>`
|
||||
* :doc:`Advanced Configuration <reference/advanced-configuration>`
|
||||
* :doc:`Limitations and known issues <reference/limitations-and-known-issues>`
|
||||
* :doc:`Commandline Tools <reference/tools>`
|
||||
* :doc:`Transactions and Concurrency <reference/transactions-and-concurrency>`
|
||||
* :doc:`Filters <reference/filters>`
|
||||
* :doc:`NamingStrategy <reference/namingstrategy>`
|
||||
* :doc:`Improving Performance <reference/improving-performance>`
|
||||
* :doc:`Caching <reference/caching>`
|
||||
* :doc:`Partial Objects <reference/partial-objects>`
|
||||
* :doc:`Change Tracking Policies <reference/change-tracking-policies>`
|
||||
* :doc:`Best Practices <reference/best-practices>`
|
||||
* :doc:`Metadata Drivers <reference/metadata-drivers>`
|
||||
* :doc:`Batch Processing <reference/batch-processing>`
|
||||
* :doc:`Second Level Cache <reference/second-level-cache>`
|
||||
|
||||
Tutorials
|
||||
---------
|
||||
|
||||
* :doc:`Indexed associations <tutorials/working-with-indexed-associations>`
|
||||
* :doc:`Extra Lazy Associations <tutorials/extra-lazy-associations>`
|
||||
* :doc:`Composite Primary Keys <tutorials/composite-primary-keys>`
|
||||
* :doc:`Ordered associations <tutorials/ordered-associations>`
|
||||
* :doc:`Pagination <tutorials/pagination>`
|
||||
* :doc:`Override Field/Association Mappings In Subclasses <tutorials/override-field-association-mappings-in-subclasses>`
|
||||
* :doc:`Embeddables <tutorials/embeddables>`
|
||||
|
||||
Changelogs
|
||||
----------
|
||||
|
||||
* :doc:`Migration to 2.5 <changelog/migration_2_5>`
|
||||
|
||||
Cookbook
|
||||
--------
|
||||
|
||||
* **Patterns**:
|
||||
:doc:`Aggregate Fields <cookbook/aggregate-fields>` |
|
||||
:doc:`Decorator Pattern <cookbook/decorator-pattern>` |
|
||||
:doc:`Strategy Pattern <cookbook/strategy-cookbook-introduction>`
|
||||
|
||||
* **DQL Extension Points**:
|
||||
:doc:`DQL Custom Walkers <cookbook/dql-custom-walkers>` |
|
||||
:doc:`DQL User-Defined-Functions <cookbook/dql-user-defined-functions>`
|
||||
|
||||
* **Implementation**:
|
||||
:doc:`Array Access <cookbook/implementing-arrayaccess-for-domain-objects>` |
|
||||
:doc:`Notify ChangeTracking Example <cookbook/implementing-the-notify-changetracking-policy>` |
|
||||
:doc:`Using Wakeup Or Clone <cookbook/implementing-wakeup-or-clone>` |
|
||||
:doc:`Working with DateTime <cookbook/working-with-datetime>` |
|
||||
:doc:`Validation <cookbook/validation-of-entities>` |
|
||||
:doc:`Entities in the Session <cookbook/entities-in-session>` |
|
||||
:doc:`Keeping your Modules independent <cookbook/resolve-target-entity-listener>`
|
||||
|
||||
* **Integration into Frameworks/Libraries**
|
||||
:doc:`CodeIgniter <cookbook/integrating-with-codeigniter>`
|
||||
|
||||
* **Hidden Gems**
|
||||
:doc:`Prefixing Table Name <cookbook/sql-table-prefixes>`
|
||||
|
||||
* **Custom Datatypes**
|
||||
:doc:`MySQL Enums <cookbook/mysql-enums>`
|
||||
:doc:`Advanced Field Value Conversion <cookbook/advanced-field-value-conversion-using-custom-mapping-types>`
|
||||
|
||||
.. include:: toc.rst
|
||||
The best way to get started is with the :doc:`Getting Started with Doctrine <tutorials/getting-started>` tutorial.
|
||||
Use the sidebar to browse other tutorials and documentation for the Doctrine PHP ORM.
|
||||
|
||||
113
docs/en/make.bat
113
docs/en/make.bat
@@ -1,113 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
set SPHINXBUILD=sphinx-build
|
||||
set BUILDDIR=_build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Doctrine2ORM.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Doctrine2ORM.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
||||
@@ -9,17 +9,18 @@ steps of configuration.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\EntityManager,
|
||||
Doctrine\ORM\Configuration;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Configuration;
|
||||
use Doctrine\Common\Proxy\ProxyFactory;
|
||||
|
||||
// ...
|
||||
|
||||
|
||||
if ($applicationMode == "development") {
|
||||
$cache = new \Doctrine\Common\Cache\ArrayCache;
|
||||
} else {
|
||||
$cache = new \Doctrine\Common\Cache\ApcCache;
|
||||
$cache = new \Doctrine\Common\Cache\ApcuCache;
|
||||
}
|
||||
|
||||
|
||||
$config = new Configuration;
|
||||
$config->setMetadataCacheImpl($cache);
|
||||
$driverImpl = $config->newDefaultAnnotationDriver('/path/to/lib/MyProject/Entities');
|
||||
@@ -27,18 +28,17 @@ steps of configuration.
|
||||
$config->setQueryCacheImpl($cache);
|
||||
$config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies');
|
||||
$config->setProxyNamespace('MyProject\Proxies');
|
||||
|
||||
if ($applicationMode == "development") {
|
||||
$config->setAutoGenerateProxyClasses(true);
|
||||
} else {
|
||||
$config->setAutoGenerateProxyClasses(false);
|
||||
$config->setAutoGenerateProxyClasses($applicationMode === 'development')
|
||||
|
||||
if ('development' === $applicationMode) {
|
||||
$config->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_EVAL);
|
||||
}
|
||||
|
||||
$connectionOptions = array(
|
||||
|
||||
$connectionOptions = [
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => 'database.sqlite'
|
||||
);
|
||||
|
||||
];
|
||||
|
||||
$em = EntityManager::create($connectionOptions, $config);
|
||||
|
||||
.. note::
|
||||
@@ -50,9 +50,8 @@ steps of configuration.
|
||||
conversions with the query cache. These 2 caches require only an
|
||||
absolute minimum of memory yet they heavily improve the runtime
|
||||
performance of Doctrine. The recommended cache driver to use with
|
||||
Doctrine is `APC <http://www.php.net/apc>`_. APC provides you with
|
||||
an opcode-cache (which is highly recommended anyway) and a very
|
||||
fast in-memory cache storage that you can use for the metadata and
|
||||
Doctrine is `APCu <https://php.net/apcu>`_. APCu provides you with
|
||||
a very fast in-memory cache storage that you can use for the metadata and
|
||||
query caches as seen in the previous code snippet.
|
||||
|
||||
Configuration Options
|
||||
@@ -61,30 +60,32 @@ Configuration Options
|
||||
The following sections describe all the configuration options
|
||||
available on a ``Doctrine\ORM\Configuration`` instance.
|
||||
|
||||
Proxy Directory (***REQUIRED***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Proxy Directory
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setProxyDir($dir);
|
||||
$config->getProxyDir();
|
||||
|
||||
Gets or sets the directory where Doctrine generates any proxy
|
||||
Sets the directory where Doctrine generates any proxy
|
||||
classes. For a detailed explanation on proxy classes and how they
|
||||
are used in Doctrine, refer to the "Proxy Objects" section further
|
||||
down.
|
||||
|
||||
Proxy Namespace (***REQUIRED***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Setting the proxy target directory will also implicitly cause a
|
||||
call to ``Doctrine\ORM\Configuration#setAutoGenerateProxyClasses()``
|
||||
with a value of ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``.
|
||||
|
||||
Proxy Namespace
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setProxyNamespace($namespace);
|
||||
$config->getProxyNamespace();
|
||||
|
||||
Gets or sets the namespace to use for generated proxy classes. For
|
||||
Sets the namespace to use for generated proxy classes. For
|
||||
a detailed explanation on proxy classes and how they are used in
|
||||
Doctrine, refer to the "Proxy Objects" section further down.
|
||||
|
||||
@@ -103,16 +104,13 @@ classes.
|
||||
|
||||
There are currently 4 available implementations:
|
||||
|
||||
|
||||
- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\XmlDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\YamlDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\DriverChain``
|
||||
|
||||
Throughout the most part of this manual the AnnotationDriver is
|
||||
used in the examples. For information on the usage of the XmlDriver
|
||||
or YamlDriver please refer to the dedicated chapters
|
||||
``XML Mapping`` and ``YAML Mapping``.
|
||||
please refer to the dedicated chapters ``XML Mapping``.
|
||||
|
||||
The annotation driver can be configured with a factory method on
|
||||
the ``Doctrine\ORM\Configuration``:
|
||||
@@ -141,7 +139,7 @@ Metadata Cache (***RECOMMENDED***)
|
||||
|
||||
Gets or sets the cache implementation to use for caching metadata
|
||||
information, that is, all the information you supply via
|
||||
annotations, xml or yaml, so that they do not need to be parsed and
|
||||
annotations or 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
|
||||
``Doctrine\Common\Cache\Cache`` interface.
|
||||
@@ -150,8 +148,6 @@ Usage of a metadata cache is highly recommended.
|
||||
|
||||
The recommended implementations for production are:
|
||||
|
||||
|
||||
- ``Doctrine\Common\Cache\ApcCache``
|
||||
- ``Doctrine\Common\Cache\ApcuCache``
|
||||
- ``Doctrine\Common\Cache\MemcacheCache``
|
||||
- ``Doctrine\Common\Cache\XcacheCache``
|
||||
@@ -182,8 +178,6 @@ Usage of a query cache is highly recommended.
|
||||
|
||||
The recommended implementations for production are:
|
||||
|
||||
|
||||
- ``Doctrine\Common\Cache\ApcCache``
|
||||
- ``Doctrine\Common\Cache\ApcuCache``
|
||||
- ``Doctrine\Common\Cache\MemcacheCache``
|
||||
- ``Doctrine\Common\Cache\XcacheCache``
|
||||
@@ -209,8 +203,8 @@ implementation that logs to the standard output using ``echo`` and
|
||||
``var_dump`` can be found at
|
||||
``Doctrine\DBAL\Logging\EchoSQLLogger``.
|
||||
|
||||
Auto-generating Proxy Classes (***OPTIONAL***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Auto-generating Proxy Classes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Proxy classes can either be generated manually through the Doctrine
|
||||
Console or automatically at runtime by Doctrine. The configuration
|
||||
@@ -223,65 +217,89 @@ option that controls this behavior is:
|
||||
|
||||
Possible values for ``$mode`` are:
|
||||
|
||||
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_NEVER``
|
||||
|
||||
Never autogenerate a proxy. You will need to generate the proxies
|
||||
manually, for this use the Doctrine Console like so:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine orm:generate-proxies
|
||||
|
||||
When you do this in a development environment,
|
||||
be aware that you may get class/file not found errors if certain proxies
|
||||
are not yet generated. You may also get failing lazy-loads if new
|
||||
methods were added to the entity class that are not yet in the proxy class.
|
||||
In such a case, simply use the Doctrine Console to (re)generate the
|
||||
proxy classes.
|
||||
|
||||
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_ALWAYS``
|
||||
|
||||
Always generates a new proxy in every request and writes it to disk.
|
||||
|
||||
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``
|
||||
- ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``
|
||||
|
||||
Generate the proxy class when the proxy file does not exist.
|
||||
This strategy causes a file exists call whenever any proxy is
|
||||
used the first time in a request.
|
||||
This strategy can potentially cause disk access.
|
||||
Note that autoloading will be attempted before falling back
|
||||
to generating a proxy class: if an already existing proxy class
|
||||
is found, then no file write operations will be performed.
|
||||
|
||||
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_EVAL``
|
||||
- ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_EVAL``
|
||||
|
||||
Generate the proxy classes and evaluate them on the fly via eval(),
|
||||
Generate the proxy classes and evaluate them on the fly via ``eval()``,
|
||||
avoiding writing the proxies to disk.
|
||||
This strategy is only sane for development.
|
||||
This strategy is only sane for development and long running
|
||||
processes.
|
||||
|
||||
In a production environment, it is highly recommended to use
|
||||
AUTOGENERATE_NEVER to allow for optimal performances. The other
|
||||
options are interesting in development environment.
|
||||
- ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_NEVER``
|
||||
|
||||
This flag is deprecated, and is an alias
|
||||
of ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_EVAL``
|
||||
|
||||
- ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_ALWAYS``
|
||||
|
||||
This flag is deprecated, and is an alias
|
||||
of ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``
|
||||
|
||||
Before v2.4, ``setAutoGenerateProxyClasses`` would accept a boolean
|
||||
value. This is still possible, ``FALSE`` being equivalent to
|
||||
AUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS.
|
||||
|
||||
Manually generating Proxy Classes for performance
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
While the ORM can generate proxy classes when required, it is suggested
|
||||
to not let this happen for production environments, as it has a major
|
||||
impact on your application's performance.
|
||||
|
||||
In a production environment, it is highly recommended to use
|
||||
``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``
|
||||
in combination with a well-configured
|
||||
`composer class autoloader<https://getcomposer.org/doc/01-basic-usage.md#autoloading>`_.
|
||||
|
||||
Here is an example of such setup:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"MyProject\\": "path/to/project/sources/",
|
||||
"GeneratedProxies\\": "path/to/generated/proxies/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
You would then configure the ORM to use the ``"GeneratedProxies"``
|
||||
and the ``"path/to/generated/proxies/"`` for the proxy classes:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setProxyDir('path/to/generated/proxies/');
|
||||
$config->setProxyNamespace('GeneratedProxies');
|
||||
|
||||
To make sure proxies are never generated by Doctrine, you'd forcefully
|
||||
generate them during deployment operations:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ ./vendor/bin/doctrine orm:generate-proxies
|
||||
$ composer dump-autoload
|
||||
|
||||
Development vs Production Configuration
|
||||
---------------------------------------
|
||||
|
||||
You should code your Doctrine2 bootstrapping with two different
|
||||
runtime models in mind. There are some serious benefits of using
|
||||
APC or Memcache in production. In development however this will
|
||||
APCu or Memcache in production. In development however this will
|
||||
frequently give you fatal errors, when you change your entities and
|
||||
the cache still keeps the outdated metadata. That is why we
|
||||
recommend the ``ArrayCache`` for development.
|
||||
|
||||
Furthermore you should have the Auto-generating Proxy Classes
|
||||
option to true in development and to false in production. If this
|
||||
option is set to ``TRUE`` it can seriously hurt your script
|
||||
performance if several proxy classes are re-generated during script
|
||||
execution. Filesystem calls of that magnitude can even slower than
|
||||
all the database queries Doctrine issues. Additionally writing a
|
||||
proxy sets an exclusive file lock which can cause serious
|
||||
performance bottlenecks in systems with regular concurrent
|
||||
requests.
|
||||
Furthermore you should disable the Auto-generating Proxy Classes
|
||||
option in production.
|
||||
|
||||
Connection Options
|
||||
------------------
|
||||
@@ -292,7 +310,7 @@ instance of ``Doctrine\DBAL\Connection``. If an array is passed it
|
||||
is directly passed along to the DBAL Factory
|
||||
``Doctrine\DBAL\DriverManager::getConnection()``. The DBAL
|
||||
configuration is explained in the
|
||||
`DBAL section <./../../../../../projects/doctrine-dbal/en/latest/reference/configuration.html>`_.
|
||||
`DBAL section <https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/configuration.html>`_.
|
||||
|
||||
Proxy Objects
|
||||
-------------
|
||||
@@ -330,16 +348,17 @@ identifier. You could simply do this:
|
||||
<?php
|
||||
// $em instanceof EntityManager, $cart instanceof MyProject\Model\Cart
|
||||
// $itemId comes from somewhere, probably a request parameter
|
||||
$item = $em->getReference('MyProject\Model\Item', $itemId);
|
||||
$item = $em->getReference(\MyProject\Model\Item::class, $itemId);
|
||||
$cart->addItem($item);
|
||||
|
||||
Here, we added an Item to a Cart without loading the Item from the
|
||||
database. If you invoke any method on the Item instance, it would
|
||||
fully initialize its state transparently from the database. Here
|
||||
$item is actually an instance of the proxy class that was generated
|
||||
for the Item class but your code does not need to care. In fact it
|
||||
**should not care**. Proxy objects should be transparent to your
|
||||
code.
|
||||
Here, we added an ``Item`` to a ``Cart`` without loading the Item from the
|
||||
database.
|
||||
If you access any persistent state that isn't yet available in the ``Item``
|
||||
instance, the proxying mechanism would fully initialize the object's state
|
||||
transparently from the database.
|
||||
Here ``$item`` is actually an instance of the proxy class that was generated
|
||||
for the ``Item`` class but your code does not need to care. In fact it
|
||||
**should not care**. Proxy objects should be transparent to your code.
|
||||
|
||||
Association proxies
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
@@ -347,7 +366,7 @@ Association proxies
|
||||
The second most important situation where Doctrine uses proxy
|
||||
objects is when querying for objects. Whenever you query for an
|
||||
object that has a single-valued association to another object that
|
||||
is configured LAZY, without joining that association in the same
|
||||
is configured ``LAZY``, without joining that association in the same
|
||||
query, Doctrine puts proxy objects in place where normally the
|
||||
associated object would be. Just like other proxies it will
|
||||
transparently initialize itself on first access.
|
||||
@@ -359,61 +378,12 @@ transparently initialize itself on first access.
|
||||
This will override the 'fetch' option specified in the mapping for
|
||||
that association, but only for that query.
|
||||
|
||||
|
||||
Generating Proxy classes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In a production environment, it is highly recommended to use
|
||||
``AUTOGENERATE_NEVER`` to allow for optimal performances.
|
||||
However you will be required to generate the proxies manually
|
||||
using the Doctrine Console:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine orm:generate-proxies
|
||||
|
||||
The other options are interesting in development environment:
|
||||
|
||||
- ``AUTOGENERATE_ALWAYS`` will require you to create and configure
|
||||
a proxy directory. Proxies will be generated and written to file
|
||||
on each request, so any modification to your code will be acknowledged.
|
||||
|
||||
- ``AUTOGENERATE_FILE_NOT_EXISTS`` will not overwrite an existing
|
||||
proxy file. If your code changes, you will need to regenerate the
|
||||
proxies manually.
|
||||
|
||||
- ``AUTOGENERATE_EVAL`` will regenerate each proxy on each request,
|
||||
but without writing them to disk.
|
||||
|
||||
Autoloading Proxies
|
||||
-------------------
|
||||
|
||||
When you deserialize proxy objects from the session or any other storage
|
||||
it is necessary to have an autoloading mechanism in place for these classes.
|
||||
For implementation reasons Proxy class names are not PSR-0 compliant. This
|
||||
means that you have to register a special autoloader for these classes:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\Proxy\Autoloader;
|
||||
|
||||
$proxyDir = "/path/to/proxies";
|
||||
$proxyNamespace = "MyProxies";
|
||||
|
||||
Autoloader::register($proxyDir, $proxyNamespace);
|
||||
|
||||
If you want to execute additional logic to intercept the proxy file not found
|
||||
state you can pass a closure as the third argument. It will be called with
|
||||
the arguments proxydir, namespace and className when the proxy file could not
|
||||
be found.
|
||||
|
||||
Multiple Metadata Sources
|
||||
-------------------------
|
||||
|
||||
When using different components using Doctrine 2 you may end up
|
||||
with them using two different metadata drivers, for example XML and
|
||||
YAML. You can use the DriverChain Metadata implementations to
|
||||
annotationsL. You can use the DriverChain Metadata implementations to
|
||||
aggregate these drivers based on namespaces:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -423,7 +393,7 @@ aggregate these drivers based on namespaces:
|
||||
|
||||
$chain = new DriverChain();
|
||||
$chain->addDriver($xmlDriver, 'Doctrine\Tests\Models\Company');
|
||||
$chain->addDriver($yamlDriver, 'Doctrine\Tests\ORM\Mapping');
|
||||
$chain->addDriver($annotationDriver, '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
|
||||
@@ -433,7 +403,6 @@ the entity class name against the namespace using a
|
||||
correctly if sub-namespaces use different metadata driver
|
||||
implementations.
|
||||
|
||||
|
||||
Default Repository (***OPTIONAL***)
|
||||
-----------------------------------
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ annotations for supplying object-relational mapping metadata.
|
||||
|
||||
If you're not comfortable with the concept of docblock
|
||||
annotations, don't worry, as mentioned earlier Doctrine 2 provides
|
||||
XML and YAML alternatives and you could easily implement your own
|
||||
the XML alternative and you could easily implement your own
|
||||
favourite mechanism for defining ORM metadata.
|
||||
|
||||
In this chapter a reference of every Doctrine 2 Annotation is given
|
||||
@@ -181,7 +181,7 @@ Examples:
|
||||
protected $initials;
|
||||
|
||||
/**
|
||||
* @Column(type="integer", name="login_count" nullable=false, options={"unsigned":true, "default":0})
|
||||
* @Column(type="integer", name="login_count", nullable=false, options={"unsigned":true, "default":0})
|
||||
*/
|
||||
protected $loginCount;
|
||||
|
||||
@@ -254,7 +254,7 @@ Example:
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Id
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue(strategy="CUSTOM")
|
||||
* @CustomIdGenerator(class="My\Namespace\MyIdGenerator")
|
||||
@@ -276,13 +276,11 @@ to a string column of length 255 called ``dtype``.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **name**: The column name of the discriminator. This name is also
|
||||
used during Array hydration as key to specify the class-name.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **type**: By default this is string.
|
||||
- **length**: By default this is 255.
|
||||
|
||||
@@ -312,7 +310,6 @@ depending on whether the classes are in the namespace or not.
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
.. _annref_embeddable:
|
||||
|
||||
@Embeddable
|
||||
@@ -339,7 +336,6 @@ annotation to establish the relationship between the two classes.
|
||||
*/
|
||||
private $address;
|
||||
|
||||
|
||||
.. _annref_embedded:
|
||||
|
||||
@Embedded
|
||||
@@ -352,7 +348,6 @@ Required attributes:
|
||||
|
||||
- **class**: The embeddable class
|
||||
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
@@ -372,7 +367,6 @@ Required attributes:
|
||||
{
|
||||
// ...
|
||||
|
||||
|
||||
.. _annref_entity:
|
||||
|
||||
@Entity
|
||||
@@ -383,7 +377,6 @@ the persistence of all classes marked as entities.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **repositoryClass**: Specifies the FQCN of a subclass of the
|
||||
EntityRepository. Use of repositories for entities is encouraged to keep
|
||||
specialized DQL and SQL operations separated from the Model/Domain
|
||||
@@ -398,11 +391,11 @@ Example:
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity(repositoryClass="MyProject\UserRepository")
|
||||
* @Entity(repositoryClass="MyProject\UserRepository", readOnly=true)
|
||||
*/
|
||||
class User
|
||||
{
|
||||
//...
|
||||
// ...
|
||||
}
|
||||
|
||||
.. _annref_entity_result:
|
||||
@@ -433,7 +426,6 @@ Required attributes:
|
||||
|
||||
- **name**: Name of the persistent field or property of the class.
|
||||
|
||||
|
||||
Optional attributes:
|
||||
|
||||
- **column**: Name of the column in the SELECT clause.
|
||||
@@ -453,9 +445,9 @@ used as default.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **strategy**: Set the name of the identifier generation strategy.
|
||||
Valid values are AUTO, SEQUENCE, TABLE, IDENTITY, UUID, CUSTOM and NONE.
|
||||
Valid values are ``AUTO``, ``SEQUENCE``, ``TABLE``, ``IDENTITY``, ``CUSTOM`` and ``NONE``, explained
|
||||
in the :ref:`Identifier Generation Strategies <identifier-generation-strategies>` section.
|
||||
If not specified, default value is AUTO.
|
||||
|
||||
Example:
|
||||
@@ -511,7 +503,6 @@ has meaning in the SchemaTool schema generation context.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **name**: Name of the Index
|
||||
- **columns**: Array of columns.
|
||||
|
||||
@@ -625,7 +616,6 @@ inferred from the table and primary key names.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **name**: Column name that holds the foreign key identifier for
|
||||
this relation. In the context of @JoinTable it specifies the column
|
||||
name in the join table.
|
||||
@@ -634,7 +624,6 @@ Required attributes:
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **unique**: Determines whether this relation is exclusive between the
|
||||
affected entities and should be enforced as such on the database
|
||||
constraint level. Defaults to false.
|
||||
@@ -685,7 +674,6 @@ using the affected table and the column names.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **name**: Database name of the join-table
|
||||
- **joinColumns**: An array of @JoinColumn annotations describing the
|
||||
join-relation between the owning entities table and the join table.
|
||||
@@ -717,14 +705,12 @@ describes a many-to-one relationship between two entities.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **targetEntity**: FQCN of the referenced target entity. Can be the
|
||||
unqualified class name if both classes are in the same namespace.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **cascade**: Cascade Option
|
||||
- **fetch**: One of LAZY or EAGER
|
||||
- inversedBy - The inversedBy attribute designates the field in
|
||||
@@ -753,14 +739,12 @@ entities.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **targetEntity**: FQCN of the referenced target entity. Can be the
|
||||
unqualified class name if both classes are in the same namespace.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **mappedBy**: This option specifies the property name on the
|
||||
targetEntity that is the owning side of this relation. It is a
|
||||
required attribute for the inverse side of a relationship.
|
||||
@@ -816,7 +800,6 @@ The @MappedSuperclass annotation cannot be used in conjunction with
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **repositoryClass**: (>= 2.2) Specifies the FQCN of a subclass of the EntityRepository.
|
||||
That will be inherited for all subclasses of that Mapped Superclass.
|
||||
|
||||
@@ -853,13 +836,11 @@ Required attributes:
|
||||
- **name**: The name used to refer to the query with the EntityManager methods that create query objects.
|
||||
- **query**: The SQL query string.
|
||||
|
||||
|
||||
Optional attributes:
|
||||
|
||||
- **resultClass**: The class of the result.
|
||||
- **resultSetMapping**: The name of a SqlResultSetMapping, as defined in metadata.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -925,14 +906,12 @@ primary key column names apply here too.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **targetEntity**: FQCN of the referenced target entity. Can be the
|
||||
unqualified class name if both classes are in the same namespace.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **cascade**: Cascade Option
|
||||
- **fetch**: One of LAZY or EAGER
|
||||
- **orphanRemoval**: Boolean that specifies if orphans, inverse
|
||||
@@ -959,14 +938,12 @@ Example:
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **targetEntity**: FQCN of the referenced target entity. Can be the
|
||||
unqualified class name if both classes are in the same namespace.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **cascade**: Cascade Option
|
||||
- **orphanRemoval**: Boolean that specifies if orphans, inverse
|
||||
OneToOne entities that are not connected to any owning instance,
|
||||
@@ -983,7 +960,7 @@ Example:
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @OneToMany(targetEntity="Phonenumber", mappedBy="user", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
|
||||
* @OneToMany(targetEntity="Phonenumber", mappedBy="user", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||
*/
|
||||
public $phonenumbers;
|
||||
|
||||
@@ -1091,12 +1068,10 @@ the increment size and initial values of the sequence.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **sequenceName**: Name of the sequence
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **allocationSize**: Increment the sequence by the allocation size
|
||||
when its fetched. A value larger than 1 allows optimization for
|
||||
scenarios where you create more than one new entity per request.
|
||||
@@ -1127,7 +1102,6 @@ Required attributes:
|
||||
|
||||
- **name**: The name given to the result set mapping, and used to refer to it in the methods of the Query API.
|
||||
|
||||
|
||||
Optional attributes:
|
||||
|
||||
- **entities**: Array of @EntityResult, Specifies the result set mapping to entities.
|
||||
@@ -1228,12 +1202,10 @@ unqualified classname.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **name**: Name of the table
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **indexes**: Array of @Index annotations
|
||||
- **uniqueConstraints**: Array of @UniqueConstraint annotations.
|
||||
- **schema**: (>= 2.5) Name of the schema the table lies in.
|
||||
@@ -1266,7 +1238,6 @@ context.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **name**: Name of the Index
|
||||
- **columns**: Array of columns.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ well.
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Doctrine 2 requires a minimum of PHP 5.4. For greatly improved
|
||||
Doctrine 2 requires a minimum of PHP 7.1. For greatly improved
|
||||
performance it is also recommended that you use APC with PHP.
|
||||
|
||||
Doctrine 2 Packages
|
||||
@@ -34,7 +34,6 @@ This manual mainly covers the ORM package, sometimes touching parts
|
||||
of the underlying DBAL and Common packages. The Doctrine code base
|
||||
is split in to these packages for a few reasons and they are to...
|
||||
|
||||
|
||||
- ...make things more maintainable and decoupled
|
||||
- ...allow you to use the code in Doctrine Common without the ORM
|
||||
or DBAL
|
||||
@@ -72,27 +71,16 @@ Entities
|
||||
An entity is a lightweight, persistent domain object. An entity can
|
||||
be any regular PHP class observing the following restrictions:
|
||||
|
||||
|
||||
- An entity class must not be final or contain final methods.
|
||||
- All persistent properties/field of any entity class should
|
||||
always be private or protected, otherwise lazy-loading might not
|
||||
work as expected. In case you serialize entities (for example Session)
|
||||
properties should be protected (See Serialize section below).
|
||||
- An entity class must not implement ``__clone`` or
|
||||
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
|
||||
- An entity class must not implement ``__wakeup`` or
|
||||
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
|
||||
Also consider implementing
|
||||
`Serializable <http://php.net/manual/en/class.serializable.php>`_
|
||||
instead.
|
||||
- Any two entity classes in a class hierarchy that inherit
|
||||
directly or indirectly from one another must not have a mapped
|
||||
property with the same name. That is, if B inherits from A then B
|
||||
must not have a mapped field with the same name as an already
|
||||
mapped field that is inherited from A.
|
||||
- An entity cannot make use of func_get_args() to implement variable parameters.
|
||||
Generated proxies do not support this for performance reasons and your code might
|
||||
actually fail to work when violating this restriction.
|
||||
|
||||
Entities support inheritance, polymorphic associations, and
|
||||
polymorphic queries. Both abstract and concrete classes can be
|
||||
@@ -106,14 +94,12 @@ classes, and non-entity classes may extend entity classes.
|
||||
never calls entity constructors, thus you are free to use them as
|
||||
you wish and even have it require arguments of any type.
|
||||
|
||||
|
||||
Entity states
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
An entity instance can be characterized as being NEW, MANAGED,
|
||||
DETACHED or REMOVED.
|
||||
|
||||
|
||||
- A NEW entity instance has no persistent identity, and is not yet
|
||||
associated with an EntityManager and a UnitOfWork (i.e. those just
|
||||
created with the "new" operator).
|
||||
@@ -150,18 +136,18 @@ subsequent access must be through the interface type.
|
||||
Serializing entities
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Serializing entities can be problematic and is not really
|
||||
recommended, at least not as long as an entity instance still holds
|
||||
references to proxy objects or is still managed by an
|
||||
EntityManager. If you intend to serialize (and unserialize) entity
|
||||
Serializing entities is generally to be avoided.
|
||||
|
||||
If you intend to serialize (and unserialize) entity
|
||||
instances that still hold references to proxy objects you may run
|
||||
into problems with private properties because of technical
|
||||
limitations. Proxy objects implement ``__sleep`` and it is not
|
||||
possible for ``__sleep`` to return names of private properties in
|
||||
parent classes. On the other hand it is not a solution for proxy
|
||||
objects to implement ``Serializable`` because Serializable does not
|
||||
work well with any potential cyclic object references (at least we
|
||||
did not find a way yet, if you did, please contact us).
|
||||
into problems, because all proxy properties will be initialized
|
||||
recursively, leading to large serialized object graphs, especially
|
||||
for circular associations.
|
||||
|
||||
If you really must serialize entities, regardless if proxies are
|
||||
involved or not, then consider implementing the ``Serializable``
|
||||
interface and manually checking for cyclic dependencies in your
|
||||
object graph.
|
||||
|
||||
The EntityManager
|
||||
~~~~~~~~~~~~~~~~~
|
||||
@@ -189,9 +175,8 @@ The Unit of Work
|
||||
|
||||
Internally an ``EntityManager`` uses a ``UnitOfWork``, which is a
|
||||
typical implementation of the
|
||||
`Unit of Work pattern <http://martinfowler.com/eaaCatalog/unitOfWork.html>`_,
|
||||
`Unit of Work pattern <https://martinfowler.com/eaaCatalog/unitOfWork.html>`_,
|
||||
to keep track of all the things that need to be done the next time
|
||||
``flush`` is invoked. You usually do not directly interact with a
|
||||
``UnitOfWork`` but with the ``EntityManager`` instead.
|
||||
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ One tip for working with relations is to read the relation from left to right, w
|
||||
- ManyToOne - Many instances of the current Entity refer to One instance of the refered Entity.
|
||||
- OneToOne - One instance of the current Entity refers to One instance of the refered Entity.
|
||||
|
||||
See below for all the possible relations.
|
||||
See below for all the possible relations.
|
||||
|
||||
An association is considered to be unidirectional if only one side of the association has
|
||||
An association is considered to be unidirectional if only one side of the association has
|
||||
a property referring to the other side.
|
||||
|
||||
To gain a full understanding of associations you should also read about :doc:`owning and
|
||||
@@ -68,18 +68,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
|
||||
@@ -145,17 +133,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.
|
||||
|
||||
@@ -182,7 +159,7 @@ Here is a one-to-one relationship between a ``Customer`` and a
|
||||
``Cart``. The ``Cart`` has a reference back to the ``Customer`` so
|
||||
it is bidirectional.
|
||||
|
||||
Here we see the ``mappedBy`` and ``inversedBy`` annotations for the first time.
|
||||
Here we see the ``mappedBy`` and ``inversedBy`` attributes for the first time.
|
||||
They are used to tell Doctrine which property on the other side refers to the
|
||||
object.
|
||||
|
||||
@@ -233,22 +210,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.
|
||||
|
||||
@@ -259,6 +220,7 @@ Generated MySQL Schema:
|
||||
CREATE TABLE Cart (
|
||||
id INT AUTO_INCREMENT NOT NULL,
|
||||
customer_id INT DEFAULT NULL,
|
||||
UNIQUE INDEX UNIQ_BA388B79395C3F3 (customer_id),
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = InnoDB;
|
||||
CREATE TABLE Customer (
|
||||
@@ -313,8 +275,8 @@ One-To-Many, Bidirectional
|
||||
--------------------------
|
||||
|
||||
A one-to-many association has to be bidirectional, unless you are using a
|
||||
join table. This is because the many side in a one-to-many association holds
|
||||
the foreign key, making it the owning side. Doctrine needs the many side
|
||||
join table. This is because the "many" side in a one-to-many association holds
|
||||
the foreign key, making it the owning side. Doctrine needs the "many" side
|
||||
defined in order to understand the association.
|
||||
|
||||
This bidirectional mapping requires the ``mappedBy`` attribute on the
|
||||
@@ -335,7 +297,7 @@ bidirectional many-to-one.
|
||||
{
|
||||
// ...
|
||||
/**
|
||||
* One Product has Many Features.
|
||||
* One product has many features. This is the inverse side.
|
||||
* @OneToMany(targetEntity="Feature", mappedBy="product")
|
||||
*/
|
||||
private $features;
|
||||
@@ -351,7 +313,7 @@ bidirectional many-to-one.
|
||||
{
|
||||
// ...
|
||||
/**
|
||||
* Many Features have One Product.
|
||||
* Many features have one product. This is the owning side.
|
||||
* @ManyToOne(targetEntity="Product", inversedBy="features")
|
||||
* @JoinColumn(name="product_id", referencedColumnName="id")
|
||||
*/
|
||||
@@ -372,24 +334,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.
|
||||
|
||||
@@ -469,24 +413,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
|
||||
@@ -557,19 +483,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.
|
||||
|
||||
@@ -641,22 +554,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
|
||||
@@ -754,30 +651,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.
|
||||
|
||||
@@ -923,14 +796,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:
|
||||
|
||||
@@ -956,17 +821,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:
|
||||
|
||||
@@ -977,10 +831,10 @@ similar defaults. As an example, consider this mapping:
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
//...
|
||||
// ...
|
||||
/** @ManyToMany(targetEntity="Group") */
|
||||
private $groups;
|
||||
//...
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
@@ -991,14 +845,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::
|
||||
@@ -1008,7 +854,7 @@ This is essentially the same as the following, more verbose, mapping:
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
//...
|
||||
// ...
|
||||
/**
|
||||
* Many Users have Many Groups.
|
||||
* @ManyToMany(targetEntity="Group")
|
||||
@@ -1018,7 +864,7 @@ This is essentially the same as the following, more verbose, mapping:
|
||||
* )
|
||||
*/
|
||||
private $groups;
|
||||
//...
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
@@ -1038,22 +884,6 @@ This is essentially the same as the following, more verbose, mapping:
|
||||
</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
|
||||
|
||||
@@ -52,16 +52,15 @@ mapping metadata:
|
||||
|
||||
- :doc:`Docblock Annotations <annotations-reference>`
|
||||
- :doc:`XML <xml-mapping>`
|
||||
- :doc:`YAML <yaml-mapping>`
|
||||
- :doc:`PHP code <php-mapping>`
|
||||
|
||||
This manual will usually show mapping metadata via docblock annotations, though
|
||||
many examples also show the equivalent configuration in 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 (annotations, xml or yaml) it is stored in an instance
|
||||
read from the source (annotations or xml) it is stored in an instance
|
||||
of the ``Doctrine\ORM\Mapping\ClassMetadata`` class and these instances are
|
||||
stored in the metadata cache. If you're not using a metadata cache (not
|
||||
recommended!) then the XML driver is the fastest.
|
||||
@@ -76,7 +75,7 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward:
|
||||
/** @Entity */
|
||||
class Message
|
||||
{
|
||||
//...
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
@@ -87,12 +86,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:
|
||||
@@ -108,7 +101,7 @@ You can change this by configuring information about the table:
|
||||
*/
|
||||
class Message
|
||||
{
|
||||
//...
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
@@ -119,13 +112,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
|
||||
@@ -165,19 +151,6 @@ 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. This means that:
|
||||
|
||||
@@ -227,36 +200,48 @@ 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.
|
||||
- ``string``: Type that maps an SQL VARCHAR to a PHP string.
|
||||
- ``integer``: Type that maps an 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
|
||||
- ``boolean``: Type that maps an SQL boolean or equivalent (TINYINT) to a PHP boolean.
|
||||
- ``decimal``: Type that maps an SQL DECIMAL to a PHP string.
|
||||
- ``date``: Type that maps an 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
|
||||
- ``date_immutable``: Type that maps an SQL DATETIME to a PHP DateTimeImmutable
|
||||
object.
|
||||
- ``time``: Type that maps an SQL TIME to a PHP DateTime object.
|
||||
- ``time_immutable``: Type that maps an SQL TIME to a PHP DateTimeImmutable object.
|
||||
- ``datetime``: Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTime
|
||||
object with the current timezone.
|
||||
- ``datetimetz``: Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTime
|
||||
object with the timezone specified in the value from the database.
|
||||
- ``datetime_immutable``: Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTimeImmutable
|
||||
object with the current timezone.
|
||||
- ``datetimetz_immutable``: Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTimeImmutable
|
||||
object with the timezone specified in the value from the database.
|
||||
- ``dateinterval``: Type that maps an interval to a PHP DateInterval object
|
||||
- ``text``: Type that maps an SQL CLOB to a PHP string.
|
||||
- ``object``: Type that maps an SQL CLOB to a PHP object using
|
||||
``serialize()`` and ``unserialize()``
|
||||
- ``array``: Type that maps a SQL CLOB to a PHP array using
|
||||
- ``array``: Type that maps an SQL CLOB to a PHP array using
|
||||
``serialize()`` and ``unserialize()``
|
||||
- ``simple_array``: Type that maps a SQL CLOB to a PHP array using
|
||||
- ``simple_array``: Type that maps an SQL CLOB to a one-dimensional 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
|
||||
- ``json_array``: Type that maps an SQL CLOB to a PHP array using
|
||||
``json_encode()`` and ``json_decode()``. This one has been deprecated in favor
|
||||
of ``json`` type.
|
||||
- ``json``: Type that maps an SQL CLOB to a PHP array using
|
||||
``json_encode()`` and ``json_decode()``. An empty value is correctly represented as ``null``
|
||||
- ``float``: Type that maps an 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
|
||||
- ``blob``: Type that maps an SQL BLOB to a PHP resource stream
|
||||
- ``binary``: Type that maps an SQL binary to a PHP resource stream
|
||||
|
||||
A cookbook article shows how to define :doc:`your own custom mapping types
|
||||
<../cookbook/custom-mapping-types>`.
|
||||
@@ -270,7 +255,7 @@ A cookbook article shows how to define :doc:`your own custom mapping types
|
||||
.. warning::
|
||||
|
||||
All Date types assume that you are exclusively using the default timezone
|
||||
set by `date_default_timezone_set() <http://docs.php.net/manual/en/function.date-default-timezone-set.php>`_
|
||||
set by `date_default_timezone_set() <https://php.net/manual/en/function.date-default-timezone-set.php>`_
|
||||
or by the php.ini configuration ``date.timezone``. Working with
|
||||
different timezones will cause troubles and unexpected behavior.
|
||||
|
||||
@@ -300,7 +285,7 @@ annotation.
|
||||
* @GeneratedValue
|
||||
*/
|
||||
private $id;
|
||||
//...
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
@@ -314,23 +299,13 @@ annotation.
|
||||
</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. It defaults to the identifier generation mechanism your current
|
||||
database vendor prefers: AUTO_INCREMENT with MySQL, SERIAL with PostgreSQL,
|
||||
Sequences with Oracle and so on.
|
||||
|
||||
.. _identifier-generation-strategies:
|
||||
|
||||
Identifier Generation Strategies
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -355,8 +330,6 @@ Here is the list of possible generation strategies:
|
||||
strategy does currently not provide full portability and is
|
||||
supported by the following platforms: MySQL/SQLite/SQL Anywhere
|
||||
(AUTO\_INCREMENT), MSSQL (IDENTITY) and PostgreSQL (SERIAL).
|
||||
- ``UUID``: Tells Doctrine to use the built-in Universally Unique Identifier
|
||||
generator. This strategy provides full portability.
|
||||
- ``TABLE``: Tells Doctrine to use a separate table for ID
|
||||
generation. This strategy provides full portability.
|
||||
***This strategy is not yet implemented!***
|
||||
@@ -387,7 +360,7 @@ besides specifying the sequence's name:
|
||||
* @SequenceGenerator(sequenceName="message_seq", initialValue=1, allocationSize=100)
|
||||
*/
|
||||
protected $id = null;
|
||||
//...
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
@@ -401,20 +374,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.
|
||||
|
||||
@@ -438,7 +397,6 @@ need to access the sequence once to generate the identifiers for
|
||||
configuration option is never larger than the actual sequences
|
||||
INCREMENT BY value, otherwise you may get duplicate keys.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
It is possible to use strategy="AUTO" and at the same time
|
||||
@@ -447,7 +405,6 @@ need to access the sequence once to generate the identifiers for
|
||||
of the underlying platform is SEQUENCE, such as for Oracle and
|
||||
PostgreSQL.
|
||||
|
||||
|
||||
Composite Keys
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ especially what the strategies presented here provide help with.
|
||||
you use the tools for your particular RDBMS for these bulk
|
||||
operations.
|
||||
|
||||
|
||||
Bulk Inserts
|
||||
------------
|
||||
|
||||
@@ -42,7 +41,7 @@ internally but also mean more work during ``flush``.
|
||||
$em->clear(); // Detaches all objects from Doctrine!
|
||||
}
|
||||
}
|
||||
$em->flush(); //Persist objects that did not make up an entire batch
|
||||
$em->flush(); // Persist objects that did not make up an entire batch
|
||||
$em->clear();
|
||||
|
||||
Bulk Updates
|
||||
@@ -75,7 +74,7 @@ with the batching strategy that was already used for bulk inserts:
|
||||
|
||||
<?php
|
||||
$batchSize = 20;
|
||||
$i = 0;
|
||||
$i = 1;
|
||||
$q = $em->createQuery('select u from MyProject\Model\User u');
|
||||
$iterableResult = $q->iterate();
|
||||
foreach ($iterableResult as $row) {
|
||||
@@ -102,7 +101,6 @@ with the batching strategy that was already used for bulk inserts:
|
||||
additional memory not visible to the PHP process. For large sets this
|
||||
may easily kill the process for no apparent reason.
|
||||
|
||||
|
||||
Bulk Deletes
|
||||
------------
|
||||
|
||||
@@ -136,7 +134,7 @@ The following example shows how to do this:
|
||||
|
||||
<?php
|
||||
$batchSize = 20;
|
||||
$i = 0;
|
||||
$i = 1;
|
||||
$q = $em->createQuery('select u from MyProject\Model\User u');
|
||||
$iterableResult = $q->iterate();
|
||||
while (($row = $iterableResult->next()) !== false) {
|
||||
@@ -155,7 +153,6 @@ The following example shows how to do this:
|
||||
fetch-join a collection-valued association. The nature of such SQL
|
||||
result sets is not suitable for incremental hydration.
|
||||
|
||||
|
||||
Iterating Large Results for Data-Processing
|
||||
-------------------------------------------
|
||||
|
||||
@@ -168,13 +165,13 @@ problems using the following approach:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$q = $this->_em->createQuery('select u from MyProject\Model\User u');
|
||||
$q = $this->em->createQuery('select u from MyProject\Model\User u');
|
||||
$iterableResult = $q->iterate();
|
||||
foreach ($iterableResult as $row) {
|
||||
// do stuff with the data in the row, $row[0] is always the object
|
||||
|
||||
// detach from Doctrine, so that it can be Garbage-Collected immediately
|
||||
$this->_em->detach($row[0]);
|
||||
|
||||
// detach all entities from Doctrine, so that Garbage-Collection can kick in immediately
|
||||
$this->em->clear();
|
||||
}
|
||||
|
||||
.. note::
|
||||
@@ -183,5 +180,9 @@ problems using the following approach:
|
||||
fetch-join a collection-valued association. The nature of such SQL
|
||||
result sets is not suitable for incremental hydration.
|
||||
|
||||
Packages for easing Batch Processing
|
||||
------------------------------------
|
||||
|
||||
|
||||
You can implement batch processing yourself, or use an existing
|
||||
package such as `DoctrineBatchUtils <https://github.com/Ocramius/DoctrineBatchUtils>`_,
|
||||
which already provides the logic described above in an encapsulated format.
|
||||
|
||||
@@ -12,14 +12,12 @@ Constrain relationships as much as possible
|
||||
It is important to constrain relationships as much as possible.
|
||||
This means:
|
||||
|
||||
|
||||
- Impose a traversal direction (avoid bidirectional associations
|
||||
if possible)
|
||||
- Eliminate nonessential associations
|
||||
|
||||
This has several benefits:
|
||||
|
||||
|
||||
- Reduced coupling in your domain model
|
||||
- Simpler code in your domain model (no need to maintain
|
||||
bidirectionality properly)
|
||||
@@ -43,7 +41,7 @@ should use events judiciously.
|
||||
Use cascades judiciously
|
||||
------------------------
|
||||
|
||||
Automatic cascades of the persist/remove/merge/etc. operations are
|
||||
Automatic cascades of the persist/remove/refresh/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
|
||||
@@ -74,11 +72,11 @@ collections in entities in the constructor. Example:
|
||||
<?php
|
||||
namespace MyProject\Model;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
|
||||
class User {
|
||||
private $addresses;
|
||||
private $articles;
|
||||
|
||||
|
||||
public function __construct() {
|
||||
$this->addresses = new ArrayCollection;
|
||||
$this->articles = new ArrayCollection;
|
||||
@@ -108,4 +106,3 @@ queries generally don't have any noticeable performance impact, it
|
||||
is still preferable to use fewer, well-defined transactions that
|
||||
are established through explicit transaction boundaries.
|
||||
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ Caching
|
||||
Doctrine provides cache drivers in the ``Common`` package for some
|
||||
of the most popular caching implementations such as APC, Memcache
|
||||
and Xcache. We also provide an ``ArrayCache`` driver which stores
|
||||
the data in a PHP array. Obviously, when using ``ArrayCache``, the
|
||||
cache does not persist between requests, but this is useful for
|
||||
the data in a PHP array. Obviously, when using ``ArrayCache``, the
|
||||
cache does not persist between requests, but this is useful for
|
||||
testing in a development environment.
|
||||
|
||||
Cache Drivers
|
||||
@@ -18,7 +18,6 @@ this interface.
|
||||
|
||||
The interface defines the following public methods for you to implement:
|
||||
|
||||
|
||||
- fetch($id) - Fetches an entry from the cache
|
||||
- contains($id) - Test if an entry exists in the cache
|
||||
- save($id, $data, $lifeTime = false) - Puts data into the cache for x seconds. 0 = infinite time
|
||||
@@ -28,11 +27,10 @@ Each driver extends the ``CacheProvider`` class which defines a few
|
||||
abstract protected methods that each of the drivers must
|
||||
implement:
|
||||
|
||||
|
||||
- \_doFetch($id)
|
||||
- \_doContains($id)
|
||||
- \_doSave($id, $data, $lifeTime = false)
|
||||
- \_doDelete($id)
|
||||
- doFetch($id)
|
||||
- doContains($id)
|
||||
- doSave($id, $data, $lifeTime = false)
|
||||
- doDelete($id)
|
||||
|
||||
The public methods ``fetch()``, ``contains()`` etc. use the
|
||||
above protected methods which are implemented by the drivers. The
|
||||
@@ -43,14 +41,14 @@ these methods.
|
||||
|
||||
This documentation does not cover every single cache driver included
|
||||
with Doctrine. For an up-to-date-list, see the
|
||||
`cache directory on GitHub <https://github.com/doctrine/cache/tree/master/lib/Doctrine/Common/Cache>`.
|
||||
`cache directory on GitHub <https://github.com/doctrine/cache/tree/master/lib/Doctrine/Common/Cache>`_.
|
||||
|
||||
APC
|
||||
~~~
|
||||
|
||||
In order to use the APC cache driver you must have it compiled and
|
||||
enabled in your php.ini. You can read about APC
|
||||
`in the PHP Documentation <http://us2.php.net/apc>`_. It will give
|
||||
`in the PHP Documentation <https://php.net/apc>`_. It will give
|
||||
you a little background information about what it is and how you
|
||||
can use it as well as how to install it.
|
||||
|
||||
@@ -68,7 +66,7 @@ APCu
|
||||
|
||||
In order to use the APCu cache driver you must have it compiled and
|
||||
enabled in your php.ini. You can read about APCu
|
||||
`in the PHP Documentation <http://us2.php.net/apcu>`_. It will give
|
||||
`in the PHP Documentation <https://php.net/apcu>`_. It will give
|
||||
you a little background information about what it is and how you
|
||||
can use it as well as how to install it.
|
||||
|
||||
@@ -86,7 +84,7 @@ Memcache
|
||||
|
||||
In order to use the Memcache cache driver you must have it compiled
|
||||
and enabled in your php.ini. You can read about Memcache
|
||||
`on the PHP website <http://php.net/memcache>`_. It will
|
||||
`on the PHP website <https://php.net/memcache>`_. It will
|
||||
give you a little background information about what it is and how
|
||||
you can use it as well as how to install it.
|
||||
|
||||
@@ -98,7 +96,7 @@ driver by itself.
|
||||
<?php
|
||||
$memcache = new Memcache();
|
||||
$memcache->connect('memcache_host', 11211);
|
||||
|
||||
|
||||
$cacheDriver = new \Doctrine\Common\Cache\MemcacheCache();
|
||||
$cacheDriver->setMemcache($memcache);
|
||||
$cacheDriver->save('cache_id', 'my_data');
|
||||
@@ -111,7 +109,7 @@ Memcache.
|
||||
|
||||
In order to use the Memcached cache driver you must have it compiled
|
||||
and enabled in your php.ini. You can read about Memcached
|
||||
`on the PHP website <http://php.net/memcached>`_. It will
|
||||
`on the PHP website <https://php.net/memcached>`_. It will
|
||||
give you a little background information about what it is and how
|
||||
you can use it as well as how to install it.
|
||||
|
||||
@@ -123,7 +121,7 @@ driver by itself.
|
||||
<?php
|
||||
$memcached = new Memcached();
|
||||
$memcached->addServer('memcache_host', 11211);
|
||||
|
||||
|
||||
$cacheDriver = new \Doctrine\Common\Cache\MemcachedCache();
|
||||
$cacheDriver->setMemcached($memcached);
|
||||
$cacheDriver->save('cache_id', 'my_data');
|
||||
@@ -133,7 +131,7 @@ Xcache
|
||||
|
||||
In order to use the Xcache cache driver you must have it compiled
|
||||
and enabled in your php.ini. You can read about Xcache
|
||||
`here <http://xcache.lighttpd.net/>`_. It will give you a little
|
||||
`here <https://xcache.lighttpd.net/>`_. It will give you a little
|
||||
background information about what it is and how you can use it as
|
||||
well as how to install it.
|
||||
|
||||
@@ -151,7 +149,7 @@ Redis
|
||||
|
||||
In order to use the Redis cache driver you must have it compiled
|
||||
and enabled in your php.ini. You can read about what Redis is
|
||||
`from here <http://redis.io/>`_. Also check
|
||||
`from here <https://redis.io/>`_. Also check
|
||||
`A PHP extension for Redis <https://github.com/nicolasff/phpredis/>`_ for how you can use
|
||||
and install the Redis PHP extension.
|
||||
|
||||
@@ -172,7 +170,7 @@ Using Cache Drivers
|
||||
-------------------
|
||||
|
||||
In this section we'll describe how you can fully utilize the API of
|
||||
the cache drivers to save data to a cache, check if some cached data
|
||||
the cache drivers to save data to a cache, check if some cached data
|
||||
exists, fetch the cached data and delete the cached data. We'll use the
|
||||
``ArrayCache`` implementation as our example here.
|
||||
|
||||
@@ -195,7 +193,6 @@ Saving some data to the cache driver is as simple as using the
|
||||
The ``save()`` method accepts three arguments which are described
|
||||
below:
|
||||
|
||||
|
||||
- ``$id`` - The cache id
|
||||
- ``$data`` - The cache entry/data.
|
||||
- ``$lifeTime`` - The lifetime. If != false, sets a specific
|
||||
@@ -245,7 +242,7 @@ Deleting
|
||||
~~~~~~~~
|
||||
|
||||
As you might guess, deleting is just as easy as saving, checking
|
||||
and fetching. You can delete by an individual ID, or you can
|
||||
and fetching. You can delete by an individual ID, or you can
|
||||
delete all entries.
|
||||
|
||||
By Cache ID
|
||||
@@ -311,9 +308,8 @@ Result Cache
|
||||
~~~~~~~~~~~~
|
||||
|
||||
The result cache can be used to cache the results of your queries
|
||||
so that we don't have to query the database or hydrate the data
|
||||
again after the first time. You just need to configure the result
|
||||
cache implementation.
|
||||
so that we don't have to query the database again after the first time.
|
||||
You just need to configure the result cache implementation.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -348,7 +344,6 @@ result cache driver.
|
||||
<?php
|
||||
$query->useResultCache(false);
|
||||
|
||||
|
||||
If you want to set the time the cache has to live you can use the
|
||||
``setResultCacheLifetime()`` method.
|
||||
|
||||
@@ -378,7 +373,7 @@ Metadata Cache
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Your class metadata can be parsed from a few different sources like
|
||||
YAML, XML, Annotations, etc. Instead of parsing this information on
|
||||
XML, Annotations, etc. Instead of parsing this information on
|
||||
each request we should cache it using one of the cache drivers.
|
||||
|
||||
Just like the query and result cache we need to configure it
|
||||
@@ -472,4 +467,3 @@ not letting your users' requests populate the cache.
|
||||
You can read more about cache slams
|
||||
`in this blog post <http://notmysock.org/blog/php/user-cache-timebomb.html>`_.
|
||||
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ follows:
|
||||
<?php
|
||||
use Doctrine\Common\NotifyPropertyChanged,
|
||||
Doctrine\Common\PropertyChangedListener;
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ChangeTrackingPolicy("NOTIFY")
|
||||
@@ -81,12 +81,12 @@ follows:
|
||||
class MyEntity implements NotifyPropertyChanged
|
||||
{
|
||||
// ...
|
||||
|
||||
private $_listeners = array();
|
||||
|
||||
|
||||
private $listeners = array();
|
||||
|
||||
public function addPropertyChangedListener(PropertyChangedListener $listener)
|
||||
{
|
||||
$this->_listeners[] = $listener;
|
||||
$this->listeners[] = $listener;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,30 +99,30 @@ behaviour:
|
||||
|
||||
<?php
|
||||
// ...
|
||||
|
||||
|
||||
class MyEntity implements NotifyPropertyChanged
|
||||
{
|
||||
// ...
|
||||
|
||||
protected function _onPropertyChanged($propName, $oldValue, $newValue)
|
||||
|
||||
protected function onPropertyChanged($propName, $oldValue, $newValue)
|
||||
{
|
||||
if ($this->_listeners) {
|
||||
foreach ($this->_listeners as $listener) {
|
||||
if ($this->listeners) {
|
||||
foreach ($this->listeners as $listener) {
|
||||
$listener->propertyChanged($this, $propName, $oldValue, $newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function setData($data)
|
||||
{
|
||||
if ($data != $this->data) {
|
||||
$this->_onPropertyChanged('data', $this->data, $data);
|
||||
$this->onPropertyChanged('data', $this->data, $data);
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
You have to invoke ``_onPropertyChanged`` inside every method that
|
||||
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
|
||||
@@ -148,4 +148,3 @@ 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.
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
Installation and Configuration
|
||||
==============================
|
||||
|
||||
Doctrine can be installed with `Composer <http://www.getcomposer.org>`_. For
|
||||
older versions we still have `PEAR packages
|
||||
<http://pear.doctrine-project.org>`_.
|
||||
Doctrine can be installed with `Composer <https://getcomposer.org>`_.
|
||||
|
||||
Define the following requirement in your ``composer.json`` file:
|
||||
|
||||
@@ -16,8 +14,7 @@ Define the following requirement in your ``composer.json`` file:
|
||||
}
|
||||
|
||||
Then call ``composer install`` from your command line. If you don't know
|
||||
how Composer works, check out their `Getting Started
|
||||
<http://getcomposer.org/doc/00-intro.md>`_ to set up.
|
||||
how Composer works, check out their `Getting Started <https://getcomposer.org/doc/00-intro.md>`_ to set up.
|
||||
|
||||
Class loading
|
||||
-------------
|
||||
@@ -70,22 +67,6 @@ Or if you prefer XML:
|
||||
$config = Setup::createXMLMetadataConfiguration($paths, $isDevMode);
|
||||
$entityManager = EntityManager::create($dbParams, $config);
|
||||
|
||||
Or if you prefer YAML:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$paths = array("/path/to/yml-mappings");
|
||||
$config = Setup::createYAMLMetadataConfiguration($paths, $isDevMode);
|
||||
$entityManager = EntityManager::create($dbParams, $config);
|
||||
|
||||
.. note::
|
||||
If you want to use yml mapping you should add yaml dependency to your `composer.json`:
|
||||
|
||||
::
|
||||
|
||||
"symfony/yaml": "*"
|
||||
|
||||
Inside the ``Setup`` methods several assumptions are made:
|
||||
|
||||
- If `$isDevMode` is true caching is done in memory with the ``ArrayCache``. Proxy objects are recreated on every request.
|
||||
@@ -93,8 +74,7 @@ Inside the ``Setup`` methods several assumptions are made:
|
||||
- If `$isDevMode` is false, set then proxy classes have to be explicitly created through the command line.
|
||||
- If third argument `$proxyDir` is not set, use the systems temporary directory.
|
||||
|
||||
If you want to configure Doctrine in more detail, take a look at the :doc:`Advanced
|
||||
Configuration <reference/advanced-configuration>` section.
|
||||
If you want to configure Doctrine in more detail, take a look at the :doc:`Advanced Configuration <reference/advanced-configuration>` section.
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Doctrine Query Language
|
||||
===========================
|
||||
=======================
|
||||
|
||||
DQL stands for Doctrine Query Language and is an Object
|
||||
Query Language derivative that is very similar to the Hibernate
|
||||
@@ -18,7 +18,6 @@ querying that storage to pick a certain subset of your objects.
|
||||
need to think about DQL as a query language for your object model,
|
||||
not for your relational schema.
|
||||
|
||||
|
||||
DQL is case in-sensitive, except for namespace, class and field
|
||||
names, which are case sensitive.
|
||||
|
||||
@@ -59,7 +58,6 @@ Here is an example that selects all users with an age > 20:
|
||||
|
||||
Lets examine the query:
|
||||
|
||||
|
||||
- ``u`` is a so called identification variable or alias that
|
||||
refers to the ``MyProject\Model\User`` class. By placing this alias
|
||||
in the SELECT clause we specify that we want all instances of the
|
||||
@@ -103,15 +101,15 @@ their inclusion in the SELECT clause.
|
||||
|
||||
In this case, the result will be an array of arrays. In the example
|
||||
above, each element of the result array would be an array of the
|
||||
scalar name and address values.
|
||||
scalar name and address values.
|
||||
|
||||
You can select scalars from any entity in the query.
|
||||
You can select scalars from any entity in the query.
|
||||
|
||||
**Mixed**
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
``SELECT u, p.quantity FROM Users u...``
|
||||
SELECT u, p.quantity FROM Users u...
|
||||
|
||||
Here, the result will again be an array of arrays, with each element
|
||||
being an array made up of a User object and the scalar value
|
||||
@@ -131,7 +129,6 @@ multiple FROM clauses.
|
||||
|
||||
Doctrine throws an exception if you violate this constraint.
|
||||
|
||||
|
||||
Joins
|
||||
~~~~~
|
||||
|
||||
@@ -180,16 +177,15 @@ not need to lazy load the association with another query.
|
||||
|
||||
Doctrine allows you to walk all the associations between
|
||||
all the objects in your domain model. Objects that were not already
|
||||
loaded from the database are replaced with lazy load proxy
|
||||
instances. Non-loaded Collections are also replaced by lazy-load
|
||||
loaded from the database are replaced with lazy loading proxy
|
||||
instances. Non-loaded Collections are also replaced by lazy-loading
|
||||
instances that fetch all the contained objects upon first access.
|
||||
However relying on the lazy-load mechanism leads to many small
|
||||
However relying on the lazy-loading mechanism leads to many small
|
||||
queries executed against the database, which can significantly
|
||||
affect the performance of your application. **Fetch Joins** are the
|
||||
solution to hydrate most or all of the entities that you need in a
|
||||
single SELECT query.
|
||||
|
||||
|
||||
Named and Positional Parameters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -208,7 +204,8 @@ This section contains a large set of DQL queries and some
|
||||
explanations of what is happening. The actual result also depends
|
||||
on the hydration mode.
|
||||
|
||||
Hydrate all User entities:
|
||||
Hydrate all User entities
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -216,7 +213,8 @@ Hydrate all User entities:
|
||||
$query = $em->createQuery('SELECT u FROM MyProject\Model\User u');
|
||||
$users = $query->getResult(); // array of User objects
|
||||
|
||||
Retrieve the IDs of all CmsUsers:
|
||||
Retrieve the IDs of all CmsUsers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -224,7 +222,8 @@ Retrieve the IDs of all CmsUsers:
|
||||
$query = $em->createQuery('SELECT u.id FROM CmsUser u');
|
||||
$ids = $query->getResult(); // array of CmsUser ids
|
||||
|
||||
Retrieve the IDs of all users that have written an article:
|
||||
Retrieve the IDs of all users that have written an article
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -232,6 +231,10 @@ Retrieve the IDs of all users that have written an article:
|
||||
$query = $em->createQuery('SELECT DISTINCT u.id FROM CmsArticle a JOIN a.user u');
|
||||
$ids = $query->getResult(); // array of CmsUser ids
|
||||
|
||||
|
||||
Retrieve all articles and sort them by username
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Retrieve all articles and sort them by the name of the articles
|
||||
users instance:
|
||||
|
||||
@@ -241,7 +244,8 @@ users instance:
|
||||
$query = $em->createQuery('SELECT a FROM CmsArticle a JOIN a.user u ORDER BY u.name ASC');
|
||||
$articles = $query->getResult(); // array of CmsArticle objects
|
||||
|
||||
Retrieve the Username and Name of a CmsUser:
|
||||
Retrieve the Username and Name of a CmsUser
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -250,7 +254,8 @@ Retrieve the Username and Name of a CmsUser:
|
||||
$users = $query->getResult(); // array of CmsUser username and name values
|
||||
echo $users[0]['username'];
|
||||
|
||||
Retrieve a ForumUser and his single associated entity:
|
||||
Retrieve a ForumUser and his single associated entity
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -259,7 +264,8 @@ Retrieve a ForumUser and his single associated entity:
|
||||
$users = $query->getResult(); // array of ForumUser objects with the avatar association loaded
|
||||
echo get_class($users[0]->getAvatar());
|
||||
|
||||
Retrieve a CmsUser and fetch join all the phonenumbers he has:
|
||||
Retrieve a CmsUser and fetch join all owning phonenumbers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -268,7 +274,8 @@ Retrieve a CmsUser and fetch join all the phonenumbers he has:
|
||||
$users = $query->getResult(); // array of CmsUser objects with the phonenumbers association loaded
|
||||
$phonenumbers = $users[0]->getPhonenumbers();
|
||||
|
||||
Hydrate a result in Ascending:
|
||||
Hydrate a result in Ascending
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -276,7 +283,8 @@ Hydrate a result in Ascending:
|
||||
$query = $em->createQuery('SELECT u FROM ForumUser u ORDER BY u.id ASC');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
Or in Descending Order:
|
||||
Hydrate a result in Descending Order
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -284,7 +292,8 @@ Or in Descending Order:
|
||||
$query = $em->createQuery('SELECT u FROM ForumUser u ORDER BY u.id DESC');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
Using Aggregate Functions:
|
||||
Using Aggregate Functions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -295,7 +304,8 @@ Using Aggregate Functions:
|
||||
$query = $em->createQuery('SELECT u, count(g.id) FROM Entities\User u JOIN u.groups g GROUP BY u.id');
|
||||
$result = $query->getResult();
|
||||
|
||||
With WHERE Clause and Positional Parameter:
|
||||
Using WHERE Clause and Positional Parameter
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -304,7 +314,8 @@ With WHERE Clause and Positional Parameter:
|
||||
$query->setParameter(1, 321);
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
With WHERE Clause and Named Parameter:
|
||||
Using WHERE Clause and Named Parameter
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -313,7 +324,8 @@ With WHERE Clause and Named Parameter:
|
||||
$query->setParameter('name', 'Bob');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
With Nested Conditions in WHERE Clause:
|
||||
Using Nested Conditions in WHERE Clause
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -326,7 +338,8 @@ With Nested Conditions in WHERE Clause:
|
||||
));
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
With COUNT DISTINCT:
|
||||
COUNT DISTINCT
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -334,7 +347,8 @@ With COUNT DISTINCT:
|
||||
$query = $em->createQuery('SELECT COUNT(DISTINCT u.name) FROM CmsUser');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
With Arithmetic Expression in WHERE clause:
|
||||
Using Arithmetic Expression in WHERE clause
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -342,6 +356,9 @@ With Arithmetic Expression in WHERE clause:
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
Hide aliased columns from the result
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Retrieve user entities with Arithmetic Expression in ORDER clause, using the ``HIDDEN`` keyword:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -350,6 +367,9 @@ Retrieve user entities with Arithmetic Expression in ORDER clause, using the ``H
|
||||
$query = $em->createQuery('SELECT u, u.posts_count + u.likes_count AS HIDDEN score FROM CmsUser u ORDER BY score');
|
||||
$users = $query->getResult(); // array of User objects
|
||||
|
||||
Select all user-ids and optionally associated article-ids
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Using a LEFT JOIN to hydrate all user-ids and optionally associated
|
||||
article-ids:
|
||||
|
||||
@@ -359,8 +379,8 @@ article-ids:
|
||||
$query = $em->createQuery('SELECT u.id, a.id as article_id FROM CmsUser u LEFT JOIN u.articles a');
|
||||
$results = $query->getResult(); // array of user ids and every article_id for each user
|
||||
|
||||
Restricting a JOIN clause by additional conditions specified by
|
||||
WITH:
|
||||
Restricting a JOIN clause by additional conditions specified by WITH
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -369,7 +389,8 @@ WITH:
|
||||
$query->setParameter('foo', '%foo%');
|
||||
$users = $query->getResult();
|
||||
|
||||
Using several Fetch JOINs:
|
||||
Using several Fetch JOINs
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -377,7 +398,8 @@ Using several Fetch JOINs:
|
||||
$query = $em->createQuery('SELECT u, a, p, c FROM CmsUser u JOIN u.articles a JOIN u.phonenumbers p JOIN a.comments c');
|
||||
$users = $query->getResult();
|
||||
|
||||
BETWEEN in WHERE clause:
|
||||
BETWEEN in WHERE clause
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -387,7 +409,8 @@ BETWEEN in WHERE clause:
|
||||
$query->setParameter(2, 321);
|
||||
$usernames = $query->getResult();
|
||||
|
||||
DQL Functions in WHERE clause:
|
||||
DQL Functions in WHERE clause
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -395,7 +418,8 @@ DQL Functions in WHERE clause:
|
||||
$query = $em->createQuery("SELECT u.name FROM CmsUser u WHERE TRIM(u.name) = 'someone'");
|
||||
$usernames = $query->getResult();
|
||||
|
||||
IN() Expression:
|
||||
IN() Expression
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -409,7 +433,8 @@ IN() Expression:
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.id NOT IN (1)');
|
||||
$users = $query->getResult();
|
||||
|
||||
CONCAT() DQL Function:
|
||||
CONCAT() DQL Function
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -423,6 +448,7 @@ CONCAT() DQL Function:
|
||||
$idUsernames = $query->getResult();
|
||||
|
||||
EXISTS in WHERE clause with correlated Subquery
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -430,7 +456,8 @@ EXISTS in WHERE clause with correlated Subquery
|
||||
$query = $em->createQuery('SELECT u.id FROM CmsUser u WHERE EXISTS (SELECT p.phonenumber FROM CmsPhonenumber p WHERE p.user = u.id)');
|
||||
$ids = $query->getResult();
|
||||
|
||||
Get all users who are members of $group.
|
||||
Get all users who are members of $group
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -440,6 +467,7 @@ Get all users who are members of $group.
|
||||
$ids = $query->getResult();
|
||||
|
||||
Get all users that have more than 1 phonenumber
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -448,6 +476,7 @@ Get all users that have more than 1 phonenumber
|
||||
$users = $query->getResult();
|
||||
|
||||
Get all users that have no phonenumber
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -455,8 +484,11 @@ Get all users that have no phonenumber
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.phonenumbers IS EMPTY');
|
||||
$users = $query->getResult();
|
||||
|
||||
Get all instances of a specific type, for use with inheritance
|
||||
hierarchies:
|
||||
Get all instances of a specific type
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Get all instances of a specific type, for use with inheritance hierarchies. These queries can be useful for
|
||||
:doc:`inheritance mapping <inheritance-mapping>`.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
@@ -467,7 +499,10 @@ hierarchies:
|
||||
$query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1');
|
||||
$query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u NOT INSTANCE OF ?1');
|
||||
|
||||
Get all users visible on a given website that have chosen certain gender:
|
||||
Using IDENTITY() in queries
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Get all users visible on a given website that have chosen certain gender.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
@@ -478,20 +513,43 @@ Get all users visible on a given website that have chosen certain gender:
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
Starting with 2.4, the IDENTITY() DQL function also works for composite primary keys:
|
||||
Starting with 2.4, the IDENTITY() DQL function also works for composite primary keys
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery("SELECT IDENTITY(c.location, 'latitude') AS latitude, IDENTITY(c.location, 'longitude') AS longitude FROM Checkpoint c WHERE c.user = ?1");
|
||||
|
||||
Arbitrary Join
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Joins between entities without associations were not possible until version
|
||||
2.4, where you can generate an arbitrary join with the following syntax:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM User u JOIN Blacklist b WITH u.email = b.email');
|
||||
$query = $em->createQuery('SELECT u FROM User u JOIN Banlist b WITH u.email = b.email');
|
||||
|
||||
With an arbitrary join the result differs from the joins using a mapped property.
|
||||
The result of an arbitrary join is an one dimensional array with a mix of the entity from the ``SELECT``
|
||||
and the joined entity fitting to the filtering of the query. In case of the example with ``User``
|
||||
and ``Banlist``, it can look like this:
|
||||
|
||||
- User
|
||||
- Banlist
|
||||
- Banlist
|
||||
- User
|
||||
- Banlist
|
||||
- User
|
||||
- Banlist
|
||||
- Banlist
|
||||
- Banlist
|
||||
|
||||
In this form of join, the ``Banlist`` entities found by the filtering in the ``WITH`` part are not fetched by an accessor
|
||||
method on ``User``, but are already part of the result. In case the accessor method for Banlists is invoked on a User instance,
|
||||
it loads all the related ``Banlist`` objects corresponding to this ``User``. This change of behaviour needs to be considered
|
||||
when the DQL is switched to an arbitrary join.
|
||||
|
||||
.. note::
|
||||
The differences between WHERE, WITH and HAVING clauses may be
|
||||
@@ -504,7 +562,6 @@ Joins between entities without associations were not possible until version
|
||||
- HAVING is applied to the results of a query after
|
||||
aggregation (GROUP BY)
|
||||
|
||||
|
||||
Partial Object Syntax
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -636,7 +693,6 @@ clause and using sub-selects.
|
||||
``EntityManager#clear()`` and retrieve new instances of any
|
||||
affected entity.
|
||||
|
||||
|
||||
DELETE queries
|
||||
--------------
|
||||
|
||||
@@ -657,7 +713,6 @@ The same restrictions apply for the reference of related entities.
|
||||
of the query. Additionally Deletes of specified entities are *NOT*
|
||||
cascaded to related entities even if specified in the metadata.
|
||||
|
||||
|
||||
Functions, Operators, Aggregates
|
||||
--------------------------------
|
||||
It is possible to wrap both fields and identification values into
|
||||
@@ -670,13 +725,12 @@ DQL Functions
|
||||
The following functions are supported in SELECT, WHERE and HAVING
|
||||
clauses:
|
||||
|
||||
|
||||
- IDENTITY(single\_association\_path\_expression [, fieldMapping]) - Retrieve the foreign key column of association of the owning side
|
||||
- ABS(arithmetic\_expression)
|
||||
- IDENTITY(single_association_path_expression [, fieldMapping]) - Retrieve the foreign key column of association of the owning side
|
||||
- ABS(arithmetic_expression)
|
||||
- CONCAT(str1, str2)
|
||||
- CURRENT\_DATE() - Return the current date
|
||||
- CURRENT\_TIME() - Returns the current time
|
||||
- CURRENT\_TIMESTAMP() - Returns a timestamp of the current date
|
||||
- CURRENT_DATE() - Return the current date
|
||||
- CURRENT_TIME() - Returns the current time
|
||||
- CURRENT_TIMESTAMP() - Returns a timestamp of the current date
|
||||
and time.
|
||||
- LENGTH(str) - Returns the length of the given string
|
||||
- LOCATE(needle, haystack [, offset]) - Locate the first
|
||||
@@ -691,8 +745,8 @@ clauses:
|
||||
- TRIM([LEADING \| TRAILING \| BOTH] ['trchar' FROM] str) - Trim
|
||||
the string by the given trim char, defaults to whitespaces.
|
||||
- UPPER(str) - Return the upper-case of the given string.
|
||||
- DATE_ADD(date, days, unit) - Add the number of days to a given date. (Supported units are DAY, MONTH)
|
||||
- DATE_SUB(date, days, unit) - Substract the number of days from a given date. (Supported units are DAY, MONTH)
|
||||
- DATE_ADD(date, days, unit) - Add the number of days to a given date. (Supported units are YEAR, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND)
|
||||
- DATE_SUB(date, days, unit) - Substract the number of days from a given date. (Supported units are YEAR, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND)
|
||||
- DATE_DIFF(date1, date2) - Calculate the difference in days between date1-date2.
|
||||
|
||||
Arithmetic operators
|
||||
@@ -716,7 +770,6 @@ Other Expressions
|
||||
DQL offers a wide-range of additional expressions that are known
|
||||
from SQL, here is a list of all the supported constructs:
|
||||
|
||||
|
||||
- ``ALL/ANY/SOME`` - Used in a WHERE clause followed by a
|
||||
sub-select this works like the equivalent constructs in SQL.
|
||||
- ``BETWEEN a AND b`` and ``NOT BETWEEN a AND b`` can be used to
|
||||
@@ -803,7 +856,7 @@ what type of results to expect.
|
||||
Single Table
|
||||
~~~~~~~~~~~~
|
||||
|
||||
`Single Table Inheritance <http://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
|
||||
`Single Table Inheritance <https://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
|
||||
is an inheritance mapping strategy where all classes of a hierarchy
|
||||
are mapped to a single database table. In order to distinguish
|
||||
which row represents which type in the hierarchy a so-called
|
||||
@@ -896,7 +949,7 @@ entities:
|
||||
Class Table Inheritance
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
`Class Table Inheritance <http://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
|
||||
`Class Table Inheritance <https://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
|
||||
is an inheritance mapping strategy where each class in a hierarchy
|
||||
is mapped to several tables: its own table and the tables of all
|
||||
parent classes. The table of a child class is linked to the table
|
||||
@@ -941,7 +994,6 @@ you'll notice some differences:
|
||||
) ENGINE = InnoDB;
|
||||
ALTER TABLE Employee ADD FOREIGN KEY (id) REFERENCES Person(id) ON DELETE CASCADE
|
||||
|
||||
|
||||
- The data is split between two tables
|
||||
- A foreign key exists between the two tables
|
||||
|
||||
@@ -957,7 +1009,6 @@ automatically for you:
|
||||
FROM Employee e1_ INNER JOIN Person p0_ ON e1_.id = p0_.id
|
||||
WHERE p0_.name = ?
|
||||
|
||||
|
||||
The Query class
|
||||
---------------
|
||||
|
||||
@@ -988,7 +1039,6 @@ 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:
|
||||
|
||||
|
||||
- ``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).
|
||||
@@ -1008,8 +1058,6 @@ the Query class. Here they are:
|
||||
graph in certain scenarios due to the difference of the identity
|
||||
semantics between arrays and objects.
|
||||
|
||||
|
||||
|
||||
- ``Query#getScalarResult()``: Retrieves a flat/rectangular result
|
||||
set of scalar values that can contain duplicate data. The
|
||||
pure/mixed distinction does not apply.
|
||||
@@ -1087,13 +1135,11 @@ clause, we get a mixed result.
|
||||
|
||||
Conventions for mixed results are as follows:
|
||||
|
||||
|
||||
- The object fetched in the FROM clause is always positioned with the key '0'.
|
||||
- Every scalar without a name is numbered in the order given in the query, starting with 1.
|
||||
- Every aliased scalar is given with its alias-name as the key. The case of the name is kept.
|
||||
- If several objects are fetched from the FROM clause they alternate every row.
|
||||
|
||||
|
||||
Here is how the result could look like:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -1133,7 +1179,6 @@ will return the rows iterating the different top-level entities.
|
||||
[2] => Object (User)
|
||||
[3] => Object (Group)
|
||||
|
||||
|
||||
Hydration Modes
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1143,11 +1188,10 @@ 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_OBJECT``
|
||||
- ``Query::HYDRATE_ARRAY``
|
||||
- ``Query::HYDRATE_SCALAR``
|
||||
- ``Query::HYDRATE_SINGLE_SCALAR``
|
||||
|
||||
Object Hydration
|
||||
^^^^^^^^^^^^^^^^
|
||||
@@ -1172,7 +1216,7 @@ why we are listing as many of the assumptions here for reference:
|
||||
- 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.
|
||||
previous object is still an unloaded proxy.
|
||||
|
||||
This list might be incomplete.
|
||||
|
||||
@@ -1211,9 +1255,8 @@ object graph you can use scalar hydration:
|
||||
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
|
||||
A query of the kind 'SELECT u.name ..' returns a key 'u_name' in
|
||||
the result rows.
|
||||
|
||||
Single Scalar Hydration
|
||||
@@ -1247,13 +1290,14 @@ creating a class which extends ``AbstractHydrator``:
|
||||
<?php
|
||||
namespace MyProject\Hydrators;
|
||||
|
||||
use Doctrine\DBAL\FetchMode;
|
||||
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
|
||||
|
||||
class CustomHydrator extends AbstractHydrator
|
||||
{
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
return $this->_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $this->stmt->fetchAll(FetchMode::Associative);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1295,7 +1339,6 @@ Prepared Statements that use numerical or named wildcards require
|
||||
additional parameters to be executable against the database. To
|
||||
pass parameters to the query the following methods can be used:
|
||||
|
||||
|
||||
- ``AbstractQuery::setParameter($param, $value)`` - Set the
|
||||
numerical or named wildcard to the given value.
|
||||
- ``AbstractQuery::setParameters(array $params)`` - Set an array
|
||||
@@ -1350,7 +1393,6 @@ Result Cache API:
|
||||
``Doctrine\ORM\Configuration`` instance so that it is passed to
|
||||
every ``Query`` and ``NativeQuery`` instance.
|
||||
|
||||
|
||||
Query Hints
|
||||
^^^^^^^^^^^
|
||||
|
||||
@@ -1360,22 +1402,21 @@ exist mostly internal query hints that are not be consumed in
|
||||
userland. However the following few hints are to be used in
|
||||
userland:
|
||||
|
||||
|
||||
- Query::HINT\_FORCE\_PARTIAL\_LOAD - Allows to hydrate objects
|
||||
- ``Query::HINT_FORCE_PARTIAL_LOAD`` - Allows to hydrate objects
|
||||
although not all their columns are fetched. This query hint can be
|
||||
used to handle memory consumption problems with large result-sets
|
||||
that contain char or binary data. Doctrine has no way of implicitly
|
||||
reloading this data. Partially loaded objects have to be passed to
|
||||
``EntityManager::refresh()`` if they are to be reloaded fully from
|
||||
the database.
|
||||
- Query::HINT\_REFRESH - This query is used internally by
|
||||
- ``Query::HINT_REFRESH`` - This query is used internally by
|
||||
``EntityManager::refresh()`` and can be used in userland as well.
|
||||
If you specify this hint and a query returns the data for an entity
|
||||
that is already managed by the UnitOfWork, the fields of the
|
||||
existing entity will be refreshed. In normal operation a result-set
|
||||
that loads data of an already existing entity is discarded in favor
|
||||
of the already existing entity.
|
||||
- Query::HINT\_CUSTOM\_TREE\_WALKERS - An array of additional
|
||||
- ``Query::HINT_CUSTOM_TREE_WALKERS`` - An array of additional
|
||||
``Doctrine\ORM\Query\TreeWalker`` instances that are attached to
|
||||
the DQL query parsing process.
|
||||
|
||||
@@ -1396,7 +1437,6 @@ default. This also means you don't regularly need to fiddle with
|
||||
the parameters of the Query Cache, however if you do there are
|
||||
several methods to interact with it:
|
||||
|
||||
|
||||
- ``Query::setQueryCacheDriver($driver)`` - Allows to set a Cache
|
||||
instance
|
||||
- ``Query::setQueryCacheLifeTime($seconds = 3600)`` - Set lifetime
|
||||
@@ -1415,7 +1455,6 @@ well as specify the starting offset, Doctrine then uses a strategy
|
||||
of manipulating the select query to return only the requested
|
||||
number of results:
|
||||
|
||||
|
||||
- ``Query::setMaxResults($maxResults)``
|
||||
- ``Query::setFirstResult($offset)``
|
||||
|
||||
@@ -1441,7 +1480,7 @@ can mark a many-to-one or one-to-one association as fetched temporarily to batch
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery("SELECT u FROM MyProject\User u");
|
||||
$query->setFetchMode("MyProject\User", "address", \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER);
|
||||
$query->setFetchMode("MyProject\User", "address", \Doctrine\ORM\Mapping\FetchMode::EAGER);
|
||||
$query->execute();
|
||||
|
||||
Given that there are 10 users and corresponding addresses in the database the executed queries will look something like:
|
||||
@@ -1452,15 +1491,14 @@ Given that there are 10 users and corresponding addresses in the database the ex
|
||||
SELECT * FROM address WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
|
||||
.. note::
|
||||
Changing the fetch mode during a query mostly makes sense for one-to-one and many-to-one relations. In that case,
|
||||
Changing the fetch mode during a query mostly makes sense for one-to-one and many-to-one relations. In that case,
|
||||
all the necessary IDs are available after the root entity (``user`` in the above example) has been loaded. So, one
|
||||
query per association can be executed to fetch all the referred-to entities (``address``).
|
||||
|
||||
|
||||
For one-to-many relations, changing the fetch mode to eager will cause to execute one query **for every root entity
|
||||
loaded**. This gives no improvement over the ``lazy`` fetch mode which will also initialize the associations on
|
||||
a one-by-one basis once they are accessed.
|
||||
|
||||
|
||||
EBNF
|
||||
----
|
||||
|
||||
@@ -1472,7 +1510,6 @@ correct syntax for a particular query should be.
|
||||
Document syntax:
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- non-terminals begin with an upper case character
|
||||
- terminals begin with a lower case character
|
||||
- parentheses (...) are used for grouping
|
||||
@@ -1486,10 +1523,8 @@ Document syntax:
|
||||
Terminals
|
||||
~~~~~~~~~
|
||||
|
||||
|
||||
- identifier (name, email, ...) must match ``[a-z_][a-z0-9_]*``
|
||||
- fully_qualified_name (Doctrine\Tests\Models\CMS\CmsUser) matches PHP's fully qualified class names
|
||||
- aliased_name (CMS:CmsUser) uses two identifiers, one for the namespace alias and one for the class inside it
|
||||
- string ('foo', 'bar''s house', '%ninja%', ...)
|
||||
- char ('/', '\\', ' ', ...)
|
||||
- integer (-1, 0, 1, 34, ...)
|
||||
@@ -1524,7 +1559,7 @@ Identifiers
|
||||
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
|
||||
AbstractSchemaName ::= fully_qualified_name | identifier
|
||||
|
||||
/* Alias ResultVariable declaration (the "total" of "COUNT(*) AS total") */
|
||||
AliasResultVariable = identifier
|
||||
@@ -1640,7 +1675,6 @@ Conditional Expressions
|
||||
EmptyCollectionComparisonExpression | CollectionMemberExpression |
|
||||
InstanceOfExpression
|
||||
|
||||
|
||||
Collection Expressions
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1730,7 +1764,7 @@ QUANTIFIED/BETWEEN/COMPARISON/LIKE/NULL/EXISTS
|
||||
QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")"
|
||||
BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression
|
||||
ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression )
|
||||
InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
|
||||
InExpression ::= ArithmeticExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
|
||||
InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
|
||||
InstanceOfParameter ::= AbstractSchemaName | InputParameter
|
||||
LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char]
|
||||
@@ -1771,4 +1805,3 @@ Functions
|
||||
"UPPER" "(" StringPrimary ")" |
|
||||
"IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")"
|
||||
|
||||
|
||||
|
||||
@@ -129,7 +129,6 @@ with camelcase and the value of the corresponding constant should
|
||||
be the name of the constant itself, even with spelling. This has
|
||||
several reasons:
|
||||
|
||||
|
||||
- It is easy to read.
|
||||
- Simplicity.
|
||||
- Each method within an EventSubscriber is named after the
|
||||
@@ -148,7 +147,6 @@ Lifecycle Events
|
||||
The EntityManager and UnitOfWork trigger a bunch of events during
|
||||
the life-time of their registered entities.
|
||||
|
||||
|
||||
- preRemove - The preRemove event occurs for a given entity before
|
||||
the respective EntityManager remove operation for that entity is
|
||||
executed. It is not called for a DQL DELETE statement.
|
||||
@@ -173,7 +171,7 @@ the life-time of their registered entities.
|
||||
database or after the refresh operation has been applied to it.
|
||||
- loadClassMetadata - The loadClassMetadata event occurs after the
|
||||
mapping metadata for a class has been loaded from a mapping source
|
||||
(annotations/xml/yaml). This event is not a lifecycle callback.
|
||||
(annotations/xml). This event is not a lifecycle callback.
|
||||
- onClassMetadataNotFound - Loading class metadata for a particular
|
||||
requested class name failed. Manipulating the given event args instance
|
||||
allows providing fallback metadata even when no actual metadata exists
|
||||
@@ -233,7 +231,6 @@ EntityManager and other relevant data.
|
||||
:ref:`reference-events-implementing-listeners` section very carefully
|
||||
to understand which operations are allowed in which lifecycle event.
|
||||
|
||||
|
||||
Lifecycle Callbacks
|
||||
-------------------
|
||||
|
||||
@@ -295,26 +292,8 @@ Note that the methods set as lifecycle callbacks need to be public and,
|
||||
when using these annotations, you have to apply the
|
||||
``@HasLifecycleCallbacks`` marker annotation on the entity class.
|
||||
|
||||
If you want to register lifecycle callbacks from YAML or XML you
|
||||
can do it with the following.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
fields:
|
||||
# ...
|
||||
name:
|
||||
type: string(50)
|
||||
lifecycleCallbacks:
|
||||
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ]
|
||||
postPersist: [ doStuffOnPostPersist ]
|
||||
|
||||
In YAML the ``key`` of the lifecycleCallbacks entry is the event that you
|
||||
are triggering on and the value is the method (or methods) to call. The allowed
|
||||
event types are the ones listed in the previous Lifecycle Events section.
|
||||
|
||||
XML would look something like this:
|
||||
If you want to register lifecycle callbacks from XML it would look
|
||||
something like this:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
@@ -323,7 +302,7 @@ XML would look something like this:
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
/Users/robo/dev/php/Doctrine/doctrine-mapping.xsd">
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="User">
|
||||
|
||||
@@ -340,7 +319,7 @@ In XML the ``type`` of the lifecycle-callback entry is the event that you
|
||||
are triggering on and the ``method`` is the method to call. The allowed event
|
||||
types are the ones listed in the previous Lifecycle Events section.
|
||||
|
||||
When using YAML or XML you need to remember to create public methods to match the
|
||||
When using XML you need to remember to create public methods to match the
|
||||
callback names you defined. E.g. in these examples ``doStuffOnPrePersist()``,
|
||||
``doOtherStuffOnPrePersist()`` and ``doStuffOnPostPersist()`` methods need to be
|
||||
defined on your ``User`` model.
|
||||
@@ -370,9 +349,8 @@ defined on your ``User`` model.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Lifecycle Callbacks Event Argument
|
||||
-----------------------------------
|
||||
----------------------------------
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
@@ -525,7 +503,6 @@ which has access to the entity and the entity manager.
|
||||
|
||||
The following restrictions apply to ``prePersist``:
|
||||
|
||||
|
||||
- If you are using a PrePersist Identity Generator such as
|
||||
sequences the ID value will *NOT* be available within any
|
||||
PrePersist events.
|
||||
@@ -573,7 +550,6 @@ OnFlush is a very powerful event. It is called inside
|
||||
entities and their associations have been computed. This means, the
|
||||
``onFlush`` event has access to the sets of:
|
||||
|
||||
|
||||
- Entities scheduled for insert
|
||||
- Entities scheduled for update
|
||||
- Entities scheduled for removal
|
||||
@@ -618,7 +594,6 @@ mentioned sets. See this example:
|
||||
|
||||
The following restrictions apply to the onFlush event:
|
||||
|
||||
|
||||
- If you create and persist a new entity in ``onFlush``, then
|
||||
calling ``EntityManager#persist()`` is not enough.
|
||||
You have to execute an additional call to
|
||||
@@ -667,7 +642,6 @@ This means you have access to all the fields that have changed for
|
||||
this entity with their old and new value. The following methods are
|
||||
available on the ``PreUpdateEventArgs``:
|
||||
|
||||
|
||||
- ``getEntity()`` to get access to the actual entity.
|
||||
- ``getEntityChangeSet()`` to get a copy of the changeset array.
|
||||
Changes to this returned array do not affect updating.
|
||||
@@ -721,7 +695,6 @@ lifecycle callback when there are expensive validations to call:
|
||||
|
||||
Restrictions for this event:
|
||||
|
||||
|
||||
- Changes to associations of the passed entities are not
|
||||
recognized by the flush operation anymore.
|
||||
- Changes to fields of the passed entities are not recognized by
|
||||
@@ -780,13 +753,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:
|
||||
|
||||
@@ -869,32 +835,13 @@ 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::
|
||||
|
||||
The order of execution of multiple methods for the same event (e.g. multiple @PrePersist) is not guaranteed.
|
||||
|
||||
|
||||
Entity listeners resolver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Doctrine invokes the listener resolver to get the listener instance.
|
||||
|
||||
- A resolver allows you register a specific entity listener instance.
|
||||
@@ -960,18 +907,17 @@ Load ClassMetadata Event
|
||||
------------------------
|
||||
|
||||
When the mapping information for an entity is read, it is populated
|
||||
in to a ``ClassMetadataInfo`` instance. You can hook in to this
|
||||
in to a ``Doctrine\ORM\Mapping\ClassMetadata`` instance. You can hook in to this
|
||||
process and manipulate the instance.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$test = new TestEvent();
|
||||
$metadataFactory = $em->getMetadataFactory();
|
||||
$test = new TestEventListener();
|
||||
$evm = $em->getEventManager();
|
||||
$evm->addEventListener(Events::loadClassMetadata, $test);
|
||||
$evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $test);
|
||||
|
||||
class TestEvent
|
||||
class TestEventListener
|
||||
{
|
||||
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
|
||||
{
|
||||
@@ -985,4 +931,55 @@ process and manipulate the instance.
|
||||
}
|
||||
}
|
||||
|
||||
SchemaTool Events
|
||||
-----------------
|
||||
|
||||
It is possible to access the schema metadata during schema changes that are happening in ``Doctrine\ORM\Tools\SchemaTool``.
|
||||
There are two different events where you can hook in.
|
||||
|
||||
postGenerateSchemaTable
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This event is fired for each ``Doctrine\DBAL\Schema\Table`` instance, after one was created and built up with the current class metadata
|
||||
of an entity. It is possible to access to the current state of ``Doctrine\DBAL\Schema\Schema``, the current table schema
|
||||
instance and class metadata.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$test = new TestEventListener();
|
||||
$evm = $em->getEventManager();
|
||||
$evm->addEventListener(\Doctrine\ORM\Tools\ToolEvents::postGenerateSchemaTable, $test);
|
||||
|
||||
class TestEventListener
|
||||
{
|
||||
public function postGenerateSchemaTable(\Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs $eventArgs)
|
||||
{
|
||||
$classMetadata = $eventArgs->getClassMetadata();
|
||||
$schema = $eventArgs->getSchema();
|
||||
$table = $eventArgs->getClassTable();
|
||||
}
|
||||
}
|
||||
|
||||
postGenerateSchema
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This event is fired after the schema instance was successfully built and before SQL queries are generated from the
|
||||
schema information of ``Doctrine\DBAL\Schema\Schema``. It allows to access the full object representation of the database schema
|
||||
and the EntityManager.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$test = new TestEventListener();
|
||||
$evm = $em->getEventManager();
|
||||
$evm->addEventListener(\Doctrine\ORM\Tools\ToolEvents::postGenerateSchema, $test);
|
||||
|
||||
class TestEventListener
|
||||
{
|
||||
public function postGenerateSchema(\Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs $eventArgs)
|
||||
{
|
||||
$schema = $eventArgs->getSchema();
|
||||
$em = $eventArgs->getEntityManager();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ Database Schema
|
||||
How do I set the charset and collation for MySQL tables?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can't set these values inside the annotations, yml or xml mapping files. To make a database
|
||||
You can't set these values inside the annotations or xml mapping files. To make a database
|
||||
work with the default charset and collation you should configure MySQL to use it as default charset,
|
||||
or create the database with charset and collation details. This way they get inherited to all newly
|
||||
created database tables and columns.
|
||||
@@ -21,12 +21,6 @@ created database tables and columns.
|
||||
Entity Classes
|
||||
--------------
|
||||
|
||||
I access a variable and its null, what is wrong?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If this variable is a public variable then you are violating one of the criteria for entities.
|
||||
All properties have to be protected or private for the proxy object pattern to work.
|
||||
|
||||
How can I add default values to a column?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -58,7 +52,7 @@ or adding entities to a collection twice. You have to check for both conditions
|
||||
in the code before calling ``$em->flush()`` if you know that unique constraint failures
|
||||
can occur.
|
||||
|
||||
In `Symfony2 <http://www.symfony.com>`_ for example there is a Unique Entity Validator
|
||||
In `Symfony2 <https://www.symfony.com>`_ for example there is a Unique Entity Validator
|
||||
to achieve this task.
|
||||
|
||||
For collections you can check with ``$collection->contains($entity)`` if an entity is already
|
||||
@@ -108,7 +102,6 @@ foreign keys as primary keys feature of Doctrine introduced in version 2.1.
|
||||
|
||||
See :doc:`the tutorial on composite primary keys for more information<../tutorials/composite-primary-keys>`.
|
||||
|
||||
|
||||
How can i paginate fetch-joined collections?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -118,8 +111,8 @@ over this collection using a LIMIT statement (or vendor equivalent).
|
||||
Doctrine does not offer a solution for this out of the box but there are several extensions
|
||||
that do:
|
||||
|
||||
* `DoctrineExtensions <http://github.com/beberlei/DoctrineExtensions>`_
|
||||
* `Pagerfanta <http://github.com/whiteoctober/pagerfanta>`_
|
||||
* `DoctrineExtensions <https://github.com/beberlei/DoctrineExtensions>`_
|
||||
* `Pagerfanta <https://github.com/whiteoctober/pagerfanta>`_
|
||||
|
||||
Why does pagination not work correctly with fetch joins?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -136,7 +129,7 @@ Inheritance
|
||||
|
||||
Can I use Inheritance with Doctrine 2?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
Yes, you can use Single- or Joined-Table Inheritance in Doctrine 2.
|
||||
|
||||
See the documentation chapter on :doc:`inheritance mapping <inheritance-mapping>` for
|
||||
@@ -149,23 +142,6 @@ If you set a many-to-one or one-to-one association target-entity to any parent c
|
||||
an inheritance hierarchy Doctrine does not know what PHP class the foreign is actually of.
|
||||
To find this out it has to execute a SQL query to look this information up in the database.
|
||||
|
||||
EntityGenerator
|
||||
---------------
|
||||
|
||||
Why does the EntityGenerator not do X?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The EntityGenerator is not a full fledged code-generator that solves all tasks. Code-Generation
|
||||
is not a first-class priority in Doctrine 2 anymore (compared to Doctrine 1). The EntityGenerator
|
||||
is supposed to kick-start you, but not towards 100%.
|
||||
|
||||
Why does the EntityGenerator not generate inheritance correctly?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Just from the details of the discriminator map the EntityGenerator cannot guess the inheritance hierarchy.
|
||||
This is why the generation of inherited entities does not fully work. You have to adjust some additional
|
||||
code to get this one working correctly.
|
||||
|
||||
Performance
|
||||
-----------
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ By adding SQL to the conditional clauses of queries, the filter system filters
|
||||
out rows belonging to the entities at the level of the SQL result set. This
|
||||
means that the filtered entities are never hydrated (which can be expensive).
|
||||
|
||||
|
||||
Example filter class
|
||||
--------------------
|
||||
Throughout this document the example ``MyLocaleFilter`` class will be used to
|
||||
@@ -47,7 +46,7 @@ proper quoting of parameters.
|
||||
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
|
||||
{
|
||||
// Check if the entity implements the LocalAware interface
|
||||
if (!$targetEntity->reflClass->implementsInterface('LocaleAware')) {
|
||||
if (!$targetEntity->getReflectionClass()->implementsInterface('LocaleAware')) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -55,7 +54,6 @@ proper quoting of parameters.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
Filter classes are added to the configuration as following:
|
||||
@@ -65,11 +63,9 @@ Filter classes are added to the configuration as following:
|
||||
<?php
|
||||
$config->addFilter("locale", "\Doctrine\Tests\ORM\Functional\MyLocaleFilter");
|
||||
|
||||
|
||||
The ``Configuration#addFilter()`` method takes a name for the filter and the name of the
|
||||
class responsible for the actual filtering.
|
||||
|
||||
|
||||
Disabling/Enabling Filters and Setting Parameters
|
||||
---------------------------------------------------
|
||||
Filters can be disabled and enabled via the ``FilterCollection`` which is
|
||||
|
||||
@@ -11,9 +11,8 @@ request and can greatly improve performance.
|
||||
"If you care about performance and don't use a bytecode
|
||||
cache then you don't really care about performance. Please get one
|
||||
and start using it."
|
||||
|
||||
*Stas Malyshev, Core Contributor to PHP and Zend Employee*
|
||||
|
||||
*Stas Malyshev, Core Contributor to PHP and Zend Employee*
|
||||
|
||||
Metadata and Query caches
|
||||
-------------------------
|
||||
@@ -52,7 +51,7 @@ for more information on how this fetch mode works.
|
||||
Temporarily change fetch mode in DQL
|
||||
------------------------------------
|
||||
|
||||
See :ref:`Doctrine Query Language chapter <dql-temporarily-change-fetch-mode>`
|
||||
See :ref:`dql-temporarily-change-fetch-mode`
|
||||
|
||||
|
||||
Apply Best Practices
|
||||
@@ -61,8 +60,7 @@ Apply Best Practices
|
||||
A lot of the points mentioned in the Best Practices chapter will
|
||||
also positively affect the performance of Doctrine.
|
||||
|
||||
|
||||
Change Tracking policies
|
||||
------------------------
|
||||
|
||||
See: :doc:`Change Tracking Policies <reference/change-tracking-policies>`
|
||||
See: :doc:`Change Tracking Policies <change-tracking-policies>`
|
||||
|
||||
@@ -25,36 +25,44 @@ appear in the middle of an otherwise mapped inheritance hierarchy
|
||||
For further support of inheritance, the single or
|
||||
joined table inheritance features have to be used.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @MappedSuperclass */
|
||||
class MappedSuperclassBase
|
||||
class Person
|
||||
{
|
||||
/** @Column(type="integer") */
|
||||
protected $mapped1;
|
||||
/** @Column(type="string") */
|
||||
protected $mapped2;
|
||||
/**
|
||||
* @OneToOne(targetEntity="MappedSuperclassRelated1")
|
||||
* @JoinColumn(name="related1_id", referencedColumnName="id")
|
||||
* @OneToOne(targetEntity="Toothbrush")
|
||||
* @JoinColumn(name="toothbrush_id", referencedColumnName="id")
|
||||
*/
|
||||
protected $mappedRelated1;
|
||||
|
||||
protected $toothbrush;
|
||||
|
||||
// ... more fields and methods
|
||||
}
|
||||
|
||||
|
||||
/** @Entity */
|
||||
class EntitySubClass extends MappedSuperclassBase
|
||||
class Employee extends Person
|
||||
{
|
||||
/** @Id @Column(type="integer") */
|
||||
private $id;
|
||||
/** @Column(type="string") */
|
||||
private $name;
|
||||
|
||||
|
||||
// ... more fields and methods
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class Toothbrush
|
||||
{
|
||||
/** @Id @Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
// ... more fields and methods
|
||||
}
|
||||
|
||||
@@ -73,7 +81,7 @@ defined on that class directly.
|
||||
Single Table Inheritance
|
||||
------------------------
|
||||
|
||||
`Single Table Inheritance <http://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
|
||||
`Single Table Inheritance <https://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
|
||||
is an inheritance mapping strategy where all classes of a hierarchy
|
||||
are mapped to a single database table. In order to distinguish
|
||||
which row represents which type in the hierarchy a so-called
|
||||
@@ -84,10 +92,10 @@ Example:
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
|
||||
<?php
|
||||
namespace MyProject\Model;
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("SINGLE_TABLE")
|
||||
@@ -98,7 +106,7 @@ Example:
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
@@ -107,25 +115,9 @@ Example:
|
||||
// ...
|
||||
}
|
||||
|
||||
.. 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
|
||||
|
||||
Things to note:
|
||||
|
||||
|
||||
- The @InheritanceType and @DiscriminatorColumn must be specified
|
||||
- The @InheritanceType and @DiscriminatorColumn must be specified
|
||||
on the topmost class that is part of the mapped entity hierarchy.
|
||||
- The @DiscriminatorMap specifies which values of the
|
||||
discriminator column identify a row as being of a certain type. In
|
||||
@@ -139,9 +131,7 @@ Things to note:
|
||||
be fully qualified if the classes are contained in the same
|
||||
namespace as the entity class on which the discriminator map is
|
||||
applied.
|
||||
- If no discriminator map is provided, then the map is generated
|
||||
automatically. The automatically generated discriminator map
|
||||
contains the lowercase short name of each class as key.
|
||||
- If no discriminator map is provided, an exception will be thrown.
|
||||
|
||||
Design-time considerations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -162,9 +152,9 @@ relationships involving types that employ this mapping strategy are
|
||||
very performing.
|
||||
|
||||
There is a general performance consideration with Single Table
|
||||
Inheritance: If the target-entity of a many-to-one or one-to-one
|
||||
association is an STI entity, it is preferable for performance reasons that it
|
||||
be a leaf entity in the inheritance hierarchy, (ie. have no subclasses).
|
||||
Inheritance: If the target-entity of a many-to-one or one-to-one
|
||||
association is an STI entity, it is preferable for performance reasons that it
|
||||
be a leaf entity in the inheritance hierarchy, (ie. have no subclasses).
|
||||
Otherwise Doctrine *CANNOT* create proxy instances
|
||||
of this entity and will *ALWAYS* load the entity eagerly.
|
||||
|
||||
@@ -174,14 +164,14 @@ SQL Schema considerations
|
||||
For Single-Table-Inheritance to work in scenarios where you are
|
||||
using either a legacy database schema or a self-written database
|
||||
schema you have to make sure that all columns that are not in the
|
||||
root entity but in any of the different sub-entities has to allows
|
||||
root entity but in any of the different sub-entities has to allow
|
||||
null values. Columns that have NOT NULL constraints have to be on
|
||||
the root entity of the single-table inheritance hierarchy.
|
||||
|
||||
Class Table Inheritance
|
||||
-----------------------
|
||||
|
||||
`Class Table Inheritance <http://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
|
||||
`Class Table Inheritance <https://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
|
||||
is an inheritance mapping strategy where each class in a hierarchy
|
||||
is mapped to several tables: its own table and the tables of all
|
||||
parent classes. The table of a child class is linked to the table
|
||||
@@ -196,7 +186,7 @@ Example:
|
||||
|
||||
<?php
|
||||
namespace MyProject\Model;
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("JOINED")
|
||||
@@ -207,7 +197,7 @@ Example:
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
/** @Entity */
|
||||
class Employee extends Person
|
||||
{
|
||||
@@ -216,7 +206,6 @@ Example:
|
||||
|
||||
Things to note:
|
||||
|
||||
|
||||
- The @InheritanceType, @DiscriminatorColumn and @DiscriminatorMap
|
||||
must be specified on the topmost class that is part of the mapped
|
||||
entity hierarchy.
|
||||
@@ -229,9 +218,7 @@ Things to note:
|
||||
be fully qualified if the classes are contained in the same
|
||||
namespace as the entity class on which the discriminator map is
|
||||
applied.
|
||||
- If no discriminator map is provided, then the map is generated
|
||||
automatically. The automatically generated discriminator map
|
||||
contains the lowercase short name of each class as key.
|
||||
- If no discriminator map is provided, an exception will be thrown.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -241,7 +228,6 @@ Things to note:
|
||||
``ON DELETE CASCADE`` in all database implementations. A failure to
|
||||
implement this yourself will lead to dead rows in the database.
|
||||
|
||||
|
||||
Design-time considerations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -268,9 +254,9 @@ themselves on access of any subtype fields, so accessing fields of
|
||||
subtypes after such a query is not safe.
|
||||
|
||||
There is a general performance consideration with Class Table
|
||||
Inheritance: If the target-entity of a many-to-one or one-to-one
|
||||
association is a CTI entity, it is preferable for performance reasons that it
|
||||
be a leaf entity in the inheritance hierarchy, (ie. have no subclasses).
|
||||
Inheritance: If the target-entity of a many-to-one or one-to-one
|
||||
association is a CTI entity, it is preferable for performance reasons that it
|
||||
be a leaf entity in the inheritance hierarchy, (ie. have no subclasses).
|
||||
Otherwise Doctrine *CANNOT* create proxy instances
|
||||
of this entity and will *ALWAYS* load the entity eagerly.
|
||||
|
||||
@@ -293,7 +279,6 @@ Used to override a mapping for an entity field or relationship.
|
||||
May be applied to an entity that extends a mapped superclass
|
||||
to override a relationship or field mapping defined by the mapped superclass.
|
||||
|
||||
|
||||
Association Override
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
Override a mapping for an entity relationship.
|
||||
@@ -315,7 +300,7 @@ Example:
|
||||
*/
|
||||
class User
|
||||
{
|
||||
//other fields mapping
|
||||
// other fields mapping
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity="Group", inversedBy="users")
|
||||
@@ -365,8 +350,7 @@ Example:
|
||||
<many-to-many field="groups" target-entity="Group" inversed-by="users">
|
||||
<cascade>
|
||||
<cascade-persist/>
|
||||
<cascade-merge/>
|
||||
<cascade-detach/>
|
||||
<cascade-refresh/>
|
||||
</cascade>
|
||||
<join-table name="users_groups">
|
||||
<join-columns>
|
||||
@@ -402,51 +386,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:
|
||||
|
||||
@@ -454,7 +393,7 @@ Things to note:
|
||||
- This feature is available for all kind of associations. (OneToOne, OneToMany, ManyToOne, ManyToMany)
|
||||
- The association type *CANNOT* be changed.
|
||||
- The override could redefine the joinTables or joinColumns depending on the association type.
|
||||
- The override could redefine inversedBy to reference more than one extended entity.
|
||||
- The override could redefine ``inversedBy`` to reference more than one extended entity.
|
||||
- The override could redefine fetch to modify the fetch strategy of the extended entity.
|
||||
|
||||
Attribute Override
|
||||
@@ -493,7 +432,7 @@ Could be used by an entity that extends a mapped superclass to override a field
|
||||
* column=@Column(
|
||||
* name = "guest_id",
|
||||
* type = "integer",
|
||||
length = 140
|
||||
* length = 140
|
||||
* )
|
||||
* ),
|
||||
* @AttributeOverride(name="name",
|
||||
@@ -501,7 +440,7 @@ Could be used by an entity that extends a mapped superclass to override a field
|
||||
* name = "guest_name",
|
||||
* nullable = false,
|
||||
* unique = true,
|
||||
length = 240
|
||||
* length = 240
|
||||
* )
|
||||
* )
|
||||
* })
|
||||
@@ -522,7 +461,7 @@ 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-refresh/>
|
||||
</cascade>
|
||||
<join-column name="address_id" referenced-column-name="id"/>
|
||||
</many-to-one>
|
||||
@@ -543,42 +482,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,5 +1,4 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
The installation chapter has moved to `Installation and Configuration
|
||||
<reference/configuration>`_.
|
||||
The installation chapter has moved to :doc:`Installation and Configuration <reference/configuration>`_.
|
||||
|
||||
@@ -39,7 +39,7 @@ possible either. See the following example:
|
||||
name VARCHAR,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE product_attributes (
|
||||
product_id INTEGER,
|
||||
attribute_name VARCHAR,
|
||||
@@ -63,16 +63,7 @@ Where the ``attribute_name`` column contains the key and
|
||||
``$attributes``.
|
||||
|
||||
The feature request for persistence of primitive value arrays
|
||||
`is described in the DDC-298 ticket <http://www.doctrine-project.org/jira/browse/DDC-298>`_.
|
||||
|
||||
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 <http://www.doctrine-project.org/jira/browse/DDC-875>`_ Merge can sometimes add the same entity twice into a collection
|
||||
- `DDC-763 <http://www.doctrine-project.org/jira/browse/DDC-763>`_ Cascade merge on associated entities can insert too many rows through "Persistence by Reachability"
|
||||
`is described in the DDC-298 ticket <https://github.com/doctrine/orm/issues/3743>`_.
|
||||
|
||||
Custom Persisters
|
||||
~~~~~~~~~~~~~~~~~
|
||||
@@ -83,8 +74,8 @@ Currently there is no way to overwrite the persister implementation
|
||||
for a given entity, however there are several use-cases that can
|
||||
benefit from custom persister implementations:
|
||||
|
||||
- `Add Upsert Support <http://www.doctrine-project.org/jira/browse/DDC-668>`_
|
||||
- `Evaluate possible ways in which stored-procedures can be used <http://www.doctrine-project.org/jira/browse/DDC-445>`_
|
||||
- `Add Upsert Support <https://github.com/doctrine/orm/issues/5178>`_
|
||||
- `Evaluate possible ways in which stored-procedures can be used <https://github.com/doctrine/orm/issues/4946>`_
|
||||
|
||||
Persist Keys of Collections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -94,7 +85,7 @@ PHP Arrays are ordered hash-maps and so should be the
|
||||
evaluate a feature that optionally persists and hydrates the keys
|
||||
of a Collection instance.
|
||||
|
||||
`Ticket DDC-213 <http://www.doctrine-project.org/jira/browse/DDC-213>`_
|
||||
`Ticket DDC-213 <https://github.com/doctrine/orm/issues/2817>`_
|
||||
|
||||
Mapping many tables to one entity
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -116,7 +107,6 @@ blog posts we have written on this topics:
|
||||
- `A re-usable Versionable behavior for Doctrine2 <http://www.doctrine-project.org/2010/02/24/doctrine2-versionable.html>`_
|
||||
- `Write your own ORM on top of Doctrine2 <http://www.doctrine-project.org/2010/07/19/your-own-orm-doctrine2.html>`_
|
||||
- `Doctrine 2 Behavioral Extensions <http://www.doctrine-project.org/2010/11/18/doctrine2-behavioral-extensions.html>`_
|
||||
- `Doctrator <https://github.com/pablodip/doctrator`>_
|
||||
|
||||
Doctrine 2 has enough hooks and extension points so that **you** can
|
||||
add whatever you want on top of it. None of this will ever become
|
||||
@@ -131,9 +121,8 @@ included in the core of Doctrine 2. However there are already two
|
||||
extensions out there that offer support for Nested Set with
|
||||
Doctrine 2:
|
||||
|
||||
|
||||
- `Doctrine2 Hierarchical-Structural Behavior <http://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior>`_
|
||||
- `Doctrine2 NestedSet <http://github.com/blt04/doctrine2-nestedset>`_
|
||||
- `Doctrine2 Hierarchical-Structural Behavior <https://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior>`_
|
||||
- `Doctrine2 NestedSet <https://github.com/blt04/doctrine2-nestedset>`_
|
||||
|
||||
Known Issues
|
||||
------------
|
||||
@@ -144,9 +133,7 @@ backwards compatibility issues or where no simple fix exists (yet).
|
||||
We don't plan to add every bug in the tracker there, just those
|
||||
issues that can potentially cause nightmares or pain of any sort.
|
||||
|
||||
See the Open Bugs on Jira for more details on `bugs, improvement and feature
|
||||
requests
|
||||
<http://www.doctrine-project.org/jira/secure/IssueNavigator.jspa?reset=true&mode=hide&pid=10032&resolution=-1&sorter/field=updated&sorter/order=DESC>`_.
|
||||
See bugs, improvement and feature requests on `Github issues <https://github.com/doctrine/orm/issues>`_.
|
||||
|
||||
Identifier Quoting and Legacy Databases
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -156,7 +143,6 @@ edge case problems Doctrine 2 does **NOT** do automatic identifier
|
||||
quoting. This can lead to problems when trying to get
|
||||
legacy-databases to work with Doctrine 2.
|
||||
|
||||
|
||||
- You can quote column-names as described in the
|
||||
:doc:`Basic-Mapping <basic-mapping>` section.
|
||||
- You cannot quote join column names.
|
||||
@@ -180,27 +166,3 @@ MySQL with MyISAM tables
|
||||
Doctrine cannot provide atomic operations when calling ``EntityManager#flush()`` if one
|
||||
of the tables involved uses the storage engine MyISAM. You must use InnoDB or
|
||||
other storage engines that support transactions if you need integrity.
|
||||
|
||||
Entities, Proxies and Reflection
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Using methods for Reflection on entities can be prone to error, when the entity
|
||||
is actually a proxy the following methods will not work correctly:
|
||||
|
||||
- ``new ReflectionClass``
|
||||
- ``new ReflectionObject``
|
||||
- ``get_class()``
|
||||
- ``get_parent_class()``
|
||||
|
||||
This is why ``Doctrine\Common\Util\ClassUtils`` class exists that has similar
|
||||
methods, which resolve the proxy problem beforehand.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
|
||||
$bookProxy = $entityManager->getReference('Acme\Book');
|
||||
|
||||
$reflection = ClassUtils::newReflectionClass($bookProxy);
|
||||
$class = ClassUtils::getClass($bookProxy)¸
|
||||
|
||||
@@ -11,10 +11,8 @@ Core Metadata Drivers
|
||||
Doctrine provides a few different ways for you to specify your
|
||||
metadata:
|
||||
|
||||
|
||||
- **XML files** (XmlDriver)
|
||||
- **Class DocBlock Annotations** (AnnotationDriver)
|
||||
- **YAML files** (YamlDriver)
|
||||
- **PHP Code in files or static functions** (PhpDriver)
|
||||
|
||||
Something important to note about the above drivers is they are all
|
||||
@@ -37,7 +35,6 @@ an entity.
|
||||
<?php
|
||||
$em->getConfiguration()->setMetadataCacheImpl(new ApcuCache());
|
||||
|
||||
|
||||
If you want to use one of the included core metadata drivers you
|
||||
just need to configure it. All the drivers are in the
|
||||
``Doctrine\ORM\Mapping\Driver`` namespace:
|
||||
@@ -59,26 +56,26 @@ implements the ``Driver`` interface:
|
||||
|
||||
<?php
|
||||
namespace Doctrine\ORM\Mapping\Driver;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
interface Driver
|
||||
{
|
||||
/**
|
||||
* Loads the metadata for the specified class into the provided container.
|
||||
*
|
||||
*
|
||||
* @param string $className
|
||||
* @param ClassMetadataInfo $metadata
|
||||
* @param ClassMetadata $metadata
|
||||
*/
|
||||
function loadMetadataForClass($className, ClassMetadataInfo $metadata);
|
||||
|
||||
function loadMetadataForClass($className, ClassMetadata $metadata);
|
||||
|
||||
/**
|
||||
* Gets the names of all mapped classes known to this driver.
|
||||
*
|
||||
*
|
||||
* @return array The names of all mapped classes known to this driver.
|
||||
*/
|
||||
function getAllClassNames();
|
||||
|
||||
function getAllClassNames();
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -102,22 +99,22 @@ the ``AbstractFileDriver`` implementation for you to extend from:
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $_fileExtension = '.dcm.ext';
|
||||
|
||||
protected $fileExtension = '.dcm.ext';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadMetadataForClass($className, ClassMetadataInfo $metadata)
|
||||
public function loadMetadataForClass($className, ClassMetadata $metadata)
|
||||
{
|
||||
$data = $this->_loadMappingFile($file);
|
||||
|
||||
// populate ClassMetadataInfo instance from $data
|
||||
$data = $this->loadMappingFile($file);
|
||||
|
||||
// populate ClassMetadata instance from $data
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _loadMappingFile($file)
|
||||
protected function loadMappingFile($file)
|
||||
{
|
||||
// parse contents of $file and return php data structure
|
||||
}
|
||||
@@ -133,7 +130,6 @@ the ``AbstractFileDriver`` implementation for you to extend from:
|
||||
to name the file ``Entities.User.dcm.ext`` for it to be
|
||||
recognized.
|
||||
|
||||
|
||||
Now you can use your ``MyMetadataDriver`` implementation by setting
|
||||
it with the ``setMetadataDriverImpl()`` method:
|
||||
|
||||
@@ -154,14 +150,11 @@ entity when needed.
|
||||
|
||||
You have all the methods you need to manually specify the mapping
|
||||
information instead of using some mapping file to populate it from.
|
||||
The base ``ClassMetadataInfo`` class is responsible for only data
|
||||
storage and is not meant for runtime use. It does not require that
|
||||
the class actually exists yet so it is useful for describing some
|
||||
The ``ClassMetadata`` class is responsible for only data storage
|
||||
and is not meant for runtime use. It does not require that the
|
||||
class actually exists yet so it is useful for describing some
|
||||
entity before it exists and using that information to generate for
|
||||
example the entities themselves. The class ``ClassMetadata``
|
||||
extends ``ClassMetadataInfo`` and adds some functionality required
|
||||
for runtime usage and requires that the PHP class is present and
|
||||
can be autoloaded.
|
||||
example the entities themselves.
|
||||
|
||||
You can read more about the API of the ``ClassMetadata`` classes in
|
||||
the PHP Mapping chapter.
|
||||
@@ -191,4 +184,3 @@ iterate over them:
|
||||
echo $fieldMapping['fieldName'] . "\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -102,7 +102,6 @@ achieve such standards by implementing a naming strategy.
|
||||
|
||||
You need to create a class which implements ``Doctrine\ORM\Mapping\NamingStrategy``.
|
||||
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
@@ -71,7 +71,7 @@ with inheritance hierarchies.
|
||||
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
|
||||
$sql = "SELECT u.id, u.name, a.id AS address_id, a.street, a.city " .
|
||||
$sql = "SELECT u.id, u.name, a.id AS address_id, a.street, a.city " .
|
||||
"FROM users u INNER JOIN address a ON u.address_id = a.id";
|
||||
|
||||
$rsm = new ResultSetMappingBuilder($entityManager);
|
||||
@@ -92,13 +92,12 @@ a mapping from DQL alias (key) to SQL alias (value)
|
||||
|
||||
<?php
|
||||
|
||||
$selectClause = $builder->generateSelectClause(array(
|
||||
$selectClause = $rsm->generateSelectClause(array(
|
||||
'u' => 't1',
|
||||
'g' => 't2'
|
||||
));
|
||||
$sql = "SELECT " . $selectClause . " FROM users t1 JOIN groups t2 ON t1.group_id = t2.id";
|
||||
|
||||
|
||||
The ResultSetMapping
|
||||
--------------------
|
||||
|
||||
@@ -106,7 +105,6 @@ Understanding the ``ResultSetMapping`` is the key to using a
|
||||
``NativeQuery``. A Doctrine result can contain the following
|
||||
components:
|
||||
|
||||
|
||||
- Entity results. These represent root result elements.
|
||||
- Joined entity results. These represent joined entities in
|
||||
associations of root entity results.
|
||||
@@ -132,7 +130,6 @@ components:
|
||||
``ResultSetMapping`` that describes how the results should be
|
||||
processed by the hydration routines.
|
||||
|
||||
|
||||
We will now look at each of the result types that can appear in a
|
||||
ResultSetMapping in detail.
|
||||
|
||||
@@ -267,7 +264,7 @@ detail:
|
||||
<?php
|
||||
/**
|
||||
* Adds a meta column (foreign key or discriminator column) to the result set.
|
||||
*
|
||||
*
|
||||
* @param string $alias
|
||||
* @param string $columnAlias
|
||||
* @param string $columnName
|
||||
@@ -322,10 +319,10 @@ entity.
|
||||
$rsm->addEntityResult('User', 'u');
|
||||
$rsm->addFieldResult('u', 'id', 'id');
|
||||
$rsm->addFieldResult('u', 'name', 'name');
|
||||
|
||||
$query = $this->_em->createNativeQuery('SELECT id, name FROM users WHERE name = ?', $rsm);
|
||||
|
||||
$query = $this->em->createNativeQuery('SELECT id, name FROM users WHERE name = ?', $rsm);
|
||||
$query->setParameter(1, 'romanb');
|
||||
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
The result would look like this:
|
||||
@@ -358,10 +355,10 @@ thus owns the foreign key.
|
||||
$rsm->addFieldResult('u', 'id', 'id');
|
||||
$rsm->addFieldResult('u', 'name', 'name');
|
||||
$rsm->addMetaResult('u', 'address_id', 'address_id');
|
||||
|
||||
$query = $this->_em->createNativeQuery('SELECT id, name, address_id FROM users WHERE name = ?', $rsm);
|
||||
|
||||
$query = $this->em->createNativeQuery('SELECT id, name, address_id FROM users WHERE name = ?', $rsm);
|
||||
$query->setParameter(1, 'romanb');
|
||||
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
Foreign keys are used by Doctrine for lazy-loading purposes when
|
||||
@@ -387,12 +384,12 @@ associations that are lazy.
|
||||
$rsm->addFieldResult('a', 'address_id', 'id');
|
||||
$rsm->addFieldResult('a', 'street', 'street');
|
||||
$rsm->addFieldResult('a', 'city', 'city');
|
||||
|
||||
|
||||
$sql = 'SELECT u.id, u.name, a.id AS address_id, a.street, a.city FROM users u ' .
|
||||
'INNER JOIN address a ON u.address_id = a.id WHERE u.name = ?';
|
||||
$query = $this->_em->createNativeQuery($sql, $rsm);
|
||||
$query = $this->em->createNativeQuery($sql, $rsm);
|
||||
$query->setParameter(1, 'romanb');
|
||||
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
In this case the nested entity ``Address`` is registered with the
|
||||
@@ -422,10 +419,10 @@ to map the hierarchy (both use a discriminator column).
|
||||
$rsm->addFieldResult('u', 'name', 'name');
|
||||
$rsm->addMetaResult('u', 'discr', 'discr'); // discriminator column
|
||||
$rsm->setDiscriminatorColumn('u', 'discr');
|
||||
|
||||
$query = $this->_em->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm);
|
||||
|
||||
$query = $this->em->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm);
|
||||
$query->setParameter(1, 'romanb');
|
||||
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
Note that in the case of Class Table Inheritance, an example as
|
||||
@@ -442,8 +439,7 @@ 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.
|
||||
|
||||
Like named query, a named native query can be defined at class level or in an XML file.
|
||||
|
||||
A resultSetMapping parameter is defined in @NamedNativeQuery,
|
||||
it represents the name of a defined @SqlResultSetMapping.
|
||||
@@ -538,47 +534,6 @@ it represents the name of a defined @SqlResultSetMapping.
|
||||
</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.
|
||||
@@ -589,7 +544,6 @@ Things to note:
|
||||
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,
|
||||
@@ -655,21 +609,6 @@ Let's now see an implicit declaration of the property / column.
|
||||
</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.
|
||||
@@ -679,7 +618,6 @@ 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
|
||||
@@ -758,41 +696,6 @@ followed by a dot ("."), followed by the name or the field or property of the pr
|
||||
</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:
|
||||
@@ -828,16 +731,6 @@ you can use the resultClass attribute instead of resultSetMapping:
|
||||
</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.
|
||||
@@ -888,18 +781,3 @@ You actually can even mix, entities and scalar returns in the same native query
|
||||
</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
|
||||
|
||||
@@ -23,7 +23,6 @@ of Doctrine2 to this problem is.
|
||||
to a fully-loaded object by calling ``EntityManager#refresh()``
|
||||
or a DQL query with the refresh flag.
|
||||
|
||||
|
||||
What is the problem?
|
||||
--------------------
|
||||
|
||||
@@ -87,4 +86,3 @@ Mainly for optimization purposes, but be careful of premature
|
||||
optimization as partial objects lead to potentially more fragile
|
||||
code.
|
||||
|
||||
|
||||
|
||||
@@ -180,18 +180,29 @@ It also has several methods that create builders (which are necessary for advanc
|
||||
- ``createManyToMany($name, $targetEntity)`` returns an ``ManyToManyAssociationBuilder`` instance
|
||||
- ``createOneToMany($name, $targetEntity)`` returns an ``OneToManyAssociationBuilder`` instance
|
||||
|
||||
ClassMetadataInfo API
|
||||
ClassMetadata API
|
||||
---------------------
|
||||
|
||||
The ``ClassMetadataInfo`` class is the base data object for storing
|
||||
The ``ClassMetadata`` class is the base data object for storing
|
||||
the mapping metadata for a single entity. It contains all the
|
||||
getters and setters you need populate and retrieve information for
|
||||
an entity.
|
||||
|
||||
Internal
|
||||
~~~~~~~~
|
||||
|
||||
- ``getReflectionClass()``
|
||||
- ``getReflectionProperties()``
|
||||
- ``getReflectionProperty($name)``
|
||||
- ``getSingleIdReflectionProperty()``
|
||||
- ``getIdentifierValues($entity)``
|
||||
- ``assignIdentifier($entity, $id)``
|
||||
- ``setFieldValue($entity, $field, $value)``
|
||||
- ``getFieldValue($entity, $field)``
|
||||
|
||||
General Setters
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``setTableName($tableName)``
|
||||
- ``setPrimaryTable(array $primaryTableDefinition)``
|
||||
- ``setCustomRepositoryClass($repositoryClassName)``
|
||||
@@ -204,7 +215,6 @@ General Setters
|
||||
Inheritance Setters
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``setInheritanceType($type)``
|
||||
- ``setSubclasses(array $subclasses)``
|
||||
- ``setParentClasses(array $classNames)``
|
||||
@@ -214,24 +224,18 @@ Inheritance Setters
|
||||
Field Mapping Setters
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``mapField(array $mapping)``
|
||||
- ``mapOneToOne(array $mapping)``
|
||||
- ``mapOneToMany(array $mapping)``
|
||||
- ``mapManyToOne(array $mapping)``
|
||||
- ``mapManyToMany(array $mapping)``
|
||||
- ``addProperty(Property $property)``
|
||||
- ``addAssociation(AssociationMetadata $property)``
|
||||
|
||||
Lifecycle Callback Setters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``addLifecycleCallback($callback, $event)``
|
||||
- ``setLifecycleCallbacks(array $callbacks)``
|
||||
|
||||
Versioning Setters
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``setVersionMapping(array &$mapping)``
|
||||
- ``setVersioned($bool)``
|
||||
- ``setVersionField()``
|
||||
@@ -239,7 +243,6 @@ Versioning Setters
|
||||
General Getters
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``getTableName()``
|
||||
- ``getSchemaName()``
|
||||
- ``getTemporaryIdTableName()``
|
||||
@@ -247,14 +250,8 @@ General Getters
|
||||
Identifier Getters
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``getIdentifierColumnNames()``
|
||||
- ``usesIdGenerator()``
|
||||
- ``isIdentifier($fieldName)``
|
||||
- ``isIdGeneratorIdentity()``
|
||||
- ``isIdGeneratorSequence()``
|
||||
- ``isIdGeneratorTable()``
|
||||
- ``isIdentifierNatural()``
|
||||
- ``getIdentifierFieldNames()``
|
||||
- ``getSingleIdentifierFieldName()``
|
||||
- ``getSingleIdentifierColumnName()``
|
||||
@@ -262,35 +259,18 @@ Identifier Getters
|
||||
Inheritance Getters
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``isInheritanceTypeNone()``
|
||||
- ``isInheritanceTypeJoined()``
|
||||
- ``isInheritanceTypeSingleTable()``
|
||||
- ``isInheritanceTypeTablePerClass()``
|
||||
- ``isInheritedField($fieldName)``
|
||||
- ``isInheritedAssociation($fieldName)``
|
||||
|
||||
Change Tracking Getters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``isChangeTrackingDeferredExplicit()``
|
||||
- ``isChangeTrackingDeferredImplicit()``
|
||||
- ``isChangeTrackingNotify()``
|
||||
|
||||
Field & Association Getters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``isUniqueField($fieldName)``
|
||||
- ``isNullable($fieldName)``
|
||||
- ``getColumnName($fieldName)``
|
||||
- ``getFieldMapping($fieldName)``
|
||||
- ``getAssociationMapping($fieldName)``
|
||||
- ``getAssociationMappings()``
|
||||
- ``getFieldName($columnName)``
|
||||
- ``hasField($fieldName)``
|
||||
- ``getColumnNames(array $fieldNames = null)``
|
||||
- ``getTypeOfField($fieldName)``
|
||||
- ``getTypeOfColumn($columnName)``
|
||||
- ``hasAssociation($fieldName)``
|
||||
@@ -300,26 +280,6 @@ Field & Association Getters
|
||||
Lifecycle Callback Getters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``hasLifecycleCallbacks($lifecycleEvent)``
|
||||
- ``getLifecycleCallbacks($event)``
|
||||
|
||||
ClassMetadata API
|
||||
-----------------
|
||||
|
||||
The ``ClassMetadata`` class extends ``ClassMetadataInfo`` and adds
|
||||
the runtime functionality required by Doctrine. It adds a few extra
|
||||
methods related to runtime reflection for working with the entities
|
||||
themselves.
|
||||
|
||||
|
||||
- ``getReflectionClass()``
|
||||
- ``getReflectionProperties()``
|
||||
- ``getReflectionProperty($name)``
|
||||
- ``getSingleIdReflectionProperty()``
|
||||
- ``getIdentifierValues($entity)``
|
||||
- ``setIdentifierValues($entity, $id)``
|
||||
- ``setFieldValue($entity, $field, $value)``
|
||||
- ``getFieldValue($entity, $field)``
|
||||
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ good example is to inspect what type of object the
|
||||
|
||||
There're currently 3 possible return values for ``getType()``:
|
||||
|
||||
|
||||
- ``QueryBuilder::SELECT``, which returns value 0
|
||||
- ``QueryBuilder::DELETE``, returning value 1
|
||||
- ``QueryBuilder::UPDATE``, which returns value 2
|
||||
@@ -66,7 +65,6 @@ performance. Any changes that may affect the generated DQL actually
|
||||
modifies the state of ``QueryBuilder`` to a stage we call
|
||||
STATE\_DIRTY. One ``QueryBuilder`` can be in two different states:
|
||||
|
||||
|
||||
- ``QueryBuilder::STATE_CLEAN``, which means DQL haven't been
|
||||
altered since last retrieval or nothing were added since its
|
||||
instantiation
|
||||
@@ -76,7 +74,6 @@ STATE\_DIRTY. One ``QueryBuilder`` can be in two different states:
|
||||
Working with QueryBuilder
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
High level API methods
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -247,8 +244,8 @@ while the named placeholders start with a : followed by a string.
|
||||
Calling ``setParameter()`` automatically infers which type you are setting as
|
||||
value. This works for integers, arrays of strings/integers, DateTime instances
|
||||
and for managed entities. If you want to set a type explicitly you can call
|
||||
the third argument to ``setParameter()`` explicitly. It accepts either a PDO
|
||||
type or a DBAL Type name for conversion.
|
||||
the third argument to ``setParameter()`` explicitly. It accepts either a DBAL
|
||||
Doctrine\DBAL\ParameterType::* or a DBAL Type name for conversion.
|
||||
|
||||
If you've got several parameters to bind to your query, you can
|
||||
also use setParameters() instead of setParameter() with the
|
||||
@@ -379,7 +376,6 @@ complete list of supported helper methods available:
|
||||
// Example - $qb->expr()->orX($cond1 [, $condN])->add(...)->...
|
||||
public function orX($x = null); // Returns Expr\OrX instance
|
||||
|
||||
|
||||
/** Comparison objects **/
|
||||
|
||||
// Example - $qb->expr()->eq('u.id', '?1') => u.id = ?1
|
||||
@@ -406,7 +402,6 @@ complete list of supported helper methods available:
|
||||
// Example - $qb->expr()->isNotNull('u.id') => u.id IS NOT NULL
|
||||
public function isNotNull($x); // Returns string
|
||||
|
||||
|
||||
/** Arithmetic objects **/
|
||||
|
||||
// Example - $qb->expr()->prod('u.id', '2') => u.id * 2
|
||||
@@ -421,7 +416,6 @@ complete list of supported helper methods available:
|
||||
// Example - $qb->expr()->quot('u.id', '2') => u.id / 2
|
||||
public function quot($x, $y); // Returns Expr\Math instance
|
||||
|
||||
|
||||
/** Pseudo-function objects **/
|
||||
|
||||
// Example - $qb->expr()->exists($qb2->getDql())
|
||||
@@ -456,7 +450,6 @@ complete list of supported helper methods available:
|
||||
// Example - $qb->expr()->between('u.id', '1', '10')
|
||||
public function between($val, $x, $y); // Returns Expr\Func
|
||||
|
||||
|
||||
/** Function objects **/
|
||||
|
||||
// Example - $qb->expr()->trim('u.firstname')
|
||||
@@ -502,7 +495,7 @@ complete list of supported helper methods available:
|
||||
Adding a Criteria to a Query
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can also add a :ref:`Criteria <filtering-collections>` to a QueryBuilder by
|
||||
You can also add a :ref:`filtering-collections` to a QueryBuilder by
|
||||
using ``addCriteria``:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -512,7 +505,7 @@ using ``addCriteria``:
|
||||
// ...
|
||||
|
||||
$criteria = Criteria::create()
|
||||
->orderBy(['firstName', 'ASC']);
|
||||
->orderBy(['firstName' => Criteria::ASC]);
|
||||
|
||||
// $qb instanceof QueryBuilder
|
||||
$qb->addCriteria($criteria);
|
||||
@@ -531,7 +524,6 @@ one: ``add()``. This method is responsible of building every piece
|
||||
of DQL. It takes 3 parameters: ``$dqlPartName``, ``$dqlPart`` and
|
||||
``$append`` (default=false)
|
||||
|
||||
|
||||
- ``$dqlPartName``: Where the ``$dqlPart`` should be placed.
|
||||
Possible values: select, from, where, groupBy, having, orderBy
|
||||
- ``$dqlPart``: What should be placed in ``$dqlPartName``. Accepts
|
||||
|
||||
@@ -18,7 +18,6 @@ There are some flavors of caching available, but is better to cache read-only da
|
||||
Be aware that caches are not aware of changes made to the persistent store by another application.
|
||||
They can, however, be configured to regularly expire cached data.
|
||||
|
||||
|
||||
Caching Regions
|
||||
---------------
|
||||
|
||||
@@ -42,7 +41,6 @@ Something like below for an entity region :
|
||||
'region_name:entity_3_hash' => ['id'=> 3, 'name' => 'Bar', 'associationName'=>['id'=>22]]
|
||||
];
|
||||
|
||||
|
||||
If the entity holds a collection that also needs to be cached.
|
||||
An collection region could look something like :
|
||||
|
||||
@@ -66,12 +64,10 @@ A query region might be something like :
|
||||
'region_name:query_3_hash' => ['list' => [2, 4]]
|
||||
];
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
The following data structures represents now the cache will looks like, this is not actual cached data.
|
||||
|
||||
|
||||
.. _reference-second-level-cache-regions:
|
||||
|
||||
Cache Regions
|
||||
@@ -87,7 +83,6 @@ It allows you to provide your own cache implementation that might take advantage
|
||||
|
||||
If you want to support locking for ``READ_WRITE`` strategies you should implement ``ConcurrentRegion``; ``CacheRegion`` otherwise.
|
||||
|
||||
|
||||
Cache region
|
||||
~~~~~~~~~~~~
|
||||
|
||||
@@ -97,7 +92,7 @@ Defines a contract for accessing a particular region.
|
||||
|
||||
Defines a contract for accessing a particular cache region.
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.Region.html>`_.
|
||||
`See API Doc <https://www.doctrine-project.org/api/orm/latest/Doctrine/ORM/Cache/Region.html>`_.
|
||||
|
||||
Concurrent cache region
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -111,7 +106,7 @@ If you want to use an ``READ_WRITE`` cache, you should consider providing your o
|
||||
|
||||
Defines contract for concurrently managed data region.
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.ConcurrentRegion.html>`_.
|
||||
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/ConcurrentRegion.html>`_.
|
||||
|
||||
Timestamp region
|
||||
~~~~~~~~~~~~~~~~
|
||||
@@ -120,7 +115,7 @@ Timestamp region
|
||||
|
||||
Tracks the timestamps of the most recent updates to particular entity.
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.TimestampRegion.html>`_.
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/TimestampRegion.html>`_.
|
||||
|
||||
.. _reference-second-level-cache-mode:
|
||||
|
||||
@@ -138,7 +133,6 @@ Caching mode
|
||||
|
||||
* Read Write Cache doesn’t employ any locks but can do reads, inserts, updates and deletes.
|
||||
* Good if the application needs to update data rarely.
|
||||
|
||||
|
||||
* ``READ_WRITE``
|
||||
|
||||
@@ -147,7 +141,6 @@ Caching mode
|
||||
* Slowest strategy.
|
||||
* To use it a the cache region implementation must support locking.
|
||||
|
||||
|
||||
Built-in cached persisters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -173,20 +166,20 @@ Configuration
|
||||
-------------
|
||||
Doctrine allows you to specify configurations and some points of extension for the second-level-cache
|
||||
|
||||
|
||||
Enable Second Level Cache
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To enable the second-level-cache, you should provide a cache factory
|
||||
To enable the second-level-cache, you should provide a cache factory.
|
||||
``\Doctrine\ORM\Cache\DefaultCacheFactory`` is the default implementation.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $config \Doctrine\ORM\Cache\RegionsConfiguration */
|
||||
/* @var $cache \Doctrine\Common\Cache\Cache */
|
||||
/** @var \Doctrine\ORM\Cache\RegionsConfiguration $cacheConfig */
|
||||
/** @var \Doctrine\Common\Cache\Cache $cache */
|
||||
/** @var \Doctrine\ORM\Configuration $config */
|
||||
|
||||
$factory = new \Doctrine\ORM\Cache\DefaultCacheFactory($config, $cache);
|
||||
$factory = new \Doctrine\ORM\Cache\DefaultCacheFactory($cacheConfig, $cache);
|
||||
|
||||
// Enable second-level-cache
|
||||
$config->setSecondLevelCacheEnabled();
|
||||
@@ -195,7 +188,6 @@ To enable the second-level-cache, you should provide a cache factory
|
||||
$config->getSecondLevelCacheConfiguration()
|
||||
->setCacheFactory($factory);
|
||||
|
||||
|
||||
Cache Factory
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
@@ -209,7 +201,7 @@ It allows you to provide a specific implementation of the following components :
|
||||
* ``EntityHydrator`` Transform an entity into a cache entry and cache entry into entities
|
||||
* ``CollectionHydrator`` Transform a collection into a cache entry and cache entry into collection
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.DefaultCacheFactory.html>`_.
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/DefaultCacheFactory.html>`_.
|
||||
|
||||
Region Lifetime
|
||||
~~~~~~~~~~~~~~~
|
||||
@@ -219,8 +211,9 @@ To specify a default lifetime for all regions or specify a different lifetime fo
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $config \Doctrine\ORM\Configuration */
|
||||
/* @var $cacheConfig \Doctrine\ORM\Cache\CacheConfiguration */
|
||||
/** @var \Doctrine\ORM\Configuration $config */
|
||||
/** @var \Doctrine\ORM\Cache\CacheConfiguration $cacheConfig */
|
||||
/** @var \Doctrine\ORM\Cache\RegionsConfiguration $regionConfig */
|
||||
$cacheConfig = $config->getSecondLevelCacheConfiguration();
|
||||
$regionConfig = $cacheConfig->getRegionsConfiguration();
|
||||
|
||||
@@ -228,7 +221,6 @@ To specify a default lifetime for all regions or specify a different lifetime fo
|
||||
$regionConfig->setLifetime('my_entity_region', 3600); // Time to live for a specific region; In seconds
|
||||
$regionConfig->setDefaultLifetime(7200); // Default time to live; In seconds
|
||||
|
||||
|
||||
Cache Log
|
||||
~~~~~~~~~
|
||||
By providing a cache logger you should be able to get information about all cache operations such as hits, misses and puts.
|
||||
@@ -238,7 +230,7 @@ By providing a cache logger you should be able to get information about all cach
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $config \Doctrine\ORM\Configuration */
|
||||
/** @var \Doctrine\ORM\Configuration $config */
|
||||
$logger = new \Doctrine\ORM\Cache\Logging\StatisticsCacheLogger();
|
||||
|
||||
// Cache logger
|
||||
@@ -246,7 +238,6 @@ By providing a cache logger you should be able to get information about all cach
|
||||
$config->getSecondLevelCacheConfiguration()
|
||||
->setCacheLogger($logger);
|
||||
|
||||
|
||||
// Collect cache statistics
|
||||
|
||||
// Get the number of entries successfully retrieved from a specific region.
|
||||
@@ -270,7 +261,7 @@ By providing a cache logger you should be able to get information about all cach
|
||||
If you want to get more information you should implement ``\Doctrine\ORM\Cache\Logging\CacheLogger``.
|
||||
and collect all information you want.
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.CacheLogger.html>`_.
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Logging/CacheLogger.html>`_.
|
||||
|
||||
|
||||
Entity cache definition
|
||||
@@ -280,7 +271,6 @@ Entity cache definition
|
||||
* ``usage`` Specifies the caching strategy: ``READ_ONLY``, ``NONSTRICT_READ_WRITE``, ``READ_WRITE``. see :ref:`reference-second-level-cache-mode`
|
||||
* ``region`` Optional value that specifies the name of the second level cache region.
|
||||
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
@@ -310,7 +300,7 @@ Entity cache definition
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
<entity name="Country">
|
||||
<cache usage="READ_ONLY" region="my_entity_region" />
|
||||
<id name="id" type="integer" column="id">
|
||||
@@ -320,30 +310,11 @@ Entity cache definition
|
||||
</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.
|
||||
It caches the primary keys of association and cache each element will be cached into its region.
|
||||
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
@@ -386,7 +357,7 @@ It caches the primary keys of association and cache each element will be cached
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
<entity name="State">
|
||||
|
||||
<cache usage="NONSTRICT_READ_WRITE" />
|
||||
@@ -396,7 +367,7 @@ It caches the primary keys of association and cache each element will be cached
|
||||
</id>
|
||||
|
||||
<field name="name" type="string" column="name"/>
|
||||
|
||||
|
||||
<many-to-one field="country" target-entity="Country">
|
||||
<cache usage="NONSTRICT_READ_WRITE" />
|
||||
|
||||
@@ -411,39 +382,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.
|
||||
|
||||
Cache usage
|
||||
@@ -461,8 +399,8 @@ Basic entity cache
|
||||
|
||||
$country1 = $em->find('Country', 1); // Retrieve item from cache
|
||||
|
||||
$country->setName("New Name");
|
||||
$em->persist($country);
|
||||
$country1->setName("New Name");
|
||||
|
||||
$em->flush(); // Hit database to update the row and update cache
|
||||
|
||||
$em->clear(); // Clear entity manager
|
||||
@@ -470,7 +408,6 @@ Basic entity cache
|
||||
$country2 = $em->find('Country', 1); // Retrieve item from cache
|
||||
// Notice that $country1 and $country2 are not the same instance.
|
||||
|
||||
|
||||
Association cache
|
||||
|
||||
.. code-block:: php
|
||||
@@ -538,7 +475,7 @@ The query cache stores the results of the query but as identifiers, entity value
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $em \Doctrine\ORM\EntityManager */
|
||||
/** @var \Doctrine\ORM\EntityManager $em */
|
||||
|
||||
// Execute database query, store query cache and entity cache
|
||||
$result1 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
|
||||
@@ -565,7 +502,7 @@ The Cache Mode controls how a particular query interacts with the second-level c
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $em \Doctrine\ORM\EntityManager */
|
||||
/** @var \Doctrine\ORM\EntityManager $em */
|
||||
// Will refresh the query cache and all entities the cache as it reads from the database.
|
||||
$result1 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
|
||||
->setCacheMode(Cache::MODE_GET)
|
||||
@@ -583,37 +520,34 @@ DQL UPDATE / DELETE statements are ported directly into a database and bypass th
|
||||
Entities that are already cached will NOT be invalidated.
|
||||
However the cached data could be evicted using the cache API or an special query hint.
|
||||
|
||||
|
||||
Execute the ``UPDATE`` and invalidate ``all cache entries`` using ``Query::HINT_CACHE_EVICT``
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// Execute and invalidate
|
||||
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
|
||||
$this->em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
|
||||
->setHint(Query::HINT_CACHE_EVICT, true)
|
||||
->execute();
|
||||
|
||||
|
||||
Execute the ``UPDATE`` and invalidate ``all cache entries`` using the cache API
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// Execute
|
||||
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
|
||||
$this->em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
|
||||
->execute();
|
||||
// Invoke Cache API
|
||||
$em->getCache()->evictEntityRegion('Entity\Country');
|
||||
|
||||
|
||||
Execute the ``UPDATE`` and invalidate ``a specific cache entry`` using the cache API
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// Execute
|
||||
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
|
||||
$this->em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
|
||||
->execute();
|
||||
// Invoke Cache API
|
||||
$em->getCache()->evictEntity('Entity\Country', 1);
|
||||
@@ -653,7 +587,7 @@ However, you can use the cache API to check / invalidate cache entries.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $cache \Doctrine\ORM\Cache */
|
||||
/** @var \Doctrine\ORM\Cache $cache */
|
||||
$cache = $em->getCache();
|
||||
|
||||
$cache->containsEntity('Entity\State', 1) // Check if the cache exists
|
||||
@@ -698,11 +632,11 @@ For performance reasons the cache API does not extract from composite primary ke
|
||||
}
|
||||
|
||||
// Supported
|
||||
/* @var $article Article */
|
||||
/** @var Article $article */
|
||||
$article = $em->find('Article', 1);
|
||||
|
||||
// Supported
|
||||
/* @var $article Article */
|
||||
/** @var Article $article */
|
||||
$article = $em->find('Article', $article);
|
||||
|
||||
// Supported
|
||||
@@ -723,7 +657,6 @@ should be used in conjunction with distributed caching system such as memcached,
|
||||
Caches should be used with care when using a load-balancer if you don't share the cache.
|
||||
While using APC or any file based cache update occurred in a specific machine would not reflect to the cache in other machines.
|
||||
|
||||
|
||||
Paginator
|
||||
~~~~~~~~~
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ we cannot protect you from SQL injection.
|
||||
Please also read the documentation chapter on Security in Doctrine DBAL. This
|
||||
page only handles Security issues in the ORM.
|
||||
|
||||
- [DBAL Security Page](https://github.com/doctrine/dbal/blob/master/docs/en/reference/security.rst)
|
||||
- `DBAL Security Page <http://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/security.html>`
|
||||
|
||||
If you find a Security bug in Doctrine, please report it on Jira and change the
|
||||
Security Level to "Security Issues". It will be visible to Doctrine Core
|
||||
@@ -81,7 +81,6 @@ this is technically impossible. The correct way is:
|
||||
$query = $entityManager->createQuery($dql);
|
||||
$query->setParameter(1, $_GET['status']);
|
||||
|
||||
|
||||
Preventing Mass Assignment Vulnerabilities
|
||||
------------------------------------------
|
||||
|
||||
@@ -118,8 +117,8 @@ entity might look like this:
|
||||
}
|
||||
}
|
||||
|
||||
Now the possiblity of mass-asignment exists on this entity and can
|
||||
be exploitet by attackers to set the "isAdmin" flag to true on any
|
||||
Now the possibility of mass-assignment exists on this entity and can
|
||||
be exploited by attackers to set the "isAdmin" flag to true on any
|
||||
object when you pass the whole request data to this method like:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -22,7 +22,6 @@ about the use of generate entities for example, you can call:
|
||||
|
||||
$> php vendor/bin/doctrine orm:generate-entities --help
|
||||
|
||||
|
||||
Configuration
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
@@ -73,7 +72,7 @@ sample ``cli-config.php`` file looks as follows:
|
||||
|
||||
// Any way to access the EntityManager from your application
|
||||
$em = GetMyEntityManager();
|
||||
|
||||
|
||||
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
|
||||
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
|
||||
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
|
||||
@@ -84,7 +83,7 @@ script will ultimately use. The Doctrine Binary will automatically
|
||||
find the first instance of HelperSet in the global variable
|
||||
namespace and use this.
|
||||
|
||||
.. note::
|
||||
.. note::
|
||||
|
||||
You have to adjust this snippet for your specific application or framework
|
||||
and use their facilities to access the Doctrine EntityManager and
|
||||
@@ -95,7 +94,6 @@ Command Overview
|
||||
|
||||
The following Commands are currently available:
|
||||
|
||||
|
||||
- ``help`` Displays help for a command (?)
|
||||
- ``list`` Lists commands
|
||||
- ``dbal:import`` Import SQL file(s) directly to Database.
|
||||
@@ -109,16 +107,10 @@ The following Commands are currently available:
|
||||
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
|
||||
@@ -133,12 +125,8 @@ The following Commands are currently available:
|
||||
|
||||
For these commands are also available aliases:
|
||||
|
||||
|
||||
- ``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::
|
||||
|
||||
@@ -164,7 +152,6 @@ Database Schema Generation
|
||||
they are not related to the current project that is using Doctrine.
|
||||
Please be careful!
|
||||
|
||||
|
||||
To generate your database schema from your Doctrine mapping files
|
||||
you can use the ``SchemaTool`` class or the ``schema-tool`` Console
|
||||
Command.
|
||||
@@ -173,7 +160,7 @@ When using the SchemaTool class directly, create your schema using
|
||||
the ``createSchema()`` method. First create an instance of the
|
||||
``SchemaTool`` and pass it an instance of the ``EntityManager``
|
||||
that you want to use to create the schema. This method receives an
|
||||
array of ``ClassMetadataInfo`` instances.
|
||||
array of ``ClassMetadata`` instances.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -204,8 +191,8 @@ tables of the current model to clean up with orphaned tables.
|
||||
|
||||
You can also use database introspection to update your schema
|
||||
easily with the ``updateSchema()`` method. It will compare your
|
||||
existing database schema to the passed array of
|
||||
``ClassMetadataInfo`` instances.
|
||||
existing database schema to the passed array of ``ClassMetadata``
|
||||
instances.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -261,163 +248,6 @@ your cli-config.php properly.
|
||||
(or mapping files), i.e.
|
||||
``new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em, $mappingPaths);``
|
||||
|
||||
Entity Generation
|
||||
-----------------
|
||||
|
||||
Generate entity classes and method stubs from your mapping information.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ php doctrine orm:generate-entities
|
||||
$ php doctrine orm:generate-entities --update-entities
|
||||
$ php 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 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 ``ClassMetadataInfo`` 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 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
|
||||
-----------------------------------------
|
||||
|
||||
@@ -463,7 +293,6 @@ number of elements with error messages.
|
||||
prefix backslash. PHP does this with ``get_class()`` or Reflection
|
||||
methods for backwards compatibility reasons.
|
||||
|
||||
|
||||
Adding own commands
|
||||
-------------------
|
||||
|
||||
@@ -509,7 +338,6 @@ defined ones) is possible through the command:
|
||||
new \MyProject\Tools\Console\Commands\OneMoreCommand(),
|
||||
));
|
||||
|
||||
|
||||
Re-use console application
|
||||
--------------------------
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ looks like this:
|
||||
// $em instanceof EntityManager
|
||||
$em->getConnection()->beginTransaction(); // suspend auto-commit
|
||||
try {
|
||||
//... do some work
|
||||
// ... do some work
|
||||
$user = new User;
|
||||
$user->setName('George');
|
||||
$em->persist($user);
|
||||
@@ -98,24 +98,16 @@ functionally equivalent to the previously shown code looks as follows:
|
||||
<?php
|
||||
// $em instanceof EntityManager
|
||||
$em->transactional(function($em) {
|
||||
//... do some work
|
||||
// ... 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
|
||||
commit and rolls back the transaction when an
|
||||
exception occurs.
|
||||
commit.
|
||||
|
||||
.. _transactions-and-concurrency_exception-handling:
|
||||
|
||||
@@ -208,15 +200,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):
|
||||
|
||||
@@ -241,15 +224,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,
|
||||
@@ -279,15 +253,15 @@ either when calling ``EntityManager#find()``:
|
||||
<?php
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\OptimisticLockException;
|
||||
|
||||
|
||||
$theEntityId = 1;
|
||||
$expectedVersion = 184;
|
||||
|
||||
|
||||
try {
|
||||
$entity = $em->find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion);
|
||||
|
||||
|
||||
// do the work
|
||||
|
||||
|
||||
$em->flush();
|
||||
} catch(OptimisticLockException $e) {
|
||||
echo "Sorry, but someone else has already changed this entity. Please apply the changes again!";
|
||||
@@ -300,16 +274,16 @@ Or you can use ``EntityManager#lock()`` to find out:
|
||||
<?php
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\OptimisticLockException;
|
||||
|
||||
|
||||
$theEntityId = 1;
|
||||
$expectedVersion = 184;
|
||||
|
||||
|
||||
$entity = $em->find('User', $theEntityId);
|
||||
|
||||
|
||||
try {
|
||||
// assert version
|
||||
$em->lock($entity, LockMode::OPTIMISTIC, $expectedVersion);
|
||||
|
||||
|
||||
} catch(OptimisticLockException $e) {
|
||||
echo "Sorry, but someone else has already changed this entity. Please apply the changes again!";
|
||||
}
|
||||
@@ -348,7 +322,7 @@ See the example code, The form (GET Request):
|
||||
|
||||
<?php
|
||||
$post = $em->find('BlogPost', 123456);
|
||||
|
||||
|
||||
echo '<input type="hidden" name="id" value="' . $post->getId() . '" />';
|
||||
echo '<input type="hidden" name="version" value="' . $post->getCurrentVersion() . '" />';
|
||||
|
||||
@@ -359,7 +333,7 @@ And the change headline action (POST Request):
|
||||
<?php
|
||||
$postId = (int)$_GET['id'];
|
||||
$postVersion = (int)$_GET['version'];
|
||||
|
||||
|
||||
$post = $em->find('BlogPost', $postId, \Doctrine\DBAL\LockMode::OPTIMISTIC, $postVersion);
|
||||
|
||||
.. _transactions-and-concurrency_pessimistic-locking:
|
||||
@@ -382,7 +356,6 @@ transaction is running.
|
||||
|
||||
Doctrine 2 currently supports two pessimistic lock modes:
|
||||
|
||||
|
||||
- Pessimistic Write
|
||||
(``Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE``), locks the
|
||||
underlying database rows for concurrent Read and Write Operations.
|
||||
@@ -392,7 +365,6 @@ Doctrine 2 currently supports two pessimistic lock modes:
|
||||
|
||||
You can use pessimistic locks in three different scenarios:
|
||||
|
||||
|
||||
1. Using
|
||||
``EntityManager#find($className, $id, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE)``
|
||||
or
|
||||
@@ -406,4 +378,3 @@ You can use pessimistic locks in three different scenarios:
|
||||
or
|
||||
``Query#setLockMode(\Doctrine\DBAL\LockMode::PESSIMISTIC_READ)``
|
||||
|
||||
|
||||
|
||||
@@ -16,11 +16,11 @@ Bidirectional Associations
|
||||
The following rules apply to **bidirectional** associations:
|
||||
|
||||
- The inverse side has to have the ``mappedBy`` attribute of the OneToOne,
|
||||
OneToMany, or ManyToMany mapping declaration. The mappedBy
|
||||
OneToMany, or ManyToMany mapping declaration. The ``mappedBy``
|
||||
attribute contains the name of the association-field on the owning side.
|
||||
- The owning side has to have the ``inversedBy`` attribute of the
|
||||
OneToOne, ManyToOne, or ManyToMany mapping declaration.
|
||||
The inversedBy attribute contains the name of the association-field
|
||||
OneToOne, ManyToOne, or ManyToMany mapping declaration.
|
||||
The ``inversedBy`` attribute contains the name of the association-field
|
||||
on the inverse-side.
|
||||
- ManyToOne is always the owning side of a bidirectional association.
|
||||
- OneToMany is always the inverse side of a bidirectional association.
|
||||
|
||||
@@ -36,13 +36,15 @@ will still end up with the same reference:
|
||||
|
||||
public function testIdentityMapReference()
|
||||
{
|
||||
$objectA = $this->entityManager->getReference('EntityName', 1);
|
||||
// check for proxyinterface
|
||||
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $objectA);
|
||||
/** @var EntityName|\ProxyManager\Proxy\GhostObjectInterface $objectA */
|
||||
$objectA = $this->entityManager->getReference(EntityName::class, 1);
|
||||
|
||||
$objectB = $this->entityManager->find('EntityName', 1);
|
||||
self::assertInstanceOf(\ProxyManager\Proxy\GhostObjectInterface::class, $objectA);
|
||||
self::assertFalse($objectA->isProxyInitialized());
|
||||
|
||||
$this->assertSame($objectA, $objectB)
|
||||
$objectB = $this->entityManager->find(EntityName::class, 1);
|
||||
|
||||
self::assertSame($objectA, $objectB)
|
||||
}
|
||||
|
||||
The identity map being indexed by primary keys only allows shortcuts when you
|
||||
@@ -104,7 +106,7 @@ How Doctrine Detects Changes
|
||||
Doctrine is a data-mapper that tries to achieve persistence-ignorance (PI).
|
||||
This means you map php objects into a relational database that don't
|
||||
necessarily know about the database at all. A natural question would now be,
|
||||
"how does Doctrine even detect objects have changed?".
|
||||
"how does Doctrine even detect objects have changed?".
|
||||
|
||||
For this Doctrine keeps a second map inside the UnitOfWork. Whenever you fetch
|
||||
an object from the database Doctrine will keep a copy of all the properties and
|
||||
@@ -129,12 +131,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.
|
||||
|
||||
|
||||
Query Internals
|
||||
---------------
|
||||
|
||||
@@ -198,4 +198,3 @@ ClassMetadataFactory
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
tbr
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ with associations in Doctrine:
|
||||
removed, not the entity itself. A collection of entities always
|
||||
only represents the association to the containing entities, not the
|
||||
entity itself.
|
||||
- When a bidirectional assocation is updated, Doctrine only checks
|
||||
- When a bidirectional association is updated, Doctrine only checks
|
||||
on one of both sides for these changes. This is called the :doc:`owning side <unitofwork-associations>`
|
||||
of the association.
|
||||
- A property with a reference to many entities has to be instances of the
|
||||
@@ -37,7 +37,7 @@ information about its type and if it's the owning or inverse side.
|
||||
{
|
||||
/** @Id @GeneratedValue @Column(type="string") */
|
||||
private $id;
|
||||
|
||||
|
||||
/**
|
||||
* Bidirectional - Many users have Many favorite comments (OWNING SIDE)
|
||||
*
|
||||
@@ -45,7 +45,7 @@ information about its type and if it's the owning or inverse side.
|
||||
* @JoinTable(name="user_favorite_comments")
|
||||
*/
|
||||
private $favorites;
|
||||
|
||||
|
||||
/**
|
||||
* Unidirectional - Many users have marked many comments as read
|
||||
*
|
||||
@@ -53,14 +53,14 @@ information about its type and if it's the owning or inverse side.
|
||||
* @JoinTable(name="user_read_comments")
|
||||
*/
|
||||
private $commentsRead;
|
||||
|
||||
|
||||
/**
|
||||
* Bidirectional - One-To-Many (INVERSE SIDE)
|
||||
*
|
||||
* @OneToMany(targetEntity="Comment", mappedBy="author")
|
||||
*/
|
||||
private $commentsAuthored;
|
||||
|
||||
|
||||
/**
|
||||
* Unidirectional - Many-To-One
|
||||
*
|
||||
@@ -68,20 +68,20 @@ information about its type and if it's the owning or inverse side.
|
||||
*/
|
||||
private $firstComment;
|
||||
}
|
||||
|
||||
|
||||
/** @Entity */
|
||||
class Comment
|
||||
{
|
||||
/** @Id @GeneratedValue @Column(type="string") */
|
||||
private $id;
|
||||
|
||||
|
||||
/**
|
||||
* Bidirectional - Many comments are favorited by many users (INVERSE SIDE)
|
||||
*
|
||||
* @ManyToMany(targetEntity="User", mappedBy="favorites")
|
||||
*/
|
||||
private $userFavorites;
|
||||
|
||||
|
||||
/**
|
||||
* Bidirectional - Many Comments are authored by one user (OWNING SIDE)
|
||||
*
|
||||
@@ -100,19 +100,19 @@ definitions omitted):
|
||||
firstComment_id VARCHAR(255) DEFAULT NULL,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
|
||||
CREATE TABLE Comment (
|
||||
id VARCHAR(255) NOT NULL,
|
||||
author_id VARCHAR(255) DEFAULT NULL,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
|
||||
CREATE TABLE user_favorite_comments (
|
||||
user_id VARCHAR(255) NOT NULL,
|
||||
favorite_comment_id VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY(user_id, favorite_comment_id)
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
|
||||
CREATE TABLE user_read_comments (
|
||||
user_id VARCHAR(255) NOT NULL,
|
||||
comment_id VARCHAR(255) NOT NULL,
|
||||
@@ -135,7 +135,7 @@ relations of the ``User``:
|
||||
public function getReadComments() {
|
||||
return $this->commentsRead;
|
||||
}
|
||||
|
||||
|
||||
public function setFirstComment(Comment $c) {
|
||||
$this->firstComment = $c;
|
||||
}
|
||||
@@ -148,17 +148,17 @@ The interaction code would then look like in the following snippet
|
||||
|
||||
<?php
|
||||
$user = $em->find('User', $userId);
|
||||
|
||||
|
||||
// unidirectional many to many
|
||||
$comment = $em->find('Comment', $readCommentId);
|
||||
$user->getReadComments()->add($comment);
|
||||
|
||||
|
||||
$em->flush();
|
||||
|
||||
|
||||
// unidirectional many to one
|
||||
$myFirstComment = new Comment();
|
||||
$user->setFirstComment($myFirstComment);
|
||||
|
||||
|
||||
$em->persist($myFirstComment);
|
||||
$em->flush();
|
||||
|
||||
@@ -171,40 +171,40 @@ fields on both sides:
|
||||
class User
|
||||
{
|
||||
// ..
|
||||
|
||||
|
||||
public function getAuthoredComments() {
|
||||
return $this->commentsAuthored;
|
||||
}
|
||||
|
||||
|
||||
public function getFavoriteComments() {
|
||||
return $this->favorites;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Comment
|
||||
{
|
||||
// ...
|
||||
|
||||
|
||||
public function getUserFavorites() {
|
||||
return $this->userFavorites;
|
||||
}
|
||||
|
||||
|
||||
public function setAuthor(User $author = null) {
|
||||
$this->author = $author;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Many-to-Many
|
||||
$user->getFavorites()->add($favoriteComment);
|
||||
$favoriteComment->getUserFavorites()->add($user);
|
||||
|
||||
|
||||
$em->flush();
|
||||
|
||||
|
||||
// Many-To-One / One-To-Many Bidirectional
|
||||
$newComment = new Comment();
|
||||
$user->getAuthoredComments()->add($newComment);
|
||||
$newComment->setAuthor($user);
|
||||
|
||||
|
||||
$em->persist($newComment);
|
||||
$em->flush();
|
||||
|
||||
@@ -225,10 +225,10 @@ element. Here are some examples:
|
||||
// Remove by Elements
|
||||
$user->getComments()->removeElement($comment);
|
||||
$comment->setAuthor(null);
|
||||
|
||||
|
||||
$user->getFavorites()->removeElement($comment);
|
||||
$comment->getUserFavorites()->removeElement($user);
|
||||
|
||||
|
||||
// Remove by Key
|
||||
$user->getComments()->remove($ithComment);
|
||||
$comment->setAuthor(null);
|
||||
@@ -240,7 +240,7 @@ Notice how both sides of the bidirectional association are always
|
||||
updated. Unidirectional associations are consequently simpler to
|
||||
handle.
|
||||
|
||||
Also note that if you use type-hinting in your methods, you will
|
||||
Also note that if you use type-hinting in your methods, you will
|
||||
have to specify a nullable type, i.e. ``setAddress(?Address $address)``,
|
||||
otherwise ``setAddress(null)`` will fail to remove the association.
|
||||
Another way to deal with this is to provide a special method, like
|
||||
@@ -262,7 +262,6 @@ where n is the size of the map.
|
||||
can often be used to improve performance by avoiding the loading of
|
||||
the inverse collection.
|
||||
|
||||
|
||||
You can also clear the contents of a whole collection using the
|
||||
``Collections::clear()`` method. You should be aware that using
|
||||
this method can lead to a straight and optimized database delete or
|
||||
@@ -271,8 +270,8 @@ entities that have been re-added to the collection.
|
||||
|
||||
Say you clear a collection of tags by calling
|
||||
``$post->getTags()->clear();`` and then call
|
||||
``$post->getTags()->add($tag)``. This will not recognize the tag having
|
||||
already been added previously and will consequently issue two separate database
|
||||
``$post->getTags()->add($tag)``. This will not recognize the tag having
|
||||
already been added previously and will consequently issue two separate database
|
||||
calls.
|
||||
|
||||
Association Management Methods
|
||||
@@ -291,12 +290,12 @@ example that encapsulate much of the association management code:
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
//...
|
||||
// ...
|
||||
public function markCommentRead(Comment $comment) {
|
||||
// Collections implement ArrayAccess
|
||||
$this->commentsRead[] = $comment;
|
||||
}
|
||||
|
||||
|
||||
public function addComment(Comment $comment) {
|
||||
if (count($this->commentsAuthored) == 0) {
|
||||
$this->setFirstComment($comment);
|
||||
@@ -304,30 +303,30 @@ example that encapsulate much of the association management code:
|
||||
$this->comments[] = $comment;
|
||||
$comment->setAuthor($this);
|
||||
}
|
||||
|
||||
|
||||
private function setFirstComment(Comment $c) {
|
||||
$this->firstComment = $c;
|
||||
}
|
||||
|
||||
|
||||
public function addFavorite(Comment $comment) {
|
||||
$this->favorites->add($comment);
|
||||
$comment->addUserFavorite($this);
|
||||
}
|
||||
|
||||
|
||||
public function removeFavorite(Comment $comment) {
|
||||
$this->favorites->removeElement($comment);
|
||||
$comment->removeUserFavorite($this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Comment
|
||||
{
|
||||
// ..
|
||||
|
||||
|
||||
public function addUserFavorite(User $user) {
|
||||
$this->userFavorites[] = $user;
|
||||
}
|
||||
|
||||
|
||||
public function removeUserFavorite(User $user) {
|
||||
$this->userFavorites->removeElement($user);
|
||||
}
|
||||
@@ -351,7 +350,6 @@ the details inside the classes can be challenging.
|
||||
entity cannot circumvent the logic you implement on your entity for
|
||||
association management. For example:
|
||||
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
@@ -373,7 +371,7 @@ as your preferences.
|
||||
Synchronizing Bidirectional Collections
|
||||
---------------------------------------
|
||||
|
||||
In the case of Many-To-Many associations you as the developer have the
|
||||
In the case of Many-To-Many associations you as the developer have the
|
||||
responsibility of keeping the collections on the owning and inverse side
|
||||
in sync when you apply changes to them. Doctrine can only
|
||||
guarantee a consistent state for the hydration, not for your client
|
||||
@@ -387,13 +385,12 @@ can show the possible caveats you can encounter:
|
||||
<?php
|
||||
$user->getFavorites()->add($favoriteComment);
|
||||
// not calling $favoriteComment->getUserFavorites()->add($user);
|
||||
|
||||
|
||||
$user->getFavorites()->contains($favoriteComment); // TRUE
|
||||
$favoriteComment->getUserFavorites()->contains($user); // FALSE
|
||||
|
||||
There are two approaches to handle this problem in your code:
|
||||
|
||||
|
||||
1. Ignore updating the inverse side of bidirectional collections,
|
||||
BUT never read from them in requests that changed their state. In
|
||||
the next request Doctrine hydrates the consistent collection state
|
||||
@@ -410,7 +407,7 @@ Transitive persistence / Cascade Operations
|
||||
Doctrine 2 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``, ``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
|
||||
@@ -422,7 +419,7 @@ comment might look like in your controller (without ``cascade: persist``):
|
||||
$user = new User();
|
||||
$myFirstComment = new Comment();
|
||||
$user->addComment($myFirstComment);
|
||||
|
||||
|
||||
$em->persist($user);
|
||||
$em->persist($myFirstComment); // required, if `cascade: persist` is not set
|
||||
$em->flush();
|
||||
@@ -463,14 +460,14 @@ If you then set up the cascading to the ``User#commentsAuthored`` property...
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
//...
|
||||
// ...
|
||||
/**
|
||||
* Bidirectional - One-To-Many (INVERSE SIDE)
|
||||
*
|
||||
* @OneToMany(targetEntity="Comment", mappedBy="author", cascade={"persist", "remove"})
|
||||
*/
|
||||
private $commentsAuthored;
|
||||
//...
|
||||
// ...
|
||||
}
|
||||
|
||||
...you can now create a user and an associated comment like this:
|
||||
@@ -480,7 +477,7 @@ If you then set up the cascading to the ``User#commentsAuthored`` property...
|
||||
<?php
|
||||
$user = new User();
|
||||
$user->comment('Lorem ipsum', new DateTime());
|
||||
|
||||
|
||||
$em->persist($user);
|
||||
$em->flush();
|
||||
|
||||
@@ -529,7 +526,6 @@ operation. During each ``flush()`` operation Doctrine detects if there
|
||||
are new entities in any collection and three possible cases can
|
||||
happen:
|
||||
|
||||
|
||||
1. New entities in a collection marked as ``cascade: persist`` will be
|
||||
directly persisted by Doctrine.
|
||||
2. New entities in a collection not marked as ``cascade: persist`` will
|
||||
@@ -610,10 +606,10 @@ Now two examples of what happens when you remove the references:
|
||||
|
||||
$em->flush();
|
||||
|
||||
In this case you have not only changed the ``Contact`` entity itself but
|
||||
you have also removed the references for standing data and as well as one
|
||||
address reference. When flush is called not only are the references removed
|
||||
but both the old standing data and the one address entity are also deleted
|
||||
In this case you have not only changed the ``Contact`` entity itself but
|
||||
you have also removed the references for standing data and as well as one
|
||||
address reference. When flush is called not only are the references removed
|
||||
but both the old standing data and the one address entity are also deleted
|
||||
from the database.
|
||||
|
||||
.. _filtering-collections:
|
||||
@@ -719,7 +715,6 @@ methods:
|
||||
* ``startsWith($field, $value)``
|
||||
* ``endsWith($field, $value)``
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
There is a limitation on the compatibility of Criteria comparisons.
|
||||
|
||||
@@ -25,6 +25,13 @@ Work that have not yet been persisted are lost.
|
||||
Not calling ``EntityManager#flush()`` will lead to all changes
|
||||
during that request being lost.
|
||||
|
||||
.. note::
|
||||
|
||||
Doctrine does NEVER touch the public API of methods in your entity
|
||||
classes (like getters and setters) nor the constructor method.
|
||||
Instead, it uses reflection to get/set data from/to your entity objects.
|
||||
When Doctrine fetches data from DB and saves it back,
|
||||
any code put in your get/set methods won't be implicitly taken into account.
|
||||
|
||||
Entities and the Identity Map
|
||||
-----------------------------
|
||||
@@ -41,7 +48,7 @@ headline "Hello World" with the ID 1234:
|
||||
<?php
|
||||
$article = $entityManager->find('CMS\Article', 1234);
|
||||
$article->setHeadline('Hello World dude!');
|
||||
|
||||
|
||||
$article2 = $entityManager->find('CMS\Article', 1234);
|
||||
echo $article2->getHeadline();
|
||||
|
||||
@@ -93,25 +100,25 @@ from newly opened EntityManager.
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
|
||||
|
||||
/** @Column(type="string") */
|
||||
private $headline;
|
||||
|
||||
|
||||
/** @ManyToOne(targetEntity="User") */
|
||||
private $author;
|
||||
|
||||
|
||||
/** @OneToMany(targetEntity="Comment", mappedBy="article") */
|
||||
private $comments;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->comments = new ArrayCollection();
|
||||
}
|
||||
|
||||
|
||||
public function getAuthor() { return $this->author; }
|
||||
public function getComments() { return $this->comments; }
|
||||
}
|
||||
|
||||
|
||||
$article = $em->find('Article', 1);
|
||||
|
||||
This code only retrieves the ``Article`` instance with id 1 executing
|
||||
@@ -132,22 +139,22 @@ your code. See the following code:
|
||||
|
||||
<?php
|
||||
$article = $em->find('Article', 1);
|
||||
|
||||
|
||||
// accessing a method of the user instance triggers the lazy-load
|
||||
echo "Author: " . $article->getAuthor()->getName() . "\n";
|
||||
|
||||
|
||||
// Lazy Loading Proxies pass instanceof tests:
|
||||
if ($article->getAuthor() instanceof User) {
|
||||
// a User Proxy is a generated "UserProxy" class
|
||||
}
|
||||
|
||||
|
||||
// accessing the comments as an iterator triggers the lazy-load
|
||||
// retrieving ALL the comments of this article from the database
|
||||
// using a single SELECT statement
|
||||
foreach ($article->getComments() as $comment) {
|
||||
echo $comment->getText() . "\n\n";
|
||||
}
|
||||
|
||||
|
||||
// Article::$comments passes instanceof tests for the Collection interface
|
||||
// But it will NOT pass for the ArrayCollection interface
|
||||
if ($article->getComments() instanceof \Doctrine\Common\Collections\Collection) {
|
||||
@@ -155,25 +162,28 @@ your code. See the following code:
|
||||
}
|
||||
|
||||
A slice of the generated proxy classes code looks like the
|
||||
following piece of code. A real proxy class override ALL public
|
||||
methods along the lines of the ``getName()`` method shown below:
|
||||
following piece of code. A real proxy class override all non-identity
|
||||
non-transient object state at instantiation time in order to
|
||||
enable lazy-loading mechanisms:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class UserProxy extends User implements Proxy
|
||||
class UserProxyHASH extends User implements GhostObjectInterface
|
||||
{
|
||||
private function _load()
|
||||
// ... generated code
|
||||
|
||||
public static function staticProxyConstructor($initializer)
|
||||
{
|
||||
// lazy loading code
|
||||
// ... generated code
|
||||
}
|
||||
|
||||
public function getName()
|
||||
|
||||
private function callInitializerHASH($methodName, array $parameters)
|
||||
{
|
||||
$this->_load();
|
||||
return parent::getName();
|
||||
// ... generated code
|
||||
}
|
||||
// .. other public methods of User
|
||||
|
||||
// ... generated code
|
||||
}
|
||||
|
||||
.. warning::
|
||||
@@ -183,7 +193,6 @@ methods along the lines of the ``getName()`` method shown below:
|
||||
to heavily. Make sure to use DQL to fetch-join all the parts of the
|
||||
object-graph that you need as efficiently as possible.
|
||||
|
||||
|
||||
Persisting entities
|
||||
-------------------
|
||||
|
||||
@@ -206,7 +215,6 @@ be properly synchronized with the database when
|
||||
database in the most efficient way and a single, short transaction,
|
||||
taking care of maintaining referential integrity.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -227,18 +235,16 @@ Example:
|
||||
generated identifier being not available after a failed flush
|
||||
operation.
|
||||
|
||||
|
||||
The semantics of the persist operation, applied on an entity X, are
|
||||
as follows:
|
||||
|
||||
|
||||
- If X is a new entity, it becomes managed. The entity X will be
|
||||
entered into the database as a result of the flush operation.
|
||||
- If X is a preexisting managed entity, it is ignored by the
|
||||
persist operation. However, the persist operation is cascaded to
|
||||
entities referenced by X, if the relationships from X to these
|
||||
other entities are mapped with cascade=PERSIST or cascade=ALL (see
|
||||
":ref:`Transitive Persistence <transitive-persistence>`").
|
||||
":ref:`transitive-persistence`").
|
||||
- If X is a removed entity, it becomes managed.
|
||||
- If X is a detached entity, an exception will be thrown on
|
||||
flush.
|
||||
@@ -262,7 +268,6 @@ which means that its persistent state will be deleted once
|
||||
for and appear in query and collection results. See
|
||||
the section on :ref:`Database and UnitOfWork Out-Of-Sync <workingobjects_database_uow_outofsync>`
|
||||
for more information.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
@@ -275,16 +280,15 @@ Example:
|
||||
The semantics of the remove operation, applied to an entity X are
|
||||
as follows:
|
||||
|
||||
|
||||
- If X is a new entity, it is ignored by the remove operation.
|
||||
However, the remove operation is cascaded to entities referenced by
|
||||
X, if the relationship from X to these other entities is mapped
|
||||
with cascade=REMOVE or cascade=ALL (see ":ref:`Transitive Persistence <transitive-persistence>`").
|
||||
with cascade=REMOVE or cascade=ALL (see ":ref:`transitive-persistence`").
|
||||
- If X is a managed entity, the remove operation causes it to
|
||||
become removed. The remove operation is cascaded to entities
|
||||
referenced by X, if the relationships from X to these other
|
||||
entities is mapped with cascade=REMOVE or cascade=ALL (see
|
||||
":ref:`Transitive Persistence <transitive-persistence>`").
|
||||
":ref:`transitive-persistence`").
|
||||
- If X is a detached entity, an InvalidArgumentException will be
|
||||
thrown.
|
||||
- If X is a removed entity, it is ignored by the remove operation.
|
||||
@@ -304,11 +308,10 @@ foreign key semantics of onDelete="CASCADE".
|
||||
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 2
|
||||
will fetch this association. If its a Single association it will
|
||||
will fetch this association. If it is 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()\`.
|
||||
´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
|
||||
@@ -326,133 +329,39 @@ in multiple ways with very different performance impacts.
|
||||
Detaching entities
|
||||
------------------
|
||||
|
||||
An entity is detached from an EntityManager and thus no longer
|
||||
managed by invoking the ``EntityManager#detach($entity)`` method on
|
||||
it or by cascading the detach operation to it. Changes made to the
|
||||
detached entity, if any (including removal of the entity), will not
|
||||
be synchronized to the database after the entity has been
|
||||
All entities are detached from an EntityManager and thus no longer
|
||||
managed by it after invoking the ``EntityManager#clear()`` method.
|
||||
Changes made to the detached entities, if any (including their removal),
|
||||
will not be synchronized to the database after they have been
|
||||
detached.
|
||||
|
||||
Doctrine will not hold on to any references to a detached entity.
|
||||
Doctrine will not hold on to any references to detached entities.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em->detach($entity);
|
||||
$em->clear();
|
||||
|
||||
The semantics of the detach operation, applied to an entity X are
|
||||
as follows:
|
||||
|
||||
|
||||
- If X is a managed entity, the detach operation causes it to
|
||||
become detached. The detach operation is cascaded to entities
|
||||
referenced by X, if the relationships from X to these other
|
||||
entities is mapped with cascade=DETACH or cascade=ALL (see
|
||||
":ref:`Transitive Persistence <transitive-persistence>`"). Entities which previously referenced X
|
||||
- If X is a managed entity, the ``clear`` operation causes it to
|
||||
become detached. Entities which previously referenced X
|
||||
will continue to reference X.
|
||||
- If X is a new or detached entity, it is ignored by the detach
|
||||
operation.
|
||||
- If X is a removed entity, the detach operation is cascaded to
|
||||
entities referenced by X, if the relationships from X to these
|
||||
other entities is mapped with cascade=DETACH or cascade=ALL (see
|
||||
":ref:`Transitive Persistence <transitive-persistence>`"). Entities which previously referenced X
|
||||
will continue to reference X.
|
||||
- If X is a removed entity, it will become detached, and therefore
|
||||
no longer scheduled to be removed. Entities which previously
|
||||
referenced X will continue to reference X.
|
||||
|
||||
There are several situations in which an entity is detached
|
||||
automatically without invoking the ``detach`` method:
|
||||
automatically:
|
||||
|
||||
|
||||
- When ``EntityManager#clear()`` is invoked, all entities that are
|
||||
currently managed by the EntityManager instance become detached.
|
||||
- When serializing an entity. The entity retrieved upon subsequent
|
||||
unserialization will be detached (This is the case for all entities
|
||||
that are serialized and stored in some cache, i.e. when using the
|
||||
Query Result Cache).
|
||||
|
||||
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.
|
||||
|
||||
.. note::
|
||||
|
||||
When you want to serialize/unserialize entities you
|
||||
have to make all entity properties protected, never private. The
|
||||
reason for this is, if you serialize a class that was a proxy
|
||||
instance before, the private variables won't be serialized and a
|
||||
PHP Notice is thrown.
|
||||
|
||||
|
||||
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 <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.
|
||||
|
||||
unserialization will be detached (this is the case for all entities
|
||||
that are serialized and stored in some cache).
|
||||
|
||||
Synchronization with the Database
|
||||
---------------------------------
|
||||
@@ -495,7 +404,6 @@ Synchronizing New and Managed Entities
|
||||
The flush operation applies to a managed entity with the following
|
||||
semantics:
|
||||
|
||||
|
||||
- The entity itself is synchronized to the database using a SQL
|
||||
UPDATE statement, only if at least one persistent field has
|
||||
changed.
|
||||
@@ -504,14 +412,12 @@ semantics:
|
||||
The flush operation applies to a new entity with the following
|
||||
semantics:
|
||||
|
||||
|
||||
- The entity itself is synchronized to the database using a SQL
|
||||
INSERT statement.
|
||||
|
||||
For all (initialized) relationships of the new or managed entity
|
||||
the following semantics apply to each associated entity X:
|
||||
|
||||
|
||||
- If X is new and persist operations are configured to cascade on
|
||||
the relationship, X will be persisted.
|
||||
- If X is new and no persist operations are configured to cascade
|
||||
@@ -543,7 +449,6 @@ The cost of flushing
|
||||
|
||||
How costly a flush operation is, mainly depends on two factors:
|
||||
|
||||
|
||||
- The size of the EntityManager's current UnitOfWork.
|
||||
- The configured change tracking policies
|
||||
|
||||
@@ -563,14 +468,13 @@ 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/refresh/... 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
|
||||
single HTTP request there should be usually no need for invoking
|
||||
``flush`` more than 0-2 times.
|
||||
|
||||
|
||||
Direct access to a Unit of Work
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -590,7 +494,6 @@ instance the EntityManager is currently using.
|
||||
marked as INTERNAL by not using them and carefully read the API
|
||||
documentation.
|
||||
|
||||
|
||||
Entity State
|
||||
~~~~~~~~~~~~
|
||||
|
||||
@@ -626,7 +529,7 @@ An entity is in DETACHED state if it has persistent state and
|
||||
identity but is currently not associated with an
|
||||
``EntityManager``.
|
||||
|
||||
An entity is in NEW state if has no persistent state and identity
|
||||
An entity is in NEW state if it has no persistent state and identity
|
||||
and is not associated with an ``EntityManager`` (for example those
|
||||
just created via the "new" operator).
|
||||
|
||||
@@ -680,13 +583,13 @@ methods on a repository as follows:
|
||||
|
||||
<?php
|
||||
// $em instanceof EntityManager
|
||||
|
||||
|
||||
// All users that are 20 years old
|
||||
$users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20));
|
||||
|
||||
|
||||
// All users that are 20 years old and have a surname of 'Miller'
|
||||
$users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20, 'surname' => 'Miller'));
|
||||
|
||||
|
||||
// A single user by its nickname
|
||||
$user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));
|
||||
|
||||
@@ -722,7 +625,7 @@ examples are equivalent:
|
||||
<?php
|
||||
// A single user by its nickname
|
||||
$user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));
|
||||
|
||||
|
||||
// A single user by its nickname (__call magic)
|
||||
$user = $em->getRepository('MyProject\Domain\User')->findOneByNickname('romanb');
|
||||
|
||||
@@ -739,8 +642,8 @@ By Criteria
|
||||
|
||||
.. versionadded:: 2.3
|
||||
|
||||
The Repository implement the ``Doctrine\Common\Collections\Selectable``
|
||||
interface. That means you can build ``Doctrine\Common\Collections\Criteria``
|
||||
The Repository implements the ``Doctrine\Common\Collections\Selectable``
|
||||
interface. It means you can build ``Doctrine\Common\Collections\Criteria``
|
||||
and pass them to the ``matching($criteria)`` method.
|
||||
|
||||
See section `Filtering collections` of chapter :doc:`Working with Associations <working-with-associations>`
|
||||
@@ -750,7 +653,7 @@ By Eager Loading
|
||||
|
||||
Whenever you query for an entity that has persistent associations
|
||||
and these associations are mapped as EAGER, they will automatically
|
||||
be loaded together with the entity being queried and is thus
|
||||
be loaded together with the entity being queried and are thus
|
||||
immediately available to your application.
|
||||
|
||||
By Lazy Loading
|
||||
@@ -780,7 +683,7 @@ A DQL query is represented by an instance of the
|
||||
|
||||
<?php
|
||||
// $em instanceof EntityManager
|
||||
|
||||
|
||||
// All users with an age between 20 and 30 (inclusive).
|
||||
$q = $em->createQuery("select u from MyDomain\Model\User u where u.age >= 20 and u.age <= 30");
|
||||
$users = $q->getResult();
|
||||
@@ -814,7 +717,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 Annotation, XML or YAML metadata. In large
|
||||
Repository in the Annotation 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.
|
||||
@@ -823,23 +726,23 @@ in a central location.
|
||||
|
||||
<?php
|
||||
namespace MyDomain\Model;
|
||||
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass="MyDomain\Model\UserRepository")
|
||||
*/
|
||||
class User
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
class UserRepository extends EntityRepository
|
||||
{
|
||||
public function getAllAdminUsers()
|
||||
{
|
||||
return $this->_em->createQuery('SELECT u FROM MyDomain\Model\User u WHERE u.status = "admin"')
|
||||
return $this->em->createQuery('SELECT u FROM MyDomain\Model\User u WHERE u.status = "admin"')
|
||||
->getResult();
|
||||
}
|
||||
}
|
||||
@@ -850,7 +753,6 @@ You can access your repository now by calling:
|
||||
|
||||
<?php
|
||||
// $em instanceof EntityManager
|
||||
|
||||
|
||||
$admins = $em->getRepository('MyDomain\Model\User')->getAllAdminUsers();
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ form of XML documents.
|
||||
The XML driver is backed by an XML Schema document that describes
|
||||
the structure of a mapping document. The most recent version of the
|
||||
XML Schema document is available online at
|
||||
`http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd <http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd>`_.
|
||||
`https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd <https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd>`_.
|
||||
In order to point to the latest version of the document of a
|
||||
particular stable release branch, just append the release number,
|
||||
i.e.: doctrine-mapping-2.0.xsd The most convenient way to work with
|
||||
@@ -21,7 +21,7 @@ setup for the latest code in trunk.
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
...
|
||||
|
||||
@@ -31,7 +31,6 @@ The XML 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 XML
|
||||
mapping document.
|
||||
- The name of the mapping document must consist of the fully
|
||||
@@ -107,7 +106,7 @@ of several common elements:
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Doctrine\Tests\ORM\Mapping\User" table="cms_users">
|
||||
|
||||
@@ -194,12 +193,10 @@ specified as the ``<entity />`` element as a direct child of the
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- name - The fully qualified class-name of the entity.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **table** - The Table-Name to be used for this entity. Otherwise the
|
||||
Unqualified Class-Name is used by default.
|
||||
- **repository-class** - The fully qualified class-name of an
|
||||
@@ -241,13 +238,11 @@ entity. For the ID mapping you have to use the ``<id />`` element.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- name - The name of the Property/Field on the given Entity PHP
|
||||
class.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- type - The ``Doctrine\DBAL\Types\Type`` name, defaults to
|
||||
"string"
|
||||
- column - Name of the column in the database, defaults to the
|
||||
@@ -305,7 +300,6 @@ subset of the ``<field />`` element attributes:
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- name - The name of the Property/Field on the given Entity PHP
|
||||
class.
|
||||
- type - The ``Doctrine\DBAL\Types\Type`` name, preferably
|
||||
@@ -313,7 +307,6 @@ Required attributes:
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- column - Name of the column in the database, defaults to the
|
||||
field name.
|
||||
|
||||
@@ -339,7 +332,6 @@ have to use the ``NONE`` strategy.
|
||||
The following values are allowed for the ``<generator />`` strategy
|
||||
attribute:
|
||||
|
||||
|
||||
- AUTO - Automatic detection of the identifier strategy based on
|
||||
the preferred solution of the database vendor.
|
||||
- IDENTITY - Use of a IDENTIFY strategy such as Auto-Increment IDs
|
||||
@@ -362,12 +354,10 @@ element to describe the sequence:
|
||||
|
||||
Required attributes for ``<sequence-generator />``:
|
||||
|
||||
|
||||
- sequence-name - The name of the sequence
|
||||
|
||||
Optional attributes for ``<sequence-generator />``:
|
||||
|
||||
|
||||
- allocation-size - By how much steps should the sequence be
|
||||
incremented when a value is retrieved. Defaults to 1
|
||||
- initial-value - What should the initial value of the sequence
|
||||
@@ -380,7 +370,6 @@ Optional attributes for ``<sequence-generator />``:
|
||||
element, if Doctrine chooses the sequence strategy for a
|
||||
platform.
|
||||
|
||||
|
||||
Defining a Mapped Superclass
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -400,7 +389,6 @@ can define it in XML using the ``<mapped-superclass />`` tag.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- name - Class name of the mapped superclass.
|
||||
|
||||
You can nest any number of ``<field />`` and unidirectional
|
||||
@@ -440,7 +428,6 @@ The allowed values for inheritance-type attribute are ``JOINED`` or
|
||||
All inheritance related definitions have to be defined on the root
|
||||
entity of the hierarchy.
|
||||
|
||||
|
||||
Defining Lifecycle Callbacks
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -473,7 +460,6 @@ For the inverse side the mapping is as simple as:
|
||||
|
||||
Required attributes for inverse One-To-One:
|
||||
|
||||
|
||||
- field - Name of the property/field on the entity's PHP class.
|
||||
- target-entity - Name of the entity associated entity class. If
|
||||
this is not qualified the namespace of the current class is
|
||||
@@ -491,7 +477,6 @@ For the owning side this mapping would look like:
|
||||
|
||||
Required attributes for owning One-to-One:
|
||||
|
||||
|
||||
- field - Name of the property/field on the entity's PHP class.
|
||||
- target-entity - Name of the entity associated entity class. If
|
||||
this is not qualified the namespace of the current class is
|
||||
@@ -499,7 +484,6 @@ Required attributes for owning One-to-One:
|
||||
|
||||
Optional attributes for owning One-to-One:
|
||||
|
||||
|
||||
- inversed-by - If the association is bidirectional the
|
||||
inversed-by attribute has to be specified with the name of the
|
||||
field on the inverse entity that contains the back-reference.
|
||||
@@ -542,7 +526,6 @@ like:
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- field - Name of the property/field on the entity's PHP class.
|
||||
- target-entity - Name of the entity associated entity class. If
|
||||
this is not qualified the namespace of the current class is
|
||||
@@ -550,7 +533,6 @@ Required attributes:
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- inversed-by - If the association is bidirectional the
|
||||
inversed-by attribute has to be specified with the name of the
|
||||
field on the inverse entity that contains the back-reference.
|
||||
@@ -593,7 +575,6 @@ exists for bi-directional associations.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- field - Name of the property/field on the entity's PHP class.
|
||||
- target-entity - Name of the entity associated entity class. If
|
||||
this is not qualified the namespace of the current class is
|
||||
@@ -603,7 +584,6 @@ Required attributes:
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- fetch - Either LAZY, EXTRA_LAZY or EAGER, defaults to LAZY.
|
||||
- index-by: Index the collection by a field on the target entity.
|
||||
|
||||
@@ -622,7 +602,6 @@ definitions and rely on their implicit values.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- field - Name of the property/field on the entity's PHP class.
|
||||
- target-entity - Name of the entity associated entity class. If
|
||||
this is not qualified the namespace of the current class is
|
||||
@@ -630,7 +609,6 @@ Required attributes:
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- mapped-by - Name of the field on the owning side that contains
|
||||
the owning side association if the defined many-to-many association
|
||||
is on the inverse side.
|
||||
@@ -686,9 +664,7 @@ tags.
|
||||
Besides ``<cascade-all />`` the following operations can be
|
||||
specified by their respective tags:
|
||||
|
||||
|
||||
- ``<cascade-persist />``
|
||||
- ``<cascade-merge />``
|
||||
- ``<cascade-remove />``
|
||||
- ``<cascade-refresh />``
|
||||
|
||||
@@ -701,14 +677,12 @@ key names are called that are used for joining two entities.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- name - The column name of the foreign key.
|
||||
- referenced-column-name - The column name of the associated
|
||||
entities primary key
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- unique - If the join column should contain a UNIQUE constraint.
|
||||
This makes sense for Many-To-Many join-columns only to simulate a
|
||||
one-to-many unidirectional using a join-table.
|
||||
@@ -768,7 +742,7 @@ entity relationship. You can define this in XML with the "association-key" attri
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Application\Model\ArticleAttribute">
|
||||
<id name="article" association-key="true" />
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
YAML Mapping
|
||||
============
|
||||
|
||||
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 ]
|
||||
|
||||
82
docs/en/sidebar.rst
Normal file
82
docs/en/sidebar.rst
Normal file
@@ -0,0 +1,82 @@
|
||||
.. toc::
|
||||
|
||||
.. tocheader:: Tutorials
|
||||
|
||||
.. 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
|
||||
|
||||
.. toc::
|
||||
|
||||
.. tocheader:: Reference
|
||||
|
||||
.. 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/xml-mapping
|
||||
reference/annotations-reference
|
||||
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/implementing-wakeup-or-clone
|
||||
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
|
||||
@@ -1,87 +0,0 @@
|
||||
Welcome to Doctrine 2 ORM's documentation!
|
||||
==========================================
|
||||
|
||||
Tutorials
|
||||
---------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
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.rst
|
||||
tutorials/embeddables.rst
|
||||
|
||||
Reference Guide
|
||||
---------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:numbered:
|
||||
|
||||
reference/architecture
|
||||
reference/configuration.rst
|
||||
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/xml-mapping
|
||||
reference/yaml-mapping
|
||||
reference/annotations-reference
|
||||
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
|
||||
|
||||
|
||||
Cookbook
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
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/implementing-wakeup-or-clone
|
||||
cookbook/integrating-with-codeigniter
|
||||
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
|
||||
|
||||
@@ -28,16 +28,19 @@ and year of production as primary keys:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
namespace VehicleCatalogue\Model;
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Car
|
||||
{
|
||||
/** @Id @Column(type="string") */
|
||||
/** @ORM\Id @ORM\Column(type="string") */
|
||||
private $name;
|
||||
/** @Id @Column(type="integer") */
|
||||
/** @ORM\Id @ORM\Column(type="integer") */
|
||||
private $year;
|
||||
|
||||
public function __construct($name, $year)
|
||||
@@ -63,7 +66,7 @@ and year of production as primary keys:
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="VehicleCatalogue\Model\Car">
|
||||
<id field="name" type="string" />
|
||||
@@ -71,16 +74,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
|
||||
@@ -137,9 +130,8 @@ of one or many parent entities.
|
||||
The semantics of mapping identity through foreign entities are easy:
|
||||
|
||||
- Only allowed on Many-To-One or One-To-One associations.
|
||||
- Plug an ``@Id`` annotation onto every association.
|
||||
- Plug an ``@ORM\Id`` annotation 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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -154,19 +146,21 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
|
||||
namespace Application\Model;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
|
||||
private $id;
|
||||
/** @Column(type="string") */
|
||||
|
||||
/** @ORM\Column(type="string") */
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="ArticleAttribute", mappedBy="article", cascade={"ALL"}, indexBy="attribute")
|
||||
* @ORM\OneToMany(targetEntity="ArticleAttribute", mappedBy="article", cascade={"ALL"}, indexBy="attribute")
|
||||
*/
|
||||
private $attributes;
|
||||
|
||||
@@ -177,17 +171,17 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class ArticleAttribute
|
||||
{
|
||||
/** @Id @ManyToOne(targetEntity="Article", inversedBy="attributes") */
|
||||
/** @ORM\Id @ORM\ManyToOne(targetEntity="Article", inversedBy="attributes") */
|
||||
private $article;
|
||||
|
||||
/** @Id @Column(type="string") */
|
||||
/** @ORM\Id @ORM\Column(type="string") */
|
||||
private $attribute;
|
||||
|
||||
/** @Column(type="string") */
|
||||
/** @ORM\Column(type="string") */
|
||||
private $value;
|
||||
|
||||
public function __construct($name, $value, $article)
|
||||
@@ -203,12 +197,12 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Application\Model\ArticleAttribute">
|
||||
<id name="article" association-key="true" />
|
||||
<id name="attribute" type="string" />
|
||||
|
||||
|
||||
<field name="value" type="string" />
|
||||
|
||||
<many-to-one field="article" target-entity="Article" inversed-by="attributes" />
|
||||
@@ -216,24 +210,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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -246,44 +222,27 @@ One good example for this is a user-address relationship:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
|
||||
private $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Address
|
||||
{
|
||||
/** @Id @OneToOne(targetEntity="User") */
|
||||
/** @ORM\Id @ORM\OneToOne(targetEntity="User") */
|
||||
private $user;
|
||||
}
|
||||
|
||||
.. 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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -295,23 +254,27 @@ of products purchased and maybe even the current price.
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @Entity */
|
||||
/** @ORM\Entity */
|
||||
class Order
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
|
||||
private $id;
|
||||
|
||||
/** @ManyToOne(targetEntity="Customer") */
|
||||
/** @ORM\ManyToOne(targetEntity="Customer") */
|
||||
private $customer;
|
||||
/** @OneToMany(targetEntity="OrderItem", mappedBy="order") */
|
||||
|
||||
/** @ORM\OneToMany(targetEntity="OrderItem", mappedBy="order") */
|
||||
private $items;
|
||||
|
||||
/** @Column(type="boolean") */
|
||||
private $payed = false;
|
||||
/** @Column(type="boolean") */
|
||||
/** @ORM\Column(type="boolean") */
|
||||
private $paid = false;
|
||||
|
||||
/** @ORM\Column(type="boolean") */
|
||||
private $shipped = false;
|
||||
/** @Column(type="datetime") */
|
||||
|
||||
/** @ORM\Column(type="datetime") */
|
||||
private $created;
|
||||
|
||||
public function __construct(Customer $customer)
|
||||
@@ -322,16 +285,16 @@ of products purchased and maybe even the current price.
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
/** @ORM\Entity */
|
||||
class Product
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
|
||||
private $id;
|
||||
|
||||
/** @Column(type="string") */
|
||||
/** @ORM\Column(type="string") */
|
||||
private $name;
|
||||
|
||||
/** @Column(type="decimal") */
|
||||
/** @ORM\Column(type="decimal") */
|
||||
private $currentPrice;
|
||||
|
||||
public function getCurrentPrice()
|
||||
@@ -340,19 +303,19 @@ of products purchased and maybe even the current price.
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
/** @ORM\Entity */
|
||||
class OrderItem
|
||||
{
|
||||
/** @Id @ManyToOne(targetEntity="Order") */
|
||||
/** @ORM\Id @ORM\ManyToOne(targetEntity="Order") */
|
||||
private $order;
|
||||
|
||||
/** @Id @ManyToOne(targetEntity="Product") */
|
||||
/** @ORM\Id @ORM\ManyToOne(targetEntity="Product") */
|
||||
private $product;
|
||||
|
||||
/** @Column(type="integer") */
|
||||
/** @ORM\Column(type="integer") */
|
||||
private $amount = 1;
|
||||
|
||||
/** @Column(type="decimal") */
|
||||
/** @ORM\Column(type="decimal") */
|
||||
private $offeredPrice;
|
||||
|
||||
public function __construct(Order $order, Product $product, $amount = 1)
|
||||
@@ -363,7 +326,6 @@ of products purchased and maybe even the current price.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Performance Considerations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Separating Concerns using Embeddables
|
||||
Embeddables are classes which are not entities themselves, but are embedded
|
||||
in entities and can also be queried in DQL. You'll mostly want to use them
|
||||
to reduce duplication or separating concerns. Value objects such as date range
|
||||
or address are the primary use case for this feature.
|
||||
or address are the primary use case for this feature.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -21,26 +21,28 @@ instead of simply adding the respective columns to the ``User`` class.
|
||||
|
||||
<?php
|
||||
|
||||
/** @Entity */
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\Entity */
|
||||
class User
|
||||
{
|
||||
/** @Embedded(class = "Address") */
|
||||
/** @ORM\Embedded(class = "Address") */
|
||||
private $address;
|
||||
}
|
||||
|
||||
/** @Embeddable */
|
||||
/** @ORM\Embeddable */
|
||||
class Address
|
||||
{
|
||||
/** @Column(type = "string") */
|
||||
/** @ORM\Column(type = "string") */
|
||||
private $street;
|
||||
|
||||
/** @Column(type = "string") */
|
||||
/** @ORM\Column(type = "string") */
|
||||
private $postalCode;
|
||||
|
||||
/** @Column(type = "string") */
|
||||
/** @ORM\Column(type = "string") */
|
||||
private $city;
|
||||
|
||||
/** @Column(type = "string") */
|
||||
/** @ORM\Column(type = "string") */
|
||||
private $country;
|
||||
}
|
||||
|
||||
@@ -59,22 +61,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.
|
||||
@@ -113,10 +99,12 @@ The following example shows you how to set your prefix to ``myPrefix_``:
|
||||
|
||||
<?php
|
||||
|
||||
/** @Entity */
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\Entity */
|
||||
class User
|
||||
{
|
||||
/** @Embedded(class = "Address", columnPrefix = "myPrefix_") */
|
||||
/** @ORM\Embedded(class = "Address", columnPrefix = "myPrefix_") */
|
||||
private $address;
|
||||
}
|
||||
|
||||
@@ -126,15 +114,6 @@ The following example shows you how to set your prefix to ``myPrefix_``:
|
||||
<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):
|
||||
|
||||
@@ -144,29 +123,21 @@ directly, set ``columnPrefix=false`` (``use-column-prefix="false"`` for XML):
|
||||
|
||||
<?php
|
||||
|
||||
/** @Entity */
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\Entity */
|
||||
class User
|
||||
{
|
||||
/** @Embedded(class = "Address", columnPrefix = false) */
|
||||
/** @ORM\Embedded(class = "Address", columnPrefix = false) */
|
||||
private $address;
|
||||
}
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
embedded:
|
||||
address:
|
||||
class: Address
|
||||
columnPrefix: false
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<entity name="User">
|
||||
<embedded name="address" class="Address" use-column-prefix="false" />
|
||||
</entity>
|
||||
|
||||
|
||||
DQL
|
||||
---
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ Additionally even with Doctrine 2.0 the following methods do not trigger the col
|
||||
With extra lazy collections you can now not only add entities to large collections but also paginate them
|
||||
easily using a combination of ``count`` and ``slice``.
|
||||
|
||||
|
||||
Enabling Extra-Lazy Associations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -46,15 +45,18 @@ switch to extra lazy as shown in these examples:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\CMS;
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class CmsGroup
|
||||
{
|
||||
/**
|
||||
* @ManyToMany(targetEntity="CmsUser", mappedBy="groups", fetch="EXTRA_LAZY")
|
||||
* @ORM\ManyToMany(targetEntity="CmsUser", mappedBy="groups", fetch="EXTRA_LAZY")
|
||||
*/
|
||||
public $users;
|
||||
}
|
||||
@@ -65,22 +67,10 @@ switch to extra lazy as shown in these examples:
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Doctrine\Tests\Models\CMS\CmsGroup">
|
||||
<!-- ... -->
|
||||
<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
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Getting Started: Database First
|
||||
start with developing Objects and then map them onto your database. When
|
||||
you :doc:`Model First <getting-started-models>`, you are modelling your application using tools (for
|
||||
example UML) and generate database schema and PHP code from this model.
|
||||
When you have a :doc:`Database First <getting-started-database>`, you already have a database schema
|
||||
When you have a Database First, you already have a database schema
|
||||
and generate the corresponding PHP code from it.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -5,7 +5,7 @@ Getting Started: Model First
|
||||
|
||||
When you :doc:`Code First <getting-started>`, you
|
||||
start with developing Objects and then map them onto your database. When
|
||||
you :doc:`Model First <getting-started-models>`, you are modelling your application using tools (for
|
||||
you Model First, you are modelling your application using tools (for
|
||||
example UML) and generate database schema and PHP code from this model.
|
||||
When you have a :doc:`Database First <getting-started-database>`, then you already have a database schema
|
||||
and generate the corresponding PHP code from it.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,26 +5,29 @@ There are use-cases when you'll want to sort collections when they are
|
||||
retrieved from the database. In userland you do this as long as you
|
||||
haven't initially saved an entity with its associations into the
|
||||
database. To retrieve a sorted collection from the database you can
|
||||
use the ``@OrderBy`` annotation with an collection that specifies
|
||||
use the ``@ORM\OrderBy`` annotation with an collection that specifies
|
||||
an DQL snippet that is appended to all queries with this
|
||||
collection.
|
||||
|
||||
Additional to any ``@OneToMany`` or ``@ManyToMany`` annotation you
|
||||
can specify the ``@OrderBy`` in the following way:
|
||||
Additional to any ``@ORM\OneToMany`` or ``@ORM\ManyToMany`` annotation
|
||||
you can specify the ``@ORM\OrderBy`` in the following way:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity **/
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\Entity **/
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity="Group")
|
||||
* @OrderBy({"name" = "ASC"})
|
||||
* @ORM\ManyToMany(targetEntity="Group")
|
||||
* @ORM\OrderBy({"name" = "ASC"})
|
||||
**/
|
||||
private $groups;
|
||||
}
|
||||
@@ -41,39 +44,21 @@ 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 (,).
|
||||
The referenced field names have to exist on the ``targetEntity``
|
||||
class of the ``@ManyToMany`` or ``@OneToMany`` annotation.
|
||||
class of the ``@ORM\ManyToMany`` or ``@ORM\OneToMany`` annotation.
|
||||
|
||||
The semantics of this feature can be described as follows.
|
||||
The semantics of this feature can be described as follows:
|
||||
|
||||
|
||||
- ``@OrderBy`` acts as an implicit ORDER BY clause for the given
|
||||
- ``@ORM\OrderBy`` acts as an implicit ORDER BY clause for the given
|
||||
fields, that is appended to all the explicitly given ORDER BY
|
||||
items.
|
||||
- All collections of the ordered type are always retrieved in an
|
||||
ordered fashion.
|
||||
- To keep the database impact low, these implicit ORDER BY items
|
||||
are only added to an DQL Query if the collection is fetch joined in
|
||||
are only added to a DQL Query if the collection is fetch joined in
|
||||
the DQL query.
|
||||
|
||||
Given our previously defined example, the following would not add
|
||||
@@ -107,4 +92,3 @@ You can reverse the order with an explicit DQL ORDER BY:
|
||||
|
||||
SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC, g.name ASC
|
||||
|
||||
|
||||
|
||||
@@ -14,12 +14,15 @@ Suppose we have a class ExampleEntityWithOverride. This class uses trait Example
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*
|
||||
* @AttributeOverrides({
|
||||
* @AttributeOverride(name="foo",
|
||||
* column=@Column(
|
||||
* @ORM\AttributeOverrides({
|
||||
* @ORM\AttributeOverride(name="foo",
|
||||
* column=@ORM\Column(
|
||||
* name = "foo_overridden",
|
||||
* type = "integer",
|
||||
* length = 140,
|
||||
@@ -29,9 +32,9 @@ Suppose we have a class ExampleEntityWithOverride. This class uses trait Example
|
||||
* )
|
||||
* })
|
||||
*
|
||||
* @AssociationOverrides({
|
||||
* @AssociationOverride(name="bar",
|
||||
* joinColumns=@JoinColumn(
|
||||
* @ORM\AssociationOverrides({
|
||||
* @ORM\AssociationOverride(name="bar",
|
||||
* joinColumns=@ORM\JoinColumn(
|
||||
* name="example_entity_overridden_bar_id", referencedColumnName="id"
|
||||
* )
|
||||
* )
|
||||
@@ -43,11 +46,11 @@ Suppose we have a class ExampleEntityWithOverride. This class uses trait Example
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Bar
|
||||
{
|
||||
/** @Id @Column(type="string") */
|
||||
/** @ORM\Id @ORM\Column(type="string") */
|
||||
private $id;
|
||||
}
|
||||
|
||||
@@ -59,22 +62,25 @@ which has mapping metadata that is overridden by the annotation above:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* Trait class
|
||||
*/
|
||||
trait ExampleTrait
|
||||
{
|
||||
/** @Id @Column(type="string") */
|
||||
/** @ORM\Id @ORM\Column(type="string") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Column(name="trait_foo", type="integer", length=100, nullable=true, unique=true)
|
||||
* @ORM\Column(name="trait_foo", type="integer", length=100, nullable=true, unique=true)
|
||||
*/
|
||||
protected $foo;
|
||||
|
||||
/**
|
||||
* @OneToOne(targetEntity="Bar", cascade={"persist", "merge"})
|
||||
* @JoinColumn(name="example_trait_bar_id", referencedColumnName="id")
|
||||
* @ORM\OneToOne(targetEntity="Bar", cascade={"persist", "refresh"})
|
||||
* @ORM\JoinColumn(name="example_trait_bar_id", referencedColumnName="id")
|
||||
*/
|
||||
protected $bar;
|
||||
}
|
||||
@@ -89,4 +95,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>`).
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user