mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 15:02:22 +01:00
Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1bb2ccf4b | ||
|
|
e3d7c6076c | ||
|
|
40f299f1eb | ||
|
|
68af854f46 | ||
|
|
77467cd824 | ||
|
|
802f20b8e7 | ||
|
|
96d13ac62a | ||
|
|
2ea6a1a5fb | ||
|
|
cc2ad1993c | ||
|
|
e4d46c4276 | ||
|
|
858a1adc3b | ||
|
|
3b499132d9 | ||
|
|
39153fd88a | ||
|
|
bdc9679e37 | ||
|
|
87a8ee21c9 | ||
|
|
59c8bc09ab | ||
|
|
3a7d7c9f57 | ||
|
|
06eca40134 | ||
|
|
23b35e9554 | ||
|
|
e063926cbd | ||
|
|
4a01a76a17 | ||
|
|
93c2dd9d4b | ||
|
|
75bc22980e | ||
|
|
9696c3434d | ||
|
|
d31aabb40c | ||
|
|
d66884403f | ||
|
|
552eae37a3 | ||
|
|
ee4b03aa78 | ||
|
|
c5291b4de8 | ||
|
|
831d86548c | ||
|
|
f26b3b9cf9 | ||
|
|
9ab84f7478 | ||
|
|
e6bb4ef20e | ||
|
|
94986af284 | ||
|
|
ad5c8e4bdc | ||
|
|
c363f55ad1 | ||
|
|
c973a62272 | ||
|
|
8d3446015a | ||
|
|
4e335f4044 | ||
|
|
bb36d49b38 | ||
|
|
2b81a8e260 | ||
|
|
7d3b3f28e9 | ||
|
|
cbec236e8b | ||
|
|
306963fe79 | ||
|
|
fb4578406f | ||
|
|
bdc41e2b5e | ||
|
|
90376a6431 | ||
|
|
b274893486 | ||
|
|
8709fb38b0 | ||
|
|
e9e60f2fbc | ||
|
|
5f3c1dbab8 | ||
|
|
6090141e0b | ||
|
|
e4a6c041b5 | ||
|
|
c54c557e02 | ||
|
|
46d0865339 | ||
|
|
1a5a4c674a | ||
|
|
95795c87a8 | ||
|
|
db6e702088 | ||
|
|
5ccbc201bf | ||
|
|
d15624f72f | ||
|
|
9d1a4973ae | ||
|
|
40a0964f06 | ||
|
|
08a9e60ed0 | ||
|
|
3e3c023c95 | ||
|
|
5e6d5c06a9 | ||
|
|
1622b7877d | ||
|
|
80aae2796d | ||
|
|
528ef40fc4 | ||
|
|
4b4b9b7b6f | ||
|
|
ae842259f5 | ||
|
|
820a0da4c1 | ||
|
|
fcd02b1ee2 | ||
|
|
abcad6fa45 | ||
|
|
1b6cf58a1a | ||
|
|
6501890ab5 | ||
|
|
e399d21fb3 | ||
|
|
16f355f0cc | ||
|
|
94d45a036f | ||
|
|
9acca2252f | ||
|
|
a809a71aa6 | ||
|
|
bd4449c462 | ||
|
|
e3e96745cc | ||
|
|
12e0cefba1 | ||
|
|
21221f73cc | ||
|
|
ab5e9e393b | ||
|
|
e62571c8f4 | ||
|
|
98f9de2af6 | ||
|
|
83c81f6c41 | ||
|
|
791667a9e4 | ||
|
|
abd9186d00 | ||
|
|
08d3f72755 | ||
|
|
779781173a | ||
|
|
c9c493b2fe | ||
|
|
8d4718f875 | ||
|
|
c0dfba2ef3 | ||
|
|
b59189ab48 | ||
|
|
e5e3166747 | ||
|
|
afbf293c94 | ||
|
|
7baef1e120 | ||
|
|
5049b615c5 |
@@ -5,23 +5,47 @@
|
||||
"slug": "orm",
|
||||
"docsSlug": "doctrine-orm",
|
||||
"versions": [
|
||||
{
|
||||
"name": "4.0",
|
||||
"branchName": "4.0.x",
|
||||
"slug": "latest",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "3.2",
|
||||
"branchName": "3.2.x",
|
||||
"slug": "3.2",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "3.1",
|
||||
"branchName": "3.1.x",
|
||||
"slug": "3.1",
|
||||
"current": true
|
||||
},
|
||||
{
|
||||
"name": "3.0",
|
||||
"branchName": "3.0.x",
|
||||
"slug": "latest",
|
||||
"slug": "3.0",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.20",
|
||||
"branchName": "2.20.x",
|
||||
"slug": "2.20",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.19",
|
||||
"branchName": "2.19.x",
|
||||
"slug": "2.19",
|
||||
"upcoming": true
|
||||
"maintained": true
|
||||
},
|
||||
{
|
||||
"name": "2.18",
|
||||
"branchName": "2.18.x",
|
||||
"slug": "2.18",
|
||||
"current": true
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.17",
|
||||
|
||||
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@3.0.0"
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.0.1"
|
||||
|
||||
36
.github/workflows/continuous-integration.yml
vendored
36
.github/workflows/continuous-integration.yml
vendored
@@ -80,7 +80,7 @@ jobs:
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
@@ -97,9 +97,9 @@ jobs:
|
||||
ORM_PROXY_IMPLEMENTATION: "${{ matrix.proxy }}"
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v3"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
with:
|
||||
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
|
||||
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.proxy }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ jobs:
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
@@ -170,9 +170,9 @@ jobs:
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/pdo_pgsql.xml --coverage-clover=coverage.xml"
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v3"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
|
||||
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.extension }}-coverage"
|
||||
path: "coverage.xml"
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ jobs:
|
||||
- "default"
|
||||
- "3@dev"
|
||||
mariadb-version:
|
||||
- "10.9"
|
||||
- "11.4"
|
||||
extension:
|
||||
- "mysqli"
|
||||
- "pdo_mysql"
|
||||
@@ -204,11 +204,11 @@ jobs:
|
||||
mariadb:
|
||||
image: "mariadb:${{ matrix.mariadb-version }}"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
MYSQL_DATABASE: "doctrine_tests"
|
||||
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: yes
|
||||
MARIADB_DATABASE: "doctrine_tests"
|
||||
|
||||
options: >-
|
||||
--health-cmd "mysqladmin ping --silent"
|
||||
--health-cmd "healthcheck.sh --connect --innodb_initialized"
|
||||
|
||||
ports:
|
||||
- "3306:3306"
|
||||
@@ -232,7 +232,7 @@ jobs:
|
||||
extensions: "${{ matrix.extension }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
@@ -240,7 +240,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@v3"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.mariadb-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
|
||||
path: "coverage.xml"
|
||||
@@ -302,7 +302,7 @@ jobs:
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
@@ -317,7 +317,7 @@ jobs:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 1
|
||||
|
||||
- name: "Upload coverage files"
|
||||
uses: "actions/upload-artifact@v3"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
with:
|
||||
name: "${{ github.job }}-${{ matrix.mysql-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
@@ -348,7 +348,7 @@ jobs:
|
||||
ini-values: "zend.assertions=1, apc.enable_cli=1"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
dependency-versions: "${{ matrix.deps }}"
|
||||
|
||||
@@ -372,11 +372,13 @@ jobs:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Download coverage files"
|
||||
uses: "actions/download-artifact@v3"
|
||||
uses: "actions/download-artifact@v4"
|
||||
with:
|
||||
path: "reports"
|
||||
|
||||
- name: "Upload to Codecov"
|
||||
uses: "codecov/codecov-action@v3"
|
||||
uses: "codecov/codecov-action@v4"
|
||||
with:
|
||||
directory: reports
|
||||
env:
|
||||
CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}"
|
||||
|
||||
9
.github/workflows/documentation.yml
vendored
9
.github/workflows/documentation.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "8.2"
|
||||
php-version: "8.3"
|
||||
|
||||
- name: "Remove existing composer file"
|
||||
run: "rm composer.json"
|
||||
@@ -36,9 +36,14 @@ jobs:
|
||||
run: "composer require --dev phpdocumentor/guides-cli --no-update"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
- name: "Add orphan metadata where needed"
|
||||
run: |
|
||||
printf '%s\n\n%s\n' ":orphan:" "$(cat docs/en/sidebar.rst)" > docs/en/sidebar.rst
|
||||
printf '%s\n\n%s\n' ":orphan:" "$(cat docs/en/reference/installation.rst)" > docs/en/reference/installation.rst
|
||||
|
||||
- name: "Run guides-cli"
|
||||
run: "vendor/bin/guides -vvv --no-progress docs/en 2>&1 | grep -v 'No template found for rendering directive' | ( ! grep WARNING )"
|
||||
|
||||
11
.github/workflows/phpbench.yml
vendored
11
.github/workflows/phpbench.yml
vendored
@@ -47,15 +47,8 @@ jobs:
|
||||
coverage: "pcov"
|
||||
ini-values: "zend.assertions=1, apc.enable_cli=1"
|
||||
|
||||
- name: "Cache dependencies installed with composer"
|
||||
uses: "actions/cache@v3"
|
||||
with:
|
||||
path: "~/.composer/cache"
|
||||
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
|
||||
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
|
||||
|
||||
- name: "Install dependencies with composer"
|
||||
run: "composer update --no-interaction --no-progress"
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
|
||||
- name: "Run PHPBench"
|
||||
run: "vendor/bin/phpbench run --report=default"
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@3.0.0"
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@5.0.1"
|
||||
secrets:
|
||||
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
|
||||
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}
|
||||
|
||||
8
.github/workflows/static-analysis.yml
vendored
8
.github/workflows/static-analysis.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "8.2"
|
||||
php-version: "8.3"
|
||||
|
||||
- name: "Require specific DBAL version"
|
||||
run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update"
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
run: "composer require doctrine/persistence ^$([ ${{ matrix.persistence-version }} = default ] && echo '3.1' || echo ${{ matrix.persistence-version }}) --no-update"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
@@ -89,13 +89,13 @@ jobs:
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "8.2"
|
||||
php-version: "8.3"
|
||||
|
||||
- name: "Require specific persistence version"
|
||||
run: "composer require doctrine/persistence ^3.1 --no-update"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
|
||||
24
README.md
24
README.md
@@ -1,7 +1,7 @@
|
||||
| [4.0.x][4.0] | [3.1.x][3.1] | [3.0.x][3.0] | [2.19.x][2.19] | [2.18.x][2.18] |
|
||||
|:------------------------------------------------------:|:------------------------------------------------------:|:-------------------------------------------------------:|:--------------------------------------------------------:|:---------------------------------------------------------:|
|
||||
| [![Build status][4.0 image]][4.0] | [![Build status][3.1 image]][3.1] | [![Build status][3.0 image]][3.0] | [![Build status][2.19 image]][2.19] | [![Build status][2.18 image]][2.18] |
|
||||
| [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.1 coverage image]][3.1 coverage] | [![Coverage Status][3.0 coverage image]][3.0 coverage] | [![Coverage Status][2.19 coverage image]][2.19 coverage] | [![Coverage Status][2.18 coverage image]][2.18 coverage] |
|
||||
| [4.0.x][4.0] | [3.2.x][3.2] | [3.1.x][3.1] | [2.20.x][2.20] | [2.19.x][2.19] |
|
||||
|:------------------------------------------------------:|:------------------------------------------------------:|:------------------------------------------------------:|:--------------------------------------------------------:|:--------------------------------------------------------:|
|
||||
| [![Build status][4.0 image]][4.0] | [![Build status][3.2 image]][3.2] | [![Build status][3.1 image]][3.1] | [![Build status][2.20 image]][2.20] | [![Build status][2.19 image]][2.19] |
|
||||
| [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.2 coverage image]][3.2 coverage] | [![Coverage Status][3.1 coverage image]][3.1 coverage] | [![Coverage Status][2.20 coverage image]][2.20 coverage] | [![Coverage Status][2.19 coverage image]][2.19 coverage] |
|
||||
|
||||
[<h1 align="center">🇺🇦 UKRAINE NEEDS YOUR HELP NOW!</h1>](https://www.doctrine-project.org/stop-war.html)
|
||||
|
||||
@@ -22,19 +22,19 @@ without requiring unnecessary code duplication.
|
||||
[4.0]: https://github.com/doctrine/orm/tree/4.0.x
|
||||
[4.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/4.0.x/graph/badge.svg
|
||||
[4.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/4.0.x
|
||||
[3.2 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.2.x
|
||||
[3.2]: https://github.com/doctrine/orm/tree/3.2.x
|
||||
[3.2 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.2.x/graph/badge.svg
|
||||
[3.2 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.2.x
|
||||
[3.1 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.1.x
|
||||
[3.1]: https://github.com/doctrine/orm/tree/3.1.x
|
||||
[3.1 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.1.x/graph/badge.svg
|
||||
[3.1 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.1.x
|
||||
[3.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.0.x
|
||||
[3.0]: https://github.com/doctrine/orm/tree/3.0.x
|
||||
[3.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.0.x/graph/badge.svg
|
||||
[3.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.0.x
|
||||
[2.20 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.20.x
|
||||
[2.20]: https://github.com/doctrine/orm/tree/2.20.x
|
||||
[2.20 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.20.x/graph/badge.svg
|
||||
[2.20 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.20.x
|
||||
[2.19 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.19.x
|
||||
[2.19]: https://github.com/doctrine/orm/tree/2.19.x
|
||||
[2.19 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.19.x/graph/badge.svg
|
||||
[2.19 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.19.x
|
||||
[2.18 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.18.x
|
||||
[2.18]: https://github.com/doctrine/orm/tree/2.18.x
|
||||
[2.18 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.18.x/graph/badge.svg
|
||||
[2.18 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.18.x
|
||||
|
||||
17
UPGRADE.md
17
UPGRADE.md
@@ -1,3 +1,20 @@
|
||||
# Upgrade to 2.19
|
||||
|
||||
## Deprecate calling `ClassMetadata::getAssociationMappedByTargetField()` with the owning side of an association
|
||||
|
||||
Calling
|
||||
`Doctrine\ORM\Mapping\ClassMetadata::getAssociationMappedByTargetField()` with
|
||||
the owning side of an association returns `null`, which is undocumented, and
|
||||
wrong according to the phpdoc of the parent method.
|
||||
|
||||
If you do not know whether you are on the owning or inverse side of an association,
|
||||
you can use `Doctrine\ORM\Mapping\ClassMetadata::isAssociationInverseSide()`
|
||||
to find out.
|
||||
|
||||
## Deprecate `Doctrine\ORM\Query\Lexer::T_*` constants
|
||||
|
||||
Use `Doctrine\ORM\Query\TokenType::T_*` instead.
|
||||
|
||||
# Upgrade to 2.17
|
||||
|
||||
## Deprecate annotations classes for named queries
|
||||
|
||||
@@ -42,14 +42,14 @@
|
||||
"doctrine/annotations": "^1.13 || ^2",
|
||||
"doctrine/coding-standard": "^9.0.2 || ^12.0",
|
||||
"phpbench/phpbench": "^0.16.10 || ^1.0",
|
||||
"phpstan/phpstan": "~1.4.10 || 1.10.59",
|
||||
"phpstan/phpstan": "~1.4.10 || 1.11.1",
|
||||
"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",
|
||||
"vimeo/psalm": "4.30.0 || 5.22.2"
|
||||
"vimeo/psalm": "4.30.0 || 5.24.0"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/annotations": "<1.13 || >= 3.0"
|
||||
|
||||
4
docs/.gitignore
vendored
4
docs/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
en/_exts/configurationblock.pyc
|
||||
build
|
||||
en/_build
|
||||
.idea
|
||||
3
docs/.gitmodules
vendored
3
docs/.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "en/_theme"]
|
||||
path = en/_theme
|
||||
url = https://github.com/doctrine/doctrine-sphinx-theme.git
|
||||
@@ -99,12 +99,12 @@ discuss it step by step:
|
||||
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER); // (2)
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS); // (3)
|
||||
$parser->match(TokenType::T_IDENTIFIER); // (2)
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS); // (3)
|
||||
$this->firstDateExpression = $parser->ArithmeticPrimary(); // (4)
|
||||
$parser->match(Lexer::T_COMMA); // (5)
|
||||
$parser->match(TokenType::T_COMMA); // (5)
|
||||
$this->secondDateExpression = $parser->ArithmeticPrimary(); // (6)
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS); // (3)
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS); // (3)
|
||||
}
|
||||
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
@@ -183,23 +183,23 @@ I'll skip the blah and show the code for this function:
|
||||
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->firstDateExpression = $parser->ArithmeticPrimary();
|
||||
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
|
||||
$this->intervalExpression = $parser->ArithmeticPrimary();
|
||||
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
|
||||
/** @var Lexer $lexer */
|
||||
$lexer = $parser->getLexer();
|
||||
$this->unit = $lexer->token['value'];
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
|
||||
@@ -11,7 +11,7 @@ What we offer are hooks to execute any kind of validation.
|
||||
.. note::
|
||||
|
||||
You don't need to validate your entities in the lifecycle
|
||||
events. Its only one of many options. Of course you can also
|
||||
events. It is only one of many options. Of course you can also
|
||||
perform validations in value setters or any other method of your
|
||||
entities that are used in your code.
|
||||
|
||||
|
||||
@@ -1300,8 +1300,8 @@ This is essentially the same as the following, more verbose, mapping:
|
||||
* @var Collection<int, Group>
|
||||
*/
|
||||
#[JoinTable(name: 'User_Group')]
|
||||
#[JoinColumn(name: 'User_id', referencedColumnName: 'id')]
|
||||
#[InverseJoinColumn(name: 'Group_id', referencedColumnName: 'id')]
|
||||
#[JoinColumn(name: 'user_id', referencedColumnName: 'id')]
|
||||
#[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')]
|
||||
#[ManyToMany(targetEntity: Group::class)]
|
||||
private Collection $groups;
|
||||
// ...
|
||||
@@ -1333,10 +1333,10 @@ This is essentially the same as the following, more verbose, mapping:
|
||||
<many-to-many field="groups" target-entity="Group">
|
||||
<join-table name="User_Group">
|
||||
<join-columns>
|
||||
<join-column id="User_id" referenced-column-name="id" />
|
||||
<join-column id="user_id" referenced-column-name="id" />
|
||||
</join-columns>
|
||||
<inverse-join-columns>
|
||||
<join-column id="Group_id" referenced-column-name="id" />
|
||||
<join-column id="group_id" referenced-column-name="id" />
|
||||
</inverse-join-columns>
|
||||
</join-table>
|
||||
</many-to-many>
|
||||
|
||||
@@ -300,50 +300,12 @@ and a custom ``Doctrine\ORM\Mapping\TypedFieldMapper`` implementation.
|
||||
Doctrine Mapping Types
|
||||
----------------------
|
||||
|
||||
The ``type`` option used in the ``@Column`` accepts any of the existing
|
||||
Doctrine types or even your own custom types. A Doctrine type defines
|
||||
The ``type`` option used in the ``@Column`` accepts any of the
|
||||
`existing Doctrine DBAL types <https://docs.doctrine-project.org/projects/doctrine-dbal/en/stable/reference/types.html#reference>`_
|
||||
or :doc:`your own custom mapping types
|
||||
<../cookbook/custom-mapping-types>`. A Doctrine type defines
|
||||
the conversion between PHP and SQL types, independent from the database vendor
|
||||
you are using. All Mapping Types that ship with Doctrine are fully portable
|
||||
between the supported database systems.
|
||||
|
||||
As an example, the Doctrine Mapping Type ``string`` defines the
|
||||
mapping from a PHP string to a SQL VARCHAR (or VARCHAR2 etc.
|
||||
depending on the RDBMS brand). Here is a quick overview of the
|
||||
built-in mapping types:
|
||||
|
||||
- ``string``: Type that maps a SQL VARCHAR to a PHP string.
|
||||
- ``integer``: Type that maps a SQL INT to a PHP integer.
|
||||
- ``smallint``: Type that maps a database SMALLINT to a PHP
|
||||
integer.
|
||||
- ``bigint``: Type that maps a database BIGINT to a PHP string.
|
||||
- ``boolean``: Type that maps a SQL boolean or equivalent (TINYINT) to a PHP boolean.
|
||||
- ``decimal``: Type that maps a SQL DECIMAL to a PHP string.
|
||||
- ``date``: Type that maps a SQL DATETIME to a PHP DateTime
|
||||
object.
|
||||
- ``time``: Type that maps a SQL TIME to a PHP DateTime object.
|
||||
- ``datetime``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP
|
||||
DateTime object.
|
||||
- ``datetimetz``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP
|
||||
DateTime object with timezone.
|
||||
- ``text``: Type that maps a SQL CLOB to a PHP string.
|
||||
- ``object``: Type that maps a SQL CLOB to a PHP object using
|
||||
``serialize()`` and ``unserialize()``
|
||||
- ``array``: Type that maps a SQL CLOB to a PHP array using
|
||||
``serialize()`` and ``unserialize()``
|
||||
- ``simple_array``: Type that maps a SQL CLOB to a PHP array using
|
||||
``implode()`` and ``explode()``, with a comma as delimiter. *IMPORTANT*
|
||||
Only use this type if you are sure that your values cannot contain a ",".
|
||||
- ``json_array``: Type that maps a SQL CLOB to a PHP array using
|
||||
``json_encode()`` and ``json_decode()``
|
||||
- ``float``: Type that maps a SQL Float (Double Precision) to a
|
||||
PHP double. *IMPORTANT*: Works only with locale settings that use
|
||||
decimal points as separator.
|
||||
- ``guid``: Type that maps a database GUID/UUID to a PHP string. Defaults to
|
||||
varchar but uses a specific type if the platform supports it.
|
||||
- ``blob``: Type that maps a SQL BLOB to a PHP resource stream
|
||||
|
||||
A cookbook article shows how to define :doc:`your own custom mapping types
|
||||
<../cookbook/custom-mapping-types>`.
|
||||
you are using.
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
@@ -812,7 +812,7 @@ classes have to implement the base class :
|
||||
namespace MyProject\Query\AST;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
class MysqlFloor extends FunctionNode
|
||||
{
|
||||
@@ -827,12 +827,12 @@ classes have to implement the base class :
|
||||
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1003,7 +1003,7 @@ The Query class
|
||||
---------------
|
||||
|
||||
An instance of the ``Doctrine\ORM\Query`` class represents a DQL
|
||||
query. You create a Query instance be calling
|
||||
query. You create a Query instance by calling
|
||||
``EntityManager#createQuery($dql)``, passing the DQL query string.
|
||||
Alternatively you can create an empty ``Query`` instance and invoke
|
||||
``Query#setDQL($dql)`` afterwards. Here are some examples:
|
||||
@@ -1020,58 +1020,146 @@ Alternatively you can create an empty ``Query`` instance and invoke
|
||||
$q = $em->createQuery();
|
||||
$q->setDQL('select u from MyProject\Model\User u');
|
||||
|
||||
Query Result Formats
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
Query Result Formats (Hydration Modes)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The format in which the result of a DQL SELECT query is returned
|
||||
can be influenced by a so-called ``hydration mode``. A hydration
|
||||
mode specifies a particular way in which a SQL result set is
|
||||
transformed. Each hydration mode has its own dedicated method on
|
||||
the Query class. Here they are:
|
||||
The way in which the SQL result set of a DQL SELECT query is transformed
|
||||
to PHP is determined by the so-called "hydration mode".
|
||||
|
||||
``getResult()``
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- ``Query#getResult()``: Retrieves a collection of objects. The
|
||||
result is either a plain collection of objects (pure) or an array
|
||||
where the objects are nested in the result rows (mixed).
|
||||
- ``Query#getSingleResult()``: Retrieves a single object. If the
|
||||
result contains more than one object, an ``NonUniqueResultException``
|
||||
is thrown. If the result contains no objects, an ``NoResultException``
|
||||
is thrown. The pure/mixed distinction does not apply.
|
||||
- ``Query#getOneOrNullResult()``: Retrieve a single object. If the
|
||||
result contains more than one object, a ``NonUniqueResultException``
|
||||
is thrown. If no object is found null will be returned.
|
||||
- ``Query#getArrayResult()``: Retrieves an array graph (a nested
|
||||
array) that is largely interchangeable with the object graph
|
||||
generated by ``Query#getResult()`` for read-only purposes.
|
||||
Retrieves a collection of objects. The result is either a plain collection of objects (pure) or an array
|
||||
where the objects are nested in the result rows (mixed):
|
||||
|
||||
.. note::
|
||||
.. code-block:: php
|
||||
|
||||
An array graph can differ from the corresponding object
|
||||
graph in certain scenarios due to the difference of the identity
|
||||
semantics between arrays and objects.
|
||||
<?php
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
|
||||
$query = $em->createQuery('SELECT u FROM User u');
|
||||
$users = $query->getResult();
|
||||
// same as:
|
||||
$users = $query->getResult(AbstractQuery::HYDRATE_OBJECT);
|
||||
|
||||
- Objects fetched in a FROM clause are returned as a Set, that means every
|
||||
object is only ever included in the resulting array once. This is the case
|
||||
even when using JOIN or GROUP BY in ways that return the same row for an
|
||||
object multiple times. If the hydrator sees the same object multiple times,
|
||||
then it makes sure it is only returned once.
|
||||
|
||||
- ``Query#getScalarResult()``: Retrieves a flat/rectangular result
|
||||
set of scalar values that can contain duplicate data. The
|
||||
pure/mixed distinction does not apply.
|
||||
- ``Query#getSingleScalarResult()``: Retrieves a single scalar
|
||||
value from the result returned by the dbms. If the result contains
|
||||
more than a single scalar value, an exception is thrown. The
|
||||
pure/mixed distinction does not apply.
|
||||
- If an object is already in memory from a previous query of any kind, then
|
||||
then the previous object is used, even if the database may contain more
|
||||
recent data. This even happens if the previous object is still an unloaded proxy.
|
||||
|
||||
Instead of using these methods, you can alternatively use the
|
||||
general-purpose method
|
||||
``Query#execute(array $params = [], $hydrationMode = Query::HYDRATE_OBJECT)``.
|
||||
Using this method you can directly supply the hydration mode as the
|
||||
second parameter via one of the Query constants. In fact, the
|
||||
methods mentioned earlier are just convenient shortcuts for the
|
||||
execute method. For example, the method ``Query#getResult()``
|
||||
internally invokes execute, passing in ``Query::HYDRATE_OBJECT`` as
|
||||
the hydration mode.
|
||||
``getArrayResult()``
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The use of the methods mentioned earlier is generally preferred as
|
||||
it leads to more concise code.
|
||||
Retrieves an array graph (a nested array) for read-only purposes.
|
||||
|
||||
.. note::
|
||||
|
||||
An array graph can differ from the corresponding object
|
||||
graph in certain scenarios due to the difference of the identity
|
||||
semantics between arrays and objects.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$users = $query->getArrayResult();
|
||||
// same as:
|
||||
$users = $query->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||
|
||||
``getScalarResult()``
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Retrieves a flat/rectangular result set of scalar values that can contain duplicate data. The
|
||||
pure/mixed distinction does not apply.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$users = $query->getScalarResult();
|
||||
// same as:
|
||||
$users = $query->getResult(AbstractQuery::HYDRATE_SCALAR);
|
||||
|
||||
Fields from classes are prefixed by the DQL alias in the result.
|
||||
A query of the kind `SELECT u.name ...` returns a key `u_name` in the result rows.
|
||||
|
||||
``getSingleScalarResult()``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Retrieves a single scalar value from the result returned by the database. If the result contains
|
||||
more than a single scalar value, a ``NonUniqueResultException`` is thrown. The pure/mixed distinction does not apply.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT COUNT(u.id) FROM User u');
|
||||
$numUsers = $query->getSingleScalarResult();
|
||||
// same as:
|
||||
$numUsers = $query->getResult(AbstractQuery::HYDRATE_SINGLE_SCALAR);
|
||||
|
||||
``getSingleColumnResult()``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Retrieves an array from a one-dimensional array of scalar values:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT a.id FROM User u');
|
||||
$ids = $query->getSingleColumnResult();
|
||||
// same as:
|
||||
$ids = $query->getResult(AbstractQuery::HYDRATE_SCALAR_COLUMN);
|
||||
|
||||
``getSingleResult()``
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Retrieves a single object. If the result contains more than one object, a ``NonUniqueResultException``
|
||||
is thrown. If the result contains no objects, a ``NoResultException`` is thrown. The pure/mixed distinction does not apply.
|
||||
|
||||
``getOneOrNullResult()``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Retrieves a single object. If the result contains more than one object, a ``NonUniqueResultException``
|
||||
is thrown. If no object is found, ``null`` will be returned.
|
||||
|
||||
Custom Hydration Modes
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can easily add your own custom hydration modes by first
|
||||
creating a class which extends ``AbstractHydrator``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace MyProject\Hydrators;
|
||||
|
||||
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
|
||||
|
||||
class CustomHydrator extends AbstractHydrator
|
||||
{
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
return $this->_stmt->fetchAllAssociative();
|
||||
}
|
||||
}
|
||||
|
||||
Next you just need to add the class to the ORM configuration:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em->getConfiguration()->addCustomHydrationMode('CustomHydrator', 'MyProject\Hydrators\CustomHydrator');
|
||||
|
||||
Now the hydrator is ready to be used in your queries:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$results = $query->getResult('CustomHydrator');
|
||||
|
||||
Pure and Mixed Results
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -1175,165 +1263,6 @@ will return the rows iterating the different top-level entities.
|
||||
[2] => Object (User)
|
||||
[3] => Object (Group)
|
||||
|
||||
|
||||
Hydration Modes
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Each of the Hydration Modes makes assumptions about how the result
|
||||
is returned to user land. You should know about all the details to
|
||||
make best use of the different result formats:
|
||||
|
||||
The constants for the different hydration modes are:
|
||||
|
||||
|
||||
- ``Query::HYDRATE_OBJECT``
|
||||
- ``Query::HYDRATE_ARRAY``
|
||||
- ``Query::HYDRATE_SCALAR``
|
||||
- ``Query::HYDRATE_SINGLE_SCALAR``
|
||||
- ``Query::HYDRATE_SCALAR_COLUMN``
|
||||
|
||||
Object Hydration
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Object hydration hydrates the result set into the object graph:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$users = $query->getResult(Query::HYDRATE_OBJECT);
|
||||
|
||||
Sometimes the behavior in the object hydrator can be confusing, which is
|
||||
why we are listing as many of the assumptions here for reference:
|
||||
|
||||
- Objects fetched in a FROM clause are returned as a Set, that means every
|
||||
object is only ever included in the resulting array once. This is the case
|
||||
even when using JOIN or GROUP BY in ways that return the same row for an
|
||||
object multiple times. If the hydrator sees the same object multiple times,
|
||||
then it makes sure it is only returned once.
|
||||
|
||||
- If an object is already in memory from a previous query of any kind, then
|
||||
then the previous object is used, even if the database may contain more
|
||||
recent data. Data from the database is discarded. This even happens if the
|
||||
previous object is still an unloaded proxy.
|
||||
|
||||
This list might be incomplete.
|
||||
|
||||
Array Hydration
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
You can run the same query with array hydration and the result set
|
||||
is hydrated into an array that represents the object graph:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$users = $query->getResult(Query::HYDRATE_ARRAY);
|
||||
|
||||
You can use the ``getArrayResult()`` shortcut as well:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$users = $query->getArrayResult();
|
||||
|
||||
Scalar Hydration
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
If you want to return a flat rectangular result set instead of an
|
||||
object graph you can use scalar hydration:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$users = $query->getResult(Query::HYDRATE_SCALAR);
|
||||
echo $users[0]['u_id'];
|
||||
|
||||
The following assumptions are made about selected fields using
|
||||
Scalar Hydration:
|
||||
|
||||
|
||||
1. Fields from classes are prefixed by the DQL alias in the result.
|
||||
A query of the kind 'SELECT u.name ..' returns a key 'u_name' in
|
||||
the result rows.
|
||||
|
||||
Single Scalar Hydration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you have a query which returns just a single scalar value you can use
|
||||
single scalar hydration:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT COUNT(a.id) FROM CmsUser u LEFT JOIN u.articles a WHERE u.username = ?1 GROUP BY u.id');
|
||||
$query->setParameter(1, 'jwage');
|
||||
$numArticles = $query->getResult(Query::HYDRATE_SINGLE_SCALAR);
|
||||
|
||||
You can use the ``getSingleScalarResult()`` shortcut as well:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$numArticles = $query->getSingleScalarResult();
|
||||
|
||||
Scalar Column Hydration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you have a query which returns a one-dimensional array of scalar values
|
||||
you can use scalar column hydration:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT a.id FROM CmsUser u');
|
||||
$ids = $query->getResult(Query::HYDRATE_SCALAR_COLUMN);
|
||||
|
||||
You can use the ``getSingleColumnResult()`` shortcut as well:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$ids = $query->getSingleColumnResult();
|
||||
|
||||
Custom Hydration Modes
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can easily add your own custom hydration modes by first
|
||||
creating a class which extends ``AbstractHydrator``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace MyProject\Hydrators;
|
||||
|
||||
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
|
||||
|
||||
class CustomHydrator extends AbstractHydrator
|
||||
{
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
return $this->_stmt->fetchAllAssociative();
|
||||
}
|
||||
}
|
||||
|
||||
Next you just need to add the class to the ORM configuration:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em->getConfiguration()->addCustomHydrationMode('CustomHydrator', 'MyProject\Hydrators\CustomHydrator');
|
||||
|
||||
Now the hydrator is ready to be used in your queries:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery('SELECT u FROM CmsUser u');
|
||||
$results = $query->getResult('CustomHydrator');
|
||||
|
||||
Iterating Large Result Sets
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
:orphan:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ requirement.
|
||||
|
||||
A more convenient alternative for explicit transaction demarcation is the use
|
||||
of provided control abstractions in the form of
|
||||
``Connection#transactional($func)`` and ``EntityManager#transactional($func)``.
|
||||
``Connection#transactional($func)`` and ``EntityManager#wrapInTransaction($func)``.
|
||||
When used, these control abstractions ensure that you never forget to rollback
|
||||
the transaction, in addition to the obvious code reduction. An example that is
|
||||
functionally equivalent to the previously shown code looks as follows:
|
||||
@@ -96,21 +96,23 @@ functionally equivalent to the previously shown code looks as follows:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// transactional with Connection instance
|
||||
// $conn instanceof Connection
|
||||
$conn->transactional(function($conn) {
|
||||
// ... do some work
|
||||
$user = new User;
|
||||
$user->setName('George');
|
||||
});
|
||||
|
||||
// transactional with EntityManager instance
|
||||
// $em instanceof EntityManager
|
||||
$em->transactional(function($em) {
|
||||
$em->wrapInTransaction(function($em) {
|
||||
// ... do some work
|
||||
$user = new User;
|
||||
$user->setName('George');
|
||||
$em->persist($user);
|
||||
});
|
||||
|
||||
.. warning::
|
||||
|
||||
For historical reasons, ``EntityManager#transactional($func)`` will return
|
||||
``true`` whenever the return value of ``$func`` is loosely false.
|
||||
Some examples of this include ``array()``, ``"0"``, ``""``, ``0``, and
|
||||
``null``.
|
||||
|
||||
The difference between ``Connection#transactional($func)`` and
|
||||
``EntityManager#transactional($func)`` is that the latter
|
||||
abstraction flushes the ``EntityManager`` prior to transaction
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
:orphan:
|
||||
|
||||
.. toc::
|
||||
|
||||
.. tocheader:: Tutorials
|
||||
|
||||
@@ -188,7 +188,7 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
|
||||
#[OneToMany(targetEntity: ArticleAttribute::class, mappedBy: 'article', cascade: ['ALL'], indexBy: 'attribute')]
|
||||
private Collection $attributes;
|
||||
|
||||
public function addAttribute(string $name, ArticleAttribute $value): void
|
||||
public function addAttribute(string $name, string $value): void
|
||||
{
|
||||
$this->attributes[$name] = new ArticleAttribute($name, $value, $this);
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ step:
|
||||
|
||||
// Create a simple "default" Doctrine ORM configuration for Attributes
|
||||
$config = ORMSetup::createAttributeMetadataConfiguration(
|
||||
paths: array(__DIR__."/src"),
|
||||
paths: [__DIR__ . '/src'],
|
||||
isDevMode: true,
|
||||
);
|
||||
// or if you prefer annotation, YAML or XML
|
||||
@@ -153,7 +153,7 @@ step:
|
||||
// isDevMode: true,
|
||||
// );
|
||||
// $config = ORMSetup::createXMLMetadataConfiguration(
|
||||
// paths: array(__DIR__."/config/xml"),
|
||||
// paths: [__DIR__ . '/config/xml'],
|
||||
// isDevMode: true,
|
||||
//);
|
||||
// $config = ORMSetup::createYAMLMetadataConfiguration(
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<file>src</file>
|
||||
<file>tests</file>
|
||||
|
||||
<exclude-pattern>*/src/Mapping/InverseJoinColumn.php</exclude-pattern>
|
||||
<exclude-pattern>*/tests/Tests/Proxies/__CG__*</exclude-pattern>
|
||||
<exclude-pattern>*/tests/Tests/ORM/Tools/Export/export/*</exclude-pattern>
|
||||
|
||||
|
||||
@@ -135,11 +135,6 @@ parameters:
|
||||
count: 2
|
||||
path: src/EntityManager.php
|
||||
|
||||
-
|
||||
message: "#^Template type T of method Doctrine\\\\ORM\\\\EntityManagerInterface\\:\\:getClassMetadata\\(\\) is not referenced in a parameter\\.$#"
|
||||
count: 1
|
||||
path: src/EntityManagerInterface.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\EntityRepository\\:\\:matching\\(\\) should return Doctrine\\\\Common\\\\Collections\\\\AbstractLazyCollection\\<int, T of object\\>&Doctrine\\\\Common\\\\Collections\\\\Selectable\\<int, T of object\\> but returns Doctrine\\\\ORM\\\\LazyCriteriaCollection\\<\\(int\\|string\\), object\\>\\.$#"
|
||||
count: 1
|
||||
|
||||
@@ -74,3 +74,9 @@ parameters:
|
||||
-
|
||||
message: '#^Call to method injectObjectManager\(\) on an unknown class Doctrine\\Persistence\\ObjectManagerAware\.$#'
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '#contains generic type.*but class.*is not generic#'
|
||||
paths:
|
||||
- src/Mapping/Driver/XmlDriver.php
|
||||
- src/Mapping/Driver/YamlDriver.php
|
||||
|
||||
@@ -64,3 +64,9 @@ parameters:
|
||||
|
||||
# Symfony cache supports passing a key prefix to the clear method.
|
||||
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
|
||||
|
||||
-
|
||||
message: '#contains generic type.*but class.*is not generic#'
|
||||
paths:
|
||||
- src/Mapping/Driver/XmlDriver.php
|
||||
- src/Mapping/Driver/YamlDriver.php
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<files psalm-version="5.22.2@d768d914152dbbf3486c36398802f74e80cfde48">
|
||||
<files psalm-version="5.24.0@462c80e31c34e58cc4f750c656be3927e80e550e">
|
||||
<file src="src/AbstractQuery.php">
|
||||
<DeprecatedClass>
|
||||
<code><![CDATA[IterableResult]]></code>
|
||||
@@ -257,12 +257,6 @@
|
||||
<code><![CDATA[transactional]]></code>
|
||||
<code><![CDATA[transactional]]></code>
|
||||
</DeprecatedMethod>
|
||||
<InvalidReturnStatement>
|
||||
<code><![CDATA[$this->wrapped->getClassMetadata($className)]]></code>
|
||||
</InvalidReturnStatement>
|
||||
<InvalidReturnType>
|
||||
<code><![CDATA[getClassMetadata]]></code>
|
||||
</InvalidReturnType>
|
||||
<MissingParamType>
|
||||
<code><![CDATA[$entity]]></code>
|
||||
<code><![CDATA[$lockMode]]></code>
|
||||
@@ -309,11 +303,9 @@
|
||||
<code><![CDATA[$entity instanceof $class->name ? $entity : null]]></code>
|
||||
<code><![CDATA[$persister->load($sortedId, null, null, [], $lockMode)]]></code>
|
||||
<code><![CDATA[$persister->loadById($sortedId)]]></code>
|
||||
<code><![CDATA[$this->metadataFactory->getMetadataFor($className)]]></code>
|
||||
</InvalidReturnStatement>
|
||||
<InvalidReturnType>
|
||||
<code><![CDATA[?T]]></code>
|
||||
<code><![CDATA[getClassMetadata]]></code>
|
||||
</InvalidReturnType>
|
||||
<MissingReturnType>
|
||||
<code><![CDATA[wrapInTransaction]]></code>
|
||||
@@ -388,6 +380,9 @@
|
||||
<code><![CDATA[serialize]]></code>
|
||||
<code><![CDATA[unserialize]]></code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<ParamNameMismatch>
|
||||
<code><![CDATA[$serialized]]></code>
|
||||
</ParamNameMismatch>
|
||||
</file>
|
||||
<file src="src/Id/TableGenerator.php">
|
||||
<PossiblyFalseOperand>
|
||||
@@ -462,9 +457,6 @@
|
||||
</RedundantConditionGivenDocblockType>
|
||||
</file>
|
||||
<file src="src/Internal/Hydration/ObjectHydrator.php">
|
||||
<InvalidArgument>
|
||||
<code><![CDATA[$element]]></code>
|
||||
</InvalidArgument>
|
||||
<PossiblyFalseArgument>
|
||||
<code><![CDATA[$index]]></code>
|
||||
</PossiblyFalseArgument>
|
||||
@@ -940,13 +932,8 @@
|
||||
<InvalidPropertyAssignmentValue>
|
||||
<code><![CDATA[$metadata->table]]></code>
|
||||
</InvalidPropertyAssignmentValue>
|
||||
<InvalidPropertyFetch>
|
||||
<code><![CDATA[$xmlRoot->{'discriminator-column'}]]></code>
|
||||
<code><![CDATA[$xmlRoot->{'discriminator-map'}]]></code>
|
||||
</InvalidPropertyFetch>
|
||||
<InvalidReturnStatement>
|
||||
<code><![CDATA[$mapping]]></code>
|
||||
<code><![CDATA[$result]]></code>
|
||||
<code><![CDATA[[
|
||||
'usage' => $usage,
|
||||
'region' => $region,
|
||||
@@ -970,7 +957,6 @@
|
||||
* options?: array
|
||||
* }]]></code>
|
||||
<code><![CDATA[array{usage: int|null, region?: string}]]></code>
|
||||
<code><![CDATA[loadMappingFile]]></code>
|
||||
</InvalidReturnType>
|
||||
<MissingParamType>
|
||||
<code><![CDATA[$fileExtension]]></code>
|
||||
@@ -979,15 +965,6 @@
|
||||
<MoreSpecificImplementedParamType>
|
||||
<code><![CDATA[$metadata]]></code>
|
||||
</MoreSpecificImplementedParamType>
|
||||
<NoInterfaceProperties>
|
||||
<code><![CDATA[$xmlRoot->{'discriminator-column'}]]></code>
|
||||
<code><![CDATA[$xmlRoot->{'discriminator-map'}]]></code>
|
||||
</NoInterfaceProperties>
|
||||
<TypeDoesNotContainType>
|
||||
<code><![CDATA[$xmlRoot->getName() === 'embeddable']]></code>
|
||||
<code><![CDATA[$xmlRoot->getName() === 'entity']]></code>
|
||||
<code><![CDATA[$xmlRoot->getName() === 'mapped-superclass']]></code>
|
||||
</TypeDoesNotContainType>
|
||||
</file>
|
||||
<file src="src/Mapping/Driver/YamlDriver.php">
|
||||
<ArgumentTypeCoercion>
|
||||
@@ -1019,38 +996,6 @@
|
||||
<MoreSpecificReturnType>
|
||||
<code><![CDATA[array{usage: int|null, region: string|null}]]></code>
|
||||
</MoreSpecificReturnType>
|
||||
<PossiblyUndefinedMethod>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
</PossiblyUndefinedMethod>
|
||||
<UndefinedInterfaceMethod>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
<code><![CDATA[$element]]></code>
|
||||
</UndefinedInterfaceMethod>
|
||||
</file>
|
||||
<file src="src/Mapping/Embedded.php">
|
||||
<MissingParamType>
|
||||
@@ -1492,6 +1437,7 @@
|
||||
<code><![CDATA[getValue]]></code>
|
||||
<code><![CDATA[setAccessible]]></code>
|
||||
<code><![CDATA[setValue]]></code>
|
||||
<code><![CDATA[setValue]]></code>
|
||||
</PossiblyNullReference>
|
||||
<TypeDoesNotContainType>
|
||||
<code><![CDATA[$autoGenerate < 0]]></code>
|
||||
@@ -1501,13 +1447,10 @@
|
||||
<code><![CDATA[__wakeup]]></code>
|
||||
</UndefinedInterfaceMethod>
|
||||
<UndefinedMethod>
|
||||
<code><![CDATA[self::createLazyGhost(static function (InternalProxy $object) use ($initializer, &$proxy): void {
|
||||
$initializer($object, $proxy);
|
||||
<code><![CDATA[self::createLazyGhost(static function (InternalProxy $object) use ($initializer, $identifier): void {
|
||||
$initializer($object, $identifier);
|
||||
}, $skippedProperties)]]></code>
|
||||
</UndefinedMethod>
|
||||
<UndefinedVariable>
|
||||
<code><![CDATA[$proxy]]></code>
|
||||
</UndefinedVariable>
|
||||
<UnresolvableInclude>
|
||||
<code><![CDATA[require $fileName]]></code>
|
||||
</UnresolvableInclude>
|
||||
@@ -2108,7 +2051,7 @@
|
||||
</PossiblyUndefinedVariable>
|
||||
<RedundantConditionGivenDocblockType>
|
||||
<code><![CDATA[$AST instanceof AST\SelectStatement]]></code>
|
||||
<code><![CDATA[$token === Lexer::T_IDENTIFIER]]></code>
|
||||
<code><![CDATA[$token === TokenType::T_IDENTIFIER]]></code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
</file>
|
||||
<file src="src/Query/ParserResult.php">
|
||||
@@ -2356,9 +2299,6 @@
|
||||
<InvalidReturnType>
|
||||
<code><![CDATA[ObjectRepository]]></code>
|
||||
</InvalidReturnType>
|
||||
<TypeDoesNotContainType>
|
||||
<code><![CDATA[$repository instanceof EntityRepository]]></code>
|
||||
</TypeDoesNotContainType>
|
||||
<UnsafeInstantiation>
|
||||
<code><![CDATA[new $repositoryClassName($entityManager, $metadata)]]></code>
|
||||
</UnsafeInstantiation>
|
||||
@@ -2529,6 +2469,11 @@
|
||||
<PossiblyNullArgument>
|
||||
<code><![CDATA[$variableType]]></code>
|
||||
</PossiblyNullArgument>
|
||||
<PossiblyUndefinedArrayOffset>
|
||||
<code><![CDATA[$fieldMapping['declaredField']]]></code>
|
||||
<code><![CDATA[$fieldMapping['declaredField']]]></code>
|
||||
<code><![CDATA[$fieldMapping['declaredField']]]></code>
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
<PropertyNotSetInConstructor>
|
||||
<code><![CDATA[$classToExtend]]></code>
|
||||
</PropertyNotSetInConstructor>
|
||||
|
||||
@@ -340,7 +340,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
* @psalm-param string|class-string<T> $className
|
||||
*
|
||||
* @return Mapping\ClassMetadata
|
||||
* @psalm-return Mapping\ClassMetadata<T>
|
||||
* @psalm-return ($className is class-string<T> ? Mapping\ClassMetadata<T> : Mapping\ClassMetadata<object>)
|
||||
*
|
||||
* @psalm-template T of object
|
||||
*/
|
||||
|
||||
@@ -182,29 +182,31 @@ abstract class AbstractHydrator
|
||||
|
||||
$this->prepare();
|
||||
|
||||
while (true) {
|
||||
$row = $this->statement()->fetchAssociative();
|
||||
try {
|
||||
while (true) {
|
||||
$row = $this->statement()->fetchAssociative();
|
||||
|
||||
if ($row === false) {
|
||||
$this->cleanup();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
$this->hydrateRowData($row, $result);
|
||||
|
||||
$this->cleanupAfterRowIteration();
|
||||
if (count($result) === 1) {
|
||||
if (count($resultSetMapping->indexByMap) === 0) {
|
||||
yield end($result);
|
||||
} else {
|
||||
yield from $result;
|
||||
if ($row === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
$this->hydrateRowData($row, $result);
|
||||
|
||||
$this->cleanupAfterRowIteration();
|
||||
if (count($result) === 1) {
|
||||
if (count($resultSetMapping->indexByMap) === 0) {
|
||||
yield end($result);
|
||||
} else {
|
||||
yield from $result;
|
||||
}
|
||||
} else {
|
||||
yield $result;
|
||||
}
|
||||
} else {
|
||||
yield $result;
|
||||
}
|
||||
} finally {
|
||||
$this->cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -367,11 +367,15 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$parentObject = $this->resultPointers[$parentAlias];
|
||||
} else {
|
||||
// Parent object of relation not found, mark as not-fetched again
|
||||
$element = $this->getEntity($data, $dqlAlias);
|
||||
if (isset($nonemptyComponents[$dqlAlias])) {
|
||||
$element = $this->getEntity($data, $dqlAlias);
|
||||
|
||||
// Update result pointer and provide initial fetch data for parent
|
||||
$this->resultPointers[$dqlAlias] = $element;
|
||||
$rowData['data'][$parentAlias][$relationField] = $element;
|
||||
// Update result pointer and provide initial fetch data for parent
|
||||
$this->resultPointers[$dqlAlias] = $element;
|
||||
$rowData['data'][$parentAlias][$relationField] = $element;
|
||||
} else {
|
||||
$element = null;
|
||||
}
|
||||
|
||||
// Mark as not-fetched again
|
||||
unset($this->_hints['fetched'][$parentAlias][$relationField]);
|
||||
|
||||
@@ -76,11 +76,11 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Do an optimized search of an element
|
||||
*
|
||||
* @template TMaybeContained
|
||||
* @param mixed $element The element to search for.
|
||||
*
|
||||
* @return bool TRUE if the collection contains $element, FALSE otherwise.
|
||||
*/
|
||||
public function contains($element)
|
||||
{
|
||||
|
||||
@@ -3681,6 +3681,17 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public function getAssociationMappedByTargetField($fieldName)
|
||||
{
|
||||
if (! $this->isAssociationInverseSide($fieldName)) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/11309',
|
||||
'Calling %s with owning side field %s is deprecated and will no longer be supported in Doctrine ORM 3.0. Call %s::isAssociationInverseSide() to check first.',
|
||||
__METHOD__,
|
||||
$fieldName,
|
||||
self::class
|
||||
);
|
||||
}
|
||||
|
||||
return $this->associationMappings[$fieldName]['mappedBy'];
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ use function strtoupper;
|
||||
* XmlDriver is a metadata driver that enables mapping through XML files.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*
|
||||
* @template-extends FileDriver<SimpleXMLElement>
|
||||
*/
|
||||
class XmlDriver extends FileDriver
|
||||
{
|
||||
@@ -79,7 +81,6 @@ class XmlDriver extends FileDriver
|
||||
public function loadMetadataForClass($className, PersistenceClassMetadata $metadata)
|
||||
{
|
||||
$xmlRoot = $this->getElement($className);
|
||||
assert($xmlRoot instanceof SimpleXMLElement);
|
||||
|
||||
if ($xmlRoot->getName() === 'entity') {
|
||||
if (isset($xmlRoot['repository-class'])) {
|
||||
@@ -203,6 +204,7 @@ class XmlDriver extends FileDriver
|
||||
];
|
||||
|
||||
if (isset($discrColumn['options'])) {
|
||||
assert($discrColumn['options'] instanceof SimpleXMLElement);
|
||||
$columnDef['options'] = $this->parseOptions($discrColumn['options']->children());
|
||||
}
|
||||
|
||||
@@ -214,6 +216,7 @@ class XmlDriver extends FileDriver
|
||||
// Evaluate <discriminator-map...>
|
||||
if (isset($xmlRoot->{'discriminator-map'})) {
|
||||
$map = [];
|
||||
assert($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} instanceof SimpleXMLElement);
|
||||
foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) {
|
||||
$map[(string) $discrMapElement['value']] = (string) $discrMapElement['class'];
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ use function substr;
|
||||
* The YamlDriver reads the mapping metadata from yaml schema files.
|
||||
*
|
||||
* @deprecated 2.7 This class is being removed from the ORM and won't have any replacement
|
||||
*
|
||||
* @template-extends FileDriver<array<string, mixed>>
|
||||
*/
|
||||
class YamlDriver extends FileDriver
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
@@ -5,11 +5,31 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\Common\Cache\Cache as CacheDriver;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use Doctrine\ORM\Cache\Exception\InvalidResultCacheDriver;
|
||||
use Doctrine\ORM\Cache\Exception\MetadataCacheNotConfigured;
|
||||
use Doctrine\ORM\Cache\Exception\MetadataCacheUsesNonPersistentCache;
|
||||
use Doctrine\ORM\Cache\Exception\QueryCacheNotConfigured;
|
||||
use Doctrine\ORM\Cache\Exception\QueryCacheUsesNonPersistentCache;
|
||||
use Doctrine\ORM\Exception\EntityManagerClosed;
|
||||
use Doctrine\ORM\Exception\InvalidEntityRepository;
|
||||
use Doctrine\ORM\Exception\InvalidHydrationMode;
|
||||
use Doctrine\ORM\Exception\MismatchedEventManager;
|
||||
use Doctrine\ORM\Exception\MissingIdentifierField;
|
||||
use Doctrine\ORM\Exception\MissingMappingDriverImplementation;
|
||||
use Doctrine\ORM\Exception\NamedNativeQueryNotFound;
|
||||
use Doctrine\ORM\Exception\NamedQueryNotFound;
|
||||
use Doctrine\ORM\Exception\ProxyClassesAlwaysRegenerating;
|
||||
use Doctrine\ORM\Exception\UnexpectedAssociationValue;
|
||||
use Doctrine\ORM\Exception\UnknownEntityNamespace;
|
||||
use Doctrine\ORM\Exception\UnrecognizedIdentifierFields;
|
||||
use Doctrine\ORM\Persisters\Exception\CantUseInOperatorOnCompositeKeys;
|
||||
use Doctrine\ORM\Persisters\Exception\InvalidOrientation;
|
||||
use Doctrine\ORM\Persisters\Exception\UnrecognizedField;
|
||||
use Doctrine\ORM\Repository\Exception\InvalidFindByCall;
|
||||
use Doctrine\ORM\Repository\Exception\InvalidMagicMethodCall;
|
||||
use Doctrine\ORM\Tools\Exception\NotSupported;
|
||||
use Exception;
|
||||
|
||||
use function get_debug_type;
|
||||
use function implode;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
@@ -26,8 +46,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function missingMappingDriverImpl()
|
||||
{
|
||||
return new self("It's a requirement to specify a Metadata Driver and pass it " .
|
||||
'to Doctrine\\ORM\\Configuration::setMetadataDriverImpl().');
|
||||
return MissingMappingDriverImplementation::create();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,11 +58,11 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function namedQueryNotFound($queryName)
|
||||
{
|
||||
return new self('Could not find a named query by the name "' . $queryName . '"');
|
||||
return NamedQueryNotFound::fromName($queryName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use Doctrine\ORM\Exception\NamedQueryNotFound
|
||||
* @deprecated Use Doctrine\ORM\Exception\NamedNativeQueryNotFound
|
||||
*
|
||||
* @param string $nativeQueryName
|
||||
*
|
||||
@@ -51,7 +70,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function namedNativeQueryNotFound($nativeQueryName)
|
||||
{
|
||||
return new self('Could not find a named native query by the name "' . $nativeQueryName . '"');
|
||||
return NamedNativeQueryNotFound::fromName($nativeQueryName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +82,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function unrecognizedField($field)
|
||||
{
|
||||
return new self(sprintf('Unrecognized field: %s', $field));
|
||||
return new UnrecognizedField(sprintf('Unrecognized field: %s', $field));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,7 +97,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function unexpectedAssociationValue($class, $association, $given, $expected)
|
||||
{
|
||||
return new self(sprintf('Found entity of type %s on association %s#%s, but expecting %s', $given, $class, $association, $expected));
|
||||
return UnexpectedAssociationValue::create($class, $association, $given, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,7 +110,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function invalidOrientation($className, $field)
|
||||
{
|
||||
return new self('Invalid order by orientation specified for ' . $className . '#' . $field);
|
||||
return InvalidOrientation::fromClassNameAndField($className, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +120,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function entityManagerClosed()
|
||||
{
|
||||
return new self('The EntityManager is closed.');
|
||||
return EntityManagerClosed::create();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,7 +132,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function invalidHydrationMode($mode)
|
||||
{
|
||||
return new self(sprintf("'%s' is an invalid hydration mode.", $mode));
|
||||
return InvalidHydrationMode::fromMode($mode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,7 +142,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function mismatchedEventManager()
|
||||
{
|
||||
return new self('Cannot use different EventManager instances for EntityManager and Connection.');
|
||||
return MismatchedEventManager::create();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,11 +154,11 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function findByRequiresParameter($methodName)
|
||||
{
|
||||
return new self("You need to pass a parameter to '" . $methodName . "'");
|
||||
return InvalidMagicMethodCall::onMissingParameter($methodName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Doctrine\ORM\Repository\Exception\InvalidFindByCall
|
||||
* @deprecated Doctrine\ORM\Repository\Exception\InvalidMagicMethodCall::becauseFieldNotFoundIn()
|
||||
*
|
||||
* @param string $entityName
|
||||
* @param string $fieldName
|
||||
@@ -149,10 +168,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function invalidMagicCall($entityName, $fieldName, $method)
|
||||
{
|
||||
return new self(
|
||||
"Entity '" . $entityName . "' has no field '" . $fieldName . "'. " .
|
||||
"You can therefore not call '" . $method . "' on the entities' repository"
|
||||
);
|
||||
return InvalidMagicMethodCall::becauseFieldNotFoundIn($entityName, $fieldName, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,10 +181,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function invalidFindByInverseAssociation($entityName, $associationFieldName)
|
||||
{
|
||||
return new self(
|
||||
"You cannot search for the association field '" . $entityName . '#' . $associationFieldName . "', " .
|
||||
'because it is the inverse side of an association. Find methods only work on owning side associations.'
|
||||
);
|
||||
return InvalidFindByCall::fromInverseSideUsage($entityName, $associationFieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,7 +191,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function invalidResultCacheDriver()
|
||||
{
|
||||
return new self('Invalid result cache driver; it must implement Doctrine\\Common\\Cache\\Cache.');
|
||||
return InvalidResultCacheDriver::create();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,7 +201,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function notSupported()
|
||||
{
|
||||
return new self('This behaviour is (currently) not supported by Doctrine 2');
|
||||
return NotSupported::create();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,7 +211,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function queryCacheNotConfigured()
|
||||
{
|
||||
return new self('Query Cache is not configured.');
|
||||
return QueryCacheNotConfigured::create();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,7 +221,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function metadataCacheNotConfigured()
|
||||
{
|
||||
return new self('Class Metadata Cache is not configured.');
|
||||
return MetadataCacheNotConfigured::create();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,7 +231,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function queryCacheUsesNonPersistentCache(CacheDriver $cache)
|
||||
{
|
||||
return new self('Query Cache uses a non-persistent cache driver, ' . get_debug_type($cache) . '.');
|
||||
return QueryCacheUsesNonPersistentCache::fromDriver($cache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,7 +241,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function metadataCacheUsesNonPersistentCache(CacheDriver $cache)
|
||||
{
|
||||
return new self('Metadata Cache uses a non-persistent cache driver, ' . get_debug_type($cache) . '.');
|
||||
return MetadataCacheUsesNonPersistentCache::fromDriver($cache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -238,7 +251,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function proxyClassesAlwaysRegenerating()
|
||||
{
|
||||
return new self('Proxy Classes are always regenerating.');
|
||||
return ProxyClassesAlwaysRegenerating::create();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,9 +263,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function unknownEntityNamespace($entityNamespaceAlias)
|
||||
{
|
||||
return new self(
|
||||
sprintf("Unknown Entity namespace alias '%s'.", $entityNamespaceAlias)
|
||||
);
|
||||
return UnknownEntityNamespace::fromNamespaceAlias($entityNamespaceAlias);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,11 +275,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function invalidEntityRepository($className)
|
||||
{
|
||||
return new self(sprintf(
|
||||
"Invalid repository class '%s'. It must be a %s.",
|
||||
$className,
|
||||
ObjectRepository::class
|
||||
));
|
||||
return InvalidEntityRepository::fromClassName($className);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,7 +288,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function missingIdentifierField($className, $fieldName)
|
||||
{
|
||||
return new self(sprintf('The identifier %s is missing for a query of %s', $fieldName, $className));
|
||||
return MissingIdentifierField::fromFieldAndClass($fieldName, $className);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -294,10 +301,7 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function unrecognizedIdentifierFields($className, $fieldNames)
|
||||
{
|
||||
return new self(
|
||||
"Unrecognized identifier fields: '" . implode("', '", $fieldNames) . "' " .
|
||||
"are not present on class '" . $className . "'."
|
||||
);
|
||||
return UnrecognizedIdentifierFields::fromClassAndFieldNames($className, $fieldNames);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,6 +311,6 @@ class ORMException extends Exception
|
||||
*/
|
||||
public static function cantUseInOperatorOnCompositeKeys()
|
||||
{
|
||||
return new self("Can't use IN operator on entities that have composite keys.");
|
||||
return CantUseInOperatorOnCompositeKeys::create();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,11 +412,6 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
|
||||
return parent::containsKey($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @template TMaybeContained
|
||||
*/
|
||||
public function contains($element): bool
|
||||
{
|
||||
if (! $this->initialized && $this->getMapping()['fetch'] === ClassMetadata::FETCH_EXTRA_LAZY) {
|
||||
|
||||
@@ -8,6 +8,8 @@ use BadMethodCallException;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\DBAL\Exception as DBALException;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\ORM\EntityNotFoundException;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Utility\PersisterHelper;
|
||||
|
||||
@@ -166,7 +168,11 @@ class OneToManyPersister extends AbstractCollectionPersister
|
||||
throw new BadMethodCallException('Filtering a collection by Criteria is not supported by this CollectionPersister.');
|
||||
}
|
||||
|
||||
/** @throws DBALException */
|
||||
/**
|
||||
* @throws DBALException
|
||||
* @throws EntityNotFoundException
|
||||
* @throws MappingException
|
||||
*/
|
||||
private function deleteEntityCollection(PersistentCollection $collection): int
|
||||
{
|
||||
$mapping = $collection->getMapping();
|
||||
@@ -186,6 +192,13 @@ class OneToManyPersister extends AbstractCollectionPersister
|
||||
$statement = 'DELETE FROM ' . $this->quoteStrategy->getTableName($targetClass, $this->platform)
|
||||
. ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
|
||||
|
||||
if ($targetClass->isInheritanceTypeSingleTable()) {
|
||||
$discriminatorColumn = $targetClass->getDiscriminatorColumn();
|
||||
$statement .= ' AND ' . $discriminatorColumn['name'] . ' = ?';
|
||||
$parameters[] = $targetClass->discriminatorValue;
|
||||
$types[] = $discriminatorColumn['type'];
|
||||
}
|
||||
|
||||
$numAffected = $this->conn->executeStatement($statement, $parameters, $types);
|
||||
|
||||
assert(is_int($numAffected));
|
||||
|
||||
@@ -354,17 +354,16 @@ EOPHP;
|
||||
/**
|
||||
* Creates a closure capable of initializing a proxy
|
||||
*
|
||||
* @return Closure(InternalProxy, InternalProxy):void
|
||||
* @return Closure(InternalProxy, array):void
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
*/
|
||||
private function createLazyInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister, IdentifierFlattener $identifierFlattener): Closure
|
||||
{
|
||||
return static function (InternalProxy $proxy, InternalProxy $original) use ($entityPersister, $classMetadata, $identifierFlattener): void {
|
||||
$identifier = $classMetadata->getIdentifierValues($original);
|
||||
$entity = $entityPersister->loadById($identifier, $original);
|
||||
return static function (InternalProxy $proxy, array $identifier) use ($entityPersister, $classMetadata, $identifierFlattener): void {
|
||||
$original = $entityPersister->loadById($identifier);
|
||||
|
||||
if ($entity === null) {
|
||||
if ($original === null) {
|
||||
throw EntityNotFoundException::fromClassNameAndIdentifier(
|
||||
$classMetadata->getName(),
|
||||
$identifierFlattener->flattenIdentifier($classMetadata, $identifier)
|
||||
@@ -378,11 +377,11 @@ EOPHP;
|
||||
$class = $entityPersister->getClassMetadata();
|
||||
|
||||
foreach ($class->getReflectionProperties() as $property) {
|
||||
if (! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) {
|
||||
if (isset($identifier[$property->name]) || ! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$property->setValue($proxy, $property->getValue($entity));
|
||||
$property->setValue($proxy, $property->getValue($original));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -468,8 +467,8 @@ EOPHP;
|
||||
$identifierFields = array_intersect_key($class->getReflectionProperties(), $identifiers);
|
||||
|
||||
$proxyFactory = Closure::bind(static function (array $identifier) use ($initializer, $skippedProperties, $identifierFields, $className): InternalProxy {
|
||||
$proxy = self::createLazyGhost(static function (InternalProxy $object) use ($initializer, &$proxy): void {
|
||||
$initializer($object, $proxy);
|
||||
$proxy = self::createLazyGhost(static function (InternalProxy $object) use ($initializer, $identifier): void {
|
||||
$initializer($object, $identifier);
|
||||
}, $skippedProperties);
|
||||
|
||||
foreach ($identifierFields as $idField => $reflector) {
|
||||
|
||||
@@ -5,9 +5,9 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
/**
|
||||
* "ABS" "(" SimpleArithmeticExpression ")"
|
||||
@@ -30,11 +30,11 @@ class AbsFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Node;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
/**
|
||||
* "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")"
|
||||
@@ -36,13 +36,13 @@ class BitAndFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->firstArithmetic = $parser->ArithmeticPrimary();
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
$this->secondArithmetic = $parser->ArithmeticPrimary();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Node;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
/**
|
||||
* "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")"
|
||||
@@ -36,13 +36,13 @@ class BitOrFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->firstArithmetic = $parser->ArithmeticPrimary();
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
$this->secondArithmetic = $parser->ArithmeticPrimary();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Node;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
/**
|
||||
* "CONCAT" "(" StringPrimary "," StringPrimary {"," StringPrimary }* ")"
|
||||
@@ -42,22 +42,22 @@ class ConcatFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->firstStringPrimary = $parser->StringPrimary();
|
||||
$this->concatExpressions[] = $this->firstStringPrimary;
|
||||
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
|
||||
$this->secondStringPrimary = $parser->StringPrimary();
|
||||
$this->concatExpressions[] = $this->secondStringPrimary;
|
||||
|
||||
while ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) {
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
while ($parser->getLexer()->isNextToken(TokenType::T_COMMA)) {
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
$this->concatExpressions[] = $parser->StringPrimary();
|
||||
}
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
/**
|
||||
* "CURRENT_DATE"
|
||||
@@ -24,8 +24,8 @@ class CurrentDateFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
/**
|
||||
* "CURRENT_TIME"
|
||||
@@ -24,8 +24,8 @@ class CurrentTimeFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
/**
|
||||
* "CURRENT_TIMESTAMP"
|
||||
@@ -24,8 +24,8 @@ class CurrentTimestampFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Node;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
use function strtolower;
|
||||
|
||||
@@ -84,15 +84,15 @@ class DateAddFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->firstDateExpression = $parser->ArithmeticPrimary();
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
$this->intervalExpression = $parser->ArithmeticPrimary();
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
$this->unit = $parser->StringPrimary();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Node;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
/**
|
||||
* "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")"
|
||||
@@ -34,13 +34,13 @@ class DateDiffFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->date1 = $parser->ArithmeticPrimary();
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
$this->date2 = $parser->ArithmeticPrimary();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\AST\PathExpression;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
use function assert;
|
||||
use function reset;
|
||||
@@ -77,20 +77,20 @@ class IdentityFunction extends FunctionNode
|
||||
*/
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->pathExpression = $parser->SingleValuedAssociationPathExpression();
|
||||
|
||||
if ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) {
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(Lexer::T_STRING);
|
||||
if ($parser->getLexer()->isNextToken(TokenType::T_COMMA)) {
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
$parser->match(TokenType::T_STRING);
|
||||
|
||||
$token = $parser->getLexer()->token;
|
||||
assert($token !== null);
|
||||
$this->fieldMapping = $token->value;
|
||||
}
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Query\AST\Node;
|
||||
use Doctrine\ORM\Query\AST\TypedExpression;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
/**
|
||||
* "LENGTH" "(" StringPrimary ")"
|
||||
@@ -33,12 +33,12 @@ class LengthFunction extends FunctionNode implements TypedExpression
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->stringPrimary = $parser->StringPrimary();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
|
||||
public function getReturnType(): Type
|
||||
|
||||
@@ -6,9 +6,9 @@ namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Node;
|
||||
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
/**
|
||||
* "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")"
|
||||
@@ -48,22 +48,22 @@ class LocateFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->firstStringPrimary = $parser->StringPrimary();
|
||||
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
|
||||
$this->secondStringPrimary = $parser->StringPrimary();
|
||||
|
||||
$lexer = $parser->getLexer();
|
||||
if ($lexer->isNextToken(Lexer::T_COMMA)) {
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
if ($lexer->isNextToken(TokenType::T_COMMA)) {
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
|
||||
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
|
||||
}
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Node;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
@@ -33,11 +33,11 @@ class LowerFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->stringPrimary = $parser->StringPrimary();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
/**
|
||||
* "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")"
|
||||
@@ -34,15 +34,15 @@ class ModFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();
|
||||
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
|
||||
$this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Query\AST\PathExpression;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
use function assert;
|
||||
|
||||
@@ -105,11 +105,11 @@ class SizeFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->collectionPathExpression = $parser->CollectionValuedPathExpression();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
@@ -33,11 +33,11 @@ class SqrtFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Node;
|
||||
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
/**
|
||||
* "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")"
|
||||
@@ -44,22 +44,22 @@ class SubstringFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->stringPrimary = $parser->StringPrimary();
|
||||
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
|
||||
$this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();
|
||||
|
||||
$lexer = $parser->getLexer();
|
||||
if ($lexer->isNextToken(Lexer::T_COMMA)) {
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
if ($lexer->isNextToken(TokenType::T_COMMA)) {
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
|
||||
$this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();
|
||||
}
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\DBAL\Platforms\TrimMode;
|
||||
use Doctrine\ORM\Query\AST\Node;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
use function assert;
|
||||
use function strcasecmp;
|
||||
@@ -62,25 +62,25 @@ class TrimFunction extends FunctionNode
|
||||
{
|
||||
$lexer = $parser->getLexer();
|
||||
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->parseTrimMode($parser);
|
||||
|
||||
if ($lexer->isNextToken(Lexer::T_STRING)) {
|
||||
$parser->match(Lexer::T_STRING);
|
||||
if ($lexer->isNextToken(TokenType::T_STRING)) {
|
||||
$parser->match(TokenType::T_STRING);
|
||||
|
||||
assert($lexer->token !== null);
|
||||
$this->trimChar = $lexer->token->value;
|
||||
}
|
||||
|
||||
if ($this->leading || $this->trailing || $this->both || ($this->trimChar !== false)) {
|
||||
$parser->match(Lexer::T_FROM);
|
||||
$parser->match(TokenType::T_FROM);
|
||||
}
|
||||
|
||||
$this->stringPrimary = $parser->StringPrimary();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
|
||||
/** @psalm-return TrimMode::* */
|
||||
@@ -108,7 +108,7 @@ class TrimFunction extends FunctionNode
|
||||
$value = $lexer->lookahead->value;
|
||||
|
||||
if (strcasecmp('leading', $value) === 0) {
|
||||
$parser->match(Lexer::T_LEADING);
|
||||
$parser->match(TokenType::T_LEADING);
|
||||
|
||||
$this->leading = true;
|
||||
|
||||
@@ -116,7 +116,7 @@ class TrimFunction extends FunctionNode
|
||||
}
|
||||
|
||||
if (strcasecmp('trailing', $value) === 0) {
|
||||
$parser->match(Lexer::T_TRAILING);
|
||||
$parser->match(TokenType::T_TRAILING);
|
||||
|
||||
$this->trailing = true;
|
||||
|
||||
@@ -124,7 +124,7 @@ class TrimFunction extends FunctionNode
|
||||
}
|
||||
|
||||
if (strcasecmp('both', $value) === 0) {
|
||||
$parser->match(Lexer::T_BOTH);
|
||||
$parser->match(TokenType::T_BOTH);
|
||||
|
||||
$this->both = true;
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Node;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
@@ -33,11 +33,11 @@ class UpperFunction extends FunctionNode
|
||||
/** @inheritDoc */
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->stringPrimary = $parser->StringPrimary();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,95 +21,249 @@ use function substr;
|
||||
/**
|
||||
* Scans a DQL query for tokens.
|
||||
*
|
||||
* @extends AbstractLexer<Lexer::T_*, string>
|
||||
* @extends AbstractLexer<TokenType::T_*, string>
|
||||
*/
|
||||
class Lexer extends AbstractLexer
|
||||
{
|
||||
// All tokens that are not valid identifiers must be < 100
|
||||
public const T_NONE = 1;
|
||||
public const T_INTEGER = 2;
|
||||
public const T_STRING = 3;
|
||||
public const T_INPUT_PARAMETER = 4;
|
||||
public const T_FLOAT = 5;
|
||||
public const T_CLOSE_PARENTHESIS = 6;
|
||||
public const T_OPEN_PARENTHESIS = 7;
|
||||
public const T_COMMA = 8;
|
||||
public const T_DIVIDE = 9;
|
||||
public const T_DOT = 10;
|
||||
public const T_EQUALS = 11;
|
||||
public const T_GREATER_THAN = 12;
|
||||
public const T_LOWER_THAN = 13;
|
||||
public const T_MINUS = 14;
|
||||
public const T_MULTIPLY = 15;
|
||||
public const T_NEGATE = 16;
|
||||
public const T_PLUS = 17;
|
||||
public const T_OPEN_CURLY_BRACE = 18;
|
||||
public const T_CLOSE_CURLY_BRACE = 19;
|
||||
/** @deprecated use {@see TokenType::T_NONE} */
|
||||
public const T_NONE = TokenType::T_NONE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_INTEGER} */
|
||||
public const T_INTEGER = TokenType::T_INTEGER;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_STRING} */
|
||||
public const T_STRING = TokenType::T_STRING;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_INPUT_PARAMETER} */
|
||||
public const T_INPUT_PARAMETER = TokenType::T_INPUT_PARAMETER;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_FLOAT} */
|
||||
public const T_FLOAT = TokenType::T_FLOAT;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_CLOSE_PARENTHESIS} */
|
||||
public const T_CLOSE_PARENTHESIS = TokenType::T_CLOSE_PARENTHESIS;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_OPEN_PARENTHESIS} */
|
||||
public const T_OPEN_PARENTHESIS = TokenType::T_OPEN_PARENTHESIS;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_COMMA} */
|
||||
public const T_COMMA = TokenType::T_COMMA;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_DIVIDE} */
|
||||
public const T_DIVIDE = TokenType::T_DIVIDE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_DOT} */
|
||||
public const T_DOT = TokenType::T_DOT;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_EQUALS} */
|
||||
public const T_EQUALS = TokenType::T_EQUALS;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_GREATER_THAN} */
|
||||
public const T_GREATER_THAN = TokenType::T_GREATER_THAN;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_LOWER_THAN} */
|
||||
public const T_LOWER_THAN = TokenType::T_LOWER_THAN;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_MINUS} */
|
||||
public const T_MINUS = TokenType::T_MINUS;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_MULTIPLY} */
|
||||
public const T_MULTIPLY = TokenType::T_MULTIPLY;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_NEGATE} */
|
||||
public const T_NEGATE = TokenType::T_NEGATE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_PLUS} */
|
||||
public const T_PLUS = TokenType::T_PLUS;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_OPEN_CURLY_BRACE} */
|
||||
public const T_OPEN_CURLY_BRACE = TokenType::T_OPEN_CURLY_BRACE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_CLOSE_CURLY_BRACE} */
|
||||
public const T_CLOSE_CURLY_BRACE = TokenType::T_CLOSE_CURLY_BRACE;
|
||||
|
||||
// All tokens that are identifiers or keywords that could be considered as identifiers should be >= 100
|
||||
/** @deprecated No Replacement planned. */
|
||||
public const T_ALIASED_NAME = 100;
|
||||
public const T_FULLY_QUALIFIED_NAME = 101;
|
||||
public const T_IDENTIFIER = 102;
|
||||
public const T_ALIASED_NAME = TokenType::T_ALIASED_NAME;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_FULLY_QUALIFIED_NAME} */
|
||||
public const T_FULLY_QUALIFIED_NAME = TokenType::T_FULLY_QUALIFIED_NAME;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_IDENTIFIER} */
|
||||
public const T_IDENTIFIER = TokenType::T_IDENTIFIER;
|
||||
|
||||
// All keyword tokens should be >= 200
|
||||
public const T_ALL = 200;
|
||||
public const T_AND = 201;
|
||||
public const T_ANY = 202;
|
||||
public const T_AS = 203;
|
||||
public const T_ASC = 204;
|
||||
public const T_AVG = 205;
|
||||
public const T_BETWEEN = 206;
|
||||
public const T_BOTH = 207;
|
||||
public const T_BY = 208;
|
||||
public const T_CASE = 209;
|
||||
public const T_COALESCE = 210;
|
||||
public const T_COUNT = 211;
|
||||
public const T_DELETE = 212;
|
||||
public const T_DESC = 213;
|
||||
public const T_DISTINCT = 214;
|
||||
public const T_ELSE = 215;
|
||||
public const T_EMPTY = 216;
|
||||
public const T_END = 217;
|
||||
public const T_ESCAPE = 218;
|
||||
public const T_EXISTS = 219;
|
||||
public const T_FALSE = 220;
|
||||
public const T_FROM = 221;
|
||||
public const T_GROUP = 222;
|
||||
public const T_HAVING = 223;
|
||||
public const T_HIDDEN = 224;
|
||||
public const T_IN = 225;
|
||||
public const T_INDEX = 226;
|
||||
public const T_INNER = 227;
|
||||
public const T_INSTANCE = 228;
|
||||
public const T_IS = 229;
|
||||
public const T_JOIN = 230;
|
||||
public const T_LEADING = 231;
|
||||
public const T_LEFT = 232;
|
||||
public const T_LIKE = 233;
|
||||
public const T_MAX = 234;
|
||||
public const T_MEMBER = 235;
|
||||
public const T_MIN = 236;
|
||||
public const T_NEW = 237;
|
||||
public const T_NOT = 238;
|
||||
public const T_NULL = 239;
|
||||
public const T_NULLIF = 240;
|
||||
public const T_OF = 241;
|
||||
public const T_OR = 242;
|
||||
public const T_ORDER = 243;
|
||||
public const T_OUTER = 244;
|
||||
public const T_PARTIAL = 245;
|
||||
public const T_SELECT = 246;
|
||||
public const T_SET = 247;
|
||||
public const T_SOME = 248;
|
||||
public const T_SUM = 249;
|
||||
public const T_THEN = 250;
|
||||
public const T_TRAILING = 251;
|
||||
public const T_TRUE = 252;
|
||||
public const T_UPDATE = 253;
|
||||
public const T_WHEN = 254;
|
||||
public const T_WHERE = 255;
|
||||
public const T_WITH = 256;
|
||||
/** @deprecated use {@see TokenType::T_ALL} */
|
||||
public const T_ALL = TokenType::T_ALL;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_AND} */
|
||||
public const T_AND = TokenType::T_AND;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_ANY} */
|
||||
public const T_ANY = TokenType::T_ANY;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_AS} */
|
||||
public const T_AS = TokenType::T_AS;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_ASC} */
|
||||
public const T_ASC = TokenType::T_ASC;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_AVG} */
|
||||
public const T_AVG = TokenType::T_AVG;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_BETWEEN} */
|
||||
public const T_BETWEEN = TokenType::T_BETWEEN;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_BOTH} */
|
||||
public const T_BOTH = TokenType::T_BOTH;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_BY} */
|
||||
public const T_BY = TokenType::T_BY;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_CASE} */
|
||||
public const T_CASE = TokenType::T_CASE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_COALESCE} */
|
||||
public const T_COALESCE = TokenType::T_COALESCE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_COUNT} */
|
||||
public const T_COUNT = TokenType::T_COUNT;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_DELETE} */
|
||||
public const T_DELETE = TokenType::T_DELETE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_DESC} */
|
||||
public const T_DESC = TokenType::T_DESC;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_DISTINCT} */
|
||||
public const T_DISTINCT = TokenType::T_DISTINCT;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_ELSE} */
|
||||
public const T_ELSE = TokenType::T_ELSE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_EMPTY} */
|
||||
public const T_EMPTY = TokenType::T_EMPTY;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_END} */
|
||||
public const T_END = TokenType::T_END;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_ESCAPE} */
|
||||
public const T_ESCAPE = TokenType::T_ESCAPE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_EXISTS} */
|
||||
public const T_EXISTS = TokenType::T_EXISTS;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_FALSE} */
|
||||
public const T_FALSE = TokenType::T_FALSE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_FROM} */
|
||||
public const T_FROM = TokenType::T_FROM;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_GROUP} */
|
||||
public const T_GROUP = TokenType::T_GROUP;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_HAVING} */
|
||||
public const T_HAVING = TokenType::T_HAVING;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_HIDDEN} */
|
||||
public const T_HIDDEN = TokenType::T_HIDDEN;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_IN} */
|
||||
public const T_IN = TokenType::T_IN;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_INDEX} */
|
||||
public const T_INDEX = TokenType::T_INDEX;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_INNER} */
|
||||
public const T_INNER = TokenType::T_INNER;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_INSTANCE} */
|
||||
public const T_INSTANCE = TokenType::T_INSTANCE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_IS} */
|
||||
public const T_IS = TokenType::T_IS;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_JOIN} */
|
||||
public const T_JOIN = TokenType::T_JOIN;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_LEADING} */
|
||||
public const T_LEADING = TokenType::T_LEADING;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_LEFT} */
|
||||
public const T_LEFT = TokenType::T_LEFT;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_LIKE} */
|
||||
public const T_LIKE = TokenType::T_LIKE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_MAX} */
|
||||
public const T_MAX = TokenType::T_MAX;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_MEMBER} */
|
||||
public const T_MEMBER = TokenType::T_MEMBER;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_MIN} */
|
||||
public const T_MIN = TokenType::T_MIN;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_NEW} */
|
||||
public const T_NEW = TokenType::T_NEW;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_NOT} */
|
||||
public const T_NOT = TokenType::T_NOT;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_NULL} */
|
||||
public const T_NULL = TokenType::T_NULL;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_NULLIF} */
|
||||
public const T_NULLIF = TokenType::T_NULLIF;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_OF} */
|
||||
public const T_OF = TokenType::T_OF;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_OR} */
|
||||
public const T_OR = TokenType::T_OR;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_ORDER} */
|
||||
public const T_ORDER = TokenType::T_ORDER;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_OUTER} */
|
||||
public const T_OUTER = TokenType::T_OUTER;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_PARTIAL} */
|
||||
public const T_PARTIAL = TokenType::T_PARTIAL;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_SELECT} */
|
||||
public const T_SELECT = TokenType::T_SELECT;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_SET} */
|
||||
public const T_SET = TokenType::T_SET;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_SOME} */
|
||||
public const T_SOME = TokenType::T_SOME;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_SUM} */
|
||||
public const T_SUM = TokenType::T_SUM;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_THEN} */
|
||||
public const T_THEN = TokenType::T_THEN;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_TRAILING} */
|
||||
public const T_TRAILING = TokenType::T_TRAILING;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_TRUE} */
|
||||
public const T_TRUE = TokenType::T_TRUE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_UPDATE} */
|
||||
public const T_UPDATE = TokenType::T_UPDATE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_WHEN} */
|
||||
public const T_WHEN = TokenType::T_WHEN;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_WHERE} */
|
||||
public const T_WHERE = TokenType::T_WHERE;
|
||||
|
||||
/** @deprecated use {@see TokenType::T_WITH} */
|
||||
public const T_WITH = TokenType::T_WITH;
|
||||
|
||||
/**
|
||||
* Creates a new query scanner object.
|
||||
@@ -150,26 +304,26 @@ class Lexer extends AbstractLexer
|
||||
*/
|
||||
protected function getType(&$value)
|
||||
{
|
||||
$type = self::T_NONE;
|
||||
$type = TokenType::T_NONE;
|
||||
|
||||
switch (true) {
|
||||
// Recognize numeric values
|
||||
case is_numeric($value):
|
||||
if (str_contains($value, '.') || stripos($value, 'e') !== false) {
|
||||
return self::T_FLOAT;
|
||||
return TokenType::T_FLOAT;
|
||||
}
|
||||
|
||||
return self::T_INTEGER;
|
||||
return TokenType::T_INTEGER;
|
||||
|
||||
// Recognize quoted strings
|
||||
case $value[0] === "'":
|
||||
$value = str_replace("''", "'", substr($value, 1, strlen($value) - 2));
|
||||
|
||||
return self::T_STRING;
|
||||
return TokenType::T_STRING;
|
||||
|
||||
// Recognize identifiers, aliased or qualified names
|
||||
case ctype_alpha($value[0]) || $value[0] === '_' || $value[0] === '\\':
|
||||
$name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value);
|
||||
$name = 'Doctrine\ORM\Query\TokenType::T_' . strtoupper($value);
|
||||
|
||||
if (defined($name)) {
|
||||
$type = constant($name);
|
||||
@@ -187,61 +341,61 @@ class Lexer extends AbstractLexer
|
||||
$value
|
||||
);
|
||||
|
||||
return self::T_ALIASED_NAME;
|
||||
return TokenType::T_ALIASED_NAME;
|
||||
}
|
||||
|
||||
if (str_contains($value, '\\')) {
|
||||
return self::T_FULLY_QUALIFIED_NAME;
|
||||
return TokenType::T_FULLY_QUALIFIED_NAME;
|
||||
}
|
||||
|
||||
return self::T_IDENTIFIER;
|
||||
return TokenType::T_IDENTIFIER;
|
||||
|
||||
// Recognize input parameters
|
||||
case $value[0] === '?' || $value[0] === ':':
|
||||
return self::T_INPUT_PARAMETER;
|
||||
return TokenType::T_INPUT_PARAMETER;
|
||||
|
||||
// Recognize symbols
|
||||
case $value === '.':
|
||||
return self::T_DOT;
|
||||
return TokenType::T_DOT;
|
||||
|
||||
case $value === ',':
|
||||
return self::T_COMMA;
|
||||
return TokenType::T_COMMA;
|
||||
|
||||
case $value === '(':
|
||||
return self::T_OPEN_PARENTHESIS;
|
||||
return TokenType::T_OPEN_PARENTHESIS;
|
||||
|
||||
case $value === ')':
|
||||
return self::T_CLOSE_PARENTHESIS;
|
||||
return TokenType::T_CLOSE_PARENTHESIS;
|
||||
|
||||
case $value === '=':
|
||||
return self::T_EQUALS;
|
||||
return TokenType::T_EQUALS;
|
||||
|
||||
case $value === '>':
|
||||
return self::T_GREATER_THAN;
|
||||
return TokenType::T_GREATER_THAN;
|
||||
|
||||
case $value === '<':
|
||||
return self::T_LOWER_THAN;
|
||||
return TokenType::T_LOWER_THAN;
|
||||
|
||||
case $value === '+':
|
||||
return self::T_PLUS;
|
||||
return TokenType::T_PLUS;
|
||||
|
||||
case $value === '-':
|
||||
return self::T_MINUS;
|
||||
return TokenType::T_MINUS;
|
||||
|
||||
case $value === '*':
|
||||
return self::T_MULTIPLY;
|
||||
return TokenType::T_MULTIPLY;
|
||||
|
||||
case $value === '/':
|
||||
return self::T_DIVIDE;
|
||||
return TokenType::T_DIVIDE;
|
||||
|
||||
case $value === '!':
|
||||
return self::T_NEGATE;
|
||||
return TokenType::T_NEGATE;
|
||||
|
||||
case $value === '{':
|
||||
return self::T_OPEN_CURLY_BRACE;
|
||||
return TokenType::T_OPEN_CURLY_BRACE;
|
||||
|
||||
case $value === '}':
|
||||
return self::T_CLOSE_CURLY_BRACE;
|
||||
return TokenType::T_CLOSE_CURLY_BRACE;
|
||||
|
||||
// Default
|
||||
default:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1062,7 +1062,9 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
}
|
||||
|
||||
if ($relation['fetch'] === ClassMetadata::FETCH_EAGER && $condExpr !== null) {
|
||||
$fetchMode = $this->query->getHint('fetchMode')[$assoc['sourceEntity']][$assoc['fieldName']] ?? $relation['fetch'];
|
||||
|
||||
if ($fetchMode === ClassMetadata::FETCH_EAGER && $condExpr !== null) {
|
||||
throw QueryException::eagerFetchJoinWithNotAllowed($assoc['sourceEntity'], $assoc['fieldName']);
|
||||
}
|
||||
|
||||
|
||||
99
src/Query/TokenType.php
Normal file
99
src/Query/TokenType.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Query;
|
||||
|
||||
final class TokenType
|
||||
{
|
||||
// All tokens that are not valid identifiers must be < 100
|
||||
public const T_NONE = 1;
|
||||
public const T_INTEGER = 2;
|
||||
public const T_STRING = 3;
|
||||
public const T_INPUT_PARAMETER = 4;
|
||||
public const T_FLOAT = 5;
|
||||
public const T_CLOSE_PARENTHESIS = 6;
|
||||
public const T_OPEN_PARENTHESIS = 7;
|
||||
public const T_COMMA = 8;
|
||||
public const T_DIVIDE = 9;
|
||||
public const T_DOT = 10;
|
||||
public const T_EQUALS = 11;
|
||||
public const T_GREATER_THAN = 12;
|
||||
public const T_LOWER_THAN = 13;
|
||||
public const T_MINUS = 14;
|
||||
public const T_MULTIPLY = 15;
|
||||
public const T_NEGATE = 16;
|
||||
public const T_PLUS = 17;
|
||||
public const T_OPEN_CURLY_BRACE = 18;
|
||||
public const T_CLOSE_CURLY_BRACE = 19;
|
||||
|
||||
// All tokens that are identifiers or keywords that could be considered as identifiers should be >= 100
|
||||
/** @deprecated No Replacement planned. */
|
||||
public const T_ALIASED_NAME = 100;
|
||||
public const T_FULLY_QUALIFIED_NAME = 101;
|
||||
public const T_IDENTIFIER = 102;
|
||||
|
||||
// All keyword tokens should be >= 200
|
||||
public const T_ALL = 200;
|
||||
public const T_AND = 201;
|
||||
public const T_ANY = 202;
|
||||
public const T_AS = 203;
|
||||
public const T_ASC = 204;
|
||||
public const T_AVG = 205;
|
||||
public const T_BETWEEN = 206;
|
||||
public const T_BOTH = 207;
|
||||
public const T_BY = 208;
|
||||
public const T_CASE = 209;
|
||||
public const T_COALESCE = 210;
|
||||
public const T_COUNT = 211;
|
||||
public const T_DELETE = 212;
|
||||
public const T_DESC = 213;
|
||||
public const T_DISTINCT = 214;
|
||||
public const T_ELSE = 215;
|
||||
public const T_EMPTY = 216;
|
||||
public const T_END = 217;
|
||||
public const T_ESCAPE = 218;
|
||||
public const T_EXISTS = 219;
|
||||
public const T_FALSE = 220;
|
||||
public const T_FROM = 221;
|
||||
public const T_GROUP = 222;
|
||||
public const T_HAVING = 223;
|
||||
public const T_HIDDEN = 224;
|
||||
public const T_IN = 225;
|
||||
public const T_INDEX = 226;
|
||||
public const T_INNER = 227;
|
||||
public const T_INSTANCE = 228;
|
||||
public const T_IS = 229;
|
||||
public const T_JOIN = 230;
|
||||
public const T_LEADING = 231;
|
||||
public const T_LEFT = 232;
|
||||
public const T_LIKE = 233;
|
||||
public const T_MAX = 234;
|
||||
public const T_MEMBER = 235;
|
||||
public const T_MIN = 236;
|
||||
public const T_NEW = 237;
|
||||
public const T_NOT = 238;
|
||||
public const T_NULL = 239;
|
||||
public const T_NULLIF = 240;
|
||||
public const T_OF = 241;
|
||||
public const T_OR = 242;
|
||||
public const T_ORDER = 243;
|
||||
public const T_OUTER = 244;
|
||||
public const T_PARTIAL = 245;
|
||||
public const T_SELECT = 246;
|
||||
public const T_SET = 247;
|
||||
public const T_SOME = 248;
|
||||
public const T_SUM = 249;
|
||||
public const T_THEN = 250;
|
||||
public const T_TRAILING = 251;
|
||||
public const T_TRUE = 252;
|
||||
public const T_UPDATE = 253;
|
||||
public const T_WHEN = 254;
|
||||
public const T_WHERE = 255;
|
||||
public const T_WITH = 256;
|
||||
|
||||
/** @internal */
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -365,7 +365,7 @@ class SchemaTool
|
||||
|
||||
if (isset($class->table['uniqueConstraints'])) {
|
||||
foreach ($class->table['uniqueConstraints'] as $indexName => $indexData) {
|
||||
$uniqIndex = new Index($indexName, $this->getIndexColumns($class, $indexData), true, false, [], $indexData['options'] ?? []);
|
||||
$uniqIndex = new Index('tmp__' . $indexName, $this->getIndexColumns($class, $indexData), true, false, [], $indexData['options'] ?? []);
|
||||
|
||||
foreach ($table->getIndexes() as $tableIndexName => $tableIndex) {
|
||||
$method = method_exists($tableIndex, 'isFulfilledBy') ? 'isFulfilledBy' : 'isFullfilledBy';
|
||||
|
||||
@@ -64,18 +64,18 @@ class SchemaValidator
|
||||
* It maps built-in Doctrine types to PHP types
|
||||
*/
|
||||
private const BUILTIN_TYPES_MAP = [
|
||||
AsciiStringType::class => 'string',
|
||||
BigIntType::class => 'string',
|
||||
BooleanType::class => 'bool',
|
||||
DecimalType::class => 'string',
|
||||
FloatType::class => 'float',
|
||||
GuidType::class => 'string',
|
||||
IntegerType::class => 'int',
|
||||
JsonType::class => 'array',
|
||||
SimpleArrayType::class => 'array',
|
||||
SmallIntType::class => 'int',
|
||||
StringType::class => 'string',
|
||||
TextType::class => 'string',
|
||||
AsciiStringType::class => ['string'],
|
||||
BigIntType::class => ['int', 'string'],
|
||||
BooleanType::class => ['bool'],
|
||||
DecimalType::class => ['string'],
|
||||
FloatType::class => ['float'],
|
||||
GuidType::class => ['string'],
|
||||
IntegerType::class => ['int'],
|
||||
JsonType::class => ['array'],
|
||||
SimpleArrayType::class => ['array'],
|
||||
SmallIntType::class => ['int'],
|
||||
StringType::class => ['string'],
|
||||
TextType::class => ['string'],
|
||||
];
|
||||
|
||||
public function __construct(EntityManagerInterface $em, bool $validatePropertyTypes = true)
|
||||
@@ -390,21 +390,21 @@ class SchemaValidator
|
||||
$propertyType = $propertyType->getName();
|
||||
|
||||
// If the property type is the same as the metadata field type, we are ok
|
||||
if ($propertyType === $metadataFieldType) {
|
||||
if (in_array($propertyType, $metadataFieldType, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_a($propertyType, BackedEnum::class, true)) {
|
||||
$backingType = (string) (new ReflectionEnum($propertyType))->getBackingType();
|
||||
|
||||
if ($metadataFieldType !== $backingType) {
|
||||
if (! in_array($backingType, $metadataFieldType, true)) {
|
||||
return sprintf(
|
||||
"The field '%s#%s' has the property type '%s' with a backing type of '%s' that differs from the metadata field type '%s'.",
|
||||
$class->name,
|
||||
$fieldName,
|
||||
$propertyType,
|
||||
$backingType,
|
||||
$metadataFieldType
|
||||
implode('|', $metadataFieldType)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -429,7 +429,7 @@ class SchemaValidator
|
||||
) {
|
||||
$backingType = (string) (new ReflectionEnum($fieldMapping['enumType']))->getBackingType();
|
||||
|
||||
if ($metadataFieldType === $backingType) {
|
||||
if (in_array($backingType, $metadataFieldType, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -439,7 +439,7 @@ class SchemaValidator
|
||||
$fieldName,
|
||||
$fieldMapping['enumType'],
|
||||
$backingType,
|
||||
$metadataFieldType
|
||||
implode('|', $metadataFieldType)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -455,7 +455,7 @@ class SchemaValidator
|
||||
$class->name,
|
||||
$fieldName,
|
||||
$propertyType,
|
||||
$metadataFieldType,
|
||||
implode('|', $metadataFieldType),
|
||||
$fieldMapping['type']
|
||||
);
|
||||
},
|
||||
@@ -468,8 +468,10 @@ class SchemaValidator
|
||||
/**
|
||||
* The exact DBAL type must be used (no subclasses), since consumers of doctrine/orm may have their own
|
||||
* customization around field types.
|
||||
*
|
||||
* @return list<string>|null
|
||||
*/
|
||||
private function findBuiltInType(Type $type): ?string
|
||||
private function findBuiltInType(Type $type): ?array
|
||||
{
|
||||
$typeName = get_class($type);
|
||||
|
||||
|
||||
@@ -1292,6 +1292,8 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$eventsToDispatch = [];
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
$this->removeFromIdentityMap($entity);
|
||||
|
||||
$oid = spl_object_id($entity);
|
||||
$class = $this->em->getClassMetadata(get_class($entity));
|
||||
$persister = $this->getEntityPersister($class->name);
|
||||
@@ -1667,8 +1669,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
return;
|
||||
}
|
||||
|
||||
$this->removeFromIdentityMap($entity);
|
||||
|
||||
unset($this->entityUpdates[$oid]);
|
||||
|
||||
if (! isset($this->entityDeletions[$oid])) {
|
||||
@@ -1781,18 +1781,15 @@ EXCEPTION
|
||||
*/
|
||||
final public static function getIdHashByIdentifier(array $identifier): string
|
||||
{
|
||||
foreach ($identifier as $k => $value) {
|
||||
if ($value instanceof BackedEnum) {
|
||||
$identifier[$k] = $value->value;
|
||||
}
|
||||
}
|
||||
|
||||
return implode(
|
||||
' ',
|
||||
array_map(
|
||||
static function ($value) {
|
||||
if ($value instanceof BackedEnum) {
|
||||
return $value->value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
$identifier
|
||||
)
|
||||
$identifier
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3056,10 +3053,7 @@ EXCEPTION
|
||||
} else {
|
||||
$associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
|
||||
}
|
||||
} elseif (
|
||||
$targetClass->containsForeignIdentifier
|
||||
&& in_array($targetClass->getFieldForColumn($targetColumn), $targetClass->identifier, true)
|
||||
) {
|
||||
} elseif (in_array($targetClass->getFieldForColumn($targetColumn), $targetClass->identifier, true)) {
|
||||
// the missing key is part of target's entity primary key
|
||||
$associatedId = [];
|
||||
break;
|
||||
@@ -3169,9 +3163,9 @@ EXCEPTION
|
||||
|
||||
if ($hints['fetchMode'][$class->name][$field] === ClassMetadata::FETCH_EAGER) {
|
||||
$isIteration = isset($hints[Query::HINT_INTERNAL_ITERATION]) && $hints[Query::HINT_INTERNAL_ITERATION];
|
||||
if (! $isIteration && $assoc['type'] === ClassMetadata::ONE_TO_MANY) {
|
||||
if ($assoc['type'] === ClassMetadata::ONE_TO_MANY && ! $isIteration && ! $targetClass->isIdentifierComposite && ! isset($assoc['indexBy'])) {
|
||||
$this->scheduleCollectionForBatchLoading($pColl, $class);
|
||||
} elseif (($isIteration && $assoc['type'] === ClassMetadata::ONE_TO_MANY) || $assoc['type'] === ClassMetadata::MANY_TO_MANY) {
|
||||
} else {
|
||||
$this->loadCollection($pColl);
|
||||
$pColl->takeSnapshot();
|
||||
}
|
||||
@@ -3227,7 +3221,13 @@ EXCEPTION
|
||||
*
|
||||
* @param PersistentCollection[] $collections
|
||||
* @param array<string, mixed> $mapping
|
||||
* @psalm-param array{targetEntity: class-string, sourceEntity: class-string, mappedBy: string, indexBy: string|null} $mapping
|
||||
* @psalm-param array{
|
||||
* targetEntity: class-string,
|
||||
* sourceEntity: class-string,
|
||||
* mappedBy: string,
|
||||
* indexBy: string|null,
|
||||
* orderBy: array<string, string>|null
|
||||
* } $mapping
|
||||
*/
|
||||
private function eagerLoadCollections(array $collections, array $mapping): void
|
||||
{
|
||||
@@ -3244,7 +3244,7 @@ EXCEPTION
|
||||
$entities[] = $collection->getOwner();
|
||||
}
|
||||
|
||||
$found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities]);
|
||||
$found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities], $mapping['orderBy'] ?? null);
|
||||
|
||||
$targetClass = $this->em->getClassMetadata($targetEntity);
|
||||
$targetProperty = $targetClass->getReflectionProperty($mappedBy);
|
||||
@@ -3252,7 +3252,19 @@ EXCEPTION
|
||||
foreach ($found as $targetValue) {
|
||||
$sourceEntity = $targetProperty->getValue($targetValue);
|
||||
|
||||
$id = $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($sourceEntity));
|
||||
if ($sourceEntity === null && isset($targetClass->associationMappings[$mappedBy]['joinColumns'])) {
|
||||
// case where the hydration $targetValue itself has not yet fully completed, for example
|
||||
// in case a bi-directional association is being hydrated and deferring eager loading is
|
||||
// not possible due to subclassing.
|
||||
$data = $this->getOriginalEntityData($targetValue);
|
||||
$id = [];
|
||||
foreach ($targetClass->associationMappings[$mappedBy]['joinColumns'] as $joinColumn) {
|
||||
$id[] = $data[$joinColumn['name']];
|
||||
}
|
||||
} else {
|
||||
$id = $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($sourceEntity));
|
||||
}
|
||||
|
||||
$idHash = implode(' ', $id);
|
||||
|
||||
if (isset($mapping['indexBy'])) {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\AbstractFetchEager;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity()
|
||||
* @ORM\Table(name="abstract_fetch_eager_remote_control")
|
||||
* @ORM\InheritanceType("SINGLE_TABLE")
|
||||
* @ORM\DiscriminatorColumn(name="type", type="string")
|
||||
* @ORM\DiscriminatorMap({"mobile"="MobileRemoteControl"})
|
||||
*/
|
||||
abstract class AbstractRemoteControl
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="User", mappedBy="remoteControl", fetch="EAGER")
|
||||
*
|
||||
* @var Collection<User>
|
||||
*/
|
||||
public $users;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->users = new ArrayCollection();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\AbstractFetchEager;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity()
|
||||
*/
|
||||
class MobileRemoteControl extends AbstractRemoteControl
|
||||
{
|
||||
}
|
||||
36
tests/Tests/Models/AbstractFetchEager/User.php
Normal file
36
tests/Tests/Models/AbstractFetchEager/User.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\AbstractFetchEager;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity()
|
||||
* @ORM\Table(name="abstract_fetch_eager_user")
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="AbstractRemoteControl", inversedBy="users")
|
||||
* @ORM\JoinColumn(nullable=false)
|
||||
*
|
||||
* @var AbstractRemoteControl
|
||||
*/
|
||||
public $remoteControl;
|
||||
|
||||
public function __construct(AbstractRemoteControl $control)
|
||||
{
|
||||
$this->remoteControl = $control;
|
||||
}
|
||||
}
|
||||
27
tests/Tests/Models/BigIntegers/BigIntegers.php
Normal file
27
tests/Tests/Models/BigIntegers/BigIntegers.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\BigIntegers;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/** @ORM\Entity */
|
||||
class BigIntegers
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
public ?int $id = null;
|
||||
|
||||
/** @ORM\Column(type="bigint") */
|
||||
public int $one = 1;
|
||||
|
||||
/** @ORM\Column(type="bigint") */
|
||||
public string $two = '2';
|
||||
|
||||
/** @ORM\Column(type="bigint") */
|
||||
public float $three = 3.0;
|
||||
}
|
||||
33
tests/Tests/Models/CompositeKeyRelations/CustomerClass.php
Normal file
33
tests/Tests/Models/CompositeKeyRelations/CustomerClass.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\CompositeKeyRelations;
|
||||
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
|
||||
/** @Entity */
|
||||
class CustomerClass
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Id
|
||||
* @Column(type="string")
|
||||
*/
|
||||
public $companyCode;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @Id
|
||||
* @Column(type="string")
|
||||
*/
|
||||
public $code;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @Column(type="string")
|
||||
*/
|
||||
public $name;
|
||||
}
|
||||
46
tests/Tests/Models/CompositeKeyRelations/InvoiceClass.php
Normal file
46
tests/Tests/Models/CompositeKeyRelations/InvoiceClass.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\CompositeKeyRelations;
|
||||
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
use Doctrine\ORM\Mapping\JoinColumn;
|
||||
use Doctrine\ORM\Mapping\JoinColumns;
|
||||
use Doctrine\ORM\Mapping\ManyToOne;
|
||||
|
||||
/** @Entity */
|
||||
class InvoiceClass
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Id
|
||||
* @Column(type="string")
|
||||
*/
|
||||
public $companyCode;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @Id
|
||||
* @Column(type="string")
|
||||
*/
|
||||
public $invoiceNumber;
|
||||
|
||||
/**
|
||||
* @var CustomerClass|null
|
||||
* @ManyToOne(targetEntity="CustomerClass")
|
||||
* @JoinColumns({
|
||||
* @JoinColumn(name="companyCode", referencedColumnName="companyCode"),
|
||||
* @JoinColumn(name="customerCode", referencedColumnName="code")
|
||||
* })
|
||||
*/
|
||||
public $customer;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
* @Column(type="string", nullable=true)
|
||||
*/
|
||||
public $customerCode;
|
||||
}
|
||||
52
tests/Tests/Models/ECommerce/ECommerceProduct2.php
Normal file
52
tests/Tests/Models/ECommerce/ECommerceProduct2.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\ECommerce;
|
||||
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\GeneratedValue;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
use Doctrine\ORM\Mapping\Index;
|
||||
use Doctrine\ORM\Mapping\Table;
|
||||
|
||||
/**
|
||||
* ECommerceProduct2
|
||||
* Resets the id when being cloned.
|
||||
*
|
||||
* @Entity
|
||||
* @Table(name="ecommerce_products",indexes={@Index(name="name_idx", columns={"name"})})
|
||||
*/
|
||||
class ECommerceProduct2
|
||||
{
|
||||
/**
|
||||
* @var int|null
|
||||
* @Column(type="integer")
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @Column(type="string", length=50, nullable=true)
|
||||
*/
|
||||
private $name;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->id = null;
|
||||
$this->name = 'Clone of ' . $this->name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\EagerFetchedCompositeOneToMany;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="eager_composite_join_root")
|
||||
*/
|
||||
class RootEntity
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer", nullable=false)
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
private $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="string", nullable=false, name="other_key")
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $otherKey;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(mappedBy="root", targetEntity=SecondLevel::class, fetch="EAGER")
|
||||
*
|
||||
* @var Collection<int, SecondLevel>
|
||||
*/
|
||||
private $secondLevel;
|
||||
|
||||
public function __construct(int $id, string $other)
|
||||
{
|
||||
$this->otherKey = $other;
|
||||
$this->secondLevel = new ArrayCollection();
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getOtherKey(): string
|
||||
{
|
||||
return $this->otherKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Models\EagerFetchedCompositeOneToMany;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="eager_composite_join_second_level", indexes={
|
||||
* @ORM\Index(name="root_other_key_idx", columns={"root_other_key", "root_id"})
|
||||
* })
|
||||
*/
|
||||
class SecondLevel
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer", nullable=false)
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
private $upperId;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="string", nullable=false, name="other_key")
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $otherKey;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=RootEntity::class, inversedBy="secondLevel")
|
||||
* @ORM\JoinColumns({
|
||||
* @ORM\JoinColumn(name="root_id", referencedColumnName="id"),
|
||||
* @ORM\JoinColumn(name="root_other_key", referencedColumnName="other_key")
|
||||
* })
|
||||
*
|
||||
* @var RootEntity
|
||||
*/
|
||||
private $root;
|
||||
|
||||
public function __construct(RootEntity $upper)
|
||||
{
|
||||
$this->upperId = $upper->getId();
|
||||
$this->otherKey = $upper->getOtherKey();
|
||||
$this->root = $upper;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\Tests\ORM\Cache\Persister\Entity;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\Cache\Persister\CachedPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\AbstractEntityPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
|
||||
@@ -99,7 +100,7 @@ abstract class EntityPersisterTestCase extends OrmTestCase
|
||||
->with(
|
||||
self::identicalTo(['name' => 'Foo']),
|
||||
self::identicalTo([0]),
|
||||
self::identicalTo(1),
|
||||
self::identicalTo(LockMode::OPTIMISTIC),
|
||||
self::identicalTo(2),
|
||||
self::identicalTo(3),
|
||||
self::identicalTo([4])
|
||||
@@ -109,7 +110,7 @@ abstract class EntityPersisterTestCase extends OrmTestCase
|
||||
self::assertSame('SELECT * FROM foo WERE name = ?', $persister->getSelectSQL(
|
||||
['name' => 'Foo'],
|
||||
[0],
|
||||
1,
|
||||
LockMode::OPTIMISTIC,
|
||||
2,
|
||||
3,
|
||||
[4]
|
||||
@@ -228,13 +229,21 @@ abstract class EntityPersisterTestCase extends OrmTestCase
|
||||
self::identicalTo($entity),
|
||||
self::identicalTo([0]),
|
||||
self::identicalTo([1]),
|
||||
self::identicalTo(2),
|
||||
self::identicalTo(LockMode::PESSIMISTIC_READ),
|
||||
self::identicalTo(3),
|
||||
self::identicalTo([4])
|
||||
)
|
||||
->willReturn($entity);
|
||||
|
||||
self::assertSame($entity, $persister->load(['id' => 1], $entity, [0], [1], 2, 3, [4]));
|
||||
self::assertSame($entity, $persister->load(
|
||||
['id' => 1],
|
||||
$entity,
|
||||
[0],
|
||||
[1],
|
||||
LockMode::PESSIMISTIC_READ,
|
||||
3,
|
||||
[4]
|
||||
));
|
||||
}
|
||||
|
||||
public function testInvokeLoadAll(): void
|
||||
@@ -391,9 +400,9 @@ abstract class EntityPersisterTestCase extends OrmTestCase
|
||||
|
||||
$this->entityPersister->expects(self::once())
|
||||
->method('lock')
|
||||
->with(self::identicalTo($identifier), self::identicalTo(1));
|
||||
->with(self::identicalTo($identifier), self::identicalTo(LockMode::OPTIMISTIC));
|
||||
|
||||
$persister->lock($identifier, 1);
|
||||
$persister->lock($identifier, LockMode::OPTIMISTIC);
|
||||
}
|
||||
|
||||
public function testInvokeExists(): void
|
||||
|
||||
37
tests/Tests/ORM/Functional/AbstractFetchEagerTest.php
Normal file
37
tests/Tests/ORM/Functional/AbstractFetchEagerTest.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\Tests\Models\AbstractFetchEager\AbstractRemoteControl;
|
||||
use Doctrine\Tests\Models\AbstractFetchEager\MobileRemoteControl;
|
||||
use Doctrine\Tests\Models\AbstractFetchEager\User;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
final class AbstractFetchEagerTest extends OrmFunctionalTestCase
|
||||
{
|
||||
public function testWithAbstractFetchEager(): void
|
||||
{
|
||||
$this->createSchemaForModels(
|
||||
AbstractRemoteControl::class,
|
||||
User::class
|
||||
);
|
||||
|
||||
$control = new MobileRemoteControl('smart');
|
||||
$user = new User($control);
|
||||
|
||||
$entityManage = $this->getEntityManager();
|
||||
|
||||
$entityManage->persist($control);
|
||||
$entityManage->persist($user);
|
||||
$entityManage->flush();
|
||||
$entityManage->clear();
|
||||
|
||||
$user = $entityManage->find(User::class, $user->id);
|
||||
|
||||
self::assertNotNull($user);
|
||||
self::assertEquals('smart', $user->remoteControl->name);
|
||||
self::assertTrue($user->remoteControl->users->contains($user));
|
||||
}
|
||||
}
|
||||
61
tests/Tests/ORM/Functional/CompositeKeyRelationsTest.php
Normal file
61
tests/Tests/ORM/Functional/CompositeKeyRelationsTest.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\Tests\Models\CompositeKeyRelations\CustomerClass;
|
||||
use Doctrine\Tests\Models\CompositeKeyRelations\InvoiceClass;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
class CompositeKeyRelationsTest extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->useModelSet('compositekeyrelations');
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testFindEntityWithNotNullRelation(): void
|
||||
{
|
||||
$this->_em->getConnection()->insert('CustomerClass', [
|
||||
'companyCode' => 'AA',
|
||||
'code' => 'CUST1',
|
||||
'name' => 'Customer 1',
|
||||
]);
|
||||
|
||||
$this->_em->getConnection()->insert('InvoiceClass', [
|
||||
'companyCode' => 'AA',
|
||||
'invoiceNumber' => 'INV1',
|
||||
'customerCode' => 'CUST1',
|
||||
]);
|
||||
|
||||
$entity = $this->findEntity('AA', 'INV1');
|
||||
self::assertSame('AA', $entity->companyCode);
|
||||
self::assertSame('INV1', $entity->invoiceNumber);
|
||||
self::assertInstanceOf(CustomerClass::class, $entity->customer);
|
||||
self::assertSame('Customer 1', $entity->customer->name);
|
||||
}
|
||||
|
||||
public function testFindEntityWithNullRelation(): void
|
||||
{
|
||||
$this->_em->getConnection()->insert('InvoiceClass', [
|
||||
'companyCode' => 'BB',
|
||||
'invoiceNumber' => 'INV1',
|
||||
]);
|
||||
|
||||
$entity = $this->findEntity('BB', 'INV1');
|
||||
self::assertSame('BB', $entity->companyCode);
|
||||
self::assertSame('INV1', $entity->invoiceNumber);
|
||||
self::assertNull($entity->customer);
|
||||
}
|
||||
|
||||
private function findEntity(string $companyCode, string $invoiceNumber): InvoiceClass
|
||||
{
|
||||
return $this->_em->find(
|
||||
InvoiceClass::class,
|
||||
['companyCode' => $companyCode, 'invoiceNumber' => $invoiceNumber]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,9 @@ namespace Doctrine\Tests\ORM\Functional;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
||||
use Doctrine\ORM\Query\AST\PathExpression;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
use Doctrine\Tests\Models\CMS\CmsUser;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
@@ -75,10 +75,10 @@ class NoOp extends FunctionNode
|
||||
|
||||
public function parse(Parser $parser): void
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
$this->field = $parser->ArithmeticPrimary();
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
|
||||
public function getSql(SqlWalker $sqlWalker): string
|
||||
|
||||
@@ -88,6 +88,14 @@ class EagerFetchCollectionTest extends OrmFunctionalTestCase
|
||||
$query->getResult();
|
||||
}
|
||||
|
||||
public function testSubselectFetchJoinWithAllowedWhenOverriddenNotEager(): void
|
||||
{
|
||||
$query = $this->_em->createQuery('SELECT o, c FROM ' . EagerFetchOwner::class . ' o JOIN o.children c WITH c.id = 1');
|
||||
$query->setFetchMode(EagerFetchChild::class, 'owner', ORM\ClassMetadata::FETCH_LAZY);
|
||||
|
||||
$this->assertIsString($query->getSql());
|
||||
}
|
||||
|
||||
public function testEagerFetchWithIterable(): void
|
||||
{
|
||||
$this->createOwnerWithChildren(2);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\Tests\Models\EagerFetchedCompositeOneToMany\RootEntity;
|
||||
use Doctrine\Tests\Models\EagerFetchedCompositeOneToMany\SecondLevel;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
final class EagerFetchOneToManyWithCompositeKeyTest extends OrmFunctionalTestCase
|
||||
{
|
||||
/** @ticket 11154 */
|
||||
public function testItDoesNotThrowAnExceptionWhenTriggeringALoad(): void
|
||||
{
|
||||
$this->setUpEntitySchema([RootEntity::class, SecondLevel::class]);
|
||||
|
||||
$a1 = new RootEntity(1, 'A');
|
||||
|
||||
$this->_em->persist($a1);
|
||||
$this->_em->flush();
|
||||
|
||||
$this->_em->clear();
|
||||
|
||||
self::assertCount(1, $this->_em->getRepository(RootEntity::class)->findAll());
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@ class ProxiesLikeEntitiesTest extends OrmFunctionalTestCase
|
||||
public function testPersistUpdate(): void
|
||||
{
|
||||
// Considering case (a)
|
||||
$proxy = $this->_em->getProxyFactory()->getProxy(CmsUser::class, ['id' => 123]);
|
||||
$proxy = $this->_em->getProxyFactory()->getProxy(CmsUser::class, ['id' => $this->user->getId()]);
|
||||
|
||||
$proxy->id = null;
|
||||
$proxy->username = 'ocra';
|
||||
|
||||
@@ -249,7 +249,6 @@ class QueryDqlFunctionTest extends OrmFunctionalTestCase
|
||||
self::assertEquals(1600000, $result[3]['op']);
|
||||
}
|
||||
|
||||
/** @group test */
|
||||
public function testOperatorDiv(): void
|
||||
{
|
||||
$result = $this->_em->createQuery('SELECT m, (m.salary/0.5) AS op FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC')
|
||||
|
||||
@@ -9,6 +9,7 @@ use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Proxy\InternalProxy;
|
||||
use Doctrine\Tests\Models\Company\CompanyAuction;
|
||||
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
|
||||
use Doctrine\Tests\Models\ECommerce\ECommerceProduct2;
|
||||
use Doctrine\Tests\Models\ECommerce\ECommerceShipping;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
@@ -112,6 +113,24 @@ class ReferenceProxyTest extends OrmFunctionalTestCase
|
||||
self::assertFalse($entity->isCloned);
|
||||
}
|
||||
|
||||
public function testCloneProxyWithResetId(): void
|
||||
{
|
||||
$id = $this->createProduct();
|
||||
|
||||
$entity = $this->_em->getReference(ECommerceProduct2::class, $id);
|
||||
assert($entity instanceof ECommerceProduct2);
|
||||
|
||||
$clone = clone $entity;
|
||||
assert($clone instanceof ECommerceProduct2);
|
||||
|
||||
self::assertEquals($id, $entity->getId());
|
||||
self::assertEquals('Doctrine Cookbook', $entity->getName());
|
||||
|
||||
self::assertFalse($this->_em->contains($clone));
|
||||
self::assertNull($clone->getId());
|
||||
self::assertEquals('Clone of Doctrine Cookbook', $clone->getName());
|
||||
}
|
||||
|
||||
/** @group DDC-733 */
|
||||
public function testInitializeProxy(): void
|
||||
{
|
||||
|
||||
107
tests/Tests/ORM/Functional/Ticket/GH10889Test.php
Normal file
107
tests/Tests/ORM/Functional/Ticket/GH10889Test.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
/**
|
||||
* @see https://github.com/doctrine/orm/issues/10889
|
||||
*
|
||||
* @group GH10889
|
||||
*/
|
||||
class GH10889Test extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->createSchemaForModels(
|
||||
GH10889Person::class,
|
||||
GH10889Company::class,
|
||||
GH10889Resume::class
|
||||
);
|
||||
}
|
||||
|
||||
public function testIssue(): void
|
||||
{
|
||||
$person = new GH10889Person();
|
||||
$resume = new GH10889Resume($person, null);
|
||||
|
||||
$this->_em->persist($person);
|
||||
$this->_em->persist($resume);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
/** @var list<GH10889Resume> $resumes */
|
||||
$resumes = $this->_em
|
||||
->getRepository(GH10889Resume::class)
|
||||
->createQueryBuilder('resume')
|
||||
->leftJoin('resume.currentCompany', 'company')->addSelect('company')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
|
||||
$this->assertArrayHasKey(0, $resumes);
|
||||
$this->assertEquals(1, $resumes[0]->person->id);
|
||||
$this->assertNull($resumes[0]->currentCompany);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class GH10889Person
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class GH10889Company
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class GH10889Resume
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\OneToOne(targetEntity="GH10889Person")
|
||||
*
|
||||
* @var GH10889Person
|
||||
*/
|
||||
public $person;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="GH10889Company")
|
||||
*
|
||||
* @var GH10889Company|null
|
||||
*/
|
||||
public $currentCompany;
|
||||
|
||||
public function __construct(GH10889Person $person, ?GH10889Company $currentCompany)
|
||||
{
|
||||
$this->person = $person;
|
||||
$this->currentCompany = $currentCompany;
|
||||
}
|
||||
}
|
||||
42
tests/Tests/ORM/Functional/Ticket/GH11149/EagerProduct.php
Normal file
42
tests/Tests/ORM/Functional/Ticket/GH11149/EagerProduct.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket\GH11149;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table("gh11149_eager_product")
|
||||
*/
|
||||
class EagerProduct
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(
|
||||
* targetEntity=EagerProductTranslation::class,
|
||||
* mappedBy="product",
|
||||
* fetch="EAGER",
|
||||
* indexBy="locale_code"
|
||||
* )
|
||||
*
|
||||
* @var Collection<string, EagerProductTranslation>
|
||||
*/
|
||||
public $translations;
|
||||
|
||||
public function __construct(int $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->translations = new ArrayCollection();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket\GH11149;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table("gh11149_eager_product_translation")
|
||||
*/
|
||||
class EagerProductTranslation
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=EagerProduct::class, inversedBy="translations")
|
||||
* @ORM\JoinColumn(nullable=false)
|
||||
*
|
||||
* @var EagerProduct
|
||||
*/
|
||||
public $product;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Locale::class)
|
||||
* @ORM\JoinColumn(name="locale_code", referencedColumnName="code", nullable=false)
|
||||
*
|
||||
* @var Locale
|
||||
*/
|
||||
public $locale;
|
||||
|
||||
public function __construct($id, EagerProduct $product, Locale $locale)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->product = $product;
|
||||
$this->locale = $locale;
|
||||
}
|
||||
}
|
||||
47
tests/Tests/ORM/Functional/Ticket/GH11149/GH11149Test.php
Normal file
47
tests/Tests/ORM/Functional/Ticket/GH11149/GH11149Test.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket\GH11149;
|
||||
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\Persistence\Proxy;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
class GH11149Test extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->setUpEntitySchema([
|
||||
Locale::class,
|
||||
EagerProduct::class,
|
||||
EagerProductTranslation::class,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testFetchEagerModeWithIndexBy(): void
|
||||
{
|
||||
// Load entities into database
|
||||
$this->_em->persist($product = new EagerProduct(11149));
|
||||
$this->_em->persist($locale = new Locale('fr_FR'));
|
||||
$this->_em->persist(new EagerProductTranslation(11149, $product, $locale));
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
// Fetch entity from database
|
||||
$product = $this->_em->find(EagerProduct::class, 11149);
|
||||
|
||||
// Assert associated entity is loaded eagerly
|
||||
static::assertInstanceOf(EagerProduct::class, $product);
|
||||
static::assertInstanceOf(PersistentCollection::class, $product->translations);
|
||||
static::assertTrue($product->translations->isInitialized());
|
||||
static::assertCount(1, $product->translations);
|
||||
|
||||
// Assert associated entity is indexed by given property
|
||||
$translation = $product->translations->get('fr_FR');
|
||||
static::assertInstanceOf(EagerProductTranslation::class, $translation);
|
||||
static::assertNotInstanceOf(Proxy::class, $translation);
|
||||
}
|
||||
}
|
||||
27
tests/Tests/ORM/Functional/Ticket/GH11149/Locale.php
Normal file
27
tests/Tests/ORM/Functional/Ticket/GH11149/Locale.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket\GH11149;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table("gh11149_locale")
|
||||
*/
|
||||
class Locale
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="string", length=5)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $code;
|
||||
|
||||
public function __construct(string $code)
|
||||
{
|
||||
$this->code = $code;
|
||||
}
|
||||
}
|
||||
135
tests/Tests/ORM/Functional/Ticket/GH11163Test.php
Normal file
135
tests/Tests/ORM/Functional/Ticket/GH11163Test.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
class GH11163Test extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->setUpEntitySchema([
|
||||
GH11163Bucket::class,
|
||||
GH11163BucketItem::class,
|
||||
]);
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
$conn = static::$sharedConn;
|
||||
$conn->executeStatement('DELETE FROM GH11163BucketItem');
|
||||
$conn->executeStatement('DELETE FROM GH11163Bucket');
|
||||
}
|
||||
|
||||
public function testFetchEagerModeWithOrderBy(): void
|
||||
{
|
||||
// Load entities into database
|
||||
$this->_em->persist($bucket = new GH11163Bucket(11163));
|
||||
$this->_em->persist(new GH11163BucketItem(1, $bucket, 2));
|
||||
$this->_em->persist(new GH11163BucketItem(2, $bucket, 3));
|
||||
$this->_em->persist(new GH11163BucketItem(3, $bucket, 1));
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
// Fetch entity from database
|
||||
$dql = 'SELECT bucket FROM ' . GH11163Bucket::class . ' bucket WHERE bucket.id = :id';
|
||||
$bucket = $this->_em->createQuery($dql)
|
||||
->setParameter('id', 11163)
|
||||
->getSingleResult();
|
||||
|
||||
// Assert associated entity is loaded eagerly
|
||||
static::assertInstanceOf(GH11163Bucket::class, $bucket);
|
||||
static::assertInstanceOf(PersistentCollection::class, $bucket->items);
|
||||
static::assertTrue($bucket->items->isInitialized());
|
||||
|
||||
static::assertCount(3, $bucket->items);
|
||||
|
||||
// Assert order of entities
|
||||
static::assertSame(1, $bucket->items[0]->position);
|
||||
static::assertSame(3, $bucket->items[0]->id);
|
||||
|
||||
static::assertSame(2, $bucket->items[1]->position);
|
||||
static::assertSame(1, $bucket->items[1]->id);
|
||||
|
||||
static::assertSame(3, $bucket->items[2]->position);
|
||||
static::assertSame(2, $bucket->items[2]->id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class GH11163Bucket
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(
|
||||
* targetEntity=GH11163BucketItem::class,
|
||||
* mappedBy="bucket",
|
||||
* fetch="EAGER"
|
||||
* )
|
||||
* @ORM\OrderBy({"position" = "ASC"})
|
||||
*
|
||||
* @var Collection<int, GH11163BucketItem>
|
||||
*/
|
||||
public $items;
|
||||
|
||||
public function __construct(int $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->items = new ArrayCollection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class GH11163BucketItem
|
||||
{
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=GH11163Bucket::class, inversedBy="items")
|
||||
* @ORM\JoinColumn(nullable=false)
|
||||
*
|
||||
* @var GH11163Bucket
|
||||
*/
|
||||
private $bucket;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", nullable=false)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $position;
|
||||
|
||||
public function __construct(int $id, GH11163Bucket $bucket, int $position)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->bucket = $bucket;
|
||||
$this->position = $position;
|
||||
}
|
||||
}
|
||||
40
tests/Tests/ORM/Functional/Ticket/GH11487Test.php
Normal file
40
tests/Tests/ORM/Functional/Ticket/GH11487Test.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\GeneratedValue;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
class GH11487Test extends OrmFunctionalTestCase
|
||||
{
|
||||
public function testItThrowsASyntaxErrorOnUnfinishedQuery(): void
|
||||
{
|
||||
$this->expectException(QueryException::class);
|
||||
$this->expectExceptionMessage('Syntax Error');
|
||||
$this->_em->createQuery('UPDATE Doctrine\Tests\ORM\Functional\Ticket\TaxType t SET t.default =')->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class TaxType
|
||||
{
|
||||
/**
|
||||
* @var int|null
|
||||
* @Column(type="integer")
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* @Column(type="boolean")
|
||||
*/
|
||||
public $default = false;
|
||||
}
|
||||
137
tests/Tests/ORM/Functional/Ticket/GH11500Test.php
Normal file
137
tests/Tests/ORM/Functional/Ticket/GH11500Test.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Exception\ORMException;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
class GH11500Test extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->setUpEntitySchema([
|
||||
GH11500AbstractTestEntity::class,
|
||||
GH11500TestEntityOne::class,
|
||||
GH11500TestEntityTwo::class,
|
||||
GH11500TestEntityHolder::class,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ORMException
|
||||
*/
|
||||
public function testDeleteOneToManyCollectionWithSingleTableInheritance(): void
|
||||
{
|
||||
$testEntityOne = new GH11500TestEntityOne();
|
||||
$testEntityTwo = new GH11500TestEntityTwo();
|
||||
$testEntityHolder = new GH11500TestEntityHolder();
|
||||
|
||||
$testEntityOne->testEntityHolder = $testEntityHolder;
|
||||
$testEntityHolder->testEntityOnes->add($testEntityOne);
|
||||
|
||||
$testEntityTwo->testEntityHolder = $testEntityHolder;
|
||||
$testEntityHolder->testEntityTwos->add($testEntityTwo);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$em->persist($testEntityOne);
|
||||
$em->persist($testEntityTwo);
|
||||
$em->persist($testEntityHolder);
|
||||
$em->flush();
|
||||
|
||||
$testEntityTwosBeforeRemovalOfTestEntityOnes = $testEntityHolder->testEntityTwos->toArray();
|
||||
|
||||
$testEntityHolder->testEntityOnes = new ArrayCollection();
|
||||
$em->persist($testEntityHolder);
|
||||
$em->flush();
|
||||
$em->refresh($testEntityHolder);
|
||||
|
||||
static::assertEmpty($testEntityHolder->testEntityOnes->toArray(), 'All records should have been deleted');
|
||||
static::assertEquals($testEntityTwosBeforeRemovalOfTestEntityOnes, $testEntityHolder->testEntityTwos->toArray(), 'Different Entity\'s records should not have been deleted');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="one_to_many_single_table_inheritance_test_entities")
|
||||
* @ORM\InheritanceType("SINGLE_TABLE")
|
||||
* @ORM\DiscriminatorColumn(name="type", type="string")
|
||||
* @ORM\DiscriminatorMap({"test_entity_one"="GH11500TestEntityOne", "test_entity_two"="GH11500TestEntityTwo"})
|
||||
*/
|
||||
class GH11500AbstractTestEntity
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
}
|
||||
|
||||
|
||||
/** @ORM\Entity */
|
||||
class GH11500TestEntityOne extends GH11500AbstractTestEntity
|
||||
{
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="GH11500TestEntityHolder", inversedBy="testEntityOnes")
|
||||
* @ORM\JoinColumn(name="test_entity_holder_id", referencedColumnName="id")
|
||||
*
|
||||
* @var GH11500TestEntityHolder
|
||||
*/
|
||||
public $testEntityHolder;
|
||||
}
|
||||
|
||||
/** @ORM\Entity */
|
||||
class GH11500TestEntityTwo extends GH11500AbstractTestEntity
|
||||
{
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="GH11500TestEntityHolder", inversedBy="testEntityTwos")
|
||||
* @ORM\JoinColumn(name="test_entity_holder_id", referencedColumnName="id")
|
||||
*
|
||||
* @var GH11500TestEntityHolder
|
||||
*/
|
||||
public $testEntityHolder;
|
||||
}
|
||||
|
||||
/** @ORM\Entity */
|
||||
class GH11500TestEntityHolder
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="GH11500TestEntityOne", mappedBy="testEntityHolder", orphanRemoval=true)
|
||||
*
|
||||
* @var Collection
|
||||
*/
|
||||
public $testEntityOnes;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="GH11500TestEntityTwo", mappedBy="testEntityHolder", orphanRemoval=true)
|
||||
*
|
||||
* @var Collection
|
||||
*/
|
||||
public $testEntityTwos;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->testEntityOnes = new ArrayCollection();
|
||||
$this->testEntityTwos = new ArrayCollection();
|
||||
}
|
||||
}
|
||||
88
tests/Tests/ORM/Functional/Ticket/GH6123Test.php
Normal file
88
tests/Tests/ORM/Functional/Ticket/GH6123Test.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
class GH6123Test extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->createSchemaForModels(
|
||||
GH6123Entity::class
|
||||
);
|
||||
}
|
||||
|
||||
public function testLoadingRemovedEntityFromDatabaseDoesNotCreateNewManagedEntityInstance(): void
|
||||
{
|
||||
$entity = new GH6123Entity();
|
||||
$this->_em->persist($entity);
|
||||
$this->_em->flush();
|
||||
|
||||
self::assertSame(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($entity));
|
||||
self::assertFalse($this->_em->getUnitOfWork()->isScheduledForDelete($entity));
|
||||
|
||||
$this->_em->remove($entity);
|
||||
|
||||
$freshEntity = $this->loadEntityFromDatabase($entity->id);
|
||||
self::assertSame($entity, $freshEntity);
|
||||
|
||||
self::assertSame(UnitOfWork::STATE_REMOVED, $this->_em->getUnitOfWork()->getEntityState($freshEntity));
|
||||
self::assertTrue($this->_em->getUnitOfWork()->isScheduledForDelete($freshEntity));
|
||||
}
|
||||
|
||||
public function testRemovedEntityCanBePersistedAgain(): void
|
||||
{
|
||||
$entity = new GH6123Entity();
|
||||
$this->_em->persist($entity);
|
||||
$this->_em->flush();
|
||||
|
||||
$this->_em->remove($entity);
|
||||
self::assertSame(UnitOfWork::STATE_REMOVED, $this->_em->getUnitOfWork()->getEntityState($entity));
|
||||
self::assertTrue($this->_em->getUnitOfWork()->isScheduledForDelete($entity));
|
||||
|
||||
$this->loadEntityFromDatabase($entity->id);
|
||||
|
||||
$this->_em->persist($entity);
|
||||
self::assertSame(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($entity));
|
||||
self::assertFalse($this->_em->getUnitOfWork()->isScheduledForDelete($entity));
|
||||
|
||||
$this->_em->flush();
|
||||
}
|
||||
|
||||
private function loadEntityFromDatabase(int $id): ?GH6123Entity
|
||||
{
|
||||
return $this->_em->createQueryBuilder()
|
||||
->select('e')
|
||||
->from(GH6123Entity::class, 'e')
|
||||
->where('e.id = :id')
|
||||
->setParameter('id', $id)
|
||||
->getQuery()
|
||||
->getOneOrNullResult();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
#[ORM\Entity]
|
||||
class GH6123Entity
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
* @var int
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: Types::INTEGER)]
|
||||
public $id;
|
||||
}
|
||||
@@ -10,9 +10,9 @@ use Doctrine\ORM\Mapping\GeneratedValue;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
||||
use Doctrine\ORM\Query\AST\Node;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
final class GH7286Test extends OrmFunctionalTestCase
|
||||
@@ -114,14 +114,14 @@ class GH7286CustomConcat extends FunctionNode
|
||||
|
||||
public function parse(Parser $parser): void
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->first = $parser->StringPrimary();
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(TokenType::T_COMMA);
|
||||
$this->second = $parser->StringPrimary();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
|
||||
public function getSql(SqlWalker $walker): string
|
||||
|
||||
@@ -12,6 +12,7 @@ use Doctrine\ORM\Events;
|
||||
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
|
||||
use Doctrine\ORM\ORMException;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\Tests\Models\Hydration\SimpleEntity;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
@@ -154,4 +155,33 @@ class AbstractHydratorTest extends OrmFunctionalTestCase
|
||||
$this->expectException(ORMException::class);
|
||||
$this->hydrator->hydrateAll($this->mockResult, $this->mockResultMapping);
|
||||
}
|
||||
|
||||
public function testToIterableIfYieldAndBreakBeforeFinishAlwaysCleansUp(): void
|
||||
{
|
||||
$this->setUpEntitySchema([SimpleEntity::class]);
|
||||
|
||||
$entity1 = new SimpleEntity();
|
||||
$this->_em->persist($entity1);
|
||||
$entity2 = new SimpleEntity();
|
||||
$this->_em->persist($entity2);
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$evm = $this->_em->getEventManager();
|
||||
|
||||
$q = $this->_em->createQuery('SELECT e.id FROM ' . SimpleEntity::class . ' e');
|
||||
|
||||
// select two entities, but do no iterate
|
||||
$q->toIterable();
|
||||
self::assertCount(0, $evm->getListeners(Events::onClear));
|
||||
|
||||
// select two entities, but abort after first record
|
||||
foreach ($q->toIterable() as $result) {
|
||||
self::assertCount(1, $evm->getListeners(Events::onClear));
|
||||
break;
|
||||
}
|
||||
|
||||
self::assertCount(0, $evm->getListeners(Events::onClear));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\Tests\ORM\Mapping;
|
||||
|
||||
use ArrayObject;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
|
||||
use Doctrine\ORM\Events;
|
||||
use Doctrine\ORM\Mapping\ChainTypedFieldMapper;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
@@ -54,6 +55,8 @@ require_once __DIR__ . '/../../Models/Global/GlobalNamespaceModel.php';
|
||||
|
||||
class ClassMetadataTest extends OrmTestCase
|
||||
{
|
||||
use VerifyDeprecations;
|
||||
|
||||
public function testClassMetadataInstanceSerialization(): void
|
||||
{
|
||||
$cm = new ClassMetadata(CMS\CmsUser::class);
|
||||
@@ -1379,6 +1382,16 @@ class ClassMetadataTest extends OrmTestCase
|
||||
'columnPrefix' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testInvalidCallToGetAssociationMappedByTargetFieldIsDeprecated(): void
|
||||
{
|
||||
$metadata = new ClassMetadata(self::class);
|
||||
$metadata->mapOneToOne(['fieldName' => 'foo', 'targetEntity' => 'bar']);
|
||||
|
||||
$this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/11309');
|
||||
|
||||
$metadata->getAssociationMappedByTargetField('foo');
|
||||
}
|
||||
}
|
||||
|
||||
/** @MappedSuperclass */
|
||||
|
||||
@@ -64,8 +64,7 @@ class ProxyFactoryTest extends OrmTestCase
|
||||
public function testReferenceProxyDelegatesLoadingToThePersister(): void
|
||||
{
|
||||
$identifier = ['id' => 42];
|
||||
$proxyClass = 'Proxies\__CG__\Doctrine\Tests\Models\ECommerce\ECommerceFeature';
|
||||
$persister = $this->getMockBuilderWithOnlyMethods(BasicEntityPersister::class, ['load'])
|
||||
$persister = $this->getMockBuilderWithOnlyMethods(BasicEntityPersister::class, ['loadById'])
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
@@ -75,8 +74,8 @@ class ProxyFactoryTest extends OrmTestCase
|
||||
|
||||
$persister
|
||||
->expects(self::atLeastOnce())
|
||||
->method('load')
|
||||
->with(self::equalTo($identifier), self::isInstanceOf($proxyClass))
|
||||
->method('loadById')
|
||||
->with(self::equalTo($identifier))
|
||||
->will(self::returnValue($proxy));
|
||||
|
||||
$proxy->getDescription();
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\Tests\ORM\Query;
|
||||
|
||||
use Doctrine\Common\Lexer\Token;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
|
||||
class LexerTest extends OrmTestCase
|
||||
@@ -35,7 +36,7 @@ class LexerTest extends OrmTestCase
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
|
||||
self::assertEquals(Lexer::T_ALL, $token->type);
|
||||
self::assertEquals(TokenType::T_ALL, $token->type);
|
||||
}
|
||||
|
||||
public function testScannerRecognizesDecimalInteger(): void
|
||||
@@ -43,7 +44,7 @@ class LexerTest extends OrmTestCase
|
||||
$lexer = new Lexer('1234');
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
self::assertEquals(Lexer::T_INTEGER, $token->type);
|
||||
self::assertEquals(TokenType::T_INTEGER, $token->type);
|
||||
self::assertEquals(1234, $token->value);
|
||||
}
|
||||
|
||||
@@ -52,7 +53,7 @@ class LexerTest extends OrmTestCase
|
||||
$lexer = new Lexer('1.234');
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
self::assertEquals(Lexer::T_FLOAT, $token->type);
|
||||
self::assertEquals(TokenType::T_FLOAT, $token->type);
|
||||
self::assertEquals(1.234, $token->value);
|
||||
}
|
||||
|
||||
@@ -61,7 +62,7 @@ class LexerTest extends OrmTestCase
|
||||
$lexer = new Lexer('1.2e3');
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
self::assertEquals(Lexer::T_FLOAT, $token->type);
|
||||
self::assertEquals(TokenType::T_FLOAT, $token->type);
|
||||
self::assertEquals(1.2e3, $token->value);
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ class LexerTest extends OrmTestCase
|
||||
$lexer = new Lexer('0.2e3');
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
self::assertEquals(Lexer::T_FLOAT, $token->type);
|
||||
self::assertEquals(TokenType::T_FLOAT, $token->type);
|
||||
self::assertEquals(.2e3, $token->value);
|
||||
}
|
||||
|
||||
@@ -79,7 +80,7 @@ class LexerTest extends OrmTestCase
|
||||
$lexer = new Lexer('7E-10');
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
self::assertEquals(Lexer::T_FLOAT, $token->type);
|
||||
self::assertEquals(TokenType::T_FLOAT, $token->type);
|
||||
self::assertEquals(7E-10, $token->value);
|
||||
}
|
||||
|
||||
@@ -88,7 +89,7 @@ class LexerTest extends OrmTestCase
|
||||
$lexer = new Lexer('123456789.01');
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
self::assertEquals(Lexer::T_FLOAT, $token->type);
|
||||
self::assertEquals(TokenType::T_FLOAT, $token->type);
|
||||
self::assertEquals(1.2345678901e8, $token->value);
|
||||
}
|
||||
|
||||
@@ -97,12 +98,12 @@ class LexerTest extends OrmTestCase
|
||||
$lexer = new Lexer('- 1.234e2');
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
self::assertEquals(Lexer::T_MINUS, $token->type);
|
||||
self::assertEquals(TokenType::T_MINUS, $token->type);
|
||||
self::assertEquals('-', $token->value);
|
||||
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
self::assertEquals(Lexer::T_FLOAT, $token->type);
|
||||
self::assertEquals(TokenType::T_FLOAT, $token->type);
|
||||
self::assertNotEquals(-1.234e2, $token->value);
|
||||
self::assertEquals(1.234e2, $token->value);
|
||||
}
|
||||
@@ -112,7 +113,7 @@ class LexerTest extends OrmTestCase
|
||||
$lexer = new Lexer("'This is a string.'");
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
self::assertEquals(Lexer::T_STRING, $token->type);
|
||||
self::assertEquals(TokenType::T_STRING, $token->type);
|
||||
self::assertEquals('This is a string.', $token->value);
|
||||
}
|
||||
|
||||
@@ -121,7 +122,7 @@ class LexerTest extends OrmTestCase
|
||||
$lexer = new Lexer("'abc''defg'''");
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
self::assertEquals(Lexer::T_STRING, $token->type);
|
||||
self::assertEquals(TokenType::T_STRING, $token->type);
|
||||
self::assertEquals("abc'defg'", $token->value);
|
||||
}
|
||||
|
||||
@@ -130,7 +131,7 @@ class LexerTest extends OrmTestCase
|
||||
$lexer = new Lexer('?1');
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
self::assertEquals(Lexer::T_INPUT_PARAMETER, $token->type);
|
||||
self::assertEquals(TokenType::T_INPUT_PARAMETER, $token->type);
|
||||
self::assertEquals('?1', $token->value);
|
||||
}
|
||||
|
||||
@@ -139,7 +140,7 @@ class LexerTest extends OrmTestCase
|
||||
$lexer = new Lexer(':name');
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
self::assertEquals(Lexer::T_INPUT_PARAMETER, $token->type);
|
||||
self::assertEquals(TokenType::T_INPUT_PARAMETER, $token->type);
|
||||
self::assertEquals(':name', $token->value);
|
||||
}
|
||||
|
||||
@@ -148,7 +149,7 @@ class LexerTest extends OrmTestCase
|
||||
$lexer = new Lexer(':_name');
|
||||
$lexer->moveNext();
|
||||
$token = $lexer->lookahead;
|
||||
self::assertEquals(Lexer::T_INPUT_PARAMETER, $token->type);
|
||||
self::assertEquals(TokenType::T_INPUT_PARAMETER, $token->type);
|
||||
self::assertEquals(':_name', $token->value);
|
||||
}
|
||||
|
||||
@@ -158,17 +159,17 @@ class LexerTest extends OrmTestCase
|
||||
$lexer = new Lexer($dql);
|
||||
|
||||
$tokens = [
|
||||
new Token('SELECT', Lexer::T_SELECT, 0),
|
||||
new Token('u', Lexer::T_IDENTIFIER, 7),
|
||||
new Token('FROM', Lexer::T_FROM, 9),
|
||||
new Token('My\Namespace\User', Lexer::T_FULLY_QUALIFIED_NAME, 14),
|
||||
new Token('u', Lexer::T_IDENTIFIER, 32),
|
||||
new Token('WHERE', Lexer::T_WHERE, 34),
|
||||
new Token('u', Lexer::T_IDENTIFIER, 40),
|
||||
new Token('.', Lexer::T_DOT, 41),
|
||||
new Token('name', Lexer::T_IDENTIFIER, 42),
|
||||
new Token('=', Lexer::T_EQUALS, 47),
|
||||
new Token("Jack O'Neil", Lexer::T_STRING, 49),
|
||||
new Token('SELECT', TokenType::T_SELECT, 0),
|
||||
new Token('u', TokenType::T_IDENTIFIER, 7),
|
||||
new Token('FROM', TokenType::T_FROM, 9),
|
||||
new Token('My\Namespace\User', TokenType::T_FULLY_QUALIFIED_NAME, 14),
|
||||
new Token('u', TokenType::T_IDENTIFIER, 32),
|
||||
new Token('WHERE', TokenType::T_WHERE, 34),
|
||||
new Token('u', TokenType::T_IDENTIFIER, 40),
|
||||
new Token('.', TokenType::T_DOT, 41),
|
||||
new Token('name', TokenType::T_IDENTIFIER, 42),
|
||||
new Token('=', TokenType::T_EQUALS, 47),
|
||||
new Token("Jack O'Neil", TokenType::T_STRING, 49),
|
||||
];
|
||||
|
||||
foreach ($tokens as $expected) {
|
||||
@@ -186,15 +187,15 @@ class LexerTest extends OrmTestCase
|
||||
public static function provideTokens(): array
|
||||
{
|
||||
return [
|
||||
[Lexer::T_IDENTIFIER, 'u'], // one char
|
||||
[Lexer::T_IDENTIFIER, 'someIdentifier'],
|
||||
[Lexer::T_IDENTIFIER, 's0m31d3nt1f13r'], // including digits
|
||||
[Lexer::T_IDENTIFIER, 'some_identifier'], // including underscore
|
||||
[Lexer::T_IDENTIFIER, '_some_identifier'], // starts with underscore
|
||||
[Lexer::T_IDENTIFIER, 'comma'], // name of a token class with value < 100 (whitebox test)
|
||||
[Lexer::T_FULLY_QUALIFIED_NAME, 'Some\Class'], // DQL class reference
|
||||
[Lexer::T_ALIASED_NAME, 'Some:Name'],
|
||||
[Lexer::T_ALIASED_NAME, 'Some:Subclassed\Name'],
|
||||
[TokenType::T_IDENTIFIER, 'u'], // one char
|
||||
[TokenType::T_IDENTIFIER, 'someIdentifier'],
|
||||
[TokenType::T_IDENTIFIER, 's0m31d3nt1f13r'], // including digits
|
||||
[TokenType::T_IDENTIFIER, 'some_identifier'], // including underscore
|
||||
[TokenType::T_IDENTIFIER, '_some_identifier'], // starts with underscore
|
||||
[TokenType::T_IDENTIFIER, 'comma'], // name of a token class with value < 100 (whitebox test)
|
||||
[TokenType::T_FULLY_QUALIFIED_NAME, 'Some\Class'], // DQL class reference
|
||||
[TokenType::T_ALIASED_NAME, 'Some:Name'],
|
||||
[TokenType::T_ALIASED_NAME, 'Some:Subclassed\Name'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ namespace Doctrine\Tests\ORM\Query;
|
||||
|
||||
use Doctrine\Common\Persistence\PersistentObject;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
use Doctrine\Tests\Models\CMS\CmsUser;
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
use stdClass;
|
||||
@@ -124,11 +124,11 @@ class ParserTest extends OrmTestCase
|
||||
* but in LexerTest.
|
||||
*/
|
||||
return [
|
||||
[Lexer::T_WHERE, 'where'], // keyword
|
||||
[Lexer::T_DOT, '.'], // token that cannot be an identifier
|
||||
[Lexer::T_IDENTIFIER, 'someIdentifier'],
|
||||
[Lexer::T_IDENTIFIER, 'from'], // also a terminal string (the "FROM" keyword) as in DDC-505
|
||||
[Lexer::T_IDENTIFIER, 'comma'],
|
||||
[TokenType::T_WHERE, 'where'], // keyword
|
||||
[TokenType::T_DOT, '.'], // token that cannot be an identifier
|
||||
[TokenType::T_IDENTIFIER, 'someIdentifier'],
|
||||
[TokenType::T_IDENTIFIER, 'from'], // also a terminal string (the "FROM" keyword) as in DDC-505
|
||||
[TokenType::T_IDENTIFIER, 'comma'],
|
||||
// not even a terminal string, but the name of a constant in the Lexer (whitebox test)
|
||||
];
|
||||
}
|
||||
@@ -137,15 +137,15 @@ class ParserTest extends OrmTestCase
|
||||
public static function invalidMatches(): array
|
||||
{
|
||||
return [
|
||||
[Lexer::T_DOT, 'ALL'], // ALL is a terminal string (reserved keyword) and also possibly an identifier
|
||||
[Lexer::T_DOT, ','], // "," is a token on its own, but cannot be used as identifier
|
||||
[Lexer::T_WHERE, 'WITH'], // as in DDC-3697
|
||||
[Lexer::T_WHERE, '.'],
|
||||
[TokenType::T_DOT, 'ALL'], // ALL is a terminal string (reserved keyword) and also possibly an identifier
|
||||
[TokenType::T_DOT, ','], // "," is a token on its own, but cannot be used as identifier
|
||||
[TokenType::T_WHERE, 'WITH'], // as in DDC-3697
|
||||
[TokenType::T_WHERE, '.'],
|
||||
|
||||
// The following are qualified or aliased names and must not be accepted where only an Identifier is expected
|
||||
[Lexer::T_IDENTIFIER, '\\Some\\Class'],
|
||||
[Lexer::T_IDENTIFIER, 'Some\\Class'],
|
||||
[Lexer::T_IDENTIFIER, 'Some:Name'],
|
||||
[TokenType::T_IDENTIFIER, '\\Some\\Class'],
|
||||
[TokenType::T_IDENTIFIER, 'Some\\Class'],
|
||||
[TokenType::T_IDENTIFIER, 'Some:Name'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ class ParserTest extends OrmTestCase
|
||||
$parser = new Parser($query);
|
||||
|
||||
$this->expectException(QueryException::class);
|
||||
$parser->match(Lexer::T_SELECT);
|
||||
$parser->match(TokenType::T_SELECT);
|
||||
}
|
||||
|
||||
private function createParser(string $dql): Parser
|
||||
|
||||
@@ -13,10 +13,13 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\DBAL\ArrayParameterType;
|
||||
use Doctrine\DBAL\Cache\QueryCacheProfile;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Internal\Hydration\IterableResult;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\Parameter;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
@@ -88,6 +91,33 @@ class QueryTest extends OrmTestCase
|
||||
self::assertEquals($parameters, $query->getParameters());
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param LockMode::* $lockMode
|
||||
*
|
||||
* @dataProvider provideLockModes
|
||||
*/
|
||||
public function testSetLockMode(int $lockMode): void
|
||||
{
|
||||
$query = $this->entityManager->wrapInTransaction(static function (EntityManagerInterface $em) use ($lockMode): Query {
|
||||
$query = $em->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1');
|
||||
$query->setLockMode($lockMode);
|
||||
|
||||
return $query;
|
||||
});
|
||||
|
||||
self::assertSame($lockMode, $query->getLockMode());
|
||||
self::assertSame($lockMode, $query->getHint(Query::HINT_LOCK_MODE));
|
||||
}
|
||||
|
||||
/** @psalm-return list<array{LockMode::*}> */
|
||||
public static function provideLockModes(): array
|
||||
{
|
||||
return [
|
||||
[LockMode::PESSIMISTIC_READ],
|
||||
[LockMode::PESSIMISTIC_WRITE],
|
||||
];
|
||||
}
|
||||
|
||||
public function testFree(): void
|
||||
{
|
||||
$query = $this->entityManager->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1');
|
||||
|
||||
@@ -18,10 +18,10 @@ use Doctrine\ORM\Mapping\Id;
|
||||
use Doctrine\ORM\Query as ORMQuery;
|
||||
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
||||
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
use Doctrine\Tests\DbalTypes\NegativeToPositiveType;
|
||||
use Doctrine\Tests\Models\CMS\CmsGroup;
|
||||
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
|
||||
@@ -2186,12 +2186,12 @@ class MyAbsFunction extends FunctionNode
|
||||
{
|
||||
$lexer = $parser->getLexer();
|
||||
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$parser->match(TokenType::T_IDENTIFIER);
|
||||
$parser->match(TokenType::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
/** @Entity */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user