mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 15:02:22 +01:00
Compare commits
821 Commits
old-protot
...
2.9.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75b4b88c5b | ||
|
|
d9e59d6862 | ||
|
|
5fa94969de | ||
|
|
f2c3ddac97 | ||
|
|
46f0da9ffa | ||
|
|
1e832a6782 | ||
|
|
56bdb44efd | ||
|
|
fffac44991 | ||
|
|
e42b3d6584 | ||
|
|
7ab2c3abbd | ||
|
|
498c816b65 | ||
|
|
eec740079d | ||
|
|
c359715a97 | ||
|
|
f3e55fae9f | ||
|
|
91c3bd4121 | ||
|
|
e6cf12c66f | ||
|
|
99d67cb77d | ||
|
|
43f66d5808 | ||
|
|
a6577b89a2 | ||
|
|
0ca87566a9 | ||
|
|
5d01f94a36 | ||
|
|
3d02b02636 | ||
|
|
6de321cb09 | ||
|
|
535bc92dc8 | ||
|
|
ebb5d03f7a | ||
|
|
8e13369621 | ||
|
|
8eff4b775a | ||
|
|
b85403d0a2 | ||
|
|
22ce3adfce | ||
|
|
3a194ad699 | ||
|
|
d52dab54dd | ||
|
|
b5ac7714bc | ||
|
|
590551d5c3 | ||
|
|
c9fb9fdb40 | ||
|
|
965926dcc8 | ||
|
|
a6e30c5f4c | ||
|
|
30ab6f4cea | ||
|
|
5e5a44dce2 | ||
|
|
d7bf30b291 | ||
|
|
ce8da6623f | ||
|
|
2ecec0c5d6 | ||
|
|
6f128e4515 | ||
|
|
e24b0f0be7 | ||
|
|
6753b26f73 | ||
|
|
4ccc4e19fc | ||
|
|
4e2009433b | ||
|
|
c25b822217 | ||
|
|
c3dcc5af91 | ||
|
|
b2f404b25f | ||
|
|
d141f27875 | ||
|
|
4691839201 | ||
|
|
91387382b7 | ||
|
|
f634c64b7a | ||
|
|
7ba9c980b5 | ||
|
|
dacdcf2c7b | ||
|
|
f296fee9e4 | ||
|
|
8555fc1d34 | ||
|
|
b0826fd746 | ||
|
|
fe93c2e9d5 | ||
|
|
850d57827f | ||
|
|
e1388fa986 | ||
|
|
9a48450481 | ||
|
|
cff8b96dd6 | ||
|
|
996c1c74b3 | ||
|
|
48612e6dc6 | ||
|
|
ddfee26f80 | ||
|
|
eb860a704e | ||
|
|
51ffcb4891 | ||
|
|
72f500318a | ||
|
|
55f030f66b | ||
|
|
95af30eb72 | ||
|
|
9ea0769d78 | ||
|
|
22413453da | ||
|
|
06fadcdd8c | ||
|
|
7c56aa2141 | ||
|
|
4cdcb5f760 | ||
|
|
b542b36e45 | ||
|
|
e5a7a13e1e | ||
|
|
8336dd3779 | ||
|
|
b04d7a62ae | ||
|
|
a959a474fd | ||
|
|
ce128e742b | ||
|
|
dac87dae06 | ||
|
|
a2230485b2 | ||
|
|
a68aa580c5 | ||
|
|
5ee71c54d4 | ||
|
|
dc37c2cd2f | ||
|
|
261a405970 | ||
|
|
1ea51d88c4 | ||
|
|
da3a9fa361 | ||
|
|
4fd81d26ff | ||
|
|
f8e06ad31e | ||
|
|
559c1ba806 | ||
|
|
4665758c44 | ||
|
|
e2e9f8fa97 | ||
|
|
f7249ec709 | ||
|
|
87dbcca454 | ||
|
|
ceeea8ccd1 | ||
|
|
6e16ef8c31 | ||
|
|
305e0d6664 | ||
|
|
199be94e6d | ||
|
|
09a7d9f18a | ||
|
|
f57f33b67f | ||
|
|
e86cddb360 | ||
|
|
fa588af3b1 | ||
|
|
d4741720fa | ||
|
|
343385d060 | ||
|
|
6d04dced03 | ||
|
|
22fa3a8556 | ||
|
|
eb05756dc3 | ||
|
|
5bb7e20708 | ||
|
|
a9076313c7 | ||
|
|
2a87821b28 | ||
|
|
da5877d60c | ||
|
|
67dfe8e1af | ||
|
|
2dfe51b396 | ||
|
|
5ac036de02 | ||
|
|
fda0d7b440 | ||
|
|
23e1fd8ad6 | ||
|
|
f8fa0fe069 | ||
|
|
a588555ecd | ||
|
|
501057da83 | ||
|
|
7de84537f6 | ||
|
|
97f8325dad | ||
|
|
0ebd7052d7 | ||
|
|
5d73378b92 | ||
|
|
10572ec441 | ||
|
|
76278d801d | ||
|
|
ca80830b26 | ||
|
|
1ed89c756a | ||
|
|
bb078b5cb7 | ||
|
|
bcb4889a2c | ||
|
|
961da8b0cc | ||
|
|
657a30f8ce | ||
|
|
c3f8996af5 | ||
|
|
0655083e50 | ||
|
|
0b25d4d8b0 | ||
|
|
a88242ee6c | ||
|
|
fe4964008d | ||
|
|
3f3de70c3e | ||
|
|
eb4e317144 | ||
|
|
c8f2f61ea1 | ||
|
|
c9502d3d0b | ||
|
|
b6b3c97436 | ||
|
|
8f6d146bc4 | ||
|
|
3358ccde39 | ||
|
|
1f4e6ebeeb | ||
|
|
a94db4f5c0 | ||
|
|
47475f3a67 | ||
|
|
61c4a5da0a | ||
|
|
dd34bca4eb | ||
|
|
3e21c50f61 | ||
|
|
bc3592bcc8 | ||
|
|
4fccec1322 | ||
|
|
0177133385 | ||
|
|
8df5cb84fa | ||
|
|
3b7275e183 | ||
|
|
5247c56fce | ||
|
|
cc37c490c2 | ||
|
|
95824efd61 | ||
|
|
44d4712e64 | ||
|
|
930f44c02f | ||
|
|
6e3c011e65 | ||
|
|
a82de0d422 | ||
|
|
9917488179 | ||
|
|
93f31d2c33 | ||
|
|
77356b954f | ||
|
|
92f764206e | ||
|
|
141539673e | ||
|
|
23dc804c9b | ||
|
|
9e3baa7baa | ||
|
|
322ea51ecf | ||
|
|
21b046452b | ||
|
|
c57b81ada4 | ||
|
|
4fa7c9c6de | ||
|
|
2685b65c2b | ||
|
|
3902a4eb6e | ||
|
|
b3ed525d4d | ||
|
|
3580517aac | ||
|
|
4cdc6b1a71 | ||
|
|
38ccbd8638 | ||
|
|
f9e7c3c2d8 | ||
|
|
3600c0fbca | ||
|
|
f779513042 | ||
|
|
b4e6530d2d | ||
|
|
07d426edf5 | ||
|
|
4afd4069be | ||
|
|
239215c2e5 | ||
|
|
e6f11652d2 | ||
|
|
2910a73927 | ||
|
|
3959b2743c | ||
|
|
658e54027e | ||
|
|
ba882451b0 | ||
|
|
1ed9840123 | ||
|
|
71044894a1 | ||
|
|
1a41d6b87c | ||
|
|
5dfcb08999 | ||
|
|
284bd6fd03 | ||
|
|
e40ac3e1d0 | ||
|
|
0bce2472f2 | ||
|
|
89f57de884 | ||
|
|
ae19f40958 | ||
|
|
c2d69a3c48 | ||
|
|
6ce91dd37b | ||
|
|
57e6ba25c9 | ||
|
|
9d2e67bbb4 | ||
|
|
2dce5b20ad | ||
|
|
930859f803 | ||
|
|
a70c73ae3a | ||
|
|
074346b8d5 | ||
|
|
9ed4a8c043 | ||
|
|
9c917811e5 | ||
|
|
f883820257 | ||
|
|
a32045dd51 | ||
|
|
672b04a55d | ||
|
|
261334aca2 | ||
|
|
7f6ed094cd | ||
|
|
a792655813 | ||
|
|
fb71204910 | ||
|
|
b918661cf1 | ||
|
|
7971a53164 | ||
|
|
a175f96ae8 | ||
|
|
7c1cde6471 | ||
|
|
1ffc0cacf4 | ||
|
|
e979d0d50f | ||
|
|
553ea03079 | ||
|
|
149014879d | ||
|
|
b991c58988 | ||
|
|
ee9627b82e | ||
|
|
e3f03414f9 | ||
|
|
1f406fd3df | ||
|
|
0ae53a6703 | ||
|
|
49864a7f57 | ||
|
|
b747bf15ff | ||
|
|
5fe85bfc03 | ||
|
|
0dccf05ca8 | ||
|
|
ebae57eb96 | ||
|
|
30a7c2aa67 | ||
|
|
3a9b8fde9b | ||
|
|
4f864bc178 | ||
|
|
d76cbd755f | ||
|
|
4aece04ae7 | ||
|
|
f31dbf8d4e | ||
|
|
416f35dba9 | ||
|
|
c29370e061 | ||
|
|
4e0f6837d0 | ||
|
|
e45d212f02 | ||
|
|
8f62bd39b5 | ||
|
|
5e11afcdf1 | ||
|
|
f833222017 | ||
|
|
c7f39ebbde | ||
|
|
15f08ed006 | ||
|
|
b6fd4b5ef3 | ||
|
|
e2e59e94f5 | ||
|
|
5e4dae88f3 | ||
|
|
7312ddeda7 | ||
|
|
9a67b6f699 | ||
|
|
f59a0c349b | ||
|
|
4c8831f716 | ||
|
|
6fe388a705 | ||
|
|
172a8d9414 | ||
|
|
01ca442be7 | ||
|
|
01374ca2ab | ||
|
|
94e8b1d43c | ||
|
|
61d0f96c17 | ||
|
|
41729be80a | ||
|
|
6dbaa39016 | ||
|
|
58c95a92d1 | ||
|
|
4958180b02 | ||
|
|
d00dbf7e2d | ||
|
|
8312ff0cb5 | ||
|
|
17012f1fea | ||
|
|
324ac3972f | ||
|
|
fb9b9b276e | ||
|
|
323469cdb7 | ||
|
|
792a9a9149 | ||
|
|
0f655f9fb6 | ||
|
|
2d7acbd07f | ||
|
|
835030297a | ||
|
|
b06679cc14 | ||
|
|
2693a93aed | ||
|
|
8724589c6e | ||
|
|
4cc78d9478 | ||
|
|
424305ef38 | ||
|
|
9d01f6a45c | ||
|
|
7ed487b534 | ||
|
|
68d24288ce | ||
|
|
40f3925589 | ||
|
|
f92c3dba32 | ||
|
|
5abad7c0af | ||
|
|
bcbd4401b8 | ||
|
|
d6aca8e146 | ||
|
|
8f1911a4fe | ||
|
|
7f30cd3102 | ||
|
|
f01fe3e050 | ||
|
|
210c2ee6a4 | ||
|
|
497dfd1a84 | ||
|
|
d9f0e2a27f | ||
|
|
9a40ac6e2a | ||
|
|
1687d9c479 | ||
|
|
1a46ed8901 | ||
|
|
36d0352c01 | ||
|
|
15eacd787b | ||
|
|
5b3f9bdd7b | ||
|
|
e00dba94f4 | ||
|
|
ab0e4007a5 | ||
|
|
32266c54f9 | ||
|
|
dd2120cd41 | ||
|
|
8991df0785 | ||
|
|
ca31923a39 | ||
|
|
68bc00b6c6 | ||
|
|
5b55b8c6cf | ||
|
|
10f381bc95 | ||
|
|
40aa8fe5db | ||
|
|
5801474ba3 | ||
|
|
9dbd960631 | ||
|
|
544df89055 | ||
|
|
f0ad5f72b2 | ||
|
|
378944dd27 | ||
|
|
8b749642cd | ||
|
|
277b53a970 | ||
|
|
2febb4509a | ||
|
|
cbc252f3b7 | ||
|
|
21d2c88013 | ||
|
|
e7d33eb1a9 | ||
|
|
cab7a4558d | ||
|
|
242cf1a33d | ||
|
|
da225a0db8 | ||
|
|
3ef5a30102 | ||
|
|
418587bc25 | ||
|
|
01187c9260 | ||
|
|
35cf4810c1 | ||
|
|
404edd418b | ||
|
|
011d3c21eb | ||
|
|
3d46e07887 | ||
|
|
b1ac293a50 | ||
|
|
f4ebded63c | ||
|
|
51bc596502 | ||
|
|
95f1b48422 | ||
|
|
385b5a2f80 | ||
|
|
f7d8b155db | ||
|
|
fa6fe09647 | ||
|
|
539ffea390 | ||
|
|
c270eba678 | ||
|
|
2f0eb95c90 | ||
|
|
b13b2e8bab | ||
|
|
4bfc84f035 | ||
|
|
ca27cc3f72 | ||
|
|
53dc5b2ac3 | ||
|
|
f1219f1418 | ||
|
|
072066f746 | ||
|
|
5fde5801c1 | ||
|
|
18d96fcc02 | ||
|
|
4d2908a065 | ||
|
|
8d250f5921 | ||
|
|
59fd9b5ea7 | ||
|
|
8fcc70cfbe | ||
|
|
7d84a49980 | ||
|
|
bb64fc953d | ||
|
|
e0eb82a3b1 | ||
|
|
79cdcde9ec | ||
|
|
f4524a8bb0 | ||
|
|
f1365b78d5 | ||
|
|
d810ea4111 | ||
|
|
107ba93d79 | ||
|
|
706670215d | ||
|
|
ab2b4987b3 | ||
|
|
717ef9106c | ||
|
|
ccae8f7176 | ||
|
|
da18985aca | ||
|
|
60cd524443 | ||
|
|
045d1f3bf2 | ||
|
|
1ae6f18fe9 | ||
|
|
1e2ed07731 | ||
|
|
424241f29c | ||
|
|
8230afcde9 | ||
|
|
7cffba8743 | ||
|
|
91b9dd90f4 | ||
|
|
7e5fe79349 | ||
|
|
efd25484f4 | ||
|
|
271f3480c8 | ||
|
|
aab589b596 | ||
|
|
190218b267 | ||
|
|
181114f2c7 | ||
|
|
3689b76a86 | ||
|
|
75fe18ea5f | ||
|
|
6c73a6b720 | ||
|
|
775d91c2a3 | ||
|
|
64c3f68734 | ||
|
|
2a2a0b2980 | ||
|
|
505d658e3d | ||
|
|
a438e90046 | ||
|
|
6a670d7d6d | ||
|
|
765521d257 | ||
|
|
5ced62bf83 | ||
|
|
bee8decd18 | ||
|
|
93867f8d77 | ||
|
|
6bce7e9cab | ||
|
|
4d8418fe6f | ||
|
|
d95e03ba66 | ||
|
|
825ceb6b7a | ||
|
|
de2e2a1d74 | ||
|
|
850d57e791 | ||
|
|
c3dd71704b | ||
|
|
6780a963f7 | ||
|
|
0b305e5bd3 | ||
|
|
4d172e2591 | ||
|
|
21a98234d0 | ||
|
|
061207861b | ||
|
|
8a9954e46c | ||
|
|
527fff53cc | ||
|
|
70fb1ecd78 | ||
|
|
73ec483e9d | ||
|
|
8d67eec812 | ||
|
|
a418cf6418 | ||
|
|
6138afdca9 | ||
|
|
dafe298ce5 | ||
|
|
58b8130ea1 | ||
|
|
3c91792dd8 | ||
|
|
a705f526fb | ||
|
|
a9b6b72017 | ||
|
|
cd905fff77 | ||
|
|
431d0a3c5e | ||
|
|
eb700405be | ||
|
|
9273057649 | ||
|
|
1da002ca2f | ||
|
|
e04a79526e | ||
|
|
d157a6cbeb | ||
|
|
ca57222010 | ||
|
|
9bb2bf0cce | ||
|
|
445796af0e | ||
|
|
ab93285284 | ||
|
|
ef639d4de6 | ||
|
|
31f4dd671a | ||
|
|
a692670469 | ||
|
|
58677c29b4 | ||
|
|
60c4867ed3 | ||
|
|
9a0fcb5a86 | ||
|
|
0ee1716b26 | ||
|
|
8104c65d6c | ||
|
|
a64d254d07 | ||
|
|
a236a83fa8 | ||
|
|
37f1bd7606 | ||
|
|
af4cb282ba | ||
|
|
ce4914ba0e | ||
|
|
fdad48278b | ||
|
|
fc94127d7f | ||
|
|
dea3e5df44 | ||
|
|
bdfd6c1677 | ||
|
|
5d7d3e99a0 | ||
|
|
3bc1096fd0 | ||
|
|
8e0157d97d | ||
|
|
a2f01f7ccc | ||
|
|
1767f4b8e7 | ||
|
|
401db453a2 | ||
|
|
6e59ec8f16 | ||
|
|
87e491465a | ||
|
|
8b588eceb2 | ||
|
|
edce36598f | ||
|
|
ca95b0ee13 | ||
|
|
20c46035d1 | ||
|
|
324aacfb54 | ||
|
|
1edfcabead | ||
|
|
2785cde792 | ||
|
|
d67e3e8b1b | ||
|
|
d629c4e487 | ||
|
|
4a4226213f | ||
|
|
0ce1440884 | ||
|
|
9aa28b4e33 | ||
|
|
5c2b6870bf | ||
|
|
4389b2c188 | ||
|
|
e481d9880b | ||
|
|
85528f28e2 | ||
|
|
5873242fb5 | ||
|
|
4aa09861dd | ||
|
|
24e9a7caaf | ||
|
|
d90df59118 | ||
|
|
f9103a7b41 | ||
|
|
9891477094 | ||
|
|
59e3a55110 | ||
|
|
c9e41d0aa7 | ||
|
|
f37c12834d | ||
|
|
041404e8b3 | ||
|
|
bfc68b3aba | ||
|
|
1e628370c4 | ||
|
|
ae2b9b1921 | ||
|
|
419df77a09 | ||
|
|
d6f6b2e97c | ||
|
|
75d5adf599 | ||
|
|
cfd6fadf9c | ||
|
|
2bf7916c52 | ||
|
|
253fd10cc0 | ||
|
|
2c956d55f2 | ||
|
|
3db992e953 | ||
|
|
6fc9b3ab16 | ||
|
|
2d833a5e86 | ||
|
|
a416a9a8b2 | ||
|
|
4d763ca4c9 | ||
|
|
398d74deaa | ||
|
|
3314322929 | ||
|
|
ce93817bf7 | ||
|
|
50992eafa2 | ||
|
|
9ccb8837e7 | ||
|
|
d959744c0a | ||
|
|
0264ba1759 | ||
|
|
8332fa1855 | ||
|
|
4fae126459 | ||
|
|
2d9b935183 | ||
|
|
4804f602f8 | ||
|
|
3d17290eb5 | ||
|
|
8420d24f90 | ||
|
|
52f2b37921 | ||
|
|
16751d210f | ||
|
|
686f508576 | ||
|
|
00ef1eba90 | ||
|
|
3843eee5cb | ||
|
|
f576e6c41f | ||
|
|
c79d2e0dc2 | ||
|
|
33b8d020a7 | ||
|
|
1b2daac25d | ||
|
|
977985f756 | ||
|
|
0c36f87935 | ||
|
|
e8f265d480 | ||
|
|
7bcbad076d | ||
|
|
57496e32fd | ||
|
|
797bfc53c4 | ||
|
|
8c47dcb6fc | ||
|
|
6347190886 | ||
|
|
9162f3519d | ||
|
|
fc9314d9f5 | ||
|
|
26806d08eb | ||
|
|
6a827d5b61 | ||
|
|
7d77984306 | ||
|
|
ec93014713 | ||
|
|
c83094bde0 | ||
|
|
982d1519db | ||
|
|
f7c04ae537 | ||
|
|
f9a4258ded | ||
|
|
eb9f11bf96 | ||
|
|
2b8cb9de79 | ||
|
|
570abb5bad | ||
|
|
855244fd10 | ||
|
|
c62977412c | ||
|
|
98e557b68e | ||
|
|
3a32c00dcf | ||
|
|
1dde2c9e8e | ||
|
|
adfd010a78 | ||
|
|
1bc4e1f594 | ||
|
|
21680df9bd | ||
|
|
19aa3c125c | ||
|
|
e9e012a037 | ||
|
|
d1db0655ac | ||
|
|
2d643e6b7b | ||
|
|
4d6b1f3e63 | ||
|
|
d9c30e34c4 | ||
|
|
90c1ee0bd0 | ||
|
|
cfcca3a63c | ||
|
|
af0949adab | ||
|
|
cdb652ad87 | ||
|
|
4fb1ebfc10 | ||
|
|
46c1b57560 | ||
|
|
fdbbf7edd1 | ||
|
|
2fed8204c1 | ||
|
|
76f03b5db0 | ||
|
|
9fef4e86e4 | ||
|
|
4781dc03e9 | ||
|
|
cc5f84ac22 | ||
|
|
023e94661a | ||
|
|
b59fc23f86 | ||
|
|
d71dd5d94f | ||
|
|
63513e9a05 | ||
|
|
c802bc46a5 | ||
|
|
506bf0ee12 | ||
|
|
a36809db72 | ||
|
|
5b00d7ba5e | ||
|
|
b22604352d | ||
|
|
00c6b1bc60 | ||
|
|
4b0d86ee92 | ||
|
|
3707c39124 | ||
|
|
fe72b00df2 | ||
|
|
79a7ecc92f | ||
|
|
16df8bfe0d | ||
|
|
b37ceaa9f7 | ||
|
|
c41fdbce8a | ||
|
|
7526adc80a | ||
|
|
766eb693fb | ||
|
|
f9e2ae3488 | ||
|
|
6bf2ff5d10 | ||
|
|
27fcc01d81 | ||
|
|
3ac1f8e680 | ||
|
|
b63db53552 | ||
|
|
bed8186573 | ||
|
|
f08ff83d0a | ||
|
|
7c8c0906be | ||
|
|
167cb44ea1 | ||
|
|
5d74bdb240 | ||
|
|
ca38249f6c | ||
|
|
6a74f373b9 | ||
|
|
b52ef5a100 | ||
|
|
ef783f7049 | ||
|
|
435d624d33 | ||
|
|
53775fe086 | ||
|
|
59f1679fed | ||
|
|
390d081fca | ||
|
|
37d1d57900 | ||
|
|
d7a537c941 | ||
|
|
cfe73cd74f | ||
|
|
d0e1da8c51 | ||
|
|
7fbe663ea0 | ||
|
|
409f2f5d82 | ||
|
|
3d8b672771 | ||
|
|
17650a6100 | ||
|
|
1588ca7e1f | ||
|
|
0de17319d3 | ||
|
|
681ff32e76 | ||
|
|
caee6c8685 | ||
|
|
c67a515cc2 | ||
|
|
24892779f7 | ||
|
|
39d2113549 | ||
|
|
65522d9775 | ||
|
|
50eecf698c | ||
|
|
20ab78e3c1 | ||
|
|
613ffe9bbd | ||
|
|
61ff45f98e | ||
|
|
a8aa475d09 | ||
|
|
a4215cfa59 | ||
|
|
a4ac9a721f | ||
|
|
447183e235 | ||
|
|
642e543b4b | ||
|
|
80503c4837 | ||
|
|
3577064f8c | ||
|
|
b6663733c0 | ||
|
|
b9d6834213 | ||
|
|
eafc4c5a0c | ||
|
|
ecf80b47a0 | ||
|
|
5499555862 | ||
|
|
70df74f65f | ||
|
|
74415becce | ||
|
|
3a56cf8ad9 | ||
|
|
6b7f53f0f3 | ||
|
|
e51666e8be | ||
|
|
6e56bcd75f | ||
|
|
48bfef1f7a | ||
|
|
e8f91434a7 | ||
|
|
7e26d82790 | ||
|
|
869b70e4db | ||
|
|
33904cb9c1 | ||
|
|
a42191eecf | ||
|
|
3fbf163d34 | ||
|
|
1c45e1b744 | ||
|
|
c777aa62b6 | ||
|
|
6296bd4e1d | ||
|
|
5a236c19f5 | ||
|
|
4f8a1f92a3 | ||
|
|
5612790307 | ||
|
|
0b5be00374 | ||
|
|
145cc782ff | ||
|
|
9712506be8 | ||
|
|
bd9ead11c5 | ||
|
|
a98ebf7344 | ||
|
|
c721ab63ee | ||
|
|
2820438afc | ||
|
|
180cfcc3e3 | ||
|
|
52d806a34a | ||
|
|
49a8f2ec96 | ||
|
|
7f5f4629e5 | ||
|
|
d91e0b3867 | ||
|
|
b537758b32 | ||
|
|
2ba6e473de | ||
|
|
de97061d65 | ||
|
|
624ee78081 | ||
|
|
e003bb2bb4 | ||
|
|
5c5f310646 | ||
|
|
c10433e512 | ||
|
|
580c530041 | ||
|
|
4d461afbd6 | ||
|
|
536e31f343 | ||
|
|
c6eb4df25e | ||
|
|
aae00e3987 | ||
|
|
b56800b15c | ||
|
|
be461be36b | ||
|
|
85171a9490 | ||
|
|
f5b9f2052a | ||
|
|
3d652997d1 | ||
|
|
10393dca68 | ||
|
|
597bfaea03 | ||
|
|
98b8ced814 | ||
|
|
efaee8ce85 | ||
|
|
6e93f5bb72 | ||
|
|
a41f5673bc | ||
|
|
ca436f0bae | ||
|
|
d8212e8dd6 | ||
|
|
12eb9f42dc | ||
|
|
23af164d7a | ||
|
|
960a437d46 | ||
|
|
237bebe2ed | ||
|
|
fc3dca772e | ||
|
|
ee64d31f48 | ||
|
|
493ff74a0d | ||
|
|
78c7000962 | ||
|
|
6a05e01298 | ||
|
|
7de3434733 | ||
|
|
74e6189f3e | ||
|
|
2e7a3affba | ||
|
|
505ec21f97 | ||
|
|
434820973c | ||
|
|
41ff526921 | ||
|
|
0be52b0087 | ||
|
|
ee8dc496d9 | ||
|
|
f80656cddf | ||
|
|
72121c01ec | ||
|
|
ac505390dd | ||
|
|
728e6e15c5 | ||
|
|
d21305378c | ||
|
|
0552749059 | ||
|
|
fbd3fe95e4 | ||
|
|
c6d02daee0 | ||
|
|
5208035003 | ||
|
|
d93956eff0 | ||
|
|
b3b06d3e7d | ||
|
|
427f815975 | ||
|
|
bf601ce268 | ||
|
|
8bfb363fcc | ||
|
|
ebf2630a66 | ||
|
|
9018955e1f | ||
|
|
88d58ae0a3 | ||
|
|
2fc99afd44 | ||
|
|
fa0885e25d | ||
|
|
0e4a0108d2 | ||
|
|
17bc627bf2 | ||
|
|
58370256c0 | ||
|
|
d5364231c2 | ||
|
|
4df3a4d436 | ||
|
|
812989490c | ||
|
|
892ef9edb7 | ||
|
|
982782f8c9 | ||
|
|
7319f524a3 | ||
|
|
1d71fbf77b | ||
|
|
7eacfec2c3 | ||
|
|
46f2a41cf7 | ||
|
|
fd2baf6f65 | ||
|
|
c8bf06d549 | ||
|
|
3acfa50214 | ||
|
|
3dbe205498 | ||
|
|
899cce8094 | ||
|
|
7400d51444 | ||
|
|
96c344d22b | ||
|
|
f48d71ecd0 | ||
|
|
d3acbbf79b | ||
|
|
cb9ec8234b | ||
|
|
47c72e583e | ||
|
|
568c2d308c | ||
|
|
11a7f359d1 | ||
|
|
145f1f5198 | ||
|
|
ff1df41485 | ||
|
|
d36aec8fb7 | ||
|
|
2779b5ee91 | ||
|
|
32efbd3edd | ||
|
|
68718eac1b | ||
|
|
7b64b4a207 | ||
|
|
f1143f591f | ||
|
|
07fc401d25 | ||
|
|
96f166a7e9 | ||
|
|
f4b775323d | ||
|
|
43d308116d | ||
|
|
d2b4dd71d2 | ||
|
|
36e6a73d5b | ||
|
|
e26158a45e | ||
|
|
3cfcd6a856 | ||
|
|
ff68806bfa | ||
|
|
4192c3abf4 | ||
|
|
ac1e1c7d23 | ||
|
|
9ab999618c | ||
|
|
f2666a472f | ||
|
|
ceda5d3bc7 | ||
|
|
6d81d519b6 | ||
|
|
88d1d79516 | ||
|
|
cfc6cfd1a3 | ||
|
|
6b7d67b427 | ||
|
|
b6d08b15c0 | ||
|
|
01f89a8cdc | ||
|
|
efd7a5dca6 | ||
|
|
7ba0290643 | ||
|
|
8ceb47178b | ||
|
|
2560d4f419 | ||
|
|
87ee409783 | ||
|
|
d47c1f3e9b | ||
|
|
b952dac339 | ||
|
|
ffb7d4c79c | ||
|
|
e68717b725 | ||
|
|
30a063ef9d | ||
|
|
35c3669ebc | ||
|
|
23f4f03575 | ||
|
|
a912fc09be | ||
|
|
a736a3713b | ||
|
|
f2da5bc93e | ||
|
|
2905b435db | ||
|
|
48ca6dbcec | ||
|
|
15a4302902 | ||
|
|
1f82a20312 | ||
|
|
fc943b70f6 | ||
|
|
f36470941c | ||
|
|
ae6d80daab | ||
|
|
44e82e2720 | ||
|
|
e94467d6da | ||
|
|
794c7708e8 | ||
|
|
8e73926359 | ||
|
|
8fc1d74820 | ||
|
|
496c6a9f03 | ||
|
|
7873f700b0 | ||
|
|
46c0861f45 | ||
|
|
5149c0ff25 | ||
|
|
cf99d62472 | ||
|
|
5878797eae | ||
|
|
8c2d090dc8 | ||
|
|
3f772eac32 | ||
|
|
62c952d258 | ||
|
|
c2f698e56e | ||
|
|
40f2a3efba | ||
|
|
333b9c0b99 | ||
|
|
90d19b4131 |
@@ -7,26 +7,38 @@
|
||||
"versions": [
|
||||
{
|
||||
"name": "3.0",
|
||||
"branchName": "master",
|
||||
"branchName": "3.0.x",
|
||||
"slug": "latest",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.8",
|
||||
"branchName": "2.8.x",
|
||||
"slug": "2.8",
|
||||
"name": "2.10",
|
||||
"branchName": "2.10.x",
|
||||
"slug": "2.10",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.7",
|
||||
"branchName": "2.7",
|
||||
"slug": "2.7",
|
||||
"name": "2.9",
|
||||
"branchName": "2.9.x",
|
||||
"slug": "2.9",
|
||||
"current": true,
|
||||
"aliases": [
|
||||
"current",
|
||||
"stable"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "2.8",
|
||||
"branchName": "2.8.x",
|
||||
"slug": "2.8",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.7",
|
||||
"branchName": "2.7",
|
||||
"slug": "2.7",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.6",
|
||||
"branchName": "2.6",
|
||||
|
||||
11
.gitattributes
vendored
11
.gitattributes
vendored
@@ -1,9 +1,10 @@
|
||||
/tests export-ignore
|
||||
/tools export-ignore
|
||||
/docs export-ignore
|
||||
/.github export-ignore
|
||||
.doctrine-project.json export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.gitmodules export-ignore
|
||||
.travis.yml export-ignore
|
||||
build.properties export-ignore
|
||||
build.properties.dev export-ignore
|
||||
build.xml export-ignore
|
||||
@@ -11,4 +12,8 @@ CONTRIBUTING.md export-ignore
|
||||
phpunit.xml.dist export-ignore
|
||||
run-all.sh export-ignore
|
||||
phpcs.xml.dist export-ignore
|
||||
composer.lock export-ignore
|
||||
phpbench.json export-ignore
|
||||
phpstan.neon export-ignore
|
||||
phpstan-baseline.neon export-ignore
|
||||
psalm.xml export-ignore
|
||||
psalm-baseline.xml export-ignore
|
||||
|
||||
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1,3 +0,0 @@
|
||||
patreon: phpdoctrine
|
||||
tidelift: packagist/doctrine/orm
|
||||
custom: https://www.doctrine-project.org/sponsorship.html
|
||||
37
.github/ISSUE_TEMPLATE/BC_Break.md
vendored
37
.github/ISSUE_TEMPLATE/BC_Break.md
vendored
@@ -1,37 +0,0 @@
|
||||
---
|
||||
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
34
.github/ISSUE_TEMPLATE/Bug.md
vendored
@@ -1,34 +0,0 @@
|
||||
---
|
||||
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
18
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
@@ -1,18 +0,0 @@
|
||||
---
|
||||
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
20
.github/ISSUE_TEMPLATE/Support_Question.md
vendored
@@ -1,20 +0,0 @@
|
||||
---
|
||||
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
19
.github/PULL_REQUEST_TEMPLATE/Failing_Test.md
vendored
@@ -1,19 +0,0 @@
|
||||
---
|
||||
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
18
.github/PULL_REQUEST_TEMPLATE/Improvement.md
vendored
@@ -1,18 +0,0 @@
|
||||
---
|
||||
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
26
.github/PULL_REQUEST_TEMPLATE/New_Feature.md
vendored
@@ -1,26 +0,0 @@
|
||||
---
|
||||
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
47
.github/workflows/ci.yml
vendored
@@ -1,47 +0,0 @@
|
||||
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"
|
||||
39
.github/workflows/coding-standard.yml
vendored
Normal file
39
.github/workflows/coding-standard.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: "Coding Standards"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*.x"
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
|
||||
jobs:
|
||||
coding-standards:
|
||||
name: "Coding Standards"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
tools: "cs2pr"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
# https://github.com/doctrine/.github/issues/3
|
||||
- name: "Run PHP_CodeSniffer"
|
||||
run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"
|
||||
279
.github/workflows/continuous-integration.yml
vendored
Normal file
279
.github/workflows/continuous-integration.yml
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
name: "Continuous Integration"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
|
||||
env:
|
||||
fail-fast: true
|
||||
|
||||
jobs:
|
||||
phpunit-smoke-check:
|
||||
name: "PHPUnit with SQLite"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.2"
|
||||
- "7.3"
|
||||
- "7.4"
|
||||
- "8.0"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
extensions: "pdo, pdo_sqlite"
|
||||
coverage: "pcov"
|
||||
ini-values: "zend.assertions=1"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
|
||||
- name: "Run PHPUnit"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/sqlite.xml --coverage-clover=coverage-no-cache.xml"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 0
|
||||
|
||||
- name: "Run PHPUnit with Second Level Cache"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/sqlite.xml --exclude-group performance,non-cacheable,locking_functional --coverage-clover=coverage-cache.xml"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 1
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v2"
|
||||
with:
|
||||
name: "phpunit-sqlite-${{ matrix.php-version }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
|
||||
|
||||
phpunit-postgres:
|
||||
name: "PHPUnit with PostgreSQL"
|
||||
runs-on: "ubuntu-20.04"
|
||||
needs: "phpunit-smoke-check"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
postgres-version:
|
||||
- "9.6"
|
||||
- "13"
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: "postgres:${{ matrix.postgres-version }}"
|
||||
env:
|
||||
POSTGRES_PASSWORD: "postgres"
|
||||
|
||||
options: >-
|
||||
--health-cmd "pg_isready"
|
||||
|
||||
ports:
|
||||
- "5432:5432"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
coverage: "pcov"
|
||||
ini-values: "zend.assertions=1"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
|
||||
- name: "Run PHPUnit"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/pdo_pgsql.xml --coverage-clover=coverage.xml"
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v2"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-coverage"
|
||||
path: "coverage.xml"
|
||||
|
||||
|
||||
phpunit-mariadb:
|
||||
name: "PHPUnit with MariaDB"
|
||||
runs-on: "ubuntu-20.04"
|
||||
needs: "phpunit-smoke-check"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
mariadb-version:
|
||||
- "10.5"
|
||||
extension:
|
||||
- "mysqli"
|
||||
- "pdo_mysql"
|
||||
|
||||
services:
|
||||
mariadb:
|
||||
image: "mariadb:${{ matrix.mariadb-version }}"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
MYSQL_DATABASE: "doctrine_tests"
|
||||
|
||||
options: >-
|
||||
--health-cmd "mysqladmin ping --silent"
|
||||
|
||||
ports:
|
||||
- "3306:3306"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
coverage: "pcov"
|
||||
ini-values: "zend.assertions=1"
|
||||
extensions: "${{ matrix.extension }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
|
||||
- name: "Run PHPUnit"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage.xml"
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v2"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.mariadb-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-coverage"
|
||||
path: "coverage.xml"
|
||||
|
||||
|
||||
phpunit-mysql:
|
||||
name: "PHPUnit with MySQL"
|
||||
runs-on: "ubuntu-20.04"
|
||||
needs: "phpunit-smoke-check"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
mysql-version:
|
||||
- "5.7"
|
||||
- "8.0"
|
||||
extension:
|
||||
- "mysqli"
|
||||
- "pdo_mysql"
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: "mysql:${{ matrix.mysql-version }}"
|
||||
|
||||
options: >-
|
||||
--health-cmd "mysqladmin ping --silent"
|
||||
-e MYSQL_ALLOW_EMPTY_PASSWORD=yes
|
||||
-e MYSQL_DATABASE=doctrine_tests
|
||||
|
||||
ports:
|
||||
- "3306:3306"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
coverage: "pcov"
|
||||
ini-values: "zend.assertions=1"
|
||||
extensions: "${{ matrix.extension }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
|
||||
- name: "Run PHPUnit"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage-no-cache.xml"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 0
|
||||
|
||||
- name: "Run PHPUnit with Second Level Cache"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --exclude-group performance,non-cacheable,locking_functional --coverage-clover=coverage-no-cache.xml"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 1
|
||||
|
||||
- name: "Upload coverage files"
|
||||
uses: "actions/upload-artifact@v2"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.mysql-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
|
||||
phpunit-lower-php-versions:
|
||||
name: "PHPUnit with SQLite"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.1"
|
||||
deps:
|
||||
- "highest"
|
||||
- "lowest"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
ini-values: "zend.assertions=1"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
dependency-versions: "${{ matrix.deps }}"
|
||||
|
||||
- name: "Run PHPUnit"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/sqlite.xml"
|
||||
|
||||
upload_coverage:
|
||||
name: "Upload coverage to Codecov"
|
||||
runs-on: "ubuntu-20.04"
|
||||
needs:
|
||||
- "phpunit-smoke-check"
|
||||
- "phpunit-postgres"
|
||||
- "phpunit-mariadb"
|
||||
- "phpunit-mysql"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Download coverage files"
|
||||
uses: "actions/download-artifact@v2"
|
||||
with:
|
||||
path: "reports"
|
||||
|
||||
- name: "Upload to Codecov"
|
||||
uses: "codecov/codecov-action@v1"
|
||||
with:
|
||||
directory: reports
|
||||
46
.github/workflows/release-on-milestone-closed.yml
vendored
Normal file
46
.github/workflows/release-on-milestone-closed.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: "Automatic Releases"
|
||||
|
||||
on:
|
||||
milestone:
|
||||
types:
|
||||
- "closed"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: "Git tag, release & create merge-up PR"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
|
||||
- name: "Release"
|
||||
uses: "laminas/automatic-releases@v1"
|
||||
with:
|
||||
command-name: "laminas:automatic-releases:release"
|
||||
env:
|
||||
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
|
||||
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
|
||||
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
|
||||
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
|
||||
"SHELL_VERBOSITY": "3"
|
||||
|
||||
- name: "Create Merge-Up Pull Request"
|
||||
uses: "laminas/automatic-releases@v1"
|
||||
with:
|
||||
command-name: "laminas:automatic-releases:create-merge-up-pull-request"
|
||||
env:
|
||||
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
|
||||
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
|
||||
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
|
||||
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
|
||||
|
||||
- name: "Create new milestones"
|
||||
uses: "laminas/automatic-releases@v1"
|
||||
with:
|
||||
command-name: "laminas:automatic-releases:create-milestones"
|
||||
env:
|
||||
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
|
||||
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
|
||||
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
|
||||
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
|
||||
65
.github/workflows/static-analysis.yml
vendored
Normal file
65
.github/workflows/static-analysis.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
name: "Static Analysis"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*.x"
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
|
||||
jobs:
|
||||
static-analysis-phpstan:
|
||||
name: "Static Analysis with PHPStan"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v2"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
- name: "Run a static analysis with phpstan/phpstan"
|
||||
run: "vendor/bin/phpstan analyse"
|
||||
|
||||
static-analysis-psalm:
|
||||
name: "Static Analysis with Psalm"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v2"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
- name: "Run a static analysis with vimeo/psalm"
|
||||
run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)"
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -7,13 +7,13 @@ lib/api/
|
||||
lib/Doctrine/Common
|
||||
lib/Doctrine/DBAL
|
||||
/.settings/
|
||||
*.iml
|
||||
.buildpath
|
||||
.project
|
||||
.idea
|
||||
*.iml
|
||||
vendor/
|
||||
composer.phar
|
||||
/tests/Doctrine/Performance/history.db
|
||||
/.phpcs-cache
|
||||
phpbench.phar
|
||||
phpbench.phar.pubkey
|
||||
composer.lock
|
||||
/.phpunit.result.cache
|
||||
/*.phpunit.xml
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
build:
|
||||
nodes:
|
||||
analysis:
|
||||
environment:
|
||||
php:
|
||||
version: 7.4
|
||||
cache:
|
||||
disabled: false
|
||||
directories:
|
||||
- ~/.composer/cache
|
||||
project_setup:
|
||||
override: true
|
||||
tests:
|
||||
override:
|
||||
- php-scrutinizer-run
|
||||
- phpcs-run
|
||||
dependencies:
|
||||
override:
|
||||
- composer install --no-interaction --prefer-dist
|
||||
|
||||
tools:
|
||||
external_code_coverage:
|
||||
timeout: 3600
|
||||
|
||||
filter:
|
||||
excluded_paths:
|
||||
- docs
|
||||
|
||||
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
|
||||
82
.travis.yml
82
.travis.yml
@@ -1,82 +0,0 @@
|
||||
dist: trusty
|
||||
sudo: false
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 7.3
|
||||
- 7.4
|
||||
|
||||
env:
|
||||
- DB=mariadb
|
||||
- DB=mysql
|
||||
- DB=pgsql
|
||||
- DB=sqlite
|
||||
|
||||
before_install:
|
||||
- |
|
||||
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:
|
||||
- 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
|
||||
- ENABLE_SECOND_LEVEL_CACHE=0 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml
|
||||
# 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.4"
|
||||
|
||||
- stage: Test
|
||||
env: DB=sqlite DEPENDENCIES=low
|
||||
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 --no-interaction --prefer-dist --no-suggest --no-progress
|
||||
|
||||
- stage: Test
|
||||
env: DB=sqlite COVERAGE
|
||||
before_script:
|
||||
- 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
|
||||
after_script:
|
||||
- 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 BENCHMARK
|
||||
before_script:
|
||||
- wget https://phpbench.github.io/phpbench/phpbench.phar https://phpbench.github.io/phpbench/phpbench.phar.pubkey
|
||||
script:
|
||||
- php phpbench.phar run --bootstrap=tests/Doctrine/Tests/TestInit.php -l dots --report=default
|
||||
|
||||
allow_failures:
|
||||
# temporarily disabled
|
||||
- env: DB=mysql
|
||||
- env: DB=mariadb
|
||||
- env: DB=pgsql
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
@@ -1,81 +1,60 @@
|
||||
# Contributing to Doctrine ORM
|
||||
# Contribute to Doctrine
|
||||
|
||||
Thank you for contributing to Doctrine ORM!
|
||||
Thank you for contributing to Doctrine!
|
||||
|
||||
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.
|
||||
|
||||
## Obtaining a copy
|
||||
Doctrine has [general contributing guidelines][contributor workflow], make
|
||||
sure you follow them.
|
||||
|
||||
In order to submit a pull request, you will need to [fork the project][Fork] and obtain a
|
||||
fresh copy of the source code:
|
||||
|
||||
```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.
|
||||
[contributor workflow]: https://www.doctrine-project.org/contribute/index.html
|
||||
|
||||
## Coding Standard
|
||||
|
||||
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.
|
||||
This project follows [`doctrine/coding-standard`][coding standard homepage].
|
||||
You may fix many some of the issues with `vendor/bin/phpcbf`.
|
||||
|
||||
## Tests
|
||||
[coding standard homepage]: https://github.com/doctrine/coding-standard
|
||||
|
||||
Please try to add a test for your pull request.
|
||||
## Unit-Tests
|
||||
|
||||
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 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
|
||||
``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
|
||||
depending on the scope of the feature.
|
||||
|
||||
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.
|
||||
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 the ORM, and you
|
||||
will have to run a composer installation in the project:
|
||||
|
||||
```sh
|
||||
git clone git@github.com:doctrine/orm.git
|
||||
cd orm
|
||||
curl -sS https://getcomposer.org/installer | php --
|
||||
./composer.phar install
|
||||
```
|
||||
|
||||
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:
|
||||
take a look at the ``ci/github/phpunit`` directory 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 file.
|
||||
See [DDC2306Test][Test Example] for an example.
|
||||
|
||||
## CI
|
||||
|
||||
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.
|
||||
1. If you put a test into the `Ticket` namespace as described above, put the testcase and all entities into the same class.
|
||||
See `https://github.com/doctrine/orm/tree/2.8.x/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an
|
||||
example.
|
||||
|
||||
## Getting merged
|
||||
|
||||
@@ -84,10 +63,3 @@ 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) Doctrine Project
|
||||
Copyright (c) 2006-2015 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
|
||||
|
||||
58
README.md
58
README.md
@@ -1,52 +1,30 @@
|
||||
[](https://tidelift.com/subscription/pkg/packagist-doctrine-orm?utm_source=packagist-doctrine-orm&utm_medium=referral&utm_campaign=readme)
|
||||
| [3.0.x][3.0] | [2.9.x][2.9] | [2.8.x][2.8] |
|
||||
|:----------------:|:----------------:|:----------:|
|
||||
| [![Build status][3.0 image]][3.0] | [![Build status][2.9 image]][2.9] | [![Build status][2.8 image]][2.8] |
|
||||
| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.9 coverage image]][2.9 coverage] | [![Coverage Status][2.8 coverage image]][2.8 coverage] |
|
||||
|
||||
| [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
|
||||
Doctrine 2 is an object-relational mapper (ORM) for PHP 7.1+ that provides transparent persistence
|
||||
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
|
||||
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
|
||||
inspired by Hibernate's HQL. This provides developers with a powerful alternative to SQL that maintains flexibility
|
||||
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:
|
||||
|
||||
* [Website](http://www.doctrine-project.org)
|
||||
* [Documentation](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
|
||||
* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
|
||||
|
||||
|
||||
[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
|
||||
[3.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.0.x
|
||||
[3.0]: https://github.com/doctrine/orm/tree/3.0.x
|
||||
[3.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.0.x/graph/badge.svg
|
||||
[3.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.0.x
|
||||
[2.9 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.9.x
|
||||
[2.9]: https://github.com/doctrine/orm/tree/2.9.x
|
||||
[2.9 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.9.x/graph/badge.svg
|
||||
[2.9 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.9.x
|
||||
[2.8 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg
|
||||
[2.8]: https://github.com/doctrine/orm/tree/2.8
|
||||
[2.8 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.8.x/graph/badge.svg
|
||||
[2.8 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.8.x
|
||||
|
||||
@@ -10,8 +10,8 @@ we cannot protect you from SQL injection.
|
||||
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/orm/blob/master/docs/en/reference/security.rst)
|
||||
- [DBAL Security Page](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/security.html)
|
||||
- [ORM Security Page](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/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
|
||||
|
||||
361
UPGRADE.md
361
UPGRADE.md
@@ -1,191 +1,119 @@
|
||||
# Upgrade to 3.0
|
||||
# Upgrade to 2.9
|
||||
|
||||
## BC Break: Removed ability to clear cache via console with some cache drivers
|
||||
## Minor BC BREAK: Setup tool needs cache implementation
|
||||
|
||||
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.
|
||||
With the deprecation of doctrine/cache, the setup tool might no longer work as expected without a different cache
|
||||
implementation. To work around this:
|
||||
* Install symfony/cache: `composer require symfony/cache`. This will keep previous behaviour without any changes
|
||||
* Instantiate caches yourself: to use a different cache implementation, pass a cache instance when calling any
|
||||
configuration factory in the setup tool:
|
||||
```diff
|
||||
- $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, $proxyDir);
|
||||
+ $cache = \Doctrine\Common\Cache\Psr6\DoctrineProvider::wrap($anyPsr6Implementation);
|
||||
+ $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, $proxyDir, $cache);
|
||||
```
|
||||
* As a quick workaround, you can lock the doctrine/cache dependency to work around this: `composer require doctrine/cache ^1.11`.
|
||||
Note that this is only recommended as a bandaid fix, as future versions of ORM will no longer work with doctrine/cache
|
||||
1.11.
|
||||
|
||||
## Deprecated: doctrine/cache for metadata caching
|
||||
|
||||
## BC Break: `orm:run-dql` command's `$depth` parameter removed
|
||||
The `Doctrine\ORM\Configuration#setMetadataCacheImpl()` method is deprecated and should no longer be used. Please use
|
||||
`Doctrine\ORM\Configuration#setMetadataCache()` with any PSR-6 cache adapter instead.
|
||||
|
||||
The `$depth` parameter has been removed, the dumping functionality
|
||||
is now provided by [`symfony/var-dumper`](https://github.com/symfony/var-dumper).
|
||||
## Removed: flushing metadata cache
|
||||
|
||||
## BC Break: Dropped `Doctrine\ORM\Tools\Setup::registerAutoloadDirectory()`
|
||||
To support PSR-6 caches, the `--flush` option for the `orm:clear-cache:metadata` command is ignored. Metadata cache is
|
||||
now always cleared regardless of the cache adapter being used.
|
||||
|
||||
This method used deprecated Doctrine Autoloader and has been removed. Please rely on Composer autoloading instead.
|
||||
# Upgrade to 2.8
|
||||
|
||||
## BC Break: Dropped automatic discriminator map discovery
|
||||
## Minor BC BREAK: Failed commit now throw OptimisticLockException
|
||||
|
||||
Automatic discriminator map discovery exhibited multiple flaws
|
||||
that can't be reliably addressed and supported:
|
||||
Method `Doctrine\ORM\UnitOfWork#commit()` can throw an OptimisticLockException when a commit silently fails and returns false
|
||||
since `Doctrine\DBAL\Connection#commit()` signature changed from returning void to boolean
|
||||
|
||||
* 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.
|
||||
## Deprecated: `Doctrine\ORM\AbstractQuery#iterator()`
|
||||
|
||||
As a consequence this feature has been dropped.
|
||||
The method `Doctrine\ORM\AbstractQuery#iterator()` is deprecated in favor of `Doctrine\ORM\AbstractQuery#toIterable()`.
|
||||
Note that `toIterable()` yields results of the query, unlike `iterator()` which yielded each result wrapped into an array.
|
||||
|
||||
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.
|
||||
# Upgrade to 2.7
|
||||
|
||||
## BC Break: Missing type declaration added for identifier generators
|
||||
## Added `Doctrine\ORM\AbstractQuery#enableResultCache()` and `Doctrine\ORM\AbstractQuery#disableResultCache()` methods
|
||||
|
||||
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).
|
||||
Method `Doctrine\ORM\AbstractQuery#useResultCache()` which could be used for both enabling and disabling the cache
|
||||
(depending on passed flag) was split into two.
|
||||
|
||||
## BC Break: Removed possibility to extend the doctrine mapping xml schema with anything
|
||||
## Minor BC BREAK: paginator output walkers aren't be called anymore on sub-queries for queries without max results
|
||||
|
||||
If you want to extend it now you have to provide your own validation schema.
|
||||
To optimize DB interaction, `Doctrine\ORM\Tools\Pagination\Paginator` no longer fetches identifiers to be able to
|
||||
perform the pagination with join collections when max results isn't set in the query.
|
||||
|
||||
## BC Break: Entity Listeners no long support naming convention methods
|
||||
## Minor BC BREAK: tables filtered with `schema_filter` are no longer created
|
||||
|
||||
If you want their behavior to be kept, please add the necessary Annotation methods (in case XML driver is used,
|
||||
no changes are necessary).
|
||||
When generating schema diffs, if a source table is filtered out by a `schema_filter` expression, then a `CREATE TABLE` was
|
||||
always generated, even if the table already existed. This has been changed in this release and the table will no longer
|
||||
be created.
|
||||
|
||||
## BC Break: Removed `Doctrine\ORM\Mapping\Exporter\VariableExporter` constants
|
||||
## Deprecated number unaware `Doctrine\ORM\Mapping\UnderscoreNamingStrategy`
|
||||
|
||||
This constant has been removed
|
||||
In the last patch of the `v2.6.x` series, we fixed a bug that was not converting names properly when they had numbers
|
||||
(e.g.: `base64Encoded` was wrongly converted to `base64encoded` instead of `base64_encoded`).
|
||||
|
||||
* `Doctrine\ORM\Mapping\Exporter\VariableExporter::INDENTATION`
|
||||
In order to not break BC we've introduced a way to enable the fixed behavior using a boolean constructor argument. This
|
||||
argument will be removed in 3.0 and the default behavior will be the fixed one.
|
||||
|
||||
## BC Break: Removed support for named queries and named native queries
|
||||
## Deprecated: `Doctrine\ORM\AbstractQuery#useResultCache()`
|
||||
|
||||
These classes have been removed:
|
||||
Method `Doctrine\ORM\AbstractQuery#useResultCache()` is deprecated because it is split into `enableResultCache()`
|
||||
and `disableResultCache()`. It will be removed in 3.0.
|
||||
|
||||
* `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`
|
||||
## Deprecated code generators and related console commands
|
||||
|
||||
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:
|
||||
These console commands have been deprecated:
|
||||
|
||||
* `orm:convert-mapping`
|
||||
* `orm:generate:entities`
|
||||
* `orm:generate-repositories`
|
||||
|
||||
These classes have been removed:
|
||||
These classes have been deprecated:
|
||||
|
||||
* `Doctrine\ORM\Tools\EntityGenerator`
|
||||
* `Doctrine\ORM\Tools\EntityRepositoryGenerator`
|
||||
|
||||
The whole Doctrine\ORM\Tools\Export namespace with all its members has been removed as well.
|
||||
Whole Doctrine\ORM\Tools\Export namespace with all its members have been deprecated as well.
|
||||
|
||||
## BC Break: proxies no longer implement `Doctrine\ORM\Proxy\Proxy`
|
||||
## Deprecated `Doctrine\ORM\Proxy\Proxy` marker interface
|
||||
|
||||
Proxy objects no longer implement `Doctrine\ORM\Proxy\Proxy` nor
|
||||
`Doctrine\Common\Persistence\Proxy`: instead, they implement
|
||||
Proxy objects in Doctrine ORM 3.0 will no longer implement `Doctrine\ORM\Proxy\Proxy` nor
|
||||
`Doctrine\Persistence\Proxy`: instead, they implement
|
||||
`ProxyManager\Proxy\GhostObjectInterface`.
|
||||
|
||||
These related classes have been removed:
|
||||
These related classes have been deprecated:
|
||||
|
||||
* `Doctrine\ORM\Proxy\ProxyFactory` - replaced by `Doctrine\ORM\Proxy\Factory\StaticProxyFactory`
|
||||
and `Doctrine\ORM\Proxy\Factory\ProxyFactory`
|
||||
* `Doctrine\ORM\Proxy\Proxy`
|
||||
* `Doctrine\ORM\Proxy\ProxyFactory`
|
||||
* `Doctrine\ORM\Proxy\Autoloader` - we suggest using the composer autoloader instead
|
||||
* `Doctrine\ORM\Reflection\RuntimePublicReflectionProperty`
|
||||
|
||||
These methods have been deprecated:
|
||||
|
||||
These methods have been removed:
|
||||
|
||||
* `Doctrine\ORM\Configuration#getProxyDir()`
|
||||
* `Doctrine\ORM\Configuration#getAutoGenerateProxyClasses()`
|
||||
* `Doctrine\ORM\Configuration#getProxyDir()`
|
||||
* `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.
|
||||
## Deprecated `Doctrine\ORM\Version`
|
||||
|
||||
Proxies are also always generated if not found: fatal errors due to missing
|
||||
proxy classes should no longer occur with ORM default settings.
|
||||
The `Doctrine\ORM\Version` class is now deprecated and will be removed in Doctrine ORM 3.0:
|
||||
please refrain from checking the ORM version at runtime or use
|
||||
[ocramius/package-versions](https://github.com/Ocramius/PackageVersions/).
|
||||
|
||||
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
|
||||
## Deprecated `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:
|
||||
The following API methods were therefore deprecated:
|
||||
|
||||
* `EntityManager#merge()`
|
||||
* `EntityManager#detach()`
|
||||
@@ -198,20 +126,25 @@ In order to maintain performance on batch processing jobs, it is endorsed to ena
|
||||
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
|
||||
An alternative to `EntityManager#merge()` will not be 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`
|
||||
## Extending `EntityManager` is deprecated
|
||||
|
||||
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.
|
||||
Final keyword will be added to the `EntityManager::class` in Doctrine ORM 3.0 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
|
||||
## Deprecated `EntityManager#clear($entityName)`
|
||||
|
||||
`Sequencing\Generator#generate()` now takes ``EntityManagerInterface`` as its first argument instead of ``EntityManager``. If you have any custom generators, please update your code accordingly.
|
||||
If your code relies on clearing a single entity type via `EntityManager#clear($entityName)`,
|
||||
the signature has been changed to `EntityManager#clear()`.
|
||||
|
||||
## BC Break: Removed `EntityManager#flush($entity)` and `EntityManager#flush($entities)`
|
||||
The main reason is that partial clears caused multiple issues with data integrity
|
||||
in the managed entity graph, which was constantly spawning more edge-case bugs/scenarios.
|
||||
|
||||
## Deprecated `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
|
||||
@@ -219,15 +152,15 @@ If your code relies on single entity flushing optimisations via
|
||||
|
||||
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. 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
|
||||
The `flush()` semantics will remain the same, but the change tracking will be performed
|
||||
on all entities managed by the unit of work, and not just on the provided
|
||||
`$entity` or `$entities`, as the parameter is now completely ignored.
|
||||
|
||||
The same applies to `UnitOfWork#commit($entity)`, which now is simply
|
||||
The same applies to `UnitOfWork#commit($entity)`, which will simply be
|
||||
`UnitOfWork#commit()`.
|
||||
|
||||
If you would still like to perform batching operations over small `UnitOfWork`
|
||||
@@ -237,97 +170,15 @@ instances, it is suggested to follow these paths instead:
|
||||
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.
|
||||
## Deprecated `YAML` mapping drivers.
|
||||
|
||||
If your code relies on ``YamlDriver`` or ``SimpleYamlDriver``, you **MUST** change to
|
||||
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.
|
||||
## Deprecated: `Doctrine\ORM\EntityManagerInterface#copy()`
|
||||
|
||||
Method `Doctrine\ORM\EntityManagerInterface#copy()` never got its implementation and is deprecated.
|
||||
It will be removed in 3.0.
|
||||
|
||||
# Upgrade to 2.6
|
||||
|
||||
@@ -359,10 +210,6 @@ 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/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
|
||||
|
||||
Doctrine 2.6 now requires PHP 7.1 or newer.
|
||||
@@ -379,6 +226,10 @@ As a consequence, automatic cache setup in Doctrine\ORM\Tools\Setup::create*Conf
|
||||
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/orm/pull/5600).
|
||||
|
||||
## Minor BC BREAK: removed $className parameter on `AbstractEntityInheritancePersister#getSelectJoinColumnSQL()`
|
||||
|
||||
As `$className` parameter was not used in the method, it was safely removed.
|
||||
|
||||
## Minor BC BREAK: query cache key time is now a float
|
||||
|
||||
As of 2.5.5, the `QueryCacheEntry#time` property will contain a float value
|
||||
@@ -402,8 +253,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:
|
||||
@@ -496,7 +347,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(
|
||||
@@ -527,7 +378,6 @@ 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()
|
||||
@@ -572,7 +422,6 @@ 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
|
||||
@@ -630,18 +479,17 @@ above you must implement these new methods.
|
||||
|
||||
## Metadata Drivers
|
||||
|
||||
Metadata drivers have been rewritten to reuse code from Doctrine\Common. Anyone who is using the
|
||||
Metadata drivers have been rewritten to reuse code from `Doctrine\Persistence`. Anyone who is using the
|
||||
`Doctrine\ORM\Mapping\Driver\Driver` interface should instead refer to
|
||||
`Doctrine\Common\Persistence\Mapping\Driver\MappingDriver`. Same applies to
|
||||
`Doctrine\Persistence\Mapping\Driver\MappingDriver`. Same applies to
|
||||
`Doctrine\ORM\Mapping\Driver\AbstractFileDriver`: you should now refer to
|
||||
`Doctrine\Common\Persistence\Mapping\Driver\FileDriver`.
|
||||
`Doctrine\Persistence\Mapping\Driver\FileDriver`.
|
||||
|
||||
Also, following mapping drivers have been deprecated, please use their replacements in Doctrine\Common as listed:
|
||||
|
||||
* `Doctrine\ORM\Mapping\Driver\DriverChain` => `Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain`
|
||||
* `Doctrine\ORM\Mapping\Driver\PHPDriver` => `Doctrine\Common\Persistence\Mapping\Driver\PHPDriver`
|
||||
* `Doctrine\ORM\Mapping\Driver\StaticPHPDriver` => `Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver`
|
||||
|
||||
* `Doctrine\ORM\Mapping\Driver\DriverChain` => `Doctrine\Persistence\Mapping\Driver\MappingDriverChain`
|
||||
* `Doctrine\ORM\Mapping\Driver\PHPDriver` => `Doctrine\Persistence\Mapping\Driver\PHPDriver`
|
||||
* `Doctrine\ORM\Mapping\Driver\StaticPHPDriver` => `Doctrine\Persistence\Mapping\Driver\StaticPHPDriver`
|
||||
|
||||
# Upgrade to 2.2
|
||||
|
||||
@@ -726,12 +574,11 @@ 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
|
||||
|
||||
The EntityRepository now has an interface Doctrine\Common\Persistence\ObjectRepository. This means that your classes that override EntityRepository and extend find(), findOneBy() or findBy() must be adjusted to follow this interface.
|
||||
The EntityRepository now has an interface Doctrine\Persistence\ObjectRepository. This means that your classes that override EntityRepository and extend find(), findOneBy() or findBy() must be adjusted to follow this interface.
|
||||
|
||||
## AnnotationReader changes
|
||||
|
||||
@@ -751,7 +598,6 @@ 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
|
||||
@@ -760,7 +606,6 @@ 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
|
||||
@@ -823,12 +668,10 @@ 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
|
||||
@@ -899,6 +742,7 @@ 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
|
||||
@@ -962,7 +806,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, JOINED
|
||||
NONE, SINGLE_TABLE, INHERITANCE_TYPE_JOINED
|
||||
|
||||
## YAML Mapping Driver
|
||||
|
||||
@@ -994,7 +838,6 @@ 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
|
||||
@@ -1031,8 +874,6 @@ 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,6 +1,4 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
include('doctrine.php');
|
||||
include(__DIR__ . '/doctrine.php');
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
<?php
|
||||
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* 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>.
|
||||
*/
|
||||
|
||||
require_once 'Doctrine/Common/ClassLoader.php';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
@echo off
|
||||
|
||||
if "%PHPBIN%" == "" set PHPBIN=@php_bin@
|
||||
if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH
|
||||
GOTO RUN
|
||||
:USE_PEAR_PATH
|
||||
set PHPBIN=%PHP_PEAR_PHP_BIN%
|
||||
:RUN
|
||||
"%PHPBIN%" "@bin_dir@\doctrine" %*
|
||||
@echo off
|
||||
|
||||
if "%PHPBIN%" == "" set PHPBIN=@php_bin@
|
||||
if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH
|
||||
GOTO RUN
|
||||
:USE_PEAR_PATH
|
||||
set PHPBIN=%PHP_PEAR_PHP_BIN%
|
||||
:RUN
|
||||
"%PHPBIN%" "@bin_dir@\doctrine" %*
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* 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>.
|
||||
*/
|
||||
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
version=2.0.0BETA2
|
||||
dependencies.common=2.0.0BETA4
|
||||
dependencies.dbal=2.0.0BETA4
|
||||
stability=beta
|
||||
build.dir=build
|
||||
dist.dir=dist
|
||||
report.dir=reports
|
||||
log.archive.dir=logs
|
||||
project.pirum_dir=
|
||||
project.download_dir=
|
||||
project.xsd_dir=
|
||||
test.phpunit_configuration_file=
|
||||
test.phpunit_generate_coverage=0
|
||||
test.pmd_reports=0
|
||||
test.pdepend_exec=
|
||||
test.phpmd_exec=
|
||||
78
build.xml
78
build.xml
@@ -1,78 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="DoctrineORM" default="build" basedir=".">
|
||||
<property file="build.properties" />
|
||||
|
||||
<target name="php">
|
||||
<exec executable="which" outputproperty="php_executable">
|
||||
<arg value="php" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="prepare">
|
||||
<mkdir dir="build" />
|
||||
</target>
|
||||
|
||||
<target name="build" depends="check-git-checkout-clean,prepare,php,composer">
|
||||
<exec executable="${php_executable}">
|
||||
<arg value="build/composer.phar" />
|
||||
<arg value="archive" />
|
||||
<arg value="--dir=build" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="composer" depends="php,composer-check,composer-download">
|
||||
<exec executable="${php_executable}">
|
||||
<arg value="build/composer.phar" />
|
||||
<arg value="install" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="composer-check" depends="prepare">
|
||||
<available file="build/composer.phar" property="composer.present"/>
|
||||
</target>
|
||||
|
||||
<target name="composer-download" unless="composer.present">
|
||||
<exec executable="wget">
|
||||
<arg value="-Obuild/composer.phar" />
|
||||
<arg value="http://getcomposer.org/composer.phar" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="check-git-checkout-clean">
|
||||
<exec executable="git" failonerror="true">
|
||||
<arg value="diff-index" />
|
||||
<arg value="--quiet" />
|
||||
<arg value="HEAD" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<macrodef name="git-commit">
|
||||
<attribute name="file" default="NOT SET"/>
|
||||
<attribute name="message" default="NOT SET"/>
|
||||
|
||||
<sequential>
|
||||
<exec executable="git">
|
||||
<arg value="add" />
|
||||
<arg value="@{file}" />
|
||||
</exec>
|
||||
<exec executable="git">
|
||||
<arg value="commit" />
|
||||
<arg value="-m" />
|
||||
<arg value="@{message}" />
|
||||
</exec>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<macrodef name="git-tag">
|
||||
<attribute name="version" default="NOT SET" />
|
||||
|
||||
<sequential>
|
||||
<exec executable="git">
|
||||
<arg value="tag" />
|
||||
<arg value="-m" />
|
||||
<arg value="v@{version}" />
|
||||
<arg value="v@{version}" />
|
||||
</exec>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</project>
|
||||
38
ci/github/phpunit/mysqli.xml
Normal file
38
ci/github/phpunit/mysqli.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
>
|
||||
<php>
|
||||
<var name="db_driver" value="mysqli"/>
|
||||
<var name="db_host" value="127.0.0.1" />
|
||||
<var name="db_port" value="3306"/>
|
||||
<var name="db_user" value="root" />
|
||||
<var name="db_dbname" value="doctrine_tests" />
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Doctrine DBAL Test Suite">
|
||||
<directory>../../../tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">../../../lib/Doctrine</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>performance</group>
|
||||
<group>locking_functional</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
</phpunit>
|
||||
39
ci/github/phpunit/pdo_mysql.xml
Normal file
39
ci/github/phpunit/pdo_mysql.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
>
|
||||
<php>
|
||||
<var name="db_driver" value="pdo_mysql"/>
|
||||
<var name="db_host" value="127.0.0.1" />
|
||||
<var name="db_port" value="3306"/>
|
||||
<var name="db_user" value="root" />
|
||||
<var name="db_dbname" value="doctrine_tests" />
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Doctrine DBAL Test Suite">
|
||||
<directory>../../../tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">../../../lib/Doctrine</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>performance</group>
|
||||
<group>locking_functional</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
</phpunit>
|
||||
38
ci/github/phpunit/pdo_pgsql.xml
Normal file
38
ci/github/phpunit/pdo_pgsql.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
>
|
||||
<php>
|
||||
<var name="db_driver" value="pdo_pgsql"/>
|
||||
<var name="db_host" value="localhost" />
|
||||
<var name="db_user" value="postgres" />
|
||||
<var name="db_password" value="postgres" />
|
||||
<var name="db_dbname" value="doctrine_tests" />
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Doctrine DBAL Test Suite">
|
||||
<directory>../../../tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">../../../lib/Doctrine</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>performance</group>
|
||||
<group>locking_functional</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
</phpunit>
|
||||
36
ci/github/phpunit/sqlite.xml
Normal file
36
ci/github/phpunit/sqlite.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
>
|
||||
<php>
|
||||
<!-- use an in-memory sqlite database -->
|
||||
<var name="db_driver" value="pdo_sqlite"/>
|
||||
<var name="db_memory" value="true"/>
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Doctrine DBAL Test Suite">
|
||||
<directory>../../../tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">../../../lib/Doctrine</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>performance</group>
|
||||
<group>locking_functional</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
</phpunit>
|
||||
@@ -1,18 +1,8 @@
|
||||
{
|
||||
"name": "doctrine/orm",
|
||||
"type": "library",
|
||||
"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"
|
||||
],
|
||||
"description": "Object-Relational-Mapper for PHP",
|
||||
"keywords": ["orm", "database"],
|
||||
"homepage": "https://www.doctrine-project.org/projects/orm.html",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
@@ -22,38 +12,39 @@
|
||||
{"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.3",
|
||||
"ext-ctype": "*",
|
||||
"doctrine/annotations": "~1.7",
|
||||
"doctrine/cache": "~1.6",
|
||||
"doctrine/collections": "^1.4",
|
||||
"doctrine/dbal": "dev-missed-commits",
|
||||
"doctrine/event-manager": "^1.0",
|
||||
"doctrine/inflector": "~1.0",
|
||||
"doctrine/instantiator": "~1.1",
|
||||
"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"
|
||||
"php": "^7.1|^8.0",
|
||||
"ext-pdo": "*",
|
||||
"composer/package-versions-deprecated": "^1.8",
|
||||
"doctrine/annotations": "^1.13",
|
||||
"doctrine/cache": "^1.11.3|^2.0.3",
|
||||
"doctrine/collections": "^1.5",
|
||||
"doctrine/common": "^3.0.3",
|
||||
"doctrine/dbal": "^2.13.0",
|
||||
"doctrine/deprecations": "^0.5.3",
|
||||
"doctrine/event-manager": "^1.1",
|
||||
"doctrine/inflector": "^1.4|^2.0",
|
||||
"doctrine/instantiator": "^1.3",
|
||||
"doctrine/lexer": "^1.0",
|
||||
"doctrine/persistence": "^2.2",
|
||||
"psr/cache": "^1 || ^2 || ^3",
|
||||
"symfony/console": "^3.0|^4.0|^5.0|^6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^6.0",
|
||||
"phpstan/phpstan": "^0.11",
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"doctrine/coding-standard": "^9.0",
|
||||
"phpstan/phpstan": "^0.12.83",
|
||||
"phpunit/phpunit": "^7.5|^8.5|^9.4",
|
||||
"squizlabs/php_codesniffer": "3.6.0",
|
||||
"symfony/cache": "^4.4|^5.2",
|
||||
"symfony/yaml": "^3.4|^4.0|^5.0|^6.0",
|
||||
"vimeo/psalm": "4.7.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0",
|
||||
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Doctrine\\ORM\\": "lib/Doctrine/ORM" }
|
||||
@@ -66,6 +57,6 @@
|
||||
},
|
||||
"bin": ["bin/doctrine"],
|
||||
"archive": {
|
||||
"exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"]
|
||||
"exclude": ["!vendor", "tests", "*phpunit.xml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"]
|
||||
}
|
||||
}
|
||||
|
||||
4214
composer.lock
generated
4214
composer.lock
generated
File diff suppressed because it is too large
Load Diff
4
docs/.gitignore
vendored
Normal file
4
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
en/_exts/configurationblock.pyc
|
||||
build
|
||||
en/_build
|
||||
.idea
|
||||
3
docs/.gitmodules
vendored
Normal file
3
docs/.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[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](https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US)
|
||||
The Doctrine ORM documentation is licensed under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US)
|
||||
|
||||
Creative Commons Legal Code
|
||||
|
||||
@@ -337,6 +337,7 @@ 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
|
||||
@@ -358,4 +359,5 @@ 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 https://creativecommons.org/.
|
||||
Creative Commons may be contacted at http://creativecommons.org/.
|
||||
|
||||
|
||||
18
docs/README.md
Normal file
18
docs/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# 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
|
||||
10
docs/bin/generate-docs.sh
Executable file
10
docs/bin/generate-docs.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/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
|
||||
2
docs/bin/install-dependencies.sh
Normal file
2
docs/bin/install-dependencies.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
sudo apt-get update && sudo apt-get install -y python2.7 python-sphinx python-pygments
|
||||
89
docs/en/Makefile
Normal file
89
docs/en/Makefile
Normal file
@@ -0,0 +1,89 @@
|
||||
# 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."
|
||||
93
docs/en/_exts/configurationblock.py
Normal file
93
docs/en/_exts/configurationblock.py
Normal file
@@ -0,0 +1,93 @@
|
||||
#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)
|
||||
1
docs/en/_theme
Submodule
1
docs/en/_theme
Submodule
Submodule docs/en/_theme added at 6f1bc8bead
201
docs/en/conf.py
Normal file
201
docs/en/conf.py
Normal file
@@ -0,0 +1,201 @@
|
||||
# -*- 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,7 +4,7 @@ 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
|
||||
@@ -15,7 +15,7 @@ type `Point <https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.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,25 +29,23 @@ The entity class:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
namespace Geo\Entity;
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Entity
|
||||
*/
|
||||
class Location
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="point")
|
||||
* @Column(type="point")
|
||||
*
|
||||
* @var \Geo\ValueObject\Point
|
||||
*/
|
||||
private $point;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
* @Column(type="string")
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
@@ -86,7 +84,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:
|
||||
@@ -94,7 +92,7 @@ The point class:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
namespace Geo\ValueObject;
|
||||
|
||||
class Point
|
||||
@@ -195,10 +193,10 @@ object into a string representation before saving to the database (in the
|
||||
value from the database (in the ``convertToPHPValue`` method).
|
||||
|
||||
The format of the string representation format is called
|
||||
`Well-known text (WKT) <https://en.wikipedia.org/wiki/Well-known_text>`_.
|
||||
`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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -212,13 +210,13 @@ 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
|
||||
@@ -251,8 +249,8 @@ Example usage
|
||||
$em->clear();
|
||||
|
||||
// Fetch the Location object
|
||||
$query = $em->createQuery("SELECT l FROM Geo\Entity\Location WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'");
|
||||
$query = $em->createQuery("SELECT l FROM Geo\Entity\Location l WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'");
|
||||
$location = $query->getSingleResult();
|
||||
|
||||
/** @var Geo\ValueObject\Point $point */
|
||||
/* @var Geo\ValueObject\Point */
|
||||
$point = $location->getPoint();
|
||||
|
||||
@@ -6,7 +6,7 @@ Aggregate Fields
|
||||
You will often come across the requirement to display aggregate
|
||||
values of data that can be computed by using the MIN, MAX, COUNT or
|
||||
SUM SQL functions. For any ORM this is a tricky issue
|
||||
traditionally. Doctrine 2 offers several ways to get access to
|
||||
traditionally. Doctrine ORM offers several ways to get access to
|
||||
these values and this article will describe all of them from
|
||||
different perspectives.
|
||||
|
||||
@@ -22,7 +22,7 @@ into the account can either be of positive or negative money
|
||||
values. Each account has a credit limit and the account is never
|
||||
allowed to have a balance below that value.
|
||||
|
||||
For simplicity we live in a world were money is composed of
|
||||
For simplicity we live in a world where money is composed of
|
||||
integers only. Also we omit the receiver/sender name, stated reason
|
||||
for transfer and the execution date. These all would have to be
|
||||
added on the ``Entry`` object.
|
||||
@@ -35,55 +35,73 @@ Our entities look like:
|
||||
|
||||
namespace Bank\Entities;
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Account
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/** @ORM\Column(type="string", unique=true) */
|
||||
private $no;
|
||||
|
||||
/** @ORM\OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"}) */
|
||||
private $entries;
|
||||
|
||||
/** @ORM\Column(type="integer") */
|
||||
private $maxCredit = 0;
|
||||
|
||||
public function __construct($no, $maxCredit = 0)
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private ?int $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", unique=true)
|
||||
*/
|
||||
private string $no;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"})
|
||||
*/
|
||||
private array $entries;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private int $maxCredit = 0;
|
||||
|
||||
public function __construct(string $no, int $maxCredit = 0)
|
||||
{
|
||||
$this->no = $no;
|
||||
$this->maxCredit = $maxCredit;
|
||||
$this->entries = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Entry
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/** @ORM\ManyToOne(targetEntity="Account", inversedBy="entries") */
|
||||
private $account;
|
||||
|
||||
/** @ORM\Column(type="integer") */
|
||||
private $amount;
|
||||
|
||||
public function __construct($account, $amount)
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private ?int $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Account", inversedBy="entries")
|
||||
*/
|
||||
private Account $account;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private int $amount;
|
||||
|
||||
public function __construct(Account $account, int $amount)
|
||||
{
|
||||
$this->account = $account;
|
||||
$this->amount = $amount;
|
||||
// more stuff here, from/to whom, stated reason, execution date and such
|
||||
}
|
||||
|
||||
public function getAmount()
|
||||
|
||||
public function getAmount(): Amount
|
||||
{
|
||||
return $this->amount;
|
||||
}
|
||||
@@ -141,12 +159,14 @@ collection, which means we can compute this value at runtime:
|
||||
class Account
|
||||
{
|
||||
// .. previous code
|
||||
public function getBalance()
|
||||
|
||||
public function getBalance(): int
|
||||
{
|
||||
$balance = 0;
|
||||
foreach ($this->entries as $entry) {
|
||||
$balance += $entry->getAmount();
|
||||
}
|
||||
|
||||
return $balance;
|
||||
}
|
||||
}
|
||||
@@ -170,13 +190,12 @@ relation with this method:
|
||||
<?php
|
||||
class Account
|
||||
{
|
||||
public function addEntry($amount)
|
||||
public function addEntry(int $amount): void
|
||||
{
|
||||
$this->assertAcceptEntryAllowed($amount);
|
||||
|
||||
|
||||
$e = new Entry($this, $amount);
|
||||
$this->entries[] = $e;
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,25 +204,28 @@ Now look at the following test-code for our entities:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class AccountTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AccountTest extends TestCase
|
||||
{
|
||||
public function testAddEntry()
|
||||
{
|
||||
$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");
|
||||
|
||||
$this->expectException(Exception::class);
|
||||
$account->addEntry(-1000);
|
||||
}
|
||||
}
|
||||
@@ -214,9 +236,12 @@ To enforce our rule we can now implement the assertion in
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
class Account
|
||||
{
|
||||
private function assertAcceptEntryAllowed($amount)
|
||||
// .. previous code
|
||||
|
||||
private function assertAcceptEntryAllowed(int $amount): void
|
||||
{
|
||||
$futureBalance = $this->getBalance() + $amount;
|
||||
$allowedMinimalBalance = ($this->maxCredit * -1);
|
||||
@@ -263,21 +288,20 @@ entries collection) we want to add an aggregate field called
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $balance = 0;
|
||||
|
||||
public function getBalance()
|
||||
private int $balance = 0;
|
||||
|
||||
public function getBalance(): int
|
||||
{
|
||||
return $this->balance;
|
||||
}
|
||||
|
||||
public function addEntry($amount)
|
||||
|
||||
public function addEntry(int $amount): void
|
||||
{
|
||||
$this->assertAcceptEntryAllowed($amount);
|
||||
|
||||
|
||||
$e = new Entry($this, $amount);
|
||||
$this->entries[] = $e;
|
||||
$this->balance += $amount;
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,16 +325,19 @@ potentially lead to inconsistent state. See this example:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Bank\Entities\Account;
|
||||
|
||||
// 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);
|
||||
|
||||
$account1 = $em->find(Account::class, $accId);
|
||||
|
||||
// request 2 account
|
||||
$account2 = $em->find('Bank\Entities\Account', $accId);
|
||||
|
||||
$account2 = $em->find(Account::class, $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
|
||||
@@ -327,10 +354,14 @@ Optimistic locking is as easy as adding a version column:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
class Account
|
||||
{
|
||||
/** @ORM\Column(type="integer") @ORM\Version */
|
||||
private $version;
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\Version
|
||||
*/
|
||||
private int $version;
|
||||
}
|
||||
|
||||
The previous example would then throw an exception in the face of
|
||||
@@ -344,9 +375,11 @@ the database using a FOR UPDATE.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Bank\Entities\Account;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
|
||||
$account = $em->find('Bank\Entities\Account', $accId, LockMode::PESSIMISTIC_READ);
|
||||
$account = $em->find(Account::class, $accId, LockMode::PESSIMISTIC_READ);
|
||||
|
||||
Keeping Updates and Deletes in Sync
|
||||
-----------------------------------
|
||||
@@ -367,4 +400,3 @@ field that offers serious performance benefits over iterating all
|
||||
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
|
||||
{
|
||||
/** @ORM\Column(type="mytype") */
|
||||
/** @Column(type="mytype") */
|
||||
private $field;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ Persisting the Decorator Pattern
|
||||
.. sectionauthor:: Chris Woodford <chris.woodford@gmail.com>
|
||||
|
||||
This recipe will show you a simple example of how you can use
|
||||
Doctrine 2 to persist an implementation of the
|
||||
`Decorator Pattern <https://en.wikipedia.org/wiki/Decorator_pattern>`_
|
||||
Doctrine ORM to persist an implementation of the
|
||||
`Decorator Pattern <http://en.wikipedia.org/wiki/Decorator_pattern>`_
|
||||
|
||||
Component
|
||||
---------
|
||||
@@ -23,26 +23,23 @@ concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.
|
||||
|
||||
namespace Test;
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\InheritanceType("SINGLE_TABLE")
|
||||
* @ORM\DiscriminatorColumn(name="discr", type="string")
|
||||
* @ORM\DiscriminatorMap({
|
||||
* "cc" = "Test\Component\ConcreteComponent",
|
||||
* "cd" = "Test\Decorator\ConcreteDecorator"
|
||||
* })
|
||||
* @Entity
|
||||
* @InheritanceType("SINGLE_TABLE")
|
||||
* @DiscriminatorColumn(name="discr", type="string")
|
||||
* @DiscriminatorMap({"cc" = "Test\Component\ConcreteComponent",
|
||||
"cd" = "Test\Decorator\ConcreteDecorator"})
|
||||
*/
|
||||
abstract class Component
|
||||
{
|
||||
|
||||
/**
|
||||
* @ORM\Id @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
* @Id @Column(type="integer")
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/** @ORM\Column(type="string", nullable=true) */
|
||||
/** @Column(type="string", nullable=true) */
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
@@ -87,10 +84,9 @@ purpose of keeping this example simple).
|
||||
|
||||
namespace Test\Component;
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
use Test\Component;
|
||||
|
||||
/** @ORM\Entity */
|
||||
/** @Entity */
|
||||
class ConcreteComponent extends Component
|
||||
{}
|
||||
|
||||
@@ -107,15 +103,13 @@ use a ``MappedSuperclass`` for this.
|
||||
|
||||
namespace Test;
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\MappedSuperclass */
|
||||
/** @MappedSuperclass */
|
||||
abstract class Decorator extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* @ORM\OneToOne(targetEntity="Test\Component", cascade={"all"})
|
||||
* @ORM\JoinColumn(name="decorates", referencedColumnName="id")
|
||||
* @OneToOne(targetEntity="Test\Component", cascade={"all"})
|
||||
* @JoinColumn(name="decorates", referencedColumnName="id")
|
||||
*/
|
||||
protected $decorates;
|
||||
|
||||
@@ -191,14 +185,13 @@ of the getSpecial() method to its return value.
|
||||
|
||||
namespace Test\Decorator;
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
use Test\Decorator;
|
||||
|
||||
/** @ORM\Entity */
|
||||
/** @Entity */
|
||||
class ConcreteDecorator extends Decorator
|
||||
{
|
||||
|
||||
/** @ORM\Column(type="string", nullable=true) */
|
||||
/** @Column(type="string", nullable=true) */
|
||||
protected $special;
|
||||
|
||||
/**
|
||||
@@ -244,7 +237,7 @@ objects
|
||||
use Test\Component\ConcreteComponent,
|
||||
Test\Decorator\ConcreteDecorator;
|
||||
|
||||
// assumes Doctrine 2 is configured and an instance of
|
||||
// assumes Doctrine ORM is configured and an instance of
|
||||
// an EntityManager is available as $em
|
||||
|
||||
// create a new concrete component
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Extending DQL in Doctrine 2: Custom AST Walkers
|
||||
Extending DQL in Doctrine ORM: Custom AST Walkers
|
||||
===============================================
|
||||
|
||||
.. sectionauthor:: Benjamin Eberlei <kontakt@beberlei.de>
|
||||
@@ -12,9 +12,9 @@ the Doctrine ORM.
|
||||
|
||||
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,
|
||||
Doctrine ORM in contrast has a real parser for the DQL language,
|
||||
which transforms the DQL statement into an
|
||||
`Abstract Syntax Tree <https://en.wikipedia.org/wiki/Abstract_syntax_tree>`_
|
||||
`Abstract Syntax Tree <http://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,6 +28,7 @@ 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.
|
||||
@@ -39,6 +40,7 @@ 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
|
||||
@@ -86,7 +88,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();
|
||||
|
||||
@@ -99,12 +101,12 @@ The ``Paginate::count(Query $query)`` looks like:
|
||||
{
|
||||
static public function count(Query $query)
|
||||
{
|
||||
/** @var Query $countQuery */
|
||||
/* @var $countQuery Query */
|
||||
$countQuery = clone $query;
|
||||
|
||||
|
||||
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('DoctrineExtensions\Paginate\CountSqlWalker'));
|
||||
$countQuery->setFirstResult(null)->setMaxResults(null);
|
||||
|
||||
|
||||
return $countQuery->getSingleScalarResult();
|
||||
}
|
||||
}
|
||||
@@ -128,20 +130,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
|
||||
@@ -165,7 +167,7 @@ can be set via ``Query::setHint($name, $value)`` as shown in the
|
||||
previous example with the ``HINT_CUSTOM_TREE_WALKERS`` query hint.
|
||||
|
||||
We will implement a custom Output Walker that allows to specify the
|
||||
SQL\_NO\_CACHE query hint.
|
||||
``SQL_NO_CACHE`` query hint.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -178,7 +180,7 @@ SQL\_NO\_CACHE query hint.
|
||||
|
||||
Our ``MysqlWalker`` will extend the default ``SqlWalker``. We will
|
||||
modify the generation of the SELECT clause, adding the
|
||||
SQL\_NO\_CACHE on those queries that need it:
|
||||
``SQL_NO_CACHE`` on those queries that need it:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -194,7 +196,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);
|
||||
@@ -202,7 +204,7 @@ SQL\_NO\_CACHE on those queries that need it:
|
||||
$sql = str_replace('SELECT', 'SELECT SQL_NO_CACHE', $sql);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
@@ -212,3 +214,4 @@ 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.
|
||||
|
||||
|
||||
@@ -10,13 +10,14 @@ change it during the life of your project. This decision for a
|
||||
specific vendor potentially allows you to make use of powerful SQL
|
||||
features that are unique to the vendor.
|
||||
|
||||
It is worth to mention that Doctrine 2 also allows you to handwrite
|
||||
It is worth to mention that Doctrine ORM also allows you to handwrite
|
||||
your SQL instead of extending the DQL parser. Extending DQL is sort of an
|
||||
advanced extension point. You can map arbitrary SQL to your objects
|
||||
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
|
||||
@@ -44,7 +45,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
|
||||
@@ -95,7 +96,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)
|
||||
@@ -105,7 +106,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(' .
|
||||
@@ -179,28 +180,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(' .
|
||||
@@ -239,10 +240,12 @@ functionalities in DQL, we would be excited to see user extensions
|
||||
that add vendor specific function packages, for example more math
|
||||
functions, XML + GIS Support, Hashing functions and so on.
|
||||
|
||||
For 2.0 we will come with the current set of functions, however for
|
||||
For ORM we will come with the current set of functions, however for
|
||||
a future version we will re-evaluate if we can abstract even more
|
||||
vendor sql functions and extend the DQL languages scope.
|
||||
|
||||
Code for this Extension to DQL and other Doctrine Extensions can be
|
||||
found
|
||||
`in the GitHub DoctrineExtensions repository <https://github.com/beberlei/DoctrineExtensions>`_.
|
||||
`in the GitHub DoctrineExtensions repository <http://github.com/beberlei/DoctrineExtensions>`_.
|
||||
|
||||
|
||||
|
||||
@@ -12,14 +12,12 @@ this working.
|
||||
Merging entity into an EntityManager
|
||||
------------------------------------
|
||||
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
It is a good idea to avoid storing entities in serialized formats such as
|
||||
``$_SESSION``: instead, store the entity identifiers or raw data.
|
||||
references between other entities. You can achieve this by calling
|
||||
``EntityManager#merge()``.
|
||||
|
||||
For a representative User object the code to get turn an instance from
|
||||
the session into a managed Doctrine object looks like this:
|
||||
@@ -28,28 +26,43 @@ 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'])) {
|
||||
$user = $em->find(User::class, $_SESSION['user']);
|
||||
|
||||
if (! $user instanceof User) {
|
||||
// user not found in the database
|
||||
$_SESSION['user'] = null;
|
||||
}
|
||||
if (isset($_SESSION['user']) && $_SESSION['user'] instanceof User) {
|
||||
$user = $_SESSION['user'];
|
||||
$user = $em->merge($user);
|
||||
}
|
||||
|
||||
Serializing entities into the session
|
||||
-------------------------------------
|
||||
.. note::
|
||||
|
||||
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.
|
||||
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 their
|
||||
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.
|
||||
|
||||
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 <https://martinfowler.com/eaaCatalog/layerSupertype.html>`_
|
||||
`Layer Supertype <http://martinfowler.com/eaaCatalog/layerSupertype.html>`_
|
||||
for all our domain objects.
|
||||
|
||||
Option 1
|
||||
@@ -16,6 +16,7 @@ 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
|
||||
|
||||
@@ -27,15 +28,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;
|
||||
}
|
||||
@@ -49,6 +50,7 @@ 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
|
||||
@@ -63,15 +65,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);
|
||||
}
|
||||
@@ -93,17 +95,18 @@ 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,9 +7,14 @@ The NOTIFY change-tracking policy is the most effective
|
||||
change-tracking policy provided by Doctrine but it requires some
|
||||
boilerplate code. This recipe will show you how this boilerplate
|
||||
code should look like. We will implement it on a
|
||||
`Layer Supertype <https://martinfowler.com/eaaCatalog/layerSupertype.html>`_
|
||||
`Layer Supertype <http://martinfowler.com/eaaCatalog/layerSupertype.html>`_
|
||||
for all our domain objects.
|
||||
|
||||
.. note::
|
||||
|
||||
The notify change tracking policy is deprecated and will be removed in ORM 3.0.
|
||||
(`Details <https://github.com/doctrine/orm/issues/8383>`_)
|
||||
|
||||
Implementing NotifyPropertyChanged
|
||||
----------------------------------
|
||||
|
||||
@@ -22,17 +27,17 @@ implement the ``NotifyPropertyChanged`` interface from the
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\NotifyPropertyChanged;
|
||||
use Doctrine\Common\PropertyChangedListener;
|
||||
|
||||
use Doctrine\Persistence\NotifyPropertyChanged;
|
||||
use Doctrine\Persistence\PropertyChangedListener;
|
||||
|
||||
abstract class DomainObject implements NotifyPropertyChanged
|
||||
{
|
||||
private $listeners = array();
|
||||
|
||||
|
||||
public function addPropertyChangedListener(PropertyChangedListener $listener) {
|
||||
$this->listeners[] = $listener;
|
||||
}
|
||||
|
||||
|
||||
/** Notifies listeners of a change. */
|
||||
protected function onPropertyChanged($propName, $oldValue, $newValue) {
|
||||
if ($this->listeners) {
|
||||
@@ -50,12 +55,12 @@ listeners:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// Mapping not shown, either in annotations or xml as usual
|
||||
// Mapping not shown, either in annotations, xml or yaml as usual
|
||||
class MyEntity extends DomainObject
|
||||
{
|
||||
private $data;
|
||||
// ... other fields as usual
|
||||
|
||||
|
||||
public function setData($data) {
|
||||
if ($data != $this->data) { // check: is it actually modified?
|
||||
$this->onPropertyChanged('data', $this->data, $data);
|
||||
@@ -69,3 +74,4 @@ not mandatory but recommended. That way you can avoid unnecessary
|
||||
updates and also have full control over when you consider a
|
||||
property changed.
|
||||
|
||||
|
||||
|
||||
78
docs/en/cookbook/implementing-wakeup-or-clone.rst
Normal file
78
docs/en/cookbook/implementing-wakeup-or-clone.rst
Normal file
@@ -0,0 +1,78 @@
|
||||
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,12 +1,12 @@
|
||||
Mysql Enums
|
||||
===========
|
||||
|
||||
The type system of Doctrine 2 consists of flyweights, which means there is only
|
||||
The type system of Doctrine ORM consists of flyweights, which means there is only
|
||||
one instance of any given type. Additionally types do not contain state. Both
|
||||
assumptions make it rather complicated to work with the Enum Type of MySQL that
|
||||
is used quite a lot by developers.
|
||||
|
||||
When using Enums with a non-tweaked Doctrine 2 application you will get
|
||||
When using Enums with a non-tweaked Doctrine ORM application you will get
|
||||
errors from the Schema-Tool commands due to the unknown database type "enum".
|
||||
By default Doctrine does not map the MySQL enum type to a Doctrine type.
|
||||
This is because Enums contain state (their allowed values) and Doctrine
|
||||
@@ -43,16 +43,13 @@ entities:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\Entity */
|
||||
/** @Entity */
|
||||
class Article
|
||||
{
|
||||
const STATUS_VISIBLE = 'visible';
|
||||
const STATUS_INVISIBLE = 'invisible';
|
||||
|
||||
/** @ORM\Column(type="string") */
|
||||
/** @Column(type="string") */
|
||||
private $status;
|
||||
|
||||
public function setStatus($status)
|
||||
@@ -70,13 +67,10 @@ the **columnDefinition** attribute.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\Entity */
|
||||
/** @Entity */
|
||||
class Article
|
||||
{
|
||||
/** @ORM\Column(type="string", columnDefinition="ENUM('visible', 'invisible')") */
|
||||
/** @Column(type="string", columnDefinition="ENUM('visible', 'invisible')") */
|
||||
private $status;
|
||||
}
|
||||
|
||||
@@ -137,13 +131,10 @@ Then in your entity you can just use this type:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\Entity */
|
||||
/** @Entity */
|
||||
class Article
|
||||
{
|
||||
/** @ORM\Column(type="enumvisibility") */
|
||||
/** @Column(type="enumvisibility") */
|
||||
private $status;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
Keeping your Modules independent
|
||||
=================================
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
One of the goals of using modules is to create discrete units of functionality
|
||||
that do not have many (if any) dependencies, allowing you to use that
|
||||
functionality in other applications without including unnecessary items.
|
||||
|
||||
Doctrine 2.2 includes a new utility called the ``ResolveTargetEntityListener``,
|
||||
Doctrine ORM includes a new utility called the ``ResolveTargetEntityListener``,
|
||||
that functions by intercepting certain calls inside Doctrine and rewrite
|
||||
targetEntity parameters in your metadata mapping at runtime. It means that
|
||||
in your bundle you are able to use an interface or abstract class in your
|
||||
@@ -68,7 +66,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,3 +137,4 @@ 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,32 +23,31 @@ appropriate autoloaders.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
namespace DoctrineExtensions;
|
||||
|
||||
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
|
||||
use Doctrine\ORM\Mapping;
|
||||
|
||||
use \Doctrine\ORM\Event\LoadClassMetadataEventArgs;
|
||||
|
||||
class TablePrefix
|
||||
{
|
||||
protected $prefix = '';
|
||||
|
||||
|
||||
public function __construct($prefix)
|
||||
{
|
||||
$this->prefix = (string) $prefix;
|
||||
}
|
||||
|
||||
|
||||
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
|
||||
{
|
||||
$classMetadata = $eventArgs->getClassMetadata();
|
||||
|
||||
if ($classMetadata->inheritanceType !== Mapping\InheritanceType::SINGLE_TABLE ||
|
||||
$classMetadata->getName() === $classMetadata->rootEntityName) {
|
||||
$classMetadata->setTableName($this->prefix . $classMetadata->getTableName());
|
||||
if (!$classMetadata->isInheritanceTypeSingleTable() || $classMetadata->getName() === $classMetadata->rootEntityName) {
|
||||
$classMetadata->setPrimaryTable([
|
||||
'name' => $this->prefix . $classMetadata->getTableName()
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($classMetadata->associationMappings as $fieldName => $mapping) {
|
||||
if ($mapping['type'] == Mapping\ClassMetadata::MANY_TO_MANY && $mapping['isOwningSide']) {
|
||||
foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {
|
||||
if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY && $mapping['isOwningSide']) {
|
||||
$mappedTableName = $mapping['joinTable']['name'];
|
||||
$classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName;
|
||||
}
|
||||
@@ -69,17 +68,19 @@ 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);
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ 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.
|
||||
@@ -22,13 +23,14 @@ 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
|
||||
every panel-type? This wouldn't be flexible. You might be tempted
|
||||
to add an AbstractPanelEntity and an AbstractBlockEntity that use
|
||||
class inheritance. Your page could then only confer to the
|
||||
AbstractPanelType and Doctrine 2 would do the rest for you, i.e.
|
||||
AbstractPanelType and Doctrine ORM would do the rest for you, i.e.
|
||||
load the right entities. But - you'll for sure have lots of panels
|
||||
and blocks, and even worse, you'd have to edit the discriminator
|
||||
map *manually* every time you or another developer implements a new
|
||||
@@ -56,6 +58,7 @@ the middle of your page, for example).
|
||||
|
||||
Such an interface could look like this:
|
||||
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
@@ -84,12 +87,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.
|
||||
@@ -97,7 +100,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.
|
||||
@@ -115,21 +118,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
|
||||
@@ -149,16 +152,16 @@ As you can see, we have a method "setBlockEntity" which ties a potential strateg
|
||||
|
||||
/**
|
||||
* This var contains the classname of the strategy
|
||||
* that is used for this blockitem. (This string (!) value will be persisted by Doctrine 2)
|
||||
* that is used for this blockitem. (This string (!) value will be persisted by Doctrine ORM)
|
||||
*
|
||||
* This is a doctrine field, so make sure that you use an @column annotation or setup your
|
||||
* xml files correctly
|
||||
* yaml or xml files correctly
|
||||
* @var string
|
||||
*/
|
||||
protected $strategyClassName;
|
||||
|
||||
/**
|
||||
* This var contains an instance of $this->blockStrategy. Will not be persisted by Doctrine 2.
|
||||
* This var contains an instance of $this->blockStrategy. Will not be persisted by Doctrine ORM.
|
||||
*
|
||||
* @var BlockStrategyInterface
|
||||
*/
|
||||
@@ -174,7 +177,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
|
||||
*
|
||||
@@ -183,7 +186,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!
|
||||
@@ -196,7 +199,7 @@ As you can see, we have a method "setBlockEntity" which ties a potential strateg
|
||||
$strategy->setBlockEntity($this);
|
||||
}
|
||||
|
||||
Now, the important point is that $strategyClassName is a Doctrine 2
|
||||
Now, the important point is that $strategyClassName is a Doctrine ORM
|
||||
field, i.e. Doctrine will persist this value. This is only the
|
||||
class name of your strategy and not an instance!
|
||||
|
||||
@@ -210,28 +213,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();
|
||||
@@ -248,3 +251,4 @@ This might look like this:
|
||||
In this example, even some variables are set - like a view object
|
||||
or a specific configuration object.
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ Validation of Entities
|
||||
|
||||
.. sectionauthor:: Benjamin Eberlei <kontakt@beberlei.de>
|
||||
|
||||
Doctrine 2 does not ship with any internal validators, the reason
|
||||
Doctrine ORM does not ship with any internal validators, the reason
|
||||
being that we think all the frameworks out there already ship with
|
||||
quite decent ones that can be integrated into your Domain easily.
|
||||
What we offer are hooks to execute any kind of validation.
|
||||
@@ -15,6 +15,7 @@ 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
|
||||
@@ -24,8 +25,8 @@ the additional benefit of being able to re-use your validation in
|
||||
any other part of your domain.
|
||||
|
||||
Say we have an ``Order`` with several ``OrderLine`` instances. We
|
||||
never want to allow any customer to order for a larger sum than he
|
||||
is allowed to:
|
||||
never want to allow any customer to order for a larger sum than they
|
||||
are allowed to:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -35,12 +36,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();
|
||||
}
|
||||
@@ -57,17 +58,14 @@ First Annotations:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\HasLifecycleCallbacks
|
||||
* @Entity
|
||||
* @HasLifecycleCallbacks
|
||||
*/
|
||||
class Order
|
||||
{
|
||||
/**
|
||||
* @ORM\PrePersist @ORM\PreUpdate
|
||||
* @PrePersist @PreUpdate
|
||||
*/
|
||||
public function assertCustomerAllowedBuying() {}
|
||||
}
|
||||
@@ -79,12 +77,15 @@ 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
|
||||
@@ -98,24 +99,21 @@ validation callbacks.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
class Order
|
||||
{
|
||||
/**
|
||||
* @ORM\PrePersist @ORM\PreUpdate
|
||||
* @PrePersist @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();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ Working with DateTime Instances
|
||||
|
||||
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.
|
||||
interesting pieces of information on how to work with PHP DateTime instances in ORM.
|
||||
|
||||
DateTime changes are detected by Reference
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -15,13 +15,10 @@ these comparisons are always made **BY REFERENCE**. That means the following cha
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\Entity */
|
||||
/** @Entity */
|
||||
class Article
|
||||
{
|
||||
/** @ORM\Column(type="datetime") */
|
||||
/** @Column(type="datetime") */
|
||||
private $updated;
|
||||
|
||||
public function setUpdated()
|
||||
@@ -61,16 +58,16 @@ Handling different Timezones with the DateTime Type
|
||||
|
||||
If you first come across the requirement to save different timezones you may be still optimistic about how
|
||||
to manage this mess,
|
||||
however let me crush your expectations fast. There is not a single database out there (supported by Doctrine 2)
|
||||
however let me crush your expectations fast. There is not a single database out there (supported by Doctrine ORM)
|
||||
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 <https://derickrethans.nl/storing-date-time-in-database.html>`_.
|
||||
in Databases <http://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
|
||||
in different offset directions depending on the real location.
|
||||
|
||||
The solution for this dilemma is simple. Don't use timezones with DateTime and Doctrine 2. However there is a workaround
|
||||
The solution for this dilemma is simple. Don't use timezones with DateTime and Doctrine ORM. However there is a workaround
|
||||
that even allows correct date-time handling with timezones:
|
||||
|
||||
1. Always convert any DateTime instance to UTC.
|
||||
@@ -93,7 +90,10 @@ the UTC time at the time of the booking and the timezone the event happened in.
|
||||
|
||||
class UTCDateTimeType extends DateTimeType
|
||||
{
|
||||
static private $utc;
|
||||
/**
|
||||
* @var \DateTimeZone
|
||||
*/
|
||||
private static $utc;
|
||||
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
@@ -127,9 +127,9 @@ the UTC time at the time of the booking and the timezone the event happened in.
|
||||
return $converted;
|
||||
}
|
||||
|
||||
private static function getUtc()
|
||||
private static function getUtc(): \DateTimeZone
|
||||
{
|
||||
return self::$utc ? self::$utc : self::$utc = new \DateTimeZone('UTC');
|
||||
return self::$utc ?: self::$utc = new \DateTimeZone('UTC');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ 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:
|
||||
@@ -156,20 +157,17 @@ requiring timezoned datetimes:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
namespace Shipping;
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Entity
|
||||
*/
|
||||
class Event
|
||||
{
|
||||
/** @ORM\Column(type="datetime") */
|
||||
/** @Column(type="datetime") */
|
||||
private $created;
|
||||
|
||||
/** @ORM\Column(type="string") */
|
||||
/** @Column(type="string") */
|
||||
private $timezone;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
ORM Documentation
|
||||
=================
|
||||
Welcome to Doctrine 2 ORM's documentation!
|
||||
==========================================
|
||||
|
||||
The Doctrine ORM documentation is comprised of tutorials, a reference section and
|
||||
cookbook articles that explain different parts of the Object Relational Mapper.
|
||||
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.
|
||||
|
||||
Getting Help
|
||||
------------
|
||||
@@ -17,8 +19,110 @@ Doctrine ORM don't panic. You can get help from different sources:
|
||||
- On `Twitter <https://twitter.com/search/%23doctrine2>`_ with ``#doctrine2``
|
||||
- On `StackOverflow <https://stackoverflow.com/questions/tagged/doctrine-orm>`_
|
||||
|
||||
If you need more structure over the different topics you can browse the :doc:`table
|
||||
of contents <toc>`.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
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.
|
||||
* **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
|
||||
----------
|
||||
|
||||
* `Upgrade <https://github.com/doctrine/doctrine2/blob/master/UPGRADE.md>`_
|
||||
|
||||
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>`
|
||||
|
||||
* **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
|
||||
|
||||
113
docs/en/make.bat
Normal file
113
docs/en/make.bat
Normal file
@@ -0,0 +1,113 @@
|
||||
@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,16 +9,15 @@ steps of configuration.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Configuration;
|
||||
use Doctrine\Common\Proxy\ProxyFactory;
|
||||
use Doctrine\ORM\EntityManager,
|
||||
Doctrine\ORM\Configuration;
|
||||
|
||||
// ...
|
||||
|
||||
if ($applicationMode == "development") {
|
||||
$cache = new \Doctrine\Common\Cache\ArrayCache;
|
||||
} else {
|
||||
$cache = new \Doctrine\Common\Cache\ApcuCache;
|
||||
$cache = new \Doctrine\Common\Cache\ApcCache;
|
||||
}
|
||||
|
||||
$config = new Configuration;
|
||||
@@ -28,16 +27,17 @@ steps of configuration.
|
||||
$config->setQueryCacheImpl($cache);
|
||||
$config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies');
|
||||
$config->setProxyNamespace('MyProject\Proxies');
|
||||
$config->setAutoGenerateProxyClasses($applicationMode === 'development')
|
||||
|
||||
if ('development' === $applicationMode) {
|
||||
$config->setAutoGenerateProxyClasses(ProxyFactory::AUTOGENERATE_EVAL);
|
||||
if ($applicationMode == "development") {
|
||||
$config->setAutoGenerateProxyClasses(true);
|
||||
} else {
|
||||
$config->setAutoGenerateProxyClasses(false);
|
||||
}
|
||||
|
||||
$connectionOptions = [
|
||||
$connectionOptions = array(
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => 'database.sqlite'
|
||||
];
|
||||
);
|
||||
|
||||
$em = EntityManager::create($connectionOptions, $config);
|
||||
|
||||
@@ -50,8 +50,9 @@ 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 `APCu <https://php.net/apcu>`_. APCu provides you with
|
||||
a very fast in-memory cache storage that you can use for the metadata and
|
||||
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
|
||||
query caches as seen in the previous code snippet.
|
||||
|
||||
Configuration Options
|
||||
@@ -60,32 +61,30 @@ Configuration Options
|
||||
The following sections describe all the configuration options
|
||||
available on a ``Doctrine\ORM\Configuration`` instance.
|
||||
|
||||
Proxy Directory
|
||||
~~~~~~~~~~~~~~~
|
||||
Proxy Directory (***REQUIRED***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setProxyDir($dir);
|
||||
$config->getProxyDir();
|
||||
|
||||
Sets the directory where Doctrine generates any proxy
|
||||
Gets or 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.
|
||||
|
||||
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
|
||||
~~~~~~~~~~~~~~~
|
||||
Proxy Namespace (***REQUIRED***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setProxyNamespace($namespace);
|
||||
$config->getProxyNamespace();
|
||||
|
||||
Sets the namespace to use for generated proxy classes. For
|
||||
Gets or 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.
|
||||
|
||||
@@ -102,15 +101,19 @@ Gets or sets the metadata driver implementation that is used by
|
||||
Doctrine to acquire the object-relational metadata for your
|
||||
classes.
|
||||
|
||||
There are currently 4 available implementations:
|
||||
There are currently 5 available implementations:
|
||||
|
||||
|
||||
- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\AttributeDriver``
|
||||
- ``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
|
||||
please refer to the dedicated chapters ``XML Mapping``.
|
||||
or YamlDriver please refer to the dedicated chapters
|
||||
``XML Mapping`` and ``YAML Mapping``.
|
||||
|
||||
The annotation driver can be configured with a factory method on
|
||||
the ``Doctrine\ORM\Configuration``:
|
||||
@@ -139,7 +142,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 or xml, so that they do not need to be parsed and
|
||||
annotations, xml or yaml, so that they do not need to be parsed and
|
||||
loaded from scratch on every single request which is a waste of
|
||||
resources. The cache implementation must implement the
|
||||
``Doctrine\Common\Cache\Cache`` interface.
|
||||
@@ -148,6 +151,8 @@ 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``
|
||||
@@ -178,6 +183,8 @@ 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``
|
||||
@@ -203,8 +210,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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Auto-generating Proxy Classes (***OPTIONAL***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Proxy classes can either be generated manually through the Doctrine
|
||||
Console or automatically at runtime by Doctrine. The configuration
|
||||
@@ -217,89 +224,65 @@ option that controls this behavior is:
|
||||
|
||||
Possible values for ``$mode`` are:
|
||||
|
||||
- ``Doctrine\Common\Proxy\ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``
|
||||
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_NEVER``
|
||||
|
||||
Generate the proxy class when the proxy file does not exist.
|
||||
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\ProxyFactory::AUTOGENERATE_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 and long running
|
||||
processes.
|
||||
|
||||
- ``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:
|
||||
Never autogenerate a proxy. You will need to generate the proxies
|
||||
manually, for this use the Doctrine Console like so:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setProxyDir('path/to/generated/proxies/');
|
||||
$config->setProxyNamespace('GeneratedProxies');
|
||||
$ ./doctrine orm:generate-proxies
|
||||
|
||||
To make sure proxies are never generated by Doctrine, you'd forcefully
|
||||
generate them during deployment operations:
|
||||
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.
|
||||
|
||||
.. code-block:: sh
|
||||
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_ALWAYS``
|
||||
|
||||
$ ./vendor/bin/doctrine orm:generate-proxies
|
||||
$ composer dump-autoload
|
||||
Always generates a new proxy in every request and writes it to disk.
|
||||
|
||||
- ``Doctrine\Common\Proxy\AbstractProxyFactory::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.
|
||||
|
||||
- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_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.
|
||||
|
||||
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.
|
||||
|
||||
``setAutoGenerateProxyClasses`` can accept a boolean
|
||||
value. This is still possible, ``FALSE`` being equivalent to
|
||||
AUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS.
|
||||
|
||||
Development vs Production Configuration
|
||||
---------------------------------------
|
||||
|
||||
You should code your Doctrine2 bootstrapping with two different
|
||||
runtime models in mind. There are some serious benefits of using
|
||||
APCu or Memcache in production. In development however this will
|
||||
APC 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 disable the Auto-generating Proxy Classes
|
||||
option in production.
|
||||
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.
|
||||
|
||||
Connection Options
|
||||
------------------
|
||||
@@ -317,7 +300,7 @@ Proxy Objects
|
||||
|
||||
A proxy object is an object that is put in place or used instead of
|
||||
the "real" object. A proxy object can add behavior to the object
|
||||
being proxied without that object being aware of it. In Doctrine 2,
|
||||
being proxied without that object being aware of it. In ORM,
|
||||
proxy objects are used to realize several features but mainly for
|
||||
transparent lazy-loading.
|
||||
|
||||
@@ -327,7 +310,7 @@ of the objects. This is an essential property as without it there
|
||||
would always be fragile partial objects at the outer edges of your
|
||||
object graph.
|
||||
|
||||
Doctrine 2 implements a variant of the proxy pattern where it
|
||||
Doctrine ORM implements a variant of the proxy pattern where it
|
||||
generates classes that extend your entity classes and adds
|
||||
lazy-loading capabilities to them. Doctrine can then give you an
|
||||
instance of such a proxy class whenever you request an object of
|
||||
@@ -348,17 +331,16 @@ 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::class, $itemId);
|
||||
$item = $em->getReference('MyProject\Model\Item', $itemId);
|
||||
$cart->addItem($item);
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Association proxies
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
@@ -366,7 +348,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.
|
||||
@@ -378,12 +360,61 @@ 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
|
||||
When using different components using Doctrine ORM you may end up
|
||||
with them using two different metadata drivers, for example XML and
|
||||
annotationsL. You can use the DriverChain Metadata implementations to
|
||||
YAML. You can use the DriverChain Metadata implementations to
|
||||
aggregate these drivers based on namespaces:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -393,7 +424,7 @@ aggregate these drivers based on namespaces:
|
||||
|
||||
$chain = new DriverChain();
|
||||
$chain->addDriver($xmlDriver, 'Doctrine\Tests\Models\Company');
|
||||
$chain->addDriver($annotationDriver, 'Doctrine\Tests\ORM\Mapping');
|
||||
$chain->addDriver($yamlDriver, '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
|
||||
@@ -403,6 +434,7 @@ the entity class name against the namespace using a
|
||||
correctly if sub-namespaces use different metadata driver
|
||||
implementations.
|
||||
|
||||
|
||||
Default Repository (***OPTIONAL***)
|
||||
-----------------------------------
|
||||
|
||||
|
||||
@@ -5,29 +5,29 @@ You've probably used docblock annotations in some form already,
|
||||
most likely to provide documentation metadata for a tool like
|
||||
``PHPDocumentor`` (@author, @link, ...). Docblock annotations are a
|
||||
tool to embed metadata inside the documentation section which can
|
||||
then be processed by some tool. Doctrine 2 generalizes the concept
|
||||
then be processed by some tool. Doctrine ORM generalizes the concept
|
||||
of docblock annotations so that they can be used for any kind of
|
||||
metadata and so that it is easy to define new docblock annotations.
|
||||
In order to allow more involved annotation values and to reduce the
|
||||
chances of clashes with other docblock annotations, the Doctrine 2
|
||||
chances of clashes with other docblock annotations, the Doctrine ORM
|
||||
docblock annotations feature an alternative syntax that is heavily
|
||||
inspired by the Annotation syntax introduced in Java 5.
|
||||
|
||||
The implementation of these enhanced docblock annotations is
|
||||
located in the ``Doctrine\Common\Annotations`` namespace and
|
||||
therefore part of the Common package. Doctrine 2 docblock
|
||||
therefore part of the Common package. Doctrine ORM docblock
|
||||
annotations support namespaces and nested annotations among other
|
||||
things. The Doctrine 2 ORM defines its own set of docblock
|
||||
things. The Doctrine ORM ORM defines its own set of docblock
|
||||
annotations for supplying object-relational mapping metadata.
|
||||
|
||||
.. note::
|
||||
|
||||
If you're not comfortable with the concept of docblock
|
||||
annotations, don't worry, as mentioned earlier Doctrine 2 provides
|
||||
the XML alternative and you could easily implement your own
|
||||
annotations, don't worry, as mentioned earlier Doctrine ORM provides
|
||||
XML and YAML alternatives 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
|
||||
In this chapter a reference of every Doctrine ORM Annotation is given
|
||||
with short explanations on their context and usage.
|
||||
|
||||
Index
|
||||
@@ -89,7 +89,7 @@ as part of the lifecycle of the instance variables entity-class.
|
||||
Required attributes:
|
||||
|
||||
- **type**: Name of the Doctrine Type which is converted between PHP
|
||||
and Database representation.
|
||||
and Database representation. Default to ``string`` or :ref:`Type from PHP property type <reference-php-mapping-types>`
|
||||
|
||||
Optional attributes:
|
||||
|
||||
@@ -99,7 +99,7 @@ Optional attributes:
|
||||
|
||||
- **length**: Used by the "string" type to determine its maximum
|
||||
length in the database. Doctrine does not validate the length of a
|
||||
string values for you.
|
||||
string value for you.
|
||||
|
||||
- **precision**: The precision for a decimal (exact numeric) column
|
||||
(applies only for decimal column), which is the maximum number of
|
||||
@@ -213,7 +213,7 @@ Optional attributes:
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The Change Tracking Policy annotation allows to specify how the
|
||||
Doctrine 2 UnitOfWork should detect changes in properties of
|
||||
Doctrine ORM UnitOfWork should detect changes in properties of
|
||||
entities during flush. By default each entity is checked according
|
||||
to a deferred implicit strategy, which means upon flush UnitOfWork
|
||||
compares all the properties of an entity to a previously stored
|
||||
@@ -276,11 +276,13 @@ 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.
|
||||
|
||||
@@ -310,6 +312,7 @@ depending on whether the classes are in the namespace or not.
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
.. _annref_embeddable:
|
||||
|
||||
@Embeddable
|
||||
@@ -336,6 +339,7 @@ annotation to establish the relationship between the two classes.
|
||||
*/
|
||||
private $address;
|
||||
|
||||
|
||||
.. _annref_embedded:
|
||||
|
||||
@Embedded
|
||||
@@ -346,7 +350,8 @@ in order to specify that it is an embedded class.
|
||||
|
||||
Required attributes:
|
||||
|
||||
- **class**: The embeddable class
|
||||
- **class**: The embeddable class. You can omit this value if you use a PHP property type instead.
|
||||
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -367,6 +372,7 @@ Required attributes:
|
||||
{
|
||||
// ...
|
||||
|
||||
|
||||
.. _annref_entity:
|
||||
|
||||
@Entity
|
||||
@@ -377,11 +383,12 @@ 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
|
||||
Layer.
|
||||
- **readOnly**: (>= 2.1) Specifies that this entity is marked as read only and not
|
||||
- **readOnly**: Specifies that this entity is marked as read only and not
|
||||
considered for change-tracking. Entities of this type can be persisted
|
||||
and removed though.
|
||||
|
||||
@@ -391,11 +398,11 @@ Example:
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity(repositoryClass="MyProject\UserRepository", readOnly=true)
|
||||
* @Entity(repositoryClass="MyProject\UserRepository")
|
||||
*/
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
//...
|
||||
}
|
||||
|
||||
.. _annref_entity_result:
|
||||
@@ -426,6 +433,7 @@ Required attributes:
|
||||
|
||||
- **name**: Name of the persistent field or property of the class.
|
||||
|
||||
|
||||
Optional attributes:
|
||||
|
||||
- **column**: Name of the column in the SELECT clause.
|
||||
@@ -445,9 +453,9 @@ used as default.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **strategy**: Set the name of the identifier generation strategy.
|
||||
Valid values are ``AUTO``, ``SEQUENCE``, ``TABLE``, ``IDENTITY``, ``CUSTOM`` and ``NONE``, explained
|
||||
in the :ref:`Identifier Generation Strategies <identifier-generation-strategies>` section.
|
||||
Valid values are AUTO, SEQUENCE, TABLE, IDENTITY, UUID, CUSTOM and NONE.
|
||||
If not specified, default value is AUTO.
|
||||
|
||||
Example:
|
||||
@@ -503,8 +511,10 @@ has meaning in the SchemaTool schema generation context.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **name**: Name of the Index
|
||||
- **columns**: Array of columns.
|
||||
- **fields**: Array of fields. Exactly one of **fields**, **columns** is required.
|
||||
- **columns**: Array of columns. Exactly one of **fields**, **columns** is required.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
@@ -526,6 +536,19 @@ Basic example:
|
||||
{
|
||||
}
|
||||
|
||||
Basic example using fields:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="ecommerce_products",indexes={@Index(name="search_idx", fields={"name", "email"})})
|
||||
*/
|
||||
class ECommerceProduct
|
||||
{
|
||||
}
|
||||
|
||||
Example with partial indexes:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -610,20 +633,17 @@ Examples:
|
||||
This annotation is used in the context of relations in
|
||||
:ref:`@ManyToOne <annref_manytoone>`, :ref:`@OneToOne <annref_onetoone>` fields
|
||||
and in the Context of :ref:`@JoinTable <annref_jointable>` nested inside
|
||||
a @ManyToMany. This annotation is not required. If it is not
|
||||
specified the attributes *name* and *referencedColumnName* are
|
||||
inferred from the table and primary key names.
|
||||
a @ManyToMany. If this annotation or both *name* and *referencedColumnName*
|
||||
are missing they will be computed considering the field's name and the current
|
||||
:doc:`naming strategy <namingstrategy>`.
|
||||
|
||||
Required attributes:
|
||||
Optional 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.
|
||||
- **referencedColumnName**: Name of the primary key identifier that
|
||||
is used for joining of this relation.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
is used for joining of this relation. Defaults to *id*.
|
||||
- **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.
|
||||
@@ -674,6 +694,7 @@ 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.
|
||||
@@ -705,12 +726,15 @@ 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.
|
||||
You can omit this value if you use a PHP property type instead.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **cascade**: Cascade Option
|
||||
- **fetch**: One of LAZY or EAGER
|
||||
- inversedBy - The inversedBy attribute designates the field in
|
||||
@@ -739,12 +763,14 @@ 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.
|
||||
@@ -800,7 +826,8 @@ The @MappedSuperclass annotation cannot be used in conjunction with
|
||||
|
||||
Optional attributes:
|
||||
|
||||
- **repositoryClass**: (>= 2.2) Specifies the FQCN of a subclass of the EntityRepository.
|
||||
|
||||
- **repositoryClass**: Specifies the FQCN of a subclass of the EntityRepository.
|
||||
That will be inherited for all subclasses of that Mapped Superclass.
|
||||
|
||||
Example:
|
||||
@@ -828,6 +855,11 @@ Example:
|
||||
|
||||
@NamedNativeQuery
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. note::
|
||||
|
||||
Named Native Queries are deprecated as of version 2.9 and will be removed in ORM 3.0
|
||||
|
||||
Is used to specify a native SQL named query.
|
||||
The NamedNativeQuery annotation can be applied to an entity or mapped superclass.
|
||||
|
||||
@@ -836,11 +868,13 @@ 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
|
||||
@@ -906,12 +940,15 @@ 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.
|
||||
When typed properties are used it is inherited from PHP type.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- **cascade**: Cascade Option
|
||||
- **fetch**: One of LAZY or EAGER
|
||||
- **orphanRemoval**: Boolean that specifies if orphans, inverse
|
||||
@@ -938,12 +975,14 @@ 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,
|
||||
@@ -960,7 +999,7 @@ Example:
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @OneToMany(targetEntity="Phonenumber", mappedBy="user", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||
* @OneToMany(targetEntity="Phonenumber", mappedBy="user", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
|
||||
*/
|
||||
public $phonenumbers;
|
||||
|
||||
@@ -1068,10 +1107,12 @@ 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.
|
||||
@@ -1102,6 +1143,7 @@ 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.
|
||||
@@ -1202,13 +1244,15 @@ 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.
|
||||
- **schema**: Name of the schema the table lies in.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1238,8 +1282,10 @@ context.
|
||||
|
||||
Required attributes:
|
||||
|
||||
|
||||
- **name**: Name of the Index
|
||||
- **columns**: Array of columns.
|
||||
- **fields**: Array of fields. Exactly one of **fields**, **columns** is required.
|
||||
- **columns**: Array of columns. Exactly one of **fields**, **columns** is required.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
@@ -1261,6 +1307,19 @@ Basic example:
|
||||
{
|
||||
}
|
||||
|
||||
Basic example using fields:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="ecommerce_products",uniqueConstraints={@UniqueConstraint(name="search_idx", fields={"name", "email"})})
|
||||
*/
|
||||
class ECommerceProduct
|
||||
{
|
||||
}
|
||||
|
||||
Example with partial indexes:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -2,29 +2,29 @@ Architecture
|
||||
============
|
||||
|
||||
This chapter gives an overview of the overall architecture,
|
||||
terminology and constraints of Doctrine 2. It is recommended to
|
||||
terminology and constraints of Doctrine ORM. It is recommended to
|
||||
read this chapter carefully.
|
||||
|
||||
Using an Object-Relational Mapper
|
||||
---------------------------------
|
||||
|
||||
As the term ORM already hints at, Doctrine 2 aims to simplify the
|
||||
As the term ORM already hints at, Doctrine ORM aims to simplify the
|
||||
translation between database rows and the PHP object model. The
|
||||
primary use case for Doctrine are therefore applications that
|
||||
utilize the Object-Oriented Programming Paradigm. For applications
|
||||
that do not primarily work with objects Doctrine 2 is not suited very
|
||||
that do not primarily work with objects Doctrine ORM is not suited very
|
||||
well.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Doctrine 2 requires a minimum of PHP 7.1. For greatly improved
|
||||
Doctrine ORM requires a minimum of PHP 7.1. For greatly improved
|
||||
performance it is also recommended that you use APC with PHP.
|
||||
|
||||
Doctrine 2 Packages
|
||||
Doctrine ORM Packages
|
||||
-------------------
|
||||
|
||||
Doctrine 2 is divided into three main packages.
|
||||
Doctrine ORM is divided into three main packages.
|
||||
|
||||
- Common
|
||||
- DBAL (includes Common)
|
||||
@@ -34,6 +34,7 @@ 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
|
||||
@@ -71,16 +72,27 @@ 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
|
||||
@@ -94,12 +106,14 @@ 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).
|
||||
@@ -136,24 +150,24 @@ subsequent access must be through the interface type.
|
||||
Serializing entities
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Serializing entities is generally to be avoided.
|
||||
|
||||
If you intend to serialize (and unserialize) entity
|
||||
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
|
||||
instances that still hold references to proxy objects you may run
|
||||
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.
|
||||
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).
|
||||
|
||||
The EntityManager
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``EntityManager`` class is a central access point to the ORM
|
||||
functionality provided by Doctrine 2. The ``EntityManager`` API is
|
||||
The ``EntityManager`` class is a central access point to the
|
||||
functionality provided by Doctrine ORM. The ``EntityManager`` API is
|
||||
used to manage the persistence of your objects and to query for
|
||||
persistent objects.
|
||||
|
||||
@@ -175,8 +189,9 @@ The Unit of Work
|
||||
|
||||
Internally an ``EntityManager`` uses a ``UnitOfWork``, which is a
|
||||
typical implementation of the
|
||||
`Unit of Work pattern <https://martinfowler.com/eaaCatalog/unitOfWork.html>`_,
|
||||
`Unit of Work pattern <http://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.
|
||||
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ This chapter is split into three different sections.
|
||||
|
||||
One tip for working with relations is to read the relation from left to right, where the left word refers to the current Entity. For example:
|
||||
|
||||
- OneToMany - One instance of the current Entity has Many instances (references) to the refered Entity.
|
||||
- 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.
|
||||
- OneToMany - One instance of the current Entity has Many instances (references) to the referred Entity.
|
||||
- ManyToOne - Many instances of the current Entity refer to One instance of the referred Entity.
|
||||
- OneToOne - One instance of the current Entity refers to One instance of the referred Entity.
|
||||
|
||||
See below for all the possible relations.
|
||||
|
||||
@@ -68,6 +68,18 @@ 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
|
||||
@@ -133,6 +145,17 @@ 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.
|
||||
|
||||
@@ -159,7 +182,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`` attributes for the first time.
|
||||
Here we see the ``mappedBy`` and ``inversedBy`` annotations for the first time.
|
||||
They are used to tell Doctrine which property on the other side refers to the
|
||||
object.
|
||||
|
||||
@@ -210,6 +233,22 @@ 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.
|
||||
|
||||
@@ -220,7 +259,6 @@ 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 (
|
||||
@@ -248,7 +286,7 @@ below.
|
||||
// ...
|
||||
|
||||
/**
|
||||
* One Student has One Student.
|
||||
* One Student has One Mentor.
|
||||
* @OneToOne(targetEntity="Student")
|
||||
* @JoinColumn(name="mentor_id", referencedColumnName="id")
|
||||
*/
|
||||
@@ -334,6 +372,24 @@ 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.
|
||||
|
||||
@@ -413,6 +469,24 @@ 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
|
||||
@@ -483,6 +557,19 @@ 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.
|
||||
|
||||
@@ -554,6 +641,22 @@ 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
|
||||
@@ -651,6 +754,30 @@ 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.
|
||||
|
||||
@@ -796,6 +923,14 @@ 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:
|
||||
|
||||
@@ -821,6 +956,17 @@ 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:
|
||||
|
||||
@@ -831,10 +977,10 @@ similar defaults. As an example, consider this mapping:
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
//...
|
||||
/** @ManyToMany(targetEntity="Group") */
|
||||
private $groups;
|
||||
// ...
|
||||
//...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
@@ -845,6 +991,14 @@ 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::
|
||||
@@ -854,7 +1008,7 @@ This is essentially the same as the following, more verbose, mapping:
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
//...
|
||||
/**
|
||||
* Many Users have Many Groups.
|
||||
* @ManyToMany(targetEntity="Group")
|
||||
@@ -864,7 +1018,7 @@ This is essentially the same as the following, more verbose, mapping:
|
||||
* )
|
||||
*/
|
||||
private $groups;
|
||||
// ...
|
||||
//...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
@@ -884,6 +1038,22 @@ 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
|
||||
@@ -891,6 +1061,70 @@ join columns default to the simple, unqualified class name of the
|
||||
targeted class followed by "\_id". The referencedColumnName always
|
||||
defaults to "id", just as in one-to-one or many-to-one mappings.
|
||||
|
||||
Additionally, when using typed properties with Doctrine 2.9 or newer
|
||||
you can skip ``targetEntity`` in ``ManyToOne`` and ``OneToOne``
|
||||
associations as they will be set based on type. Also ``nullable``
|
||||
attribute on ``JoinColumn`` will be inherited from PHP type. So that:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @OneToOne */
|
||||
private Shipment $shipment;
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity class="Product">
|
||||
<one-to-one field="shipment" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Product:
|
||||
type: entity
|
||||
oneToOne:
|
||||
shipment: ~
|
||||
|
||||
Is essentially the same as following:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* One Product has One Shipment.
|
||||
* @OneToOne(targetEntity="Shipment")
|
||||
* @JoinColumn(name="shipment_id", referencedColumnName="id", nullable=false)
|
||||
*/
|
||||
private Shipment $shipment;
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity class="Product">
|
||||
<one-to-one field="shipment" target-entity="Shipment">
|
||||
<join-column name="shipment_id" referenced-column-name="id" nulable=false />
|
||||
</one-to-one>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Product:
|
||||
type: entity
|
||||
oneToOne:
|
||||
shipment:
|
||||
targetEntity: Shipment
|
||||
joinColumn:
|
||||
name: shipment_id
|
||||
referencedColumnName: id
|
||||
nullable: false
|
||||
|
||||
If you accept these defaults, you can reduce the mapping code to a
|
||||
minimum.
|
||||
|
||||
|
||||
1032
docs/en/reference/attributes-reference.rst
Normal file
1032
docs/en/reference/attributes-reference.rst
Normal file
File diff suppressed because it is too large
Load Diff
@@ -52,15 +52,16 @@ 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 XML.
|
||||
many examples also show the equivalent configuration in YAML and XML.
|
||||
|
||||
.. note::
|
||||
|
||||
All metadata drivers perform equally. Once the metadata of a class has been
|
||||
read from the source (annotations or xml) it is stored in an instance
|
||||
read from the source (annotations, xml or yaml) 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.
|
||||
@@ -75,7 +76,7 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward:
|
||||
/** @Entity */
|
||||
class Message
|
||||
{
|
||||
// ...
|
||||
//...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
@@ -86,6 +87,12 @@ 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:
|
||||
@@ -101,7 +108,7 @@ You can change this by configuring information about the table:
|
||||
*/
|
||||
class Message
|
||||
{
|
||||
// ...
|
||||
//...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
@@ -112,6 +119,13 @@ 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
|
||||
@@ -151,6 +165,19 @@ 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:
|
||||
|
||||
@@ -184,6 +211,25 @@ list:
|
||||
- ``options``: (optional) Key-value pairs of options that get passed
|
||||
to the underlying database platform when generating DDL statements.
|
||||
|
||||
.. _reference-php-mapping-types:
|
||||
|
||||
PHP Types Mapping
|
||||
_________________
|
||||
|
||||
Since version 2.9 Doctrine can determine usable defaults from property types
|
||||
on entity classes. When property type is nullable the default for ``nullable``
|
||||
Column attribute is set to TRUE. Additionally, Doctrine will map PHP types
|
||||
to ``type`` attribute as follows:
|
||||
|
||||
- ``DateInterval``: ``dateinterval``
|
||||
- ``DateTime``: ``datetime``
|
||||
- ``DateTimeImmutable``: ``datetime_immutable``
|
||||
- ``array``: ``json``
|
||||
- ``bool``: ``boolean``
|
||||
- ``float``: ``float``
|
||||
- ``int``: ``integer``
|
||||
- ``string`` or any other type: ``string``
|
||||
|
||||
.. _reference-mapping-types:
|
||||
|
||||
Doctrine Mapping Types
|
||||
@@ -200,48 +246,36 @@ 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 an SQL VARCHAR to a PHP string.
|
||||
- ``integer``: Type that maps an SQL INT to a PHP integer.
|
||||
- ``string``: Type that maps a SQL VARCHAR to a PHP string.
|
||||
- ``integer``: Type that maps a SQL INT to a PHP integer.
|
||||
- ``smallint``: Type that maps a database SMALLINT to a PHP
|
||||
integer.
|
||||
- ``bigint``: Type that maps a database BIGINT to a PHP string.
|
||||
- ``boolean``: Type that maps 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
|
||||
- ``boolean``: Type that maps a SQL boolean or equivalent (TINYINT) to a PHP boolean.
|
||||
- ``decimal``: Type that maps a SQL DECIMAL to a PHP string.
|
||||
- ``date``: Type that maps a SQL DATETIME to a PHP DateTime
|
||||
object.
|
||||
- ``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
|
||||
- ``time``: Type that maps a SQL TIME to a PHP DateTime object.
|
||||
- ``datetime``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP
|
||||
DateTime object.
|
||||
- ``datetimetz``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP
|
||||
DateTime object with timezone.
|
||||
- ``text``: Type that maps a SQL CLOB to a PHP string.
|
||||
- ``object``: Type that maps a SQL CLOB to a PHP object using
|
||||
``serialize()`` and ``unserialize()``
|
||||
- ``array``: Type that maps an SQL CLOB to a PHP array using
|
||||
- ``array``: Type that maps a SQL CLOB to a PHP array using
|
||||
``serialize()`` and ``unserialize()``
|
||||
- ``simple_array``: Type that maps an SQL CLOB to a one-dimensional PHP array using
|
||||
- ``simple_array``: Type that maps a SQL CLOB to a PHP array using
|
||||
``implode()`` and ``explode()``, with a comma as delimiter. *IMPORTANT*
|
||||
Only use this type if you are sure that your values cannot contain a ",".
|
||||
- ``json_array``: Type that maps 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
|
||||
- ``json_array``: Type that maps a SQL CLOB to a PHP array using
|
||||
``json_encode()`` and ``json_decode()``
|
||||
- ``float``: Type that maps a SQL Float (Double Precision) to a
|
||||
PHP double. *IMPORTANT*: Works only with locale settings that use
|
||||
decimal points as separator.
|
||||
- ``guid``: Type that maps a database GUID/UUID to a PHP string. Defaults to
|
||||
varchar but uses a specific type if the platform supports it.
|
||||
- ``blob``: Type that maps an SQL BLOB to a PHP resource stream
|
||||
- ``binary``: Type that maps an SQL binary to a PHP resource stream
|
||||
- ``blob``: Type that maps a SQL BLOB to a PHP resource stream
|
||||
|
||||
A cookbook article shows how to define :doc:`your own custom mapping types
|
||||
<../cookbook/custom-mapping-types>`.
|
||||
@@ -255,7 +289,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() <https://php.net/manual/en/function.date-default-timezone-set.php>`_
|
||||
set by `date_default_timezone_set() <http://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.
|
||||
|
||||
@@ -285,7 +319,7 @@ annotation.
|
||||
* @GeneratedValue
|
||||
*/
|
||||
private $id;
|
||||
// ...
|
||||
//...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
@@ -299,12 +333,22 @@ 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:
|
||||
database vendor prefers: AUTO_INCREMENT with MySQL, sequences with PostgreSQL
|
||||
and Oracle and so on.
|
||||
|
||||
Identifier Generation Strategies
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -330,6 +374,8 @@ 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!***
|
||||
@@ -360,7 +406,7 @@ besides specifying the sequence's name:
|
||||
* @SequenceGenerator(sequenceName="message_seq", initialValue=1, allocationSize=100)
|
||||
*/
|
||||
protected $id = null;
|
||||
// ...
|
||||
//...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
@@ -374,6 +420,20 @@ 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.
|
||||
|
||||
@@ -382,7 +442,7 @@ performance of Doctrine. The allocationSize specifies by how much
|
||||
values the sequence is incremented whenever the next value is
|
||||
retrieved. If this is larger than 1 (one) Doctrine can generate
|
||||
identifier values for the allocationSizes amount of entities. In
|
||||
the above example with ``allocationSize=100`` Doctrine 2 would only
|
||||
the above example with ``allocationSize=100`` Doctrine ORM would only
|
||||
need to access the sequence once to generate the identifiers for
|
||||
100 new entities.
|
||||
|
||||
@@ -397,6 +457,7 @@ 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
|
||||
@@ -405,10 +466,11 @@ 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
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
With Doctrine 2 you can use composite primary keys, using ``@Id`` on more then
|
||||
With Doctrine ORM you can use composite primary keys, using ``@Id`` on more then
|
||||
one column. Some restrictions exist opposed to using a single identifier in
|
||||
this case: The use of the ``@GeneratedValue`` annotation is not supported,
|
||||
which means you can only use composite keys if you generate the primary key
|
||||
@@ -441,15 +503,11 @@ according to the used database platform.
|
||||
|
||||
.. _reference-basic-mapping-custom-mapping-types:
|
||||
|
||||
.. versionadded: 2.3
|
||||
|
||||
For more control over column quoting the ``Doctrine\ORM\Mapping\QuoteStrategy`` interface
|
||||
was introduced in 2.3. It is invoked for every column, table, alias and other
|
||||
was introduced in ORM. It is invoked for every column, table, alias and other
|
||||
SQL names. You can implement the QuoteStrategy and set it by calling
|
||||
``Doctrine\ORM\Configuration#setQuoteStrategy()``.
|
||||
|
||||
.. versionadded: 2.4
|
||||
|
||||
The ANSI Quote Strategy was added, which assumes quoting is not necessary for any SQL name.
|
||||
You can use it with the following code:
|
||||
|
||||
|
||||
@@ -15,6 +15,16 @@ especially what the strategies presented here provide help with.
|
||||
you use the tools for your particular RDBMS for these bulk
|
||||
operations.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Having an SQL logger enabled when processing batches can have a serious impact on performance and resource usage.
|
||||
To avoid that you should disable it in the DBAL configuration:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em->getConnection()->getConfiguration()->setSQLLogger(null);
|
||||
|
||||
Bulk Inserts
|
||||
------------
|
||||
|
||||
@@ -41,7 +51,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
|
||||
@@ -65,7 +75,7 @@ Iterating results
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
An alternative solution for bulk updates is to use the
|
||||
``Query#iterate()`` facility to iterate over the query results step
|
||||
``Query#toIterable()`` facility to iterate over the query results step
|
||||
by step instead of loading the whole result into memory at once.
|
||||
The following example shows how to do this, combining the iteration
|
||||
with the batching strategy that was already used for bulk inserts:
|
||||
@@ -76,16 +86,14 @@ with the batching strategy that was already used for bulk inserts:
|
||||
$batchSize = 20;
|
||||
$i = 1;
|
||||
$q = $em->createQuery('select u from MyProject\Model\User u');
|
||||
$iterableResult = $q->iterate();
|
||||
foreach ($iterableResult as $row) {
|
||||
$user = $row[0];
|
||||
foreach ($q->toIterable() as $user) {
|
||||
$user->increaseCredit();
|
||||
$user->calculateNewBonuses();
|
||||
++$i;
|
||||
if (($i % $batchSize) === 0) {
|
||||
$em->flush(); // Executes all updates.
|
||||
$em->clear(); // Detaches all objects from Doctrine!
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
$em->flush();
|
||||
|
||||
@@ -101,6 +109,7 @@ 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
|
||||
------------
|
||||
|
||||
@@ -126,7 +135,7 @@ Iterating results
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
An alternative solution for bulk deletes is to use the
|
||||
``Query#iterate()`` facility to iterate over the query results step
|
||||
``Query#toIterable()`` facility to iterate over the query results step
|
||||
by step instead of loading the whole result into memory at once.
|
||||
The following example shows how to do this:
|
||||
|
||||
@@ -136,14 +145,13 @@ The following example shows how to do this:
|
||||
$batchSize = 20;
|
||||
$i = 1;
|
||||
$q = $em->createQuery('select u from MyProject\Model\User u');
|
||||
$iterableResult = $q->iterate();
|
||||
while (($row = $iterableResult->next()) !== false) {
|
||||
$em->remove($row[0]);
|
||||
foreach($q->toIterable() as $row) {
|
||||
$em->remove($row);
|
||||
++$i;
|
||||
if (($i % $batchSize) === 0) {
|
||||
$em->flush(); // Executes all deletions.
|
||||
$em->clear(); // Detaches all objects from Doctrine!
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
$em->flush();
|
||||
|
||||
@@ -153,25 +161,24 @@ 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
|
||||
-------------------------------------------
|
||||
|
||||
You can use the ``iterate()`` method just to iterate over a large
|
||||
result and no UPDATE or DELETE intention. The ``IterableResult``
|
||||
instance returned from ``$query->iterate()`` implements the
|
||||
Iterator interface so you can process a large result without memory
|
||||
You can use the ``toIterable()`` method just to iterate over a large
|
||||
result and no UPDATE or DELETE intention. ``$query->toIterable()`` returns ``iterable``
|
||||
so you can process a large result without memory
|
||||
problems using the following approach:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$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
|
||||
$q = $this->_em->createQuery('select u from MyProject\Model\User u');
|
||||
foreach ($q->toIterable() as $row) {
|
||||
// do stuff with the data in the row
|
||||
|
||||
// detach all entities from Doctrine, so that Garbage-Collection can kick in immediately
|
||||
$this->em->clear();
|
||||
// detach from Doctrine, so that it can be Garbage-Collected immediately
|
||||
$this->_em->detach($row[0]);
|
||||
}
|
||||
|
||||
.. note::
|
||||
@@ -180,9 +187,5 @@ 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,12 +12,14 @@ 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)
|
||||
@@ -41,7 +43,7 @@ should use events judiciously.
|
||||
Use cascades judiciously
|
||||
------------------------
|
||||
|
||||
Automatic cascades of the persist/remove/refresh/etc. operations are
|
||||
Automatic cascades of the persist/remove/merge/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
|
||||
@@ -72,11 +74,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;
|
||||
@@ -106,3 +108,4 @@ 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.
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Caching
|
||||
=======
|
||||
|
||||
Doctrine provides cache drivers in the ``Common`` package for some
|
||||
of the most popular caching implementations such as APC, Memcache
|
||||
Doctrine provides cache drivers in the ``doctrine/cache`` package for some
|
||||
of the most popular caching implementations such as APCu, 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,6 +18,7 @@ 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
|
||||
@@ -27,6 +28,7 @@ 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)
|
||||
@@ -41,50 +43,38 @@ 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/2.8.x/lib/Doctrine/Common/Cache>`_.
|
||||
|
||||
APC
|
||||
~~~
|
||||
PhpFileCache
|
||||
~~~~~~~~~~~~
|
||||
|
||||
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 <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.
|
||||
The preferred cache driver for metadata and query caches is ``PhpFileCache``.
|
||||
This driver serializes cache items and writes them to a file. This allows for
|
||||
opcode caching to be used and provides high performance in most scenarios.
|
||||
|
||||
Below is a simple example of how you could use the APC cache driver
|
||||
by itself.
|
||||
In order to use the ``PhpFileCache`` driver it must be able to write to
|
||||
a directory.
|
||||
|
||||
Below is an example of how to use the ``PhpFileCache`` driver by itself.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$cacheDriver = new \Doctrine\Common\Cache\ApcCache();
|
||||
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
|
||||
'/path/to/writable/directory'
|
||||
);
|
||||
$cacheDriver->save('cache_id', 'my_data');
|
||||
|
||||
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 <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.
|
||||
|
||||
Below is a simple example of how you could use the APCu cache driver
|
||||
by itself.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$cacheDriver = new \Doctrine\Common\Cache\ApcuCache();
|
||||
$cacheDriver->save('cache_id', 'my_data');
|
||||
The PhpFileCache is not distributed across multiple machines if you are running
|
||||
your application in a distributed setup. This is ok for the metadata and query
|
||||
cache but is not a good approach for the result cache.
|
||||
|
||||
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 <https://php.net/memcache>`_. It will
|
||||
`on the PHP website <http://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.
|
||||
|
||||
@@ -96,7 +86,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');
|
||||
@@ -109,7 +99,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 <https://php.net/memcached>`_. It will
|
||||
`on the PHP website <http://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.
|
||||
|
||||
@@ -121,35 +111,17 @@ 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');
|
||||
|
||||
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 <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.
|
||||
|
||||
Below is a simple example of how you could use the Xcache cache
|
||||
driver by itself.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$cacheDriver = new \Doctrine\Common\Cache\XcacheCache();
|
||||
$cacheDriver->save('cache_id', 'my_data');
|
||||
|
||||
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 <https://redis.io/>`_. Also check
|
||||
`from here <http://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.
|
||||
|
||||
@@ -170,7 +142,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.
|
||||
|
||||
@@ -193,6 +165,7 @@ 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
|
||||
@@ -242,7 +215,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
|
||||
@@ -279,6 +252,8 @@ You can set the namespace a cache driver should use by using the
|
||||
<?php
|
||||
$cacheDriver->setNamespace('my_namespace_');
|
||||
|
||||
.. _integrating-with-the-orm:
|
||||
|
||||
Integrating with the ORM
|
||||
------------------------
|
||||
|
||||
@@ -301,20 +276,28 @@ use on your ORM configuration.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
|
||||
'/path/to/writable/directory'
|
||||
);
|
||||
$config = new \Doctrine\ORM\Configuration();
|
||||
$config->setQueryCacheImpl(new \Doctrine\Common\Cache\ApcuCache());
|
||||
$config->setQueryCacheImpl($cacheDriver);
|
||||
|
||||
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 again after the first time.
|
||||
You just need to configure the result cache implementation.
|
||||
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.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setResultCacheImpl(new \Doctrine\Common\Cache\ApcuCache());
|
||||
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
|
||||
'/path/to/writable/directory'
|
||||
);
|
||||
$config = new \Doctrine\ORM\Configuration();
|
||||
$config->setResultCacheImpl($cacheDriver);
|
||||
|
||||
Now when you're executing DQL queries you can configure them to use
|
||||
the result cache.
|
||||
@@ -323,7 +306,7 @@ the result cache.
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('select u from \Entities\User u');
|
||||
$query->useResultCache(true);
|
||||
$query->enableResultCache();
|
||||
|
||||
You can also configure an individual query to use a different
|
||||
result cache driver.
|
||||
@@ -331,18 +314,22 @@ result cache driver.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query->setResultCacheDriver(new \Doctrine\Common\Cache\ApcuCache());
|
||||
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
|
||||
'/path/to/writable/directory'
|
||||
);
|
||||
$query->setResultCacheDriver($cacheDriver);
|
||||
|
||||
.. note::
|
||||
|
||||
Setting the result cache driver on the query will
|
||||
automatically enable the result cache for the query. If you want to
|
||||
disable it pass false to ``useResultCache()``.
|
||||
disable it use ``disableResultCache()``.
|
||||
|
||||
::
|
||||
|
||||
<?php
|
||||
$query->useResultCache(false);
|
||||
$query->disableResultCache();
|
||||
|
||||
|
||||
If you want to set the time the cache has to live you can use the
|
||||
``setResultCacheLifetime()`` method.
|
||||
@@ -362,18 +349,18 @@ yourself with the ``setResultCacheId()`` method.
|
||||
$query->setResultCacheId('my_custom_id');
|
||||
|
||||
You can also set the lifetime and cache ID by passing the values as
|
||||
the second and third argument to ``useResultCache()``.
|
||||
the first and second argument to ``enableResultCache()``.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query->useResultCache(true, 3600, 'my_custom_id');
|
||||
$query->enableResultCache(3600, 'my_custom_id');
|
||||
|
||||
Metadata Cache
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Your class metadata can be parsed from a few different sources like
|
||||
XML, Annotations, etc. Instead of parsing this information on
|
||||
YAML, 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
|
||||
@@ -382,7 +369,11 @@ first.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ApcuCache());
|
||||
$cacheDriver = new \Doctrine\Common\Cache\PhpFileCache(
|
||||
'/path/to/writable/directory'
|
||||
);
|
||||
$config = new \Doctrine\ORM\Configuration();
|
||||
$config->setMetadataCacheImpl($cacheDriver);
|
||||
|
||||
Now the metadata information will only be parsed once and stored in
|
||||
the cache driver.
|
||||
@@ -418,6 +409,12 @@ To clear the result cache use the ``orm:clear-cache:result`` task.
|
||||
All these tasks accept a ``--flush`` option to flush the entire
|
||||
contents of the cache instead of invalidating the entries.
|
||||
|
||||
.. note::
|
||||
|
||||
None of these tasks will work with APC, APCu, or XCache drivers
|
||||
because the memory that the cache is stored in is only accessible
|
||||
to the webserver.
|
||||
|
||||
Cache Chaining
|
||||
--------------
|
||||
|
||||
@@ -467,3 +464,4 @@ 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>`_.
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ Deferred Explicit
|
||||
|
||||
The deferred explicit policy is similar to the deferred implicit
|
||||
policy in that it detects changes through a property-by-property
|
||||
comparison at commit time. The difference is that Doctrine 2 only
|
||||
comparison at commit time. The difference is that Doctrine ORM only
|
||||
considers entities that have been explicitly marked for change detection
|
||||
through a call to EntityManager#persist(entity) or through a save
|
||||
cascade. All other entities are skipped. This policy therefore
|
||||
@@ -61,6 +61,11 @@ This policy can be configured as follows:
|
||||
Notify
|
||||
~~~~~~
|
||||
|
||||
.. note::
|
||||
|
||||
The notify change tracking policy is deprecated and will be removed in ORM 3.0.
|
||||
(`Details <https://github.com/doctrine/orm/issues/8383>`_)
|
||||
|
||||
This policy is based on the assumption that the entities notify
|
||||
interested listeners of changes to their properties. For that
|
||||
purpose, a class that wants to use this policy needs to implement
|
||||
@@ -71,9 +76,9 @@ follows:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\NotifyPropertyChanged,
|
||||
Doctrine\Common\PropertyChangedListener;
|
||||
|
||||
use Doctrine\Persistence\NotifyPropertyChanged,
|
||||
Doctrine\Persistence\PropertyChangedListener;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ChangeTrackingPolicy("NOTIFY")
|
||||
@@ -81,12 +86,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 +104,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,3 +153,4 @@ 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.
|
||||
|
||||
|
||||
|
||||
@@ -67,6 +67,22 @@ 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.
|
||||
@@ -96,8 +112,6 @@ You need to register your applications EntityManager to the console tool
|
||||
to make use of the tasks by creating a ``cli-config.php`` file with the
|
||||
following content:
|
||||
|
||||
On Doctrine 2.4 and above:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
@@ -110,19 +124,3 @@ On Doctrine 2.4 and above:
|
||||
$entityManager = GetEntityManager();
|
||||
|
||||
return ConsoleRunner::createHelperSet($entityManager);
|
||||
|
||||
On Doctrine 2.3 and below:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// cli-config.php
|
||||
require_once 'my_bootstrap.php';
|
||||
|
||||
// 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)
|
||||
));
|
||||
|
||||
@@ -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,6 +18,7 @@ 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.
|
||||
|
||||
@@ -58,6 +59,7 @@ 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
|
||||
@@ -129,6 +131,7 @@ multiple FROM clauses.
|
||||
|
||||
Doctrine throws an exception if you violate this constraint.
|
||||
|
||||
|
||||
Joins
|
||||
~~~~~
|
||||
|
||||
@@ -177,15 +180,16 @@ 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 loading proxy
|
||||
instances. Non-loaded Collections are also replaced by lazy-loading
|
||||
loaded from the database are replaced with lazy load proxy
|
||||
instances. Non-loaded Collections are also replaced by lazy-load
|
||||
instances that fetch all the contained objects upon first access.
|
||||
However relying on the lazy-loading mechanism leads to many small
|
||||
However relying on the lazy-load 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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -204,8 +208,7 @@ 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
|
||||
|
||||
@@ -213,8 +216,7 @@ 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
|
||||
|
||||
@@ -222,8 +224,7 @@ 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
|
||||
|
||||
@@ -231,10 +232,6 @@ 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:
|
||||
|
||||
@@ -244,8 +241,7 @@ 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
|
||||
|
||||
@@ -254,8 +250,7 @@ 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 its single associated entity:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -264,8 +259,7 @@ 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 owning phonenumbers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Retrieve a CmsUser and fetch join all the phonenumbers it has:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -274,8 +268,7 @@ Retrieve a CmsUser and fetch join all owning phonenumbers
|
||||
$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
|
||||
|
||||
@@ -283,8 +276,7 @@ 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
|
||||
|
||||
Hydrate a result in Descending Order
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Or in Descending Order:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -292,8 +284,7 @@ Hydrate a result 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
|
||||
|
||||
@@ -304,8 +295,7 @@ 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();
|
||||
|
||||
Using WHERE Clause and Positional Parameter
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
With WHERE Clause and Positional Parameter:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -314,8 +304,7 @@ Using WHERE Clause and Positional Parameter
|
||||
$query->setParameter(1, 321);
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
Using WHERE Clause and Named Parameter
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
With WHERE Clause and Named Parameter:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -324,8 +313,7 @@ Using WHERE Clause and Named Parameter
|
||||
$query->setParameter('name', 'Bob');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
Using Nested Conditions in WHERE Clause
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
With Nested Conditions in WHERE Clause:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -338,8 +326,7 @@ Using Nested Conditions in WHERE Clause
|
||||
));
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
COUNT DISTINCT
|
||||
^^^^^^^^^^^^^^
|
||||
With COUNT DISTINCT:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -347,8 +334,7 @@ COUNT DISTINCT
|
||||
$query = $em->createQuery('SELECT COUNT(DISTINCT u.name) FROM CmsUser');
|
||||
$users = $query->getResult(); // array of ForumUser objects
|
||||
|
||||
Using Arithmetic Expression in WHERE clause
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
With Arithmetic Expression in WHERE clause:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -356,9 +342,6 @@ Using 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
|
||||
@@ -367,9 +350,6 @@ 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:
|
||||
|
||||
@@ -379,8 +359,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
|
||||
|
||||
@@ -389,8 +369,7 @@ Restricting a JOIN clause by additional conditions specified by WITH
|
||||
$query->setParameter('foo', '%foo%');
|
||||
$users = $query->getResult();
|
||||
|
||||
Using several Fetch JOINs
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Using several Fetch JOINs:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -398,8 +377,7 @@ 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
|
||||
|
||||
@@ -409,8 +387,7 @@ BETWEEN in WHERE clause
|
||||
$query->setParameter(2, 321);
|
||||
$usernames = $query->getResult();
|
||||
|
||||
DQL Functions in WHERE clause
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
DQL Functions in WHERE clause:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -418,8 +395,7 @@ 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
|
||||
|
||||
@@ -433,8 +409,7 @@ 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
|
||||
|
||||
@@ -448,7 +423,6 @@ CONCAT() DQL Function
|
||||
$idUsernames = $query->getResult();
|
||||
|
||||
EXISTS in WHERE clause with correlated Subquery
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -456,8 +430,7 @@ 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
|
||||
|
||||
@@ -467,7 +440,6 @@ Get all users who are members of $group
|
||||
$ids = $query->getResult();
|
||||
|
||||
Get all users that have more than 1 phonenumber
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -476,7 +448,6 @@ Get all users that have more than 1 phonenumber
|
||||
$users = $query->getResult();
|
||||
|
||||
Get all users that have no phonenumber
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -484,13 +455,8 @@ 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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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
|
||||
Get all instances of a specific type, for use with inheritance
|
||||
hierarchies:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -499,58 +465,28 @@ Get all instances of a specific type, for use with inheritance hierarchies. Thes
|
||||
$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');
|
||||
|
||||
Using IDENTITY() in queries
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Get all users visible on a given website that have chosen certain gender.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
Get all users visible on a given website that have chosen certain gender:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM User u WHERE u.gender IN (SELECT IDENTITY(agl.gender) FROM Site s JOIN s.activeGenderList agl WHERE s.id = ?1)');
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
Starting with 2.4, the IDENTITY() DQL function also works for composite primary keys
|
||||
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:
|
||||
Joins between entities without associations are available,
|
||||
where you can generate an arbitrary join with the following syntax:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$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
|
||||
confusing.
|
||||
@@ -562,6 +498,7 @@ when the DQL is switched to an arbitrary join.
|
||||
- HAVING is applied to the results of a query after
|
||||
aggregation (GROUP BY)
|
||||
|
||||
|
||||
Partial Object Syntax
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -591,8 +528,6 @@ You use the partial syntax when joining as well:
|
||||
"NEW" Operator Syntax
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
Using the ``NEW`` operator you can construct Data Transfer Objects (DTOs) directly from DQL queries.
|
||||
|
||||
- When using ``SELECT NEW`` you don't need to specify a mapped entity.
|
||||
@@ -668,6 +603,13 @@ then phonenumber-id:
|
||||
...
|
||||
'nameUpper' => string 'JWAGE' (length=5)
|
||||
|
||||
You can also index by a to-one association, which will use the id of
|
||||
the associated entity (the join column) as the key in the result set:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT p, u FROM Participant INDEX BY p.user JOIN p.user u WHERE p.event = 3
|
||||
|
||||
UPDATE queries
|
||||
--------------
|
||||
|
||||
@@ -693,6 +635,7 @@ clause and using sub-selects.
|
||||
``EntityManager#clear()`` and retrieve new instances of any
|
||||
affected entity.
|
||||
|
||||
|
||||
DELETE queries
|
||||
--------------
|
||||
|
||||
@@ -713,6 +656,17 @@ 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.
|
||||
|
||||
Comments in queries
|
||||
-------------------
|
||||
|
||||
We can use comments with the SQL syntax of comments.
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT u FROM MyProject\Model\User u
|
||||
-- my comment
|
||||
WHERE u.age > 20 -- comment at the end of a line
|
||||
|
||||
Functions, Operators, Aggregates
|
||||
--------------------------------
|
||||
It is possible to wrap both fields and identification values into
|
||||
@@ -725,12 +679,13 @@ 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
|
||||
@@ -745,8 +700,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 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_ADD(date, value, unit) - Add the given time to a given date. (Supported units are SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR)
|
||||
- DATE_SUB(date, value, unit) - Subtract the given time from a given date. (Supported units are SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR)
|
||||
- DATE_DIFF(date1, date2) - Calculate the difference in days between date1-date2.
|
||||
|
||||
Arithmetic operators
|
||||
@@ -770,6 +725,7 @@ 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
|
||||
@@ -856,7 +812,7 @@ what type of results to expect.
|
||||
Single Table
|
||||
~~~~~~~~~~~~
|
||||
|
||||
`Single Table Inheritance <https://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
|
||||
`Single Table Inheritance <http://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
|
||||
@@ -949,11 +905,11 @@ entities:
|
||||
Class Table Inheritance
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
`Class Table Inheritance <https://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
|
||||
`Class Table Inheritance <http://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
|
||||
of a parent class through a foreign key constraint. Doctrine 2
|
||||
of a parent class through a foreign key constraint. Doctrine ORM
|
||||
implements this strategy through the use of a discriminator column
|
||||
in the topmost table of the hierarchy because this is the easiest
|
||||
way to achieve polymorphic queries with Class Table Inheritance.
|
||||
@@ -994,6 +950,7 @@ 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
|
||||
|
||||
@@ -1009,6 +966,7 @@ automatically for you:
|
||||
FROM Employee e1_ INNER JOIN Person p0_ ON e1_.id = p0_.id
|
||||
WHERE p0_.name = ?
|
||||
|
||||
|
||||
The Query class
|
||||
---------------
|
||||
|
||||
@@ -1039,6 +997,7 @@ 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).
|
||||
@@ -1046,8 +1005,9 @@ the Query class. Here they are:
|
||||
result contains more than one object, an ``NonUniqueResultException``
|
||||
is thrown. If the result contains no objects, an ``NoResultException``
|
||||
is thrown. The pure/mixed distinction does not apply.
|
||||
- ``Query#getOneOrNullResult()``: Retrieve a single object. If no
|
||||
object is found null will be returned.
|
||||
- ``Query#getOneOrNullResult()``: Retrieve a single object. If the
|
||||
result contains more than one object, a ``NonUniqueResultException``
|
||||
is thrown. If no object is found null will be returned.
|
||||
- ``Query#getArrayResult()``: Retrieves an array graph (a nested
|
||||
array) that is largely interchangeable with the object graph
|
||||
generated by ``Query#getResult()`` for read-only purposes.
|
||||
@@ -1058,6 +1018,8 @@ 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.
|
||||
@@ -1135,11 +1097,13 @@ 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
|
||||
@@ -1179,6 +1143,7 @@ will return the rows iterating the different top-level entities.
|
||||
[2] => Object (User)
|
||||
[3] => Object (Group)
|
||||
|
||||
|
||||
Hydration Modes
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1188,10 +1153,11 @@ 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
|
||||
^^^^^^^^^^^^^^^^
|
||||
@@ -1255,8 +1221,9 @@ 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
|
||||
@@ -1290,14 +1257,13 @@ 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(FetchMode::Associative);
|
||||
return $this->_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1339,6 +1305,7 @@ 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
|
||||
@@ -1393,6 +1360,7 @@ Result Cache API:
|
||||
``Doctrine\ORM\Configuration`` instance so that it is passed to
|
||||
every ``Query`` and ``NativeQuery`` instance.
|
||||
|
||||
|
||||
Query Hints
|
||||
^^^^^^^^^^^
|
||||
|
||||
@@ -1402,21 +1370,23 @@ 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
|
||||
the database. This query hint is deprecated and will be removed
|
||||
in the future (`Details <https://github.com/doctrine/orm/issues/8471>`_)
|
||||
- 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.
|
||||
|
||||
@@ -1437,6 +1407,7 @@ 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
|
||||
@@ -1455,6 +1426,7 @@ 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)``
|
||||
|
||||
@@ -1480,7 +1452,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\FetchMode::EAGER);
|
||||
$query->setFetchMode("MyProject\User", "address", \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER);
|
||||
$query->execute();
|
||||
|
||||
Given that there are 10 users and corresponding addresses in the database the executed queries will look something like:
|
||||
@@ -1499,6 +1471,7 @@ Given that there are 10 users and corresponding addresses in the database the ex
|
||||
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
|
||||
----
|
||||
|
||||
@@ -1510,6 +1483,7 @@ 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
|
||||
@@ -1523,8 +1497,10 @@ 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, ...)
|
||||
@@ -1559,7 +1535,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 | identifier
|
||||
AbstractSchemaName ::= fully_qualified_name | aliased_name | identifier
|
||||
|
||||
/* Alias ResultVariable declaration (the "total" of "COUNT(*) AS total") */
|
||||
AliasResultVariable = identifier
|
||||
@@ -1647,7 +1623,7 @@ From, Join and Index by
|
||||
RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
|
||||
JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy]
|
||||
Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" (JoinAssociationDeclaration | RangeVariableDeclaration) ["WITH" ConditionalExpression]
|
||||
IndexBy ::= "INDEX" "BY" StateFieldPathExpression
|
||||
IndexBy ::= "INDEX" "BY" SingleValuedPathExpression
|
||||
|
||||
Select Expressions
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
@@ -1675,6 +1651,7 @@ Conditional Expressions
|
||||
EmptyCollectionComparisonExpression | CollectionMemberExpression |
|
||||
InstanceOfExpression
|
||||
|
||||
|
||||
Collection Expressions
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1764,7 +1741,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 ::= ArithmeticExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
|
||||
InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
|
||||
InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
|
||||
InstanceOfParameter ::= AbstractSchemaName | InputParameter
|
||||
LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char]
|
||||
@@ -1805,3 +1782,4 @@ Functions
|
||||
"UPPER" "(" StringPrimary ")" |
|
||||
"IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")"
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Events
|
||||
======
|
||||
|
||||
Doctrine 2 features a lightweight event system that is part of the
|
||||
Doctrine ORM features a lightweight event system that is part of the
|
||||
Common package. Doctrine uses it to dispatch system events, mainly
|
||||
:ref:`lifecycle events <reference-events-lifecycle-events>`.
|
||||
You can also use it for your own custom events.
|
||||
@@ -70,7 +70,7 @@ method.
|
||||
<?php
|
||||
$evm->removeEventListener(array(self::preFoo, self::postFoo), $this);
|
||||
|
||||
The Doctrine 2 event system also has a simple concept of event
|
||||
The Doctrine ORM event system also has a simple concept of event
|
||||
subscribers. We can define a simple ``TestEventSubscriber`` class
|
||||
which implements the ``\Doctrine\Common\EventSubscriber`` interface
|
||||
and implements a ``getSubscribedEvents()`` method which returns an
|
||||
@@ -124,11 +124,12 @@ Now you can test the ``$eventSubscriber`` instance to see if the
|
||||
Naming convention
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Events being used with the Doctrine 2 EventManager are best named
|
||||
Events being used with the Doctrine ORM EventManager are best named
|
||||
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
|
||||
@@ -144,60 +145,65 @@ An example for a correct notation can be found in the example
|
||||
Lifecycle Events
|
||||
----------------
|
||||
|
||||
The EntityManager and UnitOfWork trigger a bunch of events during
|
||||
the life-time of their registered entities.
|
||||
The ``EntityManager`` and ``UnitOfWork`` classes 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.
|
||||
- postRemove - The postRemove event occurs for an entity after the
|
||||
|
||||
|
||||
- ``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.
|
||||
- ``postRemove`` - The ``postRemove`` event occurs for an entity after the
|
||||
entity has been deleted. It will be invoked after the database
|
||||
delete operations. It is not called for a DQL DELETE statement.
|
||||
- prePersist - The prePersist event occurs for a given entity
|
||||
before the respective EntityManager persist operation for that
|
||||
delete operations. It is not called for a DQL ``DELETE`` statement.
|
||||
- ``prePersist`` - The ``prePersist`` event occurs for a given entity
|
||||
before the respective ``EntityManager`` persist operation for that
|
||||
entity is executed. It should be noted that this event is only triggered on
|
||||
*initial* persist of an entity (i.e. it does not trigger on future updates).
|
||||
- postPersist - The postPersist event occurs for an entity after
|
||||
- ``postPersist`` - The ``postPersist`` event occurs for an entity after
|
||||
the entity has been made persistent. It will be invoked after the
|
||||
database insert operations. Generated primary key values are
|
||||
available in the postPersist event.
|
||||
- preUpdate - The preUpdate event occurs before the database
|
||||
update operations to entity data. It is not called for a DQL UPDATE statement
|
||||
nor when the computed changeset is empty.
|
||||
- postUpdate - The postUpdate event occurs after the database
|
||||
update operations to entity data. It is not called for a DQL UPDATE statement.
|
||||
- postLoad - The postLoad event occurs for an entity after the
|
||||
entity has been loaded into the current EntityManager from the
|
||||
- ``preUpdate`` - The ``preUpdate`` event occurs before the database
|
||||
update operations to entity data. It is not called for a DQL
|
||||
``UPDATE`` statement nor when the computed changeset is empty.
|
||||
- ``postUpdate`` - The ``postUpdate`` event occurs after the database
|
||||
update operations to entity data. It is not called for a DQL
|
||||
``UPDATE`` statement.
|
||||
- ``postLoad`` - The postLoad event occurs for an entity after the
|
||||
entity has been loaded into the current ``EntityManager`` from the
|
||||
database or after the refresh operation has been applied to it.
|
||||
- loadClassMetadata - The loadClassMetadata event occurs after the
|
||||
- ``loadClassMetadata`` - The ``loadClassMetadata`` event occurs after the
|
||||
mapping metadata for a class has been loaded from a mapping source
|
||||
(annotations/xml). This event is not a lifecycle callback.
|
||||
- onClassMetadataNotFound - Loading class metadata for a particular
|
||||
(annotations/xml/yaml). 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
|
||||
or could be found. This event is not a lifecycle callback.
|
||||
- preFlush - The preFlush event occurs at the very beginning of a flush
|
||||
operation.
|
||||
- onFlush - The onFlush event occurs after the change-sets of all
|
||||
- ``preFlush`` - The ``preFlush`` event occurs at the very beginning of
|
||||
a flush operation.
|
||||
- ``onFlush`` - The ``onFlush`` event occurs after the change-sets of all
|
||||
managed entities are computed. This event is not a lifecycle
|
||||
callback.
|
||||
- postFlush - The postFlush event occurs at the end of a flush operation. This
|
||||
- ``postFlush`` - The ``postFlush`` event occurs at the end of a flush operation. This
|
||||
event is not a lifecycle callback.
|
||||
- onClear - The onClear event occurs when the EntityManager#clear() operation is
|
||||
invoked, after all references to entities have been removed from the unit of
|
||||
work. This event is not a lifecycle callback.
|
||||
- ``onClear`` - The ``onClear`` event occurs when the
|
||||
``EntityManager#clear()`` operation is invoked, after all references
|
||||
to entities have been removed from the unit of work. This event is not
|
||||
a lifecycle callback.
|
||||
|
||||
|
||||
.. warning::
|
||||
|
||||
Note that, when using ``Doctrine\ORM\AbstractQuery#iterate()``, ``postLoad``
|
||||
Note that, when using ``Doctrine\ORM\AbstractQuery#toIterable()``, ``postLoad``
|
||||
events will be executed immediately after objects are being hydrated, and therefore
|
||||
associations are not guaranteed to be initialized. It is not safe to combine
|
||||
usage of ``Doctrine\ORM\AbstractQuery#iterate()`` and ``postLoad`` event
|
||||
usage of ``Doctrine\ORM\AbstractQuery#toIterable()`` and ``postLoad`` event
|
||||
handlers.
|
||||
|
||||
.. warning::
|
||||
|
||||
Note that the postRemove event or any events triggered after an entity removal
|
||||
Note that the ``postRemove`` event or any events triggered after an entity removal
|
||||
can receive an uninitializable proxy in case you have configured an entity to
|
||||
cascade remove relations. In this case, you should load yourself the proxy in
|
||||
the associated pre event.
|
||||
@@ -215,22 +221,23 @@ These can be hooked into by two different types of event
|
||||
listeners:
|
||||
|
||||
- Lifecycle Callbacks are methods on the entity classes that are
|
||||
called when the event is triggered. As of v2.4 they receive some kind
|
||||
called when the event is triggered. They receive some kind
|
||||
of ``EventArgs`` instance.
|
||||
- Lifecycle Event Listeners and Subscribers are classes with specific callback
|
||||
methods that receives some kind of ``EventArgs`` instance.
|
||||
|
||||
The EventArgs instance received by the listener gives access to the entity,
|
||||
EntityManager and other relevant data.
|
||||
The ``EventArgs`` instance received by the listener gives access to the entity,
|
||||
``EntityManager`` instance and other relevant data.
|
||||
|
||||
.. note::
|
||||
|
||||
All Lifecycle events that happen during the ``flush()`` of
|
||||
an EntityManager have very specific constraints on the allowed
|
||||
an ``EntityManager`` have very specific constraints on the allowed
|
||||
operations that can be executed. Please read the
|
||||
:ref:`reference-events-implementing-listeners` section very carefully
|
||||
to understand which operations are allowed in which lifecycle event.
|
||||
|
||||
|
||||
Lifecycle Callbacks
|
||||
-------------------
|
||||
|
||||
@@ -240,6 +247,11 @@ a relevant lifecycle event. More than one callback can be defined for each
|
||||
lifecycle event. Lifecycle Callbacks are best used for simple operations
|
||||
specific to a particular entity class's lifecycle.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Note that Licecycle Callbacks are not supported for Embeddables.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
@@ -292,8 +304,26 @@ 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 XML it would look
|
||||
something like this:
|
||||
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:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
@@ -319,7 +349,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 XML you need to remember to create public methods to match the
|
||||
When using YAML or 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.
|
||||
@@ -349,12 +379,11 @@ defined on your ``User`` model.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Lifecycle Callbacks Event Argument
|
||||
----------------------------------
|
||||
-----------------------------------
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
Since 2.4 the triggered event is given to the lifecycle-callback.
|
||||
The triggered event is also given to the lifecycle-callback.
|
||||
|
||||
With the additional argument you have access to the
|
||||
``EntityManager`` and ``UnitOfWork`` APIs inside these callback methods.
|
||||
@@ -383,9 +412,9 @@ sit at a level above the entities and allow you to implement re-usable
|
||||
behaviors across different entity classes.
|
||||
|
||||
Note that they require much more detailed knowledge about the inner
|
||||
workings of the EntityManager and UnitOfWork. Please read the
|
||||
:ref:`reference-events-implementing-listeners` section carefully if you
|
||||
are trying to write your own listener.
|
||||
workings of the ``EntityManager`` and ``UnitOfWork`` classes. Please
|
||||
read the :ref:`reference-events-implementing-listeners` section
|
||||
carefully if you are trying to write your own listener.
|
||||
|
||||
For event subscribers, there are no surprises. They declare the
|
||||
lifecycle events in their ``getSubscribedEvents`` method and provide
|
||||
@@ -396,7 +425,7 @@ A lifecycle event listener looks like the following:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||
use Doctrine\Persistence\Event\LifecycleEventArgs;
|
||||
|
||||
class MyEventListener
|
||||
{
|
||||
@@ -418,8 +447,8 @@ A lifecycle event subscriber may look like this:
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Events;
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||
use Doctrine\EventSubscriber;
|
||||
use Doctrine\Persistence\Event\LifecycleEventArgs;
|
||||
|
||||
class MyEventSubscriber implements EventSubscriber
|
||||
{
|
||||
@@ -474,16 +503,16 @@ Implementing Event Listeners
|
||||
----------------------------
|
||||
|
||||
This section explains what is and what is not allowed during
|
||||
specific lifecycle events of the UnitOfWork. Although you get
|
||||
passed the EntityManager in all of these events, you have to follow
|
||||
these restrictions very carefully since operations in the wrong
|
||||
event may produce lots of different errors, such as inconsistent
|
||||
specific lifecycle events of the ``UnitOfWork`` class. Although you get
|
||||
passed the ``EntityManager`` instance in all of these events, you have
|
||||
to follow these restrictions very carefully since operations in the
|
||||
wrong event may produce lots of different errors, such as inconsistent
|
||||
data and lost updates/persists/removes.
|
||||
|
||||
For the described events that are also lifecycle callback events
|
||||
the restrictions apply as well, with the additional restriction
|
||||
that (prior to version 2.4) you do not have access to the
|
||||
EntityManager or UnitOfWork APIs inside these events.
|
||||
``EntityManager`` or ``UnitOfWork`` APIs inside these events.
|
||||
|
||||
prePersist
|
||||
~~~~~~~~~~
|
||||
@@ -503,6 +532,7 @@ 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.
|
||||
@@ -525,8 +555,9 @@ preFlush
|
||||
~~~~~~~~
|
||||
|
||||
``preFlush`` is called at ``EntityManager#flush()`` before
|
||||
anything else. ``EntityManager#flush()`` can be called safely
|
||||
inside its listeners.
|
||||
anything else. ``EntityManager#flush()`` should not be called inside
|
||||
its listeners, since `preFlush` event is dispatched in it, which would
|
||||
result in infinite loop.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -550,14 +581,15 @@ 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
|
||||
- Collections scheduled for update
|
||||
- Collections scheduled for removal
|
||||
|
||||
To make use of the onFlush event you have to be familiar with the
|
||||
internal UnitOfWork API, which grants you access to the previously
|
||||
To make use of the ``onFlush`` event you have to be familiar with the
|
||||
internal ``UnitOfWork`` API, which grants you access to the previously
|
||||
mentioned sets. See this example:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -594,6 +626,7 @@ 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
|
||||
@@ -642,6 +675,7 @@ 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.
|
||||
@@ -695,6 +729,7 @@ 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
|
||||
@@ -702,7 +737,7 @@ Restrictions for this event:
|
||||
the event to modify primitive field values, e.g. use
|
||||
``$eventArgs->setNewValue($field, $value);`` as in the Alice to Bob example above.
|
||||
- Any calls to ``EntityManager#persist()`` or
|
||||
``EntityManager#remove()``, even in combination with the UnitOfWork
|
||||
``EntityManager#remove()``, even in combination with the ``UnitOfWork``
|
||||
API are strongly discouraged and don't work as expected outside the
|
||||
flush operation.
|
||||
|
||||
@@ -724,8 +759,6 @@ EntityManager.
|
||||
Entity listeners
|
||||
----------------
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
An entity listener is a lifecycle listener class used for an entity.
|
||||
|
||||
- The entity listener's mapping may be applied to an entity class or mapped superclass.
|
||||
@@ -753,6 +786,13 @@ 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:
|
||||
|
||||
@@ -835,13 +875,32 @@ 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.
|
||||
@@ -907,17 +966,18 @@ Load ClassMetadata Event
|
||||
------------------------
|
||||
|
||||
When the mapping information for an entity is read, it is populated
|
||||
in to a ``Doctrine\ORM\Mapping\ClassMetadata`` instance. You can hook in to this
|
||||
in to a ``ClassMetadataInfo`` instance. You can hook in to this
|
||||
process and manipulate the instance.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$test = new TestEventListener();
|
||||
$test = new TestEvent();
|
||||
$metadataFactory = $em->getMetadataFactory();
|
||||
$evm = $em->getEventManager();
|
||||
$evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $test);
|
||||
$evm->addEventListener(Events::loadClassMetadata, $test);
|
||||
|
||||
class TestEventListener
|
||||
class TestEvent
|
||||
{
|
||||
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
|
||||
{
|
||||
@@ -931,55 +991,3 @@ 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 or xml mapping files. To make a database
|
||||
You can't set these values inside the annotations, yml 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.
|
||||
@@ -52,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 <https://www.symfony.com>`_ for example there is a Unique Entity Validator
|
||||
In `Symfony2 <http://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
|
||||
@@ -80,7 +80,7 @@ You can solve this exception by:
|
||||
How can I filter an association?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Natively you can't filter associations in 2.0 and 2.1. You should use DQL queries to query for the filtered set of entities.
|
||||
You should use DQL queries to query for the filtered set of entities.
|
||||
|
||||
I call clear() on a One-To-Many collection but the entities are not deleted
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -98,10 +98,11 @@ How can I add columns to a many-to-many table?
|
||||
|
||||
The many-to-many association is only supporting foreign keys in the table definition
|
||||
To work with many-to-many tables containing extra columns you have to use the
|
||||
foreign keys as primary keys feature of Doctrine introduced in version 2.1.
|
||||
foreign keys as primary keys feature of Doctrine ORM.
|
||||
|
||||
See :doc:`the tutorial on composite primary keys for more information<../tutorials/composite-primary-keys>`.
|
||||
|
||||
|
||||
How can i paginate fetch-joined collections?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -111,8 +112,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 <https://github.com/beberlei/DoctrineExtensions>`_
|
||||
* `Pagerfanta <https://github.com/whiteoctober/pagerfanta>`_
|
||||
* `DoctrineExtensions <http://github.com/beberlei/DoctrineExtensions>`_
|
||||
* `Pagerfanta <http://github.com/whiteoctober/pagerfanta>`_
|
||||
|
||||
Why does pagination not work correctly with fetch joins?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -127,10 +128,10 @@ See the previous question for a solution to this task.
|
||||
Inheritance
|
||||
-----------
|
||||
|
||||
Can I use Inheritance with Doctrine 2?
|
||||
Can I use Inheritance with Doctrine ORM?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Yes, you can use Single- or Joined-Table Inheritance in Doctrine 2.
|
||||
Yes, you can use Single- or Joined-Table Inheritance in ORM.
|
||||
|
||||
See the documentation chapter on :doc:`inheritance mapping <inheritance-mapping>` for
|
||||
the details.
|
||||
@@ -142,6 +143,23 @@ 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
|
||||
-----------
|
||||
|
||||
@@ -180,6 +198,21 @@ No, it is not supported to sort by function in DQL. If you need this functionali
|
||||
use a native-query or come up with another solution. As a side note: Sorting with ORDER BY RAND() is painfully slow
|
||||
starting with 1000 rows.
|
||||
|
||||
Is it better to write DQL or to generate it with the query builder?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The purpose of the ``QueryBuilder`` is to generate DQL dynamically,
|
||||
which is useful when you have optional filters, conditional joins, etc.
|
||||
|
||||
But the ``QueryBuilder`` is not an alternative to DQL, it actually generates DQL
|
||||
queries at runtime, which are then interpreted by Doctrine. This means that
|
||||
using the ``QueryBuilder`` to build and run a query is actually always slower
|
||||
than only running the corresponding DQL query.
|
||||
|
||||
So if you only need to generate a query and bind parameters to it,
|
||||
you should use plain DQL, as this is a simpler and much more readable solution.
|
||||
You should only use the ``QueryBuilder`` when you can't achieve what you want to do with a DQL query.
|
||||
|
||||
A Query fails, how can I debug it?
|
||||
----------------------------------
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
Filters
|
||||
=======
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
Doctrine 2.2 features a filter system that allows the developer to add SQL to
|
||||
Doctrine ORM features a filter system that allows the developer to add SQL to
|
||||
the conditional clauses of queries, regardless the place where the SQL is
|
||||
generated (e.g. from a DQL query, or by loading associated entities).
|
||||
|
||||
@@ -16,6 +14,7 @@ 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
|
||||
@@ -38,7 +37,7 @@ proper quoting of parameters.
|
||||
|
||||
<?php
|
||||
namespace Example;
|
||||
use Doctrine\ORM\Mapping\ClassMetaData,
|
||||
use Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\ORM\Query\Filter\SQLFilter;
|
||||
|
||||
class MyLocaleFilter extends SQLFilter
|
||||
@@ -46,7 +45,7 @@ proper quoting of parameters.
|
||||
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
|
||||
{
|
||||
// Check if the entity implements the LocalAware interface
|
||||
if (!$targetEntity->getReflectionClass()->implementsInterface('LocaleAware')) {
|
||||
if (!$targetEntity->reflClass->implementsInterface('LocaleAware')) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -54,6 +53,10 @@ proper quoting of parameters.
|
||||
}
|
||||
}
|
||||
|
||||
If the parameter is an array and should be quoted as a list of values for an IN query
|
||||
this is possible with the alternative ``SQLFilter#setParameterList()`` and
|
||||
``SQLFilter#getParameterList()`` functions.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
Filter classes are added to the configuration as following:
|
||||
@@ -63,9 +66,11 @@ 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
|
||||
|
||||
@@ -4,27 +4,35 @@ Improving Performance
|
||||
Bytecode Cache
|
||||
--------------
|
||||
|
||||
It is highly recommended to make use of a bytecode cache like APC.
|
||||
It is highly recommended to make use of a bytecode cache like OPcache.
|
||||
A bytecode cache removes the need for parsing PHP code on every
|
||||
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*
|
||||
|
||||
|
||||
Metadata and Query caches
|
||||
-------------------------
|
||||
|
||||
As already mentioned earlier in the chapter about configuring
|
||||
Doctrine, it is strongly discouraged to use Doctrine without a
|
||||
Metadata and Query cache (preferably with APC or Memcache as the
|
||||
cache driver). Operating Doctrine without these caches means
|
||||
Metadata and Query cache.
|
||||
|
||||
Operating Doctrine without these caches means
|
||||
Doctrine will need to load your mapping information on every single
|
||||
request and has to parse each DQL query on every single request.
|
||||
This is a waste of resources.
|
||||
|
||||
The preferred cache driver for metadata and query caches is ``PhpFileCache``.
|
||||
This driver serializes cache items and writes them to a file.
|
||||
This allows for opcode caching to be used and provides high performance in most scenarios.
|
||||
|
||||
See :ref:`integrating-with-the-orm`
|
||||
|
||||
Alternative Query Result Formats
|
||||
--------------------------------
|
||||
|
||||
@@ -35,11 +43,35 @@ in scenarios where data is loaded for read-only purposes.
|
||||
Read-Only Entities
|
||||
------------------
|
||||
|
||||
Starting with Doctrine 2.1 you can mark entities as read only (See metadata mapping
|
||||
references for details). This means that the entity marked as read only is never considered
|
||||
for updates, which means when you call flush on the EntityManager these entities are skipped
|
||||
even if properties changed. Read-Only allows to persist new entities of a kind and remove existing
|
||||
ones, they are just not considered for updates.
|
||||
You can mark entities as read only (See metadata mapping
|
||||
references for details).
|
||||
|
||||
This means that the entity marked as read only is never considered for updates.
|
||||
During flush on the EntityManager these entities are skipped even if properties
|
||||
changed.
|
||||
|
||||
Read-Only allows to persist new entities of a kind and remove existing ones,
|
||||
they are just not considered for updates.
|
||||
|
||||
See :ref:`annref_entity`
|
||||
|
||||
You can also explicitly mark individual entities read only directly on the
|
||||
UnitOfWork via a call to ``markReadOnly()``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$user = $entityManager->find(User::class, $id);
|
||||
$entityManager->getUnitOfWork()->markReadOnly($user);
|
||||
|
||||
Or you can set all objects that are the result of a query hydration to be
|
||||
marked as read only with the following query hint:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$query = $entityManager->createQuery('SELECT u FROM App\\Entity\\User u');
|
||||
$query->setHint(Query::HINT_READ_ONLY, true);
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
Extra-Lazy Collections
|
||||
----------------------
|
||||
@@ -60,7 +92,9 @@ Apply Best Practices
|
||||
A lot of the points mentioned in the Best Practices chapter will
|
||||
also positively affect the performance of Doctrine.
|
||||
|
||||
See :doc:`Best Practices <reference/best-practices>`
|
||||
|
||||
Change Tracking policies
|
||||
------------------------
|
||||
|
||||
See: :doc:`Change Tracking Policies <change-tracking-policies>`
|
||||
See: :doc:`Change Tracking Policies <reference/change-tracking-policies>`
|
||||
|
||||
@@ -25,44 +25,36 @@ 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 Person
|
||||
class MappedSuperclassBase
|
||||
{
|
||||
/** @Column(type="integer") */
|
||||
protected $mapped1;
|
||||
/** @Column(type="string") */
|
||||
protected $mapped2;
|
||||
/**
|
||||
* @OneToOne(targetEntity="Toothbrush")
|
||||
* @JoinColumn(name="toothbrush_id", referencedColumnName="id")
|
||||
* @OneToOne(targetEntity="MappedSuperclassRelated1")
|
||||
* @JoinColumn(name="related1_id", referencedColumnName="id")
|
||||
*/
|
||||
protected $toothbrush;
|
||||
|
||||
protected $mappedRelated1;
|
||||
|
||||
// ... more fields and methods
|
||||
}
|
||||
|
||||
|
||||
/** @Entity */
|
||||
class Employee extends Person
|
||||
class EntitySubClass extends MappedSuperclassBase
|
||||
{
|
||||
/** @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
|
||||
}
|
||||
|
||||
@@ -81,7 +73,7 @@ defined on that class directly.
|
||||
Single Table Inheritance
|
||||
------------------------
|
||||
|
||||
`Single Table Inheritance <https://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
|
||||
`Single Table Inheritance <http://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
|
||||
@@ -92,10 +84,10 @@ Example:
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
|
||||
<?php
|
||||
namespace MyProject\Model;
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("SINGLE_TABLE")
|
||||
@@ -106,7 +98,7 @@ Example:
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
@@ -115,9 +107,25 @@ 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
|
||||
@@ -131,7 +139,9 @@ 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, an exception will be thrown.
|
||||
- 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.
|
||||
|
||||
Design-time considerations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -152,9 +162,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.
|
||||
|
||||
@@ -171,11 +181,11 @@ the root entity of the single-table inheritance hierarchy.
|
||||
Class Table Inheritance
|
||||
-----------------------
|
||||
|
||||
`Class Table Inheritance <https://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
|
||||
`Class Table Inheritance <http://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
|
||||
of a parent class through a foreign key constraint. Doctrine 2
|
||||
of a parent class through a foreign key constraint. Doctrine ORM
|
||||
implements this strategy through the use of a discriminator column
|
||||
in the topmost table of the hierarchy because this is the easiest
|
||||
way to achieve polymorphic queries with Class Table Inheritance.
|
||||
@@ -186,7 +196,7 @@ Example:
|
||||
|
||||
<?php
|
||||
namespace MyProject\Model;
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("JOINED")
|
||||
@@ -197,7 +207,7 @@ Example:
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
/** @Entity */
|
||||
class Employee extends Person
|
||||
{
|
||||
@@ -206,6 +216,7 @@ Example:
|
||||
|
||||
Things to note:
|
||||
|
||||
|
||||
- The @InheritanceType, @DiscriminatorColumn and @DiscriminatorMap
|
||||
must be specified on the topmost class that is part of the mapped
|
||||
entity hierarchy.
|
||||
@@ -218,7 +229,9 @@ 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, an exception will be thrown.
|
||||
- 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.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -228,6 +241,7 @@ 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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -254,12 +268,15 @@ 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.
|
||||
|
||||
There is also another important performance consideration that it is *NOT POSSIBLE*
|
||||
to query for the base entity without any LEFT JOINs to the sub-types.
|
||||
|
||||
SQL Schema considerations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -275,9 +292,16 @@ column and cascading on delete.
|
||||
|
||||
Overrides
|
||||
---------
|
||||
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.
|
||||
|
||||
Used to override a mapping for an entity field or relationship. Can only be
|
||||
applied to an entity that extends a mapped superclass or uses a trait to
|
||||
override a relationship or field mapping defined by the mapped superclass or
|
||||
trait.
|
||||
|
||||
It is not possible to override attributes or associations in entity to entity
|
||||
inheritance scenarios, because this can cause unforseen edge case behavior and
|
||||
increases complexity in ORM internal classes.
|
||||
|
||||
|
||||
Association Override
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -300,7 +324,7 @@ Example:
|
||||
*/
|
||||
class User
|
||||
{
|
||||
// other fields mapping
|
||||
//other fields mapping
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity="Group", inversedBy="users")
|
||||
@@ -350,7 +374,8 @@ Example:
|
||||
<many-to-many field="groups" target-entity="Group" inversed-by="users">
|
||||
<cascade>
|
||||
<cascade-persist/>
|
||||
<cascade-refresh/>
|
||||
<cascade-merge/>
|
||||
<cascade-detach/>
|
||||
</cascade>
|
||||
<join-table name="users_groups">
|
||||
<join-columns>
|
||||
@@ -386,6 +411,51 @@ 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:
|
||||
|
||||
@@ -393,7 +463,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
|
||||
@@ -461,7 +531,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-refresh/>
|
||||
<cascade-merge/>
|
||||
</cascade>
|
||||
<join-column name="address_id" referenced-column-name="id"/>
|
||||
</many-to-one>
|
||||
@@ -482,12 +552,48 @@ 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:
|
||||
|
||||
- The "attribute override" specifies the overrides base on the property name.
|
||||
- The column type *CANNOT* be changed. If the column type is not equal you get a ``MappingException``
|
||||
- The override can redefine all the columns except the type.
|
||||
- The override can redefine all the attributes except the type.
|
||||
|
||||
Query the Type
|
||||
--------------
|
||||
|
||||
@@ -6,7 +6,7 @@ Therefore we think it is very important to be honest about the
|
||||
current limitations to our users. Much like every other piece of
|
||||
software Doctrine2 is not perfect and far from feature complete.
|
||||
This section should give you an overview of current limitations of
|
||||
Doctrine 2 as well as critical known issues that you should know
|
||||
Doctrine ORM as well as critical known issues that you should know
|
||||
about.
|
||||
|
||||
Current Limitations
|
||||
@@ -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,
|
||||
@@ -65,6 +65,15 @@ Where the ``attribute_name`` column contains the key and
|
||||
The feature request for persistence of primitive value arrays
|
||||
`is described in the DDC-298 ticket <https://github.com/doctrine/orm/issues/3743>`_.
|
||||
|
||||
Cascade Merge with Bi-directional Associations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are two bugs now that concern the use of cascade merge in combination with bi-directional associations.
|
||||
Make sure to study the behavior of cascade merge if you are using it:
|
||||
|
||||
- `DDC-875 <https://github.com/doctrine/orm/issues/5398>`_ Merge can sometimes add the same entity twice into a collection
|
||||
- `DDC-763 <https://github.com/doctrine/orm/issues/5277>`_ Cascade merge on associated entities can insert too many rows through "Persistence by Reachability"
|
||||
|
||||
Custom Persisters
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -98,7 +107,7 @@ to the same entity.
|
||||
Behaviors
|
||||
~~~~~~~~~
|
||||
|
||||
Doctrine 2 will **never** include a behavior system like Doctrine 1
|
||||
Doctrine ORM will **never** include a behavior system like Doctrine 1
|
||||
in the core library. We don't think behaviors add more value than
|
||||
they cost pain and debugging hell. Please see the many different
|
||||
blog posts we have written on this topics:
|
||||
@@ -106,9 +115,9 @@ blog posts we have written on this topics:
|
||||
- `Doctrine2 "Behaviors" in a Nutshell <http://www.doctrine-project.org/2010/02/17/doctrine2-behaviours-nutshell.html>`_
|
||||
- `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>`_
|
||||
- `Doctrine ORM Behavioral Extensions <http://www.doctrine-project.org/2010/11/18/doctrine2-behavioral-extensions.html>`_
|
||||
|
||||
Doctrine 2 has enough hooks and extension points so that **you** can
|
||||
Doctrine ORM has enough hooks and extension points so that **you** can
|
||||
add whatever you want on top of it. None of this will ever become
|
||||
core functionality of Doctrine2 however, you will have to rely on
|
||||
third party extensions for magical behaviors.
|
||||
@@ -117,12 +126,13 @@ Nested Set
|
||||
~~~~~~~~~~
|
||||
|
||||
NestedSet was offered as a behavior in Doctrine 1 and will not be
|
||||
included in the core of Doctrine 2. However there are already two
|
||||
included in the core of Doctrine ORM. However there are already two
|
||||
extensions out there that offer support for Nested Set with
|
||||
Doctrine 2:
|
||||
ORM:
|
||||
|
||||
- `Doctrine2 Hierarchical-Structural Behavior <https://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior>`_
|
||||
- `Doctrine2 NestedSet <https://github.com/blt04/doctrine2-nestedset>`_
|
||||
|
||||
- `Doctrine2 Hierarchical-Structural Behavior <http://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior>`_
|
||||
- `Doctrine2 NestedSet <http://github.com/blt04/doctrine2-nestedset>`_
|
||||
|
||||
Known Issues
|
||||
------------
|
||||
@@ -139,9 +149,10 @@ Identifier Quoting and Legacy Databases
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For compatibility reasons between all the supported vendors and
|
||||
edge case problems Doctrine 2 does **NOT** do automatic identifier
|
||||
edge case problems Doctrine ORM does **NOT** do automatic identifier
|
||||
quoting. This can lead to problems when trying to get
|
||||
legacy-databases to work with Doctrine 2.
|
||||
legacy-databases to work with Doctrine ORM.
|
||||
|
||||
|
||||
- You can quote column-names as described in the
|
||||
:doc:`Basic-Mapping <basic-mapping>` section.
|
||||
@@ -166,3 +177,27 @@ 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,8 +11,11 @@ Core Metadata Drivers
|
||||
Doctrine provides a few different ways for you to specify your
|
||||
metadata:
|
||||
|
||||
|
||||
- **XML files** (XmlDriver)
|
||||
- **Class DocBlock Annotations** (AnnotationDriver)
|
||||
- **Attributes** (AttributeDriver)
|
||||
- **YAML files** (YamlDriver)
|
||||
- **PHP Code in files or static functions** (PhpDriver)
|
||||
|
||||
Something important to note about the above drivers is they are all
|
||||
@@ -35,6 +38,7 @@ 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:
|
||||
@@ -56,26 +60,26 @@ implements the ``Driver`` interface:
|
||||
|
||||
<?php
|
||||
namespace Doctrine\ORM\Mapping\Driver;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
|
||||
interface Driver
|
||||
{
|
||||
/**
|
||||
* Loads the metadata for the specified class into the provided container.
|
||||
*
|
||||
*
|
||||
* @param string $className
|
||||
* @param ClassMetadata $metadata
|
||||
* @param ClassMetadataInfo $metadata
|
||||
*/
|
||||
function loadMetadataForClass($className, ClassMetadata $metadata);
|
||||
|
||||
function loadMetadataForClass($className, ClassMetadataInfo $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
|
||||
@@ -99,22 +103,22 @@ the ``AbstractFileDriver`` implementation for you to extend from:
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $fileExtension = '.dcm.ext';
|
||||
|
||||
protected $_fileExtension = '.dcm.ext';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadMetadataForClass($className, ClassMetadata $metadata)
|
||||
public function loadMetadataForClass($className, ClassMetadataInfo $metadata)
|
||||
{
|
||||
$data = $this->loadMappingFile($file);
|
||||
|
||||
// populate ClassMetadata instance from $data
|
||||
$data = $this->_loadMappingFile($file);
|
||||
|
||||
// populate ClassMetadataInfo instance from $data
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function loadMappingFile($file)
|
||||
protected function _loadMappingFile($file)
|
||||
{
|
||||
// parse contents of $file and return php data structure
|
||||
}
|
||||
@@ -130,6 +134,7 @@ 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:
|
||||
|
||||
@@ -143,18 +148,21 @@ ClassMetadata
|
||||
-------------
|
||||
|
||||
The last piece you need to know and understand about metadata in
|
||||
Doctrine 2 is the API of the ``ClassMetadata`` classes. You need to
|
||||
Doctrine ORM is the API of the ``ClassMetadata`` classes. You need to
|
||||
be familiar with them in order to implement your own drivers but
|
||||
more importantly to retrieve mapping information for a certain
|
||||
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 ``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
|
||||
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
|
||||
entity before it exists and using that information to generate for
|
||||
example the entities themselves.
|
||||
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.
|
||||
|
||||
You can read more about the API of the ``ClassMetadata`` classes in
|
||||
the PHP Mapping chapter.
|
||||
@@ -184,3 +192,4 @@ iterate over them:
|
||||
echo $fieldMapping['fieldName'] . "\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
Implementing a NamingStrategy
|
||||
==============================
|
||||
|
||||
.. versionadded:: 2.3
|
||||
|
||||
Using a naming strategy you can provide rules for generating database identifiers,
|
||||
column or table names when the column or table name is not given. This feature helps
|
||||
column or table names. This feature helps
|
||||
reduce the verbosity of the mapping document, eliminating repetitive noise (eg: ``TABLE_``).
|
||||
|
||||
.. warning
|
||||
|
||||
The naming strategy is always overridden by entity mapping such as the `Table` annotation.
|
||||
|
||||
Configuring a naming strategy
|
||||
-----------------------------
|
||||
The default strategy used by Doctrine is quite minimal.
|
||||
@@ -102,6 +104,7 @@ achieve such standards by implementing a naming strategy.
|
||||
|
||||
You need to create a class which implements ``Doctrine\ORM\Mapping\NamingStrategy``.
|
||||
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
@@ -80,9 +80,7 @@ with inheritance hierarchies.
|
||||
|
||||
The builder extends the ``ResultSetMapping`` class and as such has all the functionality of it as well.
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
Starting with Doctrine ORM 2.4 you can generate the ``SELECT`` clause
|
||||
The ``SELECT`` clause can be generated
|
||||
from a ``ResultSetMappingBuilder``. You can either cast the builder
|
||||
object to ``(string)`` and the DQL aliases are used as SQL table aliases
|
||||
or use the ``generateSelectClause($tableAliases)`` method and pass
|
||||
@@ -98,6 +96,7 @@ a mapping from DQL alias (key) to SQL alias (value)
|
||||
));
|
||||
$sql = "SELECT " . $selectClause . " FROM users t1 JOIN groups t2 ON t1.group_id = t2.id";
|
||||
|
||||
|
||||
The ResultSetMapping
|
||||
--------------------
|
||||
|
||||
@@ -105,6 +104,7 @@ 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.
|
||||
@@ -130,6 +130,7 @@ 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.
|
||||
|
||||
@@ -320,7 +321,7 @@ entity.
|
||||
$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();
|
||||
@@ -356,7 +357,7 @@ thus owns the foreign key.
|
||||
$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();
|
||||
@@ -387,7 +388,7 @@ associations that are lazy.
|
||||
|
||||
$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();
|
||||
@@ -420,7 +421,7 @@ to map the hierarchy (both use a discriminator column).
|
||||
$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();
|
||||
@@ -434,12 +435,17 @@ strategy but with native SQL it is your responsibility.
|
||||
Named Native Query
|
||||
------------------
|
||||
|
||||
.. note::
|
||||
|
||||
Named Native Queries are deprecated as of version 2.9 and will be removed in ORM 3.0
|
||||
|
||||
You can also map a native query using a named native query mapping.
|
||||
|
||||
To achieve that, you must describe the SQL resultset structure
|
||||
using named native query (and sql resultset mappings if is a several resultset mappings).
|
||||
|
||||
Like named query, a named native query can be defined at class level or in an XML file.
|
||||
Like named query, a named native query can be defined at class level or in a XML or YAML file.
|
||||
|
||||
|
||||
A resultSetMapping parameter is defined in @NamedNativeQuery,
|
||||
it represents the name of a defined @SqlResultSetMapping.
|
||||
@@ -534,6 +540,47 @@ 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.
|
||||
@@ -544,6 +591,7 @@ 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,
|
||||
@@ -609,6 +657,21 @@ 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.
|
||||
@@ -618,6 +681,7 @@ 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
|
||||
@@ -696,6 +760,41 @@ 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:
|
||||
@@ -731,6 +830,16 @@ 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.
|
||||
@@ -781,3 +890,18 @@ 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
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
Partial Objects
|
||||
===============
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Creating Partial Objects through DQL is deprecated and
|
||||
will be removed in the future, use data transfer object
|
||||
support in DQL instead. (`Details
|
||||
<https://github.com/doctrine/orm/issues/8471>`_)
|
||||
|
||||
A partial object is an object whose state is not fully initialized
|
||||
after being reconstituted from the database and that is
|
||||
disconnected from the rest of its data. The following section will
|
||||
@@ -23,6 +31,7 @@ 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?
|
||||
--------------------
|
||||
|
||||
@@ -86,3 +95,4 @@ Mainly for optimization purposes, but be careful of premature
|
||||
optimization as partial objects lead to potentially more fragile
|
||||
code.
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
PHP Mapping
|
||||
===========
|
||||
|
||||
Doctrine 2 also allows you to provide the ORM metadata in the form
|
||||
Doctrine ORM also allows you to provide the ORM metadata in the form
|
||||
of plain PHP code using the ``ClassMetadata`` API. You can write
|
||||
the code in PHP files or inside of a static function named
|
||||
``loadMetadata($class)`` on the entity class itself.
|
||||
@@ -180,29 +180,18 @@ 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
|
||||
|
||||
ClassMetadata API
|
||||
ClassMetadataInfo API
|
||||
---------------------
|
||||
|
||||
The ``ClassMetadata`` class is the base data object for storing
|
||||
The ``ClassMetadataInfo`` 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)``
|
||||
@@ -215,6 +204,7 @@ General Setters
|
||||
Inheritance Setters
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``setInheritanceType($type)``
|
||||
- ``setSubclasses(array $subclasses)``
|
||||
- ``setParentClasses(array $classNames)``
|
||||
@@ -224,18 +214,24 @@ Inheritance Setters
|
||||
Field Mapping Setters
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- ``addProperty(Property $property)``
|
||||
- ``addAssociation(AssociationMetadata $property)``
|
||||
|
||||
- ``mapField(array $mapping)``
|
||||
- ``mapOneToOne(array $mapping)``
|
||||
- ``mapOneToMany(array $mapping)``
|
||||
- ``mapManyToOne(array $mapping)``
|
||||
- ``mapManyToMany(array $mapping)``
|
||||
|
||||
Lifecycle Callback Setters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``addLifecycleCallback($callback, $event)``
|
||||
- ``setLifecycleCallbacks(array $callbacks)``
|
||||
|
||||
Versioning Setters
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``setVersionMapping(array &$mapping)``
|
||||
- ``setVersioned($bool)``
|
||||
- ``setVersionField()``
|
||||
@@ -243,6 +239,7 @@ Versioning Setters
|
||||
General Getters
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``getTableName()``
|
||||
- ``getSchemaName()``
|
||||
- ``getTemporaryIdTableName()``
|
||||
@@ -250,8 +247,14 @@ General Getters
|
||||
Identifier Getters
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
- ``getIdentifierColumnNames()``
|
||||
- ``usesIdGenerator()``
|
||||
- ``isIdentifier($fieldName)``
|
||||
- ``isIdGeneratorIdentity()``
|
||||
- ``isIdGeneratorSequence()``
|
||||
- ``isIdGeneratorTable()``
|
||||
- ``isIdentifierNatural()``
|
||||
- ``getIdentifierFieldNames()``
|
||||
- ``getSingleIdentifierFieldName()``
|
||||
- ``getSingleIdentifierColumnName()``
|
||||
@@ -259,18 +262,35 @@ 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)``
|
||||
@@ -280,6 +300,26 @@ 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)``
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,12 @@ programmatically build queries, and also provides a fluent API.
|
||||
This means that you can change between one methodology to the other
|
||||
as you want, or just pick a preferred one.
|
||||
|
||||
.. note::
|
||||
|
||||
The ``QueryBuilder`` is not an abstraction of DQL, but merely a tool to dynamically build it.
|
||||
You should still use plain DQL when you can, as it is simpler and more readable.
|
||||
More about this in the :doc:`FAQ <faq>`_.
|
||||
|
||||
Constructing a new QueryBuilder object
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -38,6 +44,7 @@ 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
|
||||
@@ -65,6 +72,7 @@ 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
|
||||
@@ -74,10 +82,11 @@ STATE\_DIRTY. One ``QueryBuilder`` can be in two different states:
|
||||
Working with QueryBuilder
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
High level API methods
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To simplify even more the way you build a query in Doctrine, you can take
|
||||
The most straightforward way to build a dynamic query with the ``QueryBuilder`` is by taking
|
||||
advantage of Helper methods. For all base code, there is a set of
|
||||
useful methods to simplify a programmer's life. To illustrate how
|
||||
to work with them, here is the same example 6 re-written using
|
||||
@@ -94,10 +103,9 @@ to work with them, here is the same example 6 re-written using
|
||||
->orderBy('u.name', 'ASC');
|
||||
|
||||
``QueryBuilder`` helper methods are considered the standard way to
|
||||
build DQL queries. Although it is supported, using string-based
|
||||
queries should be avoided. You are greatly encouraged to use
|
||||
``$qb->expr()->*`` methods. Here is a converted example 8 to
|
||||
suggested standard way to build queries:
|
||||
use the ``QueryBuilder``. The ``$qb->expr()->*`` methods can help you
|
||||
build conditional expressions dynamically. Here is a converted example 8 to
|
||||
suggested way to build queries with dynamic conditions:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -244,8 +252,23 @@ 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 DBAL
|
||||
Doctrine\DBAL\ParameterType::* or a DBAL Type name for conversion.
|
||||
the third argument to ``setParameter()`` explicitly. It accepts either a PDO
|
||||
type or a DBAL Type name for conversion.
|
||||
|
||||
.. note::
|
||||
|
||||
Even though passing DateTime instance is allowed, it impacts performance
|
||||
as by default there is an attempt to load metadata for object, and if it's not found,
|
||||
type is inferred from the original value.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
|
||||
// prevents attempt to load metadata for date time class, improving performance
|
||||
$qb->setParameter('date', new \DateTimeImmutable(), Types::DATE_IMMUTABLE)
|
||||
|
||||
If you've got several parameters to bind to your query, you can
|
||||
also use setParameters() instead of setParameter() with the
|
||||
@@ -254,10 +277,17 @@ following syntax:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Query\Parameter;
|
||||
|
||||
// $qb instanceof QueryBuilder
|
||||
|
||||
// Query here...
|
||||
$qb->setParameters(array(1 => 'value for ?1', 2 => 'value for ?2'));
|
||||
$qb->setParameters(new ArrayCollection([
|
||||
new Parameter('1', 'value for ?1'),
|
||||
new Parameter('2', 'value for ?2')
|
||||
]));
|
||||
|
||||
Getting already bound parameters is easy - simply use the above
|
||||
mentioned syntax with "getParameter()" or "getParameters()":
|
||||
@@ -331,6 +361,7 @@ a querybuilder instance into a Query object:
|
||||
|
||||
// Execute Query
|
||||
$result = $query->getResult();
|
||||
$iterableResult = $query->toIterable();
|
||||
$single = $query->getSingleResult();
|
||||
$array = $query->getArrayResult();
|
||||
$scalar = $query->getScalarResult();
|
||||
@@ -376,6 +407,7 @@ 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
|
||||
@@ -402,6 +434,7 @@ 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
|
||||
@@ -416,6 +449,7 @@ 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())
|
||||
@@ -450,6 +484,7 @@ 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')
|
||||
@@ -485,6 +520,9 @@ complete list of supported helper methods available:
|
||||
// Example - $qb->expr()->sqrt('u.currentBalance')
|
||||
public function sqrt($x); // Returns Expr\Func
|
||||
|
||||
// Example - $qb->expr()->mod('u.currentBalance', '10')
|
||||
public function mod($x); // Returns Expr\Func
|
||||
|
||||
// Example - $qb->expr()->count('u.firstname')
|
||||
public function count($x); // Returns Expr\Func
|
||||
|
||||
@@ -505,7 +543,7 @@ using ``addCriteria``:
|
||||
// ...
|
||||
|
||||
$criteria = Criteria::create()
|
||||
->orderBy(['firstName' => Criteria::ASC]);
|
||||
->orderBy(['firstName', 'ASC']);
|
||||
|
||||
// $qb instanceof QueryBuilder
|
||||
$qb->addCriteria($criteria);
|
||||
@@ -524,6 +562,7 @@ 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
|
||||
@@ -568,4 +607,3 @@ same query of example 6 written using
|
||||
->add('from', new Expr\From('User', 'u'))
|
||||
->add('where', new Expr\Comparison('u.id', '=', '?1'))
|
||||
->add('orderBy', new Expr\OrderBy('u.name', 'ASC'));
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ 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
|
||||
---------------
|
||||
|
||||
@@ -41,6 +42,7 @@ 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 :
|
||||
|
||||
@@ -64,35 +66,34 @@ 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
|
||||
-------------
|
||||
|
||||
``Doctrine\ORM\Cache\Region\DefaultRegion`` It's the default implementation.
|
||||
``Doctrine\ORM\Cache\Region\DefaultRegion`` is the default implementation.
|
||||
A simplest cache region compatible with all doctrine-cache drivers but does not support locking.
|
||||
|
||||
``Doctrine\ORM\Cache\Region`` and ``Doctrine\ORM\Cache\ConcurrentRegion``
|
||||
Defines contracts that should be implemented by a cache provider.
|
||||
define contracts that should be implemented by a cache provider.
|
||||
|
||||
It allows you to provide your own cache implementation that might take advantage of specific cache driver.
|
||||
|
||||
If you want to support locking for ``READ_WRITE`` strategies you should implement ``ConcurrentRegion``; ``CacheRegion`` otherwise.
|
||||
|
||||
|
||||
Cache region
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Defines a contract for accessing a particular region.
|
||||
``Doctrine\ORM\Cache\Region`` defines a contract for accessing a particular
|
||||
cache region.
|
||||
|
||||
``Doctrine\ORM\Cache\Region``
|
||||
|
||||
Defines a contract for accessing a particular cache region.
|
||||
|
||||
`See API Doc <https://www.doctrine-project.org/api/orm/latest/Doctrine/ORM/Cache/Region.html>`_.
|
||||
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Region.html>`_.
|
||||
|
||||
Concurrent cache region
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -102,9 +103,7 @@ By default, Doctrine provides a very simple implementation based on file locks `
|
||||
|
||||
If you want to use an ``READ_WRITE`` cache, you should consider providing your own cache region.
|
||||
|
||||
``Doctrine\ORM\Cache\ConcurrentRegion``
|
||||
|
||||
Defines contract for concurrently managed data region.
|
||||
``Doctrine\ORM\Cache\ConcurrentRegion`` defines a contract for concurrently managed data region.
|
||||
|
||||
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/ConcurrentRegion.html>`_.
|
||||
|
||||
@@ -133,6 +132,7 @@ 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``
|
||||
|
||||
@@ -141,6 +141,7 @@ Caching mode
|
||||
* Slowest strategy.
|
||||
* To use it a the cache region implementation must support locking.
|
||||
|
||||
|
||||
Built-in cached persisters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -166,6 +167,7 @@ Configuration
|
||||
-------------
|
||||
Doctrine allows you to specify configurations and some points of extension for the second-level-cache
|
||||
|
||||
|
||||
Enable Second Level Cache
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -175,11 +177,10 @@ To enable the second-level-cache, you should provide a cache factory.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @var \Doctrine\ORM\Cache\RegionsConfiguration $cacheConfig */
|
||||
/** @var \Doctrine\Common\Cache\Cache $cache */
|
||||
/** @var \Doctrine\ORM\Configuration $config */
|
||||
/* @var $config \Doctrine\ORM\Cache\RegionsConfiguration */
|
||||
/* @var $cache \Doctrine\Common\Cache\Cache */
|
||||
|
||||
$factory = new \Doctrine\ORM\Cache\DefaultCacheFactory($cacheConfig, $cache);
|
||||
$factory = new \Doctrine\ORM\Cache\DefaultCacheFactory($config, $cache);
|
||||
|
||||
// Enable second-level-cache
|
||||
$config->setSecondLevelCacheEnabled();
|
||||
@@ -188,6 +189,7 @@ To enable the second-level-cache, you should provide a cache factory.
|
||||
$config->getSecondLevelCacheConfiguration()
|
||||
->setCacheFactory($factory);
|
||||
|
||||
|
||||
Cache Factory
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
@@ -195,11 +197,16 @@ Cache Factory is the main point of extension.
|
||||
|
||||
It allows you to provide a specific implementation of the following components :
|
||||
|
||||
* ``QueryCache`` Store and retrieve query cache results.
|
||||
* ``CachedEntityPersister`` Store and retrieve entity results.
|
||||
* ``CachedCollectionPersister`` Store and retrieve query results.
|
||||
* ``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
|
||||
``QueryCache``
|
||||
stores and retrieves query cache results.
|
||||
``CachedEntityPersister``
|
||||
stores and retrieves entity results.
|
||||
``CachedCollectionPersister``
|
||||
stores and retrieves query results.
|
||||
``EntityHydrator``
|
||||
transforms entities into a cache entries and cache entries into entities
|
||||
``CollectionHydrator``
|
||||
transforms collections into cache entries and cache entries into collections
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/DefaultCacheFactory.html>`_.
|
||||
|
||||
@@ -211,15 +218,15 @@ To specify a default lifetime for all regions or specify a different lifetime fo
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @var \Doctrine\ORM\Configuration $config */
|
||||
/** @var \Doctrine\ORM\Cache\CacheConfiguration $cacheConfig */
|
||||
/** @var \Doctrine\ORM\Cache\RegionsConfiguration $regionConfig */
|
||||
/* @var $config \Doctrine\ORM\Configuration */
|
||||
/* @var $cacheConfig \Doctrine\ORM\Cache\CacheConfiguration */
|
||||
$cacheConfig = $config->getSecondLevelCacheConfiguration();
|
||||
$regionConfig = $cacheConfig->getRegionsConfiguration();
|
||||
|
||||
// Cache Region lifetime
|
||||
$regionConfig->setLifetime('my_entity_region', 3600); // Time to live for a specific region; In seconds
|
||||
$regionConfig->setDefaultLifetime(7200); // Default time to live; In seconds
|
||||
$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
|
||||
~~~~~~~~~
|
||||
@@ -230,7 +237,7 @@ By providing a cache logger you should be able to get information about all cach
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @var \Doctrine\ORM\Configuration $config */
|
||||
/* @var $config \Doctrine\ORM\Configuration */
|
||||
$logger = new \Doctrine\ORM\Cache\Logging\StatisticsCacheLogger();
|
||||
|
||||
// Cache logger
|
||||
@@ -238,6 +245,7 @@ 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.
|
||||
@@ -258,8 +266,9 @@ By providing a cache logger you should be able to get information about all cach
|
||||
// Get the total number of cached entries *not* found in all regions.
|
||||
$logger->getMissCount();
|
||||
|
||||
If you want to get more information you should implement ``\Doctrine\ORM\Cache\Logging\CacheLogger``.
|
||||
and collect all information you want.
|
||||
If you want to get more information you should implement
|
||||
``\Doctrine\ORM\Cache\Logging\CacheLogger`` and collect
|
||||
all the information you want.
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Logging/CacheLogger.html>`_.
|
||||
|
||||
@@ -268,8 +277,12 @@ Entity cache definition
|
||||
-----------------------
|
||||
* Entity cache configuration allows you to define the caching strategy and region for an entity.
|
||||
|
||||
* ``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.
|
||||
* ``usage`` specifies the caching strategy: ``READ_ONLY``,
|
||||
``NONSTRICT_READ_WRITE``, ``READ_WRITE``.
|
||||
See :ref:`reference-second-level-cache-mode`.
|
||||
* ``region`` is an optional value that specifies the name of the second
|
||||
level cache region.
|
||||
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@@ -310,11 +323,30 @@ 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
|
||||
@@ -367,7 +399,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" />
|
||||
|
||||
@@ -382,6 +414,39 @@ 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
|
||||
@@ -399,8 +464,8 @@ Basic entity cache
|
||||
|
||||
$country1 = $em->find('Country', 1); // Retrieve item from cache
|
||||
|
||||
$country1->setName("New Name");
|
||||
|
||||
$country->setName("New Name");
|
||||
$em->persist($country);
|
||||
$em->flush(); // Hit database to update the row and update cache
|
||||
|
||||
$em->clear(); // Clear entity manager
|
||||
@@ -408,6 +473,7 @@ 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
|
||||
@@ -475,7 +541,7 @@ The query cache stores the results of the query but as identifiers, entity value
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @var \Doctrine\ORM\EntityManager $em */
|
||||
/* @var $em \Doctrine\ORM\EntityManager */
|
||||
|
||||
// Execute database query, store query cache and entity cache
|
||||
$result1 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
|
||||
@@ -502,7 +568,7 @@ The Cache Mode controls how a particular query interacts with the second-level c
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @var \Doctrine\ORM\EntityManager $em */
|
||||
/* @var $em \Doctrine\ORM\EntityManager */
|
||||
// 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)
|
||||
@@ -516,38 +582,42 @@ The Cache Mode controls how a particular query interacts with the second-level c
|
||||
DELETE / UPDATE queries
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
DQL UPDATE / DELETE statements are ported directly into a database and bypass the second-level cache,
|
||||
DQL UPDATE / DELETE statements are ported directly into a database and bypass
|
||||
the second-level cache.
|
||||
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);
|
||||
@@ -556,7 +626,7 @@ Using the repository query cache
|
||||
--------------------------------
|
||||
|
||||
As well as ``Query Cache`` all persister queries store only identifier values for an individual query.
|
||||
All persister use a single timestamps cache region keeps track of the last update for each persister,
|
||||
All persisters use a single timestamp cache region to keep track of the last update for each persister,
|
||||
When a query is loaded from cache, the timestamp region is checked for the last update for that persister.
|
||||
Using the last update timestamps as part of the query key invalidate the cache key when an update occurs.
|
||||
|
||||
@@ -575,7 +645,7 @@ Using the last update timestamps as part of the query key invalidate the cache k
|
||||
$em->clear();
|
||||
|
||||
// Reload from database.
|
||||
// At this point the query cache key if not logger valid, the select goes straight
|
||||
// At this point the query cache key is no longer valid, the select goes straight to the database
|
||||
$entities = $em->getRepository('Entity\Country')->findAll();
|
||||
|
||||
Cache API
|
||||
@@ -587,7 +657,7 @@ However, you can use the cache API to check / invalidate cache entries.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @var \Doctrine\ORM\Cache $cache */
|
||||
/* @var $cache \Doctrine\ORM\Cache */
|
||||
$cache = $em->getCache();
|
||||
|
||||
$cache->containsEntity('Entity\State', 1) // Check if the cache exists
|
||||
@@ -632,11 +702,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
|
||||
@@ -657,8 +727,10 @@ 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
|
||||
~~~~~~~~~
|
||||
|
||||
Count queries generated by ``Doctrine\ORM\Tools\Pagination\Paginator`` are not cached by second-level cache.
|
||||
Although entities and query result are cached count queries will hit the database every time.
|
||||
Although entities and query result are cached, count queries will hit the
|
||||
database every time.
|
||||
|
||||
@@ -81,6 +81,7 @@ this is technically impossible. The correct way is:
|
||||
$query = $entityManager->createQuery($dql);
|
||||
$query->setParameter(1, $_GET['status']);
|
||||
|
||||
|
||||
Preventing Mass Assignment Vulnerabilities
|
||||
------------------------------------------
|
||||
|
||||
@@ -117,7 +118,7 @@ entity might look like this:
|
||||
}
|
||||
}
|
||||
|
||||
Now the possibility of mass-assignment exists on this entity and can
|
||||
Now the possiblity of mass-asignment 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:
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Doctrine Console
|
||||
----------------
|
||||
|
||||
The Doctrine Console is a Command Line Interface tool for simplifying common
|
||||
administration tasks during the development of a project that uses Doctrine 2.
|
||||
administration tasks during the development of a project that uses ORM.
|
||||
|
||||
Take a look at the :doc:`Installation and Configuration <configuration>`
|
||||
chapter for more information how to setup the console command.
|
||||
@@ -22,66 +22,37 @@ about the use of generate entities for example, you can call:
|
||||
|
||||
$> php vendor/bin/doctrine orm:generate-entities --help
|
||||
|
||||
|
||||
Configuration
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Whenever the ``doctrine`` command line tool is invoked, it can
|
||||
access all Commands that were registered by developer. There is no
|
||||
access all Commands that were registered by a developer. There is no
|
||||
auto-detection mechanism at work. The Doctrine binary
|
||||
already registers all the commands that currently ship with
|
||||
Doctrine DBAL and ORM. If you want to use additional commands you
|
||||
have to register them yourself.
|
||||
|
||||
All the commands of the Doctrine Console require access to the ``EntityManager``
|
||||
or ``DBAL`` Connection. You have to inject them into the console application
|
||||
using so called Helper-Sets. This requires either the ``db``
|
||||
or the ``em`` helpers to be defined in order to work correctly.
|
||||
All the commands of the Doctrine Console require access to the
|
||||
``EntityManager``. You have to inject it into the console application with
|
||||
``ConsoleRunner::createHelperSet``. Whenever you invoke the Doctrine
|
||||
binary, it searches the current directory for the file ``cli-config.php``.
|
||||
This file contains the project-specific configuration.
|
||||
|
||||
Whenever you invoke the Doctrine binary the current folder is searched for a
|
||||
``cli-config.php`` file. This file contains the project specific configuration:
|
||||
Here is an example of a the project-specific ``cli-config.php``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
|
||||
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($conn)
|
||||
));
|
||||
$cli->setHelperSet($helperSet);
|
||||
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
||||
|
||||
When dealing with the ORM package, the EntityManagerHelper is
|
||||
required:
|
||||
// replace this with the path to your own project bootstrap file.
|
||||
require_once 'bootstrap.php';
|
||||
|
||||
.. code-block:: php
|
||||
// replace with mechanism to retrieve EntityManager in your app
|
||||
$entityManager = GetEntityManager();
|
||||
|
||||
<?php
|
||||
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
|
||||
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
|
||||
));
|
||||
$cli->setHelperSet($helperSet);
|
||||
|
||||
The HelperSet instance has to be generated in a separate file (i.e.
|
||||
``cli-config.php``) that contains typical Doctrine bootstrap code
|
||||
and predefines the needed HelperSet attributes mentioned above. A
|
||||
sample ``cli-config.php`` file looks as follows:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// cli-config.php
|
||||
require_once 'my_bootstrap.php';
|
||||
|
||||
// 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)
|
||||
));
|
||||
|
||||
It is important to define a correct HelperSet that Doctrine binary
|
||||
script will ultimately use. The Doctrine Binary will automatically
|
||||
find the first instance of HelperSet in the global variable
|
||||
namespace and use this.
|
||||
return ConsoleRunner::createHelperSet($entityManager);
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -94,6 +65,7 @@ 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.
|
||||
@@ -107,10 +79,16 @@ 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
|
||||
@@ -125,8 +103,12 @@ 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::
|
||||
|
||||
@@ -152,6 +134,7 @@ 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.
|
||||
@@ -160,7 +143,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 ``ClassMetadata`` instances.
|
||||
array of ``ClassMetadataInfo`` instances.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -191,8 +174,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 ``ClassMetadata``
|
||||
instances.
|
||||
existing database schema to the passed array of
|
||||
``ClassMetadataInfo`` instances.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -239,19 +222,167 @@ will output the SQL for the ran operation.
|
||||
Before using the orm:schema-tool commands, remember to configure
|
||||
your cli-config.php properly.
|
||||
|
||||
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::
|
||||
|
||||
When using the Annotation Mapping Driver you have to either setup
|
||||
your autoloader in the cli-config.php correctly to find all the
|
||||
entities, or you can use the second argument of the
|
||||
``EntityManagerHelper`` to specify all the paths of your entities
|
||||
(or mapping files), i.e.
|
||||
``new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em, $mappingPaths);``
|
||||
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
|
||||
-----------------------------------------
|
||||
|
||||
For performance reasons Doctrine 2 has to skip some of the
|
||||
For performance reasons Doctrine ORM has to skip some of the
|
||||
necessary validation of metadata mappings. You have to execute
|
||||
this validation in your development workflow to verify the
|
||||
associations are correctly defined.
|
||||
@@ -293,6 +424,7 @@ number of elements with error messages.
|
||||
prefix backslash. PHP does this with ``get_class()`` or Reflection
|
||||
methods for backwards compatibility reasons.
|
||||
|
||||
|
||||
Adding own commands
|
||||
-------------------
|
||||
|
||||
@@ -338,6 +470,7 @@ defined ones) is possible through the command:
|
||||
new \MyProject\Tools\Console\Commands\OneMoreCommand(),
|
||||
));
|
||||
|
||||
|
||||
Re-use console application
|
||||
--------------------------
|
||||
|
||||
@@ -354,4 +487,3 @@ HelperSet, like it is described in the configuration section.
|
||||
|
||||
// Runs console application
|
||||
$cli->run();
|
||||
|
||||
|
||||
@@ -16,13 +16,13 @@ transaction. Without any explicit transaction demarcation from your
|
||||
side, this quickly results in poor performance because transactions
|
||||
are not cheap.
|
||||
|
||||
For the most part, Doctrine 2 already takes care of proper
|
||||
For the most part, Doctrine ORM already takes care of proper
|
||||
transaction demarcation for you: All the write operations
|
||||
(INSERT/UPDATE/DELETE) are queued until ``EntityManager#flush()``
|
||||
is invoked which wraps all of these changes in a single
|
||||
transaction.
|
||||
|
||||
However, Doctrine 2 also allows (and encourages) you to take over
|
||||
However, Doctrine ORM also allows (and encourages) you to take over
|
||||
and control transaction demarcation yourself.
|
||||
|
||||
These are two ways to deal with transactions when using the
|
||||
@@ -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,16 +98,24 @@ 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.
|
||||
commit and rolls back the transaction when an
|
||||
exception occurs.
|
||||
|
||||
.. _transactions-and-concurrency_exception-handling:
|
||||
|
||||
@@ -146,7 +154,7 @@ occurred you should do that with a new ``EntityManager``.
|
||||
Locking Support
|
||||
---------------
|
||||
|
||||
Doctrine 2 offers support for Pessimistic- and Optimistic-locking
|
||||
Doctrine ORM offers support for Pessimistic- and Optimistic-locking
|
||||
strategies natively. This allows to take very fine-grained control
|
||||
over what kind of locking is required for your Entities in your
|
||||
application.
|
||||
@@ -200,6 +208,15 @@ 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):
|
||||
|
||||
@@ -224,6 +241,15 @@ 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,
|
||||
@@ -253,15 +279,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!";
|
||||
@@ -274,16 +300,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!";
|
||||
}
|
||||
@@ -322,7 +348,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() . '" />';
|
||||
|
||||
@@ -333,7 +359,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:
|
||||
@@ -341,7 +367,7 @@ And the change headline action (POST Request):
|
||||
Pessimistic Locking
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Doctrine 2 supports Pessimistic Locking at the database level. No
|
||||
Doctrine ORM supports Pessimistic Locking at the database level. No
|
||||
attempt is being made to implement pessimistic locking inside
|
||||
Doctrine, rather vendor-specific and ANSI-SQL commands are used to
|
||||
acquire row-level locks. Every Entity can be part of a pessimistic
|
||||
@@ -350,11 +376,12 @@ lock, there is no special metadata required to use this feature.
|
||||
However for Pessimistic Locking to work you have to disable the
|
||||
Auto-Commit Mode of your Database and start a transaction around
|
||||
your pessimistic lock use-case using the "Approach 2: Explicit
|
||||
Transaction Demarcation" described above. Doctrine 2 will throw an
|
||||
Transaction Demarcation" described above. Doctrine ORM will throw an
|
||||
Exception if you attempt to acquire an pessimistic lock and no
|
||||
transaction is running.
|
||||
|
||||
Doctrine 2 currently supports two pessimistic lock modes:
|
||||
Doctrine ORM currently supports two pessimistic lock modes:
|
||||
|
||||
|
||||
- Pessimistic Write
|
||||
(``Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE``), locks the
|
||||
@@ -365,6 +392,7 @@ 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
|
||||
@@ -378,3 +406,4 @@ 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.
|
||||
@@ -39,7 +39,7 @@ side of the association and these 2 references both represent the
|
||||
same association but can change independently of one another. Of
|
||||
course, in a correct application the semantics of the bidirectional
|
||||
association are properly maintained by the application developer
|
||||
(that's his responsibility). Doctrine needs to know which of these
|
||||
(that's their responsibility). Doctrine needs to know which of these
|
||||
two in-memory references is the one that should be persisted and
|
||||
which not. This is what the owning/inverse concept is mainly used
|
||||
for.
|
||||
|
||||
@@ -36,15 +36,13 @@ will still end up with the same reference:
|
||||
|
||||
public function testIdentityMapReference()
|
||||
{
|
||||
/** @var EntityName|\ProxyManager\Proxy\GhostObjectInterface $objectA */
|
||||
$objectA = $this->entityManager->getReference(EntityName::class, 1);
|
||||
$objectA = $this->entityManager->getReference('EntityName', 1);
|
||||
// check for proxyinterface
|
||||
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $objectA);
|
||||
|
||||
self::assertInstanceOf(\ProxyManager\Proxy\GhostObjectInterface::class, $objectA);
|
||||
self::assertFalse($objectA->isProxyInitialized());
|
||||
$objectB = $this->entityManager->find('EntityName', 1);
|
||||
|
||||
$objectB = $this->entityManager->find(EntityName::class, 1);
|
||||
|
||||
self::assertSame($objectA, $objectB)
|
||||
$this->assertSame($objectA, $objectB)
|
||||
}
|
||||
|
||||
The identity map being indexed by primary keys only allows shortcuts when you
|
||||
@@ -106,7 +104,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
|
||||
@@ -131,10 +129,16 @@ optimize the performance of the Flush Operation:
|
||||
- Temporarily mark entities as read only. If you have a very large UnitOfWork
|
||||
but know that a large set of entities has not changed, just mark them as read
|
||||
only with ``$entityManager->getUnitOfWork()->markReadOnly($entity)``.
|
||||
- Flush only a single entity with ``$entityManager->flush($entity)``.
|
||||
- Use :doc:`Change Tracking Policies <change-tracking-policies>` to use more
|
||||
explicit strategies of notifying the UnitOfWork what objects/properties
|
||||
changed.
|
||||
|
||||
.. note::
|
||||
|
||||
Flush only a single entity with ``$entityManager->flush($entity)`` is deprecated and will be removed in ORM 3.0.
|
||||
(`Details <https://github.com/doctrine/orm/issues/8459>`_)
|
||||
|
||||
Query Internals
|
||||
---------------
|
||||
|
||||
@@ -148,15 +152,15 @@ Hydration
|
||||
~~~~~~~~~
|
||||
|
||||
Responsible for creating a final result from a raw database statement and a
|
||||
result-set mapping object. The developer can choose which kind of result he
|
||||
wishes to be hydrated. Default result-types include:
|
||||
result-set mapping object. The developer can choose which kind of result they
|
||||
wish to be hydrated. Default result-types include:
|
||||
|
||||
- SQL to Entities
|
||||
- SQL to structured Arrays
|
||||
- SQL to simple scalar result arrays
|
||||
- SQL to a single result variable
|
||||
|
||||
Hydration to entities and arrays is one of most complex parts of Doctrine
|
||||
Hydration to entities and arrays is one of the most complex parts of Doctrine
|
||||
algorithm-wise. It can build results with for example:
|
||||
|
||||
- Single table selects
|
||||
@@ -198,3 +202,4 @@ ClassMetadataFactory
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
tbr
|
||||
|
||||
|
||||
@@ -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,6 +262,7 @@ 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
|
||||
@@ -270,8 +271,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
|
||||
@@ -290,12 +291,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);
|
||||
@@ -303,30 +304,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);
|
||||
}
|
||||
@@ -350,6 +351,7 @@ 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
|
||||
@@ -371,7 +373,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
|
||||
@@ -385,12 +387,13 @@ 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
|
||||
@@ -404,10 +407,10 @@ There are two approaches to handle this problem in your code:
|
||||
Transitive persistence / Cascade Operations
|
||||
-------------------------------------------
|
||||
|
||||
Doctrine 2 provides a mechanism for transitive persistence through cascading of certain operations.
|
||||
Doctrine ORM provides a mechanism for transitive persistence through cascading of certain operations.
|
||||
Each association to another entity or a collection of
|
||||
entities can be configured to automatically cascade the following operations to the associated entities:
|
||||
``persist``, ``remove``, ``refresh`` or ``all``.
|
||||
``persist``, ``remove``, ``merge``, ``detach``, ``refresh`` or ``all``.
|
||||
|
||||
The main use case for ``cascade: persist`` is to avoid "exposing" associated entities to your PHP application.
|
||||
Continuing with the User-Comment example of this chapter, this is how the creation of a new user and a new
|
||||
@@ -419,7 +422,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();
|
||||
@@ -460,14 +463,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:
|
||||
@@ -477,7 +480,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();
|
||||
|
||||
@@ -526,6 +529,7 @@ 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
|
||||
@@ -606,10 +610,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:
|
||||
@@ -712,9 +716,11 @@ methods:
|
||||
* ``in($field, array $values)``
|
||||
* ``notIn($field, array $values)``
|
||||
* ``contains($field, $value)``
|
||||
* ``memberOf($value, $field)``
|
||||
* ``startsWith($field, $value)``
|
||||
* ``endsWith($field, $value)``
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
There is a limitation on the compatibility of Criteria comparisons.
|
||||
|
||||
@@ -53,7 +53,7 @@ headline "Hello World" with the ID 1234:
|
||||
echo $article2->getHeadline();
|
||||
|
||||
In this case the Article is accessed from the entity manager twice,
|
||||
but modified in between. Doctrine 2 realizes this and will only
|
||||
but modified in between. Doctrine ORM realizes this and will only
|
||||
ever give you access to one instance of the Article with ID 1234,
|
||||
no matter how often do you retrieve it from the EntityManager and
|
||||
even no matter what kind of Query method you are using (find,
|
||||
@@ -162,28 +162,25 @@ 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 non-identity
|
||||
non-transient object state at instantiation time in order to
|
||||
enable lazy-loading mechanisms:
|
||||
following piece of code. A real proxy class override ALL public
|
||||
methods along the lines of the ``getName()`` method shown below:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class UserProxyHASH extends User implements GhostObjectInterface
|
||||
class UserProxy extends User implements Proxy
|
||||
{
|
||||
// ... generated code
|
||||
|
||||
public static function staticProxyConstructor($initializer)
|
||||
private function _load()
|
||||
{
|
||||
// ... generated code
|
||||
// lazy loading code
|
||||
}
|
||||
|
||||
private function callInitializerHASH($methodName, array $parameters)
|
||||
public function getName()
|
||||
{
|
||||
// ... generated code
|
||||
$this->_load();
|
||||
return parent::getName();
|
||||
}
|
||||
|
||||
// ... generated code
|
||||
// .. other public methods of User
|
||||
}
|
||||
|
||||
.. warning::
|
||||
@@ -193,6 +190,7 @@ enable lazy-loading mechanisms:
|
||||
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
|
||||
-------------------
|
||||
|
||||
@@ -215,6 +213,7 @@ 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
|
||||
@@ -235,9 +234,11 @@ 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
|
||||
@@ -249,6 +250,12 @@ as follows:
|
||||
- If X is a detached entity, an exception will be thrown on
|
||||
flush.
|
||||
|
||||
.. caution::
|
||||
|
||||
Do not pass detached entities to the persist operation. The persist operation always
|
||||
considers entities that are not yet known to the ``EntityManager`` as new entities
|
||||
(refer to the ``STATE_NEW`` constant inside the ``UnitOfWork``).
|
||||
|
||||
Removing entities
|
||||
-----------------
|
||||
|
||||
@@ -269,6 +276,7 @@ which means that its persistent state will be deleted once
|
||||
the section on :ref:`Database and UnitOfWork Out-Of-Sync <workingobjects_database_uow_outofsync>`
|
||||
for more information.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -280,6 +288,7 @@ 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
|
||||
@@ -308,10 +317,11 @@ 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 it is a Single association it will
|
||||
|
||||
1. If an association is marked as ``CASCADE=REMOVE`` Doctrine ORM
|
||||
will fetch this association. If its a Single association it will
|
||||
pass this entity to
|
||||
´EntityManager#remove()``. If the association is a collection, Doctrine will loop over all its elements and pass them to ``EntityManager#remove()\`.
|
||||
``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,42 +336,143 @@ in multiple ways with very different performance impacts.
|
||||
because Doctrine will fetch and remove all associated entities
|
||||
explicitly nevertheless.
|
||||
|
||||
.. note::
|
||||
|
||||
Calling ``remove`` on an entity will remove the object from the identiy
|
||||
map and therefore detach it. Querying the same entity again, for example
|
||||
via a lazy loaded relation, will return a new object.
|
||||
|
||||
|
||||
Detaching entities
|
||||
------------------
|
||||
|
||||
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
|
||||
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
|
||||
detached.
|
||||
|
||||
Doctrine will not hold on to any references to detached entities.
|
||||
Doctrine will not hold on to any references to a detached entity.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em->clear();
|
||||
$em->detach($entity);
|
||||
|
||||
The semantics of the detach operation, applied to an entity X are
|
||||
as follows:
|
||||
|
||||
- If X is a managed entity, the ``clear`` operation causes it to
|
||||
become detached. Entities which previously referenced X
|
||||
|
||||
- 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`"). 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, it will become detached, and therefore
|
||||
no longer scheduled to be removed. Entities which previously
|
||||
referenced X will continue to reference X.
|
||||
- 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`"). Entities which previously referenced X
|
||||
will continue to reference X.
|
||||
|
||||
There are several situations in which an entity is detached
|
||||
automatically:
|
||||
automatically without invoking the ``detach`` method:
|
||||
|
||||
|
||||
- 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).
|
||||
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`").
|
||||
- For all entities Y referenced by relationships from X having the
|
||||
cascade element value MERGE or ALL, Y is merged recursively as Y'.
|
||||
For all such Y referenced by X, X' is set to reference Y'. (Note
|
||||
that if X is managed then X is the same object as X'.)
|
||||
- If X is an entity merged to X', with a reference to another
|
||||
entity Y, where cascade=MERGE or cascade=ALL is not specified, then
|
||||
navigation of the same association from X' yields a reference to a
|
||||
managed object Y' with the same persistent identity as Y.
|
||||
|
||||
The ``merge`` operation will throw an ``OptimisticLockException``
|
||||
if the entity being merged uses optimistic locking through a
|
||||
version field and the versions of the entity being merged and the
|
||||
managed copy don't match. This usually means that the entity has
|
||||
been modified while being detached.
|
||||
|
||||
The ``merge`` operation is usually not as frequently needed and
|
||||
used as ``persist`` and ``remove``. The most common scenario for
|
||||
the ``merge`` operation is to reattach entities to an EntityManager
|
||||
that come from some cache (and are therefore detached) and you want
|
||||
to modify and persist such an entity.
|
||||
|
||||
.. warning::
|
||||
|
||||
If you need to perform multiple merges of entities that share certain subparts
|
||||
of their object-graphs and cascade merge, then you have to call ``EntityManager#clear()`` between the
|
||||
successive calls to ``EntityManager#merge()``. Otherwise you might end up with
|
||||
multiple copies of the "same" object in the database, however with different ids.
|
||||
|
||||
.. note::
|
||||
|
||||
If you load some detached entities from a cache and you do
|
||||
not need to persist or delete them or otherwise make use of them
|
||||
without the need for persistence services there is no need to use
|
||||
``merge``. I.e. you can simply pass detached objects from a cache
|
||||
directly to the view.
|
||||
|
||||
|
||||
Synchronization with the Database
|
||||
---------------------------------
|
||||
@@ -404,6 +515,7 @@ 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.
|
||||
@@ -412,12 +524,14 @@ 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
|
||||
@@ -449,6 +563,7 @@ 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
|
||||
|
||||
@@ -468,13 +583,14 @@ during development.
|
||||
.. note::
|
||||
|
||||
Do not invoke ``flush`` after every change to an entity
|
||||
or every single invocation of persist/remove/refresh/... This is an
|
||||
or every single invocation of persist/remove/merge/... 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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -494,6 +610,7 @@ instance the EntityManager is currently using.
|
||||
marked as INTERNAL by not using them and carefully read the API
|
||||
documentation.
|
||||
|
||||
|
||||
Entity State
|
||||
~~~~~~~~~~~~
|
||||
|
||||
@@ -529,14 +646,14 @@ 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 it has no persistent state and identity
|
||||
An entity is in NEW state if has no persistent state and identity
|
||||
and is not associated with an ``EntityManager`` (for example those
|
||||
just created via the "new" operator).
|
||||
|
||||
Querying
|
||||
--------
|
||||
|
||||
Doctrine 2 provides the following ways, in increasing level of
|
||||
Doctrine ORM provides the following ways, in increasing level of
|
||||
power and flexibility, to query for persistent objects. You should
|
||||
always start with the simplest one that suits your needs.
|
||||
|
||||
@@ -640,10 +757,8 @@ Additionally, you can just count the result of the provided conditions when you
|
||||
By Criteria
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 2.3
|
||||
|
||||
The Repository implements the ``Doctrine\Common\Collections\Selectable``
|
||||
interface. It means you can build ``Doctrine\Common\Collections\Criteria``
|
||||
The Repository implement the ``Doctrine\Common\Collections\Selectable``
|
||||
interface. That 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>`
|
||||
@@ -653,7 +768,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 are thus
|
||||
be loaded together with the entity being queried and is thus
|
||||
immediately available to your application.
|
||||
|
||||
By Lazy Loading
|
||||
@@ -696,7 +811,9 @@ DQL and its syntax as well as the Doctrine class can be found in
|
||||
:doc:`the dedicated chapter <dql-doctrine-query-language>`.
|
||||
For programmatically building up queries based on conditions that
|
||||
are only known at runtime, Doctrine provides the special
|
||||
``Doctrine\ORM\QueryBuilder`` class. More information on
|
||||
``Doctrine\ORM\QueryBuilder`` class. While this a powerful tool,
|
||||
it also brings more complexity to your code compared to plain DQL,
|
||||
so you should only use it when you need it. More information on
|
||||
constructing queries with a QueryBuilder can be found
|
||||
:doc:`in Query Builder chapter <query-builder>`.
|
||||
|
||||
@@ -717,7 +834,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 or XML metadata. In large
|
||||
Repository in the Annotation, XML or YAML 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.
|
||||
@@ -742,7 +859,7 @@ in a central location.
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -756,3 +873,4 @@ You can access your repository now by calling:
|
||||
|
||||
$admins = $em->getRepository('MyDomain\Model\User')->getAllAdminUsers();
|
||||
|
||||
|
||||
|
||||
@@ -8,9 +8,7 @@ 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
|
||||
`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
|
||||
The most convenient way to work with
|
||||
XML mapping files is to use an IDE/editor that can provide
|
||||
code-completion based on such an XML Schema document. The following
|
||||
is an outline of a XML mapping document with the proper xmlns/xsi
|
||||
@@ -31,6 +29,7 @@ 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
|
||||
@@ -193,10 +192,12 @@ 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
|
||||
@@ -205,10 +206,10 @@ Optional attributes:
|
||||
- **inheritance-type** - The type of inheritance, defaults to none. A
|
||||
more detailed description follows in the
|
||||
*Defining Inheritance Mappings* section.
|
||||
- **read-only** - (>= 2.1) Specifies that this entity is marked as read only and not
|
||||
- **read-only** - Specifies that this entity is marked as read only and not
|
||||
considered for change-tracking. Entities of this type can be persisted
|
||||
and removed though.
|
||||
- **schema** - (>= 2.5) The schema the table lies in, for platforms that support schemas
|
||||
- **schema** - The schema the table lies in, for platforms that support schemas
|
||||
|
||||
Defining Fields
|
||||
~~~~~~~~~~~~~~~
|
||||
@@ -238,11 +239,13 @@ 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
|
||||
@@ -288,7 +291,7 @@ Defining Identity and Generator Strategies
|
||||
|
||||
An entity has to have at least one ``<id />`` element. For
|
||||
composite keys you can specify more than one id-element, however
|
||||
surrogate keys are recommended for use with Doctrine 2. The Id
|
||||
surrogate keys are recommended for use with Doctrine ORM. The Id
|
||||
field allows to define properties of the identifier and allows a
|
||||
subset of the ``<field />`` element attributes:
|
||||
|
||||
@@ -300,6 +303,7 @@ 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
|
||||
@@ -307,6 +311,7 @@ Required attributes:
|
||||
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- column - Name of the column in the database, defaults to the
|
||||
field name.
|
||||
|
||||
@@ -332,6 +337,7 @@ 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
|
||||
@@ -354,10 +360,12 @@ 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
|
||||
@@ -370,6 +378,7 @@ Optional attributes for ``<sequence-generator />``:
|
||||
element, if Doctrine chooses the sequence strategy for a
|
||||
platform.
|
||||
|
||||
|
||||
Defining a Mapped Superclass
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -389,6 +398,7 @@ 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
|
||||
@@ -428,6 +438,7 @@ 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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -460,6 +471,7 @@ 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
|
||||
@@ -477,6 +489,7 @@ 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
|
||||
@@ -484,6 +497,7 @@ 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.
|
||||
@@ -526,6 +540,7 @@ 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
|
||||
@@ -533,6 +548,7 @@ 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.
|
||||
@@ -575,6 +591,7 @@ 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
|
||||
@@ -584,6 +601,7 @@ 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.
|
||||
|
||||
@@ -602,6 +620,7 @@ 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
|
||||
@@ -609,6 +628,7 @@ 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.
|
||||
@@ -664,7 +684,9 @@ tags.
|
||||
Besides ``<cascade-all />`` the following operations can be
|
||||
specified by their respective tags:
|
||||
|
||||
|
||||
- ``<cascade-persist />``
|
||||
- ``<cascade-merge />``
|
||||
- ``<cascade-remove />``
|
||||
- ``<cascade-refresh />``
|
||||
|
||||
@@ -677,12 +699,14 @@ 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.
|
||||
|
||||
158
docs/en/reference/yaml-mapping.rst
Normal file
158
docs/en/reference/yaml-mapping.rst
Normal file
@@ -0,0 +1,158 @@
|
||||
YAML Mapping
|
||||
============
|
||||
|
||||
.. note::
|
||||
The YAML driver is deprecated and will be removed in version 3.0.
|
||||
It is strongly recommended to switch to one of the other mappings.
|
||||
|
||||
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 ]
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
reference/change-tracking-policies
|
||||
reference/partial-objects
|
||||
reference/xml-mapping
|
||||
reference/yaml-mapping
|
||||
reference/annotations-reference
|
||||
reference/php-mapping
|
||||
reference/caching
|
||||
|
||||
86
docs/en/toc.rst
Normal file
86
docs/en/toc.rst
Normal file
@@ -0,0 +1,86 @@
|
||||
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/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,11 +1,9 @@
|
||||
Composite and Foreign Keys as Primary Key
|
||||
=========================================
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
Doctrine 2 supports composite primary keys natively. Composite keys are a very powerful relational database concept
|
||||
and we took good care to make sure Doctrine 2 supports as many of the composite primary key use-cases.
|
||||
For Doctrine 2.0 composite keys of primitive data-types are supported, for Doctrine 2.1 even foreign keys as
|
||||
Doctrine ORM supports composite primary keys natively. Composite keys are a very powerful relational database concept
|
||||
and we took good care to make sure Doctrine ORM supports as many of the composite primary key use-cases.
|
||||
For Doctrine ORM composite keys of primitive data-types are supported, even foreign keys as
|
||||
primary keys are supported.
|
||||
|
||||
This tutorial shows how the semantics of composite primary keys work and how they map to the database.
|
||||
@@ -19,7 +17,7 @@ the ID fields have to have their values set before you call ``EntityManager#pers
|
||||
Primitive Types only
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Even in version 2.0 you can have composite keys as long as they only consist of the primitive types
|
||||
You can have composite keys as long as they only consist of the primitive types
|
||||
``integer`` and ``string``. Suppose you want to create a database of cars and use the model-name
|
||||
and year of production as primary keys:
|
||||
|
||||
@@ -28,19 +26,16 @@ and year of production as primary keys:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
namespace VehicleCatalogue\Model;
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Entity
|
||||
*/
|
||||
class Car
|
||||
{
|
||||
/** @ORM\Id @ORM\Column(type="string") */
|
||||
/** @Id @Column(type="string") */
|
||||
private $name;
|
||||
/** @ORM\Id @ORM\Column(type="integer") */
|
||||
/** @Id @Column(type="integer") */
|
||||
private $year;
|
||||
|
||||
public function __construct($name, $year)
|
||||
@@ -74,6 +69,16 @@ 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
|
||||
@@ -113,10 +118,6 @@ and to ``year`` to the related entities.
|
||||
Identity through foreign Entities
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. note::
|
||||
|
||||
Identity through foreign entities is only supported with Doctrine 2.1
|
||||
|
||||
There are tons of use-cases where the identity of an Entity should be determined by the entity
|
||||
of one or many parent entities.
|
||||
|
||||
@@ -130,8 +131,9 @@ 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 ``@ORM\Id`` annotation onto every association.
|
||||
- Plug an ``@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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -146,21 +148,19 @@ 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;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
|
||||
/** @ORM\Column(type="string") */
|
||||
/** @Column(type="string") */
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="ArticleAttribute", mappedBy="article", cascade={"ALL"}, indexBy="attribute")
|
||||
* @OneToMany(targetEntity="ArticleAttribute", mappedBy="article", cascade={"ALL"}, indexBy="attribute")
|
||||
*/
|
||||
private $attributes;
|
||||
|
||||
@@ -171,17 +171,17 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Entity
|
||||
*/
|
||||
class ArticleAttribute
|
||||
{
|
||||
/** @ORM\Id @ORM\ManyToOne(targetEntity="Article", inversedBy="attributes") */
|
||||
/** @Id @ManyToOne(targetEntity="Article", inversedBy="attributes") */
|
||||
private $article;
|
||||
|
||||
/** @ORM\Id @ORM\Column(type="string") */
|
||||
/** @Id @Column(type="string") */
|
||||
private $attribute;
|
||||
|
||||
/** @ORM\Column(type="string") */
|
||||
/** @Column(type="string") */
|
||||
private $value;
|
||||
|
||||
public function __construct($name, $value, $article)
|
||||
@@ -202,7 +202,7 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
|
||||
<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" />
|
||||
@@ -210,6 +210,24 @@ 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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -222,27 +240,44 @@ One good example for this is a user-address relationship:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Entity
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Entity
|
||||
*/
|
||||
class Address
|
||||
{
|
||||
/** @ORM\Id @ORM\OneToOne(targetEntity="User") */
|
||||
/** @Id @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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -254,27 +289,23 @@ of products purchased and maybe even the current price.
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Annotation as ORM;
|
||||
|
||||
/** @ORM\Entity */
|
||||
/** @Entity */
|
||||
class Order
|
||||
{
|
||||
/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
|
||||
/** @ORM\ManyToOne(targetEntity="Customer") */
|
||||
/** @ManyToOne(targetEntity="Customer") */
|
||||
private $customer;
|
||||
|
||||
/** @ORM\OneToMany(targetEntity="OrderItem", mappedBy="order") */
|
||||
/** @OneToMany(targetEntity="OrderItem", mappedBy="order") */
|
||||
private $items;
|
||||
|
||||
/** @ORM\Column(type="boolean") */
|
||||
private $paid = false;
|
||||
|
||||
/** @ORM\Column(type="boolean") */
|
||||
/** @Column(type="boolean") */
|
||||
private $payed = false;
|
||||
/** @Column(type="boolean") */
|
||||
private $shipped = false;
|
||||
|
||||
/** @ORM\Column(type="datetime") */
|
||||
/** @Column(type="datetime") */
|
||||
private $created;
|
||||
|
||||
public function __construct(Customer $customer)
|
||||
@@ -285,16 +316,16 @@ of products purchased and maybe even the current price.
|
||||
}
|
||||
}
|
||||
|
||||
/** @ORM\Entity */
|
||||
/** @Entity */
|
||||
class Product
|
||||
{
|
||||
/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
|
||||
/** @ORM\Column(type="string") */
|
||||
/** @Column(type="string") */
|
||||
private $name;
|
||||
|
||||
/** @ORM\Column(type="decimal") */
|
||||
/** @Column(type="decimal") */
|
||||
private $currentPrice;
|
||||
|
||||
public function getCurrentPrice()
|
||||
@@ -303,19 +334,19 @@ of products purchased and maybe even the current price.
|
||||
}
|
||||
}
|
||||
|
||||
/** @ORM\Entity */
|
||||
/** @Entity */
|
||||
class OrderItem
|
||||
{
|
||||
/** @ORM\Id @ORM\ManyToOne(targetEntity="Order") */
|
||||
/** @Id @ManyToOne(targetEntity="Order") */
|
||||
private $order;
|
||||
|
||||
/** @ORM\Id @ORM\ManyToOne(targetEntity="Product") */
|
||||
/** @Id @ManyToOne(targetEntity="Product") */
|
||||
private $product;
|
||||
|
||||
/** @ORM\Column(type="integer") */
|
||||
/** @Column(type="integer") */
|
||||
private $amount = 1;
|
||||
|
||||
/** @ORM\Column(type="decimal") */
|
||||
/** @Column(type="decimal") */
|
||||
private $offeredPrice;
|
||||
|
||||
public function __construct(Order $order, Product $product, $amount = 1)
|
||||
@@ -326,6 +357,7 @@ of products purchased and maybe even the current price.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Performance Considerations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user