mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 15:02:22 +01:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40a0964f06 | ||
|
|
08a9e60ed0 | ||
|
|
3e3c023c95 | ||
|
|
5e6d5c06a9 | ||
|
|
1622b7877d | ||
|
|
80aae2796d | ||
|
|
528ef40fc4 | ||
|
|
820a0da4c1 | ||
|
|
fcd02b1ee2 | ||
|
|
abcad6fa45 | ||
|
|
1b6cf58a1a | ||
|
|
6501890ab5 | ||
|
|
e399d21fb3 | ||
|
|
16f355f0cc | ||
|
|
94d45a036f | ||
|
|
9acca2252f | ||
|
|
a809a71aa6 | ||
|
|
bd4449c462 | ||
|
|
e3e96745cc | ||
|
|
12e0cefba1 | ||
|
|
21221f73cc | ||
|
|
ab5e9e393b | ||
|
|
e62571c8f4 | ||
|
|
98f9de2af6 | ||
|
|
52a6a21387 | ||
|
|
83c81f6c41 | ||
|
|
4fc8629414 | ||
|
|
791667a9e4 | ||
|
|
feb27f00c1 | ||
|
|
abd9186d00 | ||
|
|
719d007a81 | ||
|
|
08d3f72755 | ||
|
|
779781173a | ||
|
|
76c4539ffa | ||
|
|
0f8d193512 | ||
|
|
cc314d0fb7 | ||
|
|
c9c493b2fe | ||
|
|
e6eef1a97d | ||
|
|
8d4718f875 | ||
|
|
c0dfba2ef3 | ||
|
|
0efac09141 | ||
|
|
efb6cebd41 | ||
|
|
e4769d3191 | ||
|
|
cf408ad9ae | ||
|
|
7c29078051 | ||
|
|
b59189ab48 | ||
|
|
401a0c4fe9 | ||
|
|
dba9d72b2d | ||
|
|
fe0647053a | ||
|
|
7b3db4a037 | ||
|
|
e5e3166747 | ||
|
|
afbf293c94 | ||
|
|
1d218bae30 | ||
|
|
7baef1e120 | ||
|
|
5049b615c5 | ||
|
|
40fbbf4429 | ||
|
|
6f98147d09 | ||
|
|
cfadb5499d | ||
|
|
9ce9ae2818 | ||
|
|
fdb9d44538 | ||
|
|
a9fcaf1d18 |
@@ -6,22 +6,52 @@
|
||||
"docsSlug": "doctrine-orm",
|
||||
"versions": [
|
||||
{
|
||||
"name": "3.0",
|
||||
"branchName": "3.0.x",
|
||||
"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": "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",
|
||||
"maintained": true
|
||||
},
|
||||
{
|
||||
"name": "2.18",
|
||||
"branchName": "2.18.x",
|
||||
"slug": "2.18",
|
||||
"upcoming": true
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.17",
|
||||
"branchName": "2.17.x",
|
||||
"slug": "2.17",
|
||||
"current": true
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.16",
|
||||
|
||||
14
.github/workflows/continuous-integration.yml
vendored
14
.github/workflows/continuous-integration.yml
vendored
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -372,7 +372,7 @@ jobs:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Download coverage files"
|
||||
uses: "actions/download-artifact@v3"
|
||||
uses: "actions/download-artifact@v4"
|
||||
with:
|
||||
path: "reports"
|
||||
|
||||
|
||||
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"
|
||||
@@ -40,9 +40,10 @@ jobs:
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
- name: "Add dummy title to the sidebar"
|
||||
- name: "Add orphan metadata where needed"
|
||||
run: |
|
||||
printf '%s\n%s\n\n%s\n' "Dummy title" "===========" "$(cat docs/en/sidebar.rst)" > docs/en/sidebar.rst
|
||||
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 'Unknown directive' | ( ! grep WARNING )"
|
||||
run: "vendor/bin/guides -vvv --no-progress docs/en 2>&1 | grep -v 'No template found for rendering directive' | ( ! grep WARNING )"
|
||||
|
||||
@@ -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@4.0.0"
|
||||
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
@@ -10,7 +10,7 @@ on:
|
||||
- src/**
|
||||
- phpstan*
|
||||
- psalm*
|
||||
- tests/Doctrine/StaticAnalysis/**
|
||||
- tests/StaticAnalysis/**
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
@@ -20,7 +20,7 @@ on:
|
||||
- src/**
|
||||
- phpstan*
|
||||
- psalm*
|
||||
- tests/Doctrine/StaticAnalysis/**
|
||||
- tests/StaticAnalysis/**
|
||||
|
||||
jobs:
|
||||
static-analysis-phpstan:
|
||||
@@ -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"
|
||||
@@ -89,7 +89,7 @@ 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"
|
||||
|
||||
40
README.md
40
README.md
@@ -1,7 +1,7 @@
|
||||
| [3.0.x][3.0] | [2.18.x][2.18] | [2.17.x][2.17] |
|
||||
|:----------------:|:----------------:|:----------:|
|
||||
| [![Build status][3.0 image]][3.0] | [![Build status][2.18 image]][2.18] | [![Build status][2.17 image]][2.17] |
|
||||
| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.18 coverage image]][2.18 coverage] | [![Coverage Status][2.17 coverage image]][2.17 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)
|
||||
|
||||
@@ -18,15 +18,23 @@ without requiring unnecessary code duplication.
|
||||
* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/stable/index.html)
|
||||
|
||||
|
||||
[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.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
|
||||
[2.17 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.17.x
|
||||
[2.17]: https://github.com/doctrine/orm/tree/2.17.x
|
||||
[2.17 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.17.x/graph/badge.svg
|
||||
[2.17 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.17.x
|
||||
[4.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=4.0.x
|
||||
[4.0]: https://github.com/doctrine/orm/tree/4.0.x
|
||||
[4.0 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
|
||||
[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
|
||||
|
||||
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.35",
|
||||
"phpstan/phpstan": "~1.4.10 || 1.10.59",
|
||||
"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.16.0"
|
||||
"vimeo/psalm": "4.30.0 || 5.22.2"
|
||||
},
|
||||
"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)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Implementing ArrayAccess for Domain Objects
|
||||
===========================================
|
||||
|
||||
.. sectionauthor:: Roman Borschel (roman@code-factory.org)
|
||||
.. sectionauthor:: Roman Borschel <roman@code-factory.org>
|
||||
|
||||
This recipe will show you how to implement ArrayAccess for your
|
||||
domain objects in order to allow more uniform access, for example
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Implementing the Notify ChangeTracking Policy
|
||||
=============================================
|
||||
|
||||
.. sectionauthor:: Roman Borschel (roman@code-factory.org)
|
||||
.. sectionauthor:: Roman Borschel <roman@code-factory.org>
|
||||
|
||||
The NOTIFY change-tracking policy is the most effective
|
||||
change-tracking policy provided by Doctrine but it requires some
|
||||
|
||||
@@ -96,7 +96,7 @@ Tutorials
|
||||
Changelogs
|
||||
----------
|
||||
|
||||
* `Upgrade <https://github.com/doctrine/doctrine2/blob/master/UPGRADE.md>`_
|
||||
* `Upgrade <https://github.com/doctrine/orm/blob/HEAD/UPGRADE.md>`_
|
||||
|
||||
Cookbook
|
||||
--------
|
||||
|
||||
@@ -462,7 +462,7 @@ Here is the list of possible generation strategies:
|
||||
a new entity is passed to ``EntityManager#persist``. NONE is the
|
||||
same as leaving off the ``#[GeneratedValue]`` entirely.
|
||||
- ``CUSTOM``: With this option, you can use the ``#[CustomIdGenerator]`` attribute.
|
||||
It will allow you to pass a :ref:`class of your own to generate the identifiers.<annref_customidgenerator>`
|
||||
It will allow you to pass a :ref:`class of your own to generate the identifiers. <annref_customidgenerator>`
|
||||
|
||||
Sequence Generator
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -18,14 +18,20 @@ especially what the strategies presented here provide help with.
|
||||
|
||||
.. note::
|
||||
|
||||
Having an SQL logger enabled when processing batches can have a serious impact on performance and resource usage.
|
||||
To avoid that you should remove the corresponding middleware.
|
||||
To remove all middlewares, you can use this line:
|
||||
Having an SQL logger enabled when processing batches can have a
|
||||
serious impact on performance and resource usage.
|
||||
To avoid that, you should use a PSR logger implementation that can be
|
||||
disabled at runtime.
|
||||
For example, with Monolog, you can use ``Logger::pushHandler()``
|
||||
to push a ``NullHandler`` to the logger instance, and then pop it
|
||||
when you need to enable logging again.
|
||||
|
||||
With DBAL 2, you can disable the SQL logger like below:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$em->getConnection()->getConfiguration()->setMiddlewares([]); // DBAL 3
|
||||
$em->getConnection()->getConfiguration()->setSQLLogger(null); // DBAL 2
|
||||
$em->getConnection()->getConfiguration()->setSQLLogger(null);
|
||||
|
||||
Bulk Inserts
|
||||
------------
|
||||
@@ -188,6 +194,3 @@ problems using the following approach:
|
||||
Iterating results is not possible with queries that
|
||||
fetch-join a collection-valued association. The nature of such SQL
|
||||
result sets is not suitable for incremental hydration.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,47 +131,47 @@ There are two ways to set up an event handler:
|
||||
* For *all events* you can create a Lifecycle Event Listener or Subscriber class and register
|
||||
it by calling ``$eventManager->addEventListener()`` or ``eventManager->addEventSubscriber()``,
|
||||
see
|
||||
:ref:`Listening and subscribing to Lifecycle Events<listening-and-subscribing-to-lifecycle-events>`
|
||||
:ref:`Listening and subscribing to Lifecycle Events <listening-and-subscribing-to-lifecycle-events>`
|
||||
* For *some events* (see table below), you can create a *Lifecycle Callback* method in the
|
||||
entity, see :ref:`Lifecycle Callbacks<lifecycle-callbacks>`.
|
||||
entity, see :ref:`Lifecycle Callbacks <lifecycle-callbacks>`.
|
||||
|
||||
.. _reference-events-lifecycle-events:
|
||||
|
||||
Events Overview
|
||||
---------------
|
||||
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| Event | Dispatched by | Lifecycle | Passed |
|
||||
| | | Callback | Argument |
|
||||
+=================================================================+=======================+===========+=====================================+
|
||||
| :ref:`preRemove<reference-events-pre-remove>` | ``$em->remove()`` | Yes | `PreRemoveEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postRemove<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `PostRemoveEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`prePersist<reference-events-pre-persist>` | ``$em->persist()`` | Yes | `PrePersistEventArgs`_ |
|
||||
| | on *initial* persist | | |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postPersist<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `PostPersistEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`preUpdate<reference-events-pre-update>` | ``$em->flush()`` | Yes | `PreUpdateEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postUpdate<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `PostUpdateEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postLoad<reference-events-post-load>` | Loading from database | Yes | `PostLoadEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`loadClassMetadata<reference-events-load-class-metadata>` | Loading of mapping | No | `LoadClassMetadataEventArgs`_ |
|
||||
| | metadata | | |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| ``onClassMetadataNotFound`` | ``MappingException`` | No | `OnClassMetadataNotFoundEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`preFlush<reference-events-pre-flush>` | ``$em->flush()`` | Yes | `PreFlushEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`onFlush<reference-events-on-flush>` | ``$em->flush()`` | No | `OnFlushEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postFlush<reference-events-post-flush>` | ``$em->flush()`` | No | `PostFlushEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`onClear<reference-events-on-clear>` | ``$em->clear()`` | No | `OnClearEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| Event | Dispatched by | Lifecycle | Passed |
|
||||
| | | Callback | Argument |
|
||||
+==================================================================+=======================+===========+=====================================+
|
||||
| :ref:`preRemove <reference-events-pre-remove>` | ``$em->remove()`` | Yes | `PreRemoveEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postRemove <reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `PostRemoveEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`prePersist <reference-events-pre-persist>` | ``$em->persist()`` | Yes | `PrePersistEventArgs`_ |
|
||||
| | on *initial* persist | | |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postPersist <reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `PostPersistEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`preUpdate <reference-events-pre-update>` | ``$em->flush()`` | Yes | `PreUpdateEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postUpdate <reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `PostUpdateEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postLoad <reference-events-post-load>` | Loading from database | Yes | `PostLoadEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`loadClassMetadata <reference-events-load-class-metadata>` | Loading of mapping | No | `LoadClassMetadataEventArgs`_ |
|
||||
| | metadata | | |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| ``onClassMetadataNotFound`` | ``MappingException`` | No | `OnClassMetadataNotFoundEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`preFlush <reference-events-pre-flush>` | ``$em->flush()`` | Yes | `PreFlushEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`onFlush <reference-events-on-flush>` | ``$em->flush()`` | No | `OnFlushEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postFlush <reference-events-post-flush>` | ``$em->flush()`` | No | `PostFlushEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`onClear <reference-events-on-clear>` | ``$em->clear()`` | No | `OnClearEventArgs`_ |
|
||||
+------------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
|
||||
.. warning::
|
||||
|
||||
@@ -358,7 +358,7 @@ behaviors across different entity classes.
|
||||
|
||||
Note that they require much more detailed knowledge about the inner
|
||||
workings of the ``EntityManager`` and ``UnitOfWork`` classes. Please
|
||||
read the :ref:`Implementing Event Listeners<reference-events-implementing-listeners>` section
|
||||
read the :ref:`Implementing Event Listeners <reference-events-implementing-listeners>` section
|
||||
carefully if you are trying to write your own listener.
|
||||
|
||||
For event subscribers, there are no surprises. They declare the
|
||||
@@ -471,11 +471,11 @@ prePersist
|
||||
There are two ways for the ``prePersist`` event to be triggered:
|
||||
|
||||
- One is when you call ``EntityManager::persist()``. The
|
||||
event is also called for all :ref:`cascaded associations<transitive-persistence>`.
|
||||
event is also called for all :ref:`cascaded associations <transitive-persistence>`.
|
||||
- The other is inside the ``flush()`` method when changes to associations are computed and
|
||||
this association is marked as :ref:`cascade: persist<transitive-persistence>`. Any new entity found
|
||||
this association is marked as :ref:`cascade: persist <transitive-persistence>`. Any new entity found
|
||||
during this operation is also persisted and ``prePersist`` called
|
||||
on it. This is called :ref:`persistence by reachability<persistence-by-reachability>`.
|
||||
on it. This is called :ref:`persistence by reachability <persistence-by-reachability>`.
|
||||
|
||||
In both cases you get passed a ``PrePersistEventArgs`` instance
|
||||
which has access to the entity and the entity manager.
|
||||
@@ -499,7 +499,7 @@ preRemove
|
||||
|
||||
The ``preRemove`` event is called on every entity immediately when it is passed
|
||||
to the ``EntityManager::remove()`` method. It is cascaded for all
|
||||
associations that are marked as :ref:`cascade: remove<transitive-persistence>`
|
||||
associations that are marked as :ref:`cascade: remove <transitive-persistence>`
|
||||
|
||||
It is not called for a DQL ``DELETE`` statement.
|
||||
|
||||
@@ -547,7 +547,7 @@ entities and their associations have been computed. This means, the
|
||||
- Collections scheduled for removal
|
||||
|
||||
To make use of the ``onFlush`` event you have to be familiar with the
|
||||
internal :ref:`UnitOfWork<unit-of-work>` API, which grants you access to the previously
|
||||
internal :ref:`UnitOfWork <unit-of-work>` API, which grants you access to the previously
|
||||
mentioned sets. See this example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -101,7 +101,7 @@ The many-to-many association is only supporting foreign keys in the table defini
|
||||
To work with many-to-many tables containing extra columns you have to use the
|
||||
foreign keys as primary keys feature of Doctrine ORM.
|
||||
|
||||
See :doc:`the tutorial on composite primary keys for more information<../tutorials/composite-primary-keys>`.
|
||||
See :doc:`the tutorial on composite primary keys for more information <../tutorials/composite-primary-keys>`.
|
||||
|
||||
|
||||
How can i paginate fetch-joined collections?
|
||||
|
||||
@@ -380,7 +380,7 @@ It is not supported to use overrides in entity inheritance scenarios.
|
||||
.. note::
|
||||
|
||||
When using traits, make sure not to miss the warnings given in the
|
||||
:doc:`Limitations and Known Issues</reference/limitations-and-known-issues>` chapter.
|
||||
:doc:`Limitations and Known Issues </reference/limitations-and-known-issues>` chapter.
|
||||
|
||||
|
||||
Association Override
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
reference/inheritance-mapping
|
||||
reference/working-with-objects
|
||||
reference/working-with-associations
|
||||
reference/typedfieldmapper
|
||||
reference/events
|
||||
reference/unitofwork
|
||||
reference/unitofwork-associations
|
||||
|
||||
@@ -279,4 +279,9 @@
|
||||
<!-- https://github.com/doctrine/orm/issues/8537 -->
|
||||
<exclude-pattern>src/QueryBuilder.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.PHP.UselessParentheses">
|
||||
<!-- We need those parentheses to make enum access seem like valid syntax on PHP 7 -->
|
||||
<exclude-pattern>src/Mapping/Driver/XmlDriver.php</exclude-pattern>
|
||||
</rule>
|
||||
</ruleset>
|
||||
|
||||
@@ -170,11 +170,6 @@ parameters:
|
||||
count: 2
|
||||
path: src/Mapping/ClassMetadataFactory.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\:\\:fullyQualifiedClassName\\(\\) should return class\\-string\\|null but returns string\\|null\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadataInfo.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\NamingStrategy\\:\\:joinColumnName\\(\\) invoked with 2 parameters, 1 required\\.$#"
|
||||
count: 2
|
||||
@@ -236,7 +231,7 @@ parameters:
|
||||
path: src/Mapping/Driver/XmlDriver.php
|
||||
|
||||
-
|
||||
message: "#^Offset 'version' on \\*NEVER\\* in isset\\(\\) always exists and is always null\\.$#"
|
||||
message: "#^Offset 'version' on \\*NEVER\\* in isset\\(\\) always exists and is not nullable\\.$#"
|
||||
count: 1
|
||||
path: src/Mapping/Driver/XmlDriver.php
|
||||
|
||||
@@ -331,22 +326,7 @@ parameters:
|
||||
path: src/Query/AST/Functions/DateSubFunction.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$simpleArithmeticExpr of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkSimpleArithmeticExpression\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\SimpleArithmeticExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#"
|
||||
count: 1
|
||||
path: src/Query/AST/Functions/LengthFunction.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$simpleArithmeticExpr of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkSimpleArithmeticExpression\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\SimpleArithmeticExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#"
|
||||
count: 1
|
||||
path: src/Query/AST/Functions/LowerFunction.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$simpleArithmeticExpr of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkSimpleArithmeticExpression\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\SimpleArithmeticExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#"
|
||||
count: 1
|
||||
path: src/Query/AST/Functions/UpperFunction.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\AST\\\\IndexBy\\:\\:dispatch\\(\\) should return string but returns void\\.$#"
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\AST\\\\IndexBy\\:\\:dispatch\\(\\) should return string but returns null\\.$#"
|
||||
count: 1
|
||||
path: src/Query/AST/IndexBy.php
|
||||
|
||||
@@ -395,11 +375,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Query/Expr/Select.php
|
||||
|
||||
-
|
||||
message: "#^Comparison operation \"\\<\" between null and 102 is always true\\.$#"
|
||||
count: 1
|
||||
path: src/Query/Parser.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\Parser\\:\\:ArithmeticFactor\\(\\) should return Doctrine\\\\ORM\\\\Query\\\\AST\\\\ArithmeticFactor but returns Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string\\.$#"
|
||||
count: 1
|
||||
@@ -415,14 +390,9 @@ parameters:
|
||||
count: 1
|
||||
path: src/Query/Parser.php
|
||||
|
||||
-
|
||||
message: "#^Result of && is always true\\.$#"
|
||||
count: 1
|
||||
path: src/Query/Parser.php
|
||||
|
||||
-
|
||||
message: "#^Unreachable statement \\- code above always terminates\\.$#"
|
||||
count: 4
|
||||
count: 3
|
||||
path: src/Query/Parser.php
|
||||
|
||||
-
|
||||
|
||||
@@ -31,6 +31,31 @@ parameters:
|
||||
message: '/^Instanceof between Doctrine\\DBAL\\Platforms\\AbstractPlatform and Doctrine\\DBAL\\Platforms\\MySQLPlatform will always evaluate to false\.$/'
|
||||
path: src/Utility/LockSqlHelper.php
|
||||
|
||||
# Forward compatibility with Collections 3
|
||||
-
|
||||
message: '#^Parameter \$order of anonymous function has invalid type Doctrine\\Common\\Collections\\Order\.$#'
|
||||
path: src/Internal/CriteriaOrderings.php
|
||||
|
||||
-
|
||||
message: '#^Anonymous function has invalid return type Doctrine\\Common\\Collections\\Order\.$#'
|
||||
path: src/Internal/CriteriaOrderings.php
|
||||
|
||||
-
|
||||
message: '#^Access to property \$value on an unknown class Doctrine\\Common\\Collections\\Order\.$#'
|
||||
path: src/Internal/CriteriaOrderings.php
|
||||
|
||||
-
|
||||
message: '#^Call to static method from\(\) on an unknown class Doctrine\\Common\\Collections\\Order\.$#'
|
||||
path: src/Internal/CriteriaOrderings.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\Common\\Collections\\Criteria\:\:orderings\(\)\.$#'
|
||||
path: src/Internal/CriteriaOrderings.php
|
||||
|
||||
-
|
||||
message: '#^Method .+\:\:mapToOrderEnumIfAvailable\(\) has invalid return type Doctrine\\Common\\Collections\\Order\.$#'
|
||||
path: src/Internal/CriteriaOrderings.php
|
||||
|
||||
# False positive
|
||||
-
|
||||
message: '/^Call to an undefined method Doctrine\\Common\\Cache\\Cache::deleteAll\(\)\.$/'
|
||||
|
||||
1652
psalm-baseline.xml
1652
psalm-baseline.xml
File diff suppressed because it is too large
Load Diff
@@ -243,6 +243,12 @@
|
||||
<file name="src/Mapping/ClassMetadataFactory.php"/>
|
||||
</errorLevel>
|
||||
</ReferenceConstraintViolation>
|
||||
<RiskyTruthyFalsyComparison>
|
||||
<!-- TODO: Enable this new rule on higher branches. -->
|
||||
<errorLevel type="suppress">
|
||||
<directory name="src" />
|
||||
</errorLevel>
|
||||
</RiskyTruthyFalsyComparison>
|
||||
<TooManyArguments>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Symfony cache supports passing a key prefix to the clear method. -->
|
||||
|
||||
@@ -16,6 +16,7 @@ use Doctrine\ORM\Cache\Region;
|
||||
use Doctrine\ORM\Cache\TimestampCacheKey;
|
||||
use Doctrine\ORM\Cache\TimestampRegion;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Internal\CriteriaOrderings;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
@@ -30,6 +31,8 @@ use function sha1;
|
||||
|
||||
abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
{
|
||||
use CriteriaOrderings;
|
||||
|
||||
/** @var UnitOfWork */
|
||||
protected $uow;
|
||||
|
||||
@@ -475,7 +478,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
*/
|
||||
public function loadCriteria(Criteria $criteria)
|
||||
{
|
||||
$orderBy = $criteria->getOrderings();
|
||||
$orderBy = self::getCriteriaOrderings($criteria);
|
||||
$limit = $criteria->getMaxResults();
|
||||
$offset = $criteria->getFirstResult();
|
||||
$query = $this->persister->getSelectSQL($criteria);
|
||||
|
||||
54
src/Internal/CriteriaOrderings.php
Normal file
54
src/Internal/CriteriaOrderings.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Internal;
|
||||
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Order;
|
||||
|
||||
use function array_map;
|
||||
use function class_exists;
|
||||
use function method_exists;
|
||||
use function strtoupper;
|
||||
|
||||
trait CriteriaOrderings
|
||||
{
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*
|
||||
* @psalm-suppress DeprecatedMethod We need to call the deprecated API if the new one does not exist yet.
|
||||
*/
|
||||
private static function getCriteriaOrderings(Criteria $criteria): array
|
||||
{
|
||||
if (! method_exists(Criteria::class, 'orderings')) {
|
||||
return $criteria->getOrderings();
|
||||
}
|
||||
|
||||
return array_map(
|
||||
static function (Order $order): string {
|
||||
return $order->value;
|
||||
},
|
||||
$criteria->orderings()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $orderings
|
||||
*
|
||||
* @return array<string, string>|array<string, Order>
|
||||
*/
|
||||
private static function mapToOrderEnumIfAvailable(array $orderings): array
|
||||
{
|
||||
if (! class_exists(Order::class)) {
|
||||
return $orderings;
|
||||
}
|
||||
|
||||
return array_map(
|
||||
static function (string $order): Order {
|
||||
return Order::from(strtoupper($order));
|
||||
},
|
||||
$orderings
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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'];
|
||||
}
|
||||
|
||||
@@ -3707,7 +3718,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* @param string|null $className
|
||||
*
|
||||
* @return string|null null if the input value is null
|
||||
* @psalm-return class-string|null
|
||||
*/
|
||||
public function fullyQualifiedClassName($className)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use BackedEnum;
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
@@ -16,6 +17,7 @@ use ReflectionProperty;
|
||||
use function array_merge;
|
||||
use function assert;
|
||||
use function enum_exists;
|
||||
use function is_a;
|
||||
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
@@ -54,18 +56,18 @@ final class DefaultTypedFieldMapper implements TypedFieldMapper
|
||||
&& ($type instanceof ReflectionNamedType)
|
||||
) {
|
||||
if (PHP_VERSION_ID >= 80100 && ! $type->isBuiltin() && enum_exists($type->getName())) {
|
||||
$mapping['enumType'] = $type->getName();
|
||||
|
||||
$reflection = new ReflectionEnum($type->getName());
|
||||
if (! $reflection->isBacked()) {
|
||||
throw MappingException::backedEnumTypeRequired(
|
||||
$field->class,
|
||||
$mapping['fieldName'],
|
||||
$mapping['enumType']
|
||||
$type->getName()
|
||||
);
|
||||
}
|
||||
|
||||
$type = $reflection->getBackingType();
|
||||
assert(is_a($type->getName(), BackedEnum::class, true));
|
||||
$mapping['enumType'] = $type->getName();
|
||||
$type = $reflection->getBackingType();
|
||||
|
||||
assert($type instanceof ReflectionNamedType);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Mapping\Driver;
|
||||
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Order;
|
||||
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
@@ -16,6 +17,7 @@ use LogicException;
|
||||
use SimpleXMLElement;
|
||||
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
use function constant;
|
||||
use function count;
|
||||
use function defined;
|
||||
@@ -481,9 +483,10 @@ class XmlDriver extends FileDriver
|
||||
if (isset($oneToManyElement->{'order-by'})) {
|
||||
$orderBy = [];
|
||||
foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {
|
||||
/** @psalm-suppress DeprecatedConstant */
|
||||
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
|
||||
? (string) $orderByField['direction']
|
||||
: Criteria::ASC;
|
||||
: (class_exists(Order::class) ? (Order::Ascending)->value : Criteria::ASC);
|
||||
}
|
||||
|
||||
$mapping['orderBy'] = $orderBy;
|
||||
@@ -609,9 +612,10 @@ class XmlDriver extends FileDriver
|
||||
if (isset($manyToManyElement->{'order-by'})) {
|
||||
$orderBy = [];
|
||||
foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {
|
||||
/** @psalm-suppress DeprecatedConstant */
|
||||
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
|
||||
? (string) $orderByField['direction']
|
||||
: Criteria::ASC;
|
||||
: (class_exists(Order::class) ? (Order::Ascending)->value : Criteria::ASC);
|
||||
}
|
||||
|
||||
$mapping['orderBy'] = $orderBy;
|
||||
|
||||
@@ -9,6 +9,7 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\ORM\Internal\CriteriaOrderings;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use ReturnTypeWillChange;
|
||||
use RuntimeException;
|
||||
@@ -41,6 +42,8 @@ use function spl_object_id;
|
||||
*/
|
||||
final class PersistentCollection extends AbstractLazyCollection implements Selectable
|
||||
{
|
||||
use CriteriaOrderings;
|
||||
|
||||
/**
|
||||
* A snapshot of the collection at the moment it was fetched from the database.
|
||||
* This is used to create a diff of the collection at commit time.
|
||||
@@ -671,7 +674,9 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
|
||||
|
||||
$criteria = clone $criteria;
|
||||
$criteria->where($expression);
|
||||
$criteria->orderBy($criteria->getOrderings() ?: $association['orderBy'] ?? []);
|
||||
$criteria->orderBy(self::mapToOrderEnumIfAvailable(
|
||||
self::getCriteriaOrderings($criteria) ?: $association['orderBy'] ?? []
|
||||
));
|
||||
|
||||
$persister = $this->getUnitOfWork()->getEntityPersister($association['targetEntity']);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use BadMethodCallException;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Expr\Comparison;
|
||||
use Doctrine\DBAL\Exception as DBALException;
|
||||
use Doctrine\ORM\Internal\CriteriaOrderings;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Persisters\SqlValueVisitor;
|
||||
@@ -30,6 +31,8 @@ use function sprintf;
|
||||
*/
|
||||
class ManyToManyPersister extends AbstractCollectionPersister
|
||||
{
|
||||
use CriteriaOrderings;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@@ -745,7 +748,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
|
||||
private function getOrderingSql(Criteria $criteria, ClassMetadata $targetClass): string
|
||||
{
|
||||
$orderings = $criteria->getOrderings();
|
||||
$orderings = self::getCriteriaOrderings($criteria);
|
||||
if ($orderings) {
|
||||
$orderBy = [];
|
||||
foreach ($orderings as $name => $direction) {
|
||||
|
||||
@@ -15,6 +15,7 @@ use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Internal\CriteriaOrderings;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Doctrine\ORM\Mapping\QuoteStrategy;
|
||||
@@ -93,6 +94,7 @@ use function trim;
|
||||
*/
|
||||
class BasicEntityPersister implements EntityPersister
|
||||
{
|
||||
use CriteriaOrderings;
|
||||
use LockSqlHelper;
|
||||
|
||||
/** @var array<string,string> */
|
||||
@@ -884,7 +886,7 @@ class BasicEntityPersister implements EntityPersister
|
||||
*/
|
||||
public function loadCriteria(Criteria $criteria)
|
||||
{
|
||||
$orderBy = $criteria->getOrderings();
|
||||
$orderBy = self::getCriteriaOrderings($criteria);
|
||||
$limit = $criteria->getMaxResults();
|
||||
$offset = $criteria->getFirstResult();
|
||||
$query = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);
|
||||
|
||||
@@ -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) {
|
||||
$parser->match(Lexer::T_FROM);
|
||||
if ($this->leading || $this->trailing || $this->both || ($this->trimChar !== false)) {
|
||||
$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
@@ -471,6 +471,10 @@ class SqlWalker implements TreeWalker
|
||||
continue;
|
||||
}
|
||||
|
||||
$sqlTableAlias = $this->useSqlTableAliases
|
||||
? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'
|
||||
: '';
|
||||
|
||||
$conn = $this->em->getConnection();
|
||||
$values = [];
|
||||
|
||||
@@ -479,14 +483,22 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
foreach ($class->subClasses as $subclassName) {
|
||||
$values[] = $conn->quote($this->em->getClassMetadata($subclassName)->discriminatorValue);
|
||||
$subclassMetadata = $this->em->getClassMetadata($subclassName);
|
||||
|
||||
// Abstract entity classes show up in the list of subClasses, but may be omitted
|
||||
// from the discriminator map. In that case, they have a null discriminator value.
|
||||
if ($subclassMetadata->discriminatorValue === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$values[] = $conn->quote($subclassMetadata->discriminatorValue);
|
||||
}
|
||||
|
||||
$sqlTableAlias = $this->useSqlTableAliases
|
||||
? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'
|
||||
: '';
|
||||
|
||||
$sqlParts[] = $sqlTableAlias . $class->getDiscriminatorColumn()['name'] . ' IN (' . implode(', ', $values) . ')';
|
||||
if ($values !== []) {
|
||||
$sqlParts[] = $sqlTableAlias . $class->getDiscriminatorColumn()['name'] . ' IN (' . implode(', ', $values) . ')';
|
||||
} else {
|
||||
$sqlParts[] = '1=0'; // impossible condition
|
||||
}
|
||||
}
|
||||
|
||||
$sql = implode(' AND ', $sqlParts);
|
||||
@@ -2576,7 +2588,7 @@ class SqlWalker implements TreeWalker
|
||||
/**
|
||||
* Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param AST\SimpleArithmeticExpression $simpleArithmeticExpr
|
||||
* @param AST\Node|string $simpleArithmeticExpr
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
|
||||
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()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ namespace Doctrine\ORM;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Internal\CriteriaOrderings;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\Query\Parameter;
|
||||
use Doctrine\ORM\Query\QueryExpressionVisitor;
|
||||
@@ -39,6 +40,8 @@ use function substr;
|
||||
*/
|
||||
class QueryBuilder
|
||||
{
|
||||
use CriteriaOrderings;
|
||||
|
||||
/** @deprecated */
|
||||
public const SELECT = 0;
|
||||
|
||||
@@ -1375,22 +1378,20 @@ class QueryBuilder
|
||||
}
|
||||
}
|
||||
|
||||
if ($criteria->getOrderings()) {
|
||||
foreach ($criteria->getOrderings() as $sort => $order) {
|
||||
$hasValidAlias = false;
|
||||
foreach ($allAliases as $alias) {
|
||||
if (str_starts_with($sort . '.', $alias . '.')) {
|
||||
$hasValidAlias = true;
|
||||
break;
|
||||
}
|
||||
foreach (self::getCriteriaOrderings($criteria) as $sort => $order) {
|
||||
$hasValidAlias = false;
|
||||
foreach ($allAliases as $alias) {
|
||||
if (str_starts_with($sort . '.', $alias . '.')) {
|
||||
$hasValidAlias = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (! $hasValidAlias) {
|
||||
$sort = $allAliases[0] . '.' . $sort;
|
||||
}
|
||||
|
||||
$this->addOrderBy($sort, $order);
|
||||
}
|
||||
|
||||
if (! $hasValidAlias) {
|
||||
$sort = $allAliases[0] . '.' . $sort;
|
||||
}
|
||||
|
||||
$this->addOrderBy($sort, $order);
|
||||
}
|
||||
|
||||
// Overwrite limits only if they was set in criteria
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3169,9 +3166,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) {
|
||||
$this->scheduleCollectionForBatchLoading($pColl, $class);
|
||||
} elseif (($isIteration && $assoc['type'] === ClassMetadata::ONE_TO_MANY) || $assoc['type'] === ClassMetadata::MANY_TO_MANY) {
|
||||
} else {
|
||||
$this->loadCollection($pColl);
|
||||
$pColl->takeSnapshot();
|
||||
}
|
||||
@@ -3252,7 +3249,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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Order;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Doctrine\Tests\Models\CMS\CmsGroup;
|
||||
@@ -14,6 +15,7 @@ use Doctrine\Tests\Models\CMS\CmsUser;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
use function get_class;
|
||||
|
||||
/**
|
||||
@@ -436,7 +438,7 @@ class ManyToManyBasicAssociationTest extends OrmFunctionalTestCase
|
||||
$user = $this->_em->find(get_class($user), $user->id);
|
||||
|
||||
$criteria = Criteria::create()
|
||||
->orderBy(['name' => Criteria::ASC]);
|
||||
->orderBy(['name' => class_exists(Order::class) ? Order::Ascending : Criteria::ASC]);
|
||||
|
||||
self::assertEquals(
|
||||
['A', 'B', 'C', 'Developers_0'],
|
||||
@@ -478,7 +480,7 @@ class ManyToManyBasicAssociationTest extends OrmFunctionalTestCase
|
||||
$user = $this->_em->find(get_class($user), $user->id);
|
||||
|
||||
$criteria = Criteria::create()
|
||||
->orderBy(['name' => Criteria::ASC]);
|
||||
->orderBy(['name' => class_exists(Order::class) ? Order::Ascending : Criteria::ASC]);
|
||||
|
||||
self::assertEquals(
|
||||
['A', 'B', 'C'],
|
||||
|
||||
127
tests/Tests/ORM/Functional/QueryParameterTest.php
Normal file
127
tests/Tests/ORM/Functional/QueryParameterTest.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\DBAL\ArrayParameterType;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Tests\Models\CMS\CmsUser;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
use function class_exists;
|
||||
|
||||
/** @group GH-11278 */
|
||||
final class QueryParameterTest extends OrmFunctionalTestCase
|
||||
{
|
||||
/** @var int */
|
||||
private $userId;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->useModelSet('cms');
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$user = new CmsUser();
|
||||
$user->name = 'John Doe';
|
||||
$user->username = 'john';
|
||||
$user2 = new CmsUser();
|
||||
$user2->name = 'Jane Doe';
|
||||
$user2->username = 'jane';
|
||||
$user3 = new CmsUser();
|
||||
$user3->name = 'Just Bill';
|
||||
$user3->username = 'bill';
|
||||
|
||||
$this->_em->persist($user);
|
||||
$this->_em->persist($user2);
|
||||
$this->_em->persist($user3);
|
||||
$this->_em->flush();
|
||||
|
||||
$this->userId = $user->id;
|
||||
|
||||
$this->_em->clear();
|
||||
}
|
||||
|
||||
public function testParameterTypeInBuilder(): void
|
||||
{
|
||||
$result = $this->_em->createQueryBuilder()
|
||||
->from(CmsUser::class, 'u')
|
||||
->select('u.name')
|
||||
->where('u.id = :id')
|
||||
->setParameter('id', $this->userId, ParameterType::INTEGER)
|
||||
->getQuery()
|
||||
->getArrayResult();
|
||||
|
||||
self::assertSame([['name' => 'John Doe']], $result);
|
||||
}
|
||||
|
||||
public function testParameterTypeInQuery(): void
|
||||
{
|
||||
$result = $this->_em->createQueryBuilder()
|
||||
->from(CmsUser::class, 'u')
|
||||
->select('u.name')
|
||||
->where('u.id = :id')
|
||||
->getQuery()
|
||||
->setParameter('id', $this->userId, ParameterType::INTEGER)
|
||||
->getArrayResult();
|
||||
|
||||
self::assertSame([['name' => 'John Doe']], $result);
|
||||
}
|
||||
|
||||
public function testDbalTypeStringInBuilder(): void
|
||||
{
|
||||
$result = $this->_em->createQueryBuilder()
|
||||
->from(CmsUser::class, 'u')
|
||||
->select('u.name')
|
||||
->where('u.id = :id')
|
||||
->setParameter('id', $this->userId, Types::INTEGER)
|
||||
->getQuery()
|
||||
->getArrayResult();
|
||||
|
||||
self::assertSame([['name' => 'John Doe']], $result);
|
||||
}
|
||||
|
||||
public function testDbalTypeStringInQuery(): void
|
||||
{
|
||||
$result = $this->_em->createQueryBuilder()
|
||||
->from(CmsUser::class, 'u')
|
||||
->select('u.name')
|
||||
->where('u.id = :id')
|
||||
->getQuery()
|
||||
->setParameter('id', $this->userId, Types::INTEGER)
|
||||
->getArrayResult();
|
||||
|
||||
self::assertSame([['name' => 'John Doe']], $result);
|
||||
}
|
||||
|
||||
public function testArrayParameterTypeInBuilder(): void
|
||||
{
|
||||
$result = $this->_em->createQueryBuilder()
|
||||
->from(CmsUser::class, 'u')
|
||||
->select('u.name')
|
||||
->where('u.username IN (:usernames)')
|
||||
->orderBy('u.username')
|
||||
->setParameter('usernames', ['john', 'jane'], class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY)
|
||||
->getQuery()
|
||||
->getArrayResult();
|
||||
|
||||
self::assertSame([['name' => 'Jane Doe'], ['name' => 'John Doe']], $result);
|
||||
}
|
||||
|
||||
public function testArrayParameterTypeInQuery(): void
|
||||
{
|
||||
$result = $this->_em->createQueryBuilder()
|
||||
->from(CmsUser::class, 'u')
|
||||
->select('u.name')
|
||||
->where('u.username IN (:usernames)')
|
||||
->orderBy('u.username')
|
||||
->getQuery()
|
||||
->setParameter('usernames', ['john', 'jane'], class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY)
|
||||
->getArrayResult();
|
||||
|
||||
self::assertSame([['name' => 'Jane Doe'], ['name' => 'John Doe']], $result);
|
||||
}
|
||||
}
|
||||
96
tests/Tests/ORM/Functional/Ticket/GH11199Test.php
Normal file
96
tests/Tests/ORM/Functional/Ticket/GH11199Test.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
use Generator;
|
||||
|
||||
class GH11199Test extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->setUpEntitySchema([
|
||||
GH11199Root::class,
|
||||
GH11199Parent::class,
|
||||
GH11199Foo::class,
|
||||
GH11199Baz::class,
|
||||
GH11199AbstractLeaf::class,
|
||||
]);
|
||||
}
|
||||
|
||||
public function dqlStatements(): Generator
|
||||
{
|
||||
yield ['SELECT e FROM ' . GH11199Root::class . ' e', "/WHERE g0_.asset_type IN \('root', 'foo', 'baz'\)$/"];
|
||||
yield ['SELECT e FROM ' . GH11199Parent::class . ' e', "/WHERE g0_.asset_type IN \('foo'\)$/"];
|
||||
yield ['SELECT e FROM ' . GH11199Foo::class . ' e', "/WHERE g0_.asset_type IN \('foo'\)$/"];
|
||||
yield ['SELECT e FROM ' . GH11199Baz::class . ' e', "/WHERE g0_.asset_type IN \('baz'\)$/"];
|
||||
yield ['SELECT e FROM ' . GH11199AbstractLeaf::class . ' e', '/WHERE 1=0/'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dqlStatements
|
||||
*/
|
||||
public function testGH11199(string $dql, string $expectedDiscriminatorValues): void
|
||||
{
|
||||
$query = $this->_em->createQuery($dql);
|
||||
$sql = $query->getSQL();
|
||||
|
||||
self::assertMatchesRegularExpression($expectedDiscriminatorValues, $sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity()
|
||||
* @ORM\Table(name="gh11199")
|
||||
* @ORM\InheritanceType("SINGLE_TABLE")
|
||||
* @ORM\DiscriminatorColumn(name="asset_type", type="string")
|
||||
* @ORM\DiscriminatorMap({
|
||||
* "root" = "\Doctrine\Tests\ORM\Functional\Ticket\GH11199Root",
|
||||
* "foo" = "\Doctrine\Tests\ORM\Functional\Ticket\GH11199Foo",
|
||||
* "baz" = "\Doctrine\Tests\ORM\Functional\Ticket\GH11199Baz",
|
||||
* })
|
||||
*/
|
||||
class GH11199Root
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue(strategy="IDENTITY")
|
||||
* @ORM\Column(type="integer")
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
private $id = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity()
|
||||
*/
|
||||
abstract class GH11199Parent extends GH11199Root
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity()
|
||||
*/
|
||||
class GH11199Foo extends GH11199Parent
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity()
|
||||
*/
|
||||
class GH11199Baz extends GH11199Root
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\Entity()
|
||||
*/
|
||||
abstract class GH11199AbstractLeaf extends GH11199Root
|
||||
{
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Order;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\GeneratedValue;
|
||||
@@ -16,6 +18,7 @@ use Doctrine\ORM\Mapping\OrderBy;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
|
||||
/** @group GH7767 */
|
||||
class GH7767Test extends OrmFunctionalTestCase
|
||||
@@ -53,7 +56,9 @@ class GH7767Test extends OrmFunctionalTestCase
|
||||
$parent = $this->_em->find(GH7767ParentEntity::class, 1);
|
||||
assert($parent instanceof GH7767ParentEntity);
|
||||
|
||||
$children = $parent->getChildren()->matching(Criteria::create()->orderBy(['position' => 'DESC']));
|
||||
$children = $parent->getChildren()->matching(
|
||||
Criteria::create()->orderBy(['position' => class_exists(Order::class) ? Order::Descending : 'DESC'])
|
||||
);
|
||||
|
||||
self::assertEquals(300, $children[0]->position);
|
||||
self::assertEquals(200, $children[1]->position);
|
||||
@@ -73,7 +78,7 @@ class GH7767ParentEntity
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @psalm-var Collection<int, GH7767ChildEntity>
|
||||
* @psalm-var Collection<int, GH7767ChildEntity>&Selectable<int, GH7767ChildEntity>
|
||||
* @OneToMany(targetEntity=GH7767ChildEntity::class, mappedBy="parent", fetch="EXTRA_LAZY", cascade={"persist"})
|
||||
* @OrderBy({"position" = "ASC"})
|
||||
*/
|
||||
@@ -84,7 +89,7 @@ class GH7767ParentEntity
|
||||
$this->children[] = new GH7767ChildEntity($this, $position);
|
||||
}
|
||||
|
||||
/** @psalm-return Collection<int, GH7767ChildEntity> */
|
||||
/** @psalm-return Collection<int, GH7767ChildEntity>&Selectable<int, GH7767ChildEntity> */
|
||||
public function getChildren(): Collection
|
||||
{
|
||||
return $this->children;
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Order;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\GeneratedValue;
|
||||
@@ -16,6 +18,7 @@ use Doctrine\ORM\Mapping\OrderBy;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
|
||||
/** @group GH7836 */
|
||||
class GH7836Test extends OrmFunctionalTestCase
|
||||
@@ -56,7 +59,13 @@ class GH7836Test extends OrmFunctionalTestCase
|
||||
$parent = $this->_em->find(GH7836ParentEntity::class, 1);
|
||||
assert($parent instanceof GH7836ParentEntity);
|
||||
|
||||
$children = $parent->getChildren()->matching(Criteria::create()->orderBy(['position' => 'DESC', 'name' => 'ASC']));
|
||||
$children = $parent->getChildren()->matching(
|
||||
Criteria::create()->orderBy(
|
||||
class_exists(Order::class)
|
||||
? ['position' => Order::Descending, 'name' => Order::Ascending]
|
||||
: ['position' => 'DESC', 'name' => 'ASC']
|
||||
)
|
||||
);
|
||||
|
||||
self::assertSame(200, $children[0]->position);
|
||||
self::assertSame('baz', $children[0]->name);
|
||||
@@ -71,7 +80,13 @@ class GH7836Test extends OrmFunctionalTestCase
|
||||
$parent = $this->_em->find(GH7836ParentEntity::class, 1);
|
||||
assert($parent instanceof GH7836ParentEntity);
|
||||
|
||||
$children = $parent->getChildren()->matching(Criteria::create()->orderBy(['name' => 'ASC', 'position' => 'ASC']));
|
||||
$children = $parent->getChildren()->matching(
|
||||
Criteria::create()->orderBy(
|
||||
class_exists(Order::class)
|
||||
? ['name' => Order::Ascending, 'position' => Order::Ascending]
|
||||
: ['name' => 'ASC', 'position' => 'ASC']
|
||||
)
|
||||
);
|
||||
|
||||
self::assertSame(100, $children[0]->position);
|
||||
self::assertSame('bar', $children[0]->name);
|
||||
@@ -94,7 +109,7 @@ class GH7836ParentEntity
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var Collection<int, GH7836ChildEntity>
|
||||
* @var Collection<int, GH7836ChildEntity>&Selectable<int, GH7836ChildEntity>
|
||||
* @OneToMany(targetEntity=GH7836ChildEntity::class, mappedBy="parent", fetch="EXTRA_LAZY", cascade={"persist"})
|
||||
* @OrderBy({"position" = "ASC", "name" = "ASC"})
|
||||
*/
|
||||
@@ -105,7 +120,7 @@ class GH7836ParentEntity
|
||||
$this->children[] = new GH7836ChildEntity($this, $position, $name);
|
||||
}
|
||||
|
||||
/** @psalm-return Collection<int, GH7836ChildEntity> */
|
||||
/** @psalm-return Collection<int, GH7836ChildEntity>&Selectable<int, GH7836ChildEntity> */
|
||||
public function getChildren(): Collection
|
||||
{
|
||||
return $this->children;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -172,6 +172,11 @@ class LanguageRecognitionTest extends OrmTestCase
|
||||
$this->assertValidDQL("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(u.name) = 'someone'");
|
||||
}
|
||||
|
||||
public function testTrimFalsyString(): void
|
||||
{
|
||||
$this->assertValidDQL("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM('0' FROM u.name) = 'someone'");
|
||||
}
|
||||
|
||||
public function testArithmeticExpressionsSupportedInWherePart(): void
|
||||
{
|
||||
$this->assertValidDQL('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000');
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -7,8 +7,8 @@ namespace Doctrine\Tests\ORM\Query;
|
||||
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\ParserResult;
|
||||
use Doctrine\ORM\Query\TokenType;
|
||||
use Doctrine\ORM\Query\TreeWalkerAdapter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use stdClass;
|
||||
@@ -33,7 +33,7 @@ class TreeWalkerAdapterTest extends TestCase
|
||||
'relation' => null,
|
||||
'map' => null,
|
||||
'nestingLevel' => 0,
|
||||
'token' => ['value' => '', 'type' => Lexer::T_NONE, 'position' => 0],
|
||||
'token' => ['value' => '', 'type' => TokenType::T_NONE, 'position' => 0],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class TreeWalkerAdapterTest extends TestCase
|
||||
'relation' => null,
|
||||
'map' => null,
|
||||
'nestingLevel' => 0,
|
||||
'token' => ['value' => '', 'type' => Lexer::T_NONE, 'position' => 0],
|
||||
'token' => ['value' => '', 'type' => TokenType::T_NONE, 'position' => 0],
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\Tests\ORM;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Order;
|
||||
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
|
||||
use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Query;
|
||||
@@ -22,6 +23,7 @@ use Doctrine\Tests\OrmTestCase;
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function array_filter;
|
||||
use function class_exists;
|
||||
use function get_class;
|
||||
|
||||
/**
|
||||
@@ -576,7 +578,7 @@ class QueryBuilderTest extends OrmTestCase
|
||||
->from(CmsUser::class, 'u');
|
||||
|
||||
$criteria = new Criteria();
|
||||
$criteria->orderBy(['field' => Criteria::DESC]);
|
||||
$criteria->orderBy(['field' => class_exists(Order::class) ? Order::Descending : Criteria::DESC]);
|
||||
|
||||
$qb->addCriteria($criteria);
|
||||
|
||||
@@ -593,7 +595,7 @@ class QueryBuilderTest extends OrmTestCase
|
||||
->join('u.article', 'a');
|
||||
|
||||
$criteria = new Criteria();
|
||||
$criteria->orderBy(['a.field' => Criteria::DESC]);
|
||||
$criteria->orderBy(['a.field' => class_exists(Order::class) ? Order::Descending : Criteria::DESC]);
|
||||
|
||||
$qb->addCriteria($criteria);
|
||||
|
||||
|
||||
@@ -374,6 +374,27 @@ class SchemaToolTest extends OrmTestCase
|
||||
self::assertTrue($schema->hasTable('first_entity'), 'Table first_entity should exist.');
|
||||
self::assertFalse($schema->hasTable('second_entity'), 'Table second_entity should not exist.');
|
||||
}
|
||||
|
||||
/** @group GH-11314 */
|
||||
public function testLoadUniqueConstraintWithoutName(): void
|
||||
{
|
||||
$em = $this->getTestEntityManager();
|
||||
$entity = $em->getClassMetadata(GH11314Entity::class);
|
||||
|
||||
$schemaTool = new SchemaTool($em);
|
||||
$schema = $schemaTool->getSchemaFromMetadata([$entity]);
|
||||
|
||||
self::assertTrue($schema->hasTable('GH11314Entity'));
|
||||
|
||||
$tableEntity = $schema->getTable('GH11314Entity');
|
||||
|
||||
self::assertTrue($tableEntity->hasIndex('uniq_2d81a3ed5bf54558875f7fd5'));
|
||||
|
||||
$tableIndex = $tableEntity->getIndex('uniq_2d81a3ed5bf54558875f7fd5');
|
||||
|
||||
self::assertTrue($tableIndex->isUnique());
|
||||
self::assertSame(['field', 'anotherField'], $tableIndex->getColumns());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -559,6 +580,32 @@ class IndexByFieldEntity
|
||||
public $fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(uniqueConstraints={@UniqueConstraint(columns={"field", "anotherField"})})
|
||||
*/
|
||||
class GH11314Entity
|
||||
{
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
* @Id
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Column(name="field", type="string")
|
||||
* @var string
|
||||
*/
|
||||
private $field;
|
||||
|
||||
/**
|
||||
* @Column(name="anotherField", type="string")
|
||||
* @var string
|
||||
*/
|
||||
private $anotherField;
|
||||
}
|
||||
|
||||
class IncorrectIndexByFieldEntity
|
||||
{
|
||||
/** @var int */
|
||||
|
||||
Reference in New Issue
Block a user