mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 06:52:09 +01:00
Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
17d28b5c4c | ||
|
|
a2d510c6f4 | ||
|
|
2a4ebca90e | ||
|
|
9bf407f336 | ||
|
|
cc29ae0d36 | ||
|
|
bd4a053d29 | ||
|
|
52fbfb3785 | ||
|
|
c259371e5f | ||
|
|
dcdd58b642 | ||
|
|
7b9c53854f | ||
|
|
cdc5fe11dd | ||
|
|
69ece00564 | ||
|
|
c679d1b007 | ||
|
|
1e15b22dcb | ||
|
|
44057b4683 | ||
|
|
013df03795 | ||
|
|
2d2a34407c | ||
|
|
067ad51b3f | ||
|
|
00c77213fb | ||
|
|
3303cd3b5d | ||
|
|
afcf91e839 | ||
|
|
c61a9b3b6d | ||
|
|
c68b8f90b3 | ||
|
|
aa4f9ce9e9 | ||
|
|
d96fc23327 | ||
|
|
3aed6912a3 | ||
|
|
4fb044d5f6 | ||
|
|
8ce7b310c5 | ||
|
|
2a953c5e2b | ||
|
|
abc6a40ccb | ||
|
|
158605bf24 | ||
|
|
2c2ef65817 | ||
|
|
1c33a86983 | ||
|
|
310fe1cccb | ||
|
|
a67f677747 | ||
|
|
73e68f3c7d | ||
|
|
73777d0bd4 | ||
|
|
ec6d1b9f72 | ||
|
|
d809fed52a | ||
|
|
0e4786dfa8 | ||
|
|
c429262f02 | ||
|
|
f4fdcbcdcb | ||
|
|
b0806469d5 | ||
|
|
e89b58a13f | ||
|
|
2b94ec18b9 | ||
|
|
2a662149f4 | ||
|
|
37051d57ce | ||
|
|
4563f2f9a7 | ||
|
|
91201c094a | ||
|
|
14866461c5 | ||
|
|
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.1"
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@12.1.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@v5"
|
||||
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@v5"
|
||||
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@v5"
|
||||
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@v5"
|
||||
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@v5"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -373,12 +377,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v4"
|
||||
uses: "actions/checkout@v5"
|
||||
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.1"
|
||||
uses: "doctrine/.github/.github/workflows/documentation.yml@12.1.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@v5"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@7.2.1"
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@12.1.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@v5"
|
||||
|
||||
- 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
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
|
||||
{"name": "Marco Pivetta", "email": "ocramius@gmail.com"}
|
||||
],
|
||||
"scripts": {
|
||||
"docs": "composer --working-dir docs update && ./docs/vendor/bin/build-docs.sh @additional_args"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
@@ -41,14 +44,13 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/annotations": "^1.13 || ^2",
|
||||
"doctrine/coding-standard": "^9.0.2 || ^12.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.22",
|
||||
"phpstan/phpstan-deprecation-rules": "^1 || ^2",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6",
|
||||
"psr/log": "^1 || ^2 || ^3",
|
||||
"squizlabs/php_codesniffer": "3.7.2",
|
||||
"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"
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -103,6 +103,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 +124,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>`
|
||||
|
||||
@@ -71,8 +71,8 @@ Configuration Options
|
||||
The following sections describe all the configuration options
|
||||
available on a ``Doctrine\ORM\Configuration`` instance.
|
||||
|
||||
Proxy Directory (***REQUIRED***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Proxy Directory (**REQUIRED**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -85,8 +85,8 @@ classes. For a detailed explanation on proxy classes and how they
|
||||
are used in Doctrine, refer to the "Proxy Objects" section further
|
||||
down.
|
||||
|
||||
Proxy Namespace (***REQUIRED***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Proxy Namespace (**REQUIRED**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -98,8 +98,8 @@ Gets or sets the namespace to use for generated proxy classes. For
|
||||
a detailed explanation on proxy classes and how they are used in
|
||||
Doctrine, refer to the "Proxy Objects" section further down.
|
||||
|
||||
Metadata Driver (***REQUIRED***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Metadata Driver (**REQUIRED**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -144,8 +144,8 @@ accept either a single directory as a string or an array of
|
||||
directories. With this feature a single driver can support multiple
|
||||
directories of Entities.
|
||||
|
||||
Metadata Cache (***RECOMMENDED***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Metadata Cache (**RECOMMENDED**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -166,8 +166,8 @@ For development you should use an array cache like
|
||||
``Symfony\Component\Cache\Adapter\ArrayAdapter``
|
||||
which only caches data on a per-request basis.
|
||||
|
||||
Query Cache (***RECOMMENDED***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Query Cache (**RECOMMENDED**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -189,8 +189,8 @@ For development you should use an array cache like
|
||||
``Symfony\Component\Cache\Adapter\ArrayAdapter``
|
||||
which only caches data on a per-request basis.
|
||||
|
||||
SQL Logger (***Optional***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
SQL Logger (**Optional**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -202,8 +202,8 @@ Gets or sets the logger to use for logging all SQL statements
|
||||
executed by Doctrine. The logger class must implement the
|
||||
deprecated ``Doctrine\DBAL\Logging\SQLLogger`` interface.
|
||||
|
||||
Auto-generating Proxy Classes (***OPTIONAL***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Auto-generating Proxy Classes (**OPTIONAL**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Proxy classes can either be generated manually through the Doctrine
|
||||
Console or automatically at runtime by Doctrine. The configuration
|
||||
@@ -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
|
||||
@@ -446,7 +446,7 @@ correctly if sub-namespaces use different metadata driver
|
||||
implementations.
|
||||
|
||||
|
||||
Default Repository (***OPTIONAL***)
|
||||
Default Repository (**OPTIONAL**)
|
||||
-----------------------------------
|
||||
|
||||
Specifies the FQCN of a subclass of the EntityRepository.
|
||||
@@ -461,7 +461,7 @@ That will be available for all entities without a custom repository class.
|
||||
The default value is ``Doctrine\ORM\EntityRepository``.
|
||||
Any repository class must be a subclass of EntityRepository otherwise you got an ORMException
|
||||
|
||||
Ignoring entities (***OPTIONAL***)
|
||||
Ignoring entities (**OPTIONAL**)
|
||||
-----------------------------------
|
||||
|
||||
Specifies the Entity FQCNs to ignore.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1426,7 +1426,7 @@ Is essentially the same as following:
|
||||
<doctrine-mapping>
|
||||
<entity class="Product">
|
||||
<one-to-one field="shipment" target-entity="Shipment">
|
||||
<join-column name="shipment_id" referenced-column-name="id" nulable=false />
|
||||
<join-column name="shipment_id" referenced-column-name="id" nullable=false />
|
||||
</one-to-one>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -299,7 +299,7 @@ specific to a particular entity class's lifecycle.
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
<entity name="User">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -323,7 +323,7 @@ level cache region.
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
<entity name="Country">
|
||||
@@ -431,7 +431,7 @@ It caches the primary keys of association and cache each element will be cached
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
<entity name="State">
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -736,6 +736,35 @@ methods:
|
||||
|
||||
.. note::
|
||||
|
||||
There is a limitation on the compatibility of Criteria comparisons.
|
||||
You have to use scalar values only as the value in a comparison or
|
||||
the behaviour between different backends is not the same.
|
||||
Depending on whether the collection has already been loaded from the
|
||||
database or not, criteria matching may happen at the database/SQL level
|
||||
or on objects in memory. This may lead to different results and come
|
||||
surprising, for example when a code change in one place leads to a collection
|
||||
becoming initialized and, as a side effect, returning a different result
|
||||
or even breaking a ``matching()`` call somewhere else. Also, collection
|
||||
initialization state in practical use cases may differ from the one covered
|
||||
in unit tests.
|
||||
|
||||
Database level comparisons are based on scalar representations of the values
|
||||
stored in entity properties. The field names passed to expressions correspond
|
||||
to property names. Comparison and sorting may be affected by
|
||||
database-specific behavior. For example, MySQL enum types sort by index position,
|
||||
not lexicographically by value.
|
||||
|
||||
In-memory handling is based on the ``Selectable`` API of `Doctrine Collections <https://www.doctrine-project.org/projects/doctrine-collections/en/stable/index.html#matching>`.
|
||||
In this case, field names passed to expressions are being used to derive accessor
|
||||
method names. Strict type comparisons are used for equal and not-equal checks,
|
||||
and generally PHP language rules are being used for other comparison operators
|
||||
or sorting.
|
||||
|
||||
As a general guidance, for consistent results use the Criteria API with scalar
|
||||
values only. Note that ``DateTime`` and ``DateTimeImmutable`` are two predominant
|
||||
examples of value objects that are *not* scalars.
|
||||
|
||||
Refrain from using special database-level column types or custom Doctrine Types
|
||||
that may lead to database-specific comparison or sorting rules being applied, or
|
||||
to database-level values being different from object field values.
|
||||
|
||||
Provide accessor methods for all entity fields used in criteria expressions,
|
||||
and implement those methods in a way that their return value is the
|
||||
same as the database-level value.
|
||||
|
||||
@@ -17,7 +17,7 @@ setup for the latest code in trunk.
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
@@ -103,7 +103,7 @@ of several common elements:
|
||||
// Doctrine.Tests.ORM.Mapping.User.dcm.xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
@@ -770,7 +770,7 @@ entity relationship. You can define this in XML with the "association-key" attri
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -86,7 +86,7 @@ and year of production as primary keys:
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
@@ -127,11 +127,12 @@ And for querying you can use arrays to both DQL and EntityRepositories:
|
||||
namespace VehicleCatalogue\Model;
|
||||
|
||||
// $em is the EntityManager
|
||||
$audi = $em->find("VehicleCatalogue\Model\Car", array("name" => "Audi A8", "year" => 2010));
|
||||
$audi = $em->find("VehicleCatalogue\Model\Car", ["name" => "Audi A8", "year" => 2010]);
|
||||
|
||||
$dql = "SELECT c FROM VehicleCatalogue\Model\Car c WHERE c.id = ?1";
|
||||
$dql = "SELECT c FROM VehicleCatalogue\Model\Car c WHERE c.name = ?1 AND c.year = ?2";
|
||||
$audi = $em->createQuery($dql)
|
||||
->setParameter(1, ["name" => "Audi A8", "year" => 2010])
|
||||
->setParameter(1, "Audi A8")
|
||||
->setParameter(2, 2010)
|
||||
->getSingleResult();
|
||||
|
||||
You can also use this entity in associations. Doctrine will then generate two foreign keys one for ``name``
|
||||
@@ -268,7 +269,7 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ switch to extra lazy as shown in these examples:
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
|
||||
@@ -558,7 +558,7 @@ methods, but you only need to choose one.
|
||||
|
||||
<!-- config/xml/Product.dcm.xml -->
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
@@ -1139,7 +1139,7 @@ the ``Product`` before:
|
||||
|
||||
<!-- config/xml/Bug.dcm.xml -->
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
@@ -1294,7 +1294,7 @@ Finally, we'll add metadata mappings for the ``User`` entity.
|
||||
|
||||
<!-- config/xml/User.dcm.xml -->
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
@@ -1818,7 +1818,7 @@ we have to adjust the metadata slightly.
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ here are the code and mappings for it:
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -1188,6 +1188,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 +1675,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 +1687,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 +1716,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 +2742,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 +2778,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 +2838,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 +2892,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 +2946,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 +3000,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 +3141,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 +3162,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
|
||||
@@ -3987,12 +3897,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
|
||||
@@ -5067,6 +4971,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 +5001,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 +5493,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,6 +1109,7 @@ 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'
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1278,7 +1278,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
|
||||
/**
|
||||
* @param string $fieldName
|
||||
* @param array $cache
|
||||
* @phpstan-param array{usage?: int|null, region?: string|null} $cache
|
||||
*
|
||||
* @return int[]|string[]
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -309,7 +309,7 @@ EXCEPTION
|
||||
. ' configured to cascade persist operations for entity: ' . self::objToStr($entity) . '.'
|
||||
. ' To solve this issue: Either explicitly call EntityManager#persist()'
|
||||
. ' on this unknown entity or configure cascade persist'
|
||||
. ' this association in the mapping for example @ManyToOne(..,cascade={"persist"}).'
|
||||
. ' this association in the mapping for example #[ORM\ManyToOne(..., cascade: [\'persist\'])].'
|
||||
. (method_exists($entity, '__toString')
|
||||
? ''
|
||||
: ' If you cannot find out which entity causes the problem implement \''
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1374,6 +1397,12 @@ class BasicEntityPersister implements EntityPersister
|
||||
$joinCondition[] = $this->getSQLTableAlias($association['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = '
|
||||
. $this->getSQLTableAlias($association['targetEntity']) . '.' . $targetCol;
|
||||
}
|
||||
|
||||
// Add filter SQL
|
||||
$filterSql = $this->generateFilterConditionSQL($eagerEntity, $joinTableAlias);
|
||||
if ($filterSql) {
|
||||
$joinCondition[] = $filterSql;
|
||||
}
|
||||
}
|
||||
|
||||
$this->currentPersisterContext->selectJoinSql .= ' ' . $joinTableName . ' ' . $joinTableAlias . ' ON ';
|
||||
@@ -1381,7 +1410,7 @@ class BasicEntityPersister implements EntityPersister
|
||||
}
|
||||
|
||||
$this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);
|
||||
$this->filterHash = $this->em->getFilters()->getHash();
|
||||
$this->updateFilterHash();
|
||||
|
||||
return $this->currentPersisterContext->selectColumnListSql;
|
||||
}
|
||||
@@ -1457,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 = '?';
|
||||
@@ -1486,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1692,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);
|
||||
|
||||
@@ -1711,46 +1735,45 @@ 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;
|
||||
}
|
||||
|
||||
continue;
|
||||
if ($comparison === Comparison::IN || $comparison === Comparison::NIN) {
|
||||
if (! is_array($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
|
||||
$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);
|
||||
@@ -1941,8 +1964,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];
|
||||
@@ -1970,127 +2003,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);
|
||||
}
|
||||
|
||||
@@ -378,7 +382,7 @@ EOPHP;
|
||||
$class = $entityPersister->getClassMetadata();
|
||||
|
||||
foreach ($class->getReflectionProperties() as $property) {
|
||||
if (isset($identifier[$property->name]) || ! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) {
|
||||
if (isset($identifier[$property->name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -422,7 +426,10 @@ EOPHP;
|
||||
continue;
|
||||
}
|
||||
|
||||
$property->setAccessible(true);
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$property->setAccessible(true);
|
||||
}
|
||||
|
||||
$property->setValue($proxy, $property->getValue($original));
|
||||
}
|
||||
};
|
||||
@@ -448,7 +455,7 @@ EOPHP;
|
||||
foreach ($reflector->getProperties($filter) as $property) {
|
||||
$name = $property->name;
|
||||
|
||||
if ($property->isStatic() || (($class->hasField($name) || $class->hasAssociation($name)) && ! isset($identifiers[$name]))) {
|
||||
if ($property->isStatic() || ! isset($identifiers[$name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
. ')';
|
||||
|
||||
28
src/Tools/Console/ApplicationCompatibility.php
Normal file
28
src/Tools/Console/ApplicationCompatibility.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?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
|
||||
{
|
||||
if (method_exists(Application::class, 'addCommand')) {
|
||||
// @phpstan-ignore method.notFound (This method will be added in Symfony 7.4)
|
||||
return $application->addCommand($command);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,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 +48,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),
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -105,17 +105,24 @@ class LimitSubqueryOutputWalker extends SqlOutputWalker
|
||||
$this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform();
|
||||
$this->rsm = $parserResult->getResultSetMapping();
|
||||
|
||||
$query = clone $query;
|
||||
$cloneQuery = clone $query;
|
||||
|
||||
$cloneQuery->setParameters(clone $query->getParameters());
|
||||
$cloneQuery->setCacheable(false);
|
||||
|
||||
foreach ($query->getHints() as $name => $value) {
|
||||
$cloneQuery->setHint($name, $value);
|
||||
}
|
||||
|
||||
// Reset limit and offset
|
||||
$this->firstResult = $query->getFirstResult();
|
||||
$this->maxResults = $query->getMaxResults();
|
||||
$query->setFirstResult(0)->setMaxResults(null);
|
||||
$this->firstResult = $cloneQuery->getFirstResult();
|
||||
$this->maxResults = $cloneQuery->getMaxResults();
|
||||
$cloneQuery->setFirstResult(0)->setMaxResults(null);
|
||||
|
||||
$this->em = $query->getEntityManager();
|
||||
$this->em = $cloneQuery->getEntityManager();
|
||||
$this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy();
|
||||
|
||||
parent::__construct($query, $parserResult, $queryComponents);
|
||||
parent::__construct($cloneQuery, $parserResult, $queryComponents);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ use Doctrine\Persistence\PropertyChangedListener;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\VarExporter\Hydrator;
|
||||
use UnexpectedValueException;
|
||||
|
||||
use function array_chunk;
|
||||
@@ -76,6 +77,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
|
||||
@@ -1059,7 +1062,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 */
|
||||
@@ -2944,6 +2949,11 @@ EXCEPTION
|
||||
|
||||
if ($this->isUninitializedObject($entity)) {
|
||||
$entity->__setInitialized(true);
|
||||
|
||||
if ($this->em->getConfiguration()->isLazyGhostObjectEnabled()) {
|
||||
// Initialize properties that have default values to their default value (similar to what
|
||||
Hydrator::hydrate($entity, (array) $class->reflClass->newInstanceWithoutConstructor());
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
! isset($hints[Query::HINT_REFRESH])
|
||||
@@ -3878,7 +3888,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();
|
||||
}
|
||||
}
|
||||
@@ -118,13 +118,11 @@ class ConnectionMock extends Connection
|
||||
$this->_platformMock = $platform;
|
||||
}
|
||||
|
||||
/** @return array */
|
||||
public function getExecuteStatements(): array
|
||||
{
|
||||
return $this->_executeStatements;
|
||||
}
|
||||
|
||||
/** @return array */
|
||||
public function getDeletes(): array
|
||||
{
|
||||
return $this->_deletes;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
11
tests/Tests/Models/Enums/BookGenre.php
Normal file
11
tests/Tests/Models/Enums/BookGenre.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\Enums;
|
||||
|
||||
enum BookGenre: string
|
||||
{
|
||||
case FICTION = 'fiction';
|
||||
case NON_FICTION = 'non fiction';
|
||||
}
|
||||
38
tests/Tests/Models/Enums/BookWithGenre.php
Normal file
38
tests/Tests/Models/Enums/BookWithGenre.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?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;
|
||||
use Doctrine\ORM\Mapping\ManyToOne;
|
||||
|
||||
#[Entity]
|
||||
class BookWithGenre
|
||||
{
|
||||
#[Id]
|
||||
#[GeneratedValue]
|
||||
#[Column]
|
||||
public int $id;
|
||||
|
||||
#[ManyToOne(targetEntity: Library::class, inversedBy: 'books')]
|
||||
public Library $library;
|
||||
|
||||
#[Column(enumType: BookGenre::class)]
|
||||
public BookGenre $genre;
|
||||
|
||||
#[ManyToMany(targetEntity: BookCategory::class, inversedBy: 'books')]
|
||||
public Collection $categories;
|
||||
|
||||
public function __construct(BookGenre $genre)
|
||||
{
|
||||
$this->genre = $genre;
|
||||
$this->categories = new ArrayCollection();
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,9 @@ use Doctrine\ORM\Mapping\Id;
|
||||
class Card
|
||||
{
|
||||
/**
|
||||
* @Id @GeneratedValue @Column(type="integer")
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
* @Column(type="integer")
|
||||
* @var int
|
||||
*/
|
||||
#[Id]
|
||||
|
||||
@@ -15,7 +15,9 @@ use Doctrine\ORM\Mapping\Id;
|
||||
class CardWithNullable
|
||||
{
|
||||
/**
|
||||
* @Id @GeneratedValue @Column(type="integer")
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
* @Column(type="integer")
|
||||
* @var int
|
||||
*/
|
||||
#[Id]
|
||||
|
||||
32
tests/Tests/Models/Enums/Library.php
Normal file
32
tests/Tests/Models/Enums/Library.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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\OneToMany;
|
||||
use Doctrine\ORM\Mapping\Table;
|
||||
|
||||
#[Entity]
|
||||
#[Table('`library`')]
|
||||
class Library
|
||||
{
|
||||
#[Id]
|
||||
#[GeneratedValue]
|
||||
#[Column]
|
||||
public int $id;
|
||||
|
||||
#[OneToMany(targetEntity: BookWithGenre::class, mappedBy: 'library')]
|
||||
public Collection $books;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->books = new ArrayCollection();
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,9 @@ use Doctrine\ORM\Mapping\Id;
|
||||
class Scale
|
||||
{
|
||||
/**
|
||||
* @Id @GeneratedValue @Column(type="integer")
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
* @Column(type="integer")
|
||||
* @var int
|
||||
*/
|
||||
#[Id]
|
||||
|
||||
@@ -19,8 +19,6 @@ class GH10336Relation
|
||||
*/
|
||||
public ?int $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
/** @ORM\Column(type="string") */
|
||||
public string $value;
|
||||
}
|
||||
|
||||
31
tests/Tests/Models/GH11524/GH11524Entity.php
Normal file
31
tests/Tests/Models/GH11524/GH11524Entity.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\GH11524;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="gh11524_entities")
|
||||
*/
|
||||
class GH11524Entity
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
public $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="GH11524Relation")
|
||||
* @ORM\JoinColumn(name="relation_id", referencedColumnName="id", nullable=true)
|
||||
*
|
||||
* @var GH11524Relation|null
|
||||
*/
|
||||
public $relation = null;
|
||||
}
|
||||
21
tests/Tests/Models/GH11524/GH11524Listener.php
Normal file
21
tests/Tests/Models/GH11524/GH11524Listener.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\GH11524;
|
||||
|
||||
use Doctrine\ORM\Event\PostLoadEventArgs;
|
||||
|
||||
class GH11524Listener
|
||||
{
|
||||
public function postLoad(PostloadEventArgs $eventArgs): void
|
||||
{
|
||||
$object = $eventArgs->getObject();
|
||||
|
||||
if (! $object instanceof GH11524Relation) {
|
||||
return;
|
||||
}
|
||||
|
||||
$object->setCurrentLocale('en');
|
||||
}
|
||||
}
|
||||
48
tests/Tests/Models/GH11524/GH11524Relation.php
Normal file
48
tests/Tests/Models/GH11524/GH11524Relation.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\GH11524;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="gh11524_relations")
|
||||
*/
|
||||
class GH11524Relation
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/** @var string|null */
|
||||
private $currentLocale;
|
||||
|
||||
public function setCurrentLocale(string $locale): void
|
||||
{
|
||||
$this->currentLocale = $locale;
|
||||
}
|
||||
|
||||
public function getTranslation(): string
|
||||
{
|
||||
if ($this->currentLocale === null) {
|
||||
throw new LogicException('The current locale must be set to retrieve translation.');
|
||||
}
|
||||
|
||||
return 'fake';
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,6 @@ class GH7717Child
|
||||
*/
|
||||
public ?int $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", nullable=true)
|
||||
*/
|
||||
/** @ORM\Column(type="string", nullable=true) */
|
||||
public ?string $nullableProperty = null;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,10 @@ namespace Doctrine\Tests\Models\Project;
|
||||
|
||||
class Project
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
private $name;
|
||||
|
||||
public function __construct(string $id, string $name)
|
||||
|
||||
@@ -6,9 +6,7 @@ namespace Doctrine\Tests\Models\Project;
|
||||
|
||||
class ProjectId
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
private $id;
|
||||
|
||||
public function __construct(string $id)
|
||||
|
||||
@@ -6,14 +6,10 @@ namespace Doctrine\Tests\Models\Project;
|
||||
|
||||
class ProjectInvalidMapping
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
private $name;
|
||||
|
||||
public function __construct(string $id, string $name)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user