mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 15:02:22 +01:00
Compare commits
137 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87f1ba74e0 | ||
|
|
ab148d3d9d | ||
|
|
3924c38fab | ||
|
|
9814078a2c | ||
|
|
6de5684fd9 | ||
|
|
c142503a52 | ||
|
|
15537bc218 | ||
|
|
bc95c7c08d | ||
|
|
c1becd54e6 | ||
|
|
e4d7df29c2 | ||
|
|
e38278bfca | ||
|
|
5bff0919a7 | ||
|
|
9ef0f5301b | ||
|
|
4989ca6f15 | ||
|
|
32d1e97ce7 | ||
|
|
ca8147b148 | ||
|
|
c8ebea77f0 | ||
|
|
23f22860f1 | ||
|
|
b24586b1b5 | ||
|
|
fe5ee705db | ||
|
|
0511a9f790 | ||
|
|
0e3d5e8c82 | ||
|
|
72ffb3bfbf | ||
|
|
2e9a1adc23 | ||
|
|
59938cae57 | ||
|
|
298dc9bb6a | ||
|
|
da67f323e0 | ||
|
|
63635cad0e | ||
|
|
a0d401b688 | ||
|
|
6acbadfbbe | ||
|
|
c64dcb4d38 | ||
|
|
3304290b21 | ||
|
|
1865717721 | ||
|
|
828b06e20f | ||
|
|
c2b844d2e3 | ||
|
|
b45d5329f8 | ||
|
|
c1047b30e3 | ||
|
|
aa62efa30a | ||
|
|
f71956f001 | ||
|
|
96f9b29573 | ||
|
|
9a55cf4f30 | ||
|
|
9d680a6de4 | ||
|
|
7a59281157 | ||
|
|
5def068fe9 | ||
|
|
693acbf812 | ||
|
|
c1ce2bb687 | ||
|
|
5f6896a2f9 | ||
|
|
930a790a5a | ||
|
|
3b7de17f2e | ||
|
|
8afaa63d73 | ||
|
|
2ad720b304 | ||
|
|
a939dc2e0d | ||
|
|
f8186b1203 | ||
|
|
048e308241 | ||
|
|
4274dac8a2 | ||
|
|
c1af765960 | ||
|
|
5f3551852f | ||
|
|
8144cad07c | ||
|
|
70fd68cf7f | ||
|
|
437259556c | ||
|
|
e7e2fef56c | ||
|
|
3e34b8e86a | ||
|
|
7d950aba62 | ||
|
|
8fe1200edf | ||
|
|
397358c308 | ||
|
|
ac37a87a3d | ||
|
|
613f52db5a | ||
|
|
cf5503b0d8 | ||
|
|
ae5e9c8c6c | ||
|
|
1a2826d147 | ||
|
|
05760f9454 | ||
|
|
26af013842 | ||
|
|
c322c71cd4 | ||
|
|
3c733a2fee | ||
|
|
5984ad586a | ||
|
|
ee2c3a506b | ||
|
|
04694a9f7b | ||
|
|
5b2060e25f | ||
|
|
39e35fc06c | ||
|
|
7f061c3870 | ||
|
|
74495711fb | ||
|
|
97a7cb8d2f | ||
|
|
e0052390e1 | ||
|
|
8c6419e0e0 | ||
|
|
6f5ce1aca2 | ||
|
|
98e7a53b42 | ||
|
|
3aaaf37dfb | ||
|
|
154a4652ee | ||
|
|
ae7489ff19 | ||
|
|
1ee01f4473 | ||
|
|
8a9ed138a8 | ||
|
|
41ea59ac66 | ||
|
|
e605e6d569 | ||
|
|
d68c1dcd6d | ||
|
|
6307b4fa7d | ||
|
|
5d21bb158b | ||
|
|
71550106d4 | ||
|
|
36011f0d0f | ||
|
|
c97d775370 | ||
|
|
e9f0345a97 | ||
|
|
0feb09d0d6 | ||
|
|
fe5f8bbaa1 | ||
|
|
ecf3cec376 | ||
|
|
0a714db4d9 | ||
|
|
471fda8d0b | ||
|
|
dfe32c2f74 | ||
|
|
c51ba3ce6b | ||
|
|
fe025e8d23 | ||
|
|
ae2957cf7e | ||
|
|
e172b3bf9c | ||
|
|
c9c6e8da2e | ||
|
|
528b8837e1 | ||
|
|
9bf407f336 | ||
|
|
067ad51b3f | ||
|
|
00c77213fb | ||
|
|
c68b8f90b3 | ||
|
|
aa4f9ce9e9 | ||
|
|
d96fc23327 | ||
|
|
4fb044d5f6 | ||
|
|
8ce7b310c5 | ||
|
|
2a953c5e2b | ||
|
|
abc6a40ccb | ||
|
|
73e68f3c7d | ||
|
|
73777d0bd4 | ||
|
|
ec6d1b9f72 | ||
|
|
d809fed52a | ||
|
|
0e4786dfa8 | ||
|
|
c429262f02 | ||
|
|
f4fdcbcdcb | ||
|
|
b0806469d5 | ||
|
|
e89b58a13f | ||
|
|
2b94ec18b9 | ||
|
|
2a662149f4 | ||
|
|
37051d57ce | ||
|
|
4563f2f9a7 | ||
|
|
91201c094a | ||
|
|
a4a15ad243 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -11,7 +11,6 @@ build.properties.dev export-ignore
|
||||
build.xml export-ignore
|
||||
CONTRIBUTING.md export-ignore
|
||||
phpunit.xml.dist export-ignore
|
||||
run-all.sh export-ignore
|
||||
phpcs.xml.dist export-ignore
|
||||
phpbench.json export-ignore
|
||||
phpstan.neon export-ignore
|
||||
|
||||
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@7.2.2"
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@13.0.0"
|
||||
|
||||
20
.github/workflows/composer-lint.yml
vendored
Normal file
20
.github/workflows/composer-lint.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: "Composer Lint"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*.x"
|
||||
paths:
|
||||
- ".github/workflows/composer-lint.yml"
|
||||
- "composer.json"
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
paths:
|
||||
- ".github/workflows/composer-lint.yml"
|
||||
- "composer.json"
|
||||
|
||||
jobs:
|
||||
composer-lint:
|
||||
name: "Composer Lint"
|
||||
uses: "doctrine/.github/.github/workflows/composer-lint.yml@13.0.0"
|
||||
28
.github/workflows/continuous-integration.yml
vendored
28
.github/workflows/continuous-integration.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: "Continuous Integration"
|
||||
name: "CI"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -41,6 +41,7 @@ jobs:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
dbal-version:
|
||||
- "default"
|
||||
extension:
|
||||
@@ -64,7 +65,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -98,7 +99,7 @@ jobs:
|
||||
ORM_PROXY_IMPLEMENTATION: "${{ matrix.proxy }}"
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
uses: "actions/upload-artifact@v5"
|
||||
with:
|
||||
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.proxy }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
@@ -115,6 +116,7 @@ jobs:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3@dev"
|
||||
@@ -147,7 +149,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -172,7 +174,7 @@ jobs:
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/pdo_pgsql.xml --coverage-clover=coverage.xml"
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
uses: "actions/upload-artifact@v5"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.extension }}-coverage"
|
||||
path: "coverage.xml"
|
||||
@@ -189,6 +191,7 @@ jobs:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3@dev"
|
||||
@@ -218,7 +221,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -243,7 +246,7 @@ jobs:
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage.xml"
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
uses: "actions/upload-artifact@v5"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.mariadb-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
|
||||
path: "coverage.xml"
|
||||
@@ -260,6 +263,7 @@ jobs:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3@dev"
|
||||
@@ -289,7 +293,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -321,7 +325,7 @@ jobs:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 1
|
||||
|
||||
- name: "Upload coverage files"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
uses: "actions/upload-artifact@v5"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.mysql-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
@@ -341,7 +345,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -373,12 +377,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Download coverage files"
|
||||
uses: "actions/download-artifact@v4"
|
||||
uses: "actions/download-artifact@v6"
|
||||
with:
|
||||
path: "reports"
|
||||
|
||||
|
||||
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@7.2.2"
|
||||
uses: "doctrine/.github/.github/workflows/documentation.yml@13.0.0"
|
||||
|
||||
2
.github/workflows/phpbench.yml
vendored
2
.github/workflows/phpbench.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@7.2.2"
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@13.0.0"
|
||||
secrets:
|
||||
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
|
||||
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}
|
||||
|
||||
2
.github/workflows/static-analysis.yml
vendored
2
.github/workflows/static-analysis.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v6"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
|
||||
27
README.md
27
README.md
@@ -1,7 +1,7 @@
|
||||
| [4.0.x][4.0] | [3.4.x][3.4] | [3.3.x][3.3] | [2.21.x][2.21] | [2.20.x][2.20] |
|
||||
| [4.0.x][4.0] | [3.6.x][3.6] | [3.5.x][3.5] | [2.21.x][2.21] | [2.20.x][2.20] |
|
||||
|:------------------------------------------------------:|:------------------------------------------------------:|:------------------------------------------------------:|:--------------------------------------------------------:|:--------------------------------------------------------:|
|
||||
| [![Build status][4.0 image]][4.0] | [![Build status][3.4 image]][3.4] | [![Build status][3.3 image]][3.3] | [![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.4 coverage image]][3.4 coverage] | [![Coverage Status][3.3 coverage image]][3.3 coverage] | [![Coverage Status][2.21 coverage image]][2.21 coverage] | [![Coverage Status][2.20 coverage image]][2.20 coverage] |
|
||||
| [![Build status][4.0 image]][4.0 workflow] | [![Build status][3.6 image]][3.6 workflow] | [![Build status][3.5 image]][3.5 workflow] | [![Build status][2.21 image]][2.21 workflow] | [![Build status][2.20 image]][2.20 workflow] |
|
||||
| [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.6 coverage image]][3.6 coverage] | [![Coverage Status][3.5 coverage image]][3.5 coverage] | [![Coverage Status][2.21 coverage image]][2.21 coverage] | [![Coverage Status][2.20 coverage image]][2.20 coverage] |
|
||||
|
||||
[<h1 align="center">🇺🇦 UKRAINE NEEDS YOUR HELP NOW!</h1>](https://www.doctrine-project.org/stop-war.html)
|
||||
|
||||
@@ -20,21 +20,26 @@ without requiring unnecessary code duplication.
|
||||
|
||||
[4.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=4.0.x
|
||||
[4.0]: https://github.com/doctrine/orm/tree/4.0.x
|
||||
[4.0 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A4.0.x
|
||||
[4.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/4.0.x/graph/badge.svg
|
||||
[4.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/4.0.x
|
||||
[3.4 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.4.x
|
||||
[3.4]: https://github.com/doctrine/orm/tree/3.4.x
|
||||
[3.4 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.4.x/graph/badge.svg
|
||||
[3.4 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.4.x
|
||||
[3.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.6 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.6.x
|
||||
[3.6]: https://github.com/doctrine/orm/tree/3.6.x
|
||||
[3.6 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A3.6.x
|
||||
[3.6 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.6.x/graph/badge.svg
|
||||
[3.6 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.6.x
|
||||
[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 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A3.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
|
||||
[2.21 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.21.x
|
||||
[2.21]: https://github.com/doctrine/orm/tree/2.21.x
|
||||
[2.21 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A2.21.x
|
||||
[2.21 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.21.x/graph/badge.svg
|
||||
[2.21 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.21.x
|
||||
[2.20 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.20.x
|
||||
[2.20]: https://github.com/doctrine/orm/tree/2.20.x
|
||||
[2.20 workflow]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml?query=branch%3A2.20.x
|
||||
[2.20 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.20.x/graph/badge.svg
|
||||
[2.20 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.20.x
|
||||
|
||||
@@ -1,29 +1,39 @@
|
||||
{
|
||||
"name": "doctrine/orm",
|
||||
"type": "library",
|
||||
"description": "Object-Relational-Mapper for PHP",
|
||||
"keywords": ["orm", "database"],
|
||||
"homepage": "https://www.doctrine-project.org/projects/orm.html",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
|
||||
{"name": "Roman Borschel", "email": "roman@code-factory.org"},
|
||||
{"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
|
||||
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
|
||||
{"name": "Marco Pivetta", "email": "ocramius@gmail.com"}
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"orm",
|
||||
"database"
|
||||
],
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"phpstan/extension-installer": true
|
||||
"authors": [
|
||||
{
|
||||
"name": "Guilherme Blanco",
|
||||
"email": "guilhermeblanco@gmail.com"
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
{
|
||||
"name": "Roman Borschel",
|
||||
"email": "roman@code-factory.org"
|
||||
},
|
||||
{
|
||||
"name": "Benjamin Eberlei",
|
||||
"email": "kontakt@beberlei.de"
|
||||
},
|
||||
{
|
||||
"name": "Jonathan Wage",
|
||||
"email": "jonwage@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Marco Pivetta",
|
||||
"email": "ocramius@gmail.com"
|
||||
}
|
||||
],
|
||||
"homepage": "https://www.doctrine-project.org/projects/orm.html",
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0",
|
||||
"composer-runtime-api": "^2",
|
||||
"ext-ctype": "*",
|
||||
"composer-runtime-api": "^2",
|
||||
"doctrine/cache": "^1.12.1 || ^2.1.1",
|
||||
"doctrine/collections": "^1.5 || ^2.1",
|
||||
"doctrine/common": "^3.0.3",
|
||||
@@ -35,23 +45,22 @@
|
||||
"doctrine/lexer": "^2 || ^3",
|
||||
"doctrine/persistence": "^2.4 || ^3",
|
||||
"psr/cache": "^1 || ^2 || ^3",
|
||||
"symfony/console": "^4.2 || ^5.0 || ^6.0 || ^7.0",
|
||||
"symfony/console": "^4.2 || ^5.0 || ^6.0 || ^7.0 || ^8.0",
|
||||
"symfony/polyfill-php72": "^1.23",
|
||||
"symfony/polyfill-php80": "^1.16"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/annotations": "^1.13 || ^2",
|
||||
"doctrine/coding-standard": "^9.0.2 || ^13.0",
|
||||
"doctrine/coding-standard": "^9.0.2 || ^14.0",
|
||||
"phpbench/phpbench": "^0.16.10 || ^1.0",
|
||||
"phpstan/extension-installer": "~1.1.0 || ^1.4",
|
||||
"phpstan/phpstan": "~1.4.10 || 2.0.3",
|
||||
"phpstan/phpstan": "~1.4.10 || 2.1.23",
|
||||
"phpstan/phpstan-deprecation-rules": "^1 || ^2",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6",
|
||||
"psr/log": "^1 || ^2 || ^3",
|
||||
"squizlabs/php_codesniffer": "3.12.0",
|
||||
"symfony/cache": "^4.4 || ^5.4 || ^6.4 || ^7.0",
|
||||
"symfony/var-exporter": "^4.4 || ^5.4 || ^6.2 || ^7.0",
|
||||
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0"
|
||||
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/annotations": "<1.13 || >= 3.0"
|
||||
@@ -62,17 +71,29 @@
|
||||
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Doctrine\\ORM\\": "src" }
|
||||
"psr-4": {
|
||||
"Doctrine\\ORM\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Tests\\": "tests/Tests",
|
||||
"Doctrine\\Performance\\": "tests/Performance",
|
||||
"Doctrine\\StaticAnalysis\\": "tests/StaticAnalysis",
|
||||
"Doctrine\\Performance\\": "tests/Performance"
|
||||
"Doctrine\\Tests\\": "tests/Tests"
|
||||
}
|
||||
},
|
||||
"bin": ["bin/doctrine"],
|
||||
"archive": {
|
||||
"exclude": ["!vendor", "tests", "*phpunit.xml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"]
|
||||
"bin": [
|
||||
"bin/doctrine"
|
||||
],
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"phpstan/extension-installer": true
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"scripts": {
|
||||
"docs": "composer --working-dir docs update && ./docs/vendor/bin/build-docs.sh @additional_args"
|
||||
}
|
||||
}
|
||||
|
||||
3
docs/.gitignore
vendored
Normal file
3
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
composer.lock
|
||||
vendor/
|
||||
output/
|
||||
@@ -1,18 +1,18 @@
|
||||
# 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 project root, run
|
||||
|
||||
It will generate the documentation into the build directory of the checkout.
|
||||
composer docs
|
||||
|
||||
This will generate the documentation into the `docs/output` subdirectory.
|
||||
|
||||
## Theme issues
|
||||
To browse the documentation, you need to run a webserver:
|
||||
|
||||
If you get a "Theme error", check if the `en/_theme` subdirectory is empty,
|
||||
in which case you will need to run:
|
||||
cd docs/output
|
||||
php -S localhost:8000
|
||||
|
||||
1. git submodule init
|
||||
2. git submodule update
|
||||
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
|
||||
9
docs/composer.json
Normal file
9
docs/composer.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "doctrine/orm-docs",
|
||||
"description": "Documentation for the Object-Relational Mapper\"",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"require-dev": {
|
||||
"doctrine/docs-builder": "^1.0"
|
||||
}
|
||||
}
|
||||
@@ -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."
|
||||
@@ -101,8 +101,16 @@ The ``Paginate::count(Query $query)`` looks like:
|
||||
{
|
||||
static public function count(Query $query)
|
||||
{
|
||||
/** @var Query $countQuery */
|
||||
$countQuery = clone $query;
|
||||
/*
|
||||
To avoid changing the $query passed into the method and to make sure a possibly existing
|
||||
ResultSetMapping is discarded, we create a new query object any copy relevant data over.
|
||||
*/
|
||||
$countQuery = new Query($query->getEntityManager());
|
||||
$countQuery->setDQL($query->getDQL());
|
||||
$countQuery->setParameters(clone $query->getParameters());
|
||||
foreach ($query->getHints() as $name => $value) {
|
||||
$countQuery->setHint($name, $value);
|
||||
}
|
||||
|
||||
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('DoctrineExtensions\Paginate\CountSqlWalker'));
|
||||
$countQuery->setFirstResult(null)->setMaxResults(null);
|
||||
@@ -111,7 +119,7 @@ The ``Paginate::count(Query $query)`` looks like:
|
||||
}
|
||||
}
|
||||
|
||||
It clones the query, resets the limit clause first and max results
|
||||
This resets the limit clause first and max results
|
||||
and registers the ``CountSqlWalker`` custom tree walker which
|
||||
will modify the AST to execute a count query. The walkers
|
||||
implementation is:
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -13,7 +13,6 @@ If this documentation is not helping to answer questions you have about
|
||||
Doctrine ORM don't panic. You can get help from different sources:
|
||||
|
||||
- There is a :doc:`FAQ <reference/faq>` with answers to frequent questions.
|
||||
- The `Doctrine Mailing List <https://groups.google.com/group/doctrine-user>`_
|
||||
- Slack chat room `#orm <https://www.doctrine-project.org/slack>`_
|
||||
- Report a bug on `GitHub <https://github.com/doctrine/orm/issues>`_.
|
||||
- On `StackOverflow <https://stackoverflow.com/questions/tagged/doctrine-orm>`_
|
||||
@@ -103,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>`
|
||||
|
||||
@@ -123,4 +123,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>`
|
||||
|
||||
@@ -279,7 +279,7 @@ requests.
|
||||
Connection
|
||||
----------
|
||||
|
||||
The ``$connection`` passed as the first argument to he constructor of
|
||||
The ``$connection`` passed as the first argument to the constructor of
|
||||
``EntityManager`` has to be an instance of ``Doctrine\DBAL\Connection``.
|
||||
You can use the factory ``Doctrine\DBAL\DriverManager::getConnection()``
|
||||
to create such a connection. The DBAL configuration is explained in the
|
||||
|
||||
@@ -168,7 +168,7 @@ recommended, at least not as long as an entity instance still holds
|
||||
references to proxy objects or is still managed by an EntityManager.
|
||||
By default, serializing proxy objects does not initialize them. On
|
||||
unserialization, resulting objects are detached from the entity
|
||||
manager and cannot be initialiazed anymore. You can implement the
|
||||
manager and cannot be initialized anymore. You can implement the
|
||||
``__serialize()`` method if you want to change that behavior, but
|
||||
then you need to ensure that you won't generate large serialized
|
||||
object graphs and take care of circular associations.
|
||||
|
||||
@@ -213,12 +213,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
|
||||
@@ -261,10 +264,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>`.
|
||||
|
||||
@@ -678,8 +682,10 @@ Optional parameters:
|
||||
- **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
|
||||
|
||||
@@ -254,6 +254,21 @@ Here is a complete list of ``Column``s attributes (all optional):
|
||||
- ``options``: Key-value pairs of options that get passed
|
||||
to the underlying database platform when generating DDL statements.
|
||||
|
||||
Specifying default values
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
While it is possible to specify default values for properties in your
|
||||
PHP class, Doctrine also allows you to specify default values for
|
||||
database columns using the ``default`` key in the ``options`` array of
|
||||
the ``Column`` attribute.
|
||||
|
||||
.. configuration-block::
|
||||
.. literalinclude:: basic-mapping/DefaultValues.php
|
||||
:language: attribute
|
||||
|
||||
.. literalinclude:: basic-mapping/default-values.xml
|
||||
:language: xml
|
||||
|
||||
.. _reference-php-mapping-types:
|
||||
|
||||
PHP Types Mapping
|
||||
|
||||
15
docs/en/reference/basic-mapping/DefaultValues.php
Normal file
15
docs/en/reference/basic-mapping/DefaultValues.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
|
||||
#[Entity]
|
||||
class Message
|
||||
{
|
||||
#[Column(options: ['default' => 'Hello World!'])]
|
||||
private string $text;
|
||||
}
|
||||
9
docs/en/reference/basic-mapping/default-values.xml
Normal file
9
docs/en/reference/basic-mapping/default-values.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<doctrine-mapping>
|
||||
<entity name="Message">
|
||||
<field name="text">
|
||||
<options>
|
||||
<option name="default">Hello World!</option>
|
||||
</options>
|
||||
</field>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@@ -18,30 +18,6 @@ In your mapping configuration, the column definition (for example, the
|
||||
the ``charset`` and ``collation``. The default values are ``utf8`` and
|
||||
``utf8_unicode_ci``, respectively.
|
||||
|
||||
Entity Classes
|
||||
--------------
|
||||
|
||||
How can I add default values to a column?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Doctrine does not support to set the default values in columns through the "DEFAULT" keyword in SQL.
|
||||
This is not necessary however, you can just use your class properties as default values. These are then used
|
||||
upon insert:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
class User
|
||||
{
|
||||
private const STATUS_DISABLED = 0;
|
||||
private const STATUS_ENABLED = 1;
|
||||
|
||||
private string $algorithm = "sha1";
|
||||
/** @var self::STATUS_* */
|
||||
private int $status = self::STATUS_DISABLED;
|
||||
}
|
||||
|
||||
.
|
||||
|
||||
Mapping
|
||||
-------
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ table alias of the SQL table of the entity.
|
||||
|
||||
For the filter to correctly function, the following rules must be followed. Failure to do so will lead to unexpected results from the query cache.
|
||||
1. Parameters for the query should be set on the filter object by ``SQLFilter#setParameter()`` before the filter is used by the ORM ( i.e. do not set parameters inside ``SQLFilter#addFilterConstraint()`` function ).
|
||||
2. The filter must be deterministic. Don't change the values base on external inputs.
|
||||
2. The filter must be deterministic. Don't change the values based on external inputs.
|
||||
|
||||
The ``SQLFilter#getParameter()`` function takes care of the proper quoting of parameters.
|
||||
|
||||
|
||||
@@ -187,6 +187,14 @@ internally by the ORM currently refers to fields by their name only, without tak
|
||||
class containing the field into consideration. This makes it impossible to keep separate
|
||||
mapping configuration for both fields.
|
||||
|
||||
Apart from that, in the case of having multiple ``private`` fields of the same name within
|
||||
the class hierarchy an entity or mapped superclass, the Collection filtering API cannot determine
|
||||
the right field to look at. Even if only one of these fields is actually mapped, the ``ArrayCollection``
|
||||
will not be able to tell, since it does not have access to any metadata.
|
||||
|
||||
Thus, to avoid problems in this regard, it is best to avoid having multiple ``private`` fields of the
|
||||
same name in class hierarchies containing entity and mapped superclasses.
|
||||
|
||||
Known Issues
|
||||
------------
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ Something like below for an entity region:
|
||||
|
||||
|
||||
If the entity holds a collection that also needs to be cached.
|
||||
An collection region could look something like:
|
||||
A collection region could look something like:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -630,7 +630,7 @@ DELETE / UPDATE queries
|
||||
DQL UPDATE / DELETE statements are ported directly into a database and bypass
|
||||
the second-level cache.
|
||||
Entities that are already cached will NOT be invalidated.
|
||||
However the cached data could be evicted using the cache API or an special query hint.
|
||||
However the cached data could be evicted using the cache API or a special query hint.
|
||||
|
||||
|
||||
Execute the ``UPDATE`` and invalidate ``all cache entries`` using ``Query::HINT_CACHE_EVICT``
|
||||
|
||||
@@ -118,7 +118,7 @@ entity might look like this:
|
||||
}
|
||||
}
|
||||
|
||||
Now the possiblity of mass-assignment exists on this entity and can
|
||||
Now the possibility of mass-assignment exists on this entity and can
|
||||
be exploited by attackers to set the "isAdmin" flag to true on any
|
||||
object when you pass the whole request data to this method like:
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
cookbook/decorator-pattern
|
||||
cookbook/dql-custom-walkers
|
||||
cookbook/dql-user-defined-functions
|
||||
cookbook/generated-columns
|
||||
cookbook/implementing-arrayaccess-for-domain-objects
|
||||
cookbook/implementing-the-notify-changetracking-policy
|
||||
cookbook/resolve-target-entity-listener
|
||||
|
||||
@@ -234,6 +234,7 @@
|
||||
<!-- extending a class from another package -->
|
||||
<exclude-pattern>tests/Tests/Mocks/DatabasePlatformMock.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Tests/Mocks/SchemaManagerMock.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Tests/ORM/AbstractQueryTest.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Tests/ORM/Functional/Ticket/DDC3634Test.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
|
||||
@@ -18,18 +18,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/AbstractQuery.php
|
||||
|
||||
-
|
||||
message: '#^Expression "\$setCacheEntry\(\$data\)" on a separate line does not do anything\.$#'
|
||||
identifier: expr.resultUnused
|
||||
count: 1
|
||||
path: src/AbstractQuery.php
|
||||
|
||||
-
|
||||
message: '#^Expression "\$setCacheEntry\(\$stmt\)" on a separate line does not do anything\.$#'
|
||||
identifier: expr.resultUnused
|
||||
count: 1
|
||||
path: src/AbstractQuery.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\AbstractQuery\:\:getParameter\(\) should return Doctrine\\ORM\\Query\\Parameter\|null but returns Doctrine\\ORM\\Query\\Parameter\|false\|null\.$#'
|
||||
identifier: return.type
|
||||
@@ -102,6 +90,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Cache/CollectionHydrator.php
|
||||
|
||||
-
|
||||
message: '#^If condition is always false\.$#'
|
||||
identifier: if.alwaysFalse
|
||||
count: 1
|
||||
path: src/Cache/DefaultCache.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Cache\\DefaultCache\:\:buildCollectionCacheKey\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -264,12 +258,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Cache/DefaultQueryCache.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$result of class Doctrine\\ORM\\Cache\\QueryCacheEntry constructor expects array\<string, mixed\>, array\<int\|string, array\<string, array\<mixed\>\>\> given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Cache/DefaultQueryCache.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$key of method Doctrine\\ORM\\Cache\\Logging\\CacheLogger\:\:entityCacheHit\(\) expects Doctrine\\ORM\\Cache\\EntityCacheKey, Doctrine\\ORM\\Cache\\CacheKey given\.$#'
|
||||
identifier: argument.type
|
||||
@@ -546,12 +534,6 @@ parameters:
|
||||
count: 5
|
||||
path: src/Cache/Persister/Entity/AbstractEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:loadAll\(\) has no return type specified\.$#'
|
||||
identifier: missingType.return
|
||||
count: 1
|
||||
path: src/Cache/Persister/Entity/AbstractEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Cache\\Persister\\Entity\\AbstractEntityPersister\:\:loadManyToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
@@ -630,6 +612,18 @@ parameters:
|
||||
count: 1
|
||||
path: src/Cache/TimestampQueryCacheValidator.php
|
||||
|
||||
-
|
||||
message: '#^Call to function is_a\(\) with arguments class\-string\<Doctrine\\ORM\\EntityRepository\>, ''Doctrine\\\\ORM\\\\EntityRepository'' and true will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 1
|
||||
path: src/Configuration.php
|
||||
|
||||
-
|
||||
message: '#^Call to function is_a\(\) with arguments class\-string\<Doctrine\\ORM\\EntityRepository\>, ''Doctrine\\\\Persistence\\\\ObjectRepository'' and true will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 1
|
||||
path: src/Configuration.php
|
||||
|
||||
-
|
||||
message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Configuration'' and ''getResultCache'' will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
@@ -852,6 +846,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/EntityManager.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\EntityRepository\:\:findBy\(\) should return list\<T of object\> but returns array\<mixed\>\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/EntityRepository.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\EntityRepository\:\:findOneBy\(\) should return \(T of object\)\|null but returns object\|null\.$#'
|
||||
identifier: return.type
|
||||
@@ -973,13 +973,13 @@ parameters:
|
||||
path: src/Internal/Hydration/AbstractHydrator.php
|
||||
|
||||
-
|
||||
message: '#^Parameter &\$id by\-ref type of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:gatherRowData\(\) expects array\<string, string\>, array\<int\|string, string\> given\.$#'
|
||||
message: '#^Parameter &\$id by\-ref type of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:gatherRowData\(\) expects array\<string, string\>, array\<string\> given\.$#'
|
||||
identifier: parameterByRef.type
|
||||
count: 1
|
||||
path: src/Internal/Hydration/AbstractHydrator.php
|
||||
|
||||
-
|
||||
message: '#^Parameter &\$nonemptyComponents by\-ref type of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:gatherRowData\(\) expects array\<string, bool\>, array\<int\|string, bool\> given\.$#'
|
||||
message: '#^Parameter &\$nonemptyComponents by\-ref type of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:gatherRowData\(\) expects array\<string, bool\>, array\<bool\> given\.$#'
|
||||
identifier: parameterByRef.type
|
||||
count: 1
|
||||
path: src/Internal/Hydration/AbstractHydrator.php
|
||||
@@ -1056,6 +1056,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Internal/HydrationCompleteHandler.php
|
||||
|
||||
-
|
||||
message: '#^Offset int\|null might not exist on array\<int, object\>\.$#'
|
||||
identifier: offsetAccess.notFound
|
||||
count: 1
|
||||
path: src/Internal/StronglyConnectedComponents.php
|
||||
|
||||
-
|
||||
message: '#^Property Doctrine\\ORM\\Internal\\StronglyConnectedComponents\:\:\$representingNodes \(array\<int, object\>\) does not accept array\<int\|string, object\>\.$#'
|
||||
identifier: assign.propertyType
|
||||
@@ -1188,6 +1194,12 @@ parameters:
|
||||
count: 5
|
||||
path: src/Mapping/ClassMetadata.php
|
||||
|
||||
-
|
||||
message: '#^Call to function is_a\(\) with Doctrine\\DBAL\\Platforms\\OraclePlatform and ''Doctrine\\\\DBAL…'' will always evaluate to false\.$#'
|
||||
identifier: function.impossibleType
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadataFactory.php
|
||||
|
||||
-
|
||||
message: '#^If condition is always true\.$#'
|
||||
identifier: if.alwaysTrue
|
||||
@@ -1669,7 +1681,7 @@ parameters:
|
||||
path: src/Mapping/ClassMetadataInfo.php
|
||||
|
||||
-
|
||||
message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\<T of object\>\:\:\$embeddedClasses \(array\<string, array\{class\: class\-string, columnPrefix\: string\|null, declaredField\: string\|null, originalField\: string\|null, inherited\?\: class\-string, declared\?\: class\-string\}\>\) does not accept non\-empty\-array\<int\|string, array\{class\: string, columnPrefix\: mixed, declaredField\: mixed, originalField\: mixed, inherited\?\: class\-string, declared\?\: class\-string\}\>\.$#'
|
||||
message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\<T of object\>\:\:\$embeddedClasses \(array\<string, array\{class\: class\-string, columnPrefix\: string\|null, declaredField\: string\|null, originalField\: string\|null, inherited\?\: class\-string, declared\?\: class\-string\}\>\) does not accept non\-empty\-array\<array\{class\: string, columnPrefix\: mixed, declaredField\: mixed, originalField\: mixed, inherited\?\: class\-string, declared\?\: class\-string\}\>\.$#'
|
||||
identifier: assign.propertyType
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadataInfo.php
|
||||
@@ -1681,19 +1693,7 @@ parameters:
|
||||
path: src/Mapping/ClassMetadataInfo.php
|
||||
|
||||
-
|
||||
message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\<T of object\>\:\:\$namedNativeQueries \(array\<string, array\<string, mixed\>\>\) does not accept array\<int\|string, array\<string, mixed\>\>\.$#'
|
||||
identifier: assign.propertyType
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadataInfo.php
|
||||
|
||||
-
|
||||
message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\<T of object\>\:\:\$namedQueries \(array\<string, array\<string, mixed\>\>\) does not accept array\<int\|string, array\<string, mixed\>\>\.$#'
|
||||
identifier: assign.propertyType
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadataInfo.php
|
||||
|
||||
-
|
||||
message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\<T of object\>\:\:\$sqlResultSetMappings \(array\<string, array\{name\: string, entities\: array\<mixed\>, columns\: array\<mixed\>\}\>\) does not accept non\-empty\-array\<int\|string, array\<string, mixed\>\>\.$#'
|
||||
message: '#^Property Doctrine\\ORM\\Mapping\\ClassMetadataInfo\<T of object\>\:\:\$sqlResultSetMappings \(array\<string, array\{name\: string, entities\: array\<mixed\>, columns\: array\<mixed\>\}\>\) does not accept non\-empty\-array\<non\-empty\-array\<string, mixed\>\>\.$#'
|
||||
identifier: assign.propertyType
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadataInfo.php
|
||||
@@ -1722,6 +1722,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadataInfo.php
|
||||
|
||||
-
|
||||
message: '#^Template type T is declared as covariant, but occurs in invariant position in return type of method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\:\:getReflectionClass\(\)\.$#'
|
||||
identifier: generics.variance
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadataInfo.php
|
||||
|
||||
-
|
||||
message: '#^@readonly property cannot have a default value\.$#'
|
||||
identifier: property.readOnlyByPhpDocDefaultValue
|
||||
@@ -2742,18 +2748,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:expandCriteriaParameters\(\) should return array\{list\<mixed\>, list\<int\|string\|null\>\} but returns array\{array\<mixed\>, list\<int\|string\|null\>\}\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:expandParameters\(\) should return array\{list\<mixed\>, list\<int\|string\|null\>\} but returns array\{array\<mixed\>, list\<int\|string\|null\>\}\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:expandToManyParameters\(\) return type has no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
@@ -2790,12 +2784,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getIndividualValue\(\) should return list\<mixed\> but returns array\<mixed\>\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getManyToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
@@ -2856,24 +2844,12 @@ parameters:
|
||||
count: 5
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:getTypes\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:load\(\) has parameter \$assoc with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 5
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:loadAll\(\) has no return type specified\.$#'
|
||||
identifier: missingType.return
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\BasicEntityPersister\:\:loadCollectionFromStatement\(\) has parameter \$coll with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -2922,18 +2898,6 @@ parameters:
|
||||
count: 8
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Offset ''relationToTargetKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\<string\>, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#'
|
||||
identifier: offsetAccess.notFound
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Offset ''sourceToTargetKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\<string\>, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#'
|
||||
identifier: offsetAccess.notFound
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Offset ''targetToSourceKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\<string\>, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#'
|
||||
identifier: offsetAccess.notFound
|
||||
@@ -2988,12 +2952,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Strict comparison using \=\=\= between string and null will always evaluate to false\.$#'
|
||||
identifier: identical.alwaysFalse
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\CachedPersisterContext\:\:__construct\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -3048,12 +3006,6 @@ parameters:
|
||||
count: 5
|
||||
path: src/Persisters/Entity/EntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:loadAll\(\) has no return type specified\.$#'
|
||||
identifier: missingType.return
|
||||
count: 1
|
||||
path: src/Persisters/Entity/EntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Entity\\EntityPersister\:\:loadManyToManyCollection\(\) has parameter \$assoc with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
@@ -3195,33 +3147,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '''
|
||||
#^Call to method __construct\(\) of deprecated class Doctrine\\Common\\Proxy\\AbstractProxyFactory\:
|
||||
The AbstractProxyFactory class is deprecated since doctrine/common 3\.5\.$#
|
||||
'''
|
||||
identifier: staticMethod.deprecatedClass
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '''
|
||||
#^Call to method generateProxyClasses\(\) of deprecated class Doctrine\\Common\\Proxy\\AbstractProxyFactory\:
|
||||
The AbstractProxyFactory class is deprecated since doctrine/common 3\.5\.$#
|
||||
'''
|
||||
identifier: staticMethod.deprecatedClass
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '''
|
||||
#^Call to method getProxy\(\) of deprecated class Doctrine\\Common\\Proxy\\AbstractProxyFactory\:
|
||||
The AbstractProxyFactory class is deprecated since doctrine/common 3\.5\.$#
|
||||
'''
|
||||
identifier: staticMethod.deprecatedClass
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '''
|
||||
#^Class Doctrine\\ORM\\Proxy\\ProxyFactory extends deprecated class Doctrine\\Common\\Proxy\\AbstractProxyFactory\:
|
||||
@@ -3243,15 +3168,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '''
|
||||
#^Instantiation of deprecated class Doctrine\\Common\\Proxy\\ProxyGenerator\:
|
||||
The ProxyGenerator class is deprecated since doctrine/common 3\.5\.$#
|
||||
'''
|
||||
identifier: new.deprecated
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createCloner\(\) has Doctrine\\ORM\\EntityNotFoundException in PHPDoc @throws tag but it''s not thrown\.$#'
|
||||
identifier: throws.unusedType
|
||||
@@ -3684,7 +3600,7 @@ parameters:
|
||||
-
|
||||
message: '#^Property Doctrine\\ORM\\Query\\Filter\\SQLFilter\:\:\$parameters \(array\<string, array\{type\: string, value\: mixed, is_list\: bool\}\>\) does not accept non\-empty\-array\<string, array\{value\: mixed, type\: int\|string, is_list\: bool\}\>\.$#'
|
||||
identifier: assign.propertyType
|
||||
count: 1
|
||||
count: 2
|
||||
path: src/Query/Filter/SQLFilter.php
|
||||
|
||||
-
|
||||
@@ -3987,12 +3903,6 @@ parameters:
|
||||
count: 5
|
||||
path: src/Query/SqlWalker.php
|
||||
|
||||
-
|
||||
message: '#^Property Doctrine\\ORM\\Query\\SqlWalker\:\:\$selectedClasses \(array\<string, array\{class\: Doctrine\\ORM\\Mapping\\ClassMetadata, dqlAlias\: string, resultAlias\: string\|null\}\>\) does not accept non\-empty\-array\<int\|string, array\{class\: Doctrine\\ORM\\Mapping\\ClassMetadata, dqlAlias\: mixed, resultAlias\: string\|null\}\>\.$#'
|
||||
identifier: assign.propertyType
|
||||
count: 1
|
||||
path: src/Query/SqlWalker.php
|
||||
|
||||
-
|
||||
message: '#^Property Doctrine\\ORM\\Query\\SqlWalker\:\:\$selectedClasses with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -4269,12 +4179,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/ConvertMappingCommand.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$destPath of method Doctrine\\ORM\\Tools\\Console\\Command\\ConvertMappingCommand\:\:getExporter\(\) expects string, string\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/ConvertMappingCommand.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$name\.$#'
|
||||
identifier: property.notFound
|
||||
@@ -4299,12 +4203,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateEntitiesCommand.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$outputDirectory of method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generate\(\) expects string, string\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateEntitiesCommand.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$name\.$#'
|
||||
identifier: property.notFound
|
||||
@@ -4323,12 +4221,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateProxiesCommand.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$proxyDir of method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateProxyClasses\(\) expects string\|null, string\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateProxiesCommand.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$customRepositoryClassName\.$#'
|
||||
identifier: property.notFound
|
||||
@@ -4347,12 +4239,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateRepositoriesCommand.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$outputDirectory of method Doctrine\\ORM\\Tools\\EntityRepositoryGenerator\:\:writeEntityRepositoryClass\(\) expects string, string\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateRepositoriesCommand.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Tools\\Console\\Command\\MappingDescribeCommand\:\:formatMappings\(\) has parameter \$propertyMappings with no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
@@ -5067,6 +4953,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Tools/Pagination/LimitSubqueryOutputWalker.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Tools\\Pagination\\Paginator\:\:setUseOutputWalkers\(\) should return static\(Doctrine\\ORM\\Tools\\Pagination\\Paginator\<T\>\) but returns \$this\(Doctrine\\ORM\\Tools\\Pagination\\Paginator\<T\>\)\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Tools/Pagination/Paginator.php
|
||||
|
||||
-
|
||||
message: '#^PHPDoc tag @var for variable \$parameters contains generic interface Doctrine\\Common\\Collections\\Collection but does not specify its types\: TKey, T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -5091,12 +4983,6 @@ parameters:
|
||||
count: 5
|
||||
path: src/Tools/ResolveTargetEntityListener.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$className of method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory\<Doctrine\\ORM\\Mapping\\ClassMetadata\>\:\:setMetadataFor\(\) expects class\-string, \(int\|string\) given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/ResolveTargetEntityListener.php
|
||||
|
||||
-
|
||||
message: '#^Call to function is_numeric\(\) with int\<0, max\> will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
@@ -5589,8 +5475,26 @@ parameters:
|
||||
count: 1
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Utility\\PersisterHelper\:\:inferParameterTypes\(\) has parameter \$class with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
-
|
||||
message: '#^Offset ''joinTable'' might not exist on array\{cache\?\: array, cascade\: array\<string\>, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#'
|
||||
identifier: offsetAccess.notFound
|
||||
count: 1
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
-
|
||||
message: '#^Offset ''relationToTargetKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\<string\>, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#'
|
||||
identifier: offsetAccess.notFound
|
||||
count: 1
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
-
|
||||
message: '#^Offset ''sourceToTargetKeyColumns'' might not exist on array\{cache\?\: array, cascade\: array\<string\>, declared\?\: class\-string, fetch\: mixed, fieldName\: string, id\?\: bool, inherited\?\: class\-string, indexBy\?\: string, \.\.\.\}\.$#'
|
||||
identifier: offsetAccess.notFound
|
||||
count: 1
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
@@ -57,6 +57,17 @@ parameters:
|
||||
count: 2
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#introspectSchema#'
|
||||
count: 2
|
||||
identifier: 'method.notFound'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '#getMaxIdentifierLength#'
|
||||
identifier: 'method.deprecatedClass'
|
||||
path: src/Mapping/ClassMetadataFactory.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$start of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getSubstringExpression\\(\\) expects int, string given\\.$#"
|
||||
count: 1
|
||||
|
||||
@@ -11,7 +11,7 @@ parameters:
|
||||
|
||||
# Fallback logic for DBAL 2
|
||||
-
|
||||
message: '/Application::add\(\) expects Symfony\\Component\\Console\\Command\\Command/'
|
||||
message: '/Parameter #2 \$command of static method Doctrine\\ORM\\Tools\\Console\\ConsoleRunner\:\:addCommandToApplication\(\) expects Symfony\\Component\\Console\\Command\\Command/'
|
||||
path: src/Tools/Console/ConsoleRunner.php
|
||||
|
||||
- '/^Class Doctrine\\DBAL\\Platforms\\SQLAnywherePlatform not found\.$/'
|
||||
@@ -100,3 +100,19 @@ parameters:
|
||||
-
|
||||
message: '#^Parameter \#1 \$classNames of method Doctrine\\ORM\\Mapping\\ClassMetadataInfo\<object\>\:\:setParentClasses\(\) expects list\<class\-string\>, array\<string\> given\.$#'
|
||||
path: src/Mapping/ClassMetadataFactory.php
|
||||
|
||||
-
|
||||
message: '#loadMappingFile#'
|
||||
identifier: 'return.type'
|
||||
path: src/Mapping/Driver/XmlDriver.php
|
||||
|
||||
-
|
||||
message: '#injectObjectManager#'
|
||||
identifier: 'method.deprecatedInterface'
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '#^Static method Doctrine\\Common\\Collections\\Criteria\:\:create\(\) invoked with 1 parameter, 0 required\.$#'
|
||||
identifier: arguments.count
|
||||
count: 3
|
||||
path: src/Persisters/Collection/OneToManyPersister.php
|
||||
|
||||
@@ -9,7 +9,7 @@ parameters:
|
||||
|
||||
# Fallback logic for DBAL 2
|
||||
-
|
||||
message: '/Application::add\(\) expects Symfony\\Component\\Console\\Command\\Command/'
|
||||
message: '/Parameter #2 \$command of static method Doctrine\\ORM\\Tools\\Console\\ConsoleRunner\:\:addCommandToApplication\(\) expects Symfony\\Component\\Console\\Command\\Command/'
|
||||
path: src/Tools/Console/ConsoleRunner.php
|
||||
|
||||
- '/^Class Doctrine\\DBAL\\Platforms\\SQLAnywherePlatform not found\.$/'
|
||||
|
||||
21
run-all.sh
21
run-all.sh
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is a small convenience wrapper for running the doctrine testsuite against a large bunch of databases.
|
||||
# Just create the phpunit.xmls as described in the array below and configure the specific files <php /> section
|
||||
# to connect to that database. Just omit a file if you don't have that database and the tests will be skipped.
|
||||
|
||||
configs[1]="mysql.phpunit.xml"
|
||||
configs[2]='postgres.phpunit.xml'
|
||||
configs[3]='sqlite.phpunit.xml'
|
||||
configs[4]='oracle.phpunit.xml'
|
||||
configs[5]='db2.phpunit.xml'
|
||||
configs[6]='pdo-ibm.phpunit.xml'
|
||||
configs[7]='sqlsrv.phpunit.xml'
|
||||
|
||||
for i in "${configs[@]}"; do
|
||||
if [ -f "$i" ];
|
||||
then
|
||||
echo "RUNNING TESTS WITH CONFIG $i"
|
||||
phpunit -c "$i" "$@"
|
||||
fi;
|
||||
done
|
||||
@@ -21,7 +21,6 @@ use Doctrine\ORM\Internal\Hydration\IterableResult;
|
||||
use Doctrine\ORM\Mapping\MappingException as ORMMappingException;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Query\Parameter;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\Persistence\Mapping\MappingException;
|
||||
use LogicException;
|
||||
@@ -1144,10 +1143,6 @@ abstract class AbstractQuery
|
||||
throw new LogicException('Uninitialized result set mapping.');
|
||||
}
|
||||
|
||||
if ($rsm->isMixed && count($rsm->scalarMappings) > 0) {
|
||||
throw QueryException::iterateWithMixedResultNotAllowed();
|
||||
}
|
||||
|
||||
$stmt = $this->_doExecute();
|
||||
|
||||
return $this->_em->newHydrator($this->_hydrationMode)->toIterable($stmt, $rsm, $this->_hints);
|
||||
|
||||
@@ -59,7 +59,10 @@ class DefaultCollectionHydrator implements CollectionHydrator
|
||||
$targetRegion = $targetPersister->getCacheRegion();
|
||||
$list = [];
|
||||
|
||||
/** @var EntityCacheEntry[]|null $entityEntries */
|
||||
/**
|
||||
* @var EntityCacheEntry[]|null $entityEntries
|
||||
* @phpstan-ignore method.deprecatedInterface
|
||||
*/
|
||||
$entityEntries = $targetRegion->getMultiple($entry);
|
||||
|
||||
if ($entityEntries === null) {
|
||||
|
||||
@@ -103,7 +103,8 @@ class DefaultQueryCache implements QueryCache
|
||||
};
|
||||
|
||||
$cacheKeys = new CollectionCacheEntry(array_map($generateKeys, $cacheEntry->result));
|
||||
$entries = $region->getMultiple($cacheKeys) ?? [];
|
||||
/** @phpstan-ignore method.deprecatedInterface */
|
||||
$entries = $region->getMultiple($cacheKeys) ?? [];
|
||||
|
||||
// @TODO - move to cache hydration component
|
||||
foreach ($cacheEntry->result as $index => $entry) {
|
||||
@@ -167,8 +168,9 @@ class DefaultQueryCache implements QueryCache
|
||||
return new EntityCacheKey($assocMetadata->rootEntityName, $id);
|
||||
};
|
||||
|
||||
$collection = new PersistentCollection($this->em, $assocMetadata, new ArrayCollection());
|
||||
$assocKeys = new CollectionCacheEntry(array_map($generateKeys, $assoc['list']));
|
||||
$collection = new PersistentCollection($this->em, $assocMetadata, new ArrayCollection());
|
||||
$assocKeys = new CollectionCacheEntry(array_map($generateKeys, $assoc['list']));
|
||||
/** @phpstan-ignore method.deprecatedInterface */
|
||||
$assocEntries = $assocRegion->getMultiple($assocKeys);
|
||||
|
||||
foreach ($assoc['list'] as $assocIndex => $assocId) {
|
||||
|
||||
@@ -156,6 +156,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @phpstan-ignore method.deprecatedInterface */
|
||||
return $this->region->getMultiple($collection);
|
||||
}
|
||||
|
||||
|
||||
@@ -1109,10 +1109,11 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
|
||||
public function setLazyGhostObjectEnabled(bool $flag): void
|
||||
{
|
||||
// @phpstan-ignore classConstant.deprecatedTrait (Because we support Symfony < 7.3)
|
||||
if ($flag && ! trait_exists(LazyGhostTrait::class)) {
|
||||
throw new LogicException(
|
||||
'Lazy ghost objects cannot be enabled because the "symfony/var-exporter" library'
|
||||
. ' version 6.2 or higher is not installed. Please run "composer require symfony/var-exporter:^6.2".'
|
||||
. ' version 6.2 or 7 is not installed. Please run "composer require symfony/var-exporter:^6.4".'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class PreUpdateEventArgs extends LifecycleEventArgs
|
||||
*/
|
||||
public function __construct($entity, EntityManagerInterface $em, array &$changeSet)
|
||||
{
|
||||
// @phpstan-ignore staticMethod.deprecatedClass
|
||||
// @phpstan-ignore method.deprecatedClass
|
||||
parent::__construct($entity, $em);
|
||||
|
||||
$this->entityChangeSet = &$changeSet;
|
||||
|
||||
@@ -25,10 +25,12 @@ use TypeError;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function count;
|
||||
use function current;
|
||||
use function end;
|
||||
use function get_debug_type;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
@@ -201,8 +203,10 @@ abstract class AbstractHydrator
|
||||
} else {
|
||||
yield from $result;
|
||||
}
|
||||
} else {
|
||||
} elseif (is_object(current($result))) {
|
||||
yield $result;
|
||||
} else {
|
||||
yield array_merge(...$result);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -722,7 +722,7 @@ DEPRECATION
|
||||
'Mapping for %s: the "UUID" id generator strategy is deprecated with no replacement',
|
||||
$class->name
|
||||
);
|
||||
// @phpstan-ignore new.deprecated
|
||||
// @phpstan-ignore method.deprecatedClass, new.deprecatedClass
|
||||
$class->setIdGenerator(new UuidGenerator());
|
||||
break;
|
||||
|
||||
|
||||
@@ -32,13 +32,8 @@ trait ReflectionBasedDriver
|
||||
|| $metadata->isInheritedEmbeddedClass($property->name);
|
||||
}
|
||||
|
||||
/** @var class-string $declaringClass */
|
||||
$declaringClass = $property->class;
|
||||
|
||||
if ($this->isTransient($declaringClass)) {
|
||||
return isset($metadata->fieldMappings[$property->name]);
|
||||
}
|
||||
|
||||
if (
|
||||
isset($metadata->fieldMappings[$property->name]['declared'])
|
||||
&& $metadata->fieldMappings[$property->name]['declared'] === $declaringClass
|
||||
|
||||
@@ -10,8 +10,6 @@ use Doctrine\Persistence\Mapping\Driver\SymfonyFileLocator;
|
||||
* YamlDriver that additionally looks for mapping information in a global file.
|
||||
*
|
||||
* @deprecated This class is being removed from the ORM and won't have any replacement
|
||||
*
|
||||
* @phpstan-ignore class.extendsDeprecatedClass
|
||||
*/
|
||||
class SimplifiedYamlDriver extends YamlDriver
|
||||
{
|
||||
|
||||
@@ -206,7 +206,6 @@ class XmlDriver extends FileDriver
|
||||
];
|
||||
|
||||
if (isset($discrColumn['options'])) {
|
||||
assert($discrColumn['options'] instanceof SimpleXMLElement);
|
||||
$columnDef['options'] = $this->parseOptions($discrColumn['options']->children());
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Utility\PersisterHelper;
|
||||
|
||||
use function array_fill;
|
||||
use function array_merge;
|
||||
use function array_pop;
|
||||
use function count;
|
||||
use function get_class;
|
||||
@@ -256,10 +257,17 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
|
||||
if ($value === null && ($operator === Comparison::EQ || $operator === Comparison::NEQ)) {
|
||||
$whereClauses[] = sprintf('te.%s %s NULL', $field, $operator === Comparison::EQ ? 'IS' : 'IS NOT');
|
||||
} elseif ($operator === Comparison::IN || $operator === Comparison::NIN) {
|
||||
$whereClauses[] = sprintf('te.%s %s (%s)', $field, $operator === Comparison::IN ? 'IN' : 'NOT IN', implode(', ', array_fill(0, count($value), '?')));
|
||||
foreach ($value as $item) {
|
||||
$params = array_merge($params, PersisterHelper::convertToParameterValue($item, $this->em));
|
||||
$paramTypes = array_merge($paramTypes, PersisterHelper::inferParameterTypes($name, $item, $targetClass, $this->em));
|
||||
}
|
||||
} else {
|
||||
$whereClauses[] = sprintf('te.%s %s ?', $field, $operator);
|
||||
$params[] = $value;
|
||||
$paramTypes[] = PersisterHelper::getTypeOfField($name, $targetClass, $this->em)[0];
|
||||
|
||||
$params = array_merge($params, PersisterHelper::convertToParameterValue($value, $this->em));
|
||||
$paramTypes = array_merge($paramTypes, PersisterHelper::inferParameterTypes($name, $value, $targetClass, $this->em));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ class OneToManyPersister extends AbstractCollectionPersister
|
||||
// only works with single id identifier entities. Will throw an
|
||||
// exception in Entity Persisters if that is not the case for the
|
||||
// 'mappedBy' field.
|
||||
$criteria = new Criteria(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner()));
|
||||
$criteria = Criteria::create(true)->where(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner()));
|
||||
|
||||
return $persister->count($criteria);
|
||||
}
|
||||
@@ -135,7 +135,7 @@ class OneToManyPersister extends AbstractCollectionPersister
|
||||
// only works with single id identifier entities. Will throw an
|
||||
// exception in Entity Persisters if that is not the case for the
|
||||
// 'mappedBy' field.
|
||||
$criteria = new Criteria();
|
||||
$criteria = Criteria::create(true);
|
||||
|
||||
$criteria->andWhere(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner()));
|
||||
$criteria->andWhere(Criteria::expr()->eq($mapping['indexBy'], $key));
|
||||
@@ -158,7 +158,7 @@ class OneToManyPersister extends AbstractCollectionPersister
|
||||
// only works with single id identifier entities. Will throw an
|
||||
// exception in Entity Persisters if that is not the case for the
|
||||
// 'mappedBy' field.
|
||||
$criteria = new Criteria(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner()));
|
||||
$criteria = Criteria::create(true)->where(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner()));
|
||||
|
||||
return $persister->exists($element, $criteria);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Persisters\Entity;
|
||||
|
||||
use BackedEnum;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Expr\Comparison;
|
||||
use Doctrine\DBAL\Connection;
|
||||
@@ -26,9 +25,7 @@ use Doctrine\ORM\Persisters\Exception\InvalidOrientation;
|
||||
use Doctrine\ORM\Persisters\Exception\UnrecognizedField;
|
||||
use Doctrine\ORM\Persisters\SqlExpressionVisitor;
|
||||
use Doctrine\ORM\Persisters\SqlValueVisitor;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\ORM\Repository\Exception\InvalidFindByCall;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Doctrine\ORM\Utility\IdentifierFlattener;
|
||||
@@ -37,17 +34,18 @@ use Doctrine\ORM\Utility\PersisterHelper;
|
||||
use LengthException;
|
||||
|
||||
use function array_combine;
|
||||
use function array_diff_key;
|
||||
use function array_fill;
|
||||
use function array_flip;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function array_search;
|
||||
use function array_unique;
|
||||
use function array_values;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function reset;
|
||||
use function spl_object_id;
|
||||
use function sprintf;
|
||||
@@ -168,14 +166,6 @@ class BasicEntityPersister implements EntityPersister
|
||||
*/
|
||||
protected $quotedColumns = [];
|
||||
|
||||
/**
|
||||
* The INSERT SQL statement used for entities handled by this persister.
|
||||
* This SQL is only generated once per request, if at all.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $insertSql;
|
||||
|
||||
/**
|
||||
* The quote strategy.
|
||||
*
|
||||
@@ -226,6 +216,16 @@ class BasicEntityPersister implements EntityPersister
|
||||
);
|
||||
}
|
||||
|
||||
final protected function isFilterHashUpToDate(): bool
|
||||
{
|
||||
return $this->filterHash === $this->em->getFilters()->getHash();
|
||||
}
|
||||
|
||||
final protected function updateFilterHash(): void
|
||||
{
|
||||
$this->filterHash = $this->em->getFilters()->getHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@@ -300,8 +300,8 @@ class BasicEntityPersister implements EntityPersister
|
||||
$this->assignDefaultVersionAndUpsertableValues($entity, $id);
|
||||
}
|
||||
|
||||
// Unset this queued insert, so that the prepareUpdateData() method knows right away
|
||||
// (for the next entity already) that the current entity has been written to the database
|
||||
// Unset this queued insert, so that the prepareUpdateData() method (called via prepareInsertData() method)
|
||||
// knows right away (for the next entity already) that the current entity has been written to the database
|
||||
// and no extra updates need to be scheduled to refer to it.
|
||||
//
|
||||
// In \Doctrine\ORM\UnitOfWork::executeInserts(), the UoW already removed entities
|
||||
@@ -392,7 +392,7 @@ class BasicEntityPersister implements EntityPersister
|
||||
$types = [];
|
||||
|
||||
foreach ($id as $field => $value) {
|
||||
$types = array_merge($types, $this->getTypes($field, $value, $versionedClass));
|
||||
$types = array_merge($types, PersisterHelper::inferParameterTypes($field, $value, $versionedClass, $this->em));
|
||||
}
|
||||
|
||||
return $types;
|
||||
@@ -953,8 +953,31 @@ class BasicEntityPersister implements EntityPersister
|
||||
continue;
|
||||
}
|
||||
|
||||
$sqlParams = array_merge($sqlParams, $this->getValues($value));
|
||||
$sqlTypes = array_merge($sqlTypes, $this->getTypes($field, $value, $this->class));
|
||||
if ($operator === Comparison::IN || $operator === Comparison::NIN) {
|
||||
if (! is_array($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
|
||||
foreach ($value as $item) {
|
||||
if ($item === null) {
|
||||
/*
|
||||
* Compare this to how \Doctrine\ORM\Persisters\Entity\BasicEntityPersister::getSelectConditionStatementSQL
|
||||
* creates the "[NOT] IN (...)" expression - for NULL values, it does _not_ insert a placeholder in the
|
||||
* SQL and instead adds an extra ... OR ... IS NULL condition. So we need to skip NULL values here as
|
||||
* well to create a parameters list that matches the SQL.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
$sqlParams = array_merge($sqlParams, PersisterHelper::convertToParameterValue($item, $this->em));
|
||||
$sqlTypes = array_merge($sqlTypes, PersisterHelper::inferParameterTypes($field, $item, $this->class, $this->em));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$sqlParams = array_merge($sqlParams, PersisterHelper::convertToParameterValue($value, $this->em));
|
||||
$sqlTypes = array_merge($sqlTypes, PersisterHelper::inferParameterTypes($field, $value, $this->class, $this->em));
|
||||
}
|
||||
|
||||
return [$sqlParams, $sqlTypes];
|
||||
@@ -1274,7 +1297,7 @@ class BasicEntityPersister implements EntityPersister
|
||||
*/
|
||||
protected function getSelectColumnsSQL()
|
||||
{
|
||||
if ($this->currentPersisterContext->selectColumnListSql !== null && $this->filterHash === $this->em->getFilters()->getHash()) {
|
||||
if ($this->currentPersisterContext->selectColumnListSql !== null && $this->isFilterHashUpToDate()) {
|
||||
return $this->currentPersisterContext->selectColumnListSql;
|
||||
}
|
||||
|
||||
@@ -1387,7 +1410,7 @@ class BasicEntityPersister implements EntityPersister
|
||||
}
|
||||
|
||||
$this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);
|
||||
$this->filterHash = $this->em->getFilters()->getHash();
|
||||
$this->updateFilterHash();
|
||||
|
||||
return $this->currentPersisterContext->selectColumnListSql;
|
||||
}
|
||||
@@ -1463,22 +1486,17 @@ class BasicEntityPersister implements EntityPersister
|
||||
*/
|
||||
public function getInsertSQL()
|
||||
{
|
||||
if ($this->insertSql !== null) {
|
||||
return $this->insertSql;
|
||||
}
|
||||
|
||||
$columns = $this->getInsertColumnList();
|
||||
$tableName = $this->quoteStrategy->getTableName($this->class, $this->platform);
|
||||
|
||||
if (empty($columns)) {
|
||||
$identityColumn = $this->quoteStrategy->getColumnName($this->class->identifier[0], $this->class, $this->platform);
|
||||
$this->insertSql = $this->platform->getEmptyIdentityInsertSQL($tableName, $identityColumn);
|
||||
if ($columns === []) {
|
||||
$identityColumn = $this->quoteStrategy->getColumnName($this->class->identifier[0], $this->class, $this->platform);
|
||||
|
||||
return $this->insertSql;
|
||||
return $this->platform->getEmptyIdentityInsertSQL($tableName, $identityColumn);
|
||||
}
|
||||
|
||||
$values = [];
|
||||
$columns = array_unique($columns);
|
||||
$placeholders = [];
|
||||
$columns = array_unique($columns);
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$placeholder = '?';
|
||||
@@ -1492,15 +1510,13 @@ class BasicEntityPersister implements EntityPersister
|
||||
$placeholder = $type->convertToDatabaseValueSQL('?', $this->platform);
|
||||
}
|
||||
|
||||
$values[] = $placeholder;
|
||||
$placeholders[] = $placeholder;
|
||||
}
|
||||
|
||||
$columns = implode(', ', $columns);
|
||||
$values = implode(', ', $values);
|
||||
$columns = implode(', ', $columns);
|
||||
$placeholders = implode(', ', $placeholders);
|
||||
|
||||
$this->insertSql = sprintf('INSERT INTO %s (%s) VALUES (%s)', $tableName, $columns, $values);
|
||||
|
||||
return $this->insertSql;
|
||||
return sprintf('INSERT INTO %s (%s) VALUES (%s)', $tableName, $columns, $placeholders);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1698,6 +1714,8 @@ class BasicEntityPersister implements EntityPersister
|
||||
*/
|
||||
public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null)
|
||||
{
|
||||
$comparison = $comparison ?? (is_array($value) ? Comparison::IN : Comparison::EQ);
|
||||
|
||||
$selectedColumns = [];
|
||||
$columns = $this->getSelectConditionStatementColumnSQL($field, $assoc);
|
||||
|
||||
@@ -1717,46 +1735,50 @@ class BasicEntityPersister implements EntityPersister
|
||||
$placeholder = $type->convertToDatabaseValueSQL($placeholder, $this->platform);
|
||||
}
|
||||
|
||||
if ($comparison !== null) {
|
||||
// special case null value handling
|
||||
if (($comparison === Comparison::EQ || $comparison === Comparison::IS) && $value === null) {
|
||||
$selectedColumns[] = $column . ' IS NULL';
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($comparison === Comparison::NEQ && $value === null) {
|
||||
$selectedColumns[] = $column . ' IS NOT NULL';
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$selectedColumns[] = $column . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder);
|
||||
// special case null value handling
|
||||
if (($comparison === Comparison::EQ || $comparison === Comparison::IS) && $value === null) {
|
||||
$selectedColumns[] = $column . ' IS NULL';
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
$in = sprintf('%s IN (%s)', $column, $placeholder);
|
||||
if ($comparison === Comparison::NEQ && $value === null) {
|
||||
$selectedColumns[] = $column . ' IS NOT NULL';
|
||||
|
||||
if (array_search(null, $value, true) !== false) {
|
||||
$selectedColumns[] = sprintf('(%s OR %s IS NULL)', $in, $column);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($comparison === Comparison::IN || $comparison === Comparison::NIN) {
|
||||
if (! is_array($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
|
||||
if ($value === []) {
|
||||
$selectedColumns[] = '1=0';
|
||||
continue;
|
||||
}
|
||||
|
||||
$selectedColumns[] = $in;
|
||||
$nullKeys = array_keys($value, null, true);
|
||||
$nonNullValues = array_diff_key($value, array_flip($nullKeys));
|
||||
|
||||
$placeholders = implode(', ', array_fill(0, count($nonNullValues), $placeholder));
|
||||
|
||||
$in = $column . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholders);
|
||||
|
||||
if ($nullKeys) {
|
||||
if ($nonNullValues) {
|
||||
$selectedColumns[] = sprintf('(%s OR %s IS NULL)', $in, $column);
|
||||
} else {
|
||||
$selectedColumns[] = $column . ' IS NULL';
|
||||
}
|
||||
} else {
|
||||
$selectedColumns[] = $in;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value === null) {
|
||||
$selectedColumns[] = sprintf('%s IS NULL', $column);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$selectedColumns[] = sprintf('%s = %s', $column, $placeholder);
|
||||
$selectedColumns[] = $column . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder);
|
||||
}
|
||||
|
||||
return implode(' AND ', $selectedColumns);
|
||||
@@ -1947,8 +1969,18 @@ class BasicEntityPersister implements EntityPersister
|
||||
continue; // skip null values.
|
||||
}
|
||||
|
||||
$types = array_merge($types, $this->getTypes($field, $value, $this->class));
|
||||
$params = array_merge($params, $this->getValues($value));
|
||||
if (is_array($value)) {
|
||||
$nonNullValues = array_diff_key($value, array_flip(array_keys($value, null, true)));
|
||||
foreach ($nonNullValues as $item) {
|
||||
$types = array_merge($types, PersisterHelper::inferParameterTypes($field, $item, $this->class, $this->em));
|
||||
$params = array_merge($params, PersisterHelper::convertToParameterValue($item, $this->em));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$types = array_merge($types, PersisterHelper::inferParameterTypes($field, $value, $this->class, $this->em));
|
||||
$params = array_merge($params, PersisterHelper::convertToParameterValue($value, $this->em));
|
||||
}
|
||||
|
||||
return [$params, $types];
|
||||
@@ -1976,127 +2008,13 @@ class BasicEntityPersister implements EntityPersister
|
||||
continue; // skip null values.
|
||||
}
|
||||
|
||||
$types = array_merge($types, $this->getTypes($criterion['field'], $criterion['value'], $criterion['class']));
|
||||
$params = array_merge($params, $this->getValues($criterion['value']));
|
||||
$types = array_merge($types, PersisterHelper::inferParameterTypes($criterion['field'], $criterion['value'], $criterion['class'], $this->em));
|
||||
$params = array_merge($params, PersisterHelper::convertToParameterValue($criterion['value'], $this->em));
|
||||
}
|
||||
|
||||
return [$params, $types];
|
||||
}
|
||||
|
||||
/**
|
||||
* Infers field types to be used by parameter type casting.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return int[]|null[]|string[]
|
||||
* @phpstan-return list<int|string|null>
|
||||
*
|
||||
* @throws QueryException
|
||||
*/
|
||||
private function getTypes(string $field, $value, ClassMetadata $class): array
|
||||
{
|
||||
$types = [];
|
||||
|
||||
switch (true) {
|
||||
case isset($class->fieldMappings[$field]):
|
||||
$types = array_merge($types, [$class->fieldMappings[$field]['type']]);
|
||||
break;
|
||||
|
||||
case isset($class->associationMappings[$field]):
|
||||
$assoc = $class->associationMappings[$field];
|
||||
$class = $this->em->getClassMetadata($assoc['targetEntity']);
|
||||
|
||||
if (! $assoc['isOwningSide']) {
|
||||
$assoc = $class->associationMappings[$assoc['mappedBy']];
|
||||
$class = $this->em->getClassMetadata($assoc['targetEntity']);
|
||||
}
|
||||
|
||||
$columns = $assoc['type'] === ClassMetadata::MANY_TO_MANY
|
||||
? $assoc['relationToTargetKeyColumns']
|
||||
: $assoc['sourceToTargetKeyColumns'];
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$types[] = PersisterHelper::getTypeOfColumn($column, $class, $this->em);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$types[] = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
return array_map(static function ($type) {
|
||||
$type = Type::getType($type);
|
||||
|
||||
return $type->getBindingType() + Connection::ARRAY_PARAM_OFFSET;
|
||||
}, $types);
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the parameters that identifies a value.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function getValues($value): array
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$newValue = [];
|
||||
|
||||
foreach ($value as $itemValue) {
|
||||
$newValue = array_merge($newValue, $this->getValues($itemValue));
|
||||
}
|
||||
|
||||
return [$newValue];
|
||||
}
|
||||
|
||||
return $this->getIndividualValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an individual parameter value.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @phpstan-return list<mixed>
|
||||
*/
|
||||
private function getIndividualValue($value): array
|
||||
{
|
||||
if (! is_object($value)) {
|
||||
return [$value];
|
||||
}
|
||||
|
||||
if ($value instanceof BackedEnum) {
|
||||
return [$value->value];
|
||||
}
|
||||
|
||||
$valueClass = DefaultProxyClassNameResolver::getClass($value);
|
||||
|
||||
if ($this->em->getMetadataFactory()->isTransient($valueClass)) {
|
||||
return [$value];
|
||||
}
|
||||
|
||||
$class = $this->em->getClassMetadata($valueClass);
|
||||
|
||||
if ($class->isIdentifierComposite) {
|
||||
$newValue = [];
|
||||
|
||||
foreach ($class->getIdentifierValues($value) as $innerValue) {
|
||||
$newValue = array_merge($newValue, $this->getValues($innerValue));
|
||||
}
|
||||
|
||||
return $newValue;
|
||||
}
|
||||
|
||||
return [$this->em->getUnitOfWork()->getSingleIdentifierValue($value)];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -73,7 +73,7 @@ interface EntityPersister
|
||||
/**
|
||||
* Expands the parameters from the given criteria and use the correct binding types if found.
|
||||
*
|
||||
* @param string[] $criteria
|
||||
* @param array<string, mixed> $criteria
|
||||
*
|
||||
* @phpstan-return array{list<mixed>, list<int|string|null>}
|
||||
*/
|
||||
@@ -258,6 +258,8 @@ interface EntityPersister
|
||||
* @param int|null $offset
|
||||
* @phpstan-param array<string, string>|null $orderBy
|
||||
* @phpstan-param array<string, mixed> $criteria
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function loadAll(array $criteria = [], ?array $orderBy = null, $limit = null, $offset = null);
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
*/
|
||||
private function getVersionedClassMetadata(): ClassMetadata
|
||||
{
|
||||
if (isset($this->class->fieldMappings[$this->class->versionField]['inherited'])) {
|
||||
if ($this->class->versionField !== null && isset($this->class->fieldMappings[$this->class->versionField]['inherited'])) {
|
||||
$definingClassName = $this->class->fieldMappings[$this->class->versionField]['inherited'];
|
||||
|
||||
return $this->em->getClassMetadata($definingClassName);
|
||||
@@ -149,7 +149,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
// Execute all inserts. For each entity:
|
||||
// 1) Insert on root table
|
||||
// 2) Insert on sub tables
|
||||
foreach ($this->queuedInserts as $entity) {
|
||||
foreach ($this->queuedInserts as $key => $entity) {
|
||||
$insertData = $this->prepareInsertData($entity);
|
||||
|
||||
// Execute insert on root table
|
||||
@@ -194,9 +194,16 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
if ($this->class->requiresFetchAfterChange) {
|
||||
$this->assignDefaultVersionAndUpsertableValues($entity, $id);
|
||||
}
|
||||
}
|
||||
|
||||
$this->queuedInserts = [];
|
||||
// Unset this queued insert, so that the prepareUpdateData() method (called via prepareInsertData() method)
|
||||
// knows right away (for the next entity already) that the current entity has been written to the database
|
||||
// and no extra updates need to be scheduled to refer to it.
|
||||
//
|
||||
// In \Doctrine\ORM\UnitOfWork::executeInserts(), the UoW already removed entities
|
||||
// from its own list (\Doctrine\ORM\UnitOfWork::$entityInsertions) right after they
|
||||
// were given to our addInsert() method.
|
||||
unset($this->queuedInserts[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,7 +411,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
protected function getSelectColumnsSQL()
|
||||
{
|
||||
// Create the column list fragment only once
|
||||
if ($this->currentPersisterContext->selectColumnListSql !== null) {
|
||||
if ($this->currentPersisterContext->selectColumnListSql !== null && $this->isFilterHashUpToDate()) {
|
||||
return $this->currentPersisterContext->selectColumnListSql;
|
||||
}
|
||||
|
||||
@@ -495,6 +502,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
}
|
||||
|
||||
$this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);
|
||||
$this->updateFilterHash();
|
||||
|
||||
return $this->currentPersisterContext->selectColumnListSql;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
|
||||
*/
|
||||
protected function getSelectColumnsSQL()
|
||||
{
|
||||
if ($this->currentPersisterContext->selectColumnListSql !== null) {
|
||||
if ($this->currentPersisterContext->selectColumnListSql !== null && $this->isFilterHashUpToDate()) {
|
||||
return $this->currentPersisterContext->selectColumnListSql;
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
|
||||
}
|
||||
|
||||
$this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);
|
||||
$this->updateFilterHash();
|
||||
|
||||
return $this->currentPersisterContext->selectColumnListSql;
|
||||
}
|
||||
|
||||
@@ -172,10 +172,12 @@ EOPHP;
|
||||
|
||||
$this->isLazyGhostObjectEnabled = false;
|
||||
|
||||
// @phpstan-ignore new.deprecatedClass, method.deprecatedClass
|
||||
$proxyGenerator = new ProxyGenerator($proxyDir, $proxyNs);
|
||||
// @phpstan-ignore classConstant.deprecatedInterface
|
||||
// @phpstan-ignore classConstant.deprecatedInterface, method.deprecatedClass
|
||||
$proxyGenerator->setPlaceholder('baseProxyInterface', LegacyProxy::class);
|
||||
|
||||
// @phpstan-ignore method.deprecatedClass
|
||||
parent::__construct($proxyGenerator, $em->getMetadataFactory(), $autoGenerate);
|
||||
}
|
||||
|
||||
@@ -205,6 +207,7 @@ EOPHP;
|
||||
public function getProxy($className, array $identifier)
|
||||
{
|
||||
if (! $this->isLazyGhostObjectEnabled) {
|
||||
// @phpstan-ignore method.deprecatedClass
|
||||
return parent::getProxy($className, $identifier);
|
||||
}
|
||||
|
||||
@@ -226,6 +229,7 @@ EOPHP;
|
||||
public function generateProxyClasses(array $classes, $proxyDir = null)
|
||||
{
|
||||
if (! $this->isLazyGhostObjectEnabled) {
|
||||
// @phpstan-ignore method.deprecatedClass
|
||||
return parent::generateProxyClasses($classes, $proxyDir);
|
||||
}
|
||||
|
||||
@@ -422,7 +426,10 @@ EOPHP;
|
||||
continue;
|
||||
}
|
||||
|
||||
$property->setAccessible(true);
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$property->setAccessible(true);
|
||||
}
|
||||
|
||||
$property->setValue($proxy, $property->getValue($original));
|
||||
}
|
||||
};
|
||||
@@ -568,6 +575,7 @@ EOPHP;
|
||||
|
||||
private function generateUseLazyGhostTrait(ClassMetadata $class): string
|
||||
{
|
||||
// @phpstan-ignore staticMethod.deprecated (Because we support Symfony < 7.3)
|
||||
$code = ProxyHelper::generateLazyGhost($class->getReflectionClass());
|
||||
$code = substr($code, 7 + (int) strpos($code, "\n{"));
|
||||
$code = substr($code, 0, (int) strpos($code, "\n}"));
|
||||
|
||||
@@ -14,9 +14,10 @@ class InListExpression extends InExpression
|
||||
public function __construct(ArithmeticExpression $expression, array $literals, bool $not = false)
|
||||
{
|
||||
$this->literals = $literals;
|
||||
$this->not = $not;
|
||||
// @phpstan-ignore property.deprecatedClass
|
||||
$this->not = $not;
|
||||
|
||||
// @phpstan-ignore staticMethod.deprecatedClass
|
||||
// @phpstan-ignore method.deprecatedClass
|
||||
parent::__construct($expression);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,10 @@ class InSubselectExpression extends InExpression
|
||||
public function __construct(ArithmeticExpression $expression, Subselect $subselect, bool $not = false)
|
||||
{
|
||||
$this->subselect = $subselect;
|
||||
$this->not = $not;
|
||||
// @phpstan-ignore property.deprecatedClass
|
||||
$this->not = $not;
|
||||
|
||||
// @phpstan-ignore staticMethod.deprecatedClass
|
||||
// @phpstan-ignore method.deprecatedClass
|
||||
parent::__construct($expression);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1554,7 +1554,7 @@ class Parser
|
||||
|
||||
assert($this->lexer->lookahead !== null);
|
||||
switch (true) {
|
||||
case $this->isMathOperator($peek):
|
||||
case $this->isMathOperator($peek) || $this->isMathOperator($glimpse):
|
||||
$expr = $this->SimpleArithmeticExpression();
|
||||
break;
|
||||
|
||||
|
||||
@@ -411,7 +411,7 @@ class ResultSetMappingBuilder extends ResultSetMapping
|
||||
[$relation, $fieldName] = explode('.', $fieldName);
|
||||
}
|
||||
|
||||
if (isset($classMetadata->associationMappings[$relation])) {
|
||||
if ($relation !== null && isset($classMetadata->associationMappings[$relation])) {
|
||||
if ($relation) {
|
||||
$associationMapping = $classMetadata->associationMappings[$relation];
|
||||
$joinAlias = $alias . $relation;
|
||||
|
||||
@@ -1587,7 +1587,9 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$sqlParts[] = $col . ' AS ' . $columnAlias;
|
||||
|
||||
$this->scalarResultAliasMap[$resultAlias][] = $columnAlias;
|
||||
if ($resultAlias !== null) {
|
||||
$this->scalarResultAliasMap[$resultAlias][] = $columnAlias;
|
||||
}
|
||||
|
||||
$this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name);
|
||||
|
||||
@@ -1622,7 +1624,9 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$sqlParts[] = $col . ' AS ' . $columnAlias;
|
||||
|
||||
$this->scalarResultAliasMap[$resultAlias][] = $columnAlias;
|
||||
if ($resultAlias !== null) {
|
||||
$this->scalarResultAliasMap[$resultAlias][] = $columnAlias;
|
||||
}
|
||||
|
||||
$this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName);
|
||||
}
|
||||
@@ -2356,7 +2360,9 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkInListExpression(AST\InListExpression $inExpr): string
|
||||
{
|
||||
/** @phpstan-ignore property.deprecatedClass */
|
||||
return $this->walkArithmeticExpression($inExpr->expression)
|
||||
/** @phpstan-ignore property.deprecatedClass */
|
||||
. ($inExpr->not ? ' NOT' : '') . ' IN ('
|
||||
. implode(', ', array_map([$this, 'walkInParameter'], $inExpr->literals))
|
||||
. ')';
|
||||
@@ -2367,7 +2373,9 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkInSubselectExpression(AST\InSubselectExpression $inExpr): string
|
||||
{
|
||||
/** @phpstan-ignore property.deprecatedClass */
|
||||
return $this->walkArithmeticExpression($inExpr->expression)
|
||||
/** @phpstan-ignore property.deprecatedClass */
|
||||
. ($inExpr->not ? ' NOT' : '') . ' IN ('
|
||||
. $this->walkSubselect($inExpr->subselect)
|
||||
. ')';
|
||||
|
||||
29
src/Tools/Console/ApplicationCompatibility.php
Normal file
29
src/Tools/Console/ApplicationCompatibility.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console;
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
||||
use function method_exists;
|
||||
|
||||
/**
|
||||
* Forward compatibility with Symfony Console 7.4
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait ApplicationCompatibility
|
||||
{
|
||||
private static function addCommandToApplication(Application $application, Command $command): ?Command
|
||||
{
|
||||
// @phpstan-ignore function.alreadyNarrowedType (This method did not exist before Symfony 7.4)
|
||||
if (method_exists(Application::class, 'addCommand')) {
|
||||
return $application->addCommand($command);
|
||||
}
|
||||
|
||||
// @phpstan-ignore method.notFound
|
||||
return $application->add($command);
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ abstract class AbstractEntityManagerCommand extends Command
|
||||
$helper = $this->getHelper('em');
|
||||
assert($helper instanceof EntityManagerHelper);
|
||||
|
||||
/** @phpstan-ignore method.deprecatedClass */
|
||||
return $helper->getEntityManager();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,7 @@ class CollectionRegionCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:clear-cache:region:collection')
|
||||
->setDescription('Clear a second-level cache collection region')
|
||||
|
||||
@@ -23,8 +23,7 @@ class EntityRegionCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:clear-cache:region:entity')
|
||||
->setDescription('Clear a second-level cache entity region')
|
||||
|
||||
@@ -21,8 +21,7 @@ class MetadataCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:clear-cache:metadata')
|
||||
->setDescription('Clear all metadata cache of the various cache drivers')
|
||||
|
||||
@@ -30,8 +30,7 @@ class QueryCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:clear-cache:query')
|
||||
->setDescription('Clear all query cache of the various cache drivers')
|
||||
|
||||
@@ -23,8 +23,7 @@ class QueryRegionCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:clear-cache:region:query')
|
||||
->setDescription('Clear a second-level cache query region')
|
||||
|
||||
@@ -32,8 +32,7 @@ class ResultCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:clear-cache:result')
|
||||
->setDescription('Clear all result cache of the various cache drivers')
|
||||
|
||||
@@ -75,8 +75,7 @@ class ConvertDoctrine1SchemaCommand extends Command
|
||||
$this->metadataExporter = $metadataExporter;
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:convert-d1-schema')
|
||||
->setAliases(['orm:convert:d1-schema'])
|
||||
|
||||
@@ -40,8 +40,7 @@ class ConvertMappingCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:convert-mapping')
|
||||
->setAliases(['orm:convert:mapping'])
|
||||
|
||||
@@ -22,8 +22,7 @@ class EnsureProductionSettingsCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:ensure-production-settings')
|
||||
->setDescription('Verify that Doctrine is properly configured for a production environment')
|
||||
|
||||
@@ -31,8 +31,7 @@ class GenerateEntitiesCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:generate-entities')
|
||||
->setAliases(['orm:generate:entities'])
|
||||
|
||||
@@ -29,8 +29,7 @@ class GenerateProxiesCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:generate-proxies')
|
||||
->setAliases(['orm:generate:proxies'])
|
||||
|
||||
@@ -30,8 +30,7 @@ class GenerateRepositoriesCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:generate-repositories')
|
||||
->setAliases(['orm:generate:repositories'])
|
||||
|
||||
@@ -23,8 +23,7 @@ class InfoCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:info')
|
||||
->setDescription('Show basic information about all mapped entities')
|
||||
@@ -39,7 +38,7 @@ EOT
|
||||
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
$ui = new SymfonyStyle($input, $output);
|
||||
|
||||
$entityManager = $this->getEntityManager($input);
|
||||
|
||||
@@ -48,7 +47,7 @@ EOT
|
||||
->getAllClassNames();
|
||||
|
||||
if (! $entityClassNames) {
|
||||
$ui->caution(
|
||||
$ui->getErrorStyle()->caution(
|
||||
[
|
||||
'You do not have any mapped Doctrine ORM entities according to the current configuration.',
|
||||
'If you have entities or mapping files you should check your mapping configuration for errors.',
|
||||
|
||||
@@ -65,7 +65,7 @@ EOT
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
$ui = new SymfonyStyle($input, $output);
|
||||
|
||||
$entityManager = $this->getEntityManager($input);
|
||||
|
||||
@@ -98,7 +98,7 @@ EOT
|
||||
$this->formatField('Embedded class?', $metadata->isEmbeddedClass),
|
||||
$this->formatField('Parent classes', $metadata->parentClasses),
|
||||
$this->formatField('Sub classes', $metadata->subClasses),
|
||||
$this->formatField('Embedded classes', $metadata->subClasses),
|
||||
$this->formatField('Embedded classes', $metadata->embeddedClasses),
|
||||
$this->formatField('Named queries', $metadata->namedQueries),
|
||||
$this->formatField('Named native queries', $metadata->namedNativeQueries),
|
||||
$this->formatField('SQL result set mappings', $metadata->sqlResultSetMappings),
|
||||
|
||||
@@ -30,8 +30,7 @@ class RunDqlCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:run-dql')
|
||||
->setDescription('Executes arbitrary DQL directly from the command line')
|
||||
|
||||
@@ -27,6 +27,10 @@ abstract class AbstractCommand extends AbstractEntityManagerCommand
|
||||
*/
|
||||
abstract protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui);
|
||||
|
||||
private function doConfigure(): void
|
||||
{
|
||||
}
|
||||
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = new SymfonyStyle($input, $output);
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console\Command\SchemaTool;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
@@ -19,8 +20,9 @@ use function sprintf;
|
||||
*/
|
||||
class CreateCommand extends AbstractCommand
|
||||
{
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
use CommandCompatibility;
|
||||
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:schema-tool:create')
|
||||
->setDescription('Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output')
|
||||
@@ -44,6 +46,11 @@ EOT
|
||||
);
|
||||
}
|
||||
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
return parent::execute($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console\Command\SchemaTool;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
@@ -20,8 +21,9 @@ use function sprintf;
|
||||
*/
|
||||
class DropCommand extends AbstractCommand
|
||||
{
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
use CommandCompatibility;
|
||||
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:schema-tool:drop')
|
||||
->setDescription('Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output')
|
||||
@@ -48,6 +50,11 @@ EOT
|
||||
);
|
||||
}
|
||||
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
return parent::execute($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console\Command\SchemaTool;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
@@ -21,11 +22,12 @@ use function sprintf;
|
||||
*/
|
||||
class UpdateCommand extends AbstractCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @var string */
|
||||
protected $name = 'orm:schema-tool:update';
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName($this->name)
|
||||
->setDescription('Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata')
|
||||
@@ -72,6 +74,11 @@ EOT
|
||||
);
|
||||
}
|
||||
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
return parent::execute($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -23,8 +23,7 @@ class ValidateSchemaCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
private function doConfigure(): void
|
||||
{
|
||||
$this->setName('orm:validate-schema')
|
||||
->setDescription('Validate the mapping files')
|
||||
|
||||
@@ -9,10 +9,32 @@ use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
if ((new ReflectionMethod(Command::class, 'execute'))->hasReturnType()) {
|
||||
// Symfony 8
|
||||
if ((new ReflectionMethod(Command::class, 'configure'))->hasReturnType()) {
|
||||
/** @internal */
|
||||
trait CommandCompatibility
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->doConfigure();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
return $this->doExecute($input, $output);
|
||||
}
|
||||
}
|
||||
// Symfony 7
|
||||
} elseif ((new ReflectionMethod(Command::class, 'execute'))->hasReturnType()) {
|
||||
/** @internal */
|
||||
trait CommandCompatibility
|
||||
{
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
$this->doConfigure();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
return $this->doExecute($input, $output);
|
||||
@@ -22,6 +44,12 @@ if ((new ReflectionMethod(Command::class, 'execute'))->hasReturnType()) {
|
||||
/** @internal */
|
||||
trait CommandCompatibility
|
||||
{
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
$this->doConfigure();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
||||
@@ -23,6 +23,8 @@ use function class_exists;
|
||||
*/
|
||||
final class ConsoleRunner
|
||||
{
|
||||
use ApplicationCompatibility;
|
||||
|
||||
/**
|
||||
* Create a Symfony Console HelperSet
|
||||
*
|
||||
@@ -71,7 +73,7 @@ final class ConsoleRunner
|
||||
if ($helperSetOrProvider instanceof HelperSet) {
|
||||
$cli->setHelperSet($helperSetOrProvider);
|
||||
|
||||
// @phpstan-ignore new.deprecated
|
||||
// @phpstan-ignore new.deprecatedClass, method.deprecatedClass
|
||||
$helperSetOrProvider = new HelperSetManagerProvider($helperSetOrProvider);
|
||||
}
|
||||
|
||||
@@ -84,20 +86,20 @@ final class ConsoleRunner
|
||||
public static function addCommands(Application $cli, ?EntityManagerProvider $entityManagerProvider = null): void
|
||||
{
|
||||
if ($entityManagerProvider === null) {
|
||||
// @phpstan-ignore new.deprecated
|
||||
// @phpstan-ignore new.deprecatedClass, method.deprecatedClass
|
||||
$entityManagerProvider = new HelperSetManagerProvider($cli->getHelperSet());
|
||||
}
|
||||
|
||||
$connectionProvider = new ConnectionFromManagerProvider($entityManagerProvider);
|
||||
|
||||
if (class_exists(DBALConsole\Command\ImportCommand::class)) {
|
||||
$cli->add(new DBALConsole\Command\ImportCommand());
|
||||
self::addCommandToApplication($cli, new DBALConsole\Command\ImportCommand());
|
||||
}
|
||||
|
||||
$cli->addCommands(
|
||||
[
|
||||
// DBAL Commands
|
||||
// @phpstan-ignore new.deprecated
|
||||
// @phpstan-ignore new.deprecatedClass, method.deprecatedClass
|
||||
new DBALConsole\Command\ReservedWordsCommand($connectionProvider),
|
||||
new DBALConsole\Command\RunSqlCommand($connectionProvider),
|
||||
|
||||
@@ -111,16 +113,16 @@ final class ConsoleRunner
|
||||
new Command\SchemaTool\CreateCommand($entityManagerProvider),
|
||||
new Command\SchemaTool\UpdateCommand($entityManagerProvider),
|
||||
new Command\SchemaTool\DropCommand($entityManagerProvider),
|
||||
// @phpstan-ignore new.deprecated
|
||||
// @phpstan-ignore new.deprecatedClass
|
||||
new Command\EnsureProductionSettingsCommand($entityManagerProvider),
|
||||
// @phpstan-ignore new.deprecated
|
||||
// @phpstan-ignore new.deprecatedClass
|
||||
new Command\ConvertDoctrine1SchemaCommand(),
|
||||
// @phpstan-ignore new.deprecated
|
||||
// @phpstan-ignore new.deprecatedClass
|
||||
new Command\GenerateRepositoriesCommand($entityManagerProvider),
|
||||
// @phpstan-ignore new.deprecated
|
||||
// @phpstan-ignore new.deprecatedClass
|
||||
new Command\GenerateEntitiesCommand($entityManagerProvider),
|
||||
new Command\GenerateProxiesCommand($entityManagerProvider),
|
||||
// @phpstan-ignore new.deprecated
|
||||
// @phpstan-ignore new.deprecatedClass
|
||||
new Command\ConvertMappingCommand($entityManagerProvider),
|
||||
new Command\RunDqlCommand($entityManagerProvider),
|
||||
new Command\ValidateSchemaCommand($entityManagerProvider),
|
||||
|
||||
@@ -16,8 +16,6 @@ use function str_replace;
|
||||
* @deprecated 2.7 This class is being removed from the ORM and won't have any replacement
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*
|
||||
* @phpstan-ignore class.extendsDeprecatedClass
|
||||
*/
|
||||
class AnnotationExporter extends AbstractExporter
|
||||
{
|
||||
|
||||
@@ -23,8 +23,6 @@ use const PHP_EOL;
|
||||
* @deprecated 2.7 This class is being removed from the ORM and won't have any replacement
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*
|
||||
* @phpstan-ignore class.extendsDeprecatedClass
|
||||
*/
|
||||
class PhpExporter extends AbstractExporter
|
||||
{
|
||||
|
||||
@@ -21,8 +21,6 @@ use function uasort;
|
||||
* @deprecated 2.7 This class is being removed from the ORM and won't have any replacement
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*
|
||||
* @phpstan-ignore class.extendsDeprecatedClass
|
||||
*/
|
||||
class XmlExporter extends AbstractExporter
|
||||
{
|
||||
|
||||
@@ -16,8 +16,6 @@ use function count;
|
||||
* @deprecated 2.7 This class is being removed from the ORM and won't have any replacement
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*
|
||||
* @phpstan-ignore class.extendsDeprecatedClass
|
||||
*/
|
||||
class YamlExporter extends AbstractExporter
|
||||
{
|
||||
|
||||
@@ -149,7 +149,9 @@ class LimitSubqueryOutputWalker extends SqlOutputWalker
|
||||
$selectAliasToExpressionMap = [];
|
||||
// Get any aliases that are available for select expressions.
|
||||
foreach ($AST->selectClause->selectExpressions as $selectExpression) {
|
||||
$selectAliasToExpressionMap[$selectExpression->fieldIdentificationVariable] = $selectExpression->expression;
|
||||
if ($selectExpression->fieldIdentificationVariable !== null) {
|
||||
$selectAliasToExpressionMap[$selectExpression->fieldIdentificationVariable] = $selectExpression->expression;
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild string orderby expressions to use the select expression they're referencing
|
||||
|
||||
@@ -228,7 +228,22 @@ class Paginator implements Countable, IteratorAggregate
|
||||
*/
|
||||
private function getCountQuery(): Query
|
||||
{
|
||||
$countQuery = $this->cloneQuery($this->query);
|
||||
/*
|
||||
As opposed to using self::cloneQuery, the following code does not transfer
|
||||
a potentially existing result set mapping (either set directly by the user,
|
||||
or taken from the parser result from a previous invocation of Query::parse())
|
||||
to the new query object. This is fine, since we are going to completely change the
|
||||
select clause, so a previously existing result set mapping (RSM) is probably wrong anyway.
|
||||
In the case of using output walkers, we are even creating a new RSM down below.
|
||||
In the case of using a tree walker, we want to have a new RSM created by the parser.
|
||||
*/
|
||||
$countQuery = new Query($this->query->getEntityManager());
|
||||
$countQuery->setDQL($this->query->getDQL());
|
||||
$countQuery->setParameters(clone $this->query->getParameters());
|
||||
$countQuery->setCacheable(false);
|
||||
foreach ($this->query->getHints() as $name => $value) {
|
||||
$countQuery->setHint($name, $value);
|
||||
}
|
||||
|
||||
if (! $countQuery->hasHint(CountWalker::HINT_DISTINCT)) {
|
||||
$countQuery->setHint(CountWalker::HINT_DISTINCT, true);
|
||||
|
||||
@@ -86,12 +86,6 @@ class ResolveTargetEntityListener implements EventSubscriber
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->resolveTargetEntities as $interface => $data) {
|
||||
if ($data['targetEntity'] === $cm->getName()) {
|
||||
$args->getEntityManager()->getMetadataFactory()->setMetadataFor($interface, $cm);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($cm->discriminatorMap as $value => $class) {
|
||||
if (isset($this->resolveTargetEntities[$class])) {
|
||||
$cm->addDiscriminatorMapClass($value, $this->resolveTargetEntities[$class]['targetEntity']);
|
||||
|
||||
@@ -413,7 +413,7 @@ class SchemaTool
|
||||
|
||||
// @phpstan-ignore method.deprecated
|
||||
if (array_filter($schema->getSequences() + $schema->getTables(), $filter) && ! $this->platform->canEmulateSchemas()) {
|
||||
// @phpstan-ignore method.deprecated, new.deprecated
|
||||
// @phpstan-ignore method.deprecated, method.deprecatedClass, new.deprecatedClass
|
||||
$schema->visit(new RemoveNamespacedAssets());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ use function array_merge;
|
||||
use function array_sum;
|
||||
use function array_values;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function current;
|
||||
use function func_get_arg;
|
||||
use function func_num_args;
|
||||
@@ -77,6 +78,8 @@ use function spl_object_id;
|
||||
use function sprintf;
|
||||
use function strtolower;
|
||||
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
/**
|
||||
* The UnitOfWork is responsible for tracking changes to objects during an
|
||||
* "object-level" transaction and for writing out changes to the database
|
||||
@@ -1060,7 +1063,9 @@ class UnitOfWork implements PropertyChangedListener
|
||||
|
||||
$this->entityStates[$oid] = self::STATE_MANAGED;
|
||||
|
||||
$this->scheduleForInsert($entity);
|
||||
if (! isset($this->entityInsertions[$oid])) {
|
||||
$this->scheduleForInsert($entity);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param mixed[] $idValue */
|
||||
@@ -3168,8 +3173,14 @@ EXCEPTION
|
||||
$reflField->setValue($entity, $pColl);
|
||||
|
||||
if ($hints['fetchMode'][$class->name][$field] === ClassMetadata::FETCH_EAGER) {
|
||||
$isIteration = isset($hints[Query::HINT_INTERNAL_ITERATION]) && $hints[Query::HINT_INTERNAL_ITERATION];
|
||||
if ($assoc['type'] === ClassMetadata::ONE_TO_MANY && ! $isIteration && ! $targetClass->isIdentifierComposite && ! isset($assoc['indexBy'])) {
|
||||
if (
|
||||
$assoc['type'] === ClassMetadata::ONE_TO_MANY
|
||||
// is iteration
|
||||
&& ! (isset($hints[Query::HINT_INTERNAL_ITERATION]) && $hints[Query::HINT_INTERNAL_ITERATION])
|
||||
// is foreign key composite
|
||||
&& ! ($targetClass->hasAssociation($assoc['mappedBy']) && count($targetClass->getAssociationMapping($assoc['mappedBy'])['joinColumns'] ?? []) > 1)
|
||||
&& ! isset($assoc['indexBy'])
|
||||
) {
|
||||
$this->scheduleCollectionForBatchLoading($pColl, $class);
|
||||
} else {
|
||||
$this->loadCollection($pColl);
|
||||
@@ -3884,7 +3895,9 @@ EXCEPTION
|
||||
foreach ($this->reflectionPropertiesGetter->getProperties($class->name) as $prop) {
|
||||
$name = $prop->name;
|
||||
|
||||
$prop->setAccessible(true);
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$prop->setAccessible(true);
|
||||
}
|
||||
|
||||
if (! isset($class->associationMappings[$name])) {
|
||||
if (! $class->isIdentifier($name)) {
|
||||
|
||||
@@ -4,11 +4,19 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Utility;
|
||||
|
||||
use BackedEnum;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use RuntimeException;
|
||||
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
@@ -113,4 +121,116 @@ class PersisterHelper
|
||||
$class->getName()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Infers field types to be used by parameter type casting.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return int[]|null[]|string[]
|
||||
* @phpstan-return list<int|string|null>
|
||||
*
|
||||
* @throws QueryException
|
||||
*/
|
||||
public static function inferParameterTypes(string $field, $value, ClassMetadata $class, EntityManagerInterface $em): array
|
||||
{
|
||||
$types = [];
|
||||
|
||||
switch (true) {
|
||||
case isset($class->fieldMappings[$field]):
|
||||
$types = array_merge($types, [$class->fieldMappings[$field]['type']]);
|
||||
break;
|
||||
|
||||
case isset($class->associationMappings[$field]):
|
||||
$assoc = $class->associationMappings[$field];
|
||||
$class = $em->getClassMetadata($assoc['targetEntity']);
|
||||
|
||||
if (! $assoc['isOwningSide']) {
|
||||
$assoc = $class->associationMappings[$assoc['mappedBy']];
|
||||
$class = $em->getClassMetadata($assoc['targetEntity']);
|
||||
}
|
||||
|
||||
$columns = $assoc['type'] === ClassMetadata::MANY_TO_MANY
|
||||
? $assoc['relationToTargetKeyColumns']
|
||||
: $assoc['sourceToTargetKeyColumns'];
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$types[] = self::getTypeOfColumn($column, $class, $em);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$types[] = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
return array_map(static function ($type) {
|
||||
$type = Type::getType($type);
|
||||
|
||||
return $type->getBindingType() + Connection::ARRAY_PARAM_OFFSET;
|
||||
}, $types);
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a value to the type and value required to bind it as a parameter.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return list<mixed>
|
||||
*/
|
||||
public static function convertToParameterValue($value, EntityManagerInterface $em): array
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$newValue = [];
|
||||
|
||||
foreach ($value as $itemValue) {
|
||||
$newValue = array_merge($newValue, self::convertToParameterValue($itemValue, $em));
|
||||
}
|
||||
|
||||
return [$newValue];
|
||||
}
|
||||
|
||||
return self::convertIndividualValue($value, $em);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @phpstan-return list<mixed>
|
||||
*/
|
||||
private static function convertIndividualValue($value, EntityManagerInterface $em): array
|
||||
{
|
||||
if (! is_object($value)) {
|
||||
return [$value];
|
||||
}
|
||||
|
||||
if ($value instanceof BackedEnum) {
|
||||
return [$value->value];
|
||||
}
|
||||
|
||||
$valueClass = DefaultProxyClassNameResolver::getClass($value);
|
||||
|
||||
if ($em->getMetadataFactory()->isTransient($valueClass)) {
|
||||
return [$value];
|
||||
}
|
||||
|
||||
$class = $em->getClassMetadata($valueClass);
|
||||
|
||||
if ($class->isIdentifierComposite) {
|
||||
$newValue = [];
|
||||
|
||||
foreach ($class->getIdentifierValues($value) as $innerValue) {
|
||||
$newValue = array_merge($newValue, self::convertToParameterValue($innerValue, $em));
|
||||
}
|
||||
|
||||
return $newValue;
|
||||
}
|
||||
|
||||
return [$em->getUnitOfWork()->getSingleIdentifierValue($value)];
|
||||
}
|
||||
}
|
||||
|
||||
238
tests/Doctrine/Tests/ORM/Functional/GH8011Test.php
Normal file
238
tests/Doctrine/Tests/ORM/Functional/GH8011Test.php
Normal file
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
use Doctrine\Tests\Models\Company\CompanyEmployee;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Functional tests for ordering with arithmetic expression.
|
||||
*
|
||||
* @group GH8011
|
||||
*/
|
||||
class GH8011Test extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->useModelSet('company');
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$this->generateFixture();
|
||||
}
|
||||
|
||||
private function skipIfPostgres(string $test): void
|
||||
{
|
||||
$platform = $this->_em->getConnection()->getDatabasePlatform();
|
||||
if ($platform instanceof PostgreSQLPlatform) {
|
||||
self::markTestSkipped(
|
||||
'The ' . $test . ' test does not work on postgresql (see https://github.com/doctrine/orm/pull/8012).'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testOrderWithArithmeticExpressionWithSingleValuedPathExpression(): void
|
||||
{
|
||||
$dql = 'SELECT p ' .
|
||||
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
|
||||
'ORDER BY p.id + p.id ASC';
|
||||
|
||||
/** @var CompanyEmployee[] $result */
|
||||
$result = $this->_em->createQuery($dql)->getResult();
|
||||
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertEquals('Benjamin E.', $result[0]->getName());
|
||||
$this->assertEquals('Guilherme B.', $result[1]->getName());
|
||||
}
|
||||
|
||||
public function testOrderWithArithmeticExpressionWithLiteralAndSingleValuedPathExpression(): void
|
||||
{
|
||||
$dql = 'SELECT p ' .
|
||||
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
|
||||
'ORDER BY 1 + p.id ASC';
|
||||
|
||||
/** @var CompanyEmployee[] $result */
|
||||
$result = $this->_em->createQuery($dql)->getResult();
|
||||
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertEquals('Benjamin E.', $result[0]->getName());
|
||||
$this->assertEquals('Guilherme B.', $result[1]->getName());
|
||||
}
|
||||
|
||||
public function testOrderWithArithmeticExpressionWithLiteralAndSingleValuedPathExpression2(): void
|
||||
{
|
||||
$dql = 'SELECT p ' .
|
||||
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
|
||||
'ORDER BY ((1 + p.id)) ASC';
|
||||
|
||||
/** @var CompanyEmployee[] $result */
|
||||
$result = $this->_em->createQuery($dql)->getResult();
|
||||
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertEquals('Benjamin E.', $result[0]->getName());
|
||||
$this->assertEquals('Guilherme B.', $result[1]->getName());
|
||||
}
|
||||
|
||||
public function testOrderWithArithmeticExpressionWithSingleValuedPathExpressionAndLiteral(): void
|
||||
{
|
||||
$dql = 'SELECT p ' .
|
||||
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
|
||||
'ORDER BY p.id + 1 ASC';
|
||||
|
||||
/** @var CompanyEmployee[] $result */
|
||||
$result = $this->_em->createQuery($dql)->getResult();
|
||||
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertEquals('Benjamin E.', $result[0]->getName());
|
||||
$this->assertEquals('Guilherme B.', $result[1]->getName());
|
||||
}
|
||||
|
||||
public function testOrderWithArithmeticExpressionWithResultVariableAndLiteral(): void
|
||||
{
|
||||
$this->skipIfPostgres(__FUNCTION__);
|
||||
|
||||
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
|
||||
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
|
||||
'ORDER BY s + 1 DESC';
|
||||
|
||||
/** @var CompanyEmployee[] $result */
|
||||
$result = $this->_em->createQuery($dql)->getResult();
|
||||
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertEquals('Guilherme B.', $result[0]->getName());
|
||||
$this->assertEquals('Benjamin E.', $result[1]->getName());
|
||||
}
|
||||
|
||||
public function testOrderWithArithmeticExpressionWithResultVariableAndLiteral2(): void
|
||||
{
|
||||
$this->skipIfPostgres(__FUNCTION__);
|
||||
|
||||
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
|
||||
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
|
||||
'ORDER BY ((s + 1)) DESC';
|
||||
|
||||
/** @var CompanyEmployee[] $result */
|
||||
$result = $this->_em->createQuery($dql)->getResult();
|
||||
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertEquals('Guilherme B.', $result[0]->getName());
|
||||
$this->assertEquals('Benjamin E.', $result[1]->getName());
|
||||
}
|
||||
|
||||
public function testOrderWithArithmeticExpressionWithLiteralAndResultVariable(): void
|
||||
{
|
||||
$this->skipIfPostgres(__FUNCTION__);
|
||||
|
||||
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
|
||||
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
|
||||
'ORDER BY 1 + s DESC';
|
||||
|
||||
/** @var CompanyEmployee[] $result */
|
||||
$result = $this->_em->createQuery($dql)->getResult();
|
||||
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertEquals('Guilherme B.', $result[0]->getName());
|
||||
$this->assertEquals('Benjamin E.', $result[1]->getName());
|
||||
}
|
||||
|
||||
public function testOrderWithArithmeticExpressionWithLiteralAndResultVariable2(): void
|
||||
{
|
||||
$this->skipIfPostgres(__FUNCTION__);
|
||||
|
||||
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
|
||||
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
|
||||
'ORDER BY ((1 + s)) DESC';
|
||||
|
||||
/** @var CompanyEmployee[] $result */
|
||||
$result = $this->_em->createQuery($dql)->getResult();
|
||||
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertEquals('Guilherme B.', $result[0]->getName());
|
||||
$this->assertEquals('Benjamin E.', $result[1]->getName());
|
||||
}
|
||||
|
||||
public function testOrderWithArithmeticExpressionWithResultVariableAndSingleValuedPathExpression(): void
|
||||
{
|
||||
$this->skipIfPostgres(__FUNCTION__);
|
||||
|
||||
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
|
||||
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
|
||||
'ORDER BY s + p.id DESC';
|
||||
|
||||
/** @var CompanyEmployee[] $result */
|
||||
$result = $this->_em->createQuery($dql)->getResult();
|
||||
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertEquals('Guilherme B.', $result[0]->getName());
|
||||
$this->assertEquals('Benjamin E.', $result[1]->getName());
|
||||
}
|
||||
|
||||
public function testOrderWithArithmeticExpressionWithResultVariableAndSingleValuedPathExpression2(): void
|
||||
{
|
||||
$this->skipIfPostgres(__FUNCTION__);
|
||||
|
||||
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
|
||||
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
|
||||
'ORDER BY ((s + p.id)) DESC';
|
||||
|
||||
/** @var CompanyEmployee[] $result */
|
||||
$result = $this->_em->createQuery($dql)->getResult();
|
||||
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertEquals('Guilherme B.', $result[0]->getName());
|
||||
$this->assertEquals('Benjamin E.', $result[1]->getName());
|
||||
}
|
||||
|
||||
public function testOrderWithArithmeticExpressionWithSingleValuedPathExpressionAndResultVariable(): void
|
||||
{
|
||||
$this->skipIfPostgres(__FUNCTION__);
|
||||
|
||||
$dql = 'SELECT p, p.salary AS HIDDEN s ' .
|
||||
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
|
||||
'ORDER BY p.id + s DESC';
|
||||
|
||||
/** @var CompanyEmployee[] $result */
|
||||
$result = $this->_em->createQuery($dql)->getResult();
|
||||
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertEquals('Guilherme B.', $result[0]->getName());
|
||||
$this->assertEquals('Benjamin E.', $result[1]->getName());
|
||||
}
|
||||
|
||||
public function testOrderWithArithmeticExpressionWithLiteralAndResultVariableUsingHiddenResultVariable(): void
|
||||
{
|
||||
$dql = 'SELECT p, 1 + p.salary AS HIDDEN _order ' .
|
||||
'FROM Doctrine\Tests\Models\Company\CompanyEmployee p ' .
|
||||
'ORDER BY _order DESC';
|
||||
|
||||
/** @var CompanyEmployee[] $result */
|
||||
$result = $this->_em->createQuery($dql)->getResult();
|
||||
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertEquals('Guilherme B.', $result[0]->getName());
|
||||
$this->assertEquals('Benjamin E.', $result[1]->getName());
|
||||
}
|
||||
|
||||
public function generateFixture(): void
|
||||
{
|
||||
$person1 = new CompanyEmployee();
|
||||
$person1->setName('Benjamin E.');
|
||||
$person1->setDepartment('IT');
|
||||
$person1->setSalary(200000);
|
||||
|
||||
$person2 = new CompanyEmployee();
|
||||
$person2->setName('Guilherme B.');
|
||||
$person2->setDepartment('IT2');
|
||||
$person2->setSalary(400000);
|
||||
|
||||
$this->_em->persist($person1);
|
||||
$this->_em->persist($person2);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
}
|
||||
}
|
||||
@@ -37,11 +37,19 @@ class RootEntity
|
||||
*/
|
||||
private $secondLevel;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(mappedBy="root", targetEntity=SecondLevelWithoutCompositePrimaryKey::class, fetch="EAGER")
|
||||
*
|
||||
* @var Collection<int, SecondLevelWithoutCompositePrimaryKey>
|
||||
*/
|
||||
private $anotherSecondLevel;
|
||||
|
||||
public function __construct(int $id, string $other)
|
||||
{
|
||||
$this->otherKey = $other;
|
||||
$this->secondLevel = new ArrayCollection();
|
||||
$this->id = $id;
|
||||
$this->otherKey = $other;
|
||||
$this->secondLevel = new ArrayCollection();
|
||||
$this->anotherSecondLevel = new ArrayCollection();
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\EagerFetchedCompositeOneToMany;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class SecondLevelWithoutCompositePrimaryKey
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer", nullable=false)
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=RootEntity::class, inversedBy="anotherSecondLevel")
|
||||
* @ORM\JoinColumns({
|
||||
* @ORM\JoinColumn(name="root_id", referencedColumnName="id"),
|
||||
* @ORM\JoinColumn(name="root_other_key", referencedColumnName="other_key")
|
||||
* })
|
||||
*
|
||||
* @var RootEntity
|
||||
*/
|
||||
private $root;
|
||||
|
||||
public function __construct(RootEntity $upper)
|
||||
{
|
||||
$this->root = $upper;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
30
tests/Tests/Models/Enums/BookCategory.php
Normal file
30
tests/Tests/Models/Enums/BookCategory.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\Enums;
|
||||
|
||||
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\ManyToMany;
|
||||
|
||||
#[Entity]
|
||||
class BookCategory
|
||||
{
|
||||
#[Id]
|
||||
#[Column]
|
||||
#[GeneratedValue]
|
||||
public int $id;
|
||||
|
||||
#[ManyToMany(targetEntity: BookWithGenre::class, mappedBy: 'categories')]
|
||||
public Collection $books;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->books = new ArrayCollection();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user