mirror of
https://github.com/doctrine/orm.git
synced 2026-04-25 23:48:05 +02:00
Compare commits
532 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 78ed4cddeb | |||
| 8ceb0b0d04 | |||
| e88cd591f0 | |||
| 88a8f75f62 | |||
| 9fe8ce4bf7 | |||
| a46ff16339 | |||
| 94e60e4318 | |||
| f59cd4019a | |||
| 81558a8b2a | |||
| e431ee113d | |||
| de4ec208fd | |||
| df014a74c9 | |||
| 2f46d95028 | |||
| 3fda5629f6 | |||
| 6b273234d6 | |||
| 580a95ce3f | |||
| 8b91e248eb | |||
| d2266c7d0c | |||
| eb0485869a | |||
| 8372d600c6 | |||
| dde1d71b34 | |||
| 0d03255061 | |||
| 75a18090d9 | |||
| 77ffd2ab68 | |||
| 401cc06d71 | |||
| 5029b193ee | |||
| b87781f65e | |||
| c0ff86ef69 | |||
| 79bff0f072 | |||
| 77b579287c | |||
| 27c33cf88d | |||
| 6068b61a0d | |||
| 00024f7d88 | |||
| 44c92377a0 | |||
| 255612a1ff | |||
| 331f8b52cb | |||
| b2faba62b7 | |||
| da426a0036 | |||
| 1891a76f13 | |||
| 6bb30e1cee | |||
| 6336eb5d5d | |||
| b8f404abcf | |||
| 3e8490a792 | |||
| 14bb034fe4 | |||
| afc0aab61a | |||
| 3e784c1d9a | |||
| e1d7a13a5e | |||
| 4262eb495b | |||
| ca49533d3e | |||
| 8275471110 | |||
| afd683bdb8 | |||
| fe6e5a67f8 | |||
| b20a66dcdd | |||
| 0db02df3aa | |||
| dc46af27ed | |||
| 05ab22710b | |||
| d3b47d2cbb | |||
| 026f5bfe1b | |||
| 6af7de38e1 | |||
| 0b0f2f4d86 | |||
| 0780b5a6b1 | |||
| 71b1831351 | |||
| 63d9a898ec | |||
| 0bd839a720 | |||
| b65004fc26 | |||
| d2418ab074 | |||
| 39a05e31c9 | |||
| 7376adede8 | |||
| ab156a551c | |||
| 59f7f6793d | |||
| 0fc9208d71 | |||
| fd9e572424 | |||
| 76490f2c99 | |||
| 2148940290 | |||
| d3538095fd | |||
| 0c1bf14729 | |||
| 3b8c23c51d | |||
| 60d4ea694a | |||
| e923bbc932 | |||
| 8cbd34c666 | |||
| f8bbdc40b0 | |||
| 8bdefef6d1 | |||
| 0f8730a6e5 | |||
| 62477b5d42 | |||
| 12116aa3c2 | |||
| 0aeddd0592 | |||
| 2491c4b20d | |||
| 08d6167243 | |||
| 765a9c43e6 | |||
| d4e9276e79 | |||
| cee74faa97 | |||
| 9ae2181185 | |||
| 3e25efd72b | |||
| 47496ed882 | |||
| 492745d710 | |||
| 67419cf951 | |||
| 1237f5c909 | |||
| 609e616f2d | |||
| 4016d6ba4b | |||
| dcdd46251e | |||
| 3d98b43561 | |||
| 9f3f70944a | |||
| 05e07c0ae0 | |||
| fea42ab984 | |||
| fcd9dede2e | |||
| 7c347b85c1 | |||
| 458b040d93 | |||
| 262c396639 | |||
| 396636a2c2 | |||
| 78dd074266 | |||
| ff22a00fcf | |||
| 02e8ff9663 | |||
| 01fd55e9ea | |||
| 2e75a7f1c1 | |||
| 152b0e3d65 | |||
| 9d11fdd3da | |||
| 87f1ba74e0 | |||
| f357a33d23 | |||
| ee70178314 | |||
| ab148d3d9d | |||
| 3924c38fab | |||
| 9814078a2c | |||
| 6de5684fd9 | |||
| c142503a52 | |||
| 01c178b297 | |||
| ffa50a777f | |||
| 649048f745 | |||
| 15537bc218 | |||
| bc95c7c08d | |||
| 6982c8ab9d | |||
| e767d2dbeb | |||
| 3df11d518c | |||
| c1becd54e6 | |||
| e4d7df29c2 | |||
| d65b6a6a10 | |||
| 3d8afbb7b2 | |||
| 736f6bc97f | |||
| 608705427e | |||
| f0562f4120 | |||
| 9f19310f27 | |||
| e38278bfca | |||
| 62f2cff218 | |||
| cdd774906b | |||
| 96776e091d | |||
| f7470d8a3f | |||
| 2c41cc7f1c | |||
| f18de9d569 | |||
| 37f76a8381 | |||
| a6c1e63a60 | |||
| b62292256a | |||
| b138395194 | |||
| 6881cdff4c | |||
| dede2d775a | |||
| c502190712 | |||
| 5bff0919a7 | |||
| 9ef0f5301b | |||
| 4989ca6f15 | |||
| 32d1e97ce7 | |||
| ca8147b148 | |||
| c8ebea77f0 | |||
| 23f22860f1 | |||
| b24586b1b5 | |||
| 0ed46303cc | |||
| 6e351b699c | |||
| 9e5442a892 | |||
| 7d8e51c934 | |||
| 2f8f1cfcb8 | |||
| fe5ee705db | |||
| 01774c035c | |||
| 0511a9f790 | |||
| 0e3d5e8c82 | |||
| 72ffb3bfbf | |||
| 2e9a1adc23 | |||
| 6f83166266 | |||
| ffd3f50ad7 | |||
| 483b45d449 | |||
| 1220edf953 | |||
| 7e4693d629 | |||
| 59938cae57 | |||
| 298dc9bb6a | |||
| da67f323e0 | |||
| 63635cad0e | |||
| a0d401b688 | |||
| 6acbadfbbe | |||
| c64dcb4d38 | |||
| 3304290b21 | |||
| 1865717721 | |||
| 828b06e20f | |||
| c2b844d2e3 | |||
| dd4e8fe78f | |||
| 4f36f0129a | |||
| b45d5329f8 | |||
| c1047b30e3 | |||
| f71aa73ef1 | |||
| aa62efa30a | |||
| f71956f001 | |||
| 7cc210424c | |||
| 4fd9e94819 | |||
| 587caf88a7 | |||
| 1e33b775d3 | |||
| 96f9b29573 | |||
| c6207b1793 | |||
| 8c92903430 | |||
| 8616a98023 | |||
| 9a55cf4f30 | |||
| 9d680a6de4 | |||
| 7602a5341c | |||
| 7a59281157 | |||
| 214b1ad739 | |||
| 5def068fe9 | |||
| 693acbf812 | |||
| 28d9472a38 | |||
| cf11f1e453 | |||
| c6955ec056 | |||
| c1ce2bb687 | |||
| 6863272943 | |||
| eae6577ce2 | |||
| 5f6896a2f9 | |||
| e3106d439d | |||
| 930a790a5a | |||
| 40aa44914f | |||
| 7c8a528914 | |||
| 3b7de17f2e | |||
| 40fedadecf | |||
| 0d97a44f28 | |||
| c472a1535d | |||
| 8afaa63d73 | |||
| 2ad720b304 | |||
| 64cd5cad20 | |||
| f1a8ee175c | |||
| d3e1440175 | |||
| a9bd00a70b | |||
| a939dc2e0d | |||
| 758de75b45 | |||
| 1ff63be779 | |||
| f8186b1203 | |||
| 2d5d129ee1 | |||
| 28dd32790f | |||
| 4f3a5c5514 | |||
| 6641989e35 | |||
| 048e308241 | |||
| 8ca72a4e96 | |||
| daf74b74b5 | |||
| 4274dac8a2 | |||
| ac19b21a71 | |||
| c6db9feade | |||
| c1af765960 | |||
| 86d847edb8 | |||
| 5f3551852f | |||
| 8144cad07c | |||
| 70fd68cf7f | |||
| 437259556c | |||
| e7e2fef56c | |||
| 3e34b8e86a | |||
| 7d950aba62 | |||
| 8ad560c34d | |||
| ccfb620f31 | |||
| 94c4d48ae5 | |||
| cb8a76ba3a | |||
| 2ca63df90c | |||
| 0d4413c248 | |||
| a7a14cffaf | |||
| cdc52b2a9e | |||
| c5e2f3fb23 | |||
| 48434f4c53 | |||
| 200a505f36 | |||
| 17d7814fdc | |||
| 8fe1200edf | |||
| f7d4e379bc | |||
| c164ae434f | |||
| ceb04bf3f6 | |||
| 21e9fcbfbb | |||
| ed9ba16ff4 | |||
| db456976ed | |||
| 2d9091778f | |||
| 17059e5265 | |||
| 680a9ef632 | |||
| 9d5f112c7e | |||
| b7423c96cf | |||
| c91c3a3b1d | |||
| 42a693c0d3 | |||
| fc1bf3b815 | |||
| b6b342cada | |||
| 28735afae3 | |||
| 0f229fbb4b | |||
| bea4814d55 | |||
| 85c13edc80 | |||
| 03cc07e4d7 | |||
| 397358c308 | |||
| ac37a87a3d | |||
| 238c15952c | |||
| 613f52db5a | |||
| cf5503b0d8 | |||
| 78fee8ea5c | |||
| 0c73cf93fa | |||
| fdc88ba236 | |||
| c49bf58682 | |||
| ae5e9c8c6c | |||
| c03ed691d4 | |||
| ce844d94a0 | |||
| 1a2826d147 | |||
| 05760f9454 | |||
| 26af013842 | |||
| c1f7a60c5b | |||
| d1d13d5956 | |||
| 4f8dde2d1e | |||
| e3c320c705 | |||
| 5a541b8b3a | |||
| 9fb9cc46e4 | |||
| c322c71cd4 | |||
| 3c733a2fee | |||
| 5984ad586a | |||
| ee2c3a506b | |||
| 781ed30926 | |||
| 04694a9f7b | |||
| 257c5094c4 | |||
| b521e89b20 | |||
| 831232e05e | |||
| 66e0e92816 | |||
| a6a94cdefd | |||
| a774cedb24 | |||
| 6b8207bb11 | |||
| 3d3b5b51cd | |||
| 5b2060e25f | |||
| 760616291b | |||
| 64444dcfd5 | |||
| c5e5742744 | |||
| 8584da8fdc | |||
| eb2cd5375c | |||
| de7140e105 | |||
| 07bb0def60 | |||
| 39e35fc06c | |||
| 7f061c3870 | |||
| 74495711fb | |||
| 97a7cb8d2f | |||
| 85d66de9df | |||
| 1b98be31ce | |||
| 61f2752a80 | |||
| f3371e1773 | |||
| 6476894dc4 | |||
| 8b3a6dfbbb | |||
| 5634c3ccee | |||
| a93d7d22d8 | |||
| 677e0a4ed3 | |||
| 8cf161d8bc | |||
| e0052390e1 | |||
| 8c6419e0e0 | |||
| 6f5ce1aca2 | |||
| 98e7a53b42 | |||
| 3aaaf37dfb | |||
| 154a4652ee | |||
| ae7489ff19 | |||
| 0f32569a7a | |||
| d99f74c704 | |||
| a2990e1a0a | |||
| d355c4a990 | |||
| 88c395c488 | |||
| 256d6cb0d7 | |||
| 62ca8424d8 | |||
| 3f2209a571 | |||
| 1ee01f4473 | |||
| 8a9ed138a8 | |||
| e714b1a2fc | |||
| ec0bf05853 | |||
| dc58aa3ea1 | |||
| 23b74e4f8b | |||
| d2b699e6f5 | |||
| 0338d69324 | |||
| a1fdc6eb6e | |||
| d583460d63 | |||
| 2c01dac173 | |||
| 137ecb491a | |||
| 79d4cfdce8 | |||
| f38ee09082 | |||
| 6ab858a5c5 | |||
| 3dca27ce0d | |||
| 224ff9710d | |||
| e19704e1f8 | |||
| 41ea59ac66 | |||
| e605e6d569 | |||
| 9437675d3b | |||
| 5301b99533 | |||
| 63409d638c | |||
| d68c1dcd6d | |||
| 00c7b70211 | |||
| 68c71521e7 | |||
| a2ea72c763 | |||
| 1492b01093 | |||
| 9b9160b206 | |||
| 6deec3655b | |||
| 7f40422d21 | |||
| e67fa5388b | |||
| 80053336c9 | |||
| dddcc507ef | |||
| b41d9da88d | |||
| c04bfb78b7 | |||
| 8a5dfc86d4 | |||
| 79e103c07e | |||
| 68b797161e | |||
| e17b31ee68 | |||
| 5afadf163a | |||
| ac87e6e7dc | |||
| edfaa37228 | |||
| ea056e98ba | |||
| 016d9d2074 | |||
| bab5771e98 | |||
| ee919d6231 | |||
| 04c390693a | |||
| 49293c4d48 | |||
| 8d9e2e7d4e | |||
| ef607f26c2 | |||
| ed543a205c | |||
| de1c28bb16 | |||
| a96697e436 | |||
| 60ff966d54 | |||
| b1ec1da8e6 | |||
| 6cfe0883f0 | |||
| 33684253c3 | |||
| 76852cfef3 | |||
| 3bd89caf36 | |||
| eb2e7d959c | |||
| a4b20356f4 | |||
| 2550b2d1de | |||
| e94e1ab126 | |||
| cd56fe54bb | |||
| 982ef7ac56 | |||
| 19e1a64a91 | |||
| 082e776e91 | |||
| bbde41f712 | |||
| 8c0994f35f | |||
| 3d390bc053 | |||
| 16f1be7f10 | |||
| bc904f6a0c | |||
| cddf985a7d | |||
| 2e9ca50007 | |||
| 275975a444 | |||
| 2088a469cb | |||
| 3f13f119e2 | |||
| d444a79bb0 | |||
| 01dae555bf | |||
| d9049d8ef5 | |||
| 1ad46527b6 | |||
| 1334162a56 | |||
| 68ec3ebaa3 | |||
| 4f4ed2f242 | |||
| a4b794db4f | |||
| 79cc70a62f | |||
| 4e6b5a1b0b | |||
| 21b144fff9 | |||
| 658940de38 | |||
| ad487370f5 | |||
| 259f83b549 | |||
| 4a24860dcf | |||
| 116cdf8661 | |||
| 7eeea7d66c | |||
| f96e812438 | |||
| d1b4aa013a | |||
| dda313871d | |||
| f7584df83c | |||
| 28b2591694 | |||
| 528b8837e1 | |||
| 615566507c | |||
| 9b1cb60cbc | |||
| 9bf407f336 | |||
| 4fb044d5f6 | |||
| 1d0e365401 | |||
| c12daf5206 | |||
| 2a953c5e2b | |||
| abc6a40ccb | |||
| 73e68f3c7d | |||
| 73777d0bd4 | |||
| e89b58a13f | |||
| 2b94ec18b9 | |||
| f7030d1844 | |||
| 8d7b4d8d17 | |||
| f53d35b74a | |||
| 2a662149f4 | |||
| 350597beb0 | |||
| 8dc17b2851 | |||
| ee9dd474de | |||
| 0a4a11b6e4 | |||
| a7406cc26d | |||
| f11adcb622 | |||
| 1e4b88d456 | |||
| eb5dd3f34f | |||
| 25066f801c | |||
| a0f5b4c9a3 | |||
| 8b5148a7a8 | |||
| 22198f7c19 | |||
| 9cf5593759 | |||
| b5c0e0154f | |||
| dbf26dbf90 | |||
| 9b09cd03c0 | |||
| 2c489fb8b3 | |||
| 42a36e19a5 | |||
| f46d8a1742 | |||
| 1eb67b7ca1 | |||
| 00b29a13f2 | |||
| a22e0fda8e | |||
| b58946a11f | |||
| d973867dc0 | |||
| ac5a17da2e | |||
| f4bbf8e1d0 | |||
| 95a0000446 | |||
| b2a09b9c4e | |||
| 3695d5793d | |||
| 9674168572 | |||
| 80aec02fd7 | |||
| 637cd6e405 | |||
| 9430d56d78 | |||
| a4e71b8df7 | |||
| f20955901a | |||
| 54c36d2a55 | |||
| ea7cc64911 | |||
| 22947da46b | |||
| 2928b9331a | |||
| fdd84a2290 | |||
| 8c1ad6f0c5 | |||
| a5cb7c26c5 | |||
| f8b5ef03d5 | |||
| e80f03c5d8 | |||
| c1d61802cd | |||
| ca8631c172 | |||
| 518d16b9cb | |||
| 7679fc0f4f | |||
| c5ef72d060 | |||
| cbab4d6a14 | |||
| 5733eced42 | |||
| 0917109c50 | |||
| 615eb926f4 | |||
| d734ab38fa | |||
| 0446f5b4b5 |
+6
-60
@@ -12,41 +12,17 @@
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "3.5",
|
||||
"branchName": "3.5.x",
|
||||
"slug": "3.5",
|
||||
"name": "3.7",
|
||||
"branchName": "3.7.x",
|
||||
"slug": "3.7",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "3.4",
|
||||
"branchName": "3.4.x",
|
||||
"slug": "3.4",
|
||||
"name": "3.6",
|
||||
"branchName": "3.6.x",
|
||||
"slug": "3.6",
|
||||
"current": true
|
||||
},
|
||||
{
|
||||
"name": "3.3",
|
||||
"branchName": "3.3.x",
|
||||
"slug": "3.3",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "3.2",
|
||||
"branchName": "3.2.x",
|
||||
"slug": "3.2",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "3.1",
|
||||
"branchName": "3.1.x",
|
||||
"slug": "3.1",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "3.0",
|
||||
"branchName": "3.0.x",
|
||||
"slug": "3.0",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.21",
|
||||
"branchName": "2.21.x",
|
||||
@@ -61,63 +37,33 @@
|
||||
},
|
||||
{
|
||||
"name": "2.19",
|
||||
"branchName": "2.19.x",
|
||||
"slug": "2.19",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.18",
|
||||
"branchName": "2.18.x",
|
||||
"slug": "2.18",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.17",
|
||||
"branchName": "2.17.x",
|
||||
"slug": "2.17",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.16",
|
||||
"branchName": "2.16.x",
|
||||
"slug": "2.16",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.15",
|
||||
"branchName": "2.15.x",
|
||||
"slug": "2.15",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.14",
|
||||
"branchName": "2.14.x",
|
||||
"slug": "2.14",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.13",
|
||||
"branchName": "2.13.x",
|
||||
"slug": "2.13",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.12",
|
||||
"branchName": "2.12.x",
|
||||
"slug": "2.12",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.11",
|
||||
"branchName": "2.11.x",
|
||||
"slug": "2.11",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.10",
|
||||
"branchName": "2.10.x",
|
||||
"slug": "2.10",
|
||||
"maintained": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+1
-2
@@ -11,11 +11,10 @@ build.properties.dev export-ignore
|
||||
build.xml export-ignore
|
||||
CONTRIBUTING.md export-ignore
|
||||
phpunit.xml.dist export-ignore
|
||||
run-all.sh export-ignore
|
||||
phpcs.xml.dist export-ignore
|
||||
phpbench.json export-ignore
|
||||
phpstan.neon export-ignore
|
||||
phpstan-baseline.neon export-ignore
|
||||
phpstan-dbal2.neon export-ignore
|
||||
phpstan-dbal3.neon export-ignore
|
||||
phpstan-params.neon export-ignore
|
||||
phpstan-persistence2.neon export-ignore
|
||||
|
||||
@@ -7,3 +7,7 @@ updates:
|
||||
labels:
|
||||
- "CI"
|
||||
target-branch: "2.20.x"
|
||||
groups:
|
||||
doctrine:
|
||||
patterns:
|
||||
- "doctrine/*"
|
||||
|
||||
@@ -24,4 +24,4 @@ on:
|
||||
|
||||
jobs:
|
||||
coding-standards:
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@7.3.0"
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@14.0.0"
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
name: "Composer Lint"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*.x"
|
||||
paths:
|
||||
- ".github/workflows/composer-lint.yml"
|
||||
- "composer.json"
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
paths:
|
||||
- ".github/workflows/composer-lint.yml"
|
||||
- "composer.json"
|
||||
|
||||
jobs:
|
||||
composer-lint:
|
||||
name: "Composer Lint"
|
||||
uses: "doctrine/.github/.github/workflows/composer-lint.yml@14.0.0"
|
||||
@@ -1,4 +1,4 @@
|
||||
name: "Continuous Integration"
|
||||
name: "CI: PHPUnit"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -25,49 +25,58 @@ env:
|
||||
|
||||
jobs:
|
||||
phpunit-smoke-check:
|
||||
name: "PHPUnit with SQLite"
|
||||
name: >
|
||||
SQLite -
|
||||
${{ format('PHP {0} - DBAL {1} - ext. {2} - proxy {3}',
|
||||
matrix.php-version || 'Ø',
|
||||
matrix.dbal-version || 'Ø',
|
||||
matrix.extension || 'Ø',
|
||||
matrix.proxy || 'Ø'
|
||||
) }}
|
||||
runs-on: "ubuntu-22.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.1"
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3.7"
|
||||
extension:
|
||||
- "sqlite3"
|
||||
- "pdo_sqlite"
|
||||
deps:
|
||||
- "highest"
|
||||
native_lazy:
|
||||
- "0"
|
||||
stability:
|
||||
- "stable"
|
||||
include:
|
||||
- php-version: "8.2"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "4@dev"
|
||||
extension: "pdo_sqlite"
|
||||
native_lazy: "0"
|
||||
- php-version: "8.2"
|
||||
stability: "stable"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "4@dev"
|
||||
extension: "sqlite3"
|
||||
native_lazy: "0"
|
||||
- php-version: "8.1"
|
||||
stability: "stable"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "default"
|
||||
deps: "lowest"
|
||||
extension: "pdo_sqlite"
|
||||
native_lazy: "0"
|
||||
stability: "stable"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "default"
|
||||
deps: "highest"
|
||||
extension: "pdo_sqlite"
|
||||
native_lazy: "1"
|
||||
stability: "stable"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "default"
|
||||
deps: "highest"
|
||||
extension: "sqlite3"
|
||||
stability: "dev"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -79,12 +88,20 @@ jobs:
|
||||
coverage: "pcov"
|
||||
ini-values: "zend.assertions=1, apc.enable_cli=1"
|
||||
|
||||
- name: "Allow dev dependencies"
|
||||
run: |
|
||||
composer config minimum-stability dev
|
||||
composer remove --no-update --dev phpbench/phpbench phpdocumentor/guides-cli
|
||||
composer require --no-update symfony/console:^8 symfony/var-exporter:^8 doctrine/dbal:^4.4
|
||||
composer require --dev --no-update symfony/cache:^8
|
||||
if: "${{ matrix.stability == 'dev' }}"
|
||||
|
||||
- name: "Require specific DBAL version"
|
||||
run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update"
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
uses: "ramsey/composer-install@v4"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
dependency-versions: "${{ matrix.deps }}"
|
||||
@@ -93,48 +110,100 @@ jobs:
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage-no-cache.xml"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 0
|
||||
ENABLE_NATIVE_LAZY_OBJECTS: ${{ matrix.native_lazy }}
|
||||
|
||||
- name: "Run PHPUnit with Second Level Cache"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --exclude-group performance,non-cacheable,locking_functional --coverage-clover=coverage-cache.xml"
|
||||
- name: "Run PHPUnit with Second Level Cache and PHPUnit 10"
|
||||
run: |
|
||||
vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml \
|
||||
--exclude-group=performance,non-cacheable,locking_functional \
|
||||
--coverage-clover=coverage-cache.xml
|
||||
if: "${{ matrix.php-version == '8.1' }}"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 1
|
||||
ENABLE_NATIVE_LAZY_OBJECTS: ${{ matrix.native_lazy }}
|
||||
|
||||
- name: "Run PHPUnit with Second Level Cache and PHPUnit 11+"
|
||||
run: |
|
||||
vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml \
|
||||
--exclude-group=performance \
|
||||
--exclude-group=non-cacheable \
|
||||
--exclude-group=locking_functional \
|
||||
--coverage-clover=coverage-cache.xml
|
||||
if: "${{ matrix.php-version != '8.1' }}"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 1
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
uses: "actions/upload-artifact@v7"
|
||||
with:
|
||||
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.deps }}-${{ matrix.native_lazy }}-coverage"
|
||||
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.deps }}-${{ matrix.stability }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
|
||||
|
||||
phpunit-deprecations:
|
||||
name: "PHPUnit (fail on deprecations)"
|
||||
runs-on: "ubuntu-24.04"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v5"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
php-version: "8.5"
|
||||
extensions: "apcu, pdo, sqlite3"
|
||||
coverage: "pcov"
|
||||
ini-values: "zend.assertions=1, apc.enable_cli=1"
|
||||
|
||||
- name: "Allow dev dependencies"
|
||||
run: composer config minimum-stability dev
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
dependency-versions: "highest"
|
||||
|
||||
- name: "Run PHPUnit"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/sqlite3.xml --fail-on-deprecation"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 0
|
||||
ENABLE_NATIVE_LAZY_OBJECTS: 1
|
||||
|
||||
|
||||
phpunit-postgres:
|
||||
name: "PHPUnit with PostgreSQL"
|
||||
name: >
|
||||
${{ format('PostgreSQL {0} - PHP {1} - DBAL {2} - ext. {3}',
|
||||
matrix.postgres-version || 'Ø',
|
||||
matrix.php-version || 'Ø',
|
||||
matrix.dbal-version || 'Ø',
|
||||
matrix.extension || 'Ø'
|
||||
) }}
|
||||
runs-on: "ubuntu-22.04"
|
||||
needs: "phpunit-smoke-check"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3.7"
|
||||
postgres-version:
|
||||
- "17"
|
||||
extension:
|
||||
- pdo_pgsql
|
||||
- pgsql
|
||||
include:
|
||||
- php-version: "8.2"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "4@dev"
|
||||
postgres-version: "14"
|
||||
extension: pdo_pgsql
|
||||
- php-version: "8.2"
|
||||
dbal-version: "3.7"
|
||||
postgres-version: "9.6"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "default"
|
||||
postgres-version: "10"
|
||||
extension: pdo_pgsql
|
||||
|
||||
services:
|
||||
@@ -151,7 +220,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -168,7 +237,7 @@ jobs:
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
uses: "ramsey/composer-install@v4"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
@@ -176,26 +245,30 @@ jobs:
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/pdo_pgsql.xml --coverage-clover=coverage.xml"
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
uses: "actions/upload-artifact@v7"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.extension }}-coverage"
|
||||
path: "coverage.xml"
|
||||
|
||||
|
||||
phpunit-mariadb:
|
||||
name: "PHPUnit with MariaDB"
|
||||
name: >
|
||||
${{ format('MariaDB {0} - PHP {1} - DBAL {2} - ext. {3}',
|
||||
matrix.mariadb-version || 'Ø',
|
||||
matrix.php-version || 'Ø',
|
||||
matrix.dbal-version || 'Ø',
|
||||
matrix.extension || 'Ø'
|
||||
) }}
|
||||
runs-on: "ubuntu-22.04"
|
||||
needs: "phpunit-smoke-check"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3.7"
|
||||
- "4@dev"
|
||||
mariadb-version:
|
||||
- "11.4"
|
||||
@@ -218,7 +291,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -235,7 +308,7 @@ jobs:
|
||||
extensions: "${{ matrix.extension }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
uses: "ramsey/composer-install@v4"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
@@ -243,26 +316,30 @@ jobs:
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage.xml"
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
uses: "actions/upload-artifact@v7"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.mariadb-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
|
||||
path: "coverage.xml"
|
||||
|
||||
|
||||
phpunit-mysql:
|
||||
name: "PHPUnit with MySQL"
|
||||
name: >
|
||||
${{ format('MySQL {0} - PHP {1} - DBAL {2} - ext. {3}',
|
||||
matrix.mysql-version || 'Ø',
|
||||
matrix.php-version || 'Ø',
|
||||
matrix.dbal-version || 'Ø',
|
||||
matrix.extension || 'Ø'
|
||||
) }}
|
||||
runs-on: "ubuntu-22.04"
|
||||
needs: "phpunit-smoke-check"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3.7"
|
||||
mysql-version:
|
||||
- "5.7"
|
||||
- "8.0"
|
||||
@@ -270,11 +347,11 @@ jobs:
|
||||
- "mysqli"
|
||||
- "pdo_mysql"
|
||||
include:
|
||||
- php-version: "8.2"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "4@dev"
|
||||
mysql-version: "8.0"
|
||||
extension: "mysqli"
|
||||
- php-version: "8.2"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "4@dev"
|
||||
mysql-version: "8.0"
|
||||
extension: "pdo_mysql"
|
||||
@@ -293,7 +370,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -310,7 +387,7 @@ jobs:
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
uses: "ramsey/composer-install@v4"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
@@ -319,13 +396,27 @@ jobs:
|
||||
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"
|
||||
- name: "Run PHPUnit with Second Level Cache and PHPUnit 10"
|
||||
run: |
|
||||
vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml \
|
||||
--exclude-group=performance,non-cacheable,locking_functional \
|
||||
--coverage-clover=coverage-no-cache.xml"
|
||||
if: "${{ matrix.php-version == '8.1' }}"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 1
|
||||
- name: "Run PHPUnit with Second Level Cache and PHPUnit 11+"
|
||||
run: |
|
||||
vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml \
|
||||
--exclude-group=performance \
|
||||
--exclude-group=non-cacheable \
|
||||
--exclude-group=locking_functional \
|
||||
--coverage-clover=coverage-no-cache.xml
|
||||
if: "${{ matrix.php-version != '8.1' }}"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 1
|
||||
|
||||
- name: "Upload coverage files"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
uses: "actions/upload-artifact@v7"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.mysql-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
@@ -343,17 +434,17 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Download coverage files"
|
||||
uses: "actions/download-artifact@v4"
|
||||
uses: "actions/download-artifact@v8"
|
||||
with:
|
||||
path: "reports"
|
||||
|
||||
- name: "Upload to Codecov"
|
||||
uses: "codecov/codecov-action@v5"
|
||||
uses: "codecov/codecov-action@v6"
|
||||
with:
|
||||
directory: reports
|
||||
env:
|
||||
|
||||
@@ -17,4 +17,4 @@ on:
|
||||
jobs:
|
||||
documentation:
|
||||
name: "Documentation"
|
||||
uses: "doctrine/.github/.github/workflows/documentation.yml@7.3.0"
|
||||
uses: "doctrine/.github/.github/workflows/documentation.yml@14.0.0"
|
||||
|
||||
@@ -32,11 +32,11 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.1"
|
||||
- "8.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
ini-values: "zend.assertions=1, apc.enable_cli=1"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
uses: "ramsey/composer-install@v4"
|
||||
|
||||
- name: "Run PHPBench"
|
||||
run: "vendor/bin/phpbench run --report=default"
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@7.3.0"
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@14.0.0"
|
||||
secrets:
|
||||
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
|
||||
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}
|
||||
|
||||
@@ -25,17 +25,9 @@ jobs:
|
||||
name: Static Analysis with PHPStan
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- dbal-version: default
|
||||
config: phpstan.neon
|
||||
- dbal-version: 3.8.2
|
||||
config: phpstan-dbal3.neon
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
|
||||
- name: Install PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
@@ -44,13 +36,8 @@ jobs:
|
||||
php-version: "8.4"
|
||||
tools: cs2pr
|
||||
|
||||
- name: Require specific DBAL version
|
||||
run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update"
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
|
||||
- name: Install dependencies with Composer
|
||||
uses: ramsey/composer-install@v2
|
||||
uses: ramsey/composer-install@v4
|
||||
|
||||
- name: Run static analysis with phpstan/phpstan
|
||||
run: "vendor/bin/phpstan analyse -c ${{ matrix.config }} --error-format=checkstyle | cs2pr"
|
||||
run: "vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
| [4.0.x][4.0] | [3.5.x][3.5] | [3.4.x][3.4] | [2.21.x][2.21] | [2.20.x][2.20] |
|
||||
| [4.0.x][4.0] | [3.7.x][3.7] | [3.6.x][3.6] | [2.21.x][2.21] | [2.20.x][2.20] |
|
||||
|:------------------------------------------------------:|:------------------------------------------------------:|:------------------------------------------------------:|:--------------------------------------------------------:|:--------------------------------------------------------:|
|
||||
| [![Build status][4.0 image]][4.0] | [![Build status][3.5 image]][3.5] | [![Build status][3.4 image]][3.4] | [![Build status][2.21 image]][2.21] | [![Build status][2.20 image]][2.20] |
|
||||
| [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.5 coverage image]][3.5 coverage] | [![Coverage Status][3.4 coverage image]][3.4 coverage] | [![Coverage Status][2.21 coverage image]][2.21 coverage] | [![Coverage Status][2.20 coverage image]][2.20 coverage] |
|
||||
| [![Build status][4.0 image]][4.0 workflow] | [![Build status][3.7 image]][3.7 workflow] | [![Build status][3.6 image]][3.6 workflow] | [![Build status][2.21 image]][2.21 workflow] | [![Build status][2.20 image]][2.20 workflow] |
|
||||
| [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.7 coverage image]][3.7 coverage] | [![Coverage Status][3.6 coverage image]][3.6 coverage] | [![Coverage Status][2.21 coverage image]][2.21 coverage] | [![Coverage Status][2.20 coverage image]][2.20 coverage] |
|
||||
|
||||
Doctrine ORM is an object-relational mapper for PHP 8.1+ that provides transparent persistence
|
||||
Doctrine ORM is an object-relational mapper for PHP 8.4+ that provides transparent persistence
|
||||
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
|
||||
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
|
||||
inspired by Hibernate's HQL. This provides developers with a powerful alternative to SQL that maintains flexibility
|
||||
@@ -18,21 +18,26 @@ without requiring unnecessary code duplication.
|
||||
|
||||
[4.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=4.0.x
|
||||
[4.0]: https://github.com/doctrine/orm/tree/4.0.x
|
||||
[4.0 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A4.0.x
|
||||
[4.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/4.0.x/graph/badge.svg
|
||||
[4.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/4.0.x
|
||||
[3.5 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.5.x
|
||||
[3.5]: https://github.com/doctrine/orm/tree/3.5.x
|
||||
[3.5 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.5.x/graph/badge.svg
|
||||
[3.5 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.5.x
|
||||
[3.4 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.4.x
|
||||
[3.4]: https://github.com/doctrine/orm/tree/3.4.x
|
||||
[3.4 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.4.x/graph/badge.svg
|
||||
[3.4 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.4.x
|
||||
[3.7 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.7.x
|
||||
[3.7]: https://github.com/doctrine/orm/tree/3.7.x
|
||||
[3.7 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A3.7.x
|
||||
[3.7 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.7.x/graph/badge.svg
|
||||
[3.7 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.7.x
|
||||
[3.6 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.6.x
|
||||
[3.6]: https://github.com/doctrine/orm/tree/3.6.x
|
||||
[3.6 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A3.6.x
|
||||
[3.6 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.6.x/graph/badge.svg
|
||||
[3.6 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.6.x
|
||||
[2.21 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.21.x
|
||||
[2.21]: https://github.com/doctrine/orm/tree/2.21.x
|
||||
[2.21 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A2.21.x
|
||||
[2.21 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.21.x/graph/badge.svg
|
||||
[2.21 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.21.x
|
||||
[2.20 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.20.x
|
||||
[2.20]: https://github.com/doctrine/orm/tree/2.20.x
|
||||
[2.20 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A2.20.x
|
||||
[2.20 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.20.x/graph/badge.svg
|
||||
[2.20 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.20.x
|
||||
|
||||
+433
-1
@@ -1,3 +1,399 @@
|
||||
Note about upgrading: Doctrine uses static and runtime mechanisms to raise
|
||||
awareness about deprecated code.
|
||||
|
||||
- Use of `@deprecated` docblock that is detected by IDEs (like PHPStorm) or
|
||||
Static Analysis tools (like Psalm, phpstan)
|
||||
- Use of our low-overhead runtime deprecation API, details:
|
||||
https://github.com/doctrine/deprecations/
|
||||
|
||||
# Upgrade to 4.0
|
||||
|
||||
## BC BREAK: EventManager to EventManagerInterface migration
|
||||
|
||||
The following methods used to return an instance of `Doctrine\Common\EventManager`,
|
||||
and now return an instance of `Doctrine\Common\EventManagerInterface`:
|
||||
|
||||
- `Doctrine\ORM\EntityManager::getEventManager()`
|
||||
- `Doctrine\ORM\EntityManagerDecorator::getEventManager()`
|
||||
- `Doctrine\ORM\EntityManagerInterface::getEventManager()`
|
||||
|
||||
## BC BREAK: Remove `FieldMapping::$default`
|
||||
|
||||
The `default` property of `Doctrine\ORM\Mapping\FieldMapping` has been removed.
|
||||
Use `FieldMapping::$options['default']` instead.
|
||||
|
||||
## BC BREAK: throw on `nullable` on columns that end up being used in a primary key
|
||||
|
||||
Specifying `nullable` on join columns that are part of a primary key is
|
||||
an error and will cause an exception to be thrown.
|
||||
|
||||
## BC BREAK: `Doctrine\ORM\Mapping\LegacyReflectionFields` is removed
|
||||
|
||||
The `Doctrine\ORM\Mapping\LegacyReflectionFields` class has been removed.
|
||||
Also, `Doctrine\ORM\Mapping\ClassMetadata::$reflFields` has been removed, as
|
||||
well as methods depending on it.
|
||||
|
||||
## BC BREAK: Userland lazy objects are no longer supported
|
||||
|
||||
Userland lazy objects are no longer supported.
|
||||
[Native lazy objects](https://www.php.net/manual/en/language.oop5.lazy-objects.php)
|
||||
are used instead.
|
||||
|
||||
## BC BREAK: type declarations on constants
|
||||
|
||||
All constants in the ORM now have type declarations and are final. You may no
|
||||
longer override them in extending types.
|
||||
|
||||
## Remove methods for configuring no longer configurable features
|
||||
|
||||
Since 3.0, lazy ghosts are enabled unconditionally, and so is rejecting ID
|
||||
collisions in the identity map.
|
||||
|
||||
As a consequence, the following methods are removed:
|
||||
|
||||
* `Doctrine\ORM\Configuration::setLazyGhostObjectEnabled()`
|
||||
* `Doctrine\ORM\Configuration::isLazyGhostObjectEnabled()`
|
||||
* `Doctrine\ORM\Configuration::setRejectIdCollisionInIdentityMap()`
|
||||
* `Doctrine\ORM\Configuration::isRejectIdCollisionInIdentityMapEnabled()`
|
||||
|
||||
## BC BREAK: New argument to `NewObjectExpression::dispatch()`
|
||||
|
||||
```diff
|
||||
<?php
|
||||
class NewObjectExpression extends Node
|
||||
{
|
||||
// …
|
||||
|
||||
- public function dispatch(SqlWalker $walker): string
|
||||
+ public function dispatch(SqlWalker $walker, string|null $parentAlias = null): string
|
||||
{
|
||||
// …
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## BC BREAK: New argument to `AbstractEntityPersister::buildCollectionCacheKey()`
|
||||
|
||||
|
||||
```diff
|
||||
<?php
|
||||
abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
{
|
||||
// …
|
||||
|
||||
protected function buildCollectionCacheKey(
|
||||
AssociationMapping $association,
|
||||
array $ownerId,
|
||||
+ string $filterHash
|
||||
): CollectionCacheKey {
|
||||
// …
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Require implementation of `OutputWalker`, remove `SqlWalker::getExecutor()`
|
||||
|
||||
The `SqlWalker::getExecutor()` method is removed. Output walkers should
|
||||
implement the `\Doctrine\ORM\Query\OutputWalker` interface and create
|
||||
`Doctrine\ORM\Query\Exec\SqlFinalizer` instances.
|
||||
|
||||
## Remove `DatabaseDriver`
|
||||
|
||||
The class `Doctrine\ORM\Mapping\Driver\DatabaseDriver` is removed.
|
||||
|
||||
## Remove the `NotSupported` exception
|
||||
|
||||
The class `Doctrine\ORM\Exception\NotSupported` has been removed without replacement.
|
||||
|
||||
## Remove remaining `Serializable` implementation
|
||||
|
||||
`SequenceGenerator` does not implement the `Serializable` interface anymore.
|
||||
|
||||
The following methods have been removed:
|
||||
|
||||
* `SequenceGenerator::serialize()`
|
||||
* `SequenceGenerator::unserialize()`
|
||||
|
||||
## Remove `orm:schema-tool:update` option `--complete`
|
||||
|
||||
That option was a no-op.
|
||||
|
||||
## Remove `Doctrine\ORM\Mapping\ReflectionEnumProperty`
|
||||
|
||||
This class has been removed.
|
||||
Instead, use `Doctrine\Persistence\Reflection\EnumReflectionProperty` from
|
||||
`doctrine/persistence`.
|
||||
|
||||
## Forbid passing null to `ClassMetadata::fullyQualifiedClassName()`
|
||||
|
||||
Passing `null` to `Doctrine\ORM\ClassMetadata::fullyQualifiedClassName()` is
|
||||
no longer possible.
|
||||
|
||||
## Remove array access
|
||||
|
||||
Using array access on instances of the following classes is no longer possible:
|
||||
|
||||
- `Doctrine\ORM\Mapping\DiscriminatorColumnMapping`
|
||||
- `Doctrine\ORM\Mapping\EmbedClassMapping`
|
||||
- `Doctrine\ORM\Mapping\FieldMapping`
|
||||
- `Doctrine\ORM\Mapping\JoinColumnMapping`
|
||||
- `Doctrine\ORM\Mapping\JoinTableMapping`
|
||||
|
||||
## Remove properties `$indexes` and `$uniqueConstraints` from `Doctrine\ORM\Mapping\Table`
|
||||
|
||||
The properties `$indexes` and `$uniqueConstraints` have been removed since they had no effect at all.
|
||||
The preferred way of defining indices and unique constraints is by
|
||||
using the `\Doctrine\ORM\Mapping\UniqueConstraint` and `\Doctrine\ORM\Mapping\Index` attributes.
|
||||
|
||||
# Upgrade to 3.x General Notes
|
||||
|
||||
We recommend you upgrade to DBAL 3 first before upgrading to ORM 3. See
|
||||
the DBAL upgrade docs: https://github.com/doctrine/dbal/blob/3.10.x/UPGRADE.md
|
||||
|
||||
Rather than doing several major upgrades at once, we recommend you do the following:
|
||||
|
||||
- upgrade to DBAL 3
|
||||
- deploy and monitor
|
||||
- upgrade to ORM 3
|
||||
- deploy and monitor
|
||||
- upgrade to DBAL 4
|
||||
- deploy and monitor
|
||||
|
||||
If you are using Symfony, the recommended minimal Doctrine Bundle version is 2.15
|
||||
to run with ORM 3.
|
||||
|
||||
At this point, we recommend upgrading to PHP 8.4 first and then directly from
|
||||
ORM 2.19 to 3.5 and up so that you can skip the lazy ghost proxy generation
|
||||
and directly start using native lazy objects.
|
||||
>>>>>>> origin/3.6.x
|
||||
|
||||
# Upgrade to 3.7
|
||||
|
||||
## Conditional breaking changes
|
||||
|
||||
3.7 adds support for `doctrine/collections` 3. If you upgrade to that version
|
||||
of `doctrine/collections`, there are breaking changes in `doctrine/orm` as well,
|
||||
because of cross-package inheritance and type declarations.
|
||||
|
||||
Most notably, `Doctrine\ORM\PersistentCollection::add` no longer returns a boolean:
|
||||
|
||||
```diff
|
||||
- public function add(mixed $value): bool
|
||||
+ public function add(mixed $value): void
|
||||
```
|
||||
|
||||
That method always returned `true`, so you can safely stop using the return
|
||||
value before upgrading.
|
||||
|
||||
Also, if you extend `Doctrine\ORM\Persisters\SqlValueVisitor`, you need to
|
||||
ensure the following methods have a return type in your subclasses:
|
||||
|
||||
- `walkComparison()`
|
||||
- `walkCompositeExpression()`
|
||||
- `walkValue()`
|
||||
|
||||
## Deprecate `EventManager` return type in `EntityManager` methods
|
||||
|
||||
The return type of the following methods has been changed from
|
||||
`Doctrine\Common\EventManager` to `Doctrine\Common\EventManagerInterface`:
|
||||
|
||||
- `Doctrine\ORM\Decorator\EntityManagerDecorator::getEventManager()`
|
||||
- `Doctrine\ORM\EntityManager::getEventManager()`
|
||||
- `Doctrine\ORM\EntityManagerInterface::getEventManager()`
|
||||
|
||||
All three methods continue to return an instance of `EventManager`, however
|
||||
relying on that is deprecated and will no longer be the guaranteed in 4.0.
|
||||
|
||||
# Upgrade to 3.6
|
||||
|
||||
## Deprecate using string expression for default values in mappings
|
||||
|
||||
Using a string expression for default values in field mappings is deprecated.
|
||||
Use `Doctrine\DBAL\Schema\DefaultExpression` instances instead.
|
||||
|
||||
Here is how to address this deprecation when mapping entities using PHP attributes:
|
||||
|
||||
```diff
|
||||
use DateTime;
|
||||
+use Doctrine\DBAL\Schema\DefaultExpression\CurrentDate;
|
||||
+use Doctrine\DBAL\Schema\DefaultExpression\CurrentTime;
|
||||
+use Doctrine\DBAL\Schema\DefaultExpression\CurrentTimestamp;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity]
|
||||
final class TimeEntity
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column]
|
||||
public int $id;
|
||||
|
||||
- #[ORM\Column(options: ['default' => 'CURRENT_TIMESTAMP'], insertable: false, updatable: false)]
|
||||
+ #[ORM\Column(options: ['default' => new CurrentTimestamp()], insertable: false, updatable: false)]
|
||||
public DateTime $createdAt;
|
||||
|
||||
- #[ORM\Column(options: ['default' => 'CURRENT_TIME'], insertable: false, updatable: false)]
|
||||
+ #[ORM\Column(options: ['default' => new CurrentTime()], insertable: false, updatable: false)]
|
||||
public DateTime $createdTime;
|
||||
|
||||
- #[ORM\Column(options: ['default' => 'CURRENT_DATE'], insertable: false, updatable: false)]
|
||||
+ #[ORM\Column(options: ['default' => new CurrentDate()], insertable: false, updatable: false)]
|
||||
public DateTime $createdDate;
|
||||
}
|
||||
```
|
||||
|
||||
Here is how to do the same when mapping entities using XML:
|
||||
|
||||
```diff
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Doctrine\Tests\ORM\Functional\XmlTimeEntity">
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="AUTO"/>
|
||||
</id>
|
||||
|
||||
<field name="createdAt" type="datetime" insertable="false" updatable="false">
|
||||
<options>
|
||||
- <option name="default">CURRENT_TIMESTAMP</option>
|
||||
+ <option name="default">
|
||||
+ <object class="Doctrine\DBAL\Schema\DefaultExpression\CurrentTimestamp"/>
|
||||
+ </option>
|
||||
</options>
|
||||
</field>
|
||||
|
||||
<field name="createdAtImmutable" type="datetime_immutable" insertable="false" updatable="false">
|
||||
<options>
|
||||
- <option name="default">CURRENT_TIMESTAMP</option>
|
||||
+ <option name="default">
|
||||
+ <object class="Doctrine\DBAL\Schema\DefaultExpression\CurrentTimestamp"/>
|
||||
+ </option>
|
||||
</options>
|
||||
</field>
|
||||
|
||||
<field name="createdTime" type="time" insertable="false" updatable="false">
|
||||
<options>
|
||||
- <option name="default">CURRENT_TIME</option>
|
||||
+ <option name="default">
|
||||
+ <object class="Doctrine\DBAL\Schema\DefaultExpression\CurrentTime"/>
|
||||
+ </option>
|
||||
</options>
|
||||
</field>
|
||||
<field name="createdDate" type="date" insertable="false" updatable="false">
|
||||
<options>
|
||||
- <option name="default">CURRENT_DATE</option>
|
||||
+ <option name="default">
|
||||
+ <object class="Doctrine\DBAL\Schema\DefaultExpression\CurrentDate"/>
|
||||
+ </option>
|
||||
</options>
|
||||
</field>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
```
|
||||
|
||||
|
||||
## Deprecate `FieldMapping::$default`
|
||||
|
||||
The `default` property of `Doctrine\ORM\Mapping\FieldMapping` is deprecated and
|
||||
will be removed in 4.0. Instead, use `FieldMapping::$options['default']`.
|
||||
|
||||
## Deprecate specifying `nullable` on columns that end up being used in a primary key
|
||||
|
||||
Specifying `nullable` on join columns that are part of a primary key is
|
||||
deprecated and will be an error in 4.0.
|
||||
|
||||
This can happen when using a join column mapping together with an id mapping,
|
||||
or when using a join column mapping or an inverse join column mapping on a
|
||||
many-to-many relationship.
|
||||
|
||||
```diff
|
||||
class User
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private int $id;
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\ManyToOne(targetEntity: Family::class, inversedBy: 'users')]
|
||||
- #[ORM\JoinColumn(name: 'family_id', referencedColumnName: 'id', nullable: true)]
|
||||
+ #[ORM\JoinColumn(name: 'family_id', referencedColumnName: 'id')]
|
||||
private ?Family $family;
|
||||
|
||||
#[ORM\ManyToMany(targetEntity: Group::class)]
|
||||
#[ORM\JoinTable(name: 'user_group')]
|
||||
- #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: true)]
|
||||
- #[ORM\InverseJoinColumn(name: 'group_id', referencedColumnName: 'id', nullable: true)]
|
||||
+ #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id')]
|
||||
+ #[ORM\InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')]
|
||||
private Collection $groups;
|
||||
}
|
||||
```
|
||||
|
||||
## Deprecate `Doctrine\ORM\QueryBuilder::add('join', ...)` with a list of join parts
|
||||
|
||||
Using `Doctrine\ORM\QueryBuilder::add('join', ...)` with a list of join parts
|
||||
is deprecated in favor of using an associative array of join parts with the
|
||||
root alias as key.
|
||||
|
||||
## Deprecate using the `WITH` keyword for arbitrary DQL joins
|
||||
|
||||
Using the `WITH` keyword to specify the condition for an arbitrary DQL join is
|
||||
deprecated in favor of using the `ON` keyword (similar to the SQL syntax for
|
||||
joins).
|
||||
The `WITH` keyword is now meant to be used only for filtering conditions in
|
||||
association joins.
|
||||
|
||||
# Upgrade to 3.5
|
||||
|
||||
See the General notes to upgrading to 3.x versions above.
|
||||
|
||||
## Deprecate not using native lazy objects on PHP 8.4+
|
||||
|
||||
Having native lazy objects disabled on PHP 8.4+ is deprecated and will not be
|
||||
possible in 4.0.
|
||||
|
||||
You can enable them through configuration:
|
||||
|
||||
```php
|
||||
$config->enableNativeLazyObjects(true);
|
||||
```
|
||||
|
||||
As a consequence, methods, parameters and commands related to userland lazy
|
||||
objects have been deprecated on PHP 8.4+:
|
||||
|
||||
- `Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand`
|
||||
- `Doctrine\ORM\Configuration::getAutoGenerateProxyClasses()`
|
||||
- `Doctrine\ORM\Configuration::getProxyDir()`
|
||||
- `Doctrine\ORM\Configuration::getProxyNamespace()`
|
||||
- `Doctrine\ORM\Configuration::setAutoGenerateProxyClasses()`
|
||||
- `Doctrine\ORM\Configuration::setProxyDir()`
|
||||
- `Doctrine\ORM\Configuration::setProxyNamespace()`
|
||||
- Passing more than one argument to `Doctrine\ORM\Proxy\ProxyFactory::__construct()`
|
||||
|
||||
Additionally, some methods of ORMSetup have been deprecated in favor of a new
|
||||
counterpart.
|
||||
|
||||
- `Doctrine\ORM\ORMSetup::createAttributeMetadataConfiguration()` is deprecated in favor of
|
||||
`Doctrine\ORM\ORMSetup::createAttributeMetadataConfig()`
|
||||
- `Doctrine\ORM\ORMSetup::createXMLMetadataConfiguration()` is deprecated in favor of
|
||||
`Doctrine\ORM\ORMSetup::createXMLMetadataConfig()`
|
||||
- `Doctrine\ORM\ORMSetup::createConfiguration()` is deprecated in favor of
|
||||
`Doctrine\ORM\ORMSetup::createConfig()`
|
||||
|
||||
## Deprecate methods for configuring no longer configurable features
|
||||
|
||||
Since 3.0, lazy ghosts are enabled unconditionally, and so is rejecting ID
|
||||
collisions in the identity map.
|
||||
|
||||
As a consequence, the following methods are deprecated and will be removed in 4.0:
|
||||
* `Doctrine\ORM\Configuration::setLazyGhostObjectEnabled()`
|
||||
* `Doctrine\ORM\Configuration::isLazyGhostObjectEnabled()`
|
||||
* `Doctrine\ORM\Configuration::setRejectIdCollisionInIdentityMap()`
|
||||
* `Doctrine\ORM\Configuration::isRejectIdCollisionInIdentityMapEnabled()`
|
||||
|
||||
# Upgrade to 3.4.1
|
||||
|
||||
## BC BREAK: You can no longer use the `.*` notation to get all fields of an entity in a DTO
|
||||
@@ -7,6 +403,8 @@ decide to remove it before it is used too widely.
|
||||
|
||||
# Upgrade to 3.4
|
||||
|
||||
See the General notes to upgrading to 3.x versions above.
|
||||
|
||||
## Discriminator Map class duplicates
|
||||
|
||||
Using the same class several times in a discriminator map is deprecated.
|
||||
@@ -23,8 +421,12 @@ that implements `ArrayAccess`.
|
||||
Use the new `Doctrine\ORM\Mapping\PropertyAccessors\PropertyAccessor` API and access
|
||||
through `Doctrine\ORM\Mapping\ClassMetadata::$propertyAccessors` instead.
|
||||
|
||||
Companion accessor methods are deprecated as well.
|
||||
|
||||
# Upgrade to 3.3
|
||||
|
||||
See the General notes to upgrading to 3.x versions above.
|
||||
|
||||
## Deprecate `DatabaseDriver`
|
||||
|
||||
The class `Doctrine\ORM\Mapping\Driver\DatabaseDriver` is deprecated without replacement.
|
||||
@@ -41,6 +443,8 @@ method. Details can be found at https://github.com/doctrine/orm/pull/11188.
|
||||
|
||||
# Upgrade to 3.2
|
||||
|
||||
See the General notes to upgrading to 3.x versions above.
|
||||
|
||||
## Deprecate the `NotSupported` exception
|
||||
|
||||
The class `Doctrine\ORM\Exception\NotSupported` is deprecated without replacement.
|
||||
@@ -67,6 +471,8 @@ using the `\Doctrine\ORM\Mapping\UniqueConstraint` and `\Doctrine\ORM\Mapping\In
|
||||
|
||||
# Upgrade to 3.1
|
||||
|
||||
See the General notes to upgrading to 3.x versions above.
|
||||
|
||||
## Deprecate `Doctrine\ORM\Mapping\ReflectionEnumProperty`
|
||||
|
||||
This class is deprecated and will be removed in 4.0.
|
||||
@@ -90,6 +496,8 @@ Using array access on instances of the following classes is deprecated:
|
||||
|
||||
# Upgrade to 3.0
|
||||
|
||||
See the General notes to upgrading to 3.x versions above.
|
||||
|
||||
## BC BREAK: Calling `ClassMetadata::getAssociationMappedByTargetField()` with the owning side of an association now throws an exception
|
||||
|
||||
Previously, calling
|
||||
@@ -115,6 +523,9 @@ so `$targetEntity` is a first argument now. This change affects only non-named a
|
||||
When using the `AUTO` strategy to let Doctrine determine the identity generation mechanism for
|
||||
an entity, and when using `doctrine/dbal` 4, PostgreSQL now uses `IDENTITY`
|
||||
instead of `SEQUENCE` or `SERIAL`.
|
||||
|
||||
There are three ways to handle this change.
|
||||
|
||||
* If you want to upgrade your existing tables to identity columns, you will need to follow [migration to identity columns on PostgreSQL](https://www.doctrine-project.org/projects/doctrine-dbal/en/4.0/how-to/postgresql-identity-migration.html)
|
||||
* If you want to keep using SQL sequences, you need to configure the ORM this way:
|
||||
```php
|
||||
@@ -127,6 +538,27 @@ $configuration->setIdentityGenerationPreferences([
|
||||
PostgreSQLPlatform::CLASS => ClassMetadata::GENERATOR_TYPE_SEQUENCE,
|
||||
]);
|
||||
```
|
||||
* You can change individual entities to use the `SEQUENCE` strategy instead of `AUTO`:
|
||||
```php
|
||||
|
||||
diff --git a/src/Entity/Example.php b/src/Entity/Example.php
|
||||
index 28be8df378..3b7d61bda6 100644
|
||||
--- a/src/Entity/Example.php
|
||||
+++ b/src/Entity/Example.php
|
||||
@@ -38,7 +38,7 @@ class Example
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
- #[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||
+ #[ORM\GeneratedValue(strategy: 'SEQUENCE')]
|
||||
private int $id;
|
||||
|
||||
#[Assert\Length(max: 255)]
|
||||
```
|
||||
The later two options require a small database migration that will remove the default
|
||||
expression fetching the next value from the sequence. It's not strictly necessary to
|
||||
do this migration because the code will work anyway. A benefit of this approach is
|
||||
that you can just make and roll out the code changes first and then migrate the database later.
|
||||
|
||||
## BC BREAK: Throw exceptions when using illegal attributes on Embeddable
|
||||
|
||||
@@ -2046,7 +2478,7 @@ from 2.0 have to configure the annotation driver if they don't use `Configuratio
|
||||
|
||||
## Scalar mappings can now be omitted from DQL result
|
||||
|
||||
You are now allowed to mark scalar SELECT expressions as HIDDEN an they are not hydrated anymore.
|
||||
You are now allowed to mark scalar SELECT expressions as HIDDEN and they are not hydrated anymore.
|
||||
Example:
|
||||
|
||||
SELECT u, SUM(a.id) AS HIDDEN numArticles FROM User u LEFT JOIN u.Articles a ORDER BY numArticles DESC HAVING numArticles > 10
|
||||
|
||||
+71
-53
@@ -1,17 +1,77 @@
|
||||
{
|
||||
"name": "doctrine/orm",
|
||||
"type": "library",
|
||||
"description": "Object-Relational-Mapper for PHP",
|
||||
"keywords": ["orm", "database"],
|
||||
"homepage": "https://www.doctrine-project.org/projects/orm.html",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
|
||||
{"name": "Roman Borschel", "email": "roman@code-factory.org"},
|
||||
{"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
|
||||
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
|
||||
{"name": "Marco Pivetta", "email": "ocramius@gmail.com"}
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"orm",
|
||||
"database"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Guilherme Blanco",
|
||||
"email": "guilhermeblanco@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Roman Borschel",
|
||||
"email": "roman@code-factory.org"
|
||||
},
|
||||
{
|
||||
"name": "Benjamin Eberlei",
|
||||
"email": "kontakt@beberlei.de"
|
||||
},
|
||||
{
|
||||
"name": "Jonathan Wage",
|
||||
"email": "jonwage@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Marco Pivetta",
|
||||
"email": "ocramius@gmail.com"
|
||||
}
|
||||
],
|
||||
"homepage": "https://www.doctrine-project.org/projects/orm.html",
|
||||
"require": {
|
||||
"php": "^8.4",
|
||||
"ext-ctype": "*",
|
||||
"composer-runtime-api": "^2",
|
||||
"doctrine/collections": "^3",
|
||||
"doctrine/dbal": "^4.2.1",
|
||||
"doctrine/deprecations": "^1.1",
|
||||
"doctrine/event-manager": "^2.1.1",
|
||||
"doctrine/inflector": "^1.4 || ^2.0",
|
||||
"doctrine/instantiator": "^1.3 || ^2",
|
||||
"doctrine/lexer": "^3",
|
||||
"doctrine/persistence": "^4",
|
||||
"psr/cache": "^1 || ^2 || ^3",
|
||||
"symfony/console": "^5.4 || ^6.0 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^14.0",
|
||||
"phpbench/phpbench": "^1.0",
|
||||
"phpstan/extension-installer": "^1.4",
|
||||
"phpstan/phpstan": "2.1.23",
|
||||
"phpstan/phpstan-deprecation-rules": "^2",
|
||||
"phpunit/phpunit": "^11.5.42",
|
||||
"psr/log": "^1 || ^2 || ^3",
|
||||
"symfony/cache": "^5.4 || ^6.2 || ^7.0 || ^8.0",
|
||||
"symfony/var-exporter": "^6.3.9 || ^7.0 || ^8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-dom": "Provides support for XSD validation for XML mapping files",
|
||||
"symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Doctrine\\ORM\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Performance\\": "tests/Performance",
|
||||
"Doctrine\\StaticAnalysis\\": "tests/StaticAnalysis",
|
||||
"Doctrine\\Tests\\": "tests/Tests"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
@@ -20,49 +80,7 @@
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"composer-runtime-api": "^2",
|
||||
"ext-ctype": "*",
|
||||
"doctrine/collections": "^2.2",
|
||||
"doctrine/dbal": "^3.8.2 || ^4",
|
||||
"doctrine/deprecations": "^0.5.3 || ^1",
|
||||
"doctrine/event-manager": "^1.2 || ^2",
|
||||
"doctrine/inflector": "^1.4 || ^2.0",
|
||||
"doctrine/instantiator": "^1.3 || ^2",
|
||||
"doctrine/lexer": "^3",
|
||||
"doctrine/persistence": "^3.3.1 || ^4",
|
||||
"psr/cache": "^1 || ^2 || ^3",
|
||||
"symfony/console": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/var-exporter": "^6.3.9 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^13.0",
|
||||
"phpbench/phpbench": "^1.0",
|
||||
"phpdocumentor/guides-cli": "^1.4",
|
||||
"phpstan/extension-installer": "^1.4",
|
||||
"phpstan/phpstan": "2.0.3",
|
||||
"phpstan/phpstan-deprecation-rules": "^2",
|
||||
"phpunit/phpunit": "^10.4.0",
|
||||
"psr/log": "^1 || ^2 || ^3",
|
||||
"squizlabs/php_codesniffer": "3.12.0",
|
||||
"symfony/cache": "^5.4 || ^6.2 || ^7.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-dom": "Provides support for XSD validation for XML mapping files",
|
||||
"symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Doctrine\\ORM\\": "src" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Tests\\": "tests/Tests",
|
||||
"Doctrine\\StaticAnalysis\\": "tests/StaticAnalysis",
|
||||
"Doctrine\\Performance\\": "tests/Performance"
|
||||
}
|
||||
},
|
||||
"archive": {
|
||||
"exclude": ["!vendor", "tests", "*phpunit.xml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"]
|
||||
"scripts": {
|
||||
"docs": "composer --working-dir docs update && ./docs/vendor/bin/build-docs.sh @additional_args"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
composer.lock
|
||||
vendor/
|
||||
build/
|
||||
output/
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# Makefile for Doctrine ORM documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
DOCOPTS =
|
||||
BUILD = vendor/bin/guides
|
||||
BUILDDIR = build
|
||||
|
||||
# Internal variables.
|
||||
ALLGUIDESOPTS = $(DOCOPTS) en/
|
||||
|
||||
.PHONY: help clean html
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
|
||||
clean:
|
||||
-rm -rf ./$(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(BUILD) $(ALLGUIDESOPTS) --output=$(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
+4
-8
@@ -4,19 +4,15 @@ The documentation is written in [ReStructured Text](https://docutils.sourceforge
|
||||
|
||||
## How to Generate:
|
||||
|
||||
In the `docs/` folder, run
|
||||
In the project root, run
|
||||
|
||||
composer update
|
||||
composer docs
|
||||
|
||||
Then compile the documentation with:
|
||||
|
||||
make html
|
||||
|
||||
This will generate the documentation into the `build` subdirectory.
|
||||
This will generate the documentation into the `docs/output` subdirectory.
|
||||
|
||||
To browse the documentation, you need to run a webserver:
|
||||
|
||||
cd build/html
|
||||
cd docs/output
|
||||
php -S localhost:8000
|
||||
|
||||
Now the documentation is available at [http://localhost:8000](http://localhost:8000).
|
||||
|
||||
+2
-3
@@ -3,8 +3,7 @@
|
||||
"description": "Documentation for the Object-Relational Mapper\"",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"phpdocumentor/guides-cli": "1.7.1",
|
||||
"phpdocumentor/filesystem": "1.7.1"
|
||||
"require-dev": {
|
||||
"doctrine/docs-builder": "^1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@ 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 Output walker to get the raw SQL via ``Query->getSQL()``
|
||||
with interpolated parameters.
|
||||
- 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
|
||||
@@ -50,7 +51,7 @@ example:
|
||||
- Modify the Output walker to pretty print the SQL for debugging
|
||||
purposes.
|
||||
|
||||
In this cookbook-entry I will show examples of the first two
|
||||
In this cookbook-entry I will show examples of the first three
|
||||
points. There are probably much more use-cases.
|
||||
|
||||
Generic count query for pagination
|
||||
@@ -101,8 +102,16 @@ The ``Paginate::count(Query $query)`` looks like:
|
||||
{
|
||||
static public function count(Query $query)
|
||||
{
|
||||
/** @var Query $countQuery */
|
||||
$countQuery = clone $query;
|
||||
/*
|
||||
To avoid changing the $query passed into the method and to make sure a possibly existing
|
||||
ResultSetMapping is discarded, we create a new query object any copy relevant data over.
|
||||
*/
|
||||
$countQuery = new Query($query->getEntityManager());
|
||||
$countQuery->setDQL($query->getDQL());
|
||||
$countQuery->setParameters(clone $query->getParameters());
|
||||
foreach ($query->getHints() as $name => $value) {
|
||||
$countQuery->setHint($name, $value);
|
||||
}
|
||||
|
||||
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('DoctrineExtensions\Paginate\CountSqlWalker'));
|
||||
$countQuery->setFirstResult(null)->setMaxResults(null);
|
||||
@@ -111,7 +120,7 @@ The ``Paginate::count(Query $query)`` looks like:
|
||||
}
|
||||
}
|
||||
|
||||
It clones the query, resets the limit clause first and max results
|
||||
This resets the limit clause first and max results
|
||||
and registers the ``CountSqlWalker`` custom tree walker which
|
||||
will modify the AST to execute a count query. The walkers
|
||||
implementation is:
|
||||
@@ -215,3 +224,39 @@ 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.
|
||||
|
||||
Modifying the Output Walker to get the raw SQL with interpolated parameters
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
Sometimes we may want to log or trace the raw SQL being generated from its DQL
|
||||
for profiling slow queries afterwards or audit queries that changed many rows
|
||||
``$query->getSQL()`` will give us the prepared statement being passed to database
|
||||
with all values of SQL parameters being replaced by positional ``?`` or named ``:name``
|
||||
as parameters are interpolated into prepared statements by the database while executing the SQL.
|
||||
``$query->getParameters()`` will give us details about SQL parameters that we've provided.
|
||||
So we can create an output walker to interpolate all SQL parameters that will be
|
||||
passed into prepared statement in PHP before database handle them internally:
|
||||
|
||||
.. literalinclude:: dql-custom-walkers/InterpolateParametersSQLOutputWalker.php
|
||||
:language: php
|
||||
|
||||
Then you may get the raw SQL with this output walker:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query
|
||||
->where('t.int IN (:ints)')->setParameter(':ints', [1, 2])
|
||||
->orWhere('t.string IN (?0)')->setParameter(0, ['3', '4'])
|
||||
->orWhere("t.bool = ?1")->setParameter('?1', true)
|
||||
->orWhere("t.string = :string")->setParameter(':string', 'ABC')
|
||||
->setHint(\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, InterpolateParametersSQLOutputWalker::class)
|
||||
->getSQL();
|
||||
|
||||
The where clause of the returned SQL should be like:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
WHERE t0_.int IN (1, 2)
|
||||
OR t0_.string IN ('3', '4')
|
||||
OR t0_.bool = 1
|
||||
OR t0_.string = 'ABC'
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
use Doctrine\DBAL\ArrayParameterType;
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use Doctrine\DBAL\Types\BooleanType;
|
||||
use Doctrine\DBAL\Types\Exception\ValueNotConvertible;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\ORM\Query\AST;
|
||||
use Doctrine\ORM\Query\SqlOutputWalker;
|
||||
|
||||
class InterpolateParametersSQLOutputWalker extends SqlOutputWalker
|
||||
{
|
||||
/** {@inheritdoc} */
|
||||
public function walkInputParameter(AST\InputParameter $inputParam): string
|
||||
{
|
||||
$parameter = $this->getQuery()->getParameter($inputParam->name);
|
||||
if ($parameter === null) {
|
||||
return '?';
|
||||
}
|
||||
|
||||
$value = $parameter->getValue();
|
||||
/** @var ParameterType|ArrayParameterType|int|string $typeName */
|
||||
/** @see \Doctrine\ORM\Query\ParameterTypeInferer::inferType() */
|
||||
$typeName = $parameter->getType();
|
||||
$platform = $this->getConnection()->getDatabasePlatform();
|
||||
$processParameterType = static fn(ParameterType $type) => static fn($value): string =>
|
||||
(match ($type) { /** @see Type::getBindingType() */
|
||||
ParameterType::NULL => 'NULL',
|
||||
ParameterType::INTEGER => $value,
|
||||
ParameterType::BOOLEAN => (new BooleanType())->convertToDatabaseValue($value, $platform),
|
||||
ParameterType::STRING, ParameterType::ASCII => $platform->quoteStringLiteral($value),
|
||||
default => throw new ValueNotConvertible($value, $type->name)
|
||||
});
|
||||
|
||||
if (is_string($typeName) && Type::hasType($typeName)) {
|
||||
return Type::getType($typeName)->convertToDatabaseValue($value, $platform);
|
||||
}
|
||||
if ($typeName instanceof ParameterType) {
|
||||
return $processParameterType($typeName)($value);
|
||||
}
|
||||
if ($typeName instanceof ArrayParameterType && is_array($value)) {
|
||||
$type = ArrayParameterType::toElementParameterType($typeName);
|
||||
return implode(', ', array_map($processParameterType($type), $value));
|
||||
}
|
||||
|
||||
throw new ValueNotConvertible($value, $typeName);
|
||||
}
|
||||
}
|
||||
@@ -46,17 +46,18 @@ entities:
|
||||
#[Entity]
|
||||
class Article
|
||||
{
|
||||
const STATUS_VISIBLE = 'visible';
|
||||
const STATUS_INVISIBLE = 'invisible';
|
||||
public const STATUS_VISIBLE = 'visible';
|
||||
public const STATUS_INVISIBLE = 'invisible';
|
||||
|
||||
#[Column(type: "string")]
|
||||
private $status;
|
||||
|
||||
public function setStatus($status)
|
||||
public function setStatus(string $status): void
|
||||
{
|
||||
if (!in_array($status, array(self::STATUS_VISIBLE, self::STATUS_INVISIBLE))) {
|
||||
if (!in_array($status, [self::STATUS_VISIBLE, self::STATUS_INVISIBLE], true)) {
|
||||
throw new \InvalidArgumentException("Invalid status");
|
||||
}
|
||||
|
||||
$this->status = $status;
|
||||
}
|
||||
}
|
||||
@@ -92,37 +93,33 @@ For example for the previous enum type:
|
||||
|
||||
class EnumVisibilityType extends Type
|
||||
{
|
||||
const ENUM_VISIBILITY = 'enumvisibility';
|
||||
const STATUS_VISIBLE = 'visible';
|
||||
const STATUS_INVISIBLE = 'invisible';
|
||||
private const ENUM_VISIBILITY = 'enumvisibility';
|
||||
private const STATUS_VISIBLE = 'visible';
|
||||
private const STATUS_INVISIBLE = 'invisible';
|
||||
|
||||
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
|
||||
{
|
||||
return "ENUM('visible', 'invisible')";
|
||||
}
|
||||
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): string
|
||||
{
|
||||
if (!in_array($value, array(self::STATUS_VISIBLE, self::STATUS_INVISIBLE))) {
|
||||
if (!in_array($value, [self::STATUS_VISIBLE, self::STATUS_INVISIBLE], true)) {
|
||||
throw new \InvalidArgumentException("Invalid status");
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return self::ENUM_VISIBILITY;
|
||||
}
|
||||
|
||||
public function requiresSQLCommentHint(AbstractPlatform $platform)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
You can register this type with ``Type::addType('enumvisibility', 'MyProject\DBAL\EnumVisibilityType');``.
|
||||
@@ -151,37 +148,33 @@ You can generalize this approach easily to create a base class for enums:
|
||||
abstract class EnumType extends Type
|
||||
{
|
||||
protected $name;
|
||||
protected $values = array();
|
||||
protected $values = [];
|
||||
|
||||
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
|
||||
{
|
||||
$values = array_map(function($val) { return "'".$val."'"; }, $this->values);
|
||||
$values = array_map(fn($val) => "'".$val."'", $this->values);
|
||||
|
||||
return "ENUM(".implode(", ", $values).")";
|
||||
}
|
||||
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): mixed
|
||||
{
|
||||
if (!in_array($value, $this->values)) {
|
||||
if (!in_array($value, $this->values, true)) {
|
||||
throw new \InvalidArgumentException("Invalid '".$this->name."' value.");
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function requiresSQLCommentHint(AbstractPlatform $platform)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
With this base class you can define an enum as easily as:
|
||||
@@ -194,5 +187,5 @@ With this base class you can define an enum as easily as:
|
||||
class EnumVisibilityType extends EnumType
|
||||
{
|
||||
protected $name = 'enumvisibility';
|
||||
protected $values = array('visible', 'invisible');
|
||||
protected $values = ['visible', 'invisible'];
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ If this documentation is not helping to answer questions you have about
|
||||
Doctrine ORM don't panic. You can get help from different sources:
|
||||
|
||||
- There is a :doc:`FAQ <reference/faq>` with answers to frequent questions.
|
||||
- The `Doctrine Mailing List <https://groups.google.com/group/doctrine-user>`_
|
||||
- Slack chat room `#orm <https://www.doctrine-project.org/slack>`_
|
||||
- Report a bug on `GitHub <https://github.com/doctrine/orm/issues>`_.
|
||||
- On `StackOverflow <https://stackoverflow.com/questions/tagged/doctrine-orm>`_
|
||||
|
||||
@@ -29,22 +29,11 @@ steps of configuration.
|
||||
|
||||
$config = new Configuration;
|
||||
$config->setMetadataCache($metadataCache);
|
||||
$driverImpl = new AttributeDriver(['/path/to/lib/MyProject/Entities'], true);
|
||||
$driverImpl = new AttributeDriver(['/path/to/lib/MyProject/Entities']);
|
||||
$config->setMetadataDriverImpl($driverImpl);
|
||||
$config->setQueryCache($queryCache);
|
||||
|
||||
if (PHP_VERSION_ID > 80400) {
|
||||
$config->enableNativeLazyObjects(true);
|
||||
} else {
|
||||
$config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies');
|
||||
$config->setProxyNamespace('MyProject\Proxies');
|
||||
|
||||
if ($applicationMode === "development") {
|
||||
$config->setAutoGenerateProxyClasses(true);
|
||||
} else {
|
||||
$config->setAutoGenerateProxyClasses(false);
|
||||
}
|
||||
}
|
||||
$config->enableNativeLazyObjects(true);
|
||||
|
||||
$connection = DriverManager::getConnection([
|
||||
'driver' => 'pdo_sqlite',
|
||||
@@ -76,53 +65,6 @@ Configuration Options
|
||||
The following sections describe all the configuration options
|
||||
available on a ``Doctrine\ORM\Configuration`` instance.
|
||||
|
||||
Native Lazy Objects (**OPTIONAL**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
With PHP 8.4 we recommend that you use native lazy objects instead of
|
||||
the code generation approach using the ``symfony/var-exporter`` Ghost trait.
|
||||
|
||||
With Doctrine 4, the minimal requirement will become PHP 8.4 and native lazy objects
|
||||
will become the only approach to lazy loading.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->enableNativeLazyObjects(true);
|
||||
|
||||
Proxy Directory
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Required except if you use native lazy objects with PHP 8.4.
|
||||
This setting will be removed in the future.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setProxyDir($dir);
|
||||
$config->getProxyDir();
|
||||
|
||||
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.
|
||||
|
||||
Proxy Namespace
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Required except if you use native lazy objects with PHP 8.4.
|
||||
This setting will be removed in the future.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setProxyNamespace($namespace);
|
||||
$config->getProxyNamespace();
|
||||
|
||||
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.
|
||||
|
||||
Metadata Driver (**REQUIRED**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -154,15 +96,59 @@ The attribute driver can be injected in the ``Doctrine\ORM\Configuration``:
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
|
||||
|
||||
$driverImpl = new AttributeDriver(['/path/to/lib/MyProject/Entities'], true);
|
||||
$driverImpl = new AttributeDriver(['/path/to/lib/MyProject/Entities']);
|
||||
$config->setMetadataDriverImpl($driverImpl);
|
||||
|
||||
The path information to the entities is required for the attribute
|
||||
driver, because otherwise mass-operations on all entities through
|
||||
the console could not work correctly. All of metadata drivers
|
||||
accept either a single directory as a string or an array of
|
||||
directories. With this feature a single driver can support multiple
|
||||
directories of Entities.
|
||||
the console could not work correctly. Metadata drivers can accept either
|
||||
a single directory as a string or an array of directories.
|
||||
|
||||
AttributeDriver also accepts ``Doctrine\Persistence\Mapping\Driver\ClassLocator``,
|
||||
allowing one to customize file discovery logic. You may choose to use Symfony Finder, or
|
||||
utilize directory scan with ``FileClassLocator::createFromDirectories()``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
|
||||
use Doctrine\Persistence\Mapping\Driver\FileClassLocator;
|
||||
|
||||
$paths = ['/path/to/lib/MyProject/Entities'];
|
||||
$classLocator = FileClassLocator::createFromDirectories($paths);
|
||||
|
||||
$driverImpl = new AttributeDriver($classLocator);
|
||||
$config->setMetadataDriverImpl($driverImpl);
|
||||
|
||||
With this feature, you're empowered to provide a fine-grained iterator of only necessary
|
||||
files to the Driver. For example, if you are using Vertical Slice architecture, you can
|
||||
exclude ``*Test.php``, ``*Controller.php``, ``*Service.php``, etc.:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
$finder = new Finder()->files()->in($paths)
|
||||
->name('*.php')
|
||||
->notName(['*Test.php', '*Controller.php', '*Service.php']);
|
||||
|
||||
$classLocator = new FileClassLocator($finder);
|
||||
|
||||
If you know the list of class names you want to track, use
|
||||
``Doctrine\Persistence\Mapping\Driver\ClassNames``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Persistence\Mapping\Driver\ClassNames;
|
||||
use App\Entity\{Article, Book};
|
||||
|
||||
$entityClasses = [Article::class, Book::class];
|
||||
$classLocator = new ClassNames($entityClasses);
|
||||
|
||||
$driverImpl = new AttributeDriver($classLocator);
|
||||
$config->setMetadataDriverImpl($driverImpl);
|
||||
|
||||
Metadata Cache (**RECOMMENDED**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -222,63 +208,6 @@ Gets or sets the logger to use for logging all SQL statements
|
||||
executed by Doctrine. The logger class must implement the
|
||||
deprecated ``Doctrine\DBAL\Logging\SQLLogger`` interface.
|
||||
|
||||
Auto-generating Proxy Classes (**OPTIONAL**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This setting is not required if you use native lazy objects with PHP 8.4
|
||||
and will be removed in the future.
|
||||
|
||||
Proxy classes can either be generated manually through the Doctrine
|
||||
Console or automatically at runtime by Doctrine. The configuration
|
||||
option that controls this behavior is:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setAutoGenerateProxyClasses($mode);
|
||||
|
||||
Possible values for ``$mode`` are:
|
||||
|
||||
- ``Doctrine\ORM\Proxy\ProxyFactory::AUTOGENERATE_NEVER``
|
||||
|
||||
Never autogenerate a proxy. You will need to generate the proxies
|
||||
manually, for this use the Doctrine Console like so:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine orm:generate-proxies
|
||||
|
||||
When you do this in a development environment,
|
||||
be aware that you may get class/file not found errors if certain proxies
|
||||
are not yet generated. You may also get failing lazy-loads if new
|
||||
methods were added to the entity class that are not yet in the proxy class.
|
||||
In such a case, simply use the Doctrine Console to (re)generate the
|
||||
proxy classes.
|
||||
|
||||
- ``Doctrine\ORM\Proxy\ProxyFactory::AUTOGENERATE_ALWAYS``
|
||||
|
||||
Always generates a new proxy in every request and writes it to disk.
|
||||
|
||||
- ``Doctrine\ORM\Proxy\ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``
|
||||
|
||||
Generate the proxy class when the proxy file does not exist.
|
||||
This strategy causes a file exists call whenever any proxy is
|
||||
used the first time in a request.
|
||||
|
||||
- ``Doctrine\ORM\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.
|
||||
|
||||
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
|
||||
---------------------------------------
|
||||
|
||||
@@ -302,7 +231,7 @@ requests.
|
||||
Connection
|
||||
----------
|
||||
|
||||
The ``$connection`` passed as the first argument to he constructor of
|
||||
The ``$connection`` passed as the first argument to the constructor of
|
||||
``EntityManager`` has to be an instance of ``Doctrine\DBAL\Connection``.
|
||||
You can use the factory ``Doctrine\DBAL\DriverManager::getConnection()``
|
||||
to create such a connection. The DBAL configuration is explained in the
|
||||
@@ -394,55 +323,6 @@ 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\ORM\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
|
||||
-------------------------
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ well.
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Doctrine ORM requires a minimum of PHP 8.1. For greatly improved
|
||||
Doctrine ORM requires a minimum of PHP 8.4. For greatly improved
|
||||
performance it is also recommended that you use APC with PHP.
|
||||
|
||||
Doctrine ORM Packages
|
||||
@@ -79,8 +79,8 @@ 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 nor read-only but
|
||||
it may contain final methods or read-only properties.
|
||||
- An entity class can be final or read-only. It may contain final
|
||||
methods or read-only properties too.
|
||||
- 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
|
||||
@@ -167,7 +167,7 @@ recommended, at least not as long as an entity instance still holds
|
||||
references to proxy objects or is still managed by an EntityManager.
|
||||
By default, serializing proxy objects does not initialize them. On
|
||||
unserialization, resulting objects are detached from the entity
|
||||
manager and cannot be initialiazed anymore. You can implement the
|
||||
manager and cannot be initialized anymore. You can implement the
|
||||
``__serialize()`` method if you want to change that behavior, but
|
||||
then you need to ensure that you won't generate large serialized
|
||||
object graphs and take care of circular associations.
|
||||
|
||||
@@ -175,6 +175,10 @@ Optional parameters:
|
||||
- **unique**: Boolean value to determine if the value of the column
|
||||
should be unique across all rows of the underlying entities table.
|
||||
|
||||
- **index**: Boolean value to generate an index for this column.
|
||||
For more advanced usages, take a look at :ref:`#[Index] <attrref_index>`.
|
||||
If not specified, default value is ``false``.
|
||||
|
||||
- **nullable**: Determines if NULL values allowed for this column.
|
||||
If not specified, default value is ``false``.
|
||||
|
||||
@@ -245,6 +249,9 @@ Examples:
|
||||
#[Column(type: "string", length: 32, unique: true, nullable: false)]
|
||||
protected $username;
|
||||
|
||||
#[Column(type: "string", index: true)]
|
||||
protected $firstName;
|
||||
|
||||
#[Column(type: "string", columnDefinition: "CHAR(2) NOT NULL")]
|
||||
protected $country;
|
||||
|
||||
@@ -661,11 +668,6 @@ and in the Context of a :ref:`#[ManyToMany] <attrref_manytomany>`. If this attri
|
||||
are missing they will be computed considering the field's name and the current
|
||||
:doc:`naming strategy <namingstrategy>`.
|
||||
|
||||
The ``#[InverseJoinColumn]`` is the same as ``#[JoinColumn]`` and is used in the context
|
||||
of a ``#[ManyToMany]`` attribute declaration to specifiy the details of the join table's
|
||||
column information used for the join to the inverse entity. This is only required
|
||||
on PHP 8.0, where nested attributes are not yet supported.
|
||||
|
||||
Optional parameters:
|
||||
|
||||
- **name**: Column name that holds the foreign key identifier for
|
||||
|
||||
@@ -168,7 +168,7 @@ Here is a complete list of ``Column``s attributes (all optional):
|
||||
- ``insertable`` (default: ``true``): Whether the column should be inserted.
|
||||
- ``updatable`` (default: ``true``): Whether the column should be updated.
|
||||
- ``generated`` (default: ``null``): Whether the generated strategy should be ``'NEVER'``, ``'INSERT'`` and ``ALWAYS``.
|
||||
- ``enumType`` (requires PHP 8.1 and ``doctrine/orm`` 2.11): The PHP enum class name to convert the database value into.
|
||||
- ``enumType`` (requires PHP 8.1 and ``doctrine/orm`` 2.11): The PHP enum class name to convert the database value into. See :ref:`reference-enum-mapping`.
|
||||
- ``precision`` (default: 0): The precision for a decimal (exact numeric) column
|
||||
(applies only for decimal column),
|
||||
which is the maximum number of digits that are stored for the values.
|
||||
@@ -182,6 +182,37 @@ Here is a complete list of ``Column``s attributes (all optional):
|
||||
- ``options``: Key-value pairs of options that get passed
|
||||
to the underlying database platform when generating DDL statements.
|
||||
|
||||
Specifying default values
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
While it is possible to specify default values for properties in your
|
||||
PHP class, Doctrine also allows you to specify default values for
|
||||
database columns using the ``default`` key in the ``options`` array of
|
||||
the ``Column`` attribute.
|
||||
|
||||
When using XML, you can specify object instances using the ``<object>``
|
||||
element:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<field name="createdAt" type="datetime" insertable="false" updatable="false">
|
||||
<options>
|
||||
<option name="default">
|
||||
<object class="Doctrine\DBAL\Schema\DefaultExpression\CurrentTimestamp"/>
|
||||
</option>
|
||||
</options>
|
||||
</field>
|
||||
|
||||
The ``<object>`` element requires a ``class`` attribute specifying the
|
||||
fully qualified class name to instantiate.
|
||||
|
||||
.. configuration-block::
|
||||
.. literalinclude:: basic-mapping/DefaultValues.php
|
||||
:language: attribute
|
||||
|
||||
.. literalinclude:: basic-mapping/default-values.xml
|
||||
:language: xml
|
||||
|
||||
.. _reference-php-mapping-types:
|
||||
|
||||
PHP Types Mapping
|
||||
@@ -290,6 +321,160 @@ that value and raw value are different, you have to use the raw value representa
|
||||
$messageRepository = $entityManager->getRepository(Message::class);
|
||||
$deMessages = $messageRepository->findBy(['language' => 'de']); // Use lower case here for raw value representation
|
||||
|
||||
.. _reference-enum-mapping:
|
||||
|
||||
Mapping PHP Enums
|
||||
-----------------
|
||||
|
||||
.. versionadded:: 2.11
|
||||
|
||||
Doctrine natively supports mapping PHP backed enums to database columns.
|
||||
A backed enum is a PHP enum that the same scalar type (``string`` or ``int``)
|
||||
assigned to each case. Doctrine stores the scalar value in the database and
|
||||
converts it back to the enum instance when hydrating the entity.
|
||||
|
||||
Using ``enumType`` provides three main benefits:
|
||||
|
||||
- **Automatic conversion**: Doctrine handles the conversion in both directions
|
||||
transparently. When loading an entity, scalar values from the database are
|
||||
converted into enum instances. When persisting, enum instances are reduced
|
||||
to their scalar ``->value`` before being sent to the database.
|
||||
- **Type-safety**: Entity properties contain enum instances directly. Your
|
||||
getters return ``Suit`` instead of ``string``, removing the need to call
|
||||
``Suit::from()`` manually.
|
||||
- **Validation**: When a database value does not match any enum case, Doctrine
|
||||
throws a ``MappingException`` during hydration instead of silently returning
|
||||
an invalid value.
|
||||
|
||||
This feature works with all database platforms supported by Doctrine (MySQL,
|
||||
PostgreSQL, SQLite, etc.) as it relies on standard column types (``string``,
|
||||
``integer``, ``json``, ``simple_array``) rather than any vendor-specific enum
|
||||
type.
|
||||
|
||||
.. note::
|
||||
|
||||
This is unrelated to the MySQL-specific ``ENUM`` column type covered in
|
||||
:doc:`the MySQL Enums cookbook entry </cookbook/mysql-enums>`.
|
||||
|
||||
Defining an Enum
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. literalinclude:: basic-mapping/Suit.php
|
||||
:language: php
|
||||
|
||||
Only backed enums (``string`` or ``int``) are supported. Unit enums (without
|
||||
a scalar value) cannot be mapped.
|
||||
|
||||
Single-Value Columns
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Use the ``enumType`` option on ``#[Column]`` to map a property to a backed enum.
|
||||
The underlying database column stores the enum's scalar value (``string`` or ``int``).
|
||||
|
||||
.. literalinclude:: basic-mapping/EnumMapping.php
|
||||
:language: php
|
||||
|
||||
When the PHP property is typed with the enum class, Doctrine automatically
|
||||
infers the appropriate column type (``string`` for string-backed enums,
|
||||
``integer`` for int-backed enums) and sets ``enumType``. You can also specify
|
||||
the column ``type`` explicitly.
|
||||
|
||||
Storing Collections of Enums
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can store multiple enum values in a single column by combining ``enumType``
|
||||
with a collection column type: ``json`` or ``simple_array``.
|
||||
|
||||
.. note::
|
||||
|
||||
Automatic type inference does not apply to collection columns. When the
|
||||
PHP property is typed as ``array``, Doctrine cannot detect the enum class.
|
||||
You must specify both ``type`` and ``enumType`` explicitly.
|
||||
|
||||
.. literalinclude:: basic-mapping/EnumCollectionMapping.php
|
||||
:language: php
|
||||
|
||||
With ``json``, the values are stored as a JSON array (e.g. ``["hearts","spades"]``).
|
||||
With ``simple_array``, the values are stored as a comma-separated string
|
||||
(e.g. ``hearts,spades``).
|
||||
|
||||
In both cases, Doctrine converts each element to and from the enum
|
||||
automatically during hydration and persistence.
|
||||
|
||||
.. tip::
|
||||
|
||||
Use ``json`` when enum values may contain commas, when you need to store
|
||||
int-backed enums (as it preserves value types), when the column also
|
||||
stores complex/nested data structures, or when you want to query individual
|
||||
values using database-native JSON operators (e.g. PostgreSQL ``jsonb``).
|
||||
Prefer ``simple_array`` for a compact, human-readable storage of
|
||||
string-backed enums whose values do not contain commas.
|
||||
|
||||
+-------------------+-----------------------------+-------------------------------+
|
||||
| Column type | Database storage | PHP type |
|
||||
+===================+=============================+===============================+
|
||||
| ``string`` | ``hearts`` | ``Suit`` |
|
||||
+-------------------+-----------------------------+-------------------------------+
|
||||
| ``integer`` | ``1`` | ``Priority`` |
|
||||
+-------------------+-----------------------------+-------------------------------+
|
||||
| ``json`` | ``["hearts","spades"]`` | ``array<Suit>`` |
|
||||
+-------------------+-----------------------------+-------------------------------+
|
||||
| ``simple_array`` | ``hearts,spades`` | ``array<Suit>`` |
|
||||
+-------------------+-----------------------------+-------------------------------+
|
||||
|
||||
Nullable Enums
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Enum columns can be nullable. When the database value is ``NULL``, Doctrine
|
||||
preserves it as ``null`` without triggering any validation error.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
#[ORM\Column(type: 'string', nullable: true, enumType: Suit::class)]
|
||||
private Suit|null $suit = null;
|
||||
|
||||
Default Values
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
You can specify a database-level default using an enum case directly in the
|
||||
column options:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
#[ORM\Column(options: ['default' => Suit::Hearts])]
|
||||
public Suit $suit;
|
||||
|
||||
Using Enums in Queries
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Enum instances can be used directly as parameters in DQL, QueryBuilder, and
|
||||
repository methods. Doctrine converts them to their scalar value automatically.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// QueryBuilder
|
||||
$qb = $em->createQueryBuilder();
|
||||
$qb->select('c')
|
||||
->from(Card::class, 'c')
|
||||
->where('c.suit = :suit')
|
||||
->setParameter('suit', Suit::Clubs);
|
||||
|
||||
// Repository
|
||||
$cards = $em->getRepository(Card::class)->findBy(['suit' => Suit::Clubs]);
|
||||
|
||||
XML Mapping
|
||||
~~~~~~~~~~~
|
||||
|
||||
When using XML mapping, the ``enum-type`` attribute is used on ``<field>``
|
||||
elements:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<field name="suit" type="string" enum-type="App\Entity\Suit" />
|
||||
|
||||
.. _reference-mapping-types:
|
||||
|
||||
Doctrine Mapping Types
|
||||
@@ -389,17 +574,19 @@ Here is the list of possible generation strategies:
|
||||
|
||||
- ``AUTO`` (default): Tells Doctrine to pick the strategy that is
|
||||
preferred by the used database platform. The preferred strategies
|
||||
are ``IDENTITY`` for MySQL, SQLite, MsSQL and SQL Anywhere and, for
|
||||
historical reasons, ``SEQUENCE`` for Oracle and PostgreSQL. This
|
||||
strategy provides full portability.
|
||||
are ``IDENTITY`` for MySQL, SQLite, MsSQL, SQL Anywhere and
|
||||
PostgreSQL (on DBAL 4) and, for historical reasons, ``SEQUENCE``
|
||||
for Oracle and PostgreSQL (on DBAL 3). This strategy provides
|
||||
full portability.
|
||||
- ``IDENTITY``: Tells Doctrine to use special identity columns in
|
||||
the database that generate a value on insertion of a row. This
|
||||
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``).
|
||||
(``AUTO_INCREMENT``), MSSQL (``IDENTITY``) and PostgreSQL (``SERIAL``
|
||||
on DBAL 3, ``GENERATED BY DEFAULT AS IDENTITY`` on DBAL 4).
|
||||
- ``SEQUENCE``: Tells Doctrine to use a database sequence for ID
|
||||
generation. This strategy does currently not provide full
|
||||
portability. Sequences are supported by Oracle, PostgreSql and
|
||||
portability. Sequences are supported by Oracle, PostgreSQL and
|
||||
SQL Anywhere.
|
||||
- ``NONE``: Tells Doctrine that the identifiers are assigned (and
|
||||
thus generated) by your code. The assignment must take place before
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use DateTime;
|
||||
use Doctrine\DBAL\Schema\DefaultExpression\CurrentTimestamp;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
|
||||
#[Entity]
|
||||
class Message
|
||||
{
|
||||
#[Column(options: ['default' => 'Hello World!'])]
|
||||
private string $text;
|
||||
|
||||
#[Column(options: ['default' => new CurrentTimestamp()], insertable: false, updatable: false)]
|
||||
private DateTime $createdAt;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity]
|
||||
class Player
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private int $id;
|
||||
|
||||
/** @var list<Suit> */
|
||||
#[ORM\Column(type: 'json', enumType: Suit::class)]
|
||||
private array $favouriteSuits = [];
|
||||
|
||||
/** @var list<Suit> */
|
||||
#[ORM\Column(type: 'simple_array', enumType: Suit::class)]
|
||||
private array $allowedSuits = [];
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity]
|
||||
class Card
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private int $id;
|
||||
|
||||
#[ORM\Column(enumType: Suit::class)]
|
||||
private Suit $suit;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
enum Suit: string
|
||||
{
|
||||
case Hearts = 'hearts';
|
||||
case Diamonds = 'diamonds';
|
||||
case Clubs = 'clubs';
|
||||
case Spades = 'spades';
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<doctrine-mapping>
|
||||
<entity name="Message">
|
||||
<field name="text">
|
||||
<options>
|
||||
<option name="default">Hello World!</option>
|
||||
</options>
|
||||
</field>
|
||||
<field name="createdAt" insertable="false" updatable="false">
|
||||
<options>
|
||||
<option name="default">
|
||||
<object class="Doctrine\DBAL\Schema\DefaultExpression\CurrentTimestamp"/>
|
||||
</option>
|
||||
</options>
|
||||
</field>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@@ -56,7 +56,8 @@ access point to ORM functionality provided by Doctrine.
|
||||
'dbname' => 'foo',
|
||||
];
|
||||
|
||||
$config = ORMSetup::createAttributeMetadataConfiguration($paths, $isDevMode);
|
||||
$config = ORMSetup::createAttributeMetadataConfig($paths, $isDevMode);
|
||||
// on PHP < 8.4, use ORMSetup::createAttributeMetadataConfiguration() instead
|
||||
$connection = DriverManager::getConnection($dbParams, $config);
|
||||
$entityManager = new EntityManager($connection, $config);
|
||||
|
||||
@@ -66,7 +67,8 @@ Or if you prefer XML:
|
||||
|
||||
<?php
|
||||
$paths = ['/path/to/xml-mappings'];
|
||||
$config = ORMSetup::createXMLMetadataConfiguration($paths, $isDevMode);
|
||||
$config = ORMSetup::createXMLMetadataConfig($paths, $isDevMode);
|
||||
// on PHP < 8.4, use ORMSetup::createXMLMetadataConfiguration() instead
|
||||
$connection = DriverManager::getConnection($dbParams, $config);
|
||||
$entityManager = new EntityManager($connection, $config);
|
||||
|
||||
|
||||
@@ -490,7 +490,7 @@ 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');
|
||||
$query = $em->createQuery('SELECT u FROM User u JOIN Banlist b ON 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``
|
||||
@@ -513,13 +513,15 @@ it loads all the related ``Banlist`` objects corresponding to this ``User``. Thi
|
||||
when the DQL is switched to an arbitrary join.
|
||||
|
||||
.. note::
|
||||
The differences between WHERE, WITH and HAVING clauses may be
|
||||
The differences between WHERE, WITH, ON and HAVING clauses may be
|
||||
confusing.
|
||||
|
||||
- WHERE is applied to the results of an entire query
|
||||
- WITH is applied to a join as an additional condition. For
|
||||
arbitrary joins (SELECT f, b FROM Foo f, Bar b WITH f.id = b.id)
|
||||
the WITH is required, even if it is 1 = 1
|
||||
- ON is applied to arbitrary joins as the join condition. For
|
||||
arbitrary joins (SELECT f, b FROM Foo f, Bar b ON f.id = b.id)
|
||||
the ON is required, even if it is 1 = 1. WITH is also
|
||||
supported as alternative keyword for that case for BC reasons.
|
||||
- WITH is applied to an association join as an additional condition.
|
||||
- HAVING is applied to the results of a query after
|
||||
aggregation (GROUP BY)
|
||||
|
||||
@@ -1409,8 +1411,7 @@ Result Cache API:
|
||||
|
||||
$query->setResultCacheDriver(new ApcCache());
|
||||
|
||||
$query->useResultCache(true)
|
||||
->setResultCacheLifeTime(3600);
|
||||
$query->enableResultCache(3600);
|
||||
|
||||
$result = $query->getResult(); // cache miss
|
||||
|
||||
@@ -1420,8 +1421,8 @@ Result Cache API:
|
||||
$query->setResultCacheId('my_query_result');
|
||||
$result = $query->getResult(); // saved in given result cache id.
|
||||
|
||||
// or call useResultCache() with all parameters:
|
||||
$query->useResultCache(true, 3600, 'my_query_result');
|
||||
// or call enableResultCache() with all parameters:
|
||||
$query->enableResultCache(3600, 'my_query_result');
|
||||
$result = $query->getResult(); // cache hit!
|
||||
|
||||
// Introspection
|
||||
@@ -1699,9 +1700,14 @@ From, Join and Index by
|
||||
SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration
|
||||
RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
|
||||
JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy]
|
||||
Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" (JoinAssociationDeclaration | RangeVariableDeclaration) ["WITH" ConditionalExpression]
|
||||
Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" (JoinAssociationDeclaration ["WITH" ConditionalExpression] | RangeVariableDeclaration [("ON" | "WITH") ConditionalExpression])
|
||||
IndexBy ::= "INDEX" "BY" SingleValuedPathExpression
|
||||
|
||||
.. note::
|
||||
Using the ``WITH`` keyword for the ``ConditionalExpression`` of a
|
||||
``RangeVariableDeclaration`` is deprecated and will be removed in
|
||||
ORM 4.0. Use the ``ON`` keyword instead.
|
||||
|
||||
Select Expressions
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -18,30 +18,6 @@ In your mapping configuration, the column definition (for example, the
|
||||
the ``charset`` and ``collation``. The default values are ``utf8`` and
|
||||
``utf8_unicode_ci``, respectively.
|
||||
|
||||
Entity Classes
|
||||
--------------
|
||||
|
||||
How can I add default values to a column?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Doctrine does not support to set the default values in columns through the "DEFAULT" keyword in SQL.
|
||||
This is not necessary however, you can just use your class properties as default values. These are then used
|
||||
upon insert:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
class User
|
||||
{
|
||||
private const STATUS_DISABLED = 0;
|
||||
private const STATUS_ENABLED = 1;
|
||||
|
||||
private string $algorithm = "sha1";
|
||||
/** @var self::STATUS_* */
|
||||
private int $status = self::STATUS_DISABLED;
|
||||
}
|
||||
|
||||
.
|
||||
|
||||
Mapping
|
||||
-------
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ table alias of the SQL table of the entity.
|
||||
|
||||
For the filter to correctly function, the following rules must be followed. Failure to do so will lead to unexpected results from the query cache.
|
||||
1. Parameters for the query should be set on the filter object by ``SQLFilter#setParameter()`` before the filter is used by the ORM ( i.e. do not set parameters inside ``SQLFilter#addFilterConstraint()`` function ).
|
||||
2. The filter must be deterministic. Don't change the values base on external inputs.
|
||||
2. The filter must be deterministic. Don't change the values based on external inputs.
|
||||
|
||||
The ``SQLFilter#getParameter()`` function takes care of the proper quoting of parameters.
|
||||
|
||||
|
||||
@@ -208,6 +208,22 @@ Example:
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="MyProject\Model\Person" inheritance-type="SINGLE_TABLE">
|
||||
<discriminator-column name="discr" type="string" />
|
||||
<discriminator-map>
|
||||
<discriminator-mapping value="person" class="MyProject\Model\Person"/>
|
||||
<discriminator-mapping value="employee" class="MyProject\Model\Employee"/>
|
||||
</discriminator-map>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="MyProject\Model\Employee">
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
In this example, the ``#[DiscriminatorMap]`` specifies that in the
|
||||
discriminator column, a value of "person" identifies a row as being of type
|
||||
|
||||
@@ -178,6 +178,14 @@ internally by the ORM currently refers to fields by their name only, without tak
|
||||
class containing the field into consideration. This makes it impossible to keep separate
|
||||
mapping configuration for both fields.
|
||||
|
||||
Apart from that, in the case of having multiple ``private`` fields of the same name within
|
||||
the class hierarchy an entity or mapped superclass, the Collection filtering API cannot determine
|
||||
the right field to look at. Even if only one of these fields is actually mapped, the ``ArrayCollection``
|
||||
will not be able to tell, since it does not have access to any metadata.
|
||||
|
||||
Thus, to avoid problems in this regard, it is best to avoid having multiple ``private`` fields of the
|
||||
same name in class hierarchies containing entity and mapped superclasses.
|
||||
|
||||
Known Issues
|
||||
------------
|
||||
|
||||
|
||||
@@ -209,6 +209,7 @@ Field & Association Getters
|
||||
|
||||
- ``isUniqueField($fieldName)``
|
||||
- ``isNullable($fieldName)``
|
||||
- ``isIndexed($fieldName)``
|
||||
- ``getColumnName($fieldName)``
|
||||
- ``getFieldMapping($fieldName)``
|
||||
- ``getAssociationMapping($fieldName)``
|
||||
|
||||
@@ -344,10 +344,10 @@ the Query object which can be retrieved from ``EntityManager#createQuery()``.
|
||||
Executing a Query
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The QueryBuilder is a builder object only - it has no means of actually
|
||||
executing the Query. Additionally a set of parameters such as query hints
|
||||
cannot be set on the QueryBuilder itself. This is why you always have to convert
|
||||
a querybuilder instance into a Query object:
|
||||
The QueryBuilder is only a builder object - it has no means of actually
|
||||
executing the Query. Additional functionality, such as enabling the result cache,
|
||||
cannot be set on the QueryBuilder itself. This is why you must always convert
|
||||
a QueryBuilder instance into a Query object:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -355,9 +355,8 @@ a querybuilder instance into a Query object:
|
||||
// $qb instanceof QueryBuilder
|
||||
$query = $qb->getQuery();
|
||||
|
||||
// Set additional Query options
|
||||
$query->setQueryHint('foo', 'bar');
|
||||
$query->useResultCache('my_cache_id');
|
||||
// Enable the result cache
|
||||
$query->enableResultCache(3600, 'my_custom_id');
|
||||
|
||||
// Execute Query
|
||||
$result = $query->getResult();
|
||||
@@ -555,6 +554,24 @@ using ``addCriteria``:
|
||||
$qb->addCriteria($criteria);
|
||||
// then execute your query like normal
|
||||
|
||||
Adding hints to a Query
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can also set query hints to a QueryBuilder by using ``setHint``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// ...
|
||||
|
||||
// $qb instanceof QueryBuilder
|
||||
$qb->setHint('hintName', 'hintValue');
|
||||
// then execute your query like normal
|
||||
|
||||
The query hint can hold anything the usual query hints can hold
|
||||
except null. Those hints will be applied to the query when the
|
||||
query is created.
|
||||
|
||||
Low Level API
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ Something like below for an entity region:
|
||||
|
||||
|
||||
If the entity holds a collection that also needs to be cached.
|
||||
An collection region could look something like:
|
||||
A collection region could look something like:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -133,7 +133,7 @@ Caching mode
|
||||
* Read Write cache employs locks before update/delete.
|
||||
* Use if data needs to be updated.
|
||||
* Slowest strategy.
|
||||
* To use it a the cache region implementation must support locking.
|
||||
* To use it the cache region implementation must support locking.
|
||||
|
||||
|
||||
Built-in cached persisters
|
||||
@@ -518,7 +518,7 @@ DELETE / UPDATE queries
|
||||
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.
|
||||
However the cached data could be evicted using the cache API or a special query hint.
|
||||
|
||||
|
||||
Execute the ``UPDATE`` and invalidate ``all cache entries`` using ``Query::HINT_CACHE_EVICT``
|
||||
|
||||
@@ -118,7 +118,7 @@ entity might look like this:
|
||||
}
|
||||
}
|
||||
|
||||
Now the possiblity of mass-assignment exists on this entity and can
|
||||
Now the possibility of mass-assignment exists on this entity and can
|
||||
be exploited by attackers to set the "isAdmin" flag to true on any
|
||||
object when you pass the whole request data to this method like:
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ have to register them yourself.
|
||||
All the commands of the Doctrine Console require access to the
|
||||
``EntityManager``. You have to inject it into the console application.
|
||||
|
||||
Here is an example of a the project-specific ``bin/doctrine`` binary.
|
||||
Here is an example of a project-specific ``bin/doctrine`` binary.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -83,8 +83,6 @@ The following Commands are currently available:
|
||||
cache drivers.
|
||||
- ``orm:clear-cache:result`` Clear result cache of the various
|
||||
cache drivers.
|
||||
- ``orm:generate-proxies`` Generates proxy classes for entity
|
||||
classes.
|
||||
- ``orm:run-dql`` Executes arbitrary DQL directly from the command
|
||||
line.
|
||||
- ``orm:schema-tool:create`` Processes the schema and either
|
||||
@@ -96,6 +94,10 @@ The following Commands are currently available:
|
||||
- ``orm:schema-tool:update`` Processes the schema and either
|
||||
update the database schema of EntityManager Storage Connection or
|
||||
generate the SQL output.
|
||||
- ``orm:debug:event-manager`` Lists event listeners for an entity
|
||||
manager, optionally filtered by event name.
|
||||
- ``orm:debug:entity-listeners`` Lists entity listeners for a given
|
||||
entity, optionally filtered by event name.
|
||||
|
||||
The following alias is defined:
|
||||
|
||||
|
||||
@@ -112,7 +112,6 @@ of several common elements:
|
||||
|
||||
<indexes>
|
||||
<index name="name_idx" columns="name"/>
|
||||
<index columns="user_email"/>
|
||||
</indexes>
|
||||
|
||||
<unique-constraints>
|
||||
@@ -131,7 +130,7 @@ of several common elements:
|
||||
</id>
|
||||
|
||||
<field name="name" column="name" type="string" length="50" nullable="true" unique="true" />
|
||||
<field name="email" column="user_email" type="string" column-definition="CHAR(32) NOT NULL" />
|
||||
<field name="email" column="user_email" type="string" index="true" column-definition="CHAR(32) NOT NULL" />
|
||||
|
||||
<one-to-one field="address" target-entity="Address" inversed-by="user">
|
||||
<cascade><cascade-remove /></cascade>
|
||||
@@ -255,6 +254,8 @@ Optional attributes:
|
||||
only.
|
||||
- unique - Should this field contain a unique value across the
|
||||
table? Defaults to false.
|
||||
- index - Should an index be created for this column? Defaults to
|
||||
false.
|
||||
- nullable - Should this field allow NULL as a value? Defaults to
|
||||
false.
|
||||
- insertable - Should this field be inserted? Defaults to true.
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
:depth: 3
|
||||
|
||||
tutorials/getting-started
|
||||
tutorials/getting-started-database
|
||||
tutorials/getting-started-models
|
||||
tutorials/working-with-indexed-associations
|
||||
tutorials/extra-lazy-associations
|
||||
tutorials/composite-primary-keys
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
Getting Started: Database First
|
||||
===============================
|
||||
|
||||
.. note:: *Development Workflows*
|
||||
|
||||
When you :doc:`Code First <getting-started>`, you
|
||||
start with developing Objects and then map them onto your database. When
|
||||
you :doc:`Model First <getting-started-models>`, you are modelling your application using tools (for
|
||||
example UML) and generate database schema and PHP code from this model.
|
||||
When you have a Database First, you already have a database schema
|
||||
and generate the corresponding PHP code from it.
|
||||
|
||||
.. note::
|
||||
|
||||
This getting started guide is in development.
|
||||
|
||||
Development of new applications often starts with an existing database schema.
|
||||
When the database schema is the starting point for your application, then
|
||||
development is said to use the *Database First* approach to Doctrine.
|
||||
|
||||
In this workflow you would modify the database schema first and then
|
||||
regenerate the PHP code to use with this schema. You need a flexible
|
||||
code-generator for this task.
|
||||
|
||||
We spun off a subproject, Doctrine CodeGenerator, that will fill this gap and
|
||||
allow you to do *Database First* development.
|
||||
@@ -1,24 +0,0 @@
|
||||
Getting Started: Model First
|
||||
============================
|
||||
|
||||
.. note:: *Development Workflows*
|
||||
|
||||
When you :doc:`Code First <getting-started>`, you
|
||||
start with developing Objects and then map them onto your database. When
|
||||
you Model First, you are modelling your application using tools (for
|
||||
example UML) and generate database schema and PHP code from this model.
|
||||
When you have a :doc:`Database First <getting-started-database>`, then you already have a database schema
|
||||
and generate the corresponding PHP code from it.
|
||||
|
||||
.. note::
|
||||
|
||||
This getting started guide is in development.
|
||||
|
||||
There are applications when you start with a high-level description of the
|
||||
model using modelling tools such as UML. Modelling tools could also be Excel,
|
||||
XML or CSV files that describe the model in some structured way. If your
|
||||
application is using a modelling tool, then the development workflow is said to
|
||||
be a *Model First* approach to Doctrine2.
|
||||
|
||||
In this workflow you always change the model description and then regenerate
|
||||
both PHP code and database schema from this model.
|
||||
@@ -49,8 +49,7 @@ An entity contains persistable properties. A persistable property
|
||||
is an instance variable of the entity that is saved into and retrieved from the database
|
||||
by Doctrine's data mapping capabilities.
|
||||
|
||||
An entity class must not be final nor read-only, although
|
||||
it can contain final methods or read-only properties.
|
||||
An entity class can be final or read-only. It may contain final methods or read-only properties too.
|
||||
|
||||
An Example Model: Bug Tracker
|
||||
-----------------------------
|
||||
@@ -138,12 +137,12 @@ step:
|
||||
require_once "vendor/autoload.php";
|
||||
|
||||
// Create a simple "default" Doctrine ORM configuration for Attributes
|
||||
$config = ORMSetup::createAttributeMetadataConfiguration(
|
||||
$config = ORMSetup::createAttributeMetadataConfig( // on PHP < 8.4, use ORMSetup::createAttributeMetadataConfiguration()
|
||||
paths: [__DIR__ . '/src'],
|
||||
isDevMode: true,
|
||||
);
|
||||
// or if you prefer XML
|
||||
// $config = ORMSetup::createXMLMetadataConfiguration(
|
||||
// $config = ORMSetup::createXMLMetadataConfig( // on PHP < 8.4, use ORMSetup::createXMLMetadataConfiguration()
|
||||
// paths: [__DIR__ . '/config/xml'],
|
||||
// isDevMode: true,
|
||||
//);
|
||||
@@ -534,7 +533,7 @@ the ``id`` tag. It has a ``generator`` tag nested inside, which
|
||||
specifies that the primary key generation mechanism should automatically
|
||||
use the database platform's native id generation strategy (for
|
||||
example, AUTO INCREMENT in the case of MySql, or Sequences in the
|
||||
case of PostgreSql and Oracle).
|
||||
case of PostgreSQL and Oracle).
|
||||
|
||||
Now that we have defined our first entity and its metadata,
|
||||
let's update the database schema:
|
||||
@@ -1287,7 +1286,7 @@ The console output of this script is then:
|
||||
result set to retrieve entities from the database. DQL boils down to a
|
||||
Native SQL statement and a ``ResultSetMapping`` instance itself. Using
|
||||
Native SQL you could even use stored procedures for data retrieval, or
|
||||
make use of advanced non-portable database queries like PostgreSql's
|
||||
make use of advanced non-portable database queries like PostgreSQL's
|
||||
recursive queries.
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,41 @@
|
||||
Pagination
|
||||
==========
|
||||
|
||||
Doctrine ORM provides two pagination strategies for DQL queries. Both handle
|
||||
the low-level SQL plumbing, but they make different trade-offs:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Feature
|
||||
- Offset ``Paginator``
|
||||
- ``CursorPaginator``
|
||||
* - Total count
|
||||
- Yes
|
||||
- No
|
||||
* - Random access to page N
|
||||
- Yes
|
||||
- No
|
||||
* - Stable under concurrent inserts/deletes
|
||||
- No
|
||||
- Yes
|
||||
* - Performance on deep pages
|
||||
- Degrades (OFFSET scan)
|
||||
- Constant (index range scan)
|
||||
* - Requires deterministic ORDER BY
|
||||
- No
|
||||
- Yes
|
||||
|
||||
Choose the **Offset Paginator** when you need a total page count or want to
|
||||
let users jump to an arbitrary page number.
|
||||
|
||||
Choose the **Cursor Paginator** when you need stable, high-performance
|
||||
pagination on large datasets and a simple previous/next navigation is
|
||||
sufficient.
|
||||
|
||||
Offset-Based Pagination
|
||||
-----------------------
|
||||
|
||||
Doctrine ORM ships with a Paginator for DQL queries. It
|
||||
has a very simple API and implements the SPL interfaces ``Countable`` and
|
||||
``IteratorAggregate``.
|
||||
@@ -58,3 +93,178 @@ In this way the `DISTINCT` keyword will be omitted and can bring important perfo
|
||||
->setHint(Paginator::HINT_ENABLE_DISTINCT, false)
|
||||
->setFirstResult(0)
|
||||
->setMaxResults(100);
|
||||
|
||||
Cursor-Based Pagination
|
||||
-----------------------
|
||||
|
||||
Doctrine ORM ships with a ``CursorPaginator`` for cursor-based pagination of DQL queries.
|
||||
Unlike offset-based pagination, cursor pagination uses opaque pointers (cursors) derived
|
||||
from the last seen row to fetch the next or previous page. This makes it stable and
|
||||
performant on large datasets — no matter how deep you paginate, the database always uses
|
||||
an index range scan instead of skipping rows.
|
||||
|
||||
.. note::
|
||||
|
||||
Cursor pagination requires a **deterministic ``ORDER BY`` clause**. Every column
|
||||
combination used for sorting must uniquely identify a position in the result set.
|
||||
A common pattern is to sort by a timestamp and then by primary key as a tie-breaker.
|
||||
|
||||
Basic Usage
|
||||
~~~~~~~~~~~
|
||||
|
||||
The ``$cursor`` parameter is an opaque string produced by a previous call to
|
||||
``getNextCursorAsString()`` or ``getPreviousCursorAsString()``. On the first request
|
||||
it is ``null`` or an empty string ``''`` — both are treated identically as the first
|
||||
page. It is typically read from the incoming HTTP query string:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$cursor = $_GET['cursor'] ?? null; // null or '' on the first page
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Tools\CursorPagination\CursorPaginator;
|
||||
|
||||
$dql = 'SELECT p FROM BlogPost p ORDER BY p.createdAt DESC, p.id DESC';
|
||||
$query = $entityManager->createQuery($dql);
|
||||
|
||||
$paginator = (new CursorPaginator($query))
|
||||
->paginate(cursor: $cursor, limit: 15);
|
||||
|
||||
foreach ($paginator as $post) {
|
||||
echo $post->getTitle() . "\n";
|
||||
}
|
||||
|
||||
echo $paginator->getPreviousCursorAsString(); // previous encoded cursor string
|
||||
echo $paginator->getNextCursorAsString(); // next encoded cursor string
|
||||
|
||||
Navigating Pages
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Pass the encoded cursor back on subsequent requests to move forward or backward:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// Next page
|
||||
$paginator->paginate(15, $nextCursor);
|
||||
|
||||
// Previous page
|
||||
$paginator->paginate(15, $previousCursor);
|
||||
|
||||
The cursor is an encoded string containing the location at which the next query should begin fetching results, along with the navigation direction.
|
||||
|
||||
API Reference
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
``CursorPaginator::paginate(?string $cursor, int $limit): self``
|
||||
Executes the query and stores the results. Fetches ``$limit + 1`` rows to
|
||||
detect whether a further page exists, then trims the extra row. Returns
|
||||
``$this`` for chaining.
|
||||
|
||||
``CursorPaginator::getNextCursor(): Cursor``
|
||||
Returns the ``Cursor`` object for the next page. Throws a ``LogicException``
|
||||
if there is no next page — call ``hasNextPage()`` first.
|
||||
|
||||
``CursorPaginator::getPreviousCursor(): Cursor``
|
||||
Returns the ``Cursor`` object for the previous page. Throws a ``LogicException``
|
||||
if there is no previous page — call ``hasPreviousPage()`` first.
|
||||
|
||||
``CursorPaginator::getNextCursorAsString(): string``
|
||||
Returns the encoded cursor to retrieve the next page. Throws a
|
||||
``LogicException`` if there is no next page — call ``hasNextPage()`` first.
|
||||
|
||||
``CursorPaginator::getPreviousCursorAsString(): string``
|
||||
Returns the encoded cursor to retrieve the previous page. Throws a
|
||||
``LogicException`` if there is no previous page — call ``hasPreviousPage()`` first.
|
||||
|
||||
``CursorPaginator::hasNextPage(): bool``
|
||||
Returns whether a next page is available.
|
||||
|
||||
``CursorPaginator::hasPreviousPage(): bool``
|
||||
Returns whether a previous page is available.
|
||||
|
||||
``CursorPaginator::hasToPaginate(): bool``
|
||||
Returns whether either a next or previous page exists (i.e. the result
|
||||
set spans more than one page).
|
||||
|
||||
``CursorPaginator::getValues(): array``
|
||||
Returns the raw entity array for the current page.
|
||||
|
||||
``CursorPaginator::getItems(): array``
|
||||
Returns an array of ``CursorItem`` objects, each wrapping an entity and its
|
||||
individual ``Cursor``. Useful when you need per-row cursors.
|
||||
|
||||
``CursorPaginator::getCursorForItem(mixed $item, bool $isNext = true): Cursor``
|
||||
Builds a ``Cursor`` pointing at a specific entity. ``$isNext = true`` means
|
||||
"start *after* this item"; ``false`` means "start *before* this item".
|
||||
|
||||
``CursorPaginator::count(): int``
|
||||
Returns the number of items on the current page (implements ``Countable``).
|
||||
|
||||
**Next page**
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT ...
|
||||
FROM post p
|
||||
WHERE (p.created_at < :cursor_val_0)
|
||||
OR (p.created_at = :cursor_val_0 AND p.id < :cursor_id_1)
|
||||
ORDER BY p.created_at DESC, p.id DESC
|
||||
LIMIT 16 -- limit + 1
|
||||
|
||||
**Previous page**
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT ...
|
||||
FROM post p
|
||||
WHERE (p.created_at > :cursor_val_0)
|
||||
OR (p.created_at = :cursor_val_0 AND p.id > :cursor_id_1)
|
||||
ORDER BY p.created_at ASC, p.id ASC -- reversed
|
||||
LIMIT 16
|
||||
|
||||
HTML Template Example
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following example shows how to render a paginated list with previous/next
|
||||
navigation links using the ``CursorPaginator`` in a PHP template:
|
||||
|
||||
.. literalinclude:: pagination/cursor-pagination.php
|
||||
:language: php
|
||||
|
||||
Cursor Encoding
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
A cursor is serialized to a URL-safe string via ``Cursor::encodeToString()`` and
|
||||
deserialized back via the static ``Cursor::fromEncodedString()``. The format is a
|
||||
JSON object encoded with URL-safe Base64 (no padding):
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"p.createdAt": "2024-01-15T10:30:00+00:00",
|
||||
"p.id": 42,
|
||||
"_isNext": true
|
||||
}
|
||||
|
||||
The ``_isNext`` flag distinguishes next-page cursors from previous-page cursors.
|
||||
All other keys are the DQL path expressions (``alias.field``) of the ``ORDER BY``
|
||||
columns, and their values are the database representations of the pivot row's
|
||||
field values.
|
||||
|
||||
If you need a different serialization format (e.g. encryption), build it on top of
|
||||
a ``Cursor`` instance: call ``$cursor->toArray()`` to get the raw data, apply your
|
||||
own encoding, and reconstruct with ``new Cursor($parameters, $isNext)``.
|
||||
|
||||
Limitations
|
||||
~~~~~~~~~~~
|
||||
|
||||
- Every ``ORDER BY`` column must map to an entity field. Raw SQL expressions or
|
||||
computed columns in ``ORDER BY`` are not supported.
|
||||
- ``COUNT`` queries are not available; cursor pagination does not know the total
|
||||
number of results by design. If you need a total count, use the
|
||||
offset-based ``Paginator`` described above.
|
||||
- The query must have at least one ``ORDER BY`` item; the paginator throws a
|
||||
``LogicException`` otherwise.
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Tools\CursorPagination\CursorPaginator;
|
||||
|
||||
$cursor = $_GET['cursor'] ?? null;
|
||||
|
||||
$query = $entityManager->createQuery('SELECT p FROM BlogPost p ORDER BY p.createdAt DESC, p.id DESC');
|
||||
|
||||
/** @var CursorPaginator<BlogPost> $paginator */
|
||||
$paginator = (new CursorPaginator($query))
|
||||
->paginate(cursor: $cursor, limit: 15);
|
||||
?>
|
||||
<p><?= $paginator->count() ?> result(s) on this page.</p>
|
||||
|
||||
<ul>
|
||||
<?php foreach ($paginator as $post): ?>
|
||||
<li><?= escape($post->getTitle()) ?></li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
|
||||
<?php if ($paginator->hasToPaginate()): ?>
|
||||
<nav>
|
||||
<?php if ($paginator->hasPreviousPage()): ?>
|
||||
<a href="?cursor=<?= escape($paginator->getPreviousCursorAsString()) ?>">Previous</a>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($paginator->hasNextPage()): ?>
|
||||
<a href="?cursor=<?= escape($paginator->getNextCursorAsString()) ?>">Next</a>
|
||||
<?php endif ?>
|
||||
</nav>
|
||||
<?php endif ?>
|
||||
+14
-3
@@ -155,10 +155,20 @@
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="object">
|
||||
<xs:attribute name="class" type="xs:string" use="required"/>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="option" mixed="true">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="option" type="orm:option"/>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
<xs:choice minOccurs="0" maxOccurs="1">
|
||||
<xs:element name="object" type="orm:object"/>
|
||||
<xs:sequence>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="option" type="orm:option"/>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:choice>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required"/>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
@@ -243,6 +253,7 @@
|
||||
<xs:attribute name="length" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="unique" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="nullable" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="index" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="insertable" type="xs:boolean" default="true" />
|
||||
<xs:attribute name="updatable" type="xs:boolean" default="true" />
|
||||
<xs:attribute name="generated" type="orm:generated-type" default="NEVER" />
|
||||
|
||||
+1
-9
@@ -11,7 +11,7 @@
|
||||
<!-- Ignore warnings, show progress of the run and show sniff names -->
|
||||
<arg value="nps"/>
|
||||
|
||||
<config name="php_version" value="80100"/>
|
||||
<config name="php_version" value="80400"/>
|
||||
|
||||
<file>src</file>
|
||||
<file>tests</file>
|
||||
@@ -50,8 +50,6 @@
|
||||
</rule>
|
||||
|
||||
<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
|
||||
<exclude-pattern>src/Mapping/Driver/LoadMappingFileImplementation.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/GetReflectionClassImplementation.php</exclude-pattern>
|
||||
<exclude-pattern>tests/*</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
@@ -210,12 +208,6 @@
|
||||
<exclude-pattern>tests/Tests/Models/DDC1590/DDC1590User.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
|
||||
<rule ref="Squiz.Classes.ValidClassName.NotCamelCaps">
|
||||
<!-- we need to test what happens with an stdClass proxy -->
|
||||
<exclude-pattern>tests/Tests/Proxy/DefaultProxyClassNameResolverTest.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="Squiz.Commenting.FunctionComment.WrongStyle">
|
||||
<!-- https://github.com/squizlabs/PHP_CodeSniffer/issues/1961 -->
|
||||
<exclude-pattern>tests/Tests/Mocks/DatabasePlatformMock.php</exclude-pattern>
|
||||
|
||||
+178
-505
@@ -1,17 +1,5 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: '#^Expression "\$setCacheEntry\(\$data\)" on a separate line does not do anything\.$#'
|
||||
identifier: expr.resultUnused
|
||||
count: 1
|
||||
path: src/AbstractQuery.php
|
||||
|
||||
-
|
||||
message: '#^Expression "\$setCacheEntry\(\$stmt\)" on a separate line does not do anything\.$#'
|
||||
identifier: expr.resultUnused
|
||||
count: 1
|
||||
path: src/AbstractQuery.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\AbstractQuery\:\:getParameter\(\) should return Doctrine\\ORM\\Query\\Parameter\|null but returns Doctrine\\ORM\\Query\\Parameter\|false\|null\.$#'
|
||||
identifier: return.type
|
||||
@@ -234,12 +222,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Cache/DefaultQueryCache.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$result of class Doctrine\\ORM\\Cache\\QueryCacheEntry constructor expects array\<string, mixed\>, array\<int\|string, array\<string, array\<mixed\>\>\> given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Cache/DefaultQueryCache.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$key of method Doctrine\\ORM\\Cache\\Logging\\CacheLogger\:\:entityCacheHit\(\) expects Doctrine\\ORM\\Cache\\EntityCacheKey, Doctrine\\ORM\\Cache\\CacheKey given\.$#'
|
||||
identifier: argument.type
|
||||
@@ -516,6 +498,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Cache/TimestampQueryCacheValidator.php
|
||||
|
||||
-
|
||||
message: '#^Call to function is_a\(\) with arguments class\-string\<Doctrine\\ORM\\EntityRepository\>, ''Doctrine\\\\ORM\\\\EntityRepository'' and true will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 1
|
||||
path: src/Configuration.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Configuration\:\:getDefaultRepositoryClassName\(\) return type with generic class Doctrine\\ORM\\EntityRepository does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -558,12 +546,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Decorator/EntityManagerDecorator.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method object\:\:setEntityManager\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
count: 1
|
||||
path: src/EntityManager.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\EntityManager\:\:checkLockRequirements\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -637,7 +619,7 @@ parameters:
|
||||
path: src/EntityRepository.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\EntityRepository\:\:matching\(\) should return Doctrine\\Common\\Collections\\AbstractLazyCollection\<int, T of object\>&Doctrine\\Common\\Collections\\Selectable\<int, T of object\> but returns Doctrine\\ORM\\LazyCriteriaCollection\<\(int\|string\), object\>\.$#'
|
||||
message: '#^Method Doctrine\\ORM\\EntityRepository\:\:matching\(\) should return Doctrine\\Common\\Collections\\AbstractLazyCollection\<int, T of object\> but returns Doctrine\\ORM\\LazyCriteriaCollection\<\(int\|string\), object\>\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/EntityRepository.php
|
||||
@@ -733,13 +715,13 @@ parameters:
|
||||
path: src/Internal/Hydration/AbstractHydrator.php
|
||||
|
||||
-
|
||||
message: '#^Parameter &\$id by\-ref type of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:gatherRowData\(\) expects array\<string, string\>, array\<int\|string, string\> given\.$#'
|
||||
message: '#^Parameter &\$id by\-ref type of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:gatherRowData\(\) expects array\<string, string\>, array\<string\> given\.$#'
|
||||
identifier: parameterByRef.type
|
||||
count: 1
|
||||
path: src/Internal/Hydration/AbstractHydrator.php
|
||||
|
||||
-
|
||||
message: '#^Parameter &\$nonemptyComponents by\-ref type of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:gatherRowData\(\) expects array\<string, bool\>, array\<int\|string, bool\> given\.$#'
|
||||
message: '#^Parameter &\$nonemptyComponents by\-ref type of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:gatherRowData\(\) expects array\<string, bool\>, array\<bool\> given\.$#'
|
||||
identifier: parameterByRef.type
|
||||
count: 1
|
||||
path: src/Internal/Hydration/AbstractHydrator.php
|
||||
@@ -834,6 +816,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Internal/HydrationCompleteHandler.php
|
||||
|
||||
-
|
||||
message: '#^Offset int\|null might not exist on array\<int, object\>\.$#'
|
||||
identifier: offsetAccess.notFound
|
||||
count: 1
|
||||
path: src/Internal/StronglyConnectedComponents.php
|
||||
|
||||
-
|
||||
message: '#^Property Doctrine\\ORM\\Internal\\StronglyConnectedComponents\:\:\$representingNodes \(array\<int, object\>\) does not accept array\<int\|string, object\>\.$#'
|
||||
identifier: assign.propertyType
|
||||
@@ -954,12 +942,6 @@ parameters:
|
||||
count: 4
|
||||
path: src/Mapping/ClassMetadata.php
|
||||
|
||||
-
|
||||
message: '#^If condition is always true\.$#'
|
||||
identifier: if.alwaysTrue
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadata.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadata\:\:inlineEmbeddable\(\) has parameter \$embeddable with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -1080,6 +1062,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadata.php
|
||||
|
||||
-
|
||||
message: '#^Template type T is declared as covariant, but occurs in invariant position in return type of method Doctrine\\ORM\\Mapping\\ClassMetadata\:\:getReflectionClass\(\)\.$#'
|
||||
identifier: generics.variance
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadata.php
|
||||
|
||||
-
|
||||
message: '#^Unable to resolve the template type C in call to method Doctrine\\ORM\\Mapping\\ClassMetadata\<T of object\>\:\:fullyQualifiedClassName\(\)$#'
|
||||
identifier: argument.templateType
|
||||
@@ -1314,6 +1302,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Mapping/DefaultQuoteStrategy.php
|
||||
|
||||
-
|
||||
message: '#^Expression on left side of \?\? is not nullable\.$#'
|
||||
identifier: nullCoalesce.expr
|
||||
count: 1
|
||||
path: src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver\:\:isRepeatedPropertyDeclaration\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -1332,132 +1326,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\DBAL\\Schema\\ForeignKeyConstraint\:\:getReferencedColumnNames\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\DBAL\\Schema\\ForeignKeyConstraint\:\:getReferencedTableName\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\DBAL\\Schema\\ForeignKeyConstraint\:\:getReferencingColumnNames\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\DBAL\\Schema\\Index\:\:getIndexedColumns\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\DBAL\\Schema\\Table\:\:getPrimaryKeyConstraint\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to method getColumnName\(\) on an unknown class Doctrine\\DBAL\\Schema\\Index\\IndexedColumn\.$#'
|
||||
identifier: class.notFound
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to method getColumnNames\(\) on an unknown class Doctrine\\DBAL\\Schema\\PrimaryKeyConstraint\.$#'
|
||||
identifier: class.notFound
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to method toString\(\) on an unknown class Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName\.$#'
|
||||
identifier: class.notFound
|
||||
count: 5
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Cannot call method getName\(\) on Doctrine\\DBAL\\Schema\\Column\|false\.$#'
|
||||
identifier: method.nonObject
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Class Doctrine\\DBAL\\Schema\\Index\\IndexType not found\.$#'
|
||||
identifier: class.notFound
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Class Doctrine\\DBAL\\Schema\\PrimaryKeyConstraint not found\.$#'
|
||||
identifier: class.notFound
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Instanceof between Doctrine\\ORM\\Mapping\\ClassMetadata\<T of object\> and Doctrine\\ORM\\Mapping\\ClassMetadata will always evaluate to true\.$#'
|
||||
identifier: instanceof.alwaysTrue
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:__construct\(\) has parameter \$sm with generic class Doctrine\\DBAL\\Schema\\AbstractSchemaManager but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:buildFieldMappings\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:buildIndexes\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:buildToOneAssociationMappings\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:getClassNameForTable\(\) should return class\-string but returns string\.$#'
|
||||
identifier: return.type
|
||||
count: 2
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$array of function sort contains unresolvable type\.$#'
|
||||
identifier: argument.unresolvableType
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$columnName of method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:getFieldNameForColumn\(\) expects string, string\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 4
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \$indexedColumn of anonymous function has invalid type Doctrine\\DBAL\\Schema\\Index\\IndexedColumn\.$#'
|
||||
identifier: class.notFound
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \$name of anonymous function has invalid type Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName\.$#'
|
||||
identifier: class.notFound
|
||||
count: 5
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\SimplifiedXmlDriver\:\:__construct\(\) has parameter \$fileExtension with no type specified\.$#'
|
||||
identifier: missingType.parameter
|
||||
@@ -1483,7 +1351,7 @@ parameters:
|
||||
path: src/Mapping/Driver/XmlDriver.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$columnDef of method Doctrine\\ORM\\Mapping\\ClassMetadata\<T of object\>\:\:setDiscriminatorColumn\(\) expects array\{name\: string\|null, fieldName\?\: string\|null, type\?\: string\|null, length\?\: int\|null, columnDefinition\?\: string\|null, enumType\?\: class\-string\<BackedEnum\>\|null, options\?\: array\<string, mixed\>\|null\}\|Doctrine\\ORM\\Mapping\\DiscriminatorColumnMapping\|null, array\{name\: string\|null, type\: string, length\: int, columnDefinition\: string\|null, enumType\: string\|null, options\?\: array\<int\|string, array\<int\|string, mixed\>\|bool\|string\>\} given\.$#'
|
||||
message: '#^Parameter \#1 \$columnDef of method Doctrine\\ORM\\Mapping\\ClassMetadata\<T of object\>\:\:setDiscriminatorColumn\(\) expects array\{name\: string\|null, fieldName\?\: string\|null, type\?\: string\|null, length\?\: int\|null, columnDefinition\?\: string\|null, enumType\?\: class\-string\<BackedEnum\>\|null, options\?\: array\<string, mixed\>\|null\}\|Doctrine\\ORM\\Mapping\\DiscriminatorColumnMapping\|null, array\{name\: string\|null, type\: string, length\: int, columnDefinition\: string\|null, enumType\: string\|null, options\?\: array\<int\|string, array\<int\|string, mixed\>\|bool\|object\|string\>\} given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Mapping/Driver/XmlDriver.php
|
||||
@@ -1513,7 +1381,7 @@ parameters:
|
||||
path: src/Mapping/Driver/XmlDriver.php
|
||||
|
||||
-
|
||||
message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadata\<T of object\>\:\:\$table \(array\{name\: string, schema\?\: string, indexes\?\: array, uniqueConstraints\?\: array, options\?\: array\<string, mixed\>, quoted\?\: bool\}\) does not accept array\{name\: string, schema\?\: string, indexes\?\: array, uniqueConstraints\?\: array, options\: array\<int\|string, array\<int\|string, mixed\>\|bool\|string\>, quoted\?\: bool\}\.$#'
|
||||
message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadata\<T of object\>\:\:\$table \(array\{name\: string, schema\?\: string, indexes\?\: array, uniqueConstraints\?\: array, options\?\: array\<string, mixed\>, quoted\?\: bool\}\) does not accept array\{name\: string, schema\?\: string, indexes\?\: array, uniqueConstraints\?\: array, options\: array\<int\|string, array\<int\|string, mixed\>\|bool\|object\|string\>, quoted\?\: bool\}\.$#'
|
||||
identifier: assign.propertyType
|
||||
count: 1
|
||||
path: src/Mapping/Driver/XmlDriver.php
|
||||
@@ -1561,16 +1429,10 @@ parameters:
|
||||
path: src/Mapping/JoinTableMapping.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\LegacyReflectionFields\:\:__construct\(\) has parameter \$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
message: '#^Strict comparison using \!\=\= between array\<string, string\> and null will always evaluate to true\.$#'
|
||||
identifier: notIdentical.alwaysTrue
|
||||
count: 1
|
||||
path: src/Mapping/LegacyReflectionFields.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$class of method Doctrine\\Persistence\\Mapping\\ReflectionService\:\:getAccessibleProperty\(\) expects class\-string, string given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Mapping/LegacyReflectionFields.php
|
||||
path: src/Mapping/ManyToManyOwningSideMapping.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\MappedSuperclass\:\:__construct\(\) has parameter \$repositoryClass with generic class Doctrine\\ORM\\EntityRepository but does not specify its types\: T$#'
|
||||
@@ -1638,12 +1500,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Mapping/QuoteStrategy.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\ReflectionEnumProperty\:\:getValue\(\) return type has no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: src/Mapping/ReflectionEnumProperty.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\ToOneOwningSideMapping\:\:fromMappingArray\(\) should return static\(Doctrine\\ORM\\Mapping\\ToOneOwningSideMapping\) but returns Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\.$#'
|
||||
identifier: return.type
|
||||
@@ -1717,7 +1573,7 @@ parameters:
|
||||
path: src/PersistentCollection.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:matching\(\) should return Doctrine\\Common\\Collections\\Collection\<TKey of \(int\|string\), T\> but returns Doctrine\\Common\\Collections\\ReadableCollection\<TKey of \(int\|string\), T\>&Doctrine\\Common\\Collections\\Selectable\<TKey of \(int\|string\), T\>\.$#'
|
||||
message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:matching\(\) should return Doctrine\\Common\\Collections\\Collection\<TKey of \(int\|string\), T\> but returns Doctrine\\Common\\Collections\\ReadableCollection\<TKey of \(int\|string\), T\>\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/PersistentCollection.php
|
||||
@@ -1729,7 +1585,7 @@ parameters:
|
||||
path: src/PersistentCollection.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$callback of function array_walk expects callable\(object, int\)\: mixed, array\{Doctrine\\Common\\Collections\\Collection\<TKey of \(int\|string\), T\>&Doctrine\\Common\\Collections\\Selectable\<TKey of \(int\|string\), T\>, ''add''\} given\.$#'
|
||||
message: '#^Parameter \#2 \$callback of function array_walk expects callable\(object, int\)\: mixed, array\{Doctrine\\Common\\Collections\\Collection\<TKey of \(int\|string\), T\>, ''add''\} given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/PersistentCollection.php
|
||||
@@ -1854,6 +1710,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Collection/ManyToManyPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:count\(\) should return int\<0, max\> but returns int\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Persisters/Collection/ManyToManyPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:delete\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -2022,6 +1884,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Collection/OneToManyPersister.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$columns of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:getColumnDeclarationListSQL\(\) expects list\<array\{name\: string, type\: Doctrine\\DBAL\\Types\\Type, default\: mixed, notnull\?\: bool, autoincrement\: bool, columnDefinition\: non\-empty\-string\|null, comment\: string, charset\?\: non\-empty\-string\|null, \.\.\.\}\>, array\<string, array\{name\: string, notnull\: true, type\: Doctrine\\DBAL\\Types\\Type\}\> given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Persisters/Collection/OneToManyPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\AbstractEntityInheritancePersister\:\:getSelectColumnSQL\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -2052,18 +1920,6 @@ parameters:
|
||||
count: 4
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$relationToTargetKeyColumns\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$sourceToTargetKeyColumns\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:indexBy\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
@@ -2076,18 +1932,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:expandCriteriaParameters\(\) should return array\{list\<mixed\>, list\<Doctrine\\DBAL\\ArrayParameterType\:\:ASCII\|Doctrine\\DBAL\\ArrayParameterType\:\:BINARY\|Doctrine\\DBAL\\ArrayParameterType\:\:INTEGER\|Doctrine\\DBAL\\ArrayParameterType\:\:STRING\|Doctrine\\DBAL\\ParameterType\:\:ASCII\|Doctrine\\DBAL\\ParameterType\:\:BINARY\|Doctrine\\DBAL\\ParameterType\:\:BOOLEAN\|Doctrine\\DBAL\\ParameterType\:\:INTEGER\|Doctrine\\DBAL\\ParameterType\:\:LARGE_OBJECT\|Doctrine\\DBAL\\ParameterType\:\:NULL\|Doctrine\\DBAL\\ParameterType\:\:STRING\|string\>\} but returns array\{array\<mixed\>, list\<Doctrine\\DBAL\\ArrayParameterType\:\:ASCII\|Doctrine\\DBAL\\ArrayParameterType\:\:BINARY\|Doctrine\\DBAL\\ArrayParameterType\:\:INTEGER\|Doctrine\\DBAL\\ArrayParameterType\:\:STRING\|Doctrine\\DBAL\\ParameterType\:\:ASCII\|Doctrine\\DBAL\\ParameterType\:\:BINARY\|Doctrine\\DBAL\\ParameterType\:\:BOOLEAN\|Doctrine\\DBAL\\ParameterType\:\:INTEGER\|Doctrine\\DBAL\\ParameterType\:\:LARGE_OBJECT\|Doctrine\\DBAL\\ParameterType\:\:NULL\|Doctrine\\DBAL\\ParameterType\:\:STRING\|string\>\}\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:expandParameters\(\) should return array\{list\<mixed\>, list\<Doctrine\\DBAL\\ArrayParameterType\:\:ASCII\|Doctrine\\DBAL\\ArrayParameterType\:\:BINARY\|Doctrine\\DBAL\\ArrayParameterType\:\:INTEGER\|Doctrine\\DBAL\\ArrayParameterType\:\:STRING\|Doctrine\\DBAL\\ParameterType\:\:ASCII\|Doctrine\\DBAL\\ParameterType\:\:BINARY\|Doctrine\\DBAL\\ParameterType\:\:BOOLEAN\|Doctrine\\DBAL\\ParameterType\:\:INTEGER\|Doctrine\\DBAL\\ParameterType\:\:LARGE_OBJECT\|Doctrine\\DBAL\\ParameterType\:\:NULL\|Doctrine\\DBAL\\ParameterType\:\:STRING\|string\>\} but returns array\{array\<mixed\>, list\<Doctrine\\DBAL\\ArrayParameterType\:\:ASCII\|Doctrine\\DBAL\\ArrayParameterType\:\:BINARY\|Doctrine\\DBAL\\ArrayParameterType\:\:INTEGER\|Doctrine\\DBAL\\ArrayParameterType\:\:STRING\|Doctrine\\DBAL\\ParameterType\:\:ASCII\|Doctrine\\DBAL\\ParameterType\:\:BINARY\|Doctrine\\DBAL\\ParameterType\:\:BOOLEAN\|Doctrine\\DBAL\\ParameterType\:\:INTEGER\|Doctrine\\DBAL\\ParameterType\:\:LARGE_OBJECT\|Doctrine\\DBAL\\ParameterType\:\:NULL\|Doctrine\\DBAL\\ParameterType\:\:STRING\|string\>\}\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:expandToManyParameters\(\) return type has no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
@@ -2124,12 +1968,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getIndividualValue\(\) should return list\<mixed\> but returns array\<mixed\>\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getSelectColumnAssociationSQL\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -2142,18 +1980,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getTypes\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getTypes\(\) should return list\<Doctrine\\DBAL\\ArrayParameterType\:\:ASCII\|Doctrine\\DBAL\\ArrayParameterType\:\:BINARY\|Doctrine\\DBAL\\ArrayParameterType\:\:INTEGER\|Doctrine\\DBAL\\ArrayParameterType\:\:STRING\|Doctrine\\DBAL\\ParameterType\:\:ASCII\|Doctrine\\DBAL\\ParameterType\:\:BINARY\|Doctrine\\DBAL\\ParameterType\:\:BOOLEAN\|Doctrine\\DBAL\\ParameterType\:\:INTEGER\|Doctrine\\DBAL\\ParameterType\:\:LARGE_OBJECT\|Doctrine\\DBAL\\ParameterType\:\:NULL\|Doctrine\\DBAL\\ParameterType\:\:STRING\|string\> but returns list\<Doctrine\\DBAL\\ArrayParameterType\:\:ASCII\|Doctrine\\DBAL\\ArrayParameterType\:\:BINARY\|Doctrine\\DBAL\\ArrayParameterType\:\:INTEGER\|Doctrine\\DBAL\\ArrayParameterType\:\:STRING\|int\>\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:loadCollectionFromStatement\(\) has parameter \$coll with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -2178,12 +2004,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$lockMode of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:appendLockHint\(\) expects Doctrine\\DBAL\\LockMode, Doctrine\\DBAL\\LockMode\:\:NONE\|Doctrine\\DBAL\\LockMode\:\:OPTIMISTIC\|Doctrine\\DBAL\\LockMode\:\:PESSIMISTIC_READ\|Doctrine\\DBAL\\LockMode\:\:PESSIMISTIC_WRITE\|int given\.$#'
|
||||
identifier: argument.type
|
||||
count: 2
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$hints of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:hydrateAll\(\) expects array\<string, string\>, array\<string, Doctrine\\ORM\\PersistentCollection\|true\> given\.$#'
|
||||
identifier: argument.type
|
||||
@@ -2208,12 +2028,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Strict comparison using \=\=\= between string and null will always evaluate to false\.$#'
|
||||
identifier: identical.alwaysFalse
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\CachedPersisterContext\:\:__construct\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -2262,12 +2076,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Entity/JoinedSubclassPersister.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$lockMode of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:appendLockHint\(\) expects Doctrine\\DBAL\\LockMode, Doctrine\\DBAL\\LockMode\:\:NONE\|Doctrine\\DBAL\\LockMode\:\:OPTIMISTIC\|Doctrine\\DBAL\\LockMode\:\:PESSIMISTIC_READ\|Doctrine\\DBAL\\LockMode\:\:PESSIMISTIC_WRITE\|int given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Persisters/Entity/JoinedSubclassPersister.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#'
|
||||
identifier: property.notFound
|
||||
@@ -2292,156 +2100,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/SqlValueVisitor.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$className of static method Doctrine\\ORM\\Proxy\\Autoloader\:\:resolveFile\(\) expects class\-string, string given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Proxy/Autoloader.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 of closure expects class\-string, string given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Proxy/Autoloader.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver\:\:resolveClassName\(\) should return class\-string\<T of object\> but returns class\-string\<Doctrine\\Persistence\\Proxy\<T of object\>\>\|class\-string\<T of object\>\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Proxy/DefaultProxyClassNameResolver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver\:\:resolveClassName\(\) should return class\-string\<T of object\> but returns string\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Proxy/DefaultProxyClassNameResolver.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$isEmbeddedClass\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$isMappedSuperclass\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined static method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyGhost\(\)\.$#'
|
||||
identifier: staticMethod.notFound
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Call to function is_bool\(\) with bool will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Comparison operation "\<" between 0\|1\|2\|3\|4 and 0 is always false\.$#'
|
||||
identifier: smaller.alwaysFalse
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Comparison operation "\>" between 0\|1\|2\|3\|4 and 4 is always false\.$#'
|
||||
identifier: greater.alwaysFalse
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) has Doctrine\\ORM\\EntityNotFoundException in PHPDoc @throws tag but it''s not thrown\.$#'
|
||||
identifier: throws.unusedType
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) has parameter \$classMetadata with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) return type has no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) return type with generic interface Doctrine\\ORM\\Proxy\\InternalProxy does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateProxyClass\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateProxyClasses\(\) has parameter \$classes with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateSerializeImpl\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateUseLazyGhostTrait\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:loadProxyClass\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:skipClass\(\) has parameter \$metadata with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$class of method Doctrine\\ORM\\Utility\\IdentifierFlattener\:\:flattenIdentifier\(\) expects Doctrine\\ORM\\Mapping\\ClassMetadata, Doctrine\\Persistence\\Mapping\\ClassMetadata given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$filename of function filemtime expects string, string\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$length of function substr expects int\|null, int\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$newScope of static method Closure\:\:bind\(\) expects ''static''\|class\-string\|object\|null, string given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Result of \|\| is always false\.$#'
|
||||
identifier: booleanOr.alwaysFalse
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Query\:\:processParameterMappings\(\) return type has no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
@@ -2562,12 +2220,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Query/AST/Functions/SizeFunction.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$mode of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:getTrimExpression\(\) expects Doctrine\\DBAL\\Platforms\\TrimMode, Doctrine\\DBAL\\Platforms\\TrimMode\:\:BOTH\|Doctrine\\DBAL\\Platforms\\TrimMode\:\:LEADING\|Doctrine\\DBAL\\Platforms\\TrimMode\:\:TRAILING\|Doctrine\\DBAL\\Platforms\\TrimMode\:\:UNSPECIFIED\|int given\.$#'
|
||||
identifier: argument.type
|
||||
count: 2
|
||||
path: src/Query/AST/Functions/TrimFunction.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\ORM\\Query\\SqlWalker\:\:walkJoinPathExpression\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
@@ -2610,6 +2262,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Query/Exec/MultiTableDeleteExecutor.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$columns of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:getColumnDeclarationListSQL\(\) expects list\<array\{name\: string, type\: Doctrine\\DBAL\\Types\\Type, default\: mixed, notnull\?\: bool, autoincrement\: bool, columnDefinition\: non\-empty\-string\|null, comment\: string, charset\?\: non\-empty\-string\|null, \.\.\.\}\>, array\<string, array\{name\: string, notnull\: true, type\: Doctrine\\DBAL\\Types\\Type\}\> given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Query/Exec/MultiTableDeleteExecutor.php
|
||||
|
||||
-
|
||||
message: '#^Argument of an invalid type list\<string\>\|string supplied for foreach, only iterables are supported\.$#'
|
||||
identifier: foreach.nonIterable
|
||||
@@ -2623,7 +2281,7 @@ parameters:
|
||||
path: src/Query/Exec/MultiTableUpdateExecutor.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$types of method Doctrine\\DBAL\\Connection\:\:executeStatement\(\) expects array\<int\<0, max\>\|string, Doctrine\\DBAL\\ArrayParameterType\|Doctrine\\DBAL\\ParameterType\|Doctrine\\DBAL\\Types\\Type\|string\>, list\<Doctrine\\DBAL\\ArrayParameterType\|Doctrine\\DBAL\\ParameterType\|Doctrine\\DBAL\\Types\\Type\|int\|string\> given\.$#'
|
||||
message: '#^Parameter \#1 \$columns of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:getColumnDeclarationListSQL\(\) expects list\<array\{name\: string, type\: Doctrine\\DBAL\\Types\\Type, default\: mixed, notnull\?\: bool, autoincrement\: bool, columnDefinition\: non\-empty\-string\|null, comment\: string, charset\?\: non\-empty\-string\|null, \.\.\.\}\>, array\<string, array\{name\: string, notnull\: true, type\: Doctrine\\DBAL\\Types\\Type\}\> given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Query/Exec/MultiTableUpdateExecutor.php
|
||||
@@ -2646,42 +2304,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Query/Exec/SingleTableDeleteUpdateExecutor.php
|
||||
|
||||
-
|
||||
message: '#^PHPDoc type array\<string\> of property Doctrine\\ORM\\Query\\Expr\\Andx\:\:\$allowedClasses is not covariant with PHPDoc type list\<class\-string\> of overridden property Doctrine\\ORM\\Query\\Expr\\Base\:\:\$allowedClasses\.$#'
|
||||
identifier: property.phpDocType
|
||||
count: 1
|
||||
path: src/Query/Expr/Andx.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Query\\Expr\\Func\:\:getArguments\(\) should return list\<mixed\> but returns array\<mixed\>\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Query/Expr/Func.php
|
||||
|
||||
-
|
||||
message: '#^PHPDoc type array\<string\> of property Doctrine\\ORM\\Query\\Expr\\Orx\:\:\$allowedClasses is not covariant with PHPDoc type list\<class\-string\> of overridden property Doctrine\\ORM\\Query\\Expr\\Base\:\:\$allowedClasses\.$#'
|
||||
identifier: property.phpDocType
|
||||
count: 1
|
||||
path: src/Query/Expr/Orx.php
|
||||
|
||||
-
|
||||
message: '#^PHPDoc type array\<string\> of property Doctrine\\ORM\\Query\\Expr\\Select\:\:\$allowedClasses is not covariant with PHPDoc type list\<class\-string\> of overridden property Doctrine\\ORM\\Query\\Expr\\Base\:\:\$allowedClasses\.$#'
|
||||
identifier: property.phpDocType
|
||||
count: 1
|
||||
path: src/Query/Expr/Select.php
|
||||
|
||||
-
|
||||
message: '#^Property Doctrine\\ORM\\Query\\Filter\\SQLFilter\:\:\$parameters \(array\<string, array\{type\: string, value\: mixed, is_list\: bool\}\>\) does not accept non\-empty\-array\<string, array\{value\: mixed, type\: Doctrine\\DBAL\\ArrayParameterType\|Doctrine\\DBAL\\ParameterType\|int\|string, is_list\: bool\}\>\.$#'
|
||||
identifier: assign.propertyType
|
||||
count: 1
|
||||
path: src/Query/Filter/SQLFilter.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Query\\ParameterTypeInferer\:\:inferType\(\) never returns int so it can be removed from the return type\.$#'
|
||||
identifier: return.unusedType
|
||||
count: 1
|
||||
path: src/Query/ParameterTypeInferer.php
|
||||
|
||||
-
|
||||
message: '#^@readonly property cannot have a default value\.$#'
|
||||
identifier: property.readOnlyByPhpDocDefaultValue
|
||||
@@ -2916,6 +2544,12 @@ parameters:
|
||||
count: 2
|
||||
path: src/QueryBuilder.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$type of method Doctrine\\ORM\\QueryBuilder\:\:setParameter\(\) expects Doctrine\\DBAL\\ArrayParameterType\|Doctrine\\DBAL\\ParameterType\|string\|null, Doctrine\\DBAL\\ArrayParameterType\|Doctrine\\DBAL\\ParameterType\|int\|string\|null given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/QueryBuilder.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Repository\\DefaultRepositoryFactory\:\:createRepository\(\) return type with generic class Doctrine\\ORM\\EntityRepository does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -2928,30 +2562,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Repository/DefaultRepositoryFactory.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$name\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateProxiesCommand.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$filename of function file_exists expects string, string\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateProxiesCommand.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$filename of function is_writable expects string, string\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateProxiesCommand.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$proxyDir of method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateProxyClasses\(\) expects string\|null, string\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateProxiesCommand.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Tools\\Console\\Command\\MappingDescribeCommand\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -3108,12 +2718,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Tools/ResolveTargetEntityListener.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$className of method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory\<Doctrine\\ORM\\Mapping\\ClassMetadata\>\:\:setMetadataFor\(\) expects class\-string, \(int\|string\) given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/ResolveTargetEntityListener.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#'
|
||||
identifier: property.notFound
|
||||
@@ -3121,32 +2725,71 @@ parameters:
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\DBAL\\Schema\\ForeignKeyConstraint\:\:getReferencedColumnNames\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
message: '''
|
||||
#^Call to deprecated method getColumns\(\) of class Doctrine\\DBAL\\Schema\\Index\:
|
||||
Use \{@see getIndexedColumns\(\)\} instead\.$#
|
||||
'''
|
||||
identifier: method.deprecated
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\DBAL\\Schema\\ForeignKeyConstraint\:\:getReferencedTableName\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
message: '''
|
||||
#^Call to deprecated method getForeignColumns\(\) of class Doctrine\\DBAL\\Schema\\ForeignKeyConstraint\:
|
||||
Use \{@see getReferencedColumnNames\(\)\} instead\.
|
||||
|
||||
Returns the names of the referenced table columns
|
||||
the foreign key constraint is associated with\.$#
|
||||
'''
|
||||
identifier: method.deprecated
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\DBAL\\Schema\\ForeignKeyConstraint\:\:getReferencingColumnNames\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
message: '''
|
||||
#^Call to deprecated method getForeignTableName\(\) of class Doctrine\\DBAL\\Schema\\ForeignKeyConstraint\:
|
||||
Use \{@see getReferencedTableName\(\)\} instead\.
|
||||
|
||||
Returns the name of the referenced table
|
||||
the foreign key constraint is associated with\.$#
|
||||
'''
|
||||
identifier: method.deprecated
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\DBAL\\Schema\\Index\:\:getIndexedColumns\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
message: '''
|
||||
#^Call to deprecated method getLocalColumns\(\) of class Doctrine\\DBAL\\Schema\\ForeignKeyConstraint\:
|
||||
Use \{@see getReferencingColumnNames\(\)\} instead\.$#
|
||||
'''
|
||||
identifier: method.deprecated
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\DBAL\\Schema\\Table\:\:dropForeignKey\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
message: '''
|
||||
#^Call to deprecated method getPrimaryKey\(\) of class Doctrine\\DBAL\\Schema\\Table\:
|
||||
Use \{@see getPrimaryKeyConstraint\(\)\} instead\.$#
|
||||
'''
|
||||
identifier: method.deprecated
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '''
|
||||
#^Call to deprecated method removeForeignKey\(\) of class Doctrine\\DBAL\\Schema\\Table\:
|
||||
Use \{@link dropForeignKey\(\)\} instead\.$#
|
||||
'''
|
||||
identifier: method.deprecated
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '''
|
||||
#^Call to deprecated method setPrimaryKey\(\) of class Doctrine\\DBAL\\Schema\\Table\:
|
||||
Use \{@see addPrimaryKeyConstraint\(\)\} instead\.$#
|
||||
'''
|
||||
identifier: method.deprecated
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
@@ -3157,32 +2800,14 @@ parameters:
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Call to method getColumnName\(\) on an unknown class Doctrine\\DBAL\\Schema\\Index\\IndexedColumn\.$#'
|
||||
identifier: class.notFound
|
||||
message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Schema\\\\Index'' and ''getIndexedColumns'' will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Call to method getColumnNames\(\) on an unknown class Doctrine\\DBAL\\Schema\\PrimaryKeyConstraint\.$#'
|
||||
identifier: class.notFound
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Call to method toString\(\) on an unknown class Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName\.$#'
|
||||
identifier: class.notFound
|
||||
count: 3
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Class Doctrine\\DBAL\\Schema\\PrimaryKeyConstraint not found\.$#'
|
||||
identifier: class.notFound
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\DBAL\\Schema\\AbstractSchemaManager\<Doctrine\\DBAL\\Platforms\\AbstractPlatform\>\:\:createComparator\(\) invoked with 1 parameter, 0 required\.$#'
|
||||
identifier: arguments.count
|
||||
message: '#^Call to function method_exists\(\) with Doctrine\\DBAL\\Schema\\Table and ''getPrimaryKeyConstr…'' will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
@@ -3228,6 +2853,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Tools\\SchemaTool\:\:getAssetName\(\) has parameter \$asset with generic class Doctrine\\DBAL\\Schema\\AbstractAsset but does not specify its types\: N$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Tools\\SchemaTool\:\:getCreateSchemaSql\(\) has parameter \$classes with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -3289,15 +2920,45 @@ parameters:
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \$indexedColumn of anonymous function has invalid type Doctrine\\DBAL\\Schema\\Index\\IndexedColumn\.$#'
|
||||
identifier: class.notFound
|
||||
message: '#^Parameter \#1 \$columnNames of method Doctrine\\DBAL\\Schema\\Table\:\:addIndex\(\) expects non\-empty\-list\<string\>, list\<string\> given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \$name of anonymous function has invalid type Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName\.$#'
|
||||
identifier: class.notFound
|
||||
count: 3
|
||||
message: '#^Parameter \#1 \$columnNames of method Doctrine\\DBAL\\Schema\\Table\:\:addUniqueIndex\(\) expects non\-empty\-list\<string\>, array\<string\> given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$columns of class Doctrine\\DBAL\\Schema\\Index constructor expects non\-empty\-list\<string\>, list\<string\> given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$localColumnNames of method Doctrine\\DBAL\\Schema\\Table\:\:addForeignKeyConstraint\(\) expects non\-empty\-list\<string\>, list\<string\> given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$primaryKeyColumns of method Doctrine\\ORM\\Tools\\SchemaTool\:\:addPrimaryKeyConstraint\(\) expects non\-empty\-array\<non\-empty\-string\>, list\<string\> given\.$#'
|
||||
identifier: argument.type
|
||||
count: 2
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$primaryKeyColumns of method Doctrine\\ORM\\Tools\\SchemaTool\:\:addPrimaryKeyConstraint\(\) expects non\-empty\-array\<non\-empty\-string\>, non\-empty\-list\<string\> given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$foreignColumnNames of method Doctrine\\DBAL\\Schema\\Table\:\:addForeignKeyConstraint\(\) expects non\-empty\-list\<string\>, list\<string\> given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
@@ -3366,6 +3027,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\AssociationMapping\:\:\$joinColumns\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$inversedBy\.$#'
|
||||
identifier: property.notFound
|
||||
@@ -3381,7 +3048,7 @@ parameters:
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$mappedBy\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
count: 3
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
@@ -3450,12 +3117,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '#^PHPDoc tag @phpstan\-assert\-if\-true for \$obj contains generic interface Doctrine\\ORM\\Proxy\\InternalProxy but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$assoc of method Doctrine\\ORM\\PersistentCollection\<\(int\|string\),mixed\>\:\:setOwner\(\) expects Doctrine\\ORM\\Mapping\\AssociationMapping&Doctrine\\ORM\\Mapping\\ToManyAssociationMapping, Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping given\.$#'
|
||||
identifier: argument.type
|
||||
@@ -3474,12 +3135,6 @@ parameters:
|
||||
count: 2
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$collection of class Doctrine\\ORM\\PersistentCollection constructor expects Doctrine\\Common\\Collections\\Collection\<\(int\|string\), mixed\>&Doctrine\\Common\\Collections\\Selectable\<\(int\|string\), mixed\>, Doctrine\\Common\\Collections\\Collection given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#5 \$invoke of method Doctrine\\ORM\\Event\\ListenersInvoker\:\:invoke\(\) expects int\<0, 7\>, int\<min, \-1\>\|int\<1, max\> given\.$#'
|
||||
identifier: argument.type
|
||||
@@ -3558,6 +3213,18 @@ parameters:
|
||||
count: 1
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$relationToTargetKeyColumns\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$sourceToTargetKeyColumns\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Utility\\PersisterHelper\:\:getTypeOfColumn\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -3569,3 +3236,9 @@ parameters:
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Utility\\PersisterHelper\:\:inferParameterTypes\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
includes:
|
||||
- phpstan-baseline.neon
|
||||
- phpstan-params.neon
|
||||
|
||||
parameters:
|
||||
reportUnmatchedIgnoredErrors: false # Some errors in the baseline only apply to DBAL 4
|
||||
ignoreErrors:
|
||||
# Symfony cache supports passing a key prefix to the clear method.
|
||||
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
|
||||
|
||||
# We can be certain that those values are not matched.
|
||||
-
|
||||
message: '~^Match expression does not handle remaining values:~'
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
# DBAL 4 compatibility
|
||||
-
|
||||
message: '~^Method Doctrine\\ORM\\Query\\AST\\Functions\\TrimFunction::getTrimMode\(\) never returns .* so it can be removed from the return type\.$~'
|
||||
path: src/Query/AST/Functions/TrimFunction.php
|
||||
|
||||
-
|
||||
message: '~.*getTrimExpression.*expects int.*~'
|
||||
path: src/Query/AST/Functions/TrimFunction.php
|
||||
|
||||
-
|
||||
message: '~^Call to static method unquoted\(\) on an unknown class Doctrine\\DBAL\\Schema\\Name\\Identifier\.$~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '~^Instantiated class Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName not found\.$~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '~^Call to an undefined method Doctrine\\DBAL\\Schema\\Table::addPrimaryKeyConstraint\(\)\.$~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
- '~^Class Doctrine\\DBAL\\Platforms\\SQLitePlatform not found\.$~'
|
||||
|
||||
# To be removed in 4.0
|
||||
-
|
||||
message: '#Negated boolean expression is always false\.#'
|
||||
paths:
|
||||
- src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
-
|
||||
message: '~^Call to deprecated method getEventManager\(\) of class Doctrine\\DBAL\\Connection\.$~'
|
||||
path: src/EntityManager.php
|
||||
-
|
||||
message: '~deprecated class Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand\:~'
|
||||
path: src/Tools/Console/ConsoleRunner.php
|
||||
|
||||
# Compatibility with Persistence 3
|
||||
-
|
||||
message: '#Expression on left side of \?\? is not nullable.#'
|
||||
path: src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
-
|
||||
message: '~^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getArrayBindingType\(\) never returns .* so it can be removed from the return type\.$~'
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '~getTypes.*should return~'
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '~.*appendLockHint.*expects.*LockMode given~'
|
||||
paths:
|
||||
- src/Persisters/Entity/BasicEntityPersister.php
|
||||
- src/Persisters/Entity/JoinedSubclassPersister.php
|
||||
|
||||
-
|
||||
message: '~.*executeStatement.*expects~'
|
||||
path: src/Query/Exec/MultiTableUpdateExecutor.php
|
||||
|
||||
-
|
||||
message: '~method_exists.*getEventManager~'
|
||||
path: src/EntityManager.php
|
||||
|
||||
-
|
||||
message: '~method_exists.*getIdentitySequence~'
|
||||
path: src/Mapping/ClassMetadataFactory.php
|
||||
|
||||
-
|
||||
message: '~expand(Criteria)?Parameters.*should return array~'
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '~inferType.*never returns~'
|
||||
path: src/Query/ParameterTypeInferer.php
|
||||
+3
-52
@@ -3,6 +3,8 @@ includes:
|
||||
- phpstan-params.neon
|
||||
|
||||
parameters:
|
||||
checkMissingOverrideMethodAttribute: true
|
||||
|
||||
ignoreErrors:
|
||||
# Symfony cache supports passing a key prefix to the clear method.
|
||||
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
|
||||
@@ -10,55 +12,4 @@ parameters:
|
||||
# We can be certain that those values are not matched.
|
||||
-
|
||||
message: '~^Match expression does not handle remaining values:~'
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
# DBAL 4 compatibility
|
||||
-
|
||||
message: '~^Method Doctrine\\ORM\\Query\\AST\\Functions\\TrimFunction::getTrimMode\(\) never returns .* so it can be removed from the return type\.$~'
|
||||
path: src/Query/AST/Functions/TrimFunction.php
|
||||
-
|
||||
message: '~^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getArrayBindingType\(\) never returns .* so it can be removed from the return type\.$~'
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '~^Call to static method unquoted\(\) on an unknown class Doctrine\\DBAL\\Schema\\Name\\Identifier\.$~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '~^Instantiated class Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName not found\.$~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '~^Call to an undefined method Doctrine\\DBAL\\Schema\\Table::addPrimaryKeyConstraint\(\)\.$~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
# Compatibility with DBAL 3
|
||||
# See https://github.com/doctrine/dbal/pull/3480
|
||||
-
|
||||
message: '~^Result of method Doctrine\\DBAL\\Connection::commit\(\) \(void\) is used\.$~'
|
||||
path: src/UnitOfWork.php
|
||||
-
|
||||
message: '~^Strict comparison using === between null and false will always evaluate to false\.$~'
|
||||
path: src/UnitOfWork.php
|
||||
-
|
||||
message: '~^Variable \$e on left side of \?\? always exists and is not nullable\.$~'
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '~^Parameter #2 \$command of static method Doctrine\\ORM\\Tools\\Console\\ConsoleRunner::addCommandToApplication\(\) expects Symfony\\Component\\Console\\Command\\Command, Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand given\.$~'
|
||||
path: src/Tools/Console/ConsoleRunner.php
|
||||
|
||||
-
|
||||
message: '~Strict comparison using \=\=\= between callable\(\)\: mixed and null will always evaluate to false\.~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
# To be removed in 4.0
|
||||
-
|
||||
message: '#Negated boolean expression is always false\.#'
|
||||
paths:
|
||||
- src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
# Compatibility with Persistence 3
|
||||
-
|
||||
message: '#Expression on left side of \?\? is not nullable.#'
|
||||
path: src/Mapping/Driver/AttributeDriver.php
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is a small convenience wrapper for running the doctrine testsuite against a large bunch of databases.
|
||||
# Just create the phpunit.xmls as described in the array below and configure the specific files <php /> section
|
||||
# to connect to that database. Just omit a file if you don't have that database and the tests will be skipped.
|
||||
|
||||
configs[1]="mysql.phpunit.xml"
|
||||
configs[2]='postgres.phpunit.xml'
|
||||
configs[3]='sqlite.phpunit.xml'
|
||||
configs[4]='oracle.phpunit.xml'
|
||||
configs[5]='db2.phpunit.xml'
|
||||
configs[6]='pdo-ibm.phpunit.xml'
|
||||
configs[7]='sqlsrv.phpunit.xml'
|
||||
|
||||
for i in "${configs[@]}"; do
|
||||
if [ -f "$i" ];
|
||||
then
|
||||
echo "RUNNING TESTS WITH CONFIG $i"
|
||||
phpunit -c "$i" "$@"
|
||||
fi;
|
||||
done
|
||||
+15
-21
@@ -16,9 +16,7 @@ use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
use Doctrine\ORM\Cache\TimestampCacheKey;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\MappingException as ORMMappingException;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Query\Parameter;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\Persistence\Mapping\MappingException;
|
||||
use LogicException;
|
||||
@@ -52,32 +50,32 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Hydrates an object graph. This is the default behavior.
|
||||
*/
|
||||
public const HYDRATE_OBJECT = 1;
|
||||
final public const int HYDRATE_OBJECT = 1;
|
||||
|
||||
/**
|
||||
* Hydrates an array graph.
|
||||
*/
|
||||
public const HYDRATE_ARRAY = 2;
|
||||
final public const int HYDRATE_ARRAY = 2;
|
||||
|
||||
/**
|
||||
* Hydrates a flat, rectangular result set with scalar values.
|
||||
*/
|
||||
public const HYDRATE_SCALAR = 3;
|
||||
final public const int HYDRATE_SCALAR = 3;
|
||||
|
||||
/**
|
||||
* Hydrates a single scalar value.
|
||||
*/
|
||||
public const HYDRATE_SINGLE_SCALAR = 4;
|
||||
final public const int HYDRATE_SINGLE_SCALAR = 4;
|
||||
|
||||
/**
|
||||
* Very simple object hydrator (optimized for performance).
|
||||
*/
|
||||
public const HYDRATE_SIMPLEOBJECT = 5;
|
||||
final public const int HYDRATE_SIMPLEOBJECT = 5;
|
||||
|
||||
/**
|
||||
* Hydrates scalar column value.
|
||||
*/
|
||||
public const HYDRATE_SCALAR_COLUMN = 6;
|
||||
final public const int HYDRATE_SCALAR_COLUMN = 6;
|
||||
|
||||
/**
|
||||
* The parameter map of this query.
|
||||
@@ -321,16 +319,16 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Sets a query parameter.
|
||||
*
|
||||
* @param string|int $key The parameter position or name.
|
||||
* @param mixed $value The parameter value.
|
||||
* @param ParameterType|ArrayParameterType|string|int|null $type The parameter type. If specified, the given value
|
||||
* will be run through the type conversion of this
|
||||
* type. This is usually not needed for strings and
|
||||
* numeric types.
|
||||
* @param string|int $key The parameter position or name.
|
||||
* @param mixed $value The parameter value.
|
||||
* @param ParameterType|ArrayParameterType|string|null $type The parameter type. If specified, the given value
|
||||
* will be run through the type conversion of this
|
||||
* type. This is usually not needed for strings and
|
||||
* numeric types.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParameter(string|int $key, mixed $value, ParameterType|ArrayParameterType|string|int|null $type = null): static
|
||||
public function setParameter(string|int $key, mixed $value, ParameterType|ArrayParameterType|string|null $type = null): static
|
||||
{
|
||||
$existingParameter = $this->getParameter($key);
|
||||
|
||||
@@ -379,7 +377,7 @@ abstract class AbstractQuery
|
||||
}
|
||||
|
||||
try {
|
||||
$class = DefaultProxyClassNameResolver::getClass($value);
|
||||
$class = $value::class;
|
||||
$value = $this->em->getUnitOfWork()->getSingleIdentifierValue($value);
|
||||
|
||||
if ($value === null) {
|
||||
@@ -865,10 +863,6 @@ abstract class AbstractQuery
|
||||
throw new LogicException('Uninitialized result set mapping.');
|
||||
}
|
||||
|
||||
if ($rsm->isMixed && count($rsm->scalarMappings) > 0) {
|
||||
throw QueryException::iterateWithMixedResultNotAllowed();
|
||||
}
|
||||
|
||||
$stmt = $this->_doExecute();
|
||||
|
||||
return $this->em->newHydrator($this->hydrationMode)->toIterable($stmt, $rsm, $this->hints);
|
||||
@@ -1070,7 +1064,7 @@ abstract class AbstractQuery
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and returns a the resulting Statement object.
|
||||
* Executes the query and returns the resulting Statement object.
|
||||
*
|
||||
* @return Result|int The executed database statement that holds
|
||||
* the results, or an integer indicating how
|
||||
|
||||
+6
-6
@@ -12,31 +12,31 @@ use Doctrine\ORM\Cache\Region;
|
||||
*/
|
||||
interface Cache
|
||||
{
|
||||
public const DEFAULT_QUERY_REGION_NAME = 'query_cache_region';
|
||||
final public const string DEFAULT_QUERY_REGION_NAME = 'query_cache_region';
|
||||
|
||||
public const DEFAULT_TIMESTAMP_REGION_NAME = 'timestamp_cache_region';
|
||||
final public const string DEFAULT_TIMESTAMP_REGION_NAME = 'timestamp_cache_region';
|
||||
|
||||
/**
|
||||
* May read items from the cache, but will not add items.
|
||||
*/
|
||||
public const MODE_GET = 1;
|
||||
final public const int MODE_GET = 1;
|
||||
|
||||
/**
|
||||
* Will never read items from the cache,
|
||||
* but will add items to the cache as it reads them from the database.
|
||||
*/
|
||||
public const MODE_PUT = 2;
|
||||
final public const int MODE_PUT = 2;
|
||||
|
||||
/**
|
||||
* May read items from the cache, and add items to the cache.
|
||||
*/
|
||||
public const MODE_NORMAL = 3;
|
||||
final public const int MODE_NORMAL = 3;
|
||||
|
||||
/**
|
||||
* The query will never read items from the cache,
|
||||
* but will refresh items to the cache as it reads them from the database.
|
||||
*/
|
||||
public const MODE_REFRESH = 4;
|
||||
final public const int MODE_REFRESH = 4;
|
||||
|
||||
public function getEntityCacheRegion(string $className): Region|null;
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ use Doctrine\ORM\Cache\Persister\CachedPersister;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\ORMInvalidArgumentException;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Override;
|
||||
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
@@ -40,6 +40,7 @@ class DefaultCache implements Cache
|
||||
->getCacheFactory();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getEntityCacheRegion(string $className): Region|null
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -52,6 +53,7 @@ class DefaultCache implements Cache
|
||||
return $persister->getCacheRegion();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getCollectionCacheRegion(string $className, string $association): Region|null
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -64,6 +66,7 @@ class DefaultCache implements Cache
|
||||
return $persister->getCacheRegion();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function containsEntity(string $className, mixed $identifier): bool
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -76,6 +79,7 @@ class DefaultCache implements Cache
|
||||
return $persister->getCacheRegion()->contains($this->buildEntityCacheKey($metadata, $identifier));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictEntity(string $className, mixed $identifier): void
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -88,6 +92,7 @@ class DefaultCache implements Cache
|
||||
$persister->getCacheRegion()->evict($this->buildEntityCacheKey($metadata, $identifier));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictEntityRegion(string $className): void
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -100,6 +105,7 @@ class DefaultCache implements Cache
|
||||
$persister->getCacheRegion()->evictAll();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictEntityRegions(): void
|
||||
{
|
||||
$metadatas = $this->em->getMetadataFactory()->getAllMetadata();
|
||||
@@ -115,6 +121,7 @@ class DefaultCache implements Cache
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function containsCollection(string $className, string $association, mixed $ownerIdentifier): bool
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -127,6 +134,7 @@ class DefaultCache implements Cache
|
||||
return $persister->getCacheRegion()->contains($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictCollection(string $className, string $association, mixed $ownerIdentifier): void
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -139,6 +147,7 @@ class DefaultCache implements Cache
|
||||
$persister->getCacheRegion()->evict($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictCollectionRegion(string $className, string $association): void
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -151,6 +160,7 @@ class DefaultCache implements Cache
|
||||
$persister->getCacheRegion()->evictAll();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictCollectionRegions(): void
|
||||
{
|
||||
$metadatas = $this->em->getMetadataFactory()->getAllMetadata();
|
||||
@@ -172,11 +182,13 @@ class DefaultCache implements Cache
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function containsQuery(string $regionName): bool
|
||||
{
|
||||
return isset($this->queryCaches[$regionName]);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictQueryRegion(string|null $regionName = null): void
|
||||
{
|
||||
if ($regionName === null && $this->defaultQueryCache !== null) {
|
||||
@@ -190,6 +202,7 @@ class DefaultCache implements Cache
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictQueryRegions(): void
|
||||
{
|
||||
$this->getQueryCache()->clear();
|
||||
@@ -199,6 +212,7 @@ class DefaultCache implements Cache
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getQueryCache(string|null $regionName = null): QueryCache
|
||||
{
|
||||
if ($regionName === null) {
|
||||
@@ -233,7 +247,7 @@ class DefaultCache implements Cache
|
||||
private function toIdentifierArray(ClassMetadata $metadata, mixed $identifier): array
|
||||
{
|
||||
if (is_object($identifier)) {
|
||||
$class = DefaultProxyClassNameResolver::getClass($identifier);
|
||||
$class = $identifier::class;
|
||||
if ($this->em->getMetadataFactory()->hasMetadataFor($class)) {
|
||||
$identifier = $this->uow->getSingleIdentifierValue($identifier)
|
||||
?? throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($class);
|
||||
|
||||
@@ -23,6 +23,7 @@ use Doctrine\ORM\Persisters\Collection\CollectionPersister;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use Override;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
|
||||
use function assert;
|
||||
@@ -63,6 +64,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
$this->timestampRegion = $region;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata): CachedEntityPersister
|
||||
{
|
||||
assert($metadata->cache !== null);
|
||||
@@ -88,6 +90,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildCachedCollectionPersister(
|
||||
EntityManagerInterface $em,
|
||||
CollectionPersister $persister,
|
||||
@@ -116,6 +119,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildQueryCache(EntityManagerInterface $em, string|null $regionName = null): QueryCache
|
||||
{
|
||||
return new DefaultQueryCache(
|
||||
@@ -129,11 +133,13 @@ class DefaultCacheFactory implements CacheFactory
|
||||
);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildCollectionHydrator(EntityManagerInterface $em, AssociationMapping $mapping): CollectionHydrator
|
||||
{
|
||||
return new DefaultCollectionHydrator($em);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata): EntityHydrator
|
||||
{
|
||||
return new DefaultEntityHydrator($em);
|
||||
@@ -142,6 +148,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getRegion(array $cache): Region
|
||||
{
|
||||
if (isset($this->regions[$cache['region']])) {
|
||||
@@ -170,6 +177,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
return $this->regions[$cache['region']] = $region;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getTimestampRegion(): TimestampRegion
|
||||
{
|
||||
if ($this->timestampRegion === null) {
|
||||
@@ -182,6 +190,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
return $this->timestampRegion;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createCache(EntityManagerInterface $entityManager): Cache
|
||||
{
|
||||
return new DefaultCache($entityManager);
|
||||
|
||||
@@ -11,6 +11,7 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Override;
|
||||
|
||||
use function assert;
|
||||
|
||||
@@ -30,6 +31,7 @@ class DefaultCollectionHydrator implements CollectionHydrator
|
||||
$this->uow = $em->getUnitOfWork();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, array|Collection $collection): CollectionCacheEntry
|
||||
{
|
||||
$data = [];
|
||||
@@ -41,6 +43,7 @@ class DefaultCollectionHydrator implements CollectionHydrator
|
||||
return new CollectionCacheEntry($data);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection): array|null
|
||||
{
|
||||
$assoc = $metadata->associationMappings[$key->association];
|
||||
|
||||
@@ -6,10 +6,10 @@ namespace Doctrine\ORM\Cache;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Doctrine\ORM\Utility\IdentifierFlattener;
|
||||
use Override;
|
||||
|
||||
use function assert;
|
||||
use function is_array;
|
||||
@@ -34,6 +34,7 @@ class DefaultEntityHydrator implements EntityHydrator
|
||||
$this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory());
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, object $entity): EntityCacheEntry
|
||||
{
|
||||
$data = $this->uow->getOriginalEntityData($entity);
|
||||
@@ -97,7 +98,7 @@ class DefaultEntityHydrator implements EntityHydrator
|
||||
}
|
||||
|
||||
if (! isset($assoc->id)) {
|
||||
$targetClass = DefaultProxyClassNameResolver::getClass($data[$name]);
|
||||
$targetClass = $data[$name]::class;
|
||||
$targetId = $this->uow->getEntityIdentifier($data[$name]);
|
||||
$data[$name] = new AssociationCacheEntry($targetClass, $targetId);
|
||||
|
||||
@@ -125,6 +126,7 @@ class DefaultEntityHydrator implements EntityHydrator
|
||||
return new EntityCacheEntry($metadata->name, $data);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, object|null $entity = null): object|null
|
||||
{
|
||||
$data = $entry->data;
|
||||
|
||||
@@ -18,6 +18,7 @@ use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Override;
|
||||
|
||||
use function array_map;
|
||||
use function array_shift;
|
||||
@@ -54,6 +55,7 @@ class DefaultQueryCache implements QueryCache
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = []): array|null
|
||||
{
|
||||
if (! ($key->cacheMode & Cache::MODE_GET)) {
|
||||
@@ -197,6 +199,7 @@ class DefaultQueryCache implements QueryCache
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function put(QueryCacheKey $key, ResultSetMapping $rsm, mixed $result, array $hints = []): bool
|
||||
{
|
||||
if ($rsm->scalarMappings) {
|
||||
@@ -406,11 +409,13 @@ class DefaultQueryCache implements QueryCache
|
||||
return $values;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function clear(): bool
|
||||
{
|
||||
return $this->region->evictAll();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getRegion(): Region
|
||||
{
|
||||
return $this->region;
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Doctrine\ORM\Cache\Logging;
|
||||
use Doctrine\ORM\Cache\CollectionCacheKey;
|
||||
use Doctrine\ORM\Cache\EntityCacheKey;
|
||||
use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
use Override;
|
||||
|
||||
class CacheLoggerChain implements CacheLogger
|
||||
{
|
||||
@@ -29,6 +30,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
return $this->loggers;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function collectionCacheHit(string $regionName, CollectionCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -36,6 +38,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function collectionCacheMiss(string $regionName, CollectionCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -43,6 +46,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function collectionCachePut(string $regionName, CollectionCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -50,6 +54,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function entityCacheHit(string $regionName, EntityCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -57,6 +62,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function entityCacheMiss(string $regionName, EntityCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -64,6 +70,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function entityCachePut(string $regionName, EntityCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -71,6 +78,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function queryCacheHit(string $regionName, QueryCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -78,6 +86,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function queryCacheMiss(string $regionName, QueryCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -85,6 +94,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function queryCachePut(string $regionName, QueryCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Doctrine\ORM\Cache\Logging;
|
||||
use Doctrine\ORM\Cache\CollectionCacheKey;
|
||||
use Doctrine\ORM\Cache\EntityCacheKey;
|
||||
use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
use Override;
|
||||
|
||||
use function array_sum;
|
||||
|
||||
@@ -24,54 +25,63 @@ class StatisticsCacheLogger implements CacheLogger
|
||||
/** @var array<string, int> */
|
||||
private array $cachePutCountMap = [];
|
||||
|
||||
#[Override]
|
||||
public function collectionCacheMiss(string $regionName, CollectionCacheKey $key): void
|
||||
{
|
||||
$this->cacheMissCountMap[$regionName]
|
||||
= ($this->cacheMissCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function collectionCacheHit(string $regionName, CollectionCacheKey $key): void
|
||||
{
|
||||
$this->cacheHitCountMap[$regionName]
|
||||
= ($this->cacheHitCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function collectionCachePut(string $regionName, CollectionCacheKey $key): void
|
||||
{
|
||||
$this->cachePutCountMap[$regionName]
|
||||
= ($this->cachePutCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function entityCacheMiss(string $regionName, EntityCacheKey $key): void
|
||||
{
|
||||
$this->cacheMissCountMap[$regionName]
|
||||
= ($this->cacheMissCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function entityCacheHit(string $regionName, EntityCacheKey $key): void
|
||||
{
|
||||
$this->cacheHitCountMap[$regionName]
|
||||
= ($this->cacheHitCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function entityCachePut(string $regionName, EntityCacheKey $key): void
|
||||
{
|
||||
$this->cachePutCountMap[$regionName]
|
||||
= ($this->cachePutCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function queryCacheHit(string $regionName, QueryCacheKey $key): void
|
||||
{
|
||||
$this->cacheHitCountMap[$regionName]
|
||||
= ($this->cacheHitCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function queryCacheMiss(string $regionName, QueryCacheKey $key): void
|
||||
{
|
||||
$this->cacheMissCountMap[$regionName]
|
||||
= ($this->cacheMissCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function queryCachePut(string $regionName, QueryCacheKey $key): void
|
||||
{
|
||||
$this->cachePutCountMap[$regionName]
|
||||
|
||||
@@ -17,9 +17,9 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Query\FilterCollection;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Override;
|
||||
|
||||
use function array_values;
|
||||
use function assert;
|
||||
@@ -63,21 +63,25 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
$this->targetEntity = $em->getClassMetadata($association->targetEntity);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getCacheRegion(): Region
|
||||
{
|
||||
return $this->region;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getSourceEntityMetadata(): ClassMetadata
|
||||
{
|
||||
return $this->sourceEntity;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getTargetEntityMetadata(): ClassMetadata
|
||||
{
|
||||
return $this->targetEntity;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key): array|null
|
||||
{
|
||||
$cache = $this->region->get($key);
|
||||
@@ -89,6 +93,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
return $this->hydrator->loadCacheEntry($this->sourceEntity, $key, $cache, $collection);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function storeCollectionCache(CollectionCacheKey $key, Collection|array $elements): void
|
||||
{
|
||||
$associationMapping = $this->sourceEntity->associationMappings[$key->association];
|
||||
@@ -111,7 +116,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
}
|
||||
|
||||
$class = $this->targetEntity;
|
||||
$className = DefaultProxyClassNameResolver::getClass($elements[$index]);
|
||||
$className = $elements[$index]::class;
|
||||
|
||||
if ($className !== $this->targetEntity->name) {
|
||||
$class = $this->metadataFactory->getMetadataFor($className);
|
||||
@@ -128,16 +133,19 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function contains(PersistentCollection $collection, object $element): bool
|
||||
{
|
||||
return $this->persister->contains($collection, $element);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function containsKey(PersistentCollection $collection, mixed $key): bool
|
||||
{
|
||||
return $this->persister->containsKey($collection, $key);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function count(PersistentCollection $collection): int
|
||||
{
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
@@ -151,6 +159,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
return $this->persister->count($collection);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function get(PersistentCollection $collection, mixed $index): mixed
|
||||
{
|
||||
return $this->persister->get($collection, $index);
|
||||
@@ -159,6 +168,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function slice(PersistentCollection $collection, int $offset, int|null $length = null): array
|
||||
{
|
||||
return $this->persister->slice($collection, $offset, $length);
|
||||
@@ -167,6 +177,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function loadCriteria(PersistentCollection $collection, Criteria $criteria): array
|
||||
{
|
||||
return $this->persister->loadCriteria($collection, $criteria);
|
||||
|
||||
@@ -6,11 +6,13 @@ namespace Doctrine\ORM\Cache\Persister\Collection;
|
||||
|
||||
use Doctrine\ORM\Cache\CollectionCacheKey;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Override;
|
||||
|
||||
use function spl_object_id;
|
||||
|
||||
class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPersister
|
||||
{
|
||||
#[Override]
|
||||
public function afterTransactionComplete(): void
|
||||
{
|
||||
if (isset($this->queuedCache['update'])) {
|
||||
@@ -28,11 +30,13 @@ class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPers
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function afterTransactionRolledBack(): void
|
||||
{
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function delete(PersistentCollection $collection): void
|
||||
{
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
@@ -43,6 +47,7 @@ class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPers
|
||||
$this->queuedCache['delete'][spl_object_id($collection)] = $key;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function update(PersistentCollection $collection): void
|
||||
{
|
||||
$isInitialized = $collection->isInitialized();
|
||||
|
||||
@@ -6,15 +6,16 @@ namespace Doctrine\ORM\Cache\Persister\Collection;
|
||||
|
||||
use Doctrine\ORM\Cache\Exception\CannotUpdateReadOnlyCollection;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Override;
|
||||
|
||||
class ReadOnlyCachedCollectionPersister extends NonStrictReadWriteCachedCollectionPersister
|
||||
{
|
||||
#[Override]
|
||||
public function update(PersistentCollection $collection): void
|
||||
{
|
||||
if ($collection->isDirty() && $collection->getSnapshot()) {
|
||||
throw CannotUpdateReadOnlyCollection::fromEntityAndField(
|
||||
DefaultProxyClassNameResolver::getClass($collection->getOwner()),
|
||||
$collection->getOwner()::class,
|
||||
$this->association->fieldName,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
|
||||
use Override;
|
||||
|
||||
use function spl_object_id;
|
||||
|
||||
@@ -24,6 +25,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
|
||||
parent::__construct($persister, $region, $em, $association);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function afterTransactionComplete(): void
|
||||
{
|
||||
if (isset($this->queuedCache['update'])) {
|
||||
@@ -41,6 +43,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function afterTransactionRolledBack(): void
|
||||
{
|
||||
if (isset($this->queuedCache['update'])) {
|
||||
@@ -58,6 +61,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function delete(PersistentCollection $collection): void
|
||||
{
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
@@ -76,6 +80,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
|
||||
];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function update(PersistentCollection $collection): void
|
||||
{
|
||||
$isInitialized = $collection->isInitialized();
|
||||
|
||||
@@ -23,13 +23,12 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Query\FilterCollection;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Override;
|
||||
|
||||
use function array_merge;
|
||||
use function func_get_args;
|
||||
use function serialize;
|
||||
use function sha1;
|
||||
|
||||
@@ -77,6 +76,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
$this->timestampKey = new TimestampCacheKey($this->class->rootEntityName);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function addInsert(object $entity): void
|
||||
{
|
||||
$this->persister->addInsert($entity);
|
||||
@@ -85,15 +85,17 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getInserts(): array
|
||||
{
|
||||
return $this->persister->getInserts();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getSelectSQL(
|
||||
array|Criteria $criteria,
|
||||
AssociationMapping|null $assoc = null,
|
||||
LockMode|int|null $lockMode = null,
|
||||
LockMode|null $lockMode = null,
|
||||
int|null $limit = null,
|
||||
int|null $offset = null,
|
||||
array|null $orderBy = null,
|
||||
@@ -101,21 +103,25 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $this->persister->getSelectSQL($criteria, $assoc, $lockMode, $limit, $offset, $orderBy);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getCountSQL(array|Criteria $criteria = []): string
|
||||
{
|
||||
return $this->persister->getCountSQL($criteria);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getInsertSQL(): string
|
||||
{
|
||||
return $this->persister->getInsertSQL();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getResultSetMapping(): ResultSetMapping
|
||||
{
|
||||
return $this->persister->getResultSetMapping();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getSelectConditionStatementSQL(
|
||||
string $field,
|
||||
mixed $value,
|
||||
@@ -125,6 +131,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $this->persister->getSelectConditionStatementSQL($field, $value, $assoc, $comparison);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function exists(object $entity, Criteria|null $extraConditions = null): bool
|
||||
{
|
||||
if ($extraConditions === null) {
|
||||
@@ -138,20 +145,23 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $this->persister->exists($entity, $extraConditions);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getCacheRegion(): Region
|
||||
{
|
||||
return $this->region;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getEntityHydrator(): EntityHydrator
|
||||
{
|
||||
return $this->hydrator;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function storeEntityCache(object $entity, EntityCacheKey $key): bool
|
||||
{
|
||||
$class = $this->class;
|
||||
$className = DefaultProxyClassNameResolver::getClass($entity);
|
||||
$className = $entity::class;
|
||||
|
||||
if ($className !== $this->class->name) {
|
||||
$class = $this->metadataFactory->getMetadataFor($className);
|
||||
@@ -225,6 +235,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function expandParameters(array $criteria): array
|
||||
{
|
||||
return $this->persister->expandParameters($criteria);
|
||||
@@ -233,11 +244,13 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function expandCriteriaParameters(Criteria $criteria): array
|
||||
{
|
||||
return $this->persister->expandCriteriaParameters($criteria);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getClassMetadata(): ClassMetadata
|
||||
{
|
||||
return $this->persister->getClassMetadata();
|
||||
@@ -246,6 +259,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getManyToManyCollection(
|
||||
AssociationMapping $assoc,
|
||||
object $sourceEntity,
|
||||
@@ -258,6 +272,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getOneToManyCollection(
|
||||
AssociationMapping $assoc,
|
||||
object $sourceEntity,
|
||||
@@ -267,11 +282,13 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $this->persister->getOneToManyCollection($assoc, $sourceEntity, $offset, $limit);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getOwningTable(string $fieldName): string
|
||||
{
|
||||
return $this->persister->getOwningTable($fieldName);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function executeInserts(): void
|
||||
{
|
||||
// The commit order/foreign key relationships may make it necessary that multiple calls to executeInsert()
|
||||
@@ -288,12 +305,13 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function load(
|
||||
array $criteria,
|
||||
object|null $entity = null,
|
||||
AssociationMapping|null $assoc = null,
|
||||
array $hints = [],
|
||||
LockMode|int|null $lockMode = null,
|
||||
LockMode|null $lockMode = null,
|
||||
int|null $limit = null,
|
||||
array|null $orderBy = null,
|
||||
): object|null {
|
||||
@@ -335,6 +353,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function loadAll(
|
||||
array $criteria = [],
|
||||
array|null $orderBy = null,
|
||||
@@ -371,6 +390,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function loadById(array $identifier, object|null $entity = null): object|null
|
||||
{
|
||||
$cacheKey = new EntityCacheKey($this->class->rootEntityName, $identifier);
|
||||
@@ -398,7 +418,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
}
|
||||
|
||||
$class = $this->class;
|
||||
$className = DefaultProxyClassNameResolver::getClass($entity);
|
||||
$className = $entity::class;
|
||||
|
||||
if ($className !== $this->class->name) {
|
||||
$class = $this->metadataFactory->getMetadataFor($className);
|
||||
@@ -420,6 +440,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $entity;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function count(array|Criteria $criteria = []): int
|
||||
{
|
||||
return $this->persister->count($criteria);
|
||||
@@ -428,6 +449,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function loadCriteria(Criteria $criteria): array
|
||||
{
|
||||
$orderBy = $criteria->orderings();
|
||||
@@ -463,6 +485,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function loadManyToManyCollection(
|
||||
AssociationMapping $assoc,
|
||||
object $sourceEntity,
|
||||
@@ -494,6 +517,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $list;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function loadOneToManyCollection(
|
||||
AssociationMapping $assoc,
|
||||
object $sourceEntity,
|
||||
@@ -528,6 +552,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function loadOneToOneEntity(AssociationMapping $assoc, object $sourceEntity, array $identifier = []): object|null
|
||||
{
|
||||
return $this->persister->loadOneToOneEntity($assoc, $sourceEntity, $identifier);
|
||||
@@ -536,7 +561,8 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function lock(array $criteria, LockMode|int $lockMode): void
|
||||
#[Override]
|
||||
public function lock(array $criteria, LockMode $lockMode): void
|
||||
{
|
||||
$this->persister->lock($criteria, $lockMode);
|
||||
}
|
||||
@@ -544,16 +570,15 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function refresh(array $id, object $entity, LockMode|int|null $lockMode = null): void
|
||||
#[Override]
|
||||
public function refresh(array $id, object $entity, LockMode|null $lockMode = null): void
|
||||
{
|
||||
$this->persister->refresh($id, $entity, $lockMode);
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $ownerId */
|
||||
protected function buildCollectionCacheKey(AssociationMapping $association, array $ownerId, /* string $filterHash */): CollectionCacheKey
|
||||
protected function buildCollectionCacheKey(AssociationMapping $association, array $ownerId, string $filterHash): CollectionCacheKey
|
||||
{
|
||||
$filterHash = (string) (func_get_args()[2] ?? ''); // todo: move to argument in next major release
|
||||
|
||||
return new CollectionCacheKey(
|
||||
$this->metadataFactory->getMetadataFor($association->sourceEntity)->rootEntityName,
|
||||
$association->fieldName,
|
||||
|
||||
@@ -5,12 +5,14 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Cache\Persister\Entity;
|
||||
|
||||
use Doctrine\ORM\Cache\EntityCacheKey;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Specific non-strict read/write cached entity persister
|
||||
*/
|
||||
class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
{
|
||||
#[Override]
|
||||
public function afterTransactionComplete(): void
|
||||
{
|
||||
$isChanged = false;
|
||||
@@ -42,11 +44,13 @@ class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function afterTransactionRolledBack(): void
|
||||
{
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function delete(object $entity): bool
|
||||
{
|
||||
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
|
||||
@@ -61,6 +65,7 @@ class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function update(object $entity): void
|
||||
{
|
||||
$this->persister->update($entity);
|
||||
|
||||
@@ -5,15 +5,16 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Cache\Persister\Entity;
|
||||
|
||||
use Doctrine\ORM\Cache\Exception\CannotUpdateReadOnlyEntity;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Specific read-only region entity persister
|
||||
*/
|
||||
class ReadOnlyCachedEntityPersister extends NonStrictReadWriteCachedEntityPersister
|
||||
{
|
||||
#[Override]
|
||||
public function update(object $entity): void
|
||||
{
|
||||
throw CannotUpdateReadOnlyEntity::fromEntity(DefaultProxyClassNameResolver::getClass($entity));
|
||||
throw CannotUpdateReadOnlyEntity::fromEntity($entity::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use Doctrine\ORM\Cache\EntityCacheKey;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Specific read-write entity persister
|
||||
@@ -20,6 +21,7 @@ class ReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
parent::__construct($persister, $region, $em, $class);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function afterTransactionComplete(): void
|
||||
{
|
||||
$isChanged = true;
|
||||
@@ -47,6 +49,7 @@ class ReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function afterTransactionRolledBack(): void
|
||||
{
|
||||
if (isset($this->queuedCache['update'])) {
|
||||
@@ -64,6 +67,7 @@ class ReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function delete(object $entity): bool
|
||||
{
|
||||
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
|
||||
@@ -86,6 +90,7 @@ class ReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function update(object $entity): void
|
||||
{
|
||||
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
|
||||
|
||||
@@ -9,6 +9,7 @@ use Doctrine\ORM\Cache\CacheKey;
|
||||
use Doctrine\ORM\Cache\CollectionCacheEntry;
|
||||
use Doctrine\ORM\Cache\Lock;
|
||||
use Doctrine\ORM\Cache\Region;
|
||||
use Override;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Traversable;
|
||||
@@ -22,8 +23,8 @@ use function strtr;
|
||||
*/
|
||||
class DefaultRegion implements Region
|
||||
{
|
||||
private const REGION_KEY_SEPARATOR = '_';
|
||||
private const REGION_PREFIX = 'DC2_REGION_';
|
||||
private const string REGION_KEY_SEPARATOR = '_';
|
||||
private const string REGION_PREFIX = 'DC2_REGION_';
|
||||
|
||||
public function __construct(
|
||||
private readonly string $name,
|
||||
@@ -32,16 +33,19 @@ class DefaultRegion implements Region
|
||||
) {
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function contains(CacheKey $key): bool
|
||||
{
|
||||
return $this->cacheItemPool->hasItem($this->getCacheEntryKey($key));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function get(CacheKey $key): CacheEntry|null
|
||||
{
|
||||
$item = $this->cacheItemPool->getItem($this->getCacheEntryKey($key));
|
||||
@@ -54,6 +58,7 @@ class DefaultRegion implements Region
|
||||
return $entry;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getMultiple(CollectionCacheEntry $collection): array|null
|
||||
{
|
||||
$keys = array_map(
|
||||
@@ -83,6 +88,7 @@ class DefaultRegion implements Region
|
||||
return $result;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool
|
||||
{
|
||||
$item = $this->cacheItemPool
|
||||
@@ -96,11 +102,13 @@ class DefaultRegion implements Region
|
||||
return $this->cacheItemPool->save($item);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evict(CacheKey $key): bool
|
||||
{
|
||||
return $this->cacheItemPool->deleteItem($this->getCacheEntryKey($key));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictAll(): bool
|
||||
{
|
||||
return $this->cacheItemPool->clear(self::REGION_PREFIX . $this->name);
|
||||
|
||||
@@ -11,6 +11,7 @@ use Doctrine\ORM\Cache\ConcurrentRegion;
|
||||
use Doctrine\ORM\Cache\Lock;
|
||||
use Doctrine\ORM\Cache\Region;
|
||||
use InvalidArgumentException;
|
||||
use Override;
|
||||
|
||||
use function array_filter;
|
||||
use function array_map;
|
||||
@@ -35,7 +36,7 @@ use const LOCK_EX;
|
||||
*/
|
||||
class FileLockRegion implements ConcurrentRegion
|
||||
{
|
||||
final public const LOCK_EXTENSION = 'lock';
|
||||
final public const string LOCK_EXTENSION = 'lock';
|
||||
|
||||
/**
|
||||
* @param numeric-string|int $lockLifetime
|
||||
@@ -102,11 +103,13 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return @fileatime($filename);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->region->getName();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function contains(CacheKey $key): bool
|
||||
{
|
||||
if ($this->isLocked($key)) {
|
||||
@@ -116,6 +119,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $this->region->contains($key);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function get(CacheKey $key): CacheEntry|null
|
||||
{
|
||||
if ($this->isLocked($key)) {
|
||||
@@ -125,6 +129,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $this->region->get($key);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getMultiple(CollectionCacheEntry $collection): array|null
|
||||
{
|
||||
if (array_filter(array_map($this->isLocked(...), $collection->identifiers))) {
|
||||
@@ -134,6 +139,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $this->region->getMultiple($collection);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool
|
||||
{
|
||||
if ($this->isLocked($key, $lock)) {
|
||||
@@ -143,6 +149,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $this->region->put($key, $entry);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evict(CacheKey $key): bool
|
||||
{
|
||||
if ($this->isLocked($key)) {
|
||||
@@ -152,6 +159,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $this->region->evict($key);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictAll(): bool
|
||||
{
|
||||
// The check below is necessary because on some platforms glob returns false
|
||||
@@ -165,6 +173,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $this->region->evictAll();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function lock(CacheKey $key): Lock|null
|
||||
{
|
||||
if ($this->isLocked($key)) {
|
||||
@@ -183,6 +192,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $lock;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function unlock(CacheKey $key, Lock $lock): bool
|
||||
{
|
||||
if ($this->isLocked($key, $lock)) {
|
||||
|
||||
@@ -7,12 +7,14 @@ namespace Doctrine\ORM\Cache\Region;
|
||||
use Doctrine\ORM\Cache\CacheKey;
|
||||
use Doctrine\ORM\Cache\TimestampCacheEntry;
|
||||
use Doctrine\ORM\Cache\TimestampRegion;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Tracks the timestamps of the most recent updates to particular keys.
|
||||
*/
|
||||
class UpdateTimestampCache extends DefaultRegion implements TimestampRegion
|
||||
{
|
||||
#[Override]
|
||||
public function update(CacheKey $key): void
|
||||
{
|
||||
$this->put($key, new TimestampCacheEntry());
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
use Override;
|
||||
|
||||
use function microtime;
|
||||
|
||||
class TimestampQueryCacheValidator implements QueryCacheValidator
|
||||
@@ -12,6 +14,7 @@ class TimestampQueryCacheValidator implements QueryCacheValidator
|
||||
{
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isValid(QueryCacheKey $key, QueryCacheEntry $entry): bool
|
||||
{
|
||||
if ($this->regionUpdated($key, $entry)) {
|
||||
|
||||
@@ -17,21 +17,17 @@ use Doctrine\ORM\Mapping\EntityListenerResolver;
|
||||
use Doctrine\ORM\Mapping\NamingStrategy;
|
||||
use Doctrine\ORM\Mapping\QuoteStrategy;
|
||||
use Doctrine\ORM\Mapping\TypedFieldMapper;
|
||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
||||
use Doctrine\ORM\Query\Filter\SQLFilter;
|
||||
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
|
||||
use Doctrine\ORM\Repository\RepositoryFactory;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
use LogicException;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
|
||||
use function class_exists;
|
||||
use function is_a;
|
||||
use function strtolower;
|
||||
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
/**
|
||||
* Configuration container for all configuration options of Doctrine.
|
||||
* It combines all configuration options from DBAL & ORM.
|
||||
@@ -58,58 +54,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
return $this->identityGenerationPreferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the directory where Doctrine generates any necessary proxy class files.
|
||||
*/
|
||||
public function setProxyDir(string $dir): void
|
||||
{
|
||||
$this->attributes['proxyDir'] = $dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the directory where Doctrine generates any necessary proxy class files.
|
||||
*/
|
||||
public function getProxyDir(): string|null
|
||||
{
|
||||
return $this->attributes['proxyDir'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the strategy for automatically generating proxy classes.
|
||||
*
|
||||
* @return ProxyFactory::AUTOGENERATE_*
|
||||
*/
|
||||
public function getAutoGenerateProxyClasses(): int
|
||||
{
|
||||
return $this->attributes['autoGenerateProxyClasses'] ?? ProxyFactory::AUTOGENERATE_ALWAYS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the strategy for automatically generating proxy classes.
|
||||
*
|
||||
* @param bool|ProxyFactory::AUTOGENERATE_* $autoGenerate True is converted to AUTOGENERATE_ALWAYS, false to AUTOGENERATE_NEVER.
|
||||
*/
|
||||
public function setAutoGenerateProxyClasses(bool|int $autoGenerate): void
|
||||
{
|
||||
$this->attributes['autoGenerateProxyClasses'] = (int) $autoGenerate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the namespace where proxy classes reside.
|
||||
*/
|
||||
public function getProxyNamespace(): string|null
|
||||
{
|
||||
return $this->attributes['proxyNamespace'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the namespace where proxy classes reside.
|
||||
*/
|
||||
public function setProxyNamespace(string $ns): void
|
||||
{
|
||||
$this->attributes['proxyNamespace'] = $ns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver implementation that is used for metadata caching.
|
||||
*
|
||||
@@ -595,62 +539,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
$this->attributes['schemaIgnoreClasses'] = $schemaIgnoreClasses;
|
||||
}
|
||||
|
||||
public function isNativeLazyObjectsEnabled(): bool
|
||||
{
|
||||
return $this->attributes['nativeLazyObjects'] ?? false;
|
||||
}
|
||||
|
||||
public function enableNativeLazyObjects(bool $nativeLazyObjects): void
|
||||
{
|
||||
if (PHP_VERSION_ID < 80400) {
|
||||
throw new LogicException('Lazy loading proxies require PHP 8.4 or higher.');
|
||||
}
|
||||
|
||||
$this->attributes['nativeLazyObjects'] = $nativeLazyObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* To be deprecated in 3.1.0
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function isLazyGhostObjectEnabled(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** To be deprecated in 3.1.0 */
|
||||
public function setLazyGhostObjectEnabled(bool $flag): void
|
||||
{
|
||||
if (! $flag) {
|
||||
throw new LogicException(<<<'EXCEPTION'
|
||||
The lazy ghost object feature cannot be disabled anymore.
|
||||
Please remove the call to setLazyGhostObjectEnabled(false).
|
||||
EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
/** To be deprecated in 3.1.0 */
|
||||
public function setRejectIdCollisionInIdentityMap(bool $flag): void
|
||||
{
|
||||
if (! $flag) {
|
||||
throw new LogicException(<<<'EXCEPTION'
|
||||
Rejecting ID collisions in the identity map cannot be disabled anymore.
|
||||
Please remove the call to setRejectIdCollisionInIdentityMap(false).
|
||||
EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To be deprecated in 3.1.0
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function isRejectIdCollisionInIdentityMapEnabled(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setEagerFetchBatchSize(int $batchSize = 100): void
|
||||
{
|
||||
$this->attributes['fetchModeSubselectBatchSize'] = $batchSize;
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Decorator;
|
||||
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\Common\EventManagerInterface;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\Cache;
|
||||
@@ -24,6 +24,7 @@ use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Doctrine\Persistence\ObjectManagerDecorator;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Base class for EntityManager decorators
|
||||
@@ -37,136 +38,163 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements
|
||||
$this->wrapped = $wrapped;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getRepository(string $className): EntityRepository
|
||||
{
|
||||
return $this->wrapped->getRepository($className);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getMetadataFactory(): ClassMetadataFactory
|
||||
{
|
||||
return $this->wrapped->getMetadataFactory();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getClassMetadata(string $className): ClassMetadata
|
||||
{
|
||||
return $this->wrapped->getClassMetadata($className);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getConnection(): Connection
|
||||
{
|
||||
return $this->wrapped->getConnection();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getExpressionBuilder(): Expr
|
||||
{
|
||||
return $this->wrapped->getExpressionBuilder();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function beginTransaction(): void
|
||||
{
|
||||
$this->wrapped->beginTransaction();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function wrapInTransaction(callable $func): mixed
|
||||
{
|
||||
return $this->wrapped->wrapInTransaction($func);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function commit(): void
|
||||
{
|
||||
$this->wrapped->commit();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function rollback(): void
|
||||
{
|
||||
$this->wrapped->rollback();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createQuery(string $dql = ''): Query
|
||||
{
|
||||
return $this->wrapped->createQuery($dql);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createNativeQuery(string $sql, ResultSetMapping $rsm): NativeQuery
|
||||
{
|
||||
return $this->wrapped->createNativeQuery($sql, $rsm);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createQueryBuilder(): QueryBuilder
|
||||
{
|
||||
return $this->wrapped->createQueryBuilder();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getReference(string $entityName, mixed $id): object|null
|
||||
{
|
||||
return $this->wrapped->getReference($entityName, $id);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function close(): void
|
||||
{
|
||||
$this->wrapped->close();
|
||||
}
|
||||
|
||||
public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void
|
||||
#[Override]
|
||||
public function lock(object $entity, LockMode $lockMode, DateTimeInterface|int|null $lockVersion = null): void
|
||||
{
|
||||
$this->wrapped->lock($entity, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
public function find(string $className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
#[Override]
|
||||
public function find(string $className, mixed $id, LockMode|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
{
|
||||
return $this->wrapped->find($className, $id, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
public function refresh(object $object, LockMode|int|null $lockMode = null): void
|
||||
#[Override]
|
||||
public function refresh(object $object, LockMode|null $lockMode = null): void
|
||||
{
|
||||
$this->wrapped->refresh($object, $lockMode);
|
||||
}
|
||||
|
||||
public function getEventManager(): EventManager
|
||||
#[Override]
|
||||
public function getEventManager(): EventManagerInterface
|
||||
{
|
||||
return $this->wrapped->getEventManager();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getConfiguration(): Configuration
|
||||
{
|
||||
return $this->wrapped->getConfiguration();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isOpen(): bool
|
||||
{
|
||||
return $this->wrapped->isOpen();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getUnitOfWork(): UnitOfWork
|
||||
{
|
||||
return $this->wrapped->getUnitOfWork();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function newHydrator(string|int $hydrationMode): AbstractHydrator
|
||||
{
|
||||
return $this->wrapped->newHydrator($hydrationMode);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getProxyFactory(): ProxyFactory
|
||||
{
|
||||
return $this->wrapped->getProxyFactory();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getFilters(): FilterCollection
|
||||
{
|
||||
return $this->wrapped->getFilters();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isFiltersStateClean(): bool
|
||||
{
|
||||
return $this->wrapped->isFiltersStateClean();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function hasFilters(): bool
|
||||
{
|
||||
return $this->wrapped->hasFilters();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getCache(): Cache|null
|
||||
{
|
||||
return $this->wrapped->getCache();
|
||||
|
||||
+48
-24
@@ -7,6 +7,7 @@ namespace Doctrine\ORM;
|
||||
use BackedEnum;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\Common\EventManagerInterface;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\Exception\EntityManagerClosed;
|
||||
@@ -18,18 +19,17 @@ use Doctrine\ORM\Exception\UnrecognizedIdentifierFields;
|
||||
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\Query\FilterCollection;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\Repository\RepositoryFactory;
|
||||
use Override;
|
||||
|
||||
use function array_keys;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function ltrim;
|
||||
use function method_exists;
|
||||
|
||||
/**
|
||||
* The EntityManager is the central access point to ORM functionality.
|
||||
@@ -43,7 +43,7 @@ use function method_exists;
|
||||
*
|
||||
* $paths = ['/path/to/entity/mapping/files'];
|
||||
*
|
||||
* $config = ORMSetup::createAttributeMetadataConfiguration($paths);
|
||||
* $config = ORMSetup::createAttributeMetadataConfig($paths);
|
||||
* $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'memory' => true], $config);
|
||||
* $entityManager = new EntityManager($connection, $config);
|
||||
*
|
||||
@@ -72,7 +72,7 @@ class EntityManager implements EntityManagerInterface
|
||||
/**
|
||||
* The event manager that is the central point of the event system.
|
||||
*/
|
||||
private EventManager $eventManager;
|
||||
private EventManagerInterface $eventManager;
|
||||
|
||||
/**
|
||||
* The proxy factory used to create dynamic proxies.
|
||||
@@ -113,17 +113,13 @@ class EntityManager implements EntityManagerInterface
|
||||
public function __construct(
|
||||
private Connection $conn,
|
||||
private Configuration $config,
|
||||
EventManager|null $eventManager = null,
|
||||
EventManagerInterface|null $eventManager = null,
|
||||
) {
|
||||
if (! $config->getMetadataDriverImpl()) {
|
||||
throw MissingMappingDriverImplementation::create();
|
||||
}
|
||||
|
||||
$this->eventManager = $eventManager
|
||||
?? (method_exists($conn, 'getEventManager')
|
||||
? $conn->getEventManager()
|
||||
: new EventManager()
|
||||
);
|
||||
$this->eventManager = $eventManager ?? new EventManager();
|
||||
|
||||
$metadataFactoryClassName = $config->getClassMetadataFactoryName();
|
||||
|
||||
@@ -134,12 +130,7 @@ class EntityManager implements EntityManagerInterface
|
||||
|
||||
$this->repositoryFactory = $config->getRepositoryFactory();
|
||||
$this->unitOfWork = new UnitOfWork($this);
|
||||
$this->proxyFactory = new ProxyFactory(
|
||||
$this,
|
||||
$config->getProxyDir(),
|
||||
$config->getProxyNamespace(),
|
||||
$config->getAutoGenerateProxyClasses(),
|
||||
);
|
||||
$this->proxyFactory = new ProxyFactory($this);
|
||||
|
||||
if ($config->isSecondLevelCacheEnabled()) {
|
||||
$cacheConfig = $config->getSecondLevelCacheConfiguration();
|
||||
@@ -148,31 +139,37 @@ class EntityManager implements EntityManagerInterface
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getConnection(): Connection
|
||||
{
|
||||
return $this->conn;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getMetadataFactory(): ClassMetadataFactory
|
||||
{
|
||||
return $this->metadataFactory;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getExpressionBuilder(): Expr
|
||||
{
|
||||
return $this->expressionBuilder ??= new Expr();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function beginTransaction(): void
|
||||
{
|
||||
$this->conn->beginTransaction();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getCache(): Cache|null
|
||||
{
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function wrapInTransaction(callable $func): mixed
|
||||
{
|
||||
$this->conn->beginTransaction();
|
||||
@@ -198,11 +195,13 @@ class EntityManager implements EntityManagerInterface
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function commit(): void
|
||||
{
|
||||
$this->conn->commit();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function rollback(): void
|
||||
{
|
||||
$this->conn->rollBack();
|
||||
@@ -215,11 +214,13 @@ class EntityManager implements EntityManagerInterface
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getClassMetadata(string $className): Mapping\ClassMetadata
|
||||
{
|
||||
return $this->metadataFactory->getMetadataFor($className);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createQuery(string $dql = ''): Query
|
||||
{
|
||||
$query = new Query($this);
|
||||
@@ -231,6 +232,7 @@ class EntityManager implements EntityManagerInterface
|
||||
return $query;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createNativeQuery(string $sql, ResultSetMapping $rsm): NativeQuery
|
||||
{
|
||||
$query = new NativeQuery($this);
|
||||
@@ -241,6 +243,7 @@ class EntityManager implements EntityManagerInterface
|
||||
return $query;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createQueryBuilder(): QueryBuilder
|
||||
{
|
||||
return new QueryBuilder($this);
|
||||
@@ -258,6 +261,7 @@ class EntityManager implements EntityManagerInterface
|
||||
* makes use of optimistic locking fails.
|
||||
* @throws ORMException
|
||||
*/
|
||||
#[Override]
|
||||
public function flush(): void
|
||||
{
|
||||
$this->errorIfClosed();
|
||||
@@ -267,7 +271,8 @@ class EntityManager implements EntityManagerInterface
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function find($className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
#[Override]
|
||||
public function find($className, mixed $id, LockMode|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
{
|
||||
$class = $this->metadataFactory->getMetadataFor(ltrim($className, '\\'));
|
||||
|
||||
@@ -285,7 +290,7 @@ class EntityManager implements EntityManagerInterface
|
||||
|
||||
foreach ($id as $i => $value) {
|
||||
if (is_object($value)) {
|
||||
$className = DefaultProxyClassNameResolver::getClass($value);
|
||||
$className = $value::class;
|
||||
if ($this->metadataFactory->hasMetadataFor($className)) {
|
||||
$id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
|
||||
|
||||
@@ -363,6 +368,7 @@ class EntityManager implements EntityManagerInterface
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getReference(string $entityName, mixed $id): object|null
|
||||
{
|
||||
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
|
||||
@@ -408,11 +414,13 @@ class EntityManager implements EntityManagerInterface
|
||||
* Clears the EntityManager. All entities that are currently managed
|
||||
* by this EntityManager become detached.
|
||||
*/
|
||||
#[Override]
|
||||
public function clear(): void
|
||||
{
|
||||
$this->unitOfWork->clear();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function close(): void
|
||||
{
|
||||
$this->clear();
|
||||
@@ -432,6 +440,7 @@ class EntityManager implements EntityManagerInterface
|
||||
* @throws ORMInvalidArgumentException
|
||||
* @throws ORMException
|
||||
*/
|
||||
#[Override]
|
||||
public function persist(object $object): void
|
||||
{
|
||||
$this->errorIfClosed();
|
||||
@@ -448,6 +457,7 @@ class EntityManager implements EntityManagerInterface
|
||||
* @throws ORMInvalidArgumentException
|
||||
* @throws ORMException
|
||||
*/
|
||||
#[Override]
|
||||
public function remove(object $object): void
|
||||
{
|
||||
$this->errorIfClosed();
|
||||
@@ -455,7 +465,8 @@ class EntityManager implements EntityManagerInterface
|
||||
$this->unitOfWork->remove($object);
|
||||
}
|
||||
|
||||
public function refresh(object $object, LockMode|int|null $lockMode = null): void
|
||||
#[Override]
|
||||
public function refresh(object $object, LockMode|null $lockMode = null): void
|
||||
{
|
||||
$this->errorIfClosed();
|
||||
|
||||
@@ -471,12 +482,14 @@ class EntityManager implements EntityManagerInterface
|
||||
*
|
||||
* @throws ORMInvalidArgumentException
|
||||
*/
|
||||
#[Override]
|
||||
public function detach(object $object): void
|
||||
{
|
||||
$this->unitOfWork->detach($object);
|
||||
}
|
||||
|
||||
public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void
|
||||
#[Override]
|
||||
public function lock(object $entity, LockMode $lockMode, DateTimeInterface|int|null $lockVersion = null): void
|
||||
{
|
||||
$this->unitOfWork->lock($entity, $lockMode, $lockVersion);
|
||||
}
|
||||
@@ -490,6 +503,7 @@ class EntityManager implements EntityManagerInterface
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
#[Override]
|
||||
public function getRepository(string $className): EntityRepository
|
||||
{
|
||||
return $this->repositoryFactory->getRepository($this, $className);
|
||||
@@ -500,6 +514,7 @@ class EntityManager implements EntityManagerInterface
|
||||
*
|
||||
* @return bool TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
|
||||
*/
|
||||
#[Override]
|
||||
public function contains(object $object): bool
|
||||
{
|
||||
return $this->unitOfWork->isScheduledForInsert($object)
|
||||
@@ -507,11 +522,13 @@ class EntityManager implements EntityManagerInterface
|
||||
&& ! $this->unitOfWork->isScheduledForDelete($object);
|
||||
}
|
||||
|
||||
public function getEventManager(): EventManager
|
||||
#[Override]
|
||||
public function getEventManager(): EventManagerInterface
|
||||
{
|
||||
return $this->eventManager;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getConfiguration(): Configuration
|
||||
{
|
||||
return $this->config;
|
||||
@@ -529,16 +546,19 @@ class EntityManager implements EntityManagerInterface
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isOpen(): bool
|
||||
{
|
||||
return ! $this->closed;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getUnitOfWork(): UnitOfWork
|
||||
{
|
||||
return $this->unitOfWork;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function newHydrator(string|int $hydrationMode): AbstractHydrator
|
||||
{
|
||||
return match ($hydrationMode) {
|
||||
@@ -552,11 +572,13 @@ class EntityManager implements EntityManagerInterface
|
||||
};
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getProxyFactory(): ProxyFactory
|
||||
{
|
||||
return $this->proxyFactory;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function initializeObject(object $obj): void
|
||||
{
|
||||
$this->unitOfWork->initializeObject($obj);
|
||||
@@ -565,33 +587,35 @@ class EntityManager implements EntityManagerInterface
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function isUninitializedObject($value): bool
|
||||
{
|
||||
return $this->unitOfWork->isUninitializedObject($value);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getFilters(): FilterCollection
|
||||
{
|
||||
return $this->filterCollection ??= new FilterCollection($this);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isFiltersStateClean(): bool
|
||||
{
|
||||
return $this->filterCollection === null || $this->filterCollection->isClean();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function hasFilters(): bool
|
||||
{
|
||||
return $this->filterCollection !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-param LockMode::* $lockMode
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
* @throws TransactionRequiredException
|
||||
*/
|
||||
private function checkLockRequirements(LockMode|int $lockMode, ClassMetadata $class): void
|
||||
private function checkLockRequirements(LockMode $lockMode, ClassMetadata $class): void
|
||||
{
|
||||
switch ($lockMode) {
|
||||
case LockMode::OPTIMISTIC:
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\Common\EventManagerInterface;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\Exception\ORMException;
|
||||
@@ -16,6 +16,7 @@ use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\Query\FilterCollection;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Override;
|
||||
|
||||
interface EntityManagerInterface extends ObjectManager
|
||||
{
|
||||
@@ -28,6 +29,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
#[Override]
|
||||
public function getRepository(string $className): EntityRepository;
|
||||
|
||||
/**
|
||||
@@ -40,6 +42,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
*/
|
||||
public function getConnection(): Connection;
|
||||
|
||||
#[Override]
|
||||
public function getMetadataFactory(): ClassMetadataFactory;
|
||||
|
||||
/**
|
||||
@@ -110,15 +113,13 @@ interface EntityManagerInterface extends ObjectManager
|
||||
/**
|
||||
* Finds an Entity by its identifier.
|
||||
*
|
||||
* @param string $className The class name of the entity to find.
|
||||
* @param mixed $id The identity of the entity to find.
|
||||
* @param LockMode|int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
|
||||
* or NULL if no specific lock mode should be used
|
||||
* during the search.
|
||||
* @param int|null $lockVersion The version of the entity to find when using
|
||||
* optimistic locking.
|
||||
* @param string $className The class name of the entity to find.
|
||||
* @param mixed $id The identity of the entity to find.
|
||||
* @param LockMode|null $lockMode The lock mode or NULL if no specific lock mode
|
||||
* should be used during the search.
|
||||
* @param int|null $lockVersion The version of the entity to find when using
|
||||
* optimistic locking.
|
||||
* @phpstan-param class-string<T> $className
|
||||
* @phpstan-param LockMode::*|null $lockMode
|
||||
*
|
||||
* @return object|null The entity instance or NULL if the entity can not be found.
|
||||
* @phpstan-return T|null
|
||||
@@ -130,22 +131,22 @@ interface EntityManagerInterface extends ObjectManager
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
public function find(string $className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null;
|
||||
#[Override]
|
||||
public function find(string $className, mixed $id, LockMode|null $lockMode = null, int|null $lockVersion = null): object|null;
|
||||
|
||||
/**
|
||||
* Refreshes the persistent state of an object from the database,
|
||||
* overriding any local changes that have not yet been persisted.
|
||||
*
|
||||
* @param LockMode|int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
|
||||
* or NULL if no specific lock mode should be used
|
||||
* during the search.
|
||||
* @phpstan-param LockMode::*|null $lockMode
|
||||
* @param LockMode|null $lockMode The lock mode or NULL if no specific lock mode
|
||||
* should be used during the search.
|
||||
*
|
||||
* @throws ORMInvalidArgumentException
|
||||
* @throws ORMException
|
||||
* @throws TransactionRequiredException
|
||||
*/
|
||||
public function refresh(object $object, LockMode|int|null $lockMode = null): void;
|
||||
#[Override]
|
||||
public function refresh(object $object, LockMode|null $lockMode = null): void;
|
||||
|
||||
/**
|
||||
* Gets a reference to the entity identified by the given type and identifier
|
||||
@@ -172,17 +173,15 @@ interface EntityManagerInterface extends ObjectManager
|
||||
/**
|
||||
* Acquire a lock on the given entity.
|
||||
*
|
||||
* @phpstan-param LockMode::* $lockMode
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
* @throws PessimisticLockException
|
||||
*/
|
||||
public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void;
|
||||
public function lock(object $entity, LockMode $lockMode, DateTimeInterface|int|null $lockVersion = null): void;
|
||||
|
||||
/**
|
||||
* Gets the EventManager used by the EntityManager.
|
||||
* Gets the EventManagerInterface used by the EntityManager.
|
||||
*/
|
||||
public function getEventManager(): EventManager;
|
||||
public function getEventManager(): EventManagerInterface;
|
||||
|
||||
/**
|
||||
* Gets the Configuration used by the EntityManager.
|
||||
@@ -237,5 +236,6 @@ interface EntityManagerInterface extends ObjectManager
|
||||
*
|
||||
* @phpstan-template T of object
|
||||
*/
|
||||
#[Override]
|
||||
public function getClassMetadata(string $className): Mapping\ClassMetadata;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
use Doctrine\ORM\Repository\Exception\InvalidMagicMethodCall;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use Override;
|
||||
|
||||
use function array_slice;
|
||||
use function lcfirst;
|
||||
@@ -73,15 +74,14 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
/**
|
||||
* Finds an entity by its primary key / identifier.
|
||||
*
|
||||
* @param LockMode|int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
|
||||
* or NULL if no specific lock mode should be used
|
||||
* during the search.
|
||||
* @phpstan-param LockMode::*|null $lockMode
|
||||
* @param LockMode|null $lockMode The lock mode or NULL if no specific lock mode
|
||||
* should be used during the search.
|
||||
*
|
||||
* @return object|null The entity instance or NULL if the entity can not be found.
|
||||
* @phpstan-return ?T
|
||||
*/
|
||||
public function find(mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
#[Override]
|
||||
public function find(mixed $id, LockMode|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
{
|
||||
return $this->em->find($this->entityName, $id, $lockMode, $lockVersion);
|
||||
}
|
||||
@@ -91,6 +91,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
*
|
||||
* @phpstan-return list<T> The entities.
|
||||
*/
|
||||
#[Override]
|
||||
public function findAll(): array
|
||||
{
|
||||
return $this->findBy([]);
|
||||
@@ -103,6 +104,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
*
|
||||
* @phpstan-return list<T>
|
||||
*/
|
||||
#[Override]
|
||||
public function findBy(array $criteria, array|null $orderBy = null, int|null $limit = null, int|null $offset = null): array
|
||||
{
|
||||
$persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
|
||||
@@ -118,6 +120,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
*
|
||||
* @phpstan-return T|null
|
||||
*/
|
||||
#[Override]
|
||||
public function findOneBy(array $criteria, array|null $orderBy = null): object|null
|
||||
{
|
||||
$persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
|
||||
@@ -175,6 +178,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
return $this->entityName;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getClassName(): string
|
||||
{
|
||||
return $this->getEntityName();
|
||||
@@ -197,6 +201,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
*
|
||||
* @phpstan-return AbstractLazyCollection<int, T>&Selectable<int, T>
|
||||
*/
|
||||
#[Override]
|
||||
public function matching(Criteria $criteria): AbstractLazyCollection&Selectable
|
||||
{
|
||||
$persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\Common\EventDispatcher;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\EntityListenerResolver;
|
||||
@@ -15,21 +15,21 @@ use Doctrine\ORM\Mapping\EntityListenerResolver;
|
||||
*/
|
||||
class ListenersInvoker
|
||||
{
|
||||
final public const INVOKE_NONE = 0;
|
||||
final public const INVOKE_LISTENERS = 1;
|
||||
final public const INVOKE_CALLBACKS = 2;
|
||||
final public const INVOKE_MANAGER = 4;
|
||||
final public const int INVOKE_NONE = 0;
|
||||
final public const int INVOKE_LISTENERS = 1;
|
||||
final public const int INVOKE_CALLBACKS = 2;
|
||||
final public const int INVOKE_MANAGER = 4;
|
||||
|
||||
/** The Entity listener resolver. */
|
||||
private readonly EntityListenerResolver $resolver;
|
||||
|
||||
/** The EventManager used for dispatching events. */
|
||||
private readonly EventManager $eventManager;
|
||||
/** The EventDispatcher used for dispatching events. */
|
||||
private readonly EventDispatcher $eventDispatcher;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->eventManager = $em->getEventManager();
|
||||
$this->resolver = $em->getConfiguration()->getEntityListenerResolver();
|
||||
$this->eventDispatcher = $em->getEventManager();
|
||||
$this->resolver = $em->getConfiguration()->getEntityListenerResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +52,7 @@ class ListenersInvoker
|
||||
$invoke |= self::INVOKE_LISTENERS;
|
||||
}
|
||||
|
||||
if ($this->eventManager->hasListeners($eventName)) {
|
||||
if ($this->eventDispatcher->hasListeners($eventName)) {
|
||||
$invoke |= self::INVOKE_MANAGER;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class ListenersInvoker
|
||||
}
|
||||
|
||||
if ($invoke & self::INVOKE_MANAGER) {
|
||||
$this->eventManager->dispatchEvent($eventName, $event);
|
||||
$this->eventDispatcher->dispatchEvent($eventName, $event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-13
@@ -24,7 +24,7 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const preRemove = 'preRemove';
|
||||
final public const string preRemove = 'preRemove';
|
||||
|
||||
/**
|
||||
* The postRemove event occurs for an entity after the entity has
|
||||
@@ -32,7 +32,7 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const postRemove = 'postRemove';
|
||||
final public const string postRemove = 'postRemove';
|
||||
|
||||
/**
|
||||
* The prePersist event occurs for a given entity before the respective
|
||||
@@ -40,7 +40,7 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const prePersist = 'prePersist';
|
||||
final public const string prePersist = 'prePersist';
|
||||
|
||||
/**
|
||||
* The postPersist event occurs for an entity after the entity has
|
||||
@@ -49,7 +49,7 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const postPersist = 'postPersist';
|
||||
final public const string postPersist = 'postPersist';
|
||||
|
||||
/**
|
||||
* The preUpdate event occurs before the database update operations to
|
||||
@@ -57,7 +57,7 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const preUpdate = 'preUpdate';
|
||||
final public const string preUpdate = 'preUpdate';
|
||||
|
||||
/**
|
||||
* The postUpdate event occurs after the database update operations to
|
||||
@@ -65,7 +65,7 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const postUpdate = 'postUpdate';
|
||||
final public const string postUpdate = 'postUpdate';
|
||||
|
||||
/**
|
||||
* The postLoad event occurs for an entity after the entity has been loaded
|
||||
@@ -78,26 +78,26 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const postLoad = 'postLoad';
|
||||
final public const string postLoad = 'postLoad';
|
||||
|
||||
/**
|
||||
* The loadClassMetadata event occurs after the mapping metadata for a class
|
||||
* has been loaded from a mapping source (attributes/xml).
|
||||
*/
|
||||
public const loadClassMetadata = 'loadClassMetadata';
|
||||
final public const string loadClassMetadata = 'loadClassMetadata';
|
||||
|
||||
/**
|
||||
* The onClassMetadataNotFound event occurs whenever loading metadata for a class
|
||||
* failed.
|
||||
*/
|
||||
public const onClassMetadataNotFound = 'onClassMetadataNotFound';
|
||||
final public const string onClassMetadataNotFound = 'onClassMetadataNotFound';
|
||||
|
||||
/**
|
||||
* The preFlush event occurs when the EntityManager#flush() operation is invoked,
|
||||
* but before any changes to managed entities have been calculated. This event is
|
||||
* always raised right after EntityManager#flush() call.
|
||||
*/
|
||||
public const preFlush = 'preFlush';
|
||||
final public const string preFlush = 'preFlush';
|
||||
|
||||
/**
|
||||
* The onFlush event occurs when the EntityManager#flush() operation is invoked,
|
||||
@@ -105,7 +105,7 @@ final class Events
|
||||
* actual database operations are executed. The event is only raised if there is
|
||||
* actually something to do for the underlying UnitOfWork.
|
||||
*/
|
||||
public const onFlush = 'onFlush';
|
||||
final public const string onFlush = 'onFlush';
|
||||
|
||||
/**
|
||||
* The postFlush event occurs when the EntityManager#flush() operation is invoked and
|
||||
@@ -113,11 +113,11 @@ final class Events
|
||||
* actually something to do for the underlying UnitOfWork. The event won't be raised if an error occurs during the
|
||||
* flush operation.
|
||||
*/
|
||||
public const postFlush = 'postFlush';
|
||||
final public const string postFlush = 'postFlush';
|
||||
|
||||
/**
|
||||
* The onClear event occurs when the EntityManager#clear() operation is invoked,
|
||||
* after all references to entities have been removed from the unit of work.
|
||||
*/
|
||||
public const onClear = 'onClear';
|
||||
final public const string onClear = 'onClear';
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use function sprintf;
|
||||
|
||||
final class MultipleSelectorsFoundException extends LogicException implements ORMException
|
||||
{
|
||||
public const MULTIPLE_SELECTORS_FOUND_EXCEPTION = 'Multiple selectors found: %s. Please select only one.';
|
||||
final public const string MULTIPLE_SELECTORS_FOUND_EXCEPTION = 'Multiple selectors found: %s. Please select only one.';
|
||||
|
||||
/** @param string[] $selectors */
|
||||
public static function create(array $selectors): self
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
/** @deprecated */
|
||||
final class NotSupported extends LogicException implements ORMException
|
||||
{
|
||||
public static function create(): self
|
||||
{
|
||||
return new self('This behaviour is (currently) not supported by Doctrine 2');
|
||||
}
|
||||
|
||||
public static function createForDbal3(string $context): self
|
||||
{
|
||||
return new self(sprintf(
|
||||
<<<'EXCEPTION'
|
||||
Context: %s
|
||||
Problem: Feature was deprecated in doctrine/dbal 2.x and is not supported by installed doctrine/dbal:3.x
|
||||
Solution: See the doctrine/deprecations logs for new alternative approaches.
|
||||
EXCEPTION
|
||||
,
|
||||
$context,
|
||||
));
|
||||
}
|
||||
|
||||
public static function createForPersistence3(string $context): self
|
||||
{
|
||||
return new self(sprintf(
|
||||
<<<'EXCEPTION'
|
||||
Context: %s
|
||||
Problem: Feature was deprecated in doctrine/persistence 2.x and is not supported by installed doctrine/persistence:3.x
|
||||
Solution: See the doctrine/deprecations logs for new alternative approaches.
|
||||
EXCEPTION
|
||||
,
|
||||
$context,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Exception\EntityMissingAssignedId;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Special generator for application-assigned identifiers (doesn't really generate anything).
|
||||
@@ -19,6 +20,7 @@ class AssignedGenerator extends AbstractIdGenerator
|
||||
*
|
||||
* @throws EntityMissingAssignedId
|
||||
*/
|
||||
#[Override]
|
||||
public function generateId(EntityManagerInterface $em, object|null $entity): array
|
||||
{
|
||||
$class = $em->getClassMetadata($entity::class);
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Id generator that obtains IDs from special "identity" columns. These are columns
|
||||
@@ -13,11 +14,13 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
*/
|
||||
class BigIntegerIdentityGenerator extends AbstractIdGenerator
|
||||
{
|
||||
#[Override]
|
||||
public function generateId(EntityManagerInterface $em, object|null $entity): string
|
||||
{
|
||||
return (string) $em->getConnection()->lastInsertId();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isPostInsertGenerator(): bool
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Id generator that obtains IDs from special "identity" columns. These are columns
|
||||
@@ -13,11 +14,13 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
*/
|
||||
class IdentityGenerator extends AbstractIdGenerator
|
||||
{
|
||||
#[Override]
|
||||
public function generateId(EntityManagerInterface $em, object|null $entity): int
|
||||
{
|
||||
return (int) $em->getConnection()->lastInsertId();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isPostInsertGenerator(): bool
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -5,17 +5,13 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Serializable;
|
||||
|
||||
use function serialize;
|
||||
use function unserialize;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Represents an ID generator that uses a database sequence.
|
||||
*/
|
||||
class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
class SequenceGenerator extends AbstractIdGenerator
|
||||
{
|
||||
private int $nextValue = 0;
|
||||
private int|null $maxValue = null;
|
||||
@@ -32,6 +28,7 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
) {
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function generateId(EntityManagerInterface $em, object|null $entity): int
|
||||
{
|
||||
if ($this->maxValue === null || $this->nextValue === $this->maxValue) {
|
||||
@@ -66,20 +63,6 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
return $this->nextValue;
|
||||
}
|
||||
|
||||
/** @deprecated without replacement. */
|
||||
final public function serialize(): string
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/11468',
|
||||
'%s() is deprecated, use __serialize() instead. %s won\'t implement the Serializable interface anymore in ORM 4.',
|
||||
__METHOD__,
|
||||
self::class,
|
||||
);
|
||||
|
||||
return serialize($this->__serialize());
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function __serialize(): array
|
||||
{
|
||||
@@ -89,20 +72,6 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
];
|
||||
}
|
||||
|
||||
/** @deprecated without replacement. */
|
||||
final public function unserialize(string $serialized): void
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/11468',
|
||||
'%s() is deprecated, use __unserialize() instead. %s won\'t implement the Serializable interface anymore in ORM 4.',
|
||||
__METHOD__,
|
||||
self::class,
|
||||
);
|
||||
|
||||
$this->__unserialize(unserialize($serialized));
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $data */
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
|
||||
@@ -17,15 +17,18 @@ use Doctrine\ORM\UnitOfWork;
|
||||
use Generator;
|
||||
use LogicException;
|
||||
use ReflectionClass;
|
||||
use ReflectionEnum;
|
||||
|
||||
use function array_key_exists;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function count;
|
||||
use function current;
|
||||
use function end;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function ksort;
|
||||
|
||||
/**
|
||||
@@ -126,8 +129,10 @@ abstract class AbstractHydrator
|
||||
} else {
|
||||
yield from $result;
|
||||
}
|
||||
} else {
|
||||
} elseif (is_object(current($result))) {
|
||||
yield $result;
|
||||
} else {
|
||||
yield array_merge(...$result);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -593,13 +598,18 @@ abstract class AbstractHydrator
|
||||
*/
|
||||
final protected function buildEnum(mixed $value, string $enumType): BackedEnum|array
|
||||
{
|
||||
$reflection = new ReflectionEnum($enumType);
|
||||
$isIntBacked = $reflection->isBacked() && $reflection->getBackingType()->getName() === 'int';
|
||||
|
||||
if (is_array($value)) {
|
||||
return array_map(
|
||||
static fn ($value) => $enumType::from($value),
|
||||
static fn ($value) => $enumType::from($isIntBacked ? (int) $value : $value),
|
||||
$value,
|
||||
);
|
||||
}
|
||||
|
||||
$value = $isIntBacked ? (int) $value : $value;
|
||||
|
||||
return $enumType::from($value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use Override;
|
||||
|
||||
use function array_key_last;
|
||||
use function count;
|
||||
use function is_array;
|
||||
@@ -32,6 +34,7 @@ class ArrayHydrator extends AbstractHydrator
|
||||
|
||||
private int $resultCounter = 0;
|
||||
|
||||
#[Override]
|
||||
protected function prepare(): void
|
||||
{
|
||||
$this->isSimpleQuery = count($this->resultSetMapping()->aliasMap) <= 1;
|
||||
@@ -46,6 +49,7 @@ class ArrayHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateAllData(): array
|
||||
{
|
||||
$result = [];
|
||||
@@ -60,6 +64,7 @@ class ArrayHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateRowData(array $row, array &$result): void
|
||||
{
|
||||
// 1) Initialize
|
||||
|
||||
@@ -10,6 +10,7 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Override;
|
||||
|
||||
use function array_fill_keys;
|
||||
use function array_keys;
|
||||
@@ -51,6 +52,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
/** @var mixed[] */
|
||||
private array $existingCollections = [];
|
||||
|
||||
#[Override]
|
||||
protected function prepare(): void
|
||||
{
|
||||
if (! isset($this->hints[UnitOfWork::HINT_DEFEREAGERLOAD])) {
|
||||
@@ -108,6 +110,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function cleanup(): void
|
||||
{
|
||||
$eagerLoad = isset($this->hints[UnitOfWork::HINT_DEFEREAGERLOAD]) && $this->hints[UnitOfWork::HINT_DEFEREAGERLOAD] === true;
|
||||
@@ -127,6 +130,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$this->uow->hydrationComplete();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function cleanupAfterRowIteration(): void
|
||||
{
|
||||
$this->identifierMap =
|
||||
@@ -139,6 +143,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateAllData(): array
|
||||
{
|
||||
$result = [];
|
||||
@@ -318,6 +323,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
* @param mixed[] $row The data of the row to process.
|
||||
* @param mixed[] $result The result array to fill.
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateRowData(array $row, array &$result): void
|
||||
{
|
||||
// Initialize
|
||||
@@ -574,6 +580,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
|
||||
/** @param mixed[] $data pre-hydrated SQL Result Row. */
|
||||
#[Override]
|
||||
protected function hydrateNestedEntity(array $data, string $dqlAlias): mixed
|
||||
{
|
||||
if (isset($this->resultSetMapping()->nestedEntities[$dqlAlias])) {
|
||||
@@ -587,6 +594,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
* When executed in a hydrate() loop we may have to clear internal state to
|
||||
* decrease memory consumption.
|
||||
*/
|
||||
#[Override]
|
||||
public function onClear(mixed $eventArgs): void
|
||||
{
|
||||
parent::onClear($eventArgs);
|
||||
|
||||
@@ -6,8 +6,8 @@ namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use Doctrine\DBAL\Driver\Exception;
|
||||
use Doctrine\ORM\Exception\MultipleSelectorsFoundException;
|
||||
use Override;
|
||||
|
||||
use function array_column;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
@@ -21,14 +21,13 @@ final class ScalarColumnHydrator extends AbstractHydrator
|
||||
* @throws MultipleSelectorsFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateAllData(): array
|
||||
{
|
||||
if (count($this->resultSetMapping()->fieldMappings) > 1) {
|
||||
throw MultipleSelectorsFoundException::create($this->resultSetMapping()->fieldMappings);
|
||||
}
|
||||
|
||||
$result = $this->statement()->fetchAllNumeric();
|
||||
|
||||
return array_column($result, 0);
|
||||
return $this->statement()->fetchFirstColumn();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Hydrator that produces flat, rectangular results of scalar data.
|
||||
* The created result is almost the same as a regular SQL result set, except
|
||||
@@ -14,6 +16,7 @@ class ScalarHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateAllData(): array
|
||||
{
|
||||
$result = [];
|
||||
@@ -28,6 +31,7 @@ class ScalarHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateRowData(array $row, array &$result): void
|
||||
{
|
||||
$result[] = $this->gatherScalarRowData($row);
|
||||
|
||||
@@ -9,6 +9,7 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Doctrine\ORM\Query;
|
||||
use Exception;
|
||||
use Override;
|
||||
use RuntimeException;
|
||||
use ValueError;
|
||||
|
||||
@@ -28,6 +29,7 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
|
||||
private ClassMetadata|null $class = null;
|
||||
|
||||
#[Override]
|
||||
protected function prepare(): void
|
||||
{
|
||||
if (count($this->resultSetMapping()->aliasMap) !== 1) {
|
||||
@@ -41,6 +43,7 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
$this->class = $this->getClassMetadata(reset($this->resultSetMapping()->aliasMap));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function cleanup(): void
|
||||
{
|
||||
parent::cleanup();
|
||||
@@ -52,6 +55,7 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateAllData(): array
|
||||
{
|
||||
$result = [];
|
||||
@@ -68,6 +72,7 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateRowData(array $row, array &$result): void
|
||||
{
|
||||
assert($this->class !== null);
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use Doctrine\ORM\NonUniqueResultException;
|
||||
use Doctrine\ORM\NoResultException;
|
||||
use Override;
|
||||
|
||||
use function array_shift;
|
||||
use function count;
|
||||
@@ -16,6 +17,7 @@ use function key;
|
||||
*/
|
||||
class SingleScalarHydrator extends AbstractHydrator
|
||||
{
|
||||
#[Override]
|
||||
protected function hydrateAllData(): mixed
|
||||
{
|
||||
$data = $this->statement()->fetchAllAssociative();
|
||||
|
||||
@@ -25,9 +25,9 @@ use function spl_object_id;
|
||||
*/
|
||||
final class StronglyConnectedComponents
|
||||
{
|
||||
private const NOT_VISITED = 1;
|
||||
private const IN_PROGRESS = 2;
|
||||
private const VISITED = 3;
|
||||
private const int NOT_VISITED = 1;
|
||||
private const int IN_PROGRESS = 2;
|
||||
private const int VISITED = 3;
|
||||
|
||||
/**
|
||||
* Array of all nodes, indexed by object ids.
|
||||
|
||||
@@ -20,9 +20,9 @@ use function spl_object_id;
|
||||
*/
|
||||
final class TopologicalSort
|
||||
{
|
||||
private const NOT_VISITED = 1;
|
||||
private const IN_PROGRESS = 2;
|
||||
private const VISITED = 3;
|
||||
private const int NOT_VISITED = 1;
|
||||
private const int IN_PROGRESS = 2;
|
||||
private const int VISITED = 3;
|
||||
|
||||
/**
|
||||
* Array of all nodes, indexed by object ids.
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Internal\UnitOfWork;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Id\AssignedGenerator;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
/**
|
||||
* An {@see InsertBatch} represents a set of entities that are safe to be batched
|
||||
* together in a single query.
|
||||
*
|
||||
* These entities are only those that have all fields already assigned, including the
|
||||
* identifier field(s).
|
||||
*
|
||||
* This data structure only exists for internal {@see UnitOfWork} optimisations, and
|
||||
* should not be relied upon outside the ORM.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @template TEntity of object
|
||||
*/
|
||||
final class InsertBatch
|
||||
{
|
||||
/**
|
||||
* @param ClassMetadata<TEntity> $class
|
||||
* @param non-empty-list<TEntity> $entities
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly ClassMetadata $class,
|
||||
public array $entities,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: Code in here is procedural/ugly due to it being in a hot path of the {@see UnitOfWork}
|
||||
*
|
||||
* This method will batch the given entity set by type, preserving their order. For example,
|
||||
* given an input [A1, A2, A3, B1, B2, A4, A5], it will create an [[A1, A2, A3], [B1, B2], [A4, A5]] batch.
|
||||
*
|
||||
* Entities for which the identifier needs to be generated or fetched by a sequence are put as single
|
||||
* items in a batch of their own, since it is unsafe to batch-insert them.
|
||||
*
|
||||
* @param list<TEntities> $entities
|
||||
*
|
||||
* @return list<self<TEntities>>
|
||||
*
|
||||
* @template TEntities of object
|
||||
*/
|
||||
public static function batchByEntityType(
|
||||
EntityManagerInterface $entityManager,
|
||||
array $entities,
|
||||
): array {
|
||||
$currentClass = null;
|
||||
$batches = [];
|
||||
$batchIndex = -1;
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
$entityClass = $entityManager->getClassMetadata($entity::class);
|
||||
|
||||
if (
|
||||
$currentClass?->name !== $entityClass->name
|
||||
|| ! $entityClass->idGenerator instanceof AssignedGenerator
|
||||
) {
|
||||
$currentClass = $entityClass;
|
||||
$batches[] = new InsertBatch($entityClass, [$entity]);
|
||||
$batchIndex += 1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$batches[$batchIndex]->entities[] = $entity;
|
||||
}
|
||||
|
||||
return $batches;
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,7 @@ use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\ReadableCollection;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
|
||||
use function assert;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* A lazy collection that allows a fast count when using criteria object
|
||||
@@ -26,6 +25,7 @@ use function assert;
|
||||
*/
|
||||
class LazyCriteriaCollection extends AbstractLazyCollection implements Selectable
|
||||
{
|
||||
/** @var non-negative-int|null */
|
||||
private int|null $count = null;
|
||||
|
||||
public function __construct(
|
||||
@@ -37,6 +37,7 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
|
||||
/**
|
||||
* Do an efficient count on the collection
|
||||
*/
|
||||
#[Override]
|
||||
public function count(): int
|
||||
{
|
||||
if ($this->isInitialized()) {
|
||||
@@ -54,6 +55,7 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
|
||||
/**
|
||||
* check if collection is empty without loading it
|
||||
*/
|
||||
#[Override]
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
if ($this->isInitialized()) {
|
||||
@@ -70,6 +72,7 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
|
||||
*
|
||||
* @return bool TRUE if the collection contains $element, FALSE otherwise.
|
||||
*/
|
||||
#[Override]
|
||||
public function contains(mixed $element): bool
|
||||
{
|
||||
if ($this->isInitialized()) {
|
||||
@@ -80,14 +83,15 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
|
||||
}
|
||||
|
||||
/** @return ReadableCollection<TKey, TValue>&Selectable<TKey, TValue> */
|
||||
#[Override]
|
||||
public function matching(Criteria $criteria): ReadableCollection&Selectable
|
||||
{
|
||||
$this->initialize();
|
||||
assert($this->collection instanceof Selectable);
|
||||
|
||||
return $this->collection->matching($criteria);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function doInitialize(): void
|
||||
{
|
||||
$elements = $this->entityPersister->loadCriteria($this->criteria);
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\ORM\Internal\SQLResultCasing;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* ANSI compliant quote strategy, this strategy does not apply any quote.
|
||||
@@ -15,6 +16,7 @@ class AnsiQuoteStrategy implements QuoteStrategy
|
||||
{
|
||||
use SQLResultCasing;
|
||||
|
||||
#[Override]
|
||||
public function getColumnName(
|
||||
string $fieldName,
|
||||
ClassMetadata $class,
|
||||
@@ -23,6 +25,7 @@ class AnsiQuoteStrategy implements QuoteStrategy
|
||||
return $class->fieldMappings[$fieldName]->columnName;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getTableName(ClassMetadata $class, AbstractPlatform $platform): string
|
||||
{
|
||||
return $class->table['name'];
|
||||
@@ -31,16 +34,19 @@ class AnsiQuoteStrategy implements QuoteStrategy
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string
|
||||
{
|
||||
return $definition['sequenceName'];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getJoinColumnName(JoinColumnMapping $joinColumn, ClassMetadata $class, AbstractPlatform $platform): string
|
||||
{
|
||||
return $joinColumn->name;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getReferencedJoinColumnName(
|
||||
JoinColumnMapping $joinColumn,
|
||||
ClassMetadata $class,
|
||||
@@ -49,6 +55,7 @@ class AnsiQuoteStrategy implements QuoteStrategy
|
||||
return $joinColumn->referencedColumnName;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getJoinTableName(
|
||||
ManyToManyOwningSideMapping $association,
|
||||
ClassMetadata $class,
|
||||
@@ -60,11 +67,13 @@ class AnsiQuoteStrategy implements QuoteStrategy
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform): array
|
||||
{
|
||||
return $class->identifier;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getColumnAlias(
|
||||
string $columnName,
|
||||
int $counter,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user