mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 15:02:22 +01:00
Compare commits
318 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef607f26c2 | ||
|
|
de1c28bb16 | ||
|
|
b4ca0cd5fb | ||
|
|
a49c1beb93 | ||
|
|
6307b4fa7d | ||
|
|
92e2f6db83 | ||
|
|
aa624f64c1 | ||
|
|
e1675eb371 | ||
|
|
cc2b6385a1 | ||
|
|
a64bed9bbb | ||
|
|
3272e1c0af | ||
|
|
69da22d517 | ||
|
|
06109f360f | ||
|
|
06a9ef1127 | ||
|
|
5d21bb158b | ||
|
|
c74df3fab3 | ||
|
|
f2c902ee03 | ||
|
|
4e5e3c5e50 | ||
|
|
da697f218f | ||
|
|
4f47a80deb | ||
|
|
ab89517093 | ||
|
|
48a51d8470 | ||
|
|
ab11244f08 | ||
|
|
a1c2be140d | ||
|
|
4664373bd0 | ||
|
|
5ab6b74f14 | ||
|
|
71550106d4 | ||
|
|
36011f0d0f | ||
|
|
c97d775370 | ||
|
|
97b29bb063 | ||
|
|
e9f0345a97 | ||
|
|
b7fff508a4 | ||
|
|
c6fa14ed52 | ||
|
|
05c8c5f114 | ||
|
|
1dbdb0e895 | ||
|
|
0feb09d0d6 | ||
|
|
fe5f8bbaa1 | ||
|
|
ecf3cec376 | ||
|
|
0a714db4d9 | ||
|
|
471fda8d0b | ||
|
|
dfe32c2f74 | ||
|
|
c51ba3ce6b | ||
|
|
fe025e8d23 | ||
|
|
0a43e4af8f | ||
|
|
7111cc09f3 | ||
|
|
777504b9c4 | ||
|
|
9d9985076a | ||
|
|
35d301b052 | ||
|
|
083b241c81 | ||
|
|
ae2957cf7e | ||
|
|
e172b3bf9c | ||
|
|
c9c6e8da2e | ||
|
|
b9989555fd | ||
|
|
80a79f6d2d | ||
|
|
1f1891d3e2 | ||
|
|
c9fc4d90e5 | ||
|
|
17d28b5c4c | ||
|
|
a2d510c6f4 | ||
|
|
0e3cff0c6a | ||
|
|
5c50ed925a | ||
|
|
5a1e560f87 | ||
|
|
78e8887759 | ||
|
|
9a3f5579f1 | ||
|
|
5eb298b99b | ||
|
|
5eb0255f47 | ||
|
|
28575f58af | ||
|
|
457d2d2841 | ||
|
|
12c721f528 | ||
|
|
9a9c3e8aba | ||
|
|
2a4ebca90e | ||
|
|
5d01c66c84 | ||
|
|
a2516b67dc | ||
|
|
46a020108d | ||
|
|
3d4e9b3ecf | ||
|
|
d1ee40e7d5 | ||
|
|
65d2720764 | ||
|
|
3e18a58de6 | ||
|
|
a9f9202c00 | ||
|
|
d8cb71fe4a | ||
|
|
d659591b6c | ||
|
|
e2430ac9a7 | ||
|
|
b286d6cd2c | ||
|
|
0b373f6c27 | ||
|
|
443cf92242 | ||
|
|
36b9064dbe | ||
|
|
eb3b984132 | ||
|
|
cc29ae0d36 | ||
|
|
bd4a053d29 | ||
|
|
52fbfb3785 | ||
|
|
c259371e5f | ||
|
|
dcdd58b642 | ||
|
|
7b9c53854f | ||
|
|
cdc5fe11dd | ||
|
|
69ece00564 | ||
|
|
04395f98f9 | ||
|
|
4baa7bd252 | ||
|
|
c55f10e501 | ||
|
|
f3fb79658e | ||
|
|
10a32cb824 | ||
|
|
c679d1b007 | ||
|
|
1e15b22dcb | ||
|
|
0c10010f9f | ||
|
|
d88371331d | ||
|
|
b260ec8a00 | ||
|
|
44057b4683 | ||
|
|
013df03795 | ||
|
|
2d2a34407c | ||
|
|
be8da83aca | ||
|
|
f5ab687226 | ||
|
|
742eead849 | ||
|
|
1072ea6db4 | ||
|
|
baf2c60cc4 | ||
|
|
8e620cad40 | ||
|
|
f98e871913 | ||
|
|
4b0c11978e | ||
|
|
daf0f82884 | ||
|
|
067ad51b3f | ||
|
|
0ef5610a6c | ||
|
|
e29d0e977d | ||
|
|
00c77213fb | ||
|
|
3303cd3b5d | ||
|
|
afcf91e839 | ||
|
|
c61a9b3b6d | ||
|
|
bd0509a064 | ||
|
|
c68b8f90b3 | ||
|
|
aa4f9ce9e9 | ||
|
|
d540f73778 | ||
|
|
d96fc23327 | ||
|
|
201d751a26 | ||
|
|
6308b2fd86 | ||
|
|
8f99e84438 | ||
|
|
e36b7755e9 | ||
|
|
7b4d869b31 | ||
|
|
708bd84fe2 | ||
|
|
3aed6912a3 | ||
|
|
8873109b4f | ||
|
|
36bef3f959 | ||
|
|
980ccc58dc | ||
|
|
62ca4624a9 | ||
|
|
1aed318b7b | ||
|
|
8ce7b310c5 | ||
|
|
5077ae41e5 | ||
|
|
8e1a27b8cc | ||
|
|
e7db1b005f | ||
|
|
72ce662e45 | ||
|
|
673cf0d4d8 | ||
|
|
1cae0534a0 | ||
|
|
6fb3083f63 | ||
|
|
68c17ca1bd | ||
|
|
82cf29407c | ||
|
|
158605bf24 | ||
|
|
2c2ef65817 | ||
|
|
f8fdeaf41e | ||
|
|
1c33a86983 | ||
|
|
310fe1cccb | ||
|
|
a67f677747 | ||
|
|
ae74be5e9d | ||
|
|
c9557c588b | ||
|
|
19912de927 | ||
|
|
737cca5b78 | ||
|
|
4163efd2f2 | ||
|
|
d7ac6123ad | ||
|
|
aff82af7de | ||
|
|
9e999ea1ff | ||
|
|
6755bb0c7b | ||
|
|
bd260d1be8 | ||
|
|
aa141bf001 | ||
|
|
cd1a52c7e4 | ||
|
|
cf39e00553 | ||
|
|
27b47841be | ||
|
|
c2a49327a7 | ||
|
|
9bd7242376 | ||
|
|
fff085b63f | ||
|
|
5ad5b11ae1 | ||
|
|
c12fd2cb94 | ||
|
|
44d5d4a779 | ||
|
|
5a599233c9 | ||
|
|
596da353c2 | ||
|
|
0d2cb6acd1 | ||
|
|
ec6d1b9f72 | ||
|
|
d809fed52a | ||
|
|
327418a4b7 | ||
|
|
0e4786dfa8 | ||
|
|
c429262f02 | ||
|
|
f4fdcbcdcb | ||
|
|
b0806469d5 | ||
|
|
68c87740aa | ||
|
|
55dc02c39f | ||
|
|
9f2b367081 | ||
|
|
a9873c86bb | ||
|
|
8ebd98ee92 | ||
|
|
b1f8253105 | ||
|
|
e3cabade99 | ||
|
|
9402f9e0f7 | ||
|
|
4feaa470af | ||
|
|
4a9101f383 | ||
|
|
f91da5b950 | ||
|
|
66f654d4e2 | ||
|
|
7b9c0d91f6 | ||
|
|
53b51ae40e | ||
|
|
95b0f5c328 | ||
|
|
5a220078e9 | ||
|
|
a15543a2ce | ||
|
|
238fb74028 | ||
|
|
6ff2b130d3 | ||
|
|
8c9bfca255 | ||
|
|
c2a2386df9 | ||
|
|
2f98e11562 | ||
|
|
073809cf5c | ||
|
|
e82690d256 | ||
|
|
23c31aec51 | ||
|
|
622ba2dcc7 | ||
|
|
0c1cf853fc | ||
|
|
79d1f07fa2 | ||
|
|
eba01f8d0e | ||
|
|
bd292481bd | ||
|
|
fcc53b260f | ||
|
|
7d61a1e73f | ||
|
|
b3cffe2d12 | ||
|
|
46c94e3729 | ||
|
|
052c7d7698 | ||
|
|
c2713adebc | ||
|
|
4e01567816 | ||
|
|
51a984be3d | ||
|
|
f7f3104451 | ||
|
|
a5c80a4c75 | ||
|
|
417444d4b5 | ||
|
|
6fd26a3933 | ||
|
|
8ef9253999 | ||
|
|
360b80afab | ||
|
|
0ed0be089c | ||
|
|
8fb1043e96 | ||
|
|
fd041fbe80 | ||
|
|
6007154484 | ||
|
|
22ce0aff37 | ||
|
|
5d73458f0b | ||
|
|
6c70d11f4e | ||
|
|
eadf96c879 | ||
|
|
0d770c89d6 | ||
|
|
0a635c1ece | ||
|
|
37051d57ce | ||
|
|
69a4199434 | ||
|
|
4bda5147f3 | ||
|
|
cbda7e2322 | ||
|
|
4563f2f9a7 | ||
|
|
38c6569645 | ||
|
|
7c0eebe90a | ||
|
|
8784f2bce9 | ||
|
|
fbcac42ebd | ||
|
|
619302dc9a | ||
|
|
50d7a0f95e | ||
|
|
d7f13a82ef | ||
|
|
9e1038075e | ||
|
|
dd3604f523 | ||
|
|
65e9f607e5 | ||
|
|
a42134ccee | ||
|
|
71e038c81d | ||
|
|
91201c094a | ||
|
|
2ff998da0e | ||
|
|
82e2c981da | ||
|
|
8422a41423 | ||
|
|
58ad1d9678 | ||
|
|
346c49832c | ||
|
|
f140651ff0 | ||
|
|
bb5b2a3300 | ||
|
|
5013d5dbef | ||
|
|
ba11851ac4 | ||
|
|
4fbce94999 | ||
|
|
486e406236 | ||
|
|
7d1b24f3b1 | ||
|
|
43ce0bef78 | ||
|
|
da51234d5a | ||
|
|
d9aa6ef6dc | ||
|
|
ff3ccff36a | ||
|
|
4b03ec7789 | ||
|
|
14866461c5 | ||
|
|
f41dc4a503 | ||
|
|
81c0d599c9 | ||
|
|
9e2bfa8169 | ||
|
|
44fa8bbde8 | ||
|
|
3ca9529c32 | ||
|
|
439b4dacf4 | ||
|
|
05f54860f7 | ||
|
|
021a9cce3d | ||
|
|
0f11a97c8e | ||
|
|
c9253ef64b | ||
|
|
af54a1696c | ||
|
|
4d821cb139 | ||
|
|
85fc95060f | ||
|
|
039b03255a | ||
|
|
e7efdede15 | ||
|
|
74155c8672 | ||
|
|
a4a15ad243 | ||
|
|
9c0e62ad44 | ||
|
|
94702d14b9 | ||
|
|
5aad44cff6 | ||
|
|
182469b346 | ||
|
|
0893d8511e | ||
|
|
e47398ecc5 | ||
|
|
9e884ccf1f | ||
|
|
fac0899ef7 | ||
|
|
d5c400e8d1 | ||
|
|
6648d68ddf | ||
|
|
982d6060a3 | ||
|
|
013f850c76 | ||
|
|
ef4508e52f | ||
|
|
6dae89ce0d | ||
|
|
f53350934f | ||
|
|
efaba02ef5 | ||
|
|
4ff909044e | ||
|
|
32682aa14d | ||
|
|
e9f3ca2a45 | ||
|
|
737aee7d98 | ||
|
|
bd20df1043 | ||
|
|
2ec2030ab2 | ||
|
|
129553da90 | ||
|
|
73e30df52b | ||
|
|
0a49274f9b |
@@ -11,17 +11,29 @@
|
||||
"slug": "latest",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "3.5",
|
||||
"branchName": "3.5.x",
|
||||
"slug": "3.5",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "3.4",
|
||||
"branchName": "3.4.x",
|
||||
"slug": "3.4",
|
||||
"current": true
|
||||
},
|
||||
{
|
||||
"name": "3.3",
|
||||
"branchName": "3.3.x",
|
||||
"slug": "3.3",
|
||||
"upcoming": true
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "3.2",
|
||||
"branchName": "3.2.x",
|
||||
"slug": "3.2",
|
||||
"current": true
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "3.1",
|
||||
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -19,5 +19,3 @@ phpstan-baseline.neon export-ignore
|
||||
phpstan-dbal2.neon export-ignore
|
||||
phpstan-params.neon export-ignore
|
||||
phpstan-persistence2.neon export-ignore
|
||||
psalm.xml export-ignore
|
||||
psalm-baseline.xml export-ignore
|
||||
|
||||
37
.github/ISSUE_TEMPLATE/BC_Break.md
vendored
37
.github/ISSUE_TEMPLATE/BC_Break.md
vendored
@@ -1,37 +0,0 @@
|
||||
---
|
||||
name: 💥 BC Break
|
||||
about: Have you encountered an issue during upgrade? 💣
|
||||
---
|
||||
|
||||
<!--
|
||||
Before reporting a BC break, please consult the upgrading document to make sure it's not an expected change: https://github.com/doctrine/orm/blob/2.9.x/UPGRADE.md
|
||||
-->
|
||||
|
||||
### BC Break Report
|
||||
|
||||
<!-- Fill in the relevant information below to help triage your issue. -->
|
||||
|
||||
| Q | A
|
||||
|------------ | ------
|
||||
| BC Break | yes
|
||||
| Version | x.y.z
|
||||
|
||||
#### Summary
|
||||
|
||||
<!-- Provide a summary describing the problem you are experiencing. -->
|
||||
|
||||
#### Previous behavior
|
||||
|
||||
<!-- What was the previous (working) behavior? -->
|
||||
|
||||
#### Current behavior
|
||||
|
||||
<!-- What is the current (broken) behavior? -->
|
||||
|
||||
#### How to reproduce
|
||||
|
||||
<!--
|
||||
Provide steps to reproduce the BC break.
|
||||
If possible, also add a code snippet with relevant configuration, entity mappings, DQL etc.
|
||||
Adding a failing Unit or Functional Test would help us a lot - you can submit it in a Pull Request separately, referencing this bug report.
|
||||
-->
|
||||
34
.github/ISSUE_TEMPLATE/Bug.md
vendored
34
.github/ISSUE_TEMPLATE/Bug.md
vendored
@@ -1,34 +0,0 @@
|
||||
---
|
||||
name: 🐞 Bug Report
|
||||
about: Something is broken? 🔨
|
||||
---
|
||||
|
||||
### Bug Report
|
||||
|
||||
<!-- Fill in the relevant information below to help triage your issue. -->
|
||||
|
||||
| Q | A
|
||||
|------------ | ------
|
||||
| BC Break | yes/no
|
||||
| Version | x.y.z
|
||||
|
||||
#### Summary
|
||||
|
||||
<!-- Provide a summary describing the problem you are experiencing. -->
|
||||
|
||||
#### Current behavior
|
||||
|
||||
<!-- What is the current (buggy) behavior? -->
|
||||
|
||||
#### How to reproduce
|
||||
|
||||
<!--
|
||||
Provide steps to reproduce the bug.
|
||||
If possible, also add a code snippet with relevant configuration, entity mappings, DQL etc.
|
||||
Adding a failing Unit or Functional Test would help us a lot - you can submit one in a Pull Request separately, referencing this bug report.
|
||||
-->
|
||||
|
||||
#### Expected behavior
|
||||
|
||||
<!-- What was the expected (correct) behavior? -->
|
||||
|
||||
18
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
18
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
@@ -1,18 +0,0 @@
|
||||
---
|
||||
name: 🎉 Feature Request
|
||||
about: You have a neat idea that should be implemented? 🎩
|
||||
---
|
||||
|
||||
### Feature Request
|
||||
|
||||
<!-- Fill in the relevant information below to help triage your issue. -->
|
||||
|
||||
| Q | A
|
||||
|------------ | ------
|
||||
| New Feature | yes
|
||||
| RFC | yes/no
|
||||
| BC Break | yes/no
|
||||
|
||||
#### Summary
|
||||
|
||||
<!-- Provide a summary of the feature you would like to see implemented. -->
|
||||
6
.github/ISSUE_TEMPLATE/Support_Question.md
vendored
6
.github/ISSUE_TEMPLATE/Support_Question.md
vendored
@@ -1,6 +0,0 @@
|
||||
---
|
||||
name: ❓ Support Question
|
||||
about: Have a problem that you can't figure out? 🤔
|
||||
---
|
||||
|
||||
Please use https://github.com/doctrine/orm/discussions instead.
|
||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -6,4 +6,4 @@ updates:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "CI"
|
||||
target-branch: "2.19.x"
|
||||
target-branch: "2.20.x"
|
||||
|
||||
2
.github/workflows/coding-standards.yml
vendored
2
.github/workflows/coding-standards.yml
vendored
@@ -24,4 +24,4 @@ on:
|
||||
|
||||
jobs:
|
||||
coding-standards:
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.1.0"
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@7.3.0"
|
||||
|
||||
22
.github/workflows/continuous-integration.yml
vendored
22
.github/workflows/continuous-integration.yml
vendored
@@ -9,7 +9,6 @@ on:
|
||||
- ci/**
|
||||
- composer.*
|
||||
- src/**
|
||||
- phpunit.xml.dist
|
||||
- tests/**
|
||||
push:
|
||||
branches:
|
||||
@@ -19,7 +18,6 @@ on:
|
||||
- ci/**
|
||||
- composer.*
|
||||
- src/**
|
||||
- phpunit.xml.dist
|
||||
- tests/**
|
||||
|
||||
env:
|
||||
@@ -45,17 +43,27 @@ jobs:
|
||||
- "pdo_sqlite"
|
||||
deps:
|
||||
- "highest"
|
||||
native_lazy:
|
||||
- "0"
|
||||
include:
|
||||
- php-version: "8.2"
|
||||
dbal-version: "4@dev"
|
||||
extension: "pdo_sqlite"
|
||||
native_lazy: "0"
|
||||
- php-version: "8.2"
|
||||
dbal-version: "4@dev"
|
||||
extension: "sqlite3"
|
||||
native_lazy: "0"
|
||||
- php-version: "8.1"
|
||||
dbal-version: "default"
|
||||
deps: "lowest"
|
||||
extension: "pdo_sqlite"
|
||||
native_lazy: "0"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "default"
|
||||
deps: "highest"
|
||||
extension: "pdo_sqlite"
|
||||
native_lazy: "1"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
@@ -85,16 +93,18 @@ 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"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 1
|
||||
ENABLE_NATIVE_LAZY_OBJECTS: ${{ matrix.native_lazy }}
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
with:
|
||||
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.deps }}-coverage"
|
||||
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.deps }}-${{ matrix.native_lazy }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
|
||||
|
||||
@@ -113,7 +123,7 @@ jobs:
|
||||
- "default"
|
||||
- "3.7"
|
||||
postgres-version:
|
||||
- "15"
|
||||
- "17"
|
||||
extension:
|
||||
- pdo_pgsql
|
||||
- pgsql
|
||||
@@ -323,6 +333,8 @@ jobs:
|
||||
upload_coverage:
|
||||
name: "Upload coverage to Codecov"
|
||||
runs-on: "ubuntu-22.04"
|
||||
# Only run on PRs from forks
|
||||
if: "github.event.pull_request.head.repo.full_name != github.repository"
|
||||
needs:
|
||||
- "phpunit-smoke-check"
|
||||
- "phpunit-postgres"
|
||||
@@ -341,7 +353,7 @@ jobs:
|
||||
path: "reports"
|
||||
|
||||
- name: "Upload to Codecov"
|
||||
uses: "codecov/codecov-action@v4"
|
||||
uses: "codecov/codecov-action@v5"
|
||||
with:
|
||||
directory: reports
|
||||
env:
|
||||
|
||||
2
.github/workflows/documentation.yml
vendored
2
.github/workflows/documentation.yml
vendored
@@ -17,4 +17,4 @@ on:
|
||||
jobs:
|
||||
documentation:
|
||||
name: "Documentation"
|
||||
uses: "doctrine/.github/.github/workflows/documentation.yml@5.1.0"
|
||||
uses: "doctrine/.github/.github/workflows/documentation.yml@7.3.0"
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@5.1.0"
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@7.3.0"
|
||||
secrets:
|
||||
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
|
||||
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}
|
||||
|
||||
24
.github/workflows/stale.yml
vendored
Normal file
24
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: 'Close stale pull requests'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 3 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
stale-pr-message: >
|
||||
There hasn't been any activity on this pull request in the past 90 days, so
|
||||
it has been marked as stale and it will be closed automatically if no
|
||||
further activity occurs in the next 7 days.
|
||||
|
||||
If you want to continue working on it, please leave a comment.
|
||||
|
||||
close-pr-message: >
|
||||
This pull request was closed due to inactivity.
|
||||
|
||||
days-before-stale: -1
|
||||
days-before-pr-stale: 90
|
||||
days-before-pr-close: 7
|
||||
35
.github/workflows/static-analysis.yml
vendored
35
.github/workflows/static-analysis.yml
vendored
@@ -9,7 +9,6 @@ on:
|
||||
- composer.*
|
||||
- src/**
|
||||
- phpstan*
|
||||
- psalm*
|
||||
- tests/StaticAnalysis/**
|
||||
push:
|
||||
branches:
|
||||
@@ -19,7 +18,6 @@ on:
|
||||
- composer.*
|
||||
- src/**
|
||||
- phpstan*
|
||||
- psalm*
|
||||
- tests/StaticAnalysis/**
|
||||
|
||||
jobs:
|
||||
@@ -43,7 +41,7 @@ jobs:
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
coverage: none
|
||||
php-version: "8.3"
|
||||
php-version: "8.4"
|
||||
tools: cs2pr
|
||||
|
||||
- name: Require specific DBAL version
|
||||
@@ -56,34 +54,3 @@ jobs:
|
||||
|
||||
- name: Run static analysis with phpstan/phpstan
|
||||
run: "vendor/bin/phpstan analyse -c ${{ matrix.config }} --error-format=checkstyle | cs2pr"
|
||||
|
||||
static-analysis-psalm:
|
||||
name: Static Analysis with Psalm
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
dbal-version:
|
||||
- default
|
||||
- 3.8.2
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: Install PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
coverage: none
|
||||
php-version: "8.3"
|
||||
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@v3
|
||||
|
||||
- name: Run static analysis with Vimeo Psalm
|
||||
run: vendor/bin/psalm --shepherd
|
||||
|
||||
21
.github/workflows/website-schema.yml
vendored
Normal file
21
.github/workflows/website-schema.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
name: "Website config validation"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*.x"
|
||||
paths:
|
||||
- ".doctrine-project.json"
|
||||
- ".github/workflows/website-schema.yml"
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
paths:
|
||||
- ".doctrine-project.json"
|
||||
- ".github/workflows/website-schema.yml"
|
||||
|
||||
jobs:
|
||||
json-validate:
|
||||
name: "Validate JSON schema"
|
||||
uses: "doctrine/.github/.github/workflows/website-schema.yml@7.1.0"
|
||||
32
README.md
32
README.md
@@ -1,9 +1,7 @@
|
||||
| [4.0.x][4.0] | [3.3.x][3.3] | [3.2.x][3.2] | [2.20.x][2.20] | [2.19.x][2.19] |
|
||||
| [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] |
|
||||
|:------------------------------------------------------:|:------------------------------------------------------:|:------------------------------------------------------:|:--------------------------------------------------------:|:--------------------------------------------------------:|
|
||||
| [![Build status][4.0 image]][4.0] | [![Build status][3.3 image]][3.3] | [![Build status][3.2 image]][3.2] | [![Build status][2.20 image]][2.20] | [![Build status][2.19 image]][2.19] |
|
||||
| [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.3 coverage image]][3.3 coverage] | [![Coverage Status][3.2 coverage image]][3.2 coverage] | [![Coverage Status][2.20 coverage image]][2.20 coverage] | [![Coverage Status][2.19 coverage image]][2.19 coverage] |
|
||||
|
||||
[<h1 align="center">🇺🇦 UKRAINE NEEDS YOUR HELP NOW!</h1>](https://www.doctrine-project.org/stop-war.html)
|
||||
| [![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] |
|
||||
|
||||
Doctrine ORM is an object-relational mapper for PHP 8.1+ that provides transparent persistence
|
||||
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
|
||||
@@ -22,19 +20,19 @@ without requiring unnecessary code duplication.
|
||||
[4.0]: https://github.com/doctrine/orm/tree/4.0.x
|
||||
[4.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/4.0.x/graph/badge.svg
|
||||
[4.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/4.0.x
|
||||
[3.3 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.3.x
|
||||
[3.3]: https://github.com/doctrine/orm/tree/3.3.x
|
||||
[3.3 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.3.x/graph/badge.svg
|
||||
[3.3 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.3.x
|
||||
[3.2 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.2.x
|
||||
[3.2]: https://github.com/doctrine/orm/tree/3.2.x
|
||||
[3.2 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.2.x/graph/badge.svg
|
||||
[3.2 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.2.x
|
||||
[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
|
||||
[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 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 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.20.x/graph/badge.svg
|
||||
[2.20 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.20.x
|
||||
[2.19 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.19.x
|
||||
[2.19]: https://github.com/doctrine/orm/tree/2.19.x
|
||||
[2.19 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.19.x/graph/badge.svg
|
||||
[2.19 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.19.x
|
||||
|
||||
69
UPGRADE.md
69
UPGRADE.md
@@ -1,9 +1,44 @@
|
||||
# Upgrade to 3.4.1
|
||||
|
||||
## BC BREAK: You can no longer use the `.*` notation to get all fields of an entity in a DTO
|
||||
|
||||
This feature was introduced in 3.4.0, and introduces several issues, so we
|
||||
decide to remove it before it is used too widely.
|
||||
|
||||
# Upgrade to 3.4
|
||||
|
||||
## Discriminator Map class duplicates
|
||||
|
||||
Using the same class several times in a discriminator map is deprecated.
|
||||
In 4.0, this will be an error.
|
||||
|
||||
## `Doctrine\ORM\Mapping\ClassMetadata::$reflFields` deprecated
|
||||
|
||||
To better support property hooks and lazy proxies in the future, `$reflFields` had to
|
||||
be deprecated because we cannot use the PHP internal reflection API directly anymore.
|
||||
|
||||
The property was changed from an array to an object of type `LegacyReflectionFields`
|
||||
that implements `ArrayAccess`.
|
||||
|
||||
Use the new `Doctrine\ORM\Mapping\PropertyAccessors\PropertyAccessor` API and access
|
||||
through `Doctrine\ORM\Mapping\ClassMetadata::$propertyAccessors` instead.
|
||||
|
||||
# Upgrade to 3.3
|
||||
|
||||
## Deprecate `DatabaseDriver`
|
||||
|
||||
The class `Doctrine\ORM\Mapping\Driver\DatabaseDriver` is deprecated without replacement.
|
||||
|
||||
## Add `Doctrine\ORM\Query\OutputWalker` interface, deprecate `Doctrine\ORM\Query\SqlWalker::getExecutor()`
|
||||
|
||||
Output walkers should implement the new `\Doctrine\ORM\Query\OutputWalker` interface and create
|
||||
`Doctrine\ORM\Query\Exec\SqlFinalizer` instances instead of `Doctrine\ORM\Query\Exec\AbstractSqlExecutor`s.
|
||||
The output walker must not base its workings on the query `firstResult`/`maxResult` values, so that the
|
||||
`SqlFinalizer` can be kept in the query cache and used regardless of the actual `firstResult`/`maxResult` values.
|
||||
Any operation dependent on `firstResult`/`maxResult` should take place within the `SqlFinalizer::createExecutor()`
|
||||
method. Details can be found at https://github.com/doctrine/orm/pull/11188.
|
||||
|
||||
|
||||
# Upgrade to 3.2
|
||||
|
||||
## Deprecate the `NotSupported` exception
|
||||
@@ -109,6 +144,36 @@ WARNING: This was relaxed in ORM 3.2 when partial was re-allowed for array-hydra
|
||||
`Doctrine\ORM\Query::HINT_FORCE_PARTIAL_LOAD` are removed.
|
||||
- `Doctrine\ORM\EntityManager*::getPartialReference()` is removed.
|
||||
|
||||
## BC BREAK: Enforce ArrayCollection Type on `\Doctrine\ORM\QueryBuilder::setParameters(ArrayCollection $parameters)`
|
||||
|
||||
The argument $parameters can no longer be a key=>value array. Only ArrayCollection types are allowed.
|
||||
|
||||
### Before
|
||||
|
||||
```php
|
||||
$qb = $em->createQueryBuilder()
|
||||
->select('u')
|
||||
->from('User', 'u')
|
||||
->where('u.id = :user_id1 OR u.id = :user_id2')
|
||||
->setParameters(array(
|
||||
'user_id1' => 1,
|
||||
'user_id2' => 2
|
||||
));
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```php
|
||||
$qb = $em->createQueryBuilder()
|
||||
->select('u')
|
||||
->from('User', 'u')
|
||||
->where('u.id = :user_id1 OR u.id = :user_id2')
|
||||
->setParameters(new ArrayCollection(array(
|
||||
new Parameter('user_id1', 1),
|
||||
new Parameter('user_id2', 2)
|
||||
)));
|
||||
```
|
||||
|
||||
## BC BREAK: `Doctrine\ORM\Persister\Entity\EntityPersister::executeInserts()` return type changed to `void`
|
||||
|
||||
Implementors should adapt to the new signature, and should call
|
||||
@@ -737,7 +802,7 @@ Use `toIterable()` instead.
|
||||
|
||||
Output walkers should implement the new `\Doctrine\ORM\Query\OutputWalker` interface and create
|
||||
`Doctrine\ORM\Query\Exec\SqlFinalizer` instances instead of `Doctrine\ORM\Query\Exec\AbstractSqlExecutor`s.
|
||||
The output walker must not base its workings on the query `firstResult`/`maxResult` values, so that the
|
||||
The output walker must not base its workings on the query `firstResult`/`maxResult` values, so that the
|
||||
`SqlFinalizer` can be kept in the query cache and used regardless of the actual `firstResult`/`maxResult` values.
|
||||
Any operation dependent on `firstResult`/`maxResult` should take place within the `SqlFinalizer::createExecutor()`
|
||||
method. Details can be found at https://github.com/doctrine/orm/pull/11188.
|
||||
@@ -750,7 +815,7 @@ change in behavior.
|
||||
|
||||
Progress on this is tracked at https://github.com/doctrine/orm/issues/11624 .
|
||||
|
||||
## PARTIAL DQL syntax is undeprecated
|
||||
## PARTIAL DQL syntax is undeprecated
|
||||
|
||||
Use of the PARTIAL keyword is not deprecated anymore in DQL, because we will be
|
||||
able to support PARTIAL objects with PHP 8.4 Lazy Objects and
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
displayDetailsOnTestsThatTriggerDeprecations="true"
|
||||
displayDetailsOnTestsThatTriggerNotices="true"
|
||||
displayDetailsOnTestsThatTriggerWarnings="true"
|
||||
failOnNotice="true"
|
||||
failOnWarning="true"
|
||||
failOnRisky="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
@@ -19,6 +24,7 @@
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
<env name="DOCTRINE_DEPRECATIONS" value="trigger"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
@@ -27,7 +33,7 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<source>
|
||||
<source ignoreSuppressionOfDeprecations="true">
|
||||
<include>
|
||||
<directory suffix=".php">../../../src</directory>
|
||||
</include>
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
displayDetailsOnTestsThatTriggerDeprecations="true"
|
||||
displayDetailsOnTestsThatTriggerNotices="true"
|
||||
displayDetailsOnTestsThatTriggerWarnings="true"
|
||||
failOnNotice="true"
|
||||
failOnWarning="true"
|
||||
failOnRisky="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
@@ -19,6 +24,7 @@
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
<env name="DOCTRINE_DEPRECATIONS" value="trigger"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
@@ -27,7 +33,7 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<source>
|
||||
<source ignoreSuppressionOfDeprecations="true">
|
||||
<include>
|
||||
<directory suffix=".php">../../../src</directory>
|
||||
</include>
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
displayDetailsOnTestsThatTriggerDeprecations="true"
|
||||
displayDetailsOnTestsThatTriggerNotices="true"
|
||||
displayDetailsOnTestsThatTriggerWarnings="true"
|
||||
failOnNotice="true"
|
||||
failOnWarning="true"
|
||||
failOnRisky="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
@@ -16,6 +21,7 @@
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
<env name="DOCTRINE_DEPRECATIONS" value="trigger"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
@@ -24,7 +30,7 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<source>
|
||||
<source ignoreSuppressionOfDeprecations="true">
|
||||
<include>
|
||||
<directory suffix=".php">../../../src</directory>
|
||||
</include>
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
displayDetailsOnTestsThatTriggerDeprecations="true"
|
||||
displayDetailsOnTestsThatTriggerNotices="true"
|
||||
displayDetailsOnTestsThatTriggerWarnings="true"
|
||||
failOnNotice="true"
|
||||
failOnWarning="true"
|
||||
failOnRisky="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
@@ -14,6 +19,7 @@
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
<env name="DOCTRINE_DEPRECATIONS" value="trigger"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
@@ -22,7 +28,7 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<source>
|
||||
<source ignoreSuppressionOfDeprecations="true">
|
||||
<include>
|
||||
<directory suffix=".php">../../../src</directory>
|
||||
</include>
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
displayDetailsOnTestsThatTriggerDeprecations="true"
|
||||
displayDetailsOnTestsThatTriggerNotices="true"
|
||||
displayDetailsOnTestsThatTriggerWarnings="true"
|
||||
failOnNotice="true"
|
||||
failOnWarning="true"
|
||||
failOnRisky="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
@@ -16,6 +21,7 @@
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
<env name="DOCTRINE_DEPRECATIONS" value="trigger"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
@@ -24,7 +30,7 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<source>
|
||||
<source ignoreSuppressionOfDeprecations="true">
|
||||
<include>
|
||||
<directory suffix=".php">../../../src</directory>
|
||||
</include>
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
displayDetailsOnTestsThatTriggerDeprecations="true"
|
||||
displayDetailsOnTestsThatTriggerNotices="true"
|
||||
displayDetailsOnTestsThatTriggerWarnings="true"
|
||||
failOnNotice="true"
|
||||
failOnWarning="true"
|
||||
failOnRisky="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
@@ -14,6 +19,7 @@
|
||||
|
||||
<!-- necessary change for some CLI/console output test assertions -->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
<env name="DOCTRINE_DEPRECATIONS" value="trigger"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
@@ -22,7 +28,7 @@
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<source>
|
||||
<source ignoreSuppressionOfDeprecations="true">
|
||||
<include>
|
||||
<directory suffix=".php">../../../src</directory>
|
||||
</include>
|
||||
|
||||
@@ -31,23 +31,22 @@
|
||||
"doctrine/inflector": "^1.4 || ^2.0",
|
||||
"doctrine/instantiator": "^1.3 || ^2",
|
||||
"doctrine/lexer": "^3",
|
||||
"doctrine/persistence": "^3.3.1",
|
||||
"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": "^12.0",
|
||||
"doctrine/coding-standard": "^13.0",
|
||||
"phpbench/phpbench": "^1.0",
|
||||
"phpdocumentor/guides-cli": "^1.4",
|
||||
"phpstan/extension-installer": "^1.4",
|
||||
"phpstan/phpstan": "1.12.6",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.2",
|
||||
"phpstan/phpstan": "2.0.3",
|
||||
"phpstan/phpstan-deprecation-rules": "^2",
|
||||
"phpunit/phpunit": "^10.4.0",
|
||||
"psr/log": "^1 || ^2 || ^3",
|
||||
"squizlabs/php_codesniffer": "3.7.2",
|
||||
"symfony/cache": "^5.4 || ^6.2 || ^7.0",
|
||||
"vimeo/psalm": "5.24.0"
|
||||
"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",
|
||||
|
||||
3
docs/.gitignore
vendored
Normal file
3
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
composer.lock
|
||||
vendor/
|
||||
build/
|
||||
24
docs/Makefile
Normal file
24
docs/Makefile
Normal file
@@ -0,0 +1,24 @@
|
||||
# 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."
|
||||
@@ -1,18 +1,22 @@
|
||||
# Doctrine ORM Documentation
|
||||
|
||||
The documentation is written in [ReStructured Text](https://docutils.sourceforge.io/rst.html).
|
||||
|
||||
## How to Generate:
|
||||
Using Ubuntu 14.04 LTS:
|
||||
|
||||
1. Run ./bin/install-dependencies.sh
|
||||
2. Run ./bin/generate-docs.sh
|
||||
In the `docs/` folder, run
|
||||
|
||||
It will generate the documentation into the build directory of the checkout.
|
||||
composer update
|
||||
|
||||
Then compile the documentation with:
|
||||
|
||||
## Theme issues
|
||||
make html
|
||||
|
||||
If you get a "Theme error", check if the `en/_theme` subdirectory is empty,
|
||||
in which case you will need to run:
|
||||
This will generate the documentation into the `build` subdirectory.
|
||||
|
||||
1. git submodule init
|
||||
2. git submodule update
|
||||
To browse the documentation, you need to run a webserver:
|
||||
|
||||
cd build/html
|
||||
php -S localhost:8000
|
||||
|
||||
Now the documentation is available at [http://localhost:8000](http://localhost:8000).
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/bash
|
||||
EXECPATH=`dirname $0`
|
||||
cd $EXECPATH
|
||||
cd ..
|
||||
|
||||
rm build -Rf
|
||||
sphinx-build en build
|
||||
|
||||
sphinx-build -b latex en build/pdf
|
||||
rubber --into build/pdf --pdf build/pdf/Doctrine2ORM.tex
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
sudo apt-get update && sudo apt-get install -y python2.7 python-sphinx python-pygments
|
||||
10
docs/composer.json
Normal file
10
docs/composer.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "doctrine/orm-docs",
|
||||
"description": "Documentation for the Object-Relational Mapper\"",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"phpdocumentor/guides-cli": "1.7.1",
|
||||
"phpdocumentor/filesystem": "1.7.1"
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Doctrine2ORM.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Doctrine2ORM.qhc"
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
|
||||
"run these through (pdf)latex."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
@@ -1,91 +0,0 @@
|
||||
#Copyright (c) 2010 Fabien Potencier
|
||||
#
|
||||
#Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
#of this software and associated documentation files (the "Software"), to deal
|
||||
#in the Software without restriction, including without limitation the rights
|
||||
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
#copies of the Software, and to permit persons to whom the Software is furnished
|
||||
#to do so, subject to the following conditions:
|
||||
#
|
||||
#The above copyright notice and this permission notice shall be included in all
|
||||
#copies or substantial portions of the Software.
|
||||
#
|
||||
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
#THE SOFTWARE.
|
||||
|
||||
from docutils.parsers.rst import Directive, directives
|
||||
from docutils import nodes
|
||||
from string import upper
|
||||
|
||||
class configurationblock(nodes.General, nodes.Element):
|
||||
pass
|
||||
|
||||
class ConfigurationBlock(Directive):
|
||||
has_content = True
|
||||
required_arguments = 0
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = True
|
||||
option_spec = {}
|
||||
formats = {
|
||||
'html': 'HTML',
|
||||
'xml': 'XML',
|
||||
'php': 'PHP',
|
||||
'jinja': 'Twig',
|
||||
'html+jinja': 'Twig',
|
||||
'jinja+html': 'Twig',
|
||||
'php+html': 'PHP',
|
||||
'html+php': 'PHP',
|
||||
'ini': 'INI',
|
||||
}
|
||||
|
||||
def run(self):
|
||||
env = self.state.document.settings.env
|
||||
|
||||
node = nodes.Element()
|
||||
node.document = self.state.document
|
||||
self.state.nested_parse(self.content, self.content_offset, node)
|
||||
|
||||
entries = []
|
||||
for i, child in enumerate(node):
|
||||
if isinstance(child, nodes.literal_block):
|
||||
# add a title (the language name) before each block
|
||||
#targetid = "configuration-block-%d" % env.new_serialno('configuration-block')
|
||||
#targetnode = nodes.target('', '', ids=[targetid])
|
||||
#targetnode.append(child)
|
||||
|
||||
innernode = nodes.emphasis(self.formats[child['language']], self.formats[child['language']])
|
||||
|
||||
para = nodes.paragraph()
|
||||
para += [innernode, child]
|
||||
|
||||
entry = nodes.list_item('')
|
||||
entry.append(para)
|
||||
entries.append(entry)
|
||||
|
||||
resultnode = configurationblock()
|
||||
resultnode.append(nodes.bullet_list('', *entries))
|
||||
|
||||
return [resultnode]
|
||||
|
||||
def visit_configurationblock_html(self, node):
|
||||
self.body.append(self.starttag(node, 'div', CLASS='configuration-block'))
|
||||
|
||||
def depart_configurationblock_html(self, node):
|
||||
self.body.append('</div>\n')
|
||||
|
||||
def visit_configurationblock_latex(self, node):
|
||||
pass
|
||||
|
||||
def depart_configurationblock_latex(self, node):
|
||||
pass
|
||||
|
||||
def setup(app):
|
||||
app.add_node(configurationblock,
|
||||
html=(visit_configurationblock_html, depart_configurationblock_html),
|
||||
latex=(visit_configurationblock_latex, depart_configurationblock_latex))
|
||||
app.add_directive('configuration-block', ConfigurationBlock)
|
||||
@@ -352,7 +352,7 @@ the database using a FOR UPDATE.
|
||||
use Bank\Entities\Account;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
|
||||
$account = $em->find(Account::class, $accId, LockMode::PESSIMISTIC_READ);
|
||||
$account = $em->find(Account::class, $accId, LockMode::PESSIMISTIC_WRITE);
|
||||
|
||||
Keeping Updates and Deletes in Sync
|
||||
-----------------------------------
|
||||
|
||||
44
docs/en/cookbook/generated-columns.rst
Normal file
44
docs/en/cookbook/generated-columns.rst
Normal file
@@ -0,0 +1,44 @@
|
||||
Generated Columns
|
||||
=================
|
||||
|
||||
Generated columns, sometimes also called virtual columns, are populated by
|
||||
the database engine itself. They are a tool for performance optimization, to
|
||||
avoid calculating a value on each query.
|
||||
|
||||
You can define generated columns on entities and have Doctrine map the values
|
||||
to your entity.
|
||||
|
||||
Declaring a generated column
|
||||
----------------------------
|
||||
|
||||
There is no explicit mapping instruction for generated columns. Instead, you
|
||||
specify that the column should not be written to, and define a custom column
|
||||
definition.
|
||||
|
||||
.. literalinclude:: generated-columns/Person.php
|
||||
:language: php
|
||||
|
||||
* ``insertable``, ``updatable``: Setting these to false tells Doctrine to never
|
||||
write this column - writing to a generated column would result in an error
|
||||
from the database.
|
||||
* ``columnDefinition``: We specify the full DDL to create the column. To allow
|
||||
to use database specific features, this attribute does not use Doctrine Query
|
||||
Language but native SQL. Note that you need to reference columns by their
|
||||
database name (either explicitly set in the mapping or per the current
|
||||
:doc:`naming strategy <../reference/namingstrategy>`).
|
||||
Be aware that specifying a column definition makes the ``SchemaTool``
|
||||
completely ignore all other configuration for this column. See also
|
||||
:ref:`#[Column] <attrref_column>`
|
||||
* ``generated``: Specifying that this column is always generated tells Doctrine
|
||||
to update the field on the entity with the value from the database after
|
||||
every write operation.
|
||||
|
||||
Advanced example: Extracting a value from a JSON structure
|
||||
----------------------------------------------------------
|
||||
|
||||
Lets assume we have an entity that stores a blogpost as structured JSON.
|
||||
To avoid extracting all titles on the fly when listing the posts, we create a
|
||||
generated column with the field.
|
||||
|
||||
.. literalinclude:: generated-columns/Article.php
|
||||
:language: php
|
||||
33
docs/en/cookbook/generated-columns/Article.php
Normal file
33
docs/en/cookbook/generated-columns/Article.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity]
|
||||
class Article
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private int $id;
|
||||
|
||||
/**
|
||||
* When working with Postgres, it is recommended to use the jsonb
|
||||
* format for better performance.
|
||||
*/
|
||||
#[ORM\Column(options: ['jsonb' => true])]
|
||||
private array $content;
|
||||
|
||||
/**
|
||||
* Because we specify NOT NULL, inserting will fail if the content does
|
||||
* not have a string in the title field.
|
||||
*/
|
||||
#[ORM\Column(
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
columnDefinition: "VARCHAR(255) generated always as (content->>'title') stored NOT NULL",
|
||||
generated: 'ALWAYS',
|
||||
)]
|
||||
private string $title;
|
||||
}
|
||||
24
docs/en/cookbook/generated-columns/Person.php
Normal file
24
docs/en/cookbook/generated-columns/Person.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity]
|
||||
class Person
|
||||
{
|
||||
#[ORM\Column(type: 'string')]
|
||||
private string $firstName;
|
||||
|
||||
#[ORM\Column(type: 'string', name: 'name')]
|
||||
private string $lastName;
|
||||
|
||||
#[ORM\Column(
|
||||
type: 'string',
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
columnDefinition: "VARCHAR(255) GENERATED ALWAYS AS (concat(firstName, ' ', name) stored NOT NULL",
|
||||
generated: 'ALWAYS',
|
||||
)]
|
||||
private string $fullName;
|
||||
}
|
||||
@@ -102,6 +102,7 @@ Cookbook
|
||||
|
||||
* **Patterns**:
|
||||
:doc:`Aggregate Fields <cookbook/aggregate-fields>` \|
|
||||
:doc:`Generated/Virtual Columns <cookbook/generated-columns>` \|
|
||||
:doc:`Decorator Pattern <cookbook/decorator-pattern>` \|
|
||||
:doc:`Strategy Pattern <cookbook/strategy-cookbook-introduction>`
|
||||
|
||||
@@ -121,4 +122,5 @@ Cookbook
|
||||
|
||||
* **Custom Datatypes**
|
||||
:doc:`MySQL Enums <cookbook/mysql-enums>`
|
||||
:doc:`Custom Mapping Types <cookbook/custom-mapping-types>`
|
||||
:doc:`Advanced Field Value Conversion <cookbook/advanced-field-value-conversion-using-custom-mapping-types>`
|
||||
|
||||
113
docs/en/make.bat
113
docs/en/make.bat
@@ -1,113 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
set SPHINXBUILD=sphinx-build
|
||||
set BUILDDIR=_build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Doctrine2ORM.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Doctrine2ORM.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
||||
@@ -19,7 +19,7 @@ steps of configuration.
|
||||
|
||||
// ...
|
||||
|
||||
if ($applicationMode == "development") {
|
||||
if ($applicationMode === "development") {
|
||||
$queryCache = new ArrayAdapter();
|
||||
$metadataCache = new ArrayAdapter();
|
||||
} else {
|
||||
@@ -32,13 +32,18 @@ steps of configuration.
|
||||
$driverImpl = new AttributeDriver(['/path/to/lib/MyProject/Entities'], true);
|
||||
$config->setMetadataDriverImpl($driverImpl);
|
||||
$config->setQueryCache($queryCache);
|
||||
$config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies');
|
||||
$config->setProxyNamespace('MyProject\Proxies');
|
||||
|
||||
if ($applicationMode == "development") {
|
||||
$config->setAutoGenerateProxyClasses(true);
|
||||
if (PHP_VERSION_ID > 80400) {
|
||||
$config->enableNativeLazyObjects(true);
|
||||
} else {
|
||||
$config->setAutoGenerateProxyClasses(false);
|
||||
$config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies');
|
||||
$config->setProxyNamespace('MyProject\Proxies');
|
||||
|
||||
if ($applicationMode === "development") {
|
||||
$config->setAutoGenerateProxyClasses(true);
|
||||
} else {
|
||||
$config->setAutoGenerateProxyClasses(false);
|
||||
}
|
||||
}
|
||||
|
||||
$connection = DriverManager::getConnection([
|
||||
@@ -71,8 +76,25 @@ Configuration Options
|
||||
The following sections describe all the configuration options
|
||||
available on a ``Doctrine\ORM\Configuration`` instance.
|
||||
|
||||
Proxy Directory (***REQUIRED***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
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
|
||||
|
||||
@@ -85,8 +107,11 @@ 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***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
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
|
||||
|
||||
@@ -98,8 +123,8 @@ 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***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Metadata Driver (**REQUIRED**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -139,8 +164,8 @@ 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.
|
||||
|
||||
Metadata Cache (***RECOMMENDED***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Metadata Cache (**RECOMMENDED**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -161,8 +186,8 @@ For development you should use an array cache like
|
||||
``Symfony\Component\Cache\Adapter\ArrayAdapter``
|
||||
which only caches data on a per-request basis.
|
||||
|
||||
Query Cache (***RECOMMENDED***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Query Cache (**RECOMMENDED**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -184,8 +209,8 @@ For development you should use an array cache like
|
||||
``Symfony\Component\Cache\Adapter\ArrayAdapter``
|
||||
which only caches data on a per-request basis.
|
||||
|
||||
SQL Logger (***Optional***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
SQL Logger (**Optional**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -197,8 +222,11 @@ 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***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
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
|
||||
@@ -441,7 +469,7 @@ correctly if sub-namespaces use different metadata driver
|
||||
implementations.
|
||||
|
||||
|
||||
Default Repository (***OPTIONAL***)
|
||||
Default Repository (**OPTIONAL**)
|
||||
-----------------------------------
|
||||
|
||||
Specifies the FQCN of a subclass of the EntityRepository.
|
||||
@@ -456,7 +484,7 @@ That will be available for all entities without a custom repository class.
|
||||
The default value is ``Doctrine\ORM\EntityRepository``.
|
||||
Any repository class must be a subclass of EntityRepository otherwise you got an ORMException
|
||||
|
||||
Ignoring entities (***OPTIONAL***)
|
||||
Ignoring entities (**OPTIONAL**)
|
||||
-----------------------------------
|
||||
|
||||
Specifies the Entity FQCNs to ignore.
|
||||
|
||||
@@ -33,14 +33,13 @@ Doctrine ORM is divided into four main packages.
|
||||
- ORM (depends on DBAL+Persistence+Collections)
|
||||
|
||||
This manual mainly covers the ORM package, sometimes touching parts
|
||||
of the underlying DBAL and Persistence packages. The Doctrine code base
|
||||
is split in to these packages for a few reasons and they are to...
|
||||
of the underlying DBAL and Persistence packages. The Doctrine codebase
|
||||
is split into these packages for a few reasons:
|
||||
|
||||
|
||||
- ...make things more maintainable and decoupled
|
||||
- ...allow you to use the code in Doctrine Persistence and Collections
|
||||
without the ORM or DBAL
|
||||
- ...allow you to use the DBAL without the ORM
|
||||
- to make things more maintainable and decoupled
|
||||
- to allow you to use the code in Doctrine Persistence and Collections without the ORM or DBAL
|
||||
- to allow you to use the DBAL without the ORM
|
||||
|
||||
Collection, Event Manager and Persistence
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -903,8 +903,7 @@ defaults to "id", just as in one-to-one or many-to-one mappings.
|
||||
|
||||
Additionally, when using typed properties with Doctrine 2.9 or newer
|
||||
you can skip ``targetEntity`` in ``ManyToOne`` and ``OneToOne``
|
||||
associations as they will be set based on type. Also ``nullable``
|
||||
attribute on ``JoinColumn`` will be inherited from PHP type. So that:
|
||||
associations as they will be set based on type. So that:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@@ -931,7 +930,7 @@ Is essentially the same as following:
|
||||
<?php
|
||||
/** One Product has One Shipment. */
|
||||
#[OneToOne(targetEntity: Shipment::class)]
|
||||
#[JoinColumn(name: 'shipment_id', referencedColumnName: 'id', nullable: false)]
|
||||
#[JoinColumn(name: 'shipment_id', referencedColumnName: 'id')]
|
||||
private Shipment $shipment;
|
||||
|
||||
.. code-block:: annotation
|
||||
@@ -940,7 +939,7 @@ Is essentially the same as following:
|
||||
/**
|
||||
* One Product has One Shipment.
|
||||
* @OneToOne(targetEntity="Shipment")
|
||||
* @JoinColumn(name="shipment_id", referencedColumnName="id", nullable=false)
|
||||
* @JoinColumn(name="shipment_id", referencedColumnName="id")
|
||||
*/
|
||||
private Shipment $shipment;
|
||||
|
||||
@@ -949,7 +948,7 @@ Is essentially the same as following:
|
||||
<doctrine-mapping>
|
||||
<entity class="Product">
|
||||
<one-to-one field="shipment" target-entity="Shipment">
|
||||
<join-column name="shipment_id" referenced-column-name="id" nulable=false />
|
||||
<join-column name="shipment_id" referenced-column-name="id" nullable=false />
|
||||
</one-to-one>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
@@ -214,12 +214,15 @@ Optional parameters:
|
||||
- ``check``: Adds a check constraint type to the column (might not
|
||||
be supported by all vendors).
|
||||
|
||||
- **columnDefinition**: DDL SQL snippet that starts after the column
|
||||
- **columnDefinition**: Specify the DDL SQL snippet that starts after the column
|
||||
name and specifies the complete (non-portable!) column definition.
|
||||
This attribute allows to make use of advanced RMDBS features.
|
||||
However you should make careful use of this feature and the
|
||||
consequences. ``SchemaTool`` will not detect changes on the column correctly
|
||||
anymore if you use ``columnDefinition``.
|
||||
However, as this needs to be specified in the DDL native to the database,
|
||||
the resulting schema changes are no longer portable. If you specify a
|
||||
``columnDefinition``, the ``SchemaTool`` ignores all other attributes
|
||||
that are normally used to build the definition DDL. Changes to the
|
||||
``columnDefinition`` are not detected, you will need to manually create a
|
||||
migration to apply changes.
|
||||
|
||||
Additionally you should remember that the ``type``
|
||||
attribute still handles the conversion between PHP and Database
|
||||
@@ -262,10 +265,11 @@ Examples:
|
||||
)]
|
||||
protected $loginCount;
|
||||
|
||||
// MySQL example: full_name char(41) GENERATED ALWAYS AS (concat(firstname,' ',lastname)),
|
||||
// columnDefinition is raw SQL, not DQL. This example works for MySQL:
|
||||
#[Column(
|
||||
type: "string",
|
||||
name: "user_fullname",
|
||||
columnDefinition: "VARCHAR(255) GENERATED ALWAYS AS (concat(firstname,' ',lastname))",
|
||||
insertable: false,
|
||||
updatable: false
|
||||
)]
|
||||
@@ -366,7 +370,7 @@ Optional parameters:
|
||||
|
||||
- **type**: By default this is string.
|
||||
- **length**: By default this is 255.
|
||||
- **columnDefinition**: By default this is null the definition according to the type will be used. This option allows to override it.
|
||||
- **columnDefinition**: Allows to override how the column is generated. See the "columnDefinition" attribute on :ref:`#[Column] <attrref_column>`
|
||||
- **enumType**: By default this is `null`. Allows to map discriminatorColumn value to PHP enum
|
||||
- **options**: See "options" attribute on :ref:`#[Column] <attrref_column>`.
|
||||
|
||||
@@ -672,13 +676,16 @@ Optional parameters:
|
||||
- **unique**: Determines whether this relation is exclusive between the
|
||||
affected entities and should be enforced as such on the database
|
||||
constraint level. Defaults to false.
|
||||
- **deferrable**: Determines whether this relation constraint can be deferred. Defaults to false.
|
||||
- **nullable**: Determine whether the related entity is required, or if
|
||||
null is an allowed state for the relation. Defaults to true.
|
||||
- **onDelete**: Cascade Action (Database-level)
|
||||
- **columnDefinition**: DDL SQL snippet that starts after the column
|
||||
name and specifies the complete (non-portable!) column definition.
|
||||
This attribute enables the use of advanced RMDBS features. Using
|
||||
this attribute on ``#[JoinColumn]`` is necessary if you need slightly
|
||||
This attribute enables the use of advanced RMDBS features. Note that you
|
||||
need to reference columns by their database name (either explicitly set in
|
||||
the mapping or per the current :doc:`naming strategy <namingstrategy>`).
|
||||
Using this attribute on ``#[JoinColumn]`` is necessary if you need
|
||||
different column definitions for joining columns, for example
|
||||
regarding NULL/NOT NULL defaults. However by default a
|
||||
"columnDefinition" attribute on :ref:`#[Column] <attrref_column>` also sets
|
||||
@@ -1133,7 +1140,7 @@ Marker attribute that defines a specified column as version attribute used in
|
||||
an :ref:`optimistic locking <transactions-and-concurrency_optimistic-locking>`
|
||||
scenario. It only works on :ref:`#[Column] <attrref_column>` attributes that have
|
||||
the type ``integer`` or ``datetime``. Setting ``#[Version]`` on a property with
|
||||
:ref:`#[Id <attrref_id>` is not supported.
|
||||
:ref:`#[Id] <attrref_id>` is not supported.
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -167,6 +167,7 @@ Here is a complete list of ``Column``s attributes (all optional):
|
||||
- ``nullable`` (default: ``false``): Whether the column is nullable.
|
||||
- ``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.
|
||||
- ``precision`` (default: 0): The precision for a decimal (exact numeric) column
|
||||
(applies only for decimal column),
|
||||
@@ -213,6 +214,8 @@ These are the "automatic" mapping rules:
|
||||
| Any other type | ``Types::STRING`` |
|
||||
+-----------------------+-------------------------------+
|
||||
|
||||
.. versionadded:: 2.11
|
||||
|
||||
As of version 2.11 Doctrine can also automatically map typed properties using a
|
||||
PHP 8.1 enum to set the right ``type`` and ``enumType``.
|
||||
|
||||
@@ -223,6 +226,70 @@ and a custom ``Doctrine\ORM\Mapping\TypedFieldMapper`` implementation.
|
||||
|
||||
:doc:`Read more about TypedFieldMapper <typedfieldmapper>`.
|
||||
|
||||
Property Hooks
|
||||
--------------
|
||||
|
||||
.. versionadded:: 3.4
|
||||
|
||||
Doctrine supports mapping hooked properties as long as they have a backed property
|
||||
and are not virtual.
|
||||
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: attribute
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
|
||||
#[Entity]
|
||||
class Message
|
||||
{
|
||||
#[Column(type: Types::INTEGER)]
|
||||
private $id;
|
||||
#[Column(type: Types::STRING)]
|
||||
public string $language = 'de' {
|
||||
// Override the "read" action with arbitrary logic.
|
||||
get => strtoupper($this->language);
|
||||
|
||||
// Override the "write" action with arbitrary logic.
|
||||
set {
|
||||
$this->language = strtolower($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="Message">
|
||||
<field name="id" type="integer" />
|
||||
<field name="language" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
If you attempt to map a virtual property with ``#[Column]`` an exception will be thrown.
|
||||
|
||||
Some caveats apply to the use of property hooks, as they behave differently when accessing the property through
|
||||
the entity or directly through DQL/EntityRepository. Because the property hook can modify the value of the property in a way
|
||||
that value and raw value are different, you have to use the raw value representation when querying for the property.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$queryBuilder = $entityManager->createQueryBuilder();
|
||||
$queryBuilder->select('m')
|
||||
->from(Message::class, 'm')
|
||||
->where('m.language = :language')
|
||||
->setParameter('language', 'de'); // Use lower case here for raw value representation
|
||||
|
||||
$query = $queryBuilder->getQuery();
|
||||
$result = $query->getResult();
|
||||
|
||||
$messageRepository = $entityManager->getRepository(Message::class);
|
||||
$deMessages = $messageRepository->findBy(['language' => 'de']); // Use lower case here for raw value representation
|
||||
|
||||
.. _reference-mapping-types:
|
||||
|
||||
Doctrine Mapping Types
|
||||
|
||||
@@ -588,7 +588,7 @@ And then use the ``NEW`` DQL keyword :
|
||||
$query = $em->createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city, SUM(o.value)) FROM Customer c JOIN c.email e JOIN c.address a JOIN c.orders o GROUP BY c');
|
||||
$users = $query->getResult(); // array of CustomerDTO
|
||||
|
||||
You can also nest several DTO :
|
||||
You can also nest several DTO :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -674,6 +674,16 @@ The ``NAMED`` keyword must precede all DTO you want to instantiate :
|
||||
If two arguments have the same name, a ``DuplicateFieldException`` is thrown.
|
||||
If a field cannot be matched with a property name, a ``NoMatchingPropertyException`` is thrown. This typically happens when using functions without aliasing them.
|
||||
|
||||
You can hydrate an entity nested in a DTO :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT NEW CustomerDTO(c.name, a AS address) FROM Customer c JOIN c.address a');
|
||||
$users = $query->getResult(); // array of CustomerDTO
|
||||
|
||||
// CustomerDTO => {name : 'DOE', email: null, address : {city: 'New York', zip: '10011', address: 'Abbey Road'}
|
||||
|
||||
Using INDEX BY
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1697,12 +1707,13 @@ Select Expressions
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
SelectExpression ::= (IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression | NewObjectExpression) [["AS"] ["HIDDEN"] AliasResultVariable]
|
||||
SimpleSelectExpression ::= (StateFieldPathExpression | IdentificationVariable | FunctionDeclaration | AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable]
|
||||
PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet
|
||||
PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
|
||||
NewObjectExpression ::= "NEW" AbstractSchemaName "(" NewObjectArg {"," NewObjectArg}* ")"
|
||||
NewObjectArg ::= (ScalarExpression | "(" Subselect ")" | NewObjectExpression) ["AS" AliasResultVariable]
|
||||
SelectExpression ::= (IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression | NewObjectExpression) [["AS"] ["HIDDEN"] AliasResultVariable]
|
||||
SimpleSelectExpression ::= (StateFieldPathExpression | IdentificationVariable | FunctionDeclaration | AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable]
|
||||
PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet
|
||||
PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
|
||||
NewObjectExpression ::= "NEW" AbstractSchemaName "(" NewObjectArg {"," NewObjectArg}* ")"
|
||||
NewObjectArg ::= (ScalarExpression | "(" Subselect ")" | NewObjectExpression | EntityAsDtoArgumentExpression) ["AS" AliasResultVariable]
|
||||
EntityAsDtoArgumentExpression ::= IdentificationVariable
|
||||
|
||||
Conditional Expressions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -265,7 +265,7 @@ specific to a particular entity class's lifecycle.
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
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="User">
|
||||
|
||||
@@ -16,7 +16,7 @@ is common to multiple entity classes.
|
||||
Mapped superclasses, just as regular, non-mapped classes, can
|
||||
appear in the middle of an otherwise mapped inheritance hierarchy
|
||||
(through Single Table Inheritance or Class Table Inheritance). They
|
||||
are not query-able, and need not have an ``#[Id]`` property.
|
||||
are not query-able, and do not require an ``#[Id]`` property.
|
||||
|
||||
No database table will be created for a mapped superclass itself,
|
||||
only for entity classes inheriting from it. That implies that a
|
||||
|
||||
@@ -299,7 +299,7 @@ level cache region.
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
<entity name="Country">
|
||||
@@ -351,7 +351,7 @@ It caches the primary keys of association and cache each element will be cached
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
<entity name="State">
|
||||
|
||||
@@ -102,7 +102,7 @@ How Doctrine Detects Changes
|
||||
----------------------------
|
||||
|
||||
Doctrine is a data-mapper that tries to achieve persistence-ignorance (PI).
|
||||
This means you map php objects into a relational database that don't
|
||||
This means you map PHP objects into a relational database that don't
|
||||
necessarily know about the database at all. A natural question would now be,
|
||||
"how does Doctrine even detect objects have changed?".
|
||||
|
||||
|
||||
@@ -736,6 +736,35 @@ methods:
|
||||
|
||||
.. note::
|
||||
|
||||
There is a limitation on the compatibility of Criteria comparisons.
|
||||
You have to use scalar values only as the value in a comparison or
|
||||
the behaviour between different backends is not the same.
|
||||
Depending on whether the collection has already been loaded from the
|
||||
database or not, criteria matching may happen at the database/SQL level
|
||||
or on objects in memory. This may lead to different results and come
|
||||
surprising, for example when a code change in one place leads to a collection
|
||||
becoming initialized and, as a side effect, returning a different result
|
||||
or even breaking a ``matching()`` call somewhere else. Also, collection
|
||||
initialization state in practical use cases may differ from the one covered
|
||||
in unit tests.
|
||||
|
||||
Database level comparisons are based on scalar representations of the values
|
||||
stored in entity properties. The field names passed to expressions correspond
|
||||
to property names. Comparison and sorting may be affected by
|
||||
database-specific behavior. For example, MySQL enum types sort by index position,
|
||||
not lexicographically by value.
|
||||
|
||||
In-memory handling is based on the ``Selectable`` API of `Doctrine Collections <https://www.doctrine-project.org/projects/doctrine-collections/en/stable/index.html#matching>`.
|
||||
In this case, field names passed to expressions are being used to derive accessor
|
||||
method names. Strict type comparisons are used for equal and not-equal checks,
|
||||
and generally PHP language rules are being used for other comparison operators
|
||||
or sorting.
|
||||
|
||||
As a general guidance, for consistent results use the Criteria API with scalar
|
||||
values only. Note that ``DateTime`` and ``DateTimeImmutable`` are two predominant
|
||||
examples of value objects that are *not* scalars.
|
||||
|
||||
Refrain from using special database-level column types or custom Doctrine Types
|
||||
that may lead to database-specific comparison or sorting rules being applied, or
|
||||
to database-level values being different from object field values.
|
||||
|
||||
Provide accessor methods for all entity fields used in criteria expressions,
|
||||
and implement those methods in a way that their return value is the
|
||||
same as the database-level value.
|
||||
|
||||
@@ -166,7 +166,7 @@ your code. See the following code:
|
||||
|
||||
Traversing the object graph for parts that are lazy-loaded will
|
||||
easily trigger lots of SQL queries and will perform badly if used
|
||||
to heavily. Make sure to use DQL to fetch-join all the parts of the
|
||||
too heavily. Make sure to use DQL to fetch-join all the parts of the
|
||||
object-graph that you need as efficiently as possible.
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ setup for the latest code in trunk.
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
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">
|
||||
|
||||
@@ -46,7 +46,7 @@ In order to work, this requires certain conventions:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$driver->setFileExtension('.xml');
|
||||
$driver->getLocator()->setFileExtension('.xml');
|
||||
|
||||
It is recommended to put all XML mapping documents in a single
|
||||
folder but you can spread the documents over several folders if you
|
||||
@@ -104,7 +104,7 @@ of several common elements:
|
||||
// Doctrine.Tests.ORM.Mapping.User.dcm.xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
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">
|
||||
|
||||
@@ -770,7 +770,7 @@ entity relationship. You can define this in XML with the "association-key" attri
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
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">
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
cookbook/decorator-pattern
|
||||
cookbook/dql-custom-walkers
|
||||
cookbook/dql-user-defined-functions
|
||||
cookbook/generated-columns
|
||||
cookbook/implementing-arrayaccess-for-domain-objects
|
||||
cookbook/resolve-target-entity-listener
|
||||
cookbook/sql-table-prefixes
|
||||
|
||||
@@ -54,7 +54,7 @@ and year of production as primary keys:
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
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">
|
||||
|
||||
@@ -85,11 +85,12 @@ And for querying you can use arrays to both DQL and EntityRepositories:
|
||||
namespace VehicleCatalogue\Model;
|
||||
|
||||
// $em is the EntityManager
|
||||
$audi = $em->find("VehicleCatalogue\Model\Car", array("name" => "Audi A8", "year" => 2010));
|
||||
$audi = $em->find("VehicleCatalogue\Model\Car", ["name" => "Audi A8", "year" => 2010]);
|
||||
|
||||
$dql = "SELECT c FROM VehicleCatalogue\Model\Car c WHERE c.id = ?1";
|
||||
$dql = "SELECT c FROM VehicleCatalogue\Model\Car c WHERE c.name = ?1 AND c.year = ?2";
|
||||
$audi = $em->createQuery($dql)
|
||||
->setParameter(1, ["name" => "Audi A8", "year" => 2010])
|
||||
->setParameter(1, "Audi A8")
|
||||
->setParameter(2, 2010)
|
||||
->getSingleResult();
|
||||
|
||||
You can also use this entity in associations. Doctrine will then generate two foreign keys one for ``name``
|
||||
@@ -174,7 +175,7 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
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">
|
||||
|
||||
@@ -249,7 +250,7 @@ of products purchased and maybe even the current price.
|
||||
|
||||
public function __construct(
|
||||
#[ManyToOne(targetEntity: Customer::class)]
|
||||
private Customer $customer,
|
||||
private Customer $customer
|
||||
) {
|
||||
$this->items = new ArrayCollection();
|
||||
$this->created = new DateTime("now");
|
||||
@@ -294,6 +295,7 @@ of products purchased and maybe even the current price.
|
||||
$this->order = $order;
|
||||
$this->product = $product;
|
||||
$this->offeredPrice = $product->getCurrentPrice();
|
||||
$this->amount = $amount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ can be called without triggering a full load of the collection:
|
||||
- ``Collection#contains($entity)``
|
||||
- ``Collection#containsKey($key)``
|
||||
- ``Collection#count()``
|
||||
- ``Collection#first()``
|
||||
- ``Collection#get($key)``
|
||||
- ``Collection#isEmpty()``
|
||||
- ``Collection#slice($offset, $length = null)``
|
||||
|
||||
For each of the above methods the following semantics apply:
|
||||
@@ -69,7 +71,7 @@ switch to extra lazy as shown in these examples:
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
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">
|
||||
|
||||
|
||||
@@ -514,7 +514,7 @@ methods, but you only need to choose one.
|
||||
|
||||
<!-- config/xml/Product.dcm.xml -->
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
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">
|
||||
|
||||
@@ -1023,7 +1023,7 @@ the ``Product`` before:
|
||||
|
||||
<!-- config/xml/Bug.dcm.xml -->
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
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">
|
||||
|
||||
@@ -1103,7 +1103,7 @@ Finally, we'll add metadata mappings for the ``User`` entity.
|
||||
|
||||
<!-- config/xml/User.dcm.xml -->
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
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">
|
||||
|
||||
@@ -1586,7 +1586,7 @@ we have to adjust the metadata slightly.
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
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">
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ i.e. attributes and associations metadata in particular. The example here shows
|
||||
the overriding of a class that uses a trait but is similar when extending a base
|
||||
class as shown at the end of this tutorial.
|
||||
|
||||
Suppose we have a class ExampleEntityWithOverride. This class uses trait ExampleTrait:
|
||||
Suppose we have a class ``ExampleEntityWithOverride``. This class uses trait ``ExampleTrait``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -17,22 +17,20 @@ Suppose we have a class ExampleEntityWithOverride. This class uses trait Example
|
||||
|
||||
#[Entity]
|
||||
#[AttributeOverrides([
|
||||
new AttributeOverride('foo', [
|
||||
'column' => new Column([
|
||||
'name' => 'foo_overridden',
|
||||
'type' => 'integer',
|
||||
'length' => 140,
|
||||
'nullable' => false,
|
||||
'unique' => false,
|
||||
]),
|
||||
]),
|
||||
new AttributeOverride('foo', new Column(
|
||||
name: 'foo_overridden',
|
||||
type: 'integer',
|
||||
length: 140,
|
||||
nullable: false,
|
||||
unique: false,
|
||||
)),
|
||||
])]
|
||||
#[AssociationOverrides([
|
||||
new AssociationOverride('bar', [
|
||||
'joinColumns' => new JoinColumn([
|
||||
'name' => 'example_entity_overridden_bar_id',
|
||||
'referencedColumnName' => 'id',
|
||||
]),
|
||||
new JoinColumn(
|
||||
name: 'example_entity_overridden_bar_id',
|
||||
referencedColumnName: 'id',
|
||||
),
|
||||
]),
|
||||
])]
|
||||
class ExampleEntityWithOverride
|
||||
@@ -47,7 +45,7 @@ Suppose we have a class ExampleEntityWithOverride. This class uses trait Example
|
||||
private $id;
|
||||
}
|
||||
|
||||
The docblock is showing metadata override of the attribute and association type. It
|
||||
``#[AttributeOverrides]`` contains metadata override of the attribute and association type. It
|
||||
basically changes the names of the columns mapped for a property ``foo`` and for
|
||||
the association ``bar`` which relates to Bar class shown above. Here is the trait
|
||||
which has mapping metadata that is overridden by the attribute above:
|
||||
|
||||
@@ -29,83 +29,11 @@ You can map indexed associations by adding:
|
||||
The code and mappings for the Market entity looks like this:
|
||||
|
||||
.. configuration-block::
|
||||
.. code-block:: attribute
|
||||
.. literalinclude:: working-with-indexed-associations/Market.php
|
||||
:language: attribute
|
||||
|
||||
<?php
|
||||
namespace Doctrine\Tests\Models\StockExchange;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
|
||||
#[Entity]
|
||||
#[Table(name: 'exchange_markets')]
|
||||
class Market
|
||||
{
|
||||
#[Id, Column(type: 'integer'), GeneratedValue]
|
||||
private int|null $id = null;
|
||||
|
||||
#[Column(type: 'string')]
|
||||
private string $name;
|
||||
|
||||
/** @var Collection<string, Stock> */
|
||||
#[OneToMany(targetEntity: Stock::class, mappedBy: 'market', indexBy: 'symbol')]
|
||||
private Collection $stocks;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->stocks = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): int|null
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function addStock(Stock $stock): void
|
||||
{
|
||||
$this->stocks[$stock->getSymbol()] = $stock;
|
||||
}
|
||||
|
||||
public function getStock(string $symbol): Stock
|
||||
{
|
||||
if (!isset($this->stocks[$symbol])) {
|
||||
throw new \InvalidArgumentException("Symbol is not traded on this market.");
|
||||
}
|
||||
|
||||
return $this->stocks[$symbol];
|
||||
}
|
||||
|
||||
/** @return array<string, Stock> */
|
||||
public function getStocks(): array
|
||||
{
|
||||
return $this->stocks->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://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\Models\StockExchange\Market">
|
||||
<id name="id" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
|
||||
<field name="name" type="string"/>
|
||||
|
||||
<one-to-many target-entity="Stock" mapped-by="market" field="stocks" index-by="symbol" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
.. literalinclude:: working-with-indexed-associations/market.xml
|
||||
:language: xml
|
||||
|
||||
Inside the ``addStock()`` method you can see how we directly set the key of the association to the symbol,
|
||||
so that we can work with the indexed association directly after invoking ``addStock()``. Inside ``getStock($symbol)``
|
||||
@@ -150,7 +78,7 @@ here are the code and mappings for it:
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
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">
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\StockExchange;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\GeneratedValue;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
use Doctrine\ORM\Mapping\OneToMany;
|
||||
use Doctrine\ORM\Mapping\Table;
|
||||
use InvalidArgumentException;
|
||||
|
||||
#[Entity]
|
||||
#[Table(name: 'exchange_markets')]
|
||||
class Market
|
||||
{
|
||||
#[Id]
|
||||
#[Column(type: 'integer')]
|
||||
#[GeneratedValue]
|
||||
private int|null $id = null;
|
||||
|
||||
#[Column(type: 'string')]
|
||||
private string $name;
|
||||
|
||||
/** @var Collection<string, Stock> */
|
||||
#[OneToMany(targetEntity: Stock::class, mappedBy: 'market', indexBy: 'symbol')]
|
||||
private Collection $stocks;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->stocks = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): int|null
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function addStock(Stock $stock): void
|
||||
{
|
||||
$this->stocks[$stock->getSymbol()] = $stock;
|
||||
}
|
||||
|
||||
public function getStock(string $symbol): Stock
|
||||
{
|
||||
if (! isset($this->stocks[$symbol])) {
|
||||
throw new InvalidArgumentException('Symbol is not traded on this market.');
|
||||
}
|
||||
|
||||
return $this->stocks[$symbol];
|
||||
}
|
||||
|
||||
/** @return array<string, Stock> */
|
||||
public function getStocks(): array
|
||||
{
|
||||
return $this->stocks->toArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?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\Models\StockExchange\Market">
|
||||
<id name="id" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
|
||||
<field name="name" type="string"/>
|
||||
|
||||
<one-to-many target-entity="Stock" mapped-by="market" field="stocks" index-by="symbol" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@@ -321,7 +321,7 @@
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="value" type="xs:NMTOKEN" use="required"/>
|
||||
<xs:attribute name="value" type="orm:type" use="required"/>
|
||||
<xs:attribute name="class" type="orm:fqcn" use="required"/>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset>
|
||||
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
name="PHP_CodeSniffer"
|
||||
xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
|
||||
<arg name="basepath" value="."/>
|
||||
<arg name="extensions" value="php"/>
|
||||
<arg name="parallel" value="80"/>
|
||||
@@ -48,6 +50,8 @@
|
||||
</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>
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@ includes:
|
||||
- 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\.$/'
|
||||
@@ -16,9 +17,22 @@ parameters:
|
||||
-
|
||||
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: '~.*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\.$~'
|
||||
|
||||
@@ -34,3 +48,42 @@ parameters:
|
||||
-
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
parameters:
|
||||
level: 5
|
||||
level: 7
|
||||
paths:
|
||||
- src
|
||||
- tests/StaticAnalysis
|
||||
@@ -8,4 +8,4 @@ parameters:
|
||||
earlyTerminatingMethodCalls:
|
||||
Doctrine\ORM\Query\Parser:
|
||||
- syntaxError
|
||||
phpVersion: 80200
|
||||
phpVersion: 80400
|
||||
|
||||
19
phpstan.neon
19
phpstan.neon
@@ -20,6 +20,18 @@ parameters:
|
||||
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
|
||||
-
|
||||
@@ -33,7 +45,7 @@ parameters:
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '~^Parameter #1 \$command of method Symfony\\Component\\Console\\Application::add\(\) expects Symfony\\Component\\Console\\Command\\Command, Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand given\.$~'
|
||||
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
|
||||
|
||||
-
|
||||
@@ -45,3 +57,8 @@ parameters:
|
||||
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
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
displayDetailsOnTestsThatTriggerDeprecations="true"
|
||||
displayDetailsOnTestsThatTriggerNotices="true"
|
||||
displayDetailsOnTestsThatTriggerWarnings="true"
|
||||
failOnNotice="true"
|
||||
@@ -67,5 +69,12 @@
|
||||
<var name="privileged_db_port" value="3306"/>
|
||||
-->
|
||||
<env name="COLUMNS" value="120"/>
|
||||
<env name="DOCTRINE_DEPRECATIONS" value="trigger"/>
|
||||
</php>
|
||||
|
||||
<source ignoreSuppressionOfDeprecations="true">
|
||||
<include>
|
||||
<directory>src</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
|
||||
1233
psalm-baseline.xml
1233
psalm-baseline.xml
File diff suppressed because it is too large
Load Diff
245
psalm.xml
245
psalm.xml
@@ -1,245 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
errorLevel="2"
|
||||
phpVersion="8.2"
|
||||
resolveFromConfigFile="true"
|
||||
findUnusedBaselineEntry="true"
|
||||
findUnusedCode="false"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
errorBaseline="psalm-baseline.xml"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
<directory name="tests/StaticAnalysis" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
<file name="src/Mapping/Driver/AttributeReader.php" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
<enableExtensions>
|
||||
<extension name="simplexml" />
|
||||
</enableExtensions>
|
||||
<issueHandlers>
|
||||
<DeprecatedClass>
|
||||
<errorLevel type="suppress">
|
||||
<!-- We wire the command as long as DBAL ships it -->
|
||||
<referencedClass name="Doctrine\DBAL\Tools\Console\Command\ReservedWordsCommand" />
|
||||
<!-- Remove on 3.0.x -->
|
||||
<referencedClass name="Doctrine\ORM\Event\LifecycleEventArgs"/>
|
||||
<referencedClass name="Doctrine\ORM\Exception\UnknownEntityNamespace"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\Driver\AnnotationDriver"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\Driver\YamlDriver"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\NamedNativeQueries"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\NamedNativeQuery"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\NamedQueries"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\NamedQuery"/>
|
||||
<referencedClass name="Doctrine\ORM\Query\AST\InExpression"/>
|
||||
<referencedClass name="Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand"/>
|
||||
<referencedClass name="Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand"/>
|
||||
<referencedClass name="Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand"/>
|
||||
<referencedClass name="Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand"/>
|
||||
<referencedClass name="Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand"/>
|
||||
<referencedClass name="Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper"/>
|
||||
<referencedClass name="Doctrine\ORM\Tools\Console\EntityManagerProvider\HelperSetManagerProvider"/>
|
||||
</errorLevel>
|
||||
</DeprecatedClass>
|
||||
<DeprecatedMethod>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Remove on 3.0.x -->
|
||||
<!-- Compatibility with DBAL 3 -->
|
||||
<referencedMethod name="Doctrine\DBAL\Connection::getEventManager"/>
|
||||
<file name="src/Query/TreeWalkerChain.php"/>
|
||||
</errorLevel>
|
||||
</DeprecatedMethod>
|
||||
<DocblockTypeContradiction>
|
||||
<errorLevel type="suppress">
|
||||
<!-- We're catching invalid input here. -->
|
||||
<file name="src/Internal/Hydration/AbstractHydrator.php"/>
|
||||
|
||||
<!-- DBAL 3.2 forward compatibility -->
|
||||
<file name="src/Tools/Pagination/CountOutputWalker.php"/>
|
||||
<file name="src/Tools/Pagination/LimitSubqueryOutputWalker.php"/>
|
||||
<!-- https://github.com/vimeo/psalm/issues/8520 -->
|
||||
<file name="src/PersistentCollection.php"/>
|
||||
<!-- Remove on 4.0.x -->
|
||||
<file name="src/Mapping/Driver/AttributeDriver.php"/>
|
||||
<file name="src/Mapping/Driver/XmlDriver.php"/>
|
||||
<file name="src/ORMSetup.php"/>
|
||||
</errorLevel>
|
||||
</DocblockTypeContradiction>
|
||||
<ForbiddenCode>
|
||||
<errorLevel type="suppress">
|
||||
<file name="src/Tools/Debug.php"/>
|
||||
</errorLevel>
|
||||
</ForbiddenCode>
|
||||
<InvalidArgument>
|
||||
<errorLevel type="suppress">
|
||||
<referencedFunction name="Doctrine\ORM\Mapping\ClassMetadata::addInheritedAssociationMapping"/>
|
||||
</errorLevel>
|
||||
</InvalidArgument>
|
||||
<InvalidArrayAccess>
|
||||
<errorLevel type="suppress">
|
||||
<!-- https://github.com/vimeo/psalm/issues/9160 -->
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
</errorLevel>
|
||||
</InvalidArrayAccess>
|
||||
<InvalidArrayAssignment>
|
||||
<errorLevel type="suppress">
|
||||
<!-- https://github.com/vimeo/psalm/issues/9160 -->
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
</errorLevel>
|
||||
</InvalidArrayAssignment>
|
||||
<LessSpecificReturnStatement>
|
||||
<errorLevel type="suppress">
|
||||
<!-- In DBAL 4, column precision is nullable. See https://github.com/doctrine/dbal/pull/3511 -->
|
||||
<file name="src/Mapping/Driver/DatabaseDriver.php"/>
|
||||
</errorLevel>
|
||||
</LessSpecificReturnStatement>
|
||||
<MoreSpecificReturnType>
|
||||
<errorLevel type="suppress">
|
||||
<!-- In DBAL 4, the default column value is mixed. See https://github.com/doctrine/dbal/pull/3511 -->
|
||||
<file name="src/Mapping/Driver/DatabaseDriver.php"/>
|
||||
</errorLevel>
|
||||
</MoreSpecificReturnType>
|
||||
<InvalidReturnType>
|
||||
<errorLevel type="suppress">
|
||||
<!-- https://github.com/vimeo/psalm/issues/8819 -->
|
||||
<file name="src/Internal/Hydration/AbstractHydrator.php"/>
|
||||
</errorLevel>
|
||||
</InvalidReturnType>
|
||||
<InvalidParamDefault>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Remove on 3.0.x -->
|
||||
<file name="src/Query/AST/InstanceOfExpression.php"/>
|
||||
</errorLevel>
|
||||
</InvalidParamDefault>
|
||||
<InvalidPropertyAssignmentValue>
|
||||
<errorLevel type="suppress">
|
||||
<!-- https://github.com/vimeo/psalm/issues/9155 -->
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
</errorLevel>
|
||||
</InvalidPropertyAssignmentValue>
|
||||
<MethodSignatureMismatch>
|
||||
<errorLevel type="suppress">
|
||||
<!-- See https://github.com/vimeo/psalm/issues/7357 -->
|
||||
<file name="src/Mapping/ReflectionReadonlyProperty.php"/>
|
||||
</errorLevel>
|
||||
</MethodSignatureMismatch>
|
||||
<MissingParamType>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Persistence 2 compatibility -->
|
||||
<file name="src/EntityManager.php"/>
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
<file name="src/Mapping/ClassMetadata.php"/>
|
||||
</errorLevel>
|
||||
</MissingParamType>
|
||||
<PossiblyInvalidArgument>
|
||||
<errorLevel type="suppress">
|
||||
<!-- https://github.com/vimeo/psalm/issues/9155 -->
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
</errorLevel>
|
||||
</PossiblyInvalidArgument>
|
||||
<PossiblyNullArrayOffset>
|
||||
<errorLevel type="suppress">
|
||||
<!-- https://github.com/vimeo/psalm/issues/7878 -->
|
||||
<file name="src/Persisters/Collection/ManyToManyPersister.php"/>
|
||||
</errorLevel>
|
||||
</PossiblyNullArrayOffset>
|
||||
<PropertyNotSetInConstructor>
|
||||
<errorLevel type="suppress">
|
||||
<directory name="src/Query/AST" />
|
||||
</errorLevel>
|
||||
</PropertyNotSetInConstructor>
|
||||
<PropertyTypeCoercion>
|
||||
<errorLevel type="suppress">
|
||||
<file name="src/Mapping/ClassMetadata.php"/>
|
||||
</errorLevel>
|
||||
</PropertyTypeCoercion>
|
||||
<RedundantCastGivenDocblockType>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Can be removed once the "getMaxResults" methods of those classes have native parameter types -->
|
||||
<file name="src/Query.php"/>
|
||||
<file name="src/QueryBuilder.php"/>
|
||||
</errorLevel>
|
||||
</RedundantCastGivenDocblockType>
|
||||
<ReferenceConstraintViolation>
|
||||
<errorLevel type="suppress">
|
||||
<!-- https://github.com/vimeo/psalm/issues/9155 -->
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
</errorLevel>
|
||||
</ReferenceConstraintViolation>
|
||||
<RiskyTruthyFalsyComparison>
|
||||
<!-- TODO: Enable this new rule on higher branches. -->
|
||||
<errorLevel type="suppress">
|
||||
<directory name="src" />
|
||||
</errorLevel>
|
||||
</RiskyTruthyFalsyComparison>
|
||||
<TooManyArguments>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Symfony cache supports passing a key prefix to the clear method. -->
|
||||
<referencedFunction name="Psr\Cache\CacheItemPoolInterface::clear"/>
|
||||
|
||||
<!-- Persistence 2 compatibility -->
|
||||
<referencedFunction name="Doctrine\Persistence\ObjectManager::clear"/>
|
||||
|
||||
<!-- See https://github.com/doctrine/orm/issues/8850 -->
|
||||
<referencedFunction name="Doctrine\DBAL\Connection::lastInsertId"/>
|
||||
|
||||
<!-- FIXME -->
|
||||
<referencedFunction name="Doctrine\DBAL\DriverManager::getConnection"/>
|
||||
</errorLevel>
|
||||
</TooManyArguments>
|
||||
<TypeDoesNotContainNull>
|
||||
<errorLevel type="suppress">
|
||||
<!-- DBAL 3 compatibility -->
|
||||
<file name="src/Tools/SchemaTool.php"/>
|
||||
</errorLevel>
|
||||
</TypeDoesNotContainNull>
|
||||
<TypeDoesNotContainType>
|
||||
<errorLevel type="suppress">
|
||||
<file name="src/Internal/SQLResultCasing.php"/>
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
<!-- DBAL 3 compatibility -->
|
||||
<file name="src/UnitOfWork.php"/>
|
||||
<file name="src/Utility/LockSqlHelper.php"/>
|
||||
</errorLevel>
|
||||
</TypeDoesNotContainType>
|
||||
<UndefinedClass>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Compatibility with DBAL 3 -->
|
||||
<referencedClass name="Doctrine\DBAL\Platforms\SQLitePlatform"/>
|
||||
</errorLevel>
|
||||
</UndefinedClass>
|
||||
<UndefinedMethod>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Compatibility with DBAL 3 -->
|
||||
<referencedMethod name="Doctrine\DBAL\Connection::getEventManager"/>
|
||||
<!-- FIXME -->
|
||||
<referencedMethod name="Doctrine\DBAL\Schema\SchemaDiff::toSaveSql"/>
|
||||
</errorLevel>
|
||||
</UndefinedMethod>
|
||||
<UndefinedPropertyFetch>
|
||||
<errorLevel type="suppress">
|
||||
<!-- https://github.com/vimeo/psalm/issues/7878 -->
|
||||
<file name="src/Persisters/Collection/ManyToManyPersister.php"/>
|
||||
<file name="src/PersistentCollection.php"/>
|
||||
<file name="src/Utility/PersisterHelper.php"/>
|
||||
<file name="src/Tools/SchemaValidator.php"/>
|
||||
</errorLevel>
|
||||
</UndefinedPropertyFetch>
|
||||
<UnhandledMatchCondition>
|
||||
<errorLevel type="suppress">
|
||||
<!-- We can be certain that those values are not matched. -->
|
||||
<file name="src/Persisters/Entity/BasicEntityPersister.php"/>
|
||||
</errorLevel>
|
||||
</UnhandledMatchCondition>
|
||||
<ArgumentTypeCoercion>
|
||||
<errorLevel type="suppress">
|
||||
<!-- See https://github.com/JetBrains/phpstorm-stubs/pull/1383 -->
|
||||
<file name="src/Mapping/ClassMetadata.php"/>
|
||||
</errorLevel>
|
||||
</ArgumentTypeCoercion>
|
||||
</issueHandlers>
|
||||
</psalm>
|
||||
@@ -83,7 +83,7 @@ abstract class AbstractQuery
|
||||
* The parameter map of this query.
|
||||
*
|
||||
* @var ArrayCollection|Parameter[]
|
||||
* @psalm-var ArrayCollection<int, Parameter>
|
||||
* @phpstan-var ArrayCollection<int, Parameter>
|
||||
*/
|
||||
protected ArrayCollection $parameters;
|
||||
|
||||
@@ -95,14 +95,14 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* The map of query hints.
|
||||
*
|
||||
* @psalm-var array<string, mixed>
|
||||
* @phpstan-var array<string, mixed>
|
||||
*/
|
||||
protected array $hints = [];
|
||||
|
||||
/**
|
||||
* The hydration mode.
|
||||
*
|
||||
* @psalm-var string|AbstractQuery::HYDRATE_*
|
||||
* @phpstan-var string|AbstractQuery::HYDRATE_*
|
||||
*/
|
||||
protected string|int $hydrationMode = self::HYDRATE_OBJECT;
|
||||
|
||||
@@ -130,7 +130,7 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Second level query cache mode.
|
||||
*
|
||||
* @psalm-var Cache::MODE_*|null
|
||||
* @phpstan-var Cache::MODE_*|null
|
||||
*/
|
||||
protected int|null $cacheMode = null;
|
||||
|
||||
@@ -217,14 +217,14 @@ abstract class AbstractQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @psalm-return Cache::MODE_*|null */
|
||||
/** @phpstan-return Cache::MODE_*|null */
|
||||
public function getCacheMode(): int|null
|
||||
{
|
||||
return $this->cacheMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param Cache::MODE_* $cacheMode
|
||||
* @phpstan-param Cache::MODE_* $cacheMode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
@@ -267,7 +267,7 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Get all defined parameters.
|
||||
*
|
||||
* @psalm-return ArrayCollection<int, Parameter>
|
||||
* @phpstan-return ArrayCollection<int, Parameter>
|
||||
*/
|
||||
public function getParameters(): ArrayCollection
|
||||
{
|
||||
@@ -286,7 +286,7 @@ abstract class AbstractQuery
|
||||
$key = Parameter::normalizeName($key);
|
||||
|
||||
$filteredParameters = $this->parameters->filter(
|
||||
static fn (Parameter $parameter): bool => $parameter->getName() === $key
|
||||
static fn (Parameter $parameter): bool => $parameter->getName() === $key,
|
||||
);
|
||||
|
||||
return ! $filteredParameters->isEmpty() ? $filteredParameters->first() : null;
|
||||
@@ -296,14 +296,14 @@ abstract class AbstractQuery
|
||||
* Sets a collection of query parameters.
|
||||
*
|
||||
* @param ArrayCollection|mixed[] $parameters
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
|
||||
* @phpstan-param ArrayCollection<int, Parameter>|mixed[] $parameters
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParameters(ArrayCollection|array $parameters): static
|
||||
{
|
||||
if (is_array($parameters)) {
|
||||
/** @psalm-var ArrayCollection<int, Parameter> $parameterCollection */
|
||||
/** @phpstan-var ArrayCollection<int, Parameter> $parameterCollection */
|
||||
$parameterCollection = new ArrayCollection();
|
||||
|
||||
foreach ($parameters as $key => $value) {
|
||||
@@ -642,7 +642,7 @@ abstract class AbstractQuery
|
||||
* Change the default fetch mode of an association for this query.
|
||||
*
|
||||
* @param class-string $class
|
||||
* @psalm-param Mapping\ClassMetadata::FETCH_EAGER|Mapping\ClassMetadata::FETCH_LAZY $fetchMode
|
||||
* @phpstan-param Mapping\ClassMetadata::FETCH_EAGER|Mapping\ClassMetadata::FETCH_LAZY $fetchMode
|
||||
*/
|
||||
public function setFetchMode(string $class, string $assocName, int $fetchMode): static
|
||||
{
|
||||
@@ -656,7 +656,7 @@ abstract class AbstractQuery
|
||||
*
|
||||
* @param string|int $hydrationMode Doctrine processing mode to be used during hydration process.
|
||||
* One of the Query::HYDRATE_* constants.
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_* $hydrationMode
|
||||
* @phpstan-param string|AbstractQuery::HYDRATE_* $hydrationMode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
@@ -670,7 +670,7 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Gets the hydration mode currently used by the query.
|
||||
*
|
||||
* @psalm-return string|AbstractQuery::HYDRATE_*
|
||||
* @phpstan-return string|AbstractQuery::HYDRATE_*
|
||||
*/
|
||||
public function getHydrationMode(): string|int
|
||||
{
|
||||
@@ -682,7 +682,7 @@ abstract class AbstractQuery
|
||||
*
|
||||
* Alias for execute(null, $hydrationMode = HYDRATE_OBJECT).
|
||||
*
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_* $hydrationMode
|
||||
* @phpstan-param string|AbstractQuery::HYDRATE_* $hydrationMode
|
||||
*/
|
||||
public function getResult(string|int $hydrationMode = self::HYDRATE_OBJECT): mixed
|
||||
{
|
||||
@@ -728,7 +728,7 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Get exactly one result or null.
|
||||
*
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
* @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
*
|
||||
* @throws NonUniqueResultException
|
||||
*/
|
||||
@@ -763,7 +763,7 @@ abstract class AbstractQuery
|
||||
* If the result is not unique, a NonUniqueResultException is thrown.
|
||||
* If there is no result, a NoResultException is thrown.
|
||||
*
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
* @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
*
|
||||
* @throws NonUniqueResultException If the query result is not unique.
|
||||
* @throws NoResultException If the query returned no result.
|
||||
@@ -843,8 +843,8 @@ abstract class AbstractQuery
|
||||
* Executes the query and returns an iterable that can be used to incrementally
|
||||
* iterate over the result.
|
||||
*
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
* @phpstan-param ArrayCollection<int, Parameter>|mixed[] $parameters
|
||||
* @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
*
|
||||
* @return iterable<mixed>
|
||||
*/
|
||||
@@ -877,8 +877,8 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Executes the query.
|
||||
*
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
* @phpstan-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
|
||||
* @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
*/
|
||||
public function execute(
|
||||
ArrayCollection|array|null $parameters = null,
|
||||
@@ -894,8 +894,8 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Execute query ignoring second level cache.
|
||||
*
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
* @phpstan-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
|
||||
* @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
*/
|
||||
private function executeIgnoreQueryCache(
|
||||
ArrayCollection|array|null $parameters = null,
|
||||
@@ -965,8 +965,8 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Load from second level cache or executes the query and put into cache.
|
||||
*
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
* @phpstan-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
|
||||
* @phpstan-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
*/
|
||||
private function executeUsingQueryCache(
|
||||
ArrayCollection|array|null $parameters = null,
|
||||
@@ -1029,7 +1029,7 @@ abstract class AbstractQuery
|
||||
* automatically generated for you.
|
||||
*
|
||||
* @return string[] ($key, $hash)
|
||||
* @psalm-return array{string, string} ($key, $hash)
|
||||
* @phpstan-return array{string, string} ($key, $hash)
|
||||
*/
|
||||
protected function getHydrationCacheId(): array
|
||||
{
|
||||
|
||||
@@ -29,11 +29,14 @@ class CollectionCacheKey extends CacheKey
|
||||
public readonly string $entityClass,
|
||||
public readonly string $association,
|
||||
array $ownerIdentifier,
|
||||
string $filterHash = '',
|
||||
) {
|
||||
ksort($ownerIdentifier);
|
||||
|
||||
$this->ownerIdentifier = $ownerIdentifier;
|
||||
|
||||
parent::__construct(str_replace('\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association);
|
||||
$filterHash = $filterHash === '' ? '' : '_' . $filterHash;
|
||||
|
||||
parent::__construct(str_replace('\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association . $filterHash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class DefaultCache implements Cache
|
||||
|
||||
/**
|
||||
* @var QueryCache[]
|
||||
* @psalm-var array<string, QueryCache>
|
||||
* @phpstan-var array<string, QueryCache>
|
||||
*/
|
||||
private array $queryCaches = [];
|
||||
|
||||
|
||||
@@ -231,7 +231,6 @@ class DefaultQueryCache implements QueryCache
|
||||
$region = $persister->getCacheRegion();
|
||||
|
||||
$cm = $this->em->getClassMetadata($entityName);
|
||||
assert($cm instanceof ClassMetadata);
|
||||
|
||||
foreach ($result as $index => $entity) {
|
||||
$identifier = $this->uow->getEntityIdentifier($entity);
|
||||
@@ -297,7 +296,7 @@ class DefaultQueryCache implements QueryCache
|
||||
|
||||
/**
|
||||
* @return mixed[]|null
|
||||
* @psalm-return array{targetEntity: class-string, type: mixed, list?: array[], identifier?: array}|null
|
||||
* @phpstan-return array{targetEntity: class-string, type: mixed, list?: array[], identifier?: array}|null
|
||||
*/
|
||||
private function storeAssociationCache(QueryCacheKey $key, AssociationMapping $assoc, mixed $assocValue): array|null
|
||||
{
|
||||
@@ -348,7 +347,7 @@ class DefaultQueryCache implements QueryCache
|
||||
];
|
||||
}
|
||||
|
||||
/** @psalm-return list<mixed>|object|null */
|
||||
/** @phpstan-return list<mixed>|object|null */
|
||||
private function getAssociationValue(
|
||||
ResultSetMapping $rsm,
|
||||
string $assocAlias,
|
||||
@@ -374,9 +373,9 @@ class DefaultQueryCache implements QueryCache
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param array<array-key, array{field: string, class: string}> $path
|
||||
* @phpstan-param array<array-key, array{field: string, class: string}> $path
|
||||
*
|
||||
* @psalm-return list<mixed>|object|null
|
||||
* @phpstan-return list<mixed>|object|null
|
||||
*/
|
||||
private function getAssociationPathValue(mixed $value, array $path): array|object|null
|
||||
{
|
||||
|
||||
@@ -18,6 +18,7 @@ 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 function array_values;
|
||||
@@ -35,6 +36,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
protected array $queuedCache = [];
|
||||
|
||||
protected string $regionName;
|
||||
protected FilterCollection $filters;
|
||||
protected CollectionHydrator $hydrator;
|
||||
protected CacheLogger|null $cacheLogger;
|
||||
|
||||
@@ -48,6 +50,10 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
$cacheConfig = $configuration->getSecondLevelCacheConfiguration();
|
||||
$cacheFactory = $cacheConfig->getCacheFactory();
|
||||
|
||||
$this->region = $region;
|
||||
$this->persister = $persister;
|
||||
$this->association = $association;
|
||||
$this->filters = $em->getFilters();
|
||||
$this->regionName = $region->getName();
|
||||
$this->uow = $em->getUnitOfWork();
|
||||
$this->metadataFactory = $em->getMetadataFactory();
|
||||
@@ -135,7 +141,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
public function count(PersistentCollection $collection): int
|
||||
{
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
|
||||
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());
|
||||
$entry = $this->region->get($key);
|
||||
|
||||
if ($entry !== null) {
|
||||
|
||||
@@ -36,7 +36,7 @@ class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPers
|
||||
public function delete(PersistentCollection $collection): void
|
||||
{
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
|
||||
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());
|
||||
|
||||
$this->persister->delete($collection);
|
||||
|
||||
@@ -53,7 +53,7 @@ class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPers
|
||||
}
|
||||
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
|
||||
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());
|
||||
|
||||
// Invalidate non initialized collections OR ordered collection
|
||||
if ($isDirty && ! $isInitialized || $this->association->isOrdered()) {
|
||||
|
||||
@@ -61,7 +61,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
|
||||
public function delete(PersistentCollection $collection): void
|
||||
{
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
|
||||
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());
|
||||
$lock = $this->region->lock($key);
|
||||
|
||||
$this->persister->delete($collection);
|
||||
@@ -88,7 +88,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
|
||||
$this->persister->update($collection);
|
||||
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
|
||||
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());
|
||||
$lock = $this->region->lock($key);
|
||||
|
||||
if ($lock === null) {
|
||||
|
||||
@@ -24,11 +24,12 @@ 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 function array_merge;
|
||||
use function assert;
|
||||
use function func_get_args;
|
||||
use function serialize;
|
||||
use function sha1;
|
||||
|
||||
@@ -44,6 +45,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
protected TimestampCacheKey $timestampKey;
|
||||
protected EntityHydrator $hydrator;
|
||||
protected Cache $cache;
|
||||
protected FilterCollection $filters;
|
||||
protected CacheLogger|null $cacheLogger = null;
|
||||
protected string $regionName;
|
||||
|
||||
@@ -65,6 +67,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
$cacheFactory = $cacheConfig->getCacheFactory();
|
||||
|
||||
$this->cache = $em->getCache();
|
||||
$this->filters = $em->getFilters();
|
||||
$this->regionName = $region->getName();
|
||||
$this->uow = $em->getUnitOfWork();
|
||||
$this->metadataFactory = $em->getMetadataFactory();
|
||||
@@ -216,7 +219,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
? $this->persister->expandCriteriaParameters($criteria)
|
||||
: $this->persister->expandParameters($criteria);
|
||||
|
||||
return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset);
|
||||
return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset . $this->filters->getHash());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -473,7 +476,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
}
|
||||
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
$key = $this->buildCollectionCacheKey($assoc, $ownerId);
|
||||
$key = $this->buildCollectionCacheKey($assoc, $ownerId, $this->filters->getHash());
|
||||
$list = $persister->loadCollectionCache($collection, $key);
|
||||
|
||||
if ($list !== null) {
|
||||
@@ -504,7 +507,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
}
|
||||
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
$key = $this->buildCollectionCacheKey($assoc, $ownerId);
|
||||
$key = $this->buildCollectionCacheKey($assoc, $ownerId, $this->filters->getHash());
|
||||
$list = $persister->loadCollectionCache($collection, $key);
|
||||
|
||||
if ($list !== null) {
|
||||
@@ -547,11 +550,15 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $ownerId */
|
||||
protected function buildCollectionCacheKey(AssociationMapping $association, array $ownerId): CollectionCacheKey
|
||||
protected function buildCollectionCacheKey(AssociationMapping $association, array $ownerId, /* string $filterHash */): CollectionCacheKey
|
||||
{
|
||||
$metadata = $this->metadataFactory->getMetadataFor($association->sourceEntity);
|
||||
assert($metadata instanceof ClassMetadata);
|
||||
$filterHash = (string) (func_get_args()[2] ?? ''); // todo: move to argument in next major release
|
||||
|
||||
return new CollectionCacheKey($metadata->rootEntityName, $association->fieldName, $ownerId);
|
||||
return new CollectionCacheKey(
|
||||
$this->metadataFactory->getMetadataFor($association->sourceEntity)->rootEntityName,
|
||||
$association->fieldName,
|
||||
$ownerId,
|
||||
$filterHash,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ 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.
|
||||
@@ -41,16 +43,16 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
/** @var mixed[] */
|
||||
protected array $attributes = [];
|
||||
|
||||
/** @psalm-var array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> */
|
||||
/** @phpstan-var array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> */
|
||||
private $identityGenerationPreferences = [];
|
||||
|
||||
/** @psalm-param array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> $value */
|
||||
/** @phpstan-param array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> $value */
|
||||
public function setIdentityGenerationPreferences(array $value): void
|
||||
{
|
||||
$this->identityGenerationPreferences = $value;
|
||||
}
|
||||
|
||||
/** @psalm-return array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> $value */
|
||||
/** @phpstan-return array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> $value */
|
||||
public function getIdentityGenerationPreferences(): array
|
||||
{
|
||||
return $this->identityGenerationPreferences;
|
||||
@@ -122,7 +124,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
/**
|
||||
* Sets the entity alias map.
|
||||
*
|
||||
* @psalm-param array<string, string> $entityNamespaces
|
||||
* @phpstan-param array<string, string> $entityNamespaces
|
||||
*/
|
||||
public function setEntityNamespaces(array $entityNamespaces): void
|
||||
{
|
||||
@@ -132,7 +134,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
/**
|
||||
* Retrieves the list of registered entity namespace aliases.
|
||||
*
|
||||
* @psalm-return array<string, string>
|
||||
* @phpstan-return array<string, string>
|
||||
*/
|
||||
public function getEntityNamespaces(): array
|
||||
{
|
||||
@@ -191,7 +193,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* DQL function names are case-insensitive.
|
||||
*
|
||||
* @param class-string|callable $className Class name or a callable that returns the function.
|
||||
* @psalm-param class-string<FunctionNode>|callable(string):FunctionNode $className
|
||||
* @phpstan-param class-string<FunctionNode>|callable(string):FunctionNode $className
|
||||
*/
|
||||
public function addCustomStringFunction(string $name, string|callable $className): void
|
||||
{
|
||||
@@ -201,7 +203,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
/**
|
||||
* Gets the implementation class name of a registered custom string DQL function.
|
||||
*
|
||||
* @psalm-return class-string<FunctionNode>|callable(string):FunctionNode|null
|
||||
* @phpstan-return class-string<FunctionNode>|callable(string):FunctionNode|null
|
||||
*/
|
||||
public function getCustomStringFunction(string $name): string|callable|null
|
||||
{
|
||||
@@ -218,7 +220,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*
|
||||
* Any previously added string functions are discarded.
|
||||
*
|
||||
* @psalm-param array<string, class-string<FunctionNode>|callable(string):FunctionNode> $functions The map of custom
|
||||
* @phpstan-param array<string, class-string<FunctionNode>|callable(string):FunctionNode> $functions The map of custom
|
||||
* DQL string functions.
|
||||
*/
|
||||
public function setCustomStringFunctions(array $functions): void
|
||||
@@ -236,7 +238,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* DQL function names are case-insensitive.
|
||||
*
|
||||
* @param class-string|callable $className Class name or a callable that returns the function.
|
||||
* @psalm-param class-string<FunctionNode>|callable(string):FunctionNode $className
|
||||
* @phpstan-param class-string<FunctionNode>|callable(string):FunctionNode $className
|
||||
*/
|
||||
public function addCustomNumericFunction(string $name, string|callable $className): void
|
||||
{
|
||||
@@ -246,7 +248,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
/**
|
||||
* Gets the implementation class name of a registered custom numeric DQL function.
|
||||
*
|
||||
* @psalm-return ?class-string<FunctionNode>|callable(string):FunctionNode
|
||||
* @phpstan-return class-string<FunctionNode>|callable(string):FunctionNode|null
|
||||
*/
|
||||
public function getCustomNumericFunction(string $name): string|callable|null
|
||||
{
|
||||
@@ -281,7 +283,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* DQL function names are case-insensitive.
|
||||
*
|
||||
* @param string|callable $className Class name or a callable that returns the function.
|
||||
* @psalm-param class-string<FunctionNode>|callable(string):FunctionNode $className
|
||||
* @phpstan-param class-string<FunctionNode>|callable(string):FunctionNode $className
|
||||
*/
|
||||
public function addCustomDatetimeFunction(string $name, string|callable $className): void
|
||||
{
|
||||
@@ -309,7 +311,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* Any previously added date/time functions are discarded.
|
||||
*
|
||||
* @param array $functions The map of custom DQL date/time functions.
|
||||
* @psalm-param array<string, class-string<FunctionNode>|callable(string):FunctionNode> $functions
|
||||
* @phpstan-param array<string, class-string<FunctionNode>|callable(string):FunctionNode> $functions
|
||||
*/
|
||||
public function setCustomDatetimeFunctions(array $functions): void
|
||||
{
|
||||
@@ -538,7 +540,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
/**
|
||||
* Returns query hints, which will be applied to every query in application
|
||||
*
|
||||
* @psalm-return array<string, mixed>
|
||||
* @phpstan-return array<string, mixed>
|
||||
*/
|
||||
public function getDefaultQueryHints(): array
|
||||
{
|
||||
@@ -548,7 +550,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
/**
|
||||
* Sets array of query hints, which will be applied to every query in application
|
||||
*
|
||||
* @psalm-param array<string, mixed> $defaultQueryHints
|
||||
* @phpstan-param array<string, mixed> $defaultQueryHints
|
||||
*/
|
||||
public function setDefaultQueryHints(array $defaultQueryHints): void
|
||||
{
|
||||
@@ -593,6 +595,20 @@ 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
|
||||
*
|
||||
|
||||
@@ -565,9 +565,9 @@ class EntityManager implements EntityManagerInterface
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isUninitializedObject($obj): bool
|
||||
public function isUninitializedObject($value): bool
|
||||
{
|
||||
return $this->unitOfWork->isUninitializedObject($obj);
|
||||
return $this->unitOfWork->isUninitializedObject($value);
|
||||
}
|
||||
|
||||
public function getFilters(): FilterCollection
|
||||
@@ -586,7 +586,7 @@ class EntityManager implements EntityManagerInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param LockMode::* $lockMode
|
||||
* @phpstan-param LockMode::* $lockMode
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
* @throws TransactionRequiredException
|
||||
|
||||
@@ -71,10 +71,10 @@ interface EntityManagerInterface extends ObjectManager
|
||||
* If an exception occurs during execution of the function or flushing or transaction commit,
|
||||
* the transaction is rolled back, the EntityManager closed and the exception re-thrown.
|
||||
*
|
||||
* @psalm-param callable(self): T $func The function to execute transactionally.
|
||||
* @phpstan-param callable(self): T $func The function to execute transactionally.
|
||||
*
|
||||
* @return mixed The value returned from the closure.
|
||||
* @psalm-return T
|
||||
* @phpstan-return T
|
||||
*
|
||||
* @template T
|
||||
*/
|
||||
@@ -117,11 +117,11 @@ interface EntityManagerInterface extends ObjectManager
|
||||
* during the search.
|
||||
* @param int|null $lockVersion The version of the entity to find when using
|
||||
* optimistic locking.
|
||||
* @psalm-param class-string<T> $className
|
||||
* @psalm-param LockMode::*|null $lockMode
|
||||
* @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.
|
||||
* @psalm-return T|null
|
||||
* @phpstan-return T|null
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
* @throws ORMInvalidArgumentException
|
||||
@@ -139,7 +139,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
* @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.
|
||||
* @psalm-param LockMode::*|null $lockMode
|
||||
* @phpstan-param LockMode::*|null $lockMode
|
||||
*
|
||||
* @throws ORMInvalidArgumentException
|
||||
* @throws ORMException
|
||||
@@ -172,7 +172,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
/**
|
||||
* Acquire a lock on the given entity.
|
||||
*
|
||||
* @psalm-param LockMode::* $lockMode
|
||||
* @phpstan-param LockMode::* $lockMode
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
* @throws PessimisticLockException
|
||||
@@ -202,7 +202,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
/**
|
||||
* Create a new instance for the given hydration mode.
|
||||
*
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_* $hydrationMode
|
||||
* @phpstan-param string|AbstractQuery::HYDRATE_* $hydrationMode
|
||||
*
|
||||
* @throws ORMException
|
||||
*/
|
||||
@@ -233,9 +233,9 @@ interface EntityManagerInterface extends ObjectManager
|
||||
*
|
||||
* @param string|class-string<T> $className
|
||||
*
|
||||
* @psalm-return ($className is class-string<T> ? Mapping\ClassMetadata<T> : Mapping\ClassMetadata<object>)
|
||||
* @phpstan-return ($className is class-string<T> ? Mapping\ClassMetadata<T> : Mapping\ClassMetadata<object>)
|
||||
*
|
||||
* @psalm-template T of object
|
||||
* @phpstan-template T of object
|
||||
*/
|
||||
public function getClassMetadata(string $className): Mapping\ClassMetadata;
|
||||
}
|
||||
|
||||
@@ -76,10 +76,10 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
* @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.
|
||||
* @psalm-param LockMode::*|null $lockMode
|
||||
* @phpstan-param LockMode::*|null $lockMode
|
||||
*
|
||||
* @return object|null The entity instance or NULL if the entity can not be found.
|
||||
* @psalm-return ?T
|
||||
* @phpstan-return ?T
|
||||
*/
|
||||
public function find(mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
{
|
||||
@@ -89,7 +89,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
/**
|
||||
* Finds all entities in the repository.
|
||||
*
|
||||
* @psalm-return list<T> The entities.
|
||||
* @phpstan-return list<T> The entities.
|
||||
*/
|
||||
public function findAll(): array
|
||||
{
|
||||
@@ -101,7 +101,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-return list<T>
|
||||
* @phpstan-return list<T>
|
||||
*/
|
||||
public function findBy(array $criteria, array|null $orderBy = null, int|null $limit = null, int|null $offset = null): array
|
||||
{
|
||||
@@ -113,10 +113,10 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
/**
|
||||
* Finds a single entity by a set of criteria.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $criteria
|
||||
* @psalm-param array<string, string>|null $orderBy
|
||||
* @phpstan-param array<string, mixed> $criteria
|
||||
* @phpstan-param array<string, string>|null $orderBy
|
||||
*
|
||||
* @psalm-return T|null
|
||||
* @phpstan-return T|null
|
||||
*/
|
||||
public function findOneBy(array $criteria, array|null $orderBy = null): object|null
|
||||
{
|
||||
@@ -128,10 +128,10 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
/**
|
||||
* Counts entities by a set of criteria.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $criteria
|
||||
* @phpstan-param array<string, mixed> $criteria
|
||||
*
|
||||
* @return int The cardinality of the objects that match the given criteria.
|
||||
* @psalm-return 0|positive-int
|
||||
* @phpstan-return 0|positive-int
|
||||
*
|
||||
* @todo Add this method to `ObjectRepository` interface in the next major release
|
||||
*/
|
||||
@@ -144,7 +144,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
* Adds support for magic method calls.
|
||||
*
|
||||
* @param mixed[] $arguments
|
||||
* @psalm-param list<mixed> $arguments
|
||||
* @phpstan-param list<mixed> $arguments
|
||||
*
|
||||
* @throws BadMethodCallException If the method called is invalid.
|
||||
*/
|
||||
@@ -185,7 +185,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
return $this->em;
|
||||
}
|
||||
|
||||
/** @psalm-return ClassMetadata<T> */
|
||||
/** @phpstan-return ClassMetadata<T> */
|
||||
protected function getClassMetadata(): ClassMetadata
|
||||
{
|
||||
return $this->class;
|
||||
@@ -195,7 +195,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
* Select all elements from a selectable that match the expression and
|
||||
* return a new collection containing these elements.
|
||||
*
|
||||
* @psalm-return AbstractLazyCollection<int, T>&Selectable<int, T>
|
||||
* @phpstan-return AbstractLazyCollection<int, T>&Selectable<int, T>
|
||||
*/
|
||||
public function matching(Criteria $criteria): AbstractLazyCollection&Selectable
|
||||
{
|
||||
@@ -209,7 +209,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
*
|
||||
* @param string $method The method to call
|
||||
* @param string $by The property name used as condition
|
||||
* @psalm-param list<mixed> $arguments The arguments to pass at method call
|
||||
* @phpstan-param list<mixed> $arguments The arguments to pass at method call
|
||||
*
|
||||
* @throws InvalidMagicMethodCall If the method called is invalid or the
|
||||
* requested field/association does not exist.
|
||||
|
||||
@@ -38,7 +38,7 @@ class ListenersInvoker
|
||||
* @param ClassMetadata $metadata The entity metadata.
|
||||
* @param string $eventName The entity lifecycle event.
|
||||
*
|
||||
* @psalm-return int-mask-of<self::INVOKE_*> Bitmask of subscribed event systems.
|
||||
* @phpstan-return int-mask-of<self::INVOKE_*> Bitmask of subscribed event systems.
|
||||
*/
|
||||
public function getSubscribedSystems(ClassMetadata $metadata, string $eventName): int
|
||||
{
|
||||
@@ -66,7 +66,7 @@ class ListenersInvoker
|
||||
* @param string $eventName The entity lifecycle event.
|
||||
* @param object $entity The Entity on which the event occurred.
|
||||
* @param EventArgs $event The Event args.
|
||||
* @psalm-param int-mask-of<self::INVOKE_*> $invoke Bitmask to invoke listeners.
|
||||
* @phpstan-param int-mask-of<self::INVOKE_*> $invoke Bitmask to invoke listeners.
|
||||
*/
|
||||
public function invoke(
|
||||
ClassMetadata $metadata,
|
||||
|
||||
@@ -24,7 +24,7 @@ class PreUpdateEventArgs extends LifecycleEventArgs
|
||||
|
||||
/**
|
||||
* @param mixed[][] $changeSet
|
||||
* @psalm-param array<string, array{mixed, mixed}|PersistentCollection> $changeSet
|
||||
* @phpstan-param array<string, array{mixed, mixed}|PersistentCollection> $changeSet
|
||||
*/
|
||||
public function __construct(object $entity, EntityManagerInterface $em, array &$changeSet)
|
||||
{
|
||||
@@ -37,7 +37,7 @@ class PreUpdateEventArgs extends LifecycleEventArgs
|
||||
* Retrieves entity changeset.
|
||||
*
|
||||
* @return mixed[][]
|
||||
* @psalm-return array<string, array{mixed, mixed}|PersistentCollection>
|
||||
* @phpstan-return array<string, array{mixed, mixed}|PersistentCollection>
|
||||
*/
|
||||
public function getEntityChangeSet(): array
|
||||
{
|
||||
|
||||
@@ -103,16 +103,14 @@ final class Events
|
||||
* The onFlush event occurs when the EntityManager#flush() operation is invoked,
|
||||
* after any changes to managed entities have been determined but before any
|
||||
* actual database operations are executed. The event is only raised if there is
|
||||
* actually something to do for the underlying UnitOfWork. If nothing needs to be done,
|
||||
* the onFlush event is not raised.
|
||||
* actually something to do for the underlying UnitOfWork.
|
||||
*/
|
||||
public const onFlush = 'onFlush';
|
||||
|
||||
/**
|
||||
* The postFlush event occurs when the EntityManager#flush() operation is invoked and
|
||||
* after all actual database operations are executed successfully. The event is only raised if there is
|
||||
* actually something to do for the underlying UnitOfWork. If nothing needs to be done,
|
||||
* the postFlush event is not raised. The event won't be raised if an error occurs during the
|
||||
* 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';
|
||||
|
||||
@@ -18,18 +18,21 @@ use Generator;
|
||||
use LogicException;
|
||||
use ReflectionClass;
|
||||
|
||||
use function array_key_exists;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function count;
|
||||
use function end;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function ksort;
|
||||
|
||||
/**
|
||||
* Base class for all hydrators. A hydrator is a class that provides some form
|
||||
* of transformation of an SQL result set into another structure.
|
||||
*
|
||||
* @psalm-consistent-constructor
|
||||
* @phpstan-consistent-constructor
|
||||
*/
|
||||
abstract class AbstractHydrator
|
||||
{
|
||||
@@ -86,7 +89,7 @@ abstract class AbstractHydrator
|
||||
/**
|
||||
* Initiates a row-by-row hydration.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $hints
|
||||
* @phpstan-param array<string, mixed> $hints
|
||||
*
|
||||
* @return Generator<array-key, mixed>
|
||||
*
|
||||
@@ -153,7 +156,7 @@ abstract class AbstractHydrator
|
||||
/**
|
||||
* Hydrates all rows returned by the passed statement instance at once.
|
||||
*
|
||||
* @psalm-param array<string, string> $hints
|
||||
* @phpstan-param array<string, string> $hints
|
||||
*/
|
||||
public function hydrateAll(Result $stmt, ResultSetMapping $resultSetMapping, array $hints = []): mixed
|
||||
{
|
||||
@@ -242,14 +245,14 @@ abstract class AbstractHydrator
|
||||
* the values applied. Scalar values are kept in a specific key 'scalars'.
|
||||
*
|
||||
* @param mixed[] $data SQL Result Row.
|
||||
* @psalm-param array<string, string> $id Dql-Alias => ID-Hash.
|
||||
* @psalm-param array<string, bool> $nonemptyComponents Does this DQL-Alias has at least one non NULL value?
|
||||
* @phpstan-param array<string, string> $id Dql-Alias => ID-Hash.
|
||||
* @phpstan-param array<string, bool> $nonemptyComponents Does this DQL-Alias has at least one non NULL value?
|
||||
*
|
||||
* @return array<string, array<string, mixed>> An array with all the fields
|
||||
* (name => value) of the data
|
||||
* row, grouped by their
|
||||
* component alias.
|
||||
* @psalm-return array{
|
||||
* @phpstan-return array{
|
||||
* data: array<array-key, array>,
|
||||
* newObjects?: array<array-key, array{
|
||||
* class: ReflectionClass,
|
||||
@@ -263,6 +266,17 @@ abstract class AbstractHydrator
|
||||
{
|
||||
$rowData = ['data' => [], 'newObjects' => []];
|
||||
|
||||
foreach ($this->rsm->newObjectMappings as $mapping) {
|
||||
if (! array_key_exists($mapping['objIndex'], $this->rsm->newObject)) {
|
||||
$this->rsm->newObject[$mapping['objIndex']] = $mapping['className'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->rsm->newObject as $objIndex => $newObject) {
|
||||
$rowData['newObjects'][$objIndex]['class'] = new ReflectionClass($newObject);
|
||||
$rowData['newObjects'][$objIndex]['args'] = [];
|
||||
}
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$cacheKeyInfo = $this->hydrateColumnInfo($key);
|
||||
if ($cacheKeyInfo === null) {
|
||||
@@ -282,7 +296,6 @@ abstract class AbstractHydrator
|
||||
$value = $this->buildEnum($value, $cacheKeyInfo['enumType']);
|
||||
}
|
||||
|
||||
$rowData['newObjects'][$objIndex]['class'] = $cacheKeyInfo['class'];
|
||||
$rowData['newObjects'][$objIndex]['args'][$argIndex] = $value;
|
||||
break;
|
||||
|
||||
@@ -336,21 +349,32 @@ abstract class AbstractHydrator
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->resultSetMapping()->nestedNewObjectArguments as $objIndex => ['ownerIndex' => $ownerIndex, 'argIndex' => $argIndex]) {
|
||||
if (! isset($rowData['newObjects'][$ownerIndex . ':' . $argIndex])) {
|
||||
continue;
|
||||
$nestedEntities = [];
|
||||
/**@var string $argAlias */
|
||||
foreach ($this->resultSetMapping()->nestedNewObjectArguments as ['ownerIndex' => $ownerIndex, 'argIndex' => $argIndex, 'argAlias' => $argAlias]) {
|
||||
if (array_key_exists($argAlias, $rowData['newObjects'])) {
|
||||
ksort($rowData['newObjects'][$argAlias]['args']);
|
||||
$rowData['newObjects'][$ownerIndex]['args'][$argIndex] = $rowData['newObjects'][$argAlias]['class']->newInstanceArgs($rowData['newObjects'][$argAlias]['args']);
|
||||
unset($rowData['newObjects'][$argAlias]);
|
||||
} elseif (array_key_exists($argAlias, $rowData['data'])) {
|
||||
if (! array_key_exists($argAlias, $nestedEntities)) {
|
||||
$nestedEntities[$argAlias] = '';
|
||||
$rowData['data'][$argAlias] = $this->hydrateNestedEntity($rowData['data'][$argAlias], $argAlias);
|
||||
}
|
||||
|
||||
$rowData['newObjects'][$ownerIndex]['args'][$argIndex] = $rowData['data'][$argAlias];
|
||||
} else {
|
||||
throw new LogicException($argAlias . ' does not exist');
|
||||
}
|
||||
}
|
||||
|
||||
$newObject = $rowData['newObjects'][$ownerIndex . ':' . $argIndex];
|
||||
unset($rowData['newObjects'][$ownerIndex . ':' . $argIndex]);
|
||||
|
||||
$obj = $newObject['class']->newInstanceArgs($newObject['args']);
|
||||
|
||||
$rowData['newObjects'][$ownerIndex]['args'][$argIndex] = $obj;
|
||||
foreach (array_keys($nestedEntities) as $entity) {
|
||||
unset($rowData['data'][$entity]);
|
||||
}
|
||||
|
||||
foreach ($rowData['newObjects'] as $objIndex => $newObject) {
|
||||
$obj = $newObject['class']->newInstanceArgs($newObject['args']);
|
||||
ksort($rowData['newObjects'][$objIndex]['args']);
|
||||
$obj = $rowData['newObjects'][$objIndex]['class']->newInstanceArgs($rowData['newObjects'][$objIndex]['args']);
|
||||
|
||||
$rowData['newObjects'][$objIndex]['obj'] = $obj;
|
||||
}
|
||||
@@ -358,6 +382,12 @@ abstract class AbstractHydrator
|
||||
return $rowData;
|
||||
}
|
||||
|
||||
/** @param mixed[] $data pre-hydrated SQL Result Row. */
|
||||
protected function hydrateNestedEntity(array $data, string $dqlAlias): mixed
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a row of the result set.
|
||||
*
|
||||
@@ -367,10 +397,10 @@ abstract class AbstractHydrator
|
||||
* of elements as before.
|
||||
*
|
||||
* @param mixed[] $data
|
||||
* @psalm-param array<string, mixed> $data
|
||||
* @phpstan-param array<string, mixed> $data
|
||||
*
|
||||
* @return mixed[] The processed row.
|
||||
* @psalm-return array<string, mixed>
|
||||
* @phpstan-return array<string, mixed>
|
||||
*/
|
||||
protected function gatherScalarRowData(array &$data): array
|
||||
{
|
||||
@@ -405,7 +435,7 @@ abstract class AbstractHydrator
|
||||
* @param string $key Column name
|
||||
*
|
||||
* @return mixed[]|null
|
||||
* @psalm-return array<string, mixed>|null
|
||||
* @phpstan-return array<string, mixed>|null
|
||||
*/
|
||||
protected function hydrateColumnInfo(string $key): array|null
|
||||
{
|
||||
@@ -454,7 +484,6 @@ abstract class AbstractHydrator
|
||||
'type' => Type::getType($this->rsm->typeMappings[$key]),
|
||||
'argIndex' => $mapping['argIndex'],
|
||||
'objIndex' => $mapping['objIndex'],
|
||||
'class' => new ReflectionClass($mapping['className']),
|
||||
'enumType' => $this->rsm->enumMappings[$key] ?? null,
|
||||
];
|
||||
|
||||
@@ -502,7 +531,7 @@ abstract class AbstractHydrator
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @psalm-return non-empty-list<string>
|
||||
* @phpstan-return non-empty-list<string>
|
||||
*/
|
||||
private function getDiscriminatorValues(ClassMetadata $classMetadata): array
|
||||
{
|
||||
|
||||
@@ -70,6 +70,10 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$parent = $this->resultSetMapping()->parentAliasMap[$dqlAlias];
|
||||
|
||||
if (! isset($this->resultSetMapping()->aliasMap[$parent])) {
|
||||
if (isset($this->resultSetMapping()->nestedEntities[$dqlAlias])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $parent);
|
||||
}
|
||||
|
||||
@@ -171,7 +175,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
): PersistentCollection {
|
||||
$oid = spl_object_id($entity);
|
||||
$relation = $class->associationMappings[$fieldName];
|
||||
$value = $class->reflFields[$fieldName]->getValue($entity);
|
||||
$value = $class->propertyAccessors[$fieldName]->getValue($entity);
|
||||
|
||||
if ($value === null || is_array($value)) {
|
||||
$value = new ArrayCollection((array) $value);
|
||||
@@ -186,7 +190,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
);
|
||||
$value->setOwner($entity, $relation);
|
||||
|
||||
$class->reflFields[$fieldName]->setValue($entity, $value);
|
||||
$class->propertyAccessors[$fieldName]->setValue($entity, $value);
|
||||
$this->uow->setOriginalEntityProperty($oid, $fieldName, $value);
|
||||
|
||||
$this->initializedCollections[$oid . $fieldName] = $value;
|
||||
@@ -213,7 +217,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
* Gets an entity instance.
|
||||
*
|
||||
* @param string $dqlAlias The DQL alias of the entity's class.
|
||||
* @psalm-param array<string, mixed> $data The instance data.
|
||||
* @phpstan-param array<string, mixed> $data The instance data.
|
||||
*
|
||||
* @throws HydrationException
|
||||
*/
|
||||
@@ -266,7 +270,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
|
||||
/**
|
||||
* @param class-string $className
|
||||
* @psalm-param array<string, mixed> $data
|
||||
* @phpstan-param array<string, mixed> $data
|
||||
*/
|
||||
private function getEntityFromIdentityMap(string $className, array $data): object|bool
|
||||
{
|
||||
@@ -346,7 +350,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$parentClass = $this->metadataCache[$this->resultSetMapping()->aliasMap[$parentAlias]];
|
||||
$relationField = $this->resultSetMapping()->relationMap[$dqlAlias];
|
||||
$relation = $parentClass->associationMappings[$relationField];
|
||||
$reflField = $parentClass->reflFields[$relationField];
|
||||
$reflField = $parentClass->propertyAccessors[$relationField];
|
||||
|
||||
// Get a reference to the parent object to which the joined element belongs.
|
||||
if ($this->resultSetMapping()->isMixed && isset($this->rootAliases[$parentAlias])) {
|
||||
@@ -446,13 +450,13 @@ class ObjectHydrator extends AbstractHydrator
|
||||
if ($relation->inversedBy !== null) {
|
||||
$inverseAssoc = $targetClass->associationMappings[$relation->inversedBy];
|
||||
if ($inverseAssoc->isToOne()) {
|
||||
$targetClass->reflFields[$inverseAssoc->fieldName]->setValue($element, $parentObject);
|
||||
$targetClass->propertyAccessors[$inverseAssoc->fieldName]->setValue($element, $parentObject);
|
||||
$this->uow->setOriginalEntityProperty(spl_object_id($element), $inverseAssoc->fieldName, $parentObject);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For sure bidirectional, as there is no inverse side in unidirectional mappings
|
||||
$targetClass->reflFields[$relation->mappedBy]->setValue($element, $parentObject);
|
||||
$targetClass->propertyAccessors[$relation->mappedBy]->setValue($element, $parentObject);
|
||||
$this->uow->setOriginalEntityProperty(spl_object_id($element), $relation->mappedBy, $parentObject);
|
||||
}
|
||||
|
||||
@@ -569,6 +573,16 @@ class ObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
}
|
||||
|
||||
/** @param mixed[] $data pre-hydrated SQL Result Row. */
|
||||
protected function hydrateNestedEntity(array $data, string $dqlAlias): mixed
|
||||
{
|
||||
if (isset($this->resultSetMapping()->nestedEntities[$dqlAlias])) {
|
||||
return $this->getEntity($data, $dqlAlias);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* When executed in a hydrate() loop we may have to clear internal state to
|
||||
* decrease memory consumption.
|
||||
|
||||
@@ -17,6 +17,7 @@ use function array_search;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function key;
|
||||
use function reset;
|
||||
use function sprintf;
|
||||
@@ -138,14 +139,21 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
|
||||
if ($value !== null && isset($cacheKeyInfo['enumType'])) {
|
||||
$originalValue = $value;
|
||||
$originalValue = $currentValue = $value;
|
||||
try {
|
||||
$value = $this->buildEnum($originalValue, $cacheKeyInfo['enumType']);
|
||||
if (! is_array($originalValue)) {
|
||||
$value = $this->buildEnum($originalValue, $cacheKeyInfo['enumType']);
|
||||
} else {
|
||||
$value = [];
|
||||
foreach ($originalValue as $i => $currentValue) {
|
||||
$value[$i] = $this->buildEnum($currentValue, $cacheKeyInfo['enumType']);
|
||||
}
|
||||
}
|
||||
} catch (ValueError $e) {
|
||||
throw MappingException::invalidEnumValue(
|
||||
$entityName,
|
||||
$cacheKeyInfo['fieldName'],
|
||||
(string) $originalValue,
|
||||
(string) $currentValue,
|
||||
$cacheKeyInfo['enumType'],
|
||||
$e,
|
||||
);
|
||||
|
||||
@@ -29,7 +29,7 @@ trait NoUnknownNamedArguments
|
||||
* @param TItem[] $parameter
|
||||
*
|
||||
* @template TItem
|
||||
* @psalm-assert list<TItem> $parameter
|
||||
* @phpstan-assert list<TItem> $parameter
|
||||
*/
|
||||
private static function validateVariadicParameter(array $parameter): void
|
||||
{
|
||||
|
||||
@@ -97,7 +97,7 @@ abstract class AssociationMapping implements ArrayAccess
|
||||
|
||||
/**
|
||||
* @param mixed[] $mappingArray
|
||||
* @psalm-param array{
|
||||
* @phpstan-param array{
|
||||
* fieldName: string,
|
||||
* sourceEntity: class-string,
|
||||
* targetEntity: class-string,
|
||||
@@ -135,7 +135,13 @@ abstract class AssociationMapping implements ArrayAccess
|
||||
continue;
|
||||
}
|
||||
|
||||
assert($mapping instanceof ManyToManyOwningSideMapping);
|
||||
if (! $mapping instanceof ManyToManyOwningSideMapping) {
|
||||
throw new MappingException(
|
||||
"Mapping error on field '" .
|
||||
$mapping->fieldName . "' in " . $mapping->sourceEntity .
|
||||
" : 'joinTable' can only be set on many-to-many owning side.",
|
||||
);
|
||||
}
|
||||
|
||||
$mapping->joinTable = JoinTableMapping::fromMappingArray($value);
|
||||
|
||||
@@ -153,75 +159,75 @@ abstract class AssociationMapping implements ArrayAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-assert-if-true OwningSideMapping $this
|
||||
* @psalm-assert-if-false InverseSideMapping $this
|
||||
* @phpstan-assert-if-true OwningSideMapping $this
|
||||
* @phpstan-assert-if-false InverseSideMapping $this
|
||||
*/
|
||||
final public function isOwningSide(): bool
|
||||
{
|
||||
return $this instanceof OwningSideMapping;
|
||||
}
|
||||
|
||||
/** @psalm-assert-if-true ToOneAssociationMapping $this */
|
||||
/** @phpstan-assert-if-true ToOneAssociationMapping $this */
|
||||
final public function isToOne(): bool
|
||||
{
|
||||
return $this instanceof ToOneAssociationMapping;
|
||||
}
|
||||
|
||||
/** @psalm-assert-if-true ToManyAssociationMapping $this */
|
||||
/** @phpstan-assert-if-true ToManyAssociationMapping $this */
|
||||
final public function isToMany(): bool
|
||||
{
|
||||
return $this instanceof ToManyAssociationMapping;
|
||||
}
|
||||
|
||||
/** @psalm-assert-if-true OneToOneOwningSideMapping $this */
|
||||
/** @phpstan-assert-if-true OneToOneOwningSideMapping $this */
|
||||
final public function isOneToOneOwningSide(): bool
|
||||
{
|
||||
return $this->isOneToOne() && $this->isOwningSide();
|
||||
}
|
||||
|
||||
/** @psalm-assert-if-true OneToOneOwningSideMapping|ManyToOneAssociationMapping $this */
|
||||
/** @phpstan-assert-if-true OneToOneOwningSideMapping|ManyToOneAssociationMapping $this */
|
||||
final public function isToOneOwningSide(): bool
|
||||
{
|
||||
return $this->isToOne() && $this->isOwningSide();
|
||||
}
|
||||
|
||||
/** @psalm-assert-if-true ManyToManyOwningSideMapping $this */
|
||||
/** @phpstan-assert-if-true ManyToManyOwningSideMapping $this */
|
||||
final public function isManyToManyOwningSide(): bool
|
||||
{
|
||||
return $this instanceof ManyToManyOwningSideMapping;
|
||||
}
|
||||
|
||||
/** @psalm-assert-if-true OneToOneAssociationMapping $this */
|
||||
/** @phpstan-assert-if-true OneToOneAssociationMapping $this */
|
||||
final public function isOneToOne(): bool
|
||||
{
|
||||
return $this instanceof OneToOneAssociationMapping;
|
||||
}
|
||||
|
||||
/** @psalm-assert-if-true OneToManyAssociationMapping $this */
|
||||
/** @phpstan-assert-if-true OneToManyAssociationMapping $this */
|
||||
final public function isOneToMany(): bool
|
||||
{
|
||||
return $this instanceof OneToManyAssociationMapping;
|
||||
}
|
||||
|
||||
/** @psalm-assert-if-true ManyToOneAssociationMapping $this */
|
||||
/** @phpstan-assert-if-true ManyToOneAssociationMapping $this */
|
||||
final public function isManyToOne(): bool
|
||||
{
|
||||
return $this instanceof ManyToOneAssociationMapping;
|
||||
}
|
||||
|
||||
/** @psalm-assert-if-true ManyToManyAssociationMapping $this */
|
||||
/** @phpstan-assert-if-true ManyToManyAssociationMapping $this */
|
||||
final public function isManyToMany(): bool
|
||||
{
|
||||
return $this instanceof ManyToManyAssociationMapping;
|
||||
}
|
||||
|
||||
/** @psalm-assert-if-true ToManyAssociationMapping $this */
|
||||
/** @phpstan-assert-if-true ToManyAssociationMapping $this */
|
||||
final public function isOrdered(): bool
|
||||
{
|
||||
return $this->isToMany() && $this->orderBy() !== [];
|
||||
}
|
||||
|
||||
/** @psalm-assert-if-true ToManyAssociationMapping $this */
|
||||
/** @phpstan-assert-if-true ToManyAssociationMapping $this */
|
||||
public function isIndexed(): bool
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -27,7 +27,7 @@ final class AssociationOverride implements MappingAttribute
|
||||
* @param JoinColumn|array<JoinColumn> $inverseJoinColumns
|
||||
* @param JoinTable|null $joinTable The join table that maps the relationship.
|
||||
* @param string|null $inversedBy The name of the association-field on the inverse-side.
|
||||
* @psalm-param 'LAZY'|'EAGER'|'EXTRA_LAZY'|null $fetch
|
||||
* @phpstan-param 'LAZY'|'EAGER'|'EXTRA_LAZY'|null $fetch
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $name,
|
||||
|
||||
@@ -109,7 +109,7 @@ class ClassMetadataBuilder
|
||||
/**
|
||||
* Adds Index.
|
||||
*
|
||||
* @psalm-param list<string> $columns
|
||||
* @phpstan-param list<string> $columns
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
@@ -127,7 +127,7 @@ class ClassMetadataBuilder
|
||||
/**
|
||||
* Adds Unique Constraint.
|
||||
*
|
||||
* @psalm-param list<string> $columns
|
||||
* @phpstan-param list<string> $columns
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
@@ -235,7 +235,7 @@ class ClassMetadataBuilder
|
||||
/**
|
||||
* Adds Field.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping
|
||||
* @phpstan-param array<string, mixed> $mapping
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Doctrine\ORM\Mapping\Builder;
|
||||
class OneToManyAssociationBuilder extends AssociationBuilder
|
||||
{
|
||||
/**
|
||||
* @psalm-param array<string, string> $fieldNames
|
||||
* @phpstan-param array<string, string> $fieldNames
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,7 @@ use Attribute;
|
||||
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
|
||||
final class Cache implements MappingAttribute
|
||||
{
|
||||
/** @psalm-param 'READ_ONLY'|'NONSTRICT_READ_WRITE'|'READ_WRITE' $usage */
|
||||
/** @phpstan-param 'READ_ONLY'|'NONSTRICT_READ_WRITE'|'READ_WRITE' $usage */
|
||||
public function __construct(
|
||||
public readonly string $usage = 'READ_ONLY',
|
||||
public readonly string|null $region = null,
|
||||
|
||||
@@ -9,7 +9,7 @@ use Attribute;
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class ChangeTrackingPolicy implements MappingAttribute
|
||||
{
|
||||
/** @psalm-param 'DEFERRED_IMPLICIT'|'DEFERRED_EXPLICIT' $value */
|
||||
/** @phpstan-param 'DEFERRED_IMPLICIT'|'DEFERRED_EXPLICIT' $value */
|
||||
public function __construct(
|
||||
public readonly string $value,
|
||||
) {
|
||||
|
||||
@@ -14,9 +14,12 @@ use Doctrine\Instantiator\InstantiatorInterface;
|
||||
use Doctrine\ORM\Cache\Exception\NonCacheableEntityAssociation;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Id\AbstractIdGenerator;
|
||||
use Doctrine\ORM\Mapping\PropertyAccessors\EmbeddablePropertyAccessor;
|
||||
use Doctrine\ORM\Mapping\PropertyAccessors\EnumPropertyAccessor;
|
||||
use Doctrine\ORM\Mapping\PropertyAccessors\PropertyAccessor;
|
||||
use Doctrine\ORM\Mapping\PropertyAccessors\PropertyAccessorFactory;
|
||||
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
|
||||
use Doctrine\Persistence\Mapping\ReflectionService;
|
||||
use Doctrine\Persistence\Reflection\EnumReflectionProperty;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use ReflectionClass;
|
||||
@@ -25,7 +28,10 @@ use ReflectionProperty;
|
||||
use Stringable;
|
||||
|
||||
use function array_column;
|
||||
use function array_count_values;
|
||||
use function array_diff;
|
||||
use function array_filter;
|
||||
use function array_flip;
|
||||
use function array_intersect;
|
||||
use function array_key_exists;
|
||||
use function array_keys;
|
||||
@@ -39,6 +45,7 @@ use function count;
|
||||
use function defined;
|
||||
use function enum_exists;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function interface_exists;
|
||||
use function is_string;
|
||||
@@ -67,12 +74,14 @@ use function trim;
|
||||
* get the whole class name, namespace inclusive, prepended to every property in
|
||||
* the serialized representation).
|
||||
*
|
||||
* @psalm-type ConcreteAssociationMapping = OneToOneOwningSideMapping|OneToOneInverseSideMapping|ManyToOneAssociationMapping|OneToManyAssociationMapping|ManyToManyOwningSideMapping|ManyToManyInverseSideMapping
|
||||
* @phpstan-type ConcreteAssociationMapping = OneToOneOwningSideMapping|OneToOneInverseSideMapping|ManyToOneAssociationMapping|OneToManyAssociationMapping|ManyToManyOwningSideMapping|ManyToManyInverseSideMapping
|
||||
* @template-covariant T of object
|
||||
* @template-implements PersistenceClassMetadata<T>
|
||||
*/
|
||||
class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
{
|
||||
use GetReflectionClassImplementation;
|
||||
|
||||
/* The inheritance mapping types */
|
||||
/**
|
||||
* NONE means the class does not participate in an inheritance hierarchy
|
||||
@@ -231,7 +240,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same
|
||||
* as {@link $name}.
|
||||
*
|
||||
* @psalm-var class-string
|
||||
* @phpstan-var class-string
|
||||
*/
|
||||
public string $rootEntityName;
|
||||
|
||||
@@ -255,7 +264,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* The name of the custom repository class used for the entity class.
|
||||
* (Optional).
|
||||
*
|
||||
* @psalm-var ?class-string<EntityRepository>
|
||||
* @phpstan-var ?class-string<EntityRepository>
|
||||
*/
|
||||
public string|null $customRepositoryClassName = null;
|
||||
|
||||
@@ -273,7 +282,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* READ-ONLY: The names of the parent <em>entity</em> classes (ancestors), starting with the
|
||||
* nearest one and ending with the root entity class.
|
||||
*
|
||||
* @psalm-var list<class-string>
|
||||
* @phpstan-var list<class-string>
|
||||
*/
|
||||
public array $parentClasses = [];
|
||||
|
||||
@@ -300,14 +309,14 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* For subclasses of such root entities, the list can be reused/passed downwards, it only needs to
|
||||
* be filtered accordingly (only keep remaining subclasses)
|
||||
*
|
||||
* @psalm-var list<class-string>
|
||||
* @phpstan-var list<class-string>
|
||||
*/
|
||||
public array $subClasses = [];
|
||||
|
||||
/**
|
||||
* READ-ONLY: The names of all embedded classes based on properties.
|
||||
*
|
||||
* @psalm-var array<string, EmbeddedClassMapping>
|
||||
* @phpstan-var array<string, EmbeddedClassMapping>
|
||||
*/
|
||||
public array $embeddedClasses = [];
|
||||
|
||||
@@ -315,21 +324,21 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* READ-ONLY: The field names of all fields that are part of the identifier/primary key
|
||||
* of the mapped entity class.
|
||||
*
|
||||
* @psalm-var list<string>
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
public array $identifier = [];
|
||||
|
||||
/**
|
||||
* READ-ONLY: The inheritance mapping type used by the class.
|
||||
*
|
||||
* @psalm-var self::INHERITANCE_TYPE_*
|
||||
* @phpstan-var self::INHERITANCE_TYPE_*
|
||||
*/
|
||||
public int $inheritanceType = self::INHERITANCE_TYPE_NONE;
|
||||
|
||||
/**
|
||||
* READ-ONLY: The Id generator type used by the class.
|
||||
*
|
||||
* @psalm-var self::GENERATOR_TYPE_*
|
||||
* @phpstan-var self::GENERATOR_TYPE_*
|
||||
*/
|
||||
public int $generatorType = self::GENERATOR_TYPE_NONE;
|
||||
|
||||
@@ -345,7 +354,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* READ-ONLY: An array of field names. Used to look up field names from column names.
|
||||
* Keys are column names and values are field names.
|
||||
*
|
||||
* @psalm-var array<string, string>
|
||||
* @phpstan-var array<string, string>
|
||||
*/
|
||||
public array $fieldNames = [];
|
||||
|
||||
@@ -380,7 +389,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
*
|
||||
* @var array<int|string, string>
|
||||
*
|
||||
* @psalm-var array<int|string, class-string>
|
||||
* @phpstan-var array<int|string, class-string>
|
||||
*/
|
||||
public array $discriminatorMap = [];
|
||||
|
||||
@@ -400,7 +409,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* uniqueConstraints => array
|
||||
*
|
||||
* @var mixed[]
|
||||
* @psalm-var array{
|
||||
* @phpstan-var array{
|
||||
* name: string,
|
||||
* schema?: string,
|
||||
* indexes?: array,
|
||||
@@ -414,14 +423,14 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* READ-ONLY: The registered lifecycle callbacks for entities of this class.
|
||||
*
|
||||
* @psalm-var array<string, list<string>>
|
||||
* @phpstan-var array<string, list<string>>
|
||||
*/
|
||||
public array $lifecycleCallbacks = [];
|
||||
|
||||
/**
|
||||
* READ-ONLY: The registered entity listeners.
|
||||
*
|
||||
* @psalm-var array<string, list<array{class: class-string, method: string}>>
|
||||
* @phpstan-var array<string, list<array{class: class-string, method: string}>>
|
||||
*/
|
||||
public array $entityListeners = [];
|
||||
|
||||
@@ -437,7 +446,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* )
|
||||
* </pre>
|
||||
*
|
||||
* @psalm-var array<string, ConcreteAssociationMapping>
|
||||
* @phpstan-var array<string, ConcreteAssociationMapping>
|
||||
*/
|
||||
public array $associationMappings = [];
|
||||
|
||||
@@ -481,7 +490,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* </code>
|
||||
*
|
||||
* @var array<string, mixed>|null
|
||||
* @psalm-var array{sequenceName: string, allocationSize: string, initialValue: string, quoted?: mixed}|null
|
||||
* @phpstan-var array{sequenceName: string, allocationSize: string, initialValue: string, quoted?: mixed}|null
|
||||
* @todo Merge with tableGeneratorDefinition into generic generatorDefinition
|
||||
*/
|
||||
public array|null $sequenceGeneratorDefinition = null;
|
||||
@@ -535,9 +544,12 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* The ReflectionProperty instances of the mapped class.
|
||||
*
|
||||
* @var array<string, ReflectionProperty|null>
|
||||
* @var LegacyReflectionFields|array<string, ReflectionProperty>
|
||||
*/
|
||||
public array $reflFields = [];
|
||||
public LegacyReflectionFields|array $reflFields = [];
|
||||
|
||||
/** @var array<string, PropertyAccessors\PropertyAccessor> */
|
||||
public array $propertyAccessors = [];
|
||||
|
||||
private InstantiatorInterface|null $instantiator = null;
|
||||
|
||||
@@ -548,7 +560,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* metadata of the class with the given name.
|
||||
*
|
||||
* @param string $name The name of the entity class the new instance is used for.
|
||||
* @psalm-param class-string<T> $name
|
||||
* @phpstan-param class-string<T> $name
|
||||
*/
|
||||
public function __construct(public string $name, NamingStrategy|null $namingStrategy = null, TypedFieldMapper|null $typedFieldMapper = null)
|
||||
{
|
||||
@@ -561,14 +573,24 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Gets the ReflectionProperties of the mapped class.
|
||||
*
|
||||
* @return ReflectionProperty[]|null[] An array of ReflectionProperty instances.
|
||||
* @psalm-return array<ReflectionProperty|null>
|
||||
* @return LegacyReflectionFields|ReflectionProperty[] An array of ReflectionProperty instances.
|
||||
* @phpstan-return LegacyReflectionFields|array<string, ReflectionProperty>
|
||||
*/
|
||||
public function getReflectionProperties(): array
|
||||
public function getReflectionProperties(): array|LegacyReflectionFields
|
||||
{
|
||||
return $this->reflFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ReflectionProperties of the mapped class.
|
||||
*
|
||||
* @return PropertyAccessor[] An array of PropertyAccessor instances.
|
||||
*/
|
||||
public function getPropertyAccessors(): array
|
||||
{
|
||||
return $this->propertyAccessors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a ReflectionProperty for a specific field of the mapped class.
|
||||
*/
|
||||
@@ -577,11 +599,12 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
return $this->reflFields[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ReflectionProperty for the single identifier field.
|
||||
*
|
||||
* @throws BadMethodCallException If the class has a composite identifier.
|
||||
*/
|
||||
public function getPropertyAccessor(string $name): PropertyAccessor|null
|
||||
{
|
||||
return $this->propertyAccessors[$name] ?? null;
|
||||
}
|
||||
|
||||
/** @throws BadMethodCallException If the class has a composite identifier. */
|
||||
public function getSingleIdReflectionProperty(): ReflectionProperty|null
|
||||
{
|
||||
if ($this->isIdentifierComposite) {
|
||||
@@ -591,6 +614,16 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
return $this->reflFields[$this->identifier[0]];
|
||||
}
|
||||
|
||||
/** @throws BadMethodCallException If the class has a composite identifier. */
|
||||
public function getSingleIdPropertyAccessor(): PropertyAccessor|null
|
||||
{
|
||||
if ($this->isIdentifierComposite) {
|
||||
throw new BadMethodCallException('Class ' . $this->name . ' has a composite identifier.');
|
||||
}
|
||||
|
||||
return $this->propertyAccessors[$this->identifier[0]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the identifier values of an entity of this class.
|
||||
*
|
||||
@@ -605,7 +638,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
$id = [];
|
||||
|
||||
foreach ($this->identifier as $idField) {
|
||||
$value = $this->reflFields[$idField]->getValue($entity);
|
||||
$value = $this->propertyAccessors[$idField]->getValue($entity);
|
||||
|
||||
if ($value !== null) {
|
||||
$id[$idField] = $value;
|
||||
@@ -616,7 +649,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
}
|
||||
|
||||
$id = $this->identifier[0];
|
||||
$value = $this->reflFields[$id]->getValue($entity);
|
||||
$value = $this->propertyAccessors[$id]->getValue($entity);
|
||||
|
||||
if ($value === null) {
|
||||
return [];
|
||||
@@ -628,14 +661,14 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Populates the entity identifier of an entity.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $id
|
||||
* @phpstan-param array<string, mixed> $id
|
||||
*
|
||||
* @todo Rename to assignIdentifier()
|
||||
*/
|
||||
public function setIdentifierValues(object $entity, array $id): void
|
||||
{
|
||||
foreach ($id as $idField => $idValue) {
|
||||
$this->reflFields[$idField]->setValue($entity, $idValue);
|
||||
$this->propertyAccessors[$idField]->setValue($entity, $idValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,7 +677,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
*/
|
||||
public function setFieldValue(object $entity, string $field, mixed $value): void
|
||||
{
|
||||
$this->reflFields[$field]->setValue($entity, $value);
|
||||
$this->propertyAccessors[$field]->setValue($entity, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -652,7 +685,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
*/
|
||||
public function getFieldValue(object $entity, string $field): mixed
|
||||
{
|
||||
return $this->reflFields[$field]->getValue($entity);
|
||||
return $this->propertyAccessors[$field]->getValue($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -786,76 +819,74 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
{
|
||||
// Restore ReflectionClass and properties
|
||||
$this->reflClass = $reflService->getClass($this->name);
|
||||
$this->reflFields = new LegacyReflectionFields($this, $reflService);
|
||||
$this->instantiator = $this->instantiator ?: new Instantiator();
|
||||
|
||||
$parentReflFields = [];
|
||||
$parentAccessors = [];
|
||||
|
||||
foreach ($this->embeddedClasses as $property => $embeddedClass) {
|
||||
if (isset($embeddedClass->declaredField)) {
|
||||
assert($embeddedClass->originalField !== null);
|
||||
$childProperty = $this->getAccessibleProperty(
|
||||
$reflService,
|
||||
$childAccessor = PropertyAccessorFactory::createPropertyAccessor(
|
||||
$this->embeddedClasses[$embeddedClass->declaredField]->class,
|
||||
$embeddedClass->originalField,
|
||||
);
|
||||
assert($childProperty !== null);
|
||||
$parentReflFields[$property] = new ReflectionEmbeddedProperty(
|
||||
$parentReflFields[$embeddedClass->declaredField],
|
||||
$childProperty,
|
||||
|
||||
$parentAccessors[$property] = new EmbeddablePropertyAccessor(
|
||||
$parentAccessors[$embeddedClass->declaredField],
|
||||
$childAccessor,
|
||||
$this->embeddedClasses[$embeddedClass->declaredField]->class,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldRefl = $this->getAccessibleProperty(
|
||||
$reflService,
|
||||
$accessor = PropertyAccessorFactory::createPropertyAccessor(
|
||||
$embeddedClass->declared ?? $this->name,
|
||||
$property,
|
||||
);
|
||||
|
||||
$parentReflFields[$property] = $fieldRefl;
|
||||
$this->reflFields[$property] = $fieldRefl;
|
||||
$parentAccessors[$property] = $accessor;
|
||||
$this->propertyAccessors[$property] = $accessor;
|
||||
}
|
||||
|
||||
foreach ($this->fieldMappings as $field => $mapping) {
|
||||
if (isset($mapping->declaredField) && isset($parentReflFields[$mapping->declaredField])) {
|
||||
if (isset($mapping->declaredField) && isset($parentAccessors[$mapping->declaredField])) {
|
||||
assert($mapping->originalField !== null);
|
||||
assert($mapping->originalClass !== null);
|
||||
$childProperty = $this->getAccessibleProperty($reflService, $mapping->originalClass, $mapping->originalField);
|
||||
assert($childProperty !== null);
|
||||
$accessor = PropertyAccessorFactory::createPropertyAccessor($mapping->originalClass, $mapping->originalField);
|
||||
|
||||
if (isset($mapping->enumType)) {
|
||||
$childProperty = new EnumReflectionProperty(
|
||||
$childProperty,
|
||||
if ($mapping->enumType !== null) {
|
||||
$accessor = new EnumPropertyAccessor(
|
||||
$accessor,
|
||||
$mapping->enumType,
|
||||
);
|
||||
}
|
||||
|
||||
$this->reflFields[$field] = new ReflectionEmbeddedProperty(
|
||||
$parentReflFields[$mapping->declaredField],
|
||||
$childProperty,
|
||||
$this->propertyAccessors[$field] = new EmbeddablePropertyAccessor(
|
||||
$parentAccessors[$mapping->declaredField],
|
||||
$accessor,
|
||||
$mapping->originalClass,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->reflFields[$field] = isset($mapping->declared)
|
||||
? $this->getAccessibleProperty($reflService, $mapping->declared, $field)
|
||||
: $this->getAccessibleProperty($reflService, $this->name, $field);
|
||||
$this->propertyAccessors[$field] = isset($mapping->declared)
|
||||
? PropertyAccessorFactory::createPropertyAccessor($mapping->declared, $field)
|
||||
: PropertyAccessorFactory::createPropertyAccessor($this->name, $field);
|
||||
|
||||
if (isset($mapping->enumType) && $this->reflFields[$field] !== null) {
|
||||
$this->reflFields[$field] = new EnumReflectionProperty(
|
||||
$this->reflFields[$field],
|
||||
if ($mapping->enumType !== null) {
|
||||
$this->propertyAccessors[$field] = new EnumPropertyAccessor(
|
||||
$this->propertyAccessors[$field],
|
||||
$mapping->enumType,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->associationMappings as $field => $mapping) {
|
||||
$this->reflFields[$field] = isset($mapping->declared)
|
||||
? $this->getAccessibleProperty($reflService, $mapping->declared, $field)
|
||||
: $this->getAccessibleProperty($reflService, $this->name, $field);
|
||||
$this->propertyAccessors[$field] = isset($mapping->declared)
|
||||
? PropertyAccessorFactory::createPropertyAccessor($mapping->declared, $field)
|
||||
: PropertyAccessorFactory::createPropertyAccessor($this->name, $field);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -932,17 +963,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Can return null when using static reflection, in violation of the LSP
|
||||
*/
|
||||
public function getReflectionClass(): ReflectionClass|null
|
||||
{
|
||||
return $this->reflClass;
|
||||
}
|
||||
|
||||
/** @psalm-param array{usage?: mixed, region?: mixed} $cache */
|
||||
/** @phpstan-param array{usage?: mixed, region?: mixed} $cache */
|
||||
public function enableCache(array $cache): void
|
||||
{
|
||||
if (! isset($cache['usage'])) {
|
||||
@@ -956,17 +977,17 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/** @psalm-param array{usage?: int, region?: string} $cache */
|
||||
/** @phpstan-param array{usage?: int, region?: string} $cache */
|
||||
public function enableAssociationCache(string $fieldName, array $cache): void
|
||||
{
|
||||
$this->associationMappings[$fieldName]->cache = $this->getAssociationCacheDefaults($fieldName, $cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param array{usage?: int, region?: string|null} $cache
|
||||
* @phpstan-param array{usage?: int, region?: string|null} $cache
|
||||
*
|
||||
* @return int[]|string[]
|
||||
* @psalm-return array{usage: int, region: string|null}
|
||||
* @phpstan-return array{usage: int, region: string|null}
|
||||
*/
|
||||
public function getAssociationCacheDefaults(string $fieldName, array $cache): array
|
||||
{
|
||||
@@ -1083,7 +1104,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Gets all association mappings of the class.
|
||||
*
|
||||
* @psalm-return array<string, AssociationMapping>
|
||||
* @phpstan-return array<string, AssociationMapping>
|
||||
*/
|
||||
public function getAssociationMappings(): array
|
||||
{
|
||||
@@ -1150,7 +1171,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Validates & completes the given field mapping.
|
||||
*
|
||||
* @psalm-param array{
|
||||
* @phpstan-param array{
|
||||
* fieldName?: string,
|
||||
* columnName?: string,
|
||||
* id?: bool,
|
||||
@@ -1250,7 +1271,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* Validates & completes the basic mapping information that is common to all
|
||||
* association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The mapping.
|
||||
* @phpstan-param array<string, mixed> $mapping The mapping.
|
||||
*
|
||||
* @return ConcreteAssociationMapping
|
||||
*
|
||||
@@ -1465,7 +1486,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* Sets the mapped identifier/primary key fields of this class.
|
||||
* Mainly used by the ClassMetadataFactory to assign inherited identifiers.
|
||||
*
|
||||
* @psalm-param list<mixed> $identifier
|
||||
* @phpstan-param list<mixed> $identifier
|
||||
*/
|
||||
public function setIdentifier(array $identifier): void
|
||||
{
|
||||
@@ -1489,10 +1510,10 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Gets an array containing all the column names.
|
||||
*
|
||||
* @psalm-param list<string>|null $fieldNames
|
||||
* @phpstan-param list<string>|null $fieldNames
|
||||
*
|
||||
* @return mixed[]
|
||||
* @psalm-return list<string>
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
public function getColumnNames(array|null $fieldNames = null): array
|
||||
{
|
||||
@@ -1506,7 +1527,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Returns an array with all the identifier column names.
|
||||
*
|
||||
* @psalm-return list<string>
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
public function getIdentifierColumnNames(): array
|
||||
{
|
||||
@@ -1533,7 +1554,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Sets the type of Id generator to use for the mapped class.
|
||||
*
|
||||
* @psalm-param self::GENERATOR_TYPE_* $generatorType
|
||||
* @phpstan-param self::GENERATOR_TYPE_* $generatorType
|
||||
*/
|
||||
public function setIdGeneratorType(int $generatorType): void
|
||||
{
|
||||
@@ -1586,7 +1607,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Checks whether the class uses a sequence for id generation.
|
||||
*
|
||||
* @psalm-assert-if-true !null $this->sequenceGeneratorDefinition
|
||||
* @phpstan-assert-if-true !null $this->sequenceGeneratorDefinition
|
||||
*/
|
||||
public function isIdGeneratorSequence(): bool
|
||||
{
|
||||
@@ -1642,7 +1663,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Sets the mapped subclasses of this class.
|
||||
*
|
||||
* @psalm-param list<string> $subclasses The names of all mapped subclasses.
|
||||
* @phpstan-param list<string> $subclasses The names of all mapped subclasses.
|
||||
*/
|
||||
public function setSubclasses(array $subclasses): void
|
||||
{
|
||||
@@ -1657,7 +1678,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* Assumes that the class names in the passed array are in the order:
|
||||
* directParent -> directParentParent -> directParentParentParent ... -> root.
|
||||
*
|
||||
* @psalm-param list<class-string> $classNames
|
||||
* @phpstan-param list<class-string> $classNames
|
||||
*/
|
||||
public function setParentClasses(array $classNames): void
|
||||
{
|
||||
@@ -1671,7 +1692,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Sets the inheritance type used by the class and its subclasses.
|
||||
*
|
||||
* @psalm-param self::INHERITANCE_TYPE_* $type
|
||||
* @phpstan-param self::INHERITANCE_TYPE_* $type
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
@@ -1687,7 +1708,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Sets the association to override association mapping of property for an entity relationship.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $overrideMapping
|
||||
* @phpstan-param array{joinColumns?: array, inversedBy?: ?string, joinTable?: array, fetch?: ?string, cascade?: string[]} $overrideMapping
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
@@ -1723,6 +1744,10 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
$mapping['fetch'] = $overrideMapping['fetch'];
|
||||
}
|
||||
|
||||
if (isset($overrideMapping['cascade'])) {
|
||||
$mapping['cascade'] = $overrideMapping['cascade'];
|
||||
}
|
||||
|
||||
switch ($mapping['type']) {
|
||||
case self::ONE_TO_ONE:
|
||||
case self::MANY_TO_ONE:
|
||||
@@ -1741,7 +1766,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Sets the override for a mapped field.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $overrideMapping
|
||||
* @phpstan-param array<string, mixed> $overrideMapping
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
@@ -1836,7 +1861,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
*
|
||||
* If a key is omitted, the current value is kept.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $table The table description.
|
||||
* @phpstan-param array<string, mixed> $table The table description.
|
||||
*/
|
||||
public function setPrimaryTable(array $table): void
|
||||
{
|
||||
@@ -1888,7 +1913,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Adds a mapped field to the class.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The field mapping.
|
||||
* @phpstan-param array<string, mixed> $mapping The field mapping.
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
@@ -1956,7 +1981,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Adds a one-to-many mapping.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The mapping.
|
||||
* @phpstan-param array<string, mixed> $mapping The mapping.
|
||||
*/
|
||||
public function mapOneToMany(array $mapping): void
|
||||
{
|
||||
@@ -1970,7 +1995,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Adds a many-to-one mapping.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The mapping.
|
||||
* @phpstan-param array<string, mixed> $mapping The mapping.
|
||||
*/
|
||||
public function mapManyToOne(array $mapping): void
|
||||
{
|
||||
@@ -1984,7 +2009,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Adds a many-to-many mapping.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The mapping.
|
||||
* @phpstan-param array<string, mixed> $mapping The mapping.
|
||||
*/
|
||||
public function mapManyToMany(array $mapping): void
|
||||
{
|
||||
@@ -2015,7 +2040,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* Registers a custom repository class for the entity class.
|
||||
*
|
||||
* @param string|null $repositoryClassName The class name of the custom mapper.
|
||||
* @psalm-param class-string<EntityRepository>|null $repositoryClassName
|
||||
* @phpstan-param class-string<EntityRepository>|null $repositoryClassName
|
||||
*/
|
||||
public function setCustomRepositoryClass(string|null $repositoryClassName): void
|
||||
{
|
||||
@@ -2055,7 +2080,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* Gets the registered lifecycle callbacks for an event.
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
public function getLifecycleCallbacks(string $event): array
|
||||
{
|
||||
@@ -2082,7 +2107,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* Sets the lifecycle callbacks for entities of this class.
|
||||
* Any previously registered callbacks are overwritten.
|
||||
*
|
||||
* @psalm-param array<string, list<string>> $callbacks
|
||||
* @phpstan-param array<string, list<string>> $callbacks
|
||||
*/
|
||||
public function setLifecycleCallbacks(array $callbacks): void
|
||||
{
|
||||
@@ -2128,7 +2153,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* @see getDiscriminatorColumn()
|
||||
*
|
||||
* @param DiscriminatorColumnMapping|mixed[]|null $columnDef
|
||||
* @psalm-param DiscriminatorColumnMapping|array{
|
||||
* @phpstan-param DiscriminatorColumnMapping|array{
|
||||
* name: string|null,
|
||||
* fieldName?: string|null,
|
||||
* type?: string|null,
|
||||
@@ -2186,6 +2211,22 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
*/
|
||||
public function setDiscriminatorMap(array $map): void
|
||||
{
|
||||
if (count(array_flip($map)) !== count($map)) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/3519',
|
||||
<<<'DEPRECATION'
|
||||
Mapping a class to multiple discriminator values is deprecated,
|
||||
and the discriminator mapping of %s contains duplicate values
|
||||
for the following discriminator values: %s.
|
||||
DEPRECATION,
|
||||
$this->name,
|
||||
implode(', ', array_keys(array_filter(array_count_values($map), static function (int $value): bool {
|
||||
return $value > 1;
|
||||
}))),
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($map as $value => $className) {
|
||||
$this->addDiscriminatorMapClass($value, $className);
|
||||
}
|
||||
@@ -2334,7 +2375,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Sets definition.
|
||||
*
|
||||
* @psalm-param array<string, string|null> $definition
|
||||
* @phpstan-param array<string, string|null> $definition
|
||||
*/
|
||||
public function setCustomGeneratorDefinition(array $definition): void
|
||||
{
|
||||
@@ -2354,7 +2395,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* @psalm-param array{sequenceName?: string, allocationSize?: int|string, initialValue?: int|string, quoted?: mixed} $definition
|
||||
* @phpstan-param array{sequenceName?: string, allocationSize?: int|string, initialValue?: int|string, quoted?: mixed} $definition
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
@@ -2387,7 +2428,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* Sets the version field mapping used for versioning. Sets the default
|
||||
* value to use depending on the column type.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The version field mapping array.
|
||||
* @phpstan-param array<string, mixed> $mapping The version field mapping array.
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
@@ -2456,7 +2497,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-return class-string
|
||||
* @phpstan-return class-string
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
@@ -2501,7 +2542,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* @param C $className
|
||||
*
|
||||
* @return string|null null if and only if the input value is null
|
||||
* @psalm-return (C is class-string ? class-string : (C is string ? string : null))
|
||||
* @phpstan-return (C is class-string ? class-string : (C is string ? string : null))
|
||||
*
|
||||
* @template C of string|null
|
||||
*/
|
||||
@@ -2533,7 +2574,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Map Embedded Class
|
||||
*
|
||||
* @psalm-param array{
|
||||
* @phpstan-param array{
|
||||
* fieldName: string,
|
||||
* class?: class-string,
|
||||
* declaredField?: string,
|
||||
@@ -2641,22 +2682,4 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
|
||||
return $sequencePrefix;
|
||||
}
|
||||
|
||||
/** @psalm-param class-string $class */
|
||||
private function getAccessibleProperty(ReflectionService $reflService, string $class, string $field): ReflectionProperty|null
|
||||
{
|
||||
$reflectionProperty = $reflService->getAccessibleProperty($class, $field);
|
||||
if ($reflectionProperty?->isReadOnly()) {
|
||||
$declaringClass = $reflectionProperty->class;
|
||||
if ($declaringClass !== $class) {
|
||||
$reflectionProperty = $reflService->getAccessibleProperty($declaringClass, $field);
|
||||
}
|
||||
|
||||
if ($reflectionProperty !== null) {
|
||||
$reflectionProperty = new ReflectionReadonlyProperty($reflectionProperty);
|
||||
}
|
||||
}
|
||||
|
||||
return $reflectionProperty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ use function strlen;
|
||||
use function strtolower;
|
||||
use function substr;
|
||||
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
/**
|
||||
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
|
||||
* metadata mapping information of a class which describes how a class should be mapped
|
||||
@@ -440,8 +442,8 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
$subClass->addInheritedFieldMapping($subClassMapping);
|
||||
}
|
||||
|
||||
foreach ($parentClass->reflFields as $name => $field) {
|
||||
$subClass->reflFields[$name] = $field;
|
||||
foreach ($parentClass->propertyAccessors as $name => $field) {
|
||||
$subClass->propertyAccessors[$name] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,7 +614,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
}
|
||||
}
|
||||
|
||||
/** @psalm-return ClassMetadata::GENERATOR_TYPE_* */
|
||||
/** @phpstan-return ClassMetadata::GENERATOR_TYPE_* */
|
||||
private function determineIdGeneratorStrategy(AbstractPlatform $platform): int
|
||||
{
|
||||
assert($this->em !== null);
|
||||
@@ -699,6 +701,18 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService): void
|
||||
{
|
||||
$class->wakeupReflection($reflService);
|
||||
|
||||
if (PHP_VERSION_ID < 80400) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($class->propertyAccessors as $propertyAccessor) {
|
||||
$property = $propertyAccessor->getUnderlyingReflector();
|
||||
|
||||
if ($property->isVirtual()) {
|
||||
throw MappingException::mappingVirtualPropertyNotAllowed($class->name, $property->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService): void
|
||||
|
||||
@@ -15,7 +15,7 @@ final class Column implements MappingAttribute
|
||||
* @param int|null $scale The scale for a decimal (exact numeric) column (Applies only for decimal column).
|
||||
* @param class-string<BackedEnum>|null $enumType
|
||||
* @param array<string,mixed> $options
|
||||
* @psalm-param 'NEVER'|'INSERT'|'ALWAYS'|null $generated
|
||||
* @phpstan-param 'NEVER'|'INSERT'|'ALWAYS'|null $generated
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string|null $name = null,
|
||||
|
||||
@@ -24,7 +24,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
public function getColumnName(string $fieldName, ClassMetadata $class, AbstractPlatform $platform): string
|
||||
{
|
||||
return isset($class->fieldMappings[$fieldName]->quoted)
|
||||
? $platform->quoteIdentifier($class->fieldMappings[$fieldName]->columnName)
|
||||
? $platform->quoteSingleIdentifier($class->fieldMappings[$fieldName]->columnName)
|
||||
: $class->fieldMappings[$fieldName]->columnName;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
}
|
||||
|
||||
return isset($class->table['quoted'])
|
||||
? $platform->quoteIdentifier($tableName)
|
||||
? $platform->quoteSingleIdentifier($tableName)
|
||||
: $tableName;
|
||||
}
|
||||
|
||||
@@ -52,14 +52,14 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string
|
||||
{
|
||||
return isset($definition['quoted'])
|
||||
? $platform->quoteIdentifier($definition['sequenceName'])
|
||||
? $platform->quoteSingleIdentifier($definition['sequenceName'])
|
||||
: $definition['sequenceName'];
|
||||
}
|
||||
|
||||
public function getJoinColumnName(JoinColumnMapping $joinColumn, ClassMetadata $class, AbstractPlatform $platform): string
|
||||
{
|
||||
return isset($joinColumn->quoted)
|
||||
? $platform->quoteIdentifier($joinColumn->name)
|
||||
? $platform->quoteSingleIdentifier($joinColumn->name)
|
||||
: $joinColumn->name;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
AbstractPlatform $platform,
|
||||
): string {
|
||||
return isset($joinColumn->quoted)
|
||||
? $platform->quoteIdentifier($joinColumn->referencedColumnName)
|
||||
? $platform->quoteSingleIdentifier($joinColumn->referencedColumnName)
|
||||
: $joinColumn->referencedColumnName;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
$tableName = $association->joinTable->name;
|
||||
|
||||
if (isset($association->joinTable->quoted)) {
|
||||
$tableName = $platform->quoteIdentifier($tableName);
|
||||
$tableName = $platform->quoteSingleIdentifier($tableName);
|
||||
}
|
||||
|
||||
return $schema . $tableName;
|
||||
@@ -113,7 +113,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
$joinColumns = $assoc->joinColumns;
|
||||
$assocQuotedColumnNames = array_map(
|
||||
static fn (JoinColumnMapping $joinColumn) => isset($joinColumn->quoted)
|
||||
? $platform->quoteIdentifier($joinColumn->name)
|
||||
? $platform->quoteSingleIdentifier($joinColumn->name)
|
||||
: $joinColumn->name,
|
||||
$joinColumns,
|
||||
);
|
||||
|
||||
@@ -20,7 +20,7 @@ use function defined;
|
||||
use function enum_exists;
|
||||
use function is_a;
|
||||
|
||||
/** @psalm-type ScalarName = 'array'|'bool'|'float'|'int'|'string' */
|
||||
/** @phpstan-type ScalarName = 'array'|'bool'|'float'|'int'|'string' */
|
||||
final class DefaultTypedFieldMapper implements TypedFieldMapper
|
||||
{
|
||||
/** @var array<class-string|ScalarName, class-string<Type>|string> $typedFieldMappings */
|
||||
@@ -74,8 +74,6 @@ final class DefaultTypedFieldMapper implements TypedFieldMapper
|
||||
assert(is_a($type->getName(), BackedEnum::class, true));
|
||||
$mapping['enumType'] = $type->getName();
|
||||
$type = $reflection->getBackingType();
|
||||
|
||||
assert($type instanceof ReflectionNamedType);
|
||||
}
|
||||
|
||||
if (isset($mapping['type'])) {
|
||||
|
||||
@@ -35,7 +35,7 @@ final class DiscriminatorColumnMapping implements ArrayAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param array{
|
||||
* @phpstan-param array{
|
||||
* type: string,
|
||||
* fieldName: string,
|
||||
* name: string,
|
||||
|
||||
@@ -15,7 +15,6 @@ use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
use InvalidArgumentException;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
@@ -273,8 +272,6 @@ class AttributeDriver implements MappingDriver
|
||||
}
|
||||
|
||||
foreach ($reflectionClass->getProperties() as $property) {
|
||||
assert($property instanceof ReflectionProperty);
|
||||
|
||||
if ($this->isRepeatedPropertyDeclaration($property, $metadata)) {
|
||||
continue;
|
||||
}
|
||||
@@ -285,8 +282,6 @@ class AttributeDriver implements MappingDriver
|
||||
// Evaluate #[Cache] attribute
|
||||
$cacheAttribute = $this->reader->getPropertyAttribute($property, Mapping\Cache::class);
|
||||
if ($cacheAttribute !== null) {
|
||||
assert($cacheAttribute instanceof Mapping\Cache);
|
||||
|
||||
$mapping['cache'] = $metadata->getAssociationCacheDefaults(
|
||||
$mapping['fieldName'],
|
||||
[
|
||||
@@ -560,7 +555,6 @@ class AttributeDriver implements MappingDriver
|
||||
$listenerClass = new ReflectionClass($listenerClassName);
|
||||
|
||||
foreach ($listenerClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
assert($method instanceof ReflectionMethod);
|
||||
// find method callbacks.
|
||||
$callbacks = $this->getMethodCallbacks($method);
|
||||
$hasMapping = $hasMapping ?: ! empty($callbacks);
|
||||
@@ -584,7 +578,6 @@ class AttributeDriver implements MappingDriver
|
||||
}
|
||||
|
||||
foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
assert($method instanceof ReflectionMethod);
|
||||
foreach ($this->getMethodCallbacks($method) as $value) {
|
||||
$metadata->addLifecycleCallback($value[0], $value[1]);
|
||||
}
|
||||
@@ -629,7 +622,7 @@ class AttributeDriver implements MappingDriver
|
||||
* Parses the given method.
|
||||
*
|
||||
* @return list<array{string, string}>
|
||||
* @psalm-return list<array{string, (Events::*)}>
|
||||
* @phpstan-return list<array{string, (Events::*)}>
|
||||
*/
|
||||
private function getMethodCallbacks(ReflectionMethod $method): array
|
||||
{
|
||||
@@ -677,7 +670,7 @@ class AttributeDriver implements MappingDriver
|
||||
* Parse the given JoinColumn as array
|
||||
*
|
||||
* @return mixed[]
|
||||
* @psalm-return array{
|
||||
* @phpstan-return array{
|
||||
* name: string|null,
|
||||
* unique: bool,
|
||||
* nullable: bool,
|
||||
@@ -691,6 +684,7 @@ class AttributeDriver implements MappingDriver
|
||||
{
|
||||
$mapping = [
|
||||
'name' => $joinColumn->name,
|
||||
'deferrable' => $joinColumn->deferrable,
|
||||
'unique' => $joinColumn->unique,
|
||||
'nullable' => $joinColumn->nullable,
|
||||
'onDelete' => $joinColumn->onDelete,
|
||||
@@ -709,7 +703,7 @@ class AttributeDriver implements MappingDriver
|
||||
* Parse the given Column as array
|
||||
*
|
||||
* @return mixed[]
|
||||
* @psalm-return array{
|
||||
* @phpstan-return array{
|
||||
* fieldName: string,
|
||||
* type: mixed,
|
||||
* scale: int,
|
||||
|
||||
@@ -24,7 +24,7 @@ final class AttributeReader
|
||||
private array $isRepeatableAttribute = [];
|
||||
|
||||
/**
|
||||
* @psalm-return class-string-map<T, T|RepeatableAttributeCollection<T>>
|
||||
* @phpstan-return class-string-map<T, T|RepeatableAttributeCollection<T>>
|
||||
*
|
||||
* @template T of MappingAttribute
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user