mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 15:02:22 +01:00
Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
393679a479 | ||
|
|
e50ae06fe7 | ||
|
|
05ef1f4f96 | ||
|
|
2b91edc525 | ||
|
|
6af7f9f7bf | ||
|
|
46cb9a980b | ||
|
|
ed1df148c2 | ||
|
|
44e943e100 | ||
|
|
23d36c0d52 | ||
|
|
212edaa80b | ||
|
|
1a4fe6e0bb | ||
|
|
0a7b939623 | ||
|
|
6be65ebc70 | ||
|
|
e8afa9f80c | ||
|
|
1267f482ef | ||
|
|
b41de2a39d | ||
|
|
6a48b0741b | ||
|
|
0e95838787 | ||
|
|
be62f72b38 | ||
|
|
7613f25d57 | ||
|
|
c075154e1e | ||
|
|
f08159eb87 | ||
|
|
300cffb942 | ||
|
|
2a9390d426 | ||
|
|
ec74c83845 | ||
|
|
4b2b4860fb | ||
|
|
609e10df36 | ||
|
|
d03aed1265 | ||
|
|
6993ad28ed | ||
|
|
16028e4fd3 | ||
|
|
609647a51a | ||
|
|
293299a314 | ||
|
|
0b7fe1862e | ||
|
|
866283d1a7 | ||
|
|
3676e3c571 | ||
|
|
d84f607487 | ||
|
|
edd962e385 | ||
|
|
a33a3813b2 | ||
|
|
bf69d0ac4e | ||
|
|
3f2fa309d4 | ||
|
|
8057b51f85 | ||
|
|
c09660ac50 | ||
|
|
8ec599bb17 | ||
|
|
bf74b434b8 | ||
|
|
cd54c56278 | ||
|
|
76fd34f766 | ||
|
|
1cec0b82bd | ||
|
|
0e74a180c4 | ||
|
|
fdfca0f0e7 | ||
|
|
42af7cabb7 | ||
|
|
c5137da90e | ||
|
|
e89b680a28 | ||
|
|
07b0917505 | ||
|
|
143ee25697 | ||
|
|
52853c2e9c | ||
|
|
40bfe07172 | ||
|
|
6983f48490 | ||
|
|
194f5062bb | ||
|
|
922365d2c5 | ||
|
|
a1e055b608 | ||
|
|
0e3489b240 | ||
|
|
ff28ba8080 | ||
|
|
41410e6be1 | ||
|
|
b9e55bad4d | ||
|
|
47952c3228 | ||
|
|
fdceb82454 | ||
|
|
dc899e26cf | ||
|
|
48edb33b3f | ||
|
|
9c2e49a665 | ||
|
|
8ef5c80148 | ||
|
|
083b1f98c1 | ||
|
|
3ff67c3e2f | ||
|
|
84a762e12e | ||
|
|
ef2123bd0f | ||
|
|
55699a9129 | ||
|
|
68fc3b6458 | ||
|
|
32192c7b01 | ||
|
|
77843e45f3 | ||
|
|
1919eea0a9 | ||
|
|
925631878f | ||
|
|
db2791001c | ||
|
|
62ed63bbbe | ||
|
|
081ec2ad26 | ||
|
|
633ce41460 | ||
|
|
e9537f4cde | ||
|
|
0f67ba2176 | ||
|
|
38ad3925e2 | ||
|
|
858b01f85e | ||
|
|
9f555ea8fb | ||
|
|
1f8c02f345 | ||
|
|
d81afdb6e3 | ||
|
|
0628204b43 | ||
|
|
816ecc6d6b | ||
|
|
f66263d859 | ||
|
|
8aa5aa2f57 | ||
|
|
96e31a3b30 | ||
|
|
a60a273423 | ||
|
|
a2d2e173c2 | ||
|
|
7986fc64dd | ||
|
|
aa333e2f1d | ||
|
|
b6441b4f26 | ||
|
|
9647d0e2ae | ||
|
|
368eb01ac3 | ||
|
|
47cf50bcd5 | ||
|
|
58df4078fc | ||
|
|
eda1909c75 | ||
|
|
ab542e97df |
@@ -11,21 +11,23 @@
|
||||
"slug": "latest",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.18",
|
||||
"branchName": "2.18.x",
|
||||
"slug": "2.18",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.17",
|
||||
"branchName": "2.17.x",
|
||||
"slug": "2.17",
|
||||
"upcoming": true
|
||||
"current": true
|
||||
},
|
||||
{
|
||||
"name": "2.16",
|
||||
"branchName": "2.16.x",
|
||||
"slug": "2.16",
|
||||
"current": true,
|
||||
"aliases": [
|
||||
"current",
|
||||
"stable"
|
||||
]
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.15",
|
||||
|
||||
16
.github/workflows/continuous-integration.yml
vendored
16
.github/workflows/continuous-integration.yml
vendored
@@ -39,6 +39,7 @@ jobs:
|
||||
- "8.0"
|
||||
- "8.1"
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
dbal-version:
|
||||
- "default"
|
||||
extension:
|
||||
@@ -62,7 +63,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -111,6 +112,7 @@ jobs:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3@dev"
|
||||
@@ -143,7 +145,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -183,6 +185,7 @@ jobs:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3@dev"
|
||||
@@ -212,7 +215,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -252,6 +255,7 @@ jobs:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3@dev"
|
||||
@@ -281,7 +285,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -333,7 +337,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -363,7 +367,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
|
||||
9
.github/workflows/documentation.yml
vendored
9
.github/workflows/documentation.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
@@ -33,10 +33,7 @@ jobs:
|
||||
run: "rm composer.json"
|
||||
|
||||
- name: "Require phpdocumentor/guides-cli"
|
||||
run: "composer require --dev phpdocumentor/guides-cli dev-main@dev --no-update"
|
||||
|
||||
- name: "Configure minimum stability"
|
||||
run: "composer config minimum-stability dev"
|
||||
run: "composer require --dev phpdocumentor/guides-cli --no-update"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v2"
|
||||
@@ -48,4 +45,4 @@ jobs:
|
||||
printf '%s\n%s\n\n%s\n' "Dummy title" "===========" "$(cat docs/en/sidebar.rst)" > docs/en/sidebar.rst
|
||||
|
||||
- name: "Run guides-cli"
|
||||
run: "vendor/bin/guides -vvv --no-progress docs/en /tmp/test 2>&1 | ( ! grep WARNING )"
|
||||
run: "vendor/bin/guides -vvv --no-progress docs/en 2>&1 | grep -v 'Unknown directive' | ( ! grep WARNING )"
|
||||
|
||||
2
.github/workflows/phpbench.yml
vendored
2
.github/workflows/phpbench.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
|
||||
4
.github/workflows/static-analysis.yml
vendored
4
.github/workflows/static-analysis.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
|
||||
73
UPGRADE.md
73
UPGRADE.md
@@ -1,9 +1,67 @@
|
||||
# Upgrade to 2.17
|
||||
|
||||
## Deprecate annotations classes for named queries
|
||||
|
||||
The following classes have been deprecated:
|
||||
|
||||
* `Doctrine\ORM\Mapping\NamedNativeQueries`
|
||||
* `Doctrine\ORM\Mapping\NamedNativeQuery`
|
||||
* `Doctrine\ORM\Mapping\NamedQueries`
|
||||
* `Doctrine\ORM\Mapping\NamedQuery`
|
||||
|
||||
## Deprecate `Doctrine\ORM\Query\Exec\AbstractSqlExecutor::_sqlStatements`
|
||||
|
||||
Use `Doctrine\ORM\Query\Exec\AbstractSqlExecutor::sqlStatements` instead.
|
||||
|
||||
## Undeprecate `Doctrine\ORM\Proxy\Autoloader`
|
||||
|
||||
It will be a full-fledged class, no longer extending
|
||||
`Doctrine\Common\Proxy\Autoloader` in 3.0.x.
|
||||
|
||||
## Deprecated: reliance on the non-optimal defaults that come with the `AUTO` identifier generation strategy
|
||||
|
||||
When the `AUTO` identifier generation strategy was introduced, the best
|
||||
strategy at the time was selected for each database platform.
|
||||
A lot of time has passed since then, and with ORM 3.0.0 and DBAL 4.0.0, support
|
||||
for better strategies will be added.
|
||||
|
||||
Because of that, it is now deprecated to rely on the historical defaults when
|
||||
they differ from what we will be recommended in the future.
|
||||
|
||||
Instead, you should pick a strategy for each database platform you use, and it
|
||||
will be used when using `AUTO`. As of now, only PostgreSQL is affected by this.
|
||||
|
||||
It is recommended that PostgreSQL users configure their existing and new
|
||||
applications to use `SEQUENCE` until `doctrine/dbal` 4.0.0 is released:
|
||||
|
||||
```php
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
use Doctrine\ORM\Configuration;
|
||||
|
||||
assert($configuration instanceof Configuration);
|
||||
$configuration->setIdentityGenerationPreferences([
|
||||
PostgreSQLPlatform::CLASS => ClassMetadata::GENERATOR_TYPE_SEQUENCE,
|
||||
]);
|
||||
```
|
||||
|
||||
When DBAL 4 is released, `AUTO` will result in `IDENTITY`, and the above
|
||||
configuration should be removed to migrate to it.
|
||||
|
||||
## Deprecate `EntityManagerInterface::getPartialReference()`
|
||||
|
||||
This method does not have a replacement and will be removed in 3.0.
|
||||
|
||||
## Deprecate not-enabling lazy-ghosts
|
||||
|
||||
Not enabling lazy ghost objects is deprecated. In ORM 3.0, they will be always enabled.
|
||||
Ensure `Doctrine\ORM\Configuration::setLazyGhostObjectEnabled(true)` is called to enable them.
|
||||
|
||||
# Upgrade to 2.16
|
||||
|
||||
## Deprecated accepting duplicate IDs in the identity map
|
||||
|
||||
For any given entity class and ID value, there should be only one object instance
|
||||
representing the entity.
|
||||
representing the entity.
|
||||
|
||||
In https://github.com/doctrine/orm/pull/10785, a check was added that will guard this
|
||||
in the identity map. The most probable cause for violations of this rule are collisions
|
||||
@@ -26,13 +84,6 @@ avoided.
|
||||
When using database-provided, auto-incrementing IDs, this may lead to IDs being assigned
|
||||
to entities in a different order than it was previously the case.
|
||||
|
||||
## Deprecated `\Doctrine\ORM\Internal\CommitOrderCalculator` and related classes
|
||||
|
||||
With changes made to the commit order computation, the internal classes
|
||||
`\Doctrine\ORM\Internal\CommitOrderCalculator`, `\Doctrine\ORM\Internal\CommitOrder\Edge`,
|
||||
`\Doctrine\ORM\Internal\CommitOrder\Vertex` and `\Doctrine\ORM\Internal\CommitOrder\VertexState`
|
||||
have been deprecated and will be removed in ORM 3.0.
|
||||
|
||||
## Deprecated returning post insert IDs from `EntityPersister::executeInserts()`
|
||||
|
||||
Persisters implementing `\Doctrine\ORM\Persisters\Entity\EntityPersister` should no longer
|
||||
@@ -44,12 +95,12 @@ persister call `Doctrine\ORM\UnitOfWork::assignPostInsertId()` instead.
|
||||
In ORM 3.0, a change will be made regarding how the `AttributeDriver` reports field mappings.
|
||||
This change is necessary to be able to detect (and reject) some invalid mapping configurations.
|
||||
|
||||
To avoid surprises during 2.x upgrades, the new mode is opt-in. It can be activated on the
|
||||
To avoid surprises during 2.x upgrades, the new mode is opt-in. It can be activated on the
|
||||
`AttributeDriver` and `AnnotationDriver` by setting the `$reportFieldsWhereDeclared`
|
||||
constructor parameter to `true`. It will cause `MappingException`s to be thrown when invalid
|
||||
configurations are detected.
|
||||
|
||||
Not enabling the new mode will cause a deprecation notice to be raised. In ORM 3.0,
|
||||
Not enabling the new mode will cause a deprecation notice to be raised. In ORM 3.0,
|
||||
only the new mode will be available.
|
||||
|
||||
# Upgrade to 2.15
|
||||
@@ -69,7 +120,7 @@ and will be an error in 3.0.
|
||||
|
||||
## Deprecated undeclared entity inheritance
|
||||
|
||||
As soon as an entity class inherits from another entity class, inheritance has to
|
||||
As soon as an entity class inherits from another entity class, inheritance has to
|
||||
be declared by adding the appropriate configuration for the root entity.
|
||||
|
||||
## Deprecated stubs for "concrete table inheritance"
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"doctrine/lexer": "^2",
|
||||
"doctrine/persistence": "^2.4 || ^3",
|
||||
"psr/cache": "^1 || ^2 || ^3",
|
||||
"symfony/console": "^4.2 || ^5.0 || ^6.0",
|
||||
"symfony/console": "^4.2 || ^5.0 || ^6.0 || ^7.0",
|
||||
"symfony/polyfill-php72": "^1.23",
|
||||
"symfony/polyfill-php80": "^1.16"
|
||||
},
|
||||
@@ -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.28",
|
||||
"phpstan/phpstan": "~1.4.10 || 1.10.35",
|
||||
"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.0",
|
||||
"symfony/var-exporter": "^4.4 || ^5.4 || ^6.2",
|
||||
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
|
||||
"vimeo/psalm": "4.30.0 || 5.14.1"
|
||||
"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"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/annotations": "<1.13 || >= 3.0"
|
||||
|
||||
@@ -211,7 +211,7 @@ Now look at the following test-code for our entities:
|
||||
{
|
||||
public function testAddEntry()
|
||||
{
|
||||
$account = new Account("123456", $maxCredit = 200);
|
||||
$account = new Account("123456", maxCredit: 200);
|
||||
$this->assertEquals(0, $account->getBalance());
|
||||
|
||||
$account->addEntry(500);
|
||||
@@ -223,7 +223,7 @@ Now look at the following test-code for our entities:
|
||||
|
||||
public function testExceedMaxLimit()
|
||||
{
|
||||
$account = new Account("123456", $maxCredit = 200);
|
||||
$account = new Account("123456", maxCredit: 200);
|
||||
|
||||
$this->expectException(Exception::class);
|
||||
$account->addEntry(-1000);
|
||||
|
||||
@@ -127,7 +127,7 @@ the targetEntity resolution will occur reliably:
|
||||
// Add the ResolveTargetEntityListener
|
||||
$evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $rtel);
|
||||
|
||||
$connection = \Doctrine\DBAL\DriverManager::createConnection($connectionOptions, $config, $evm);
|
||||
$connection = \Doctrine\DBAL\DriverManager::getConnection($connectionOptions, $config, $evm);
|
||||
$em = new \Doctrine\ORM\EntityManager($connection, $config, $evm);
|
||||
|
||||
Final Thoughts
|
||||
|
||||
@@ -408,7 +408,7 @@ means that you have to register a special autoloader for these classes:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\Proxy\Autoloader;
|
||||
use Doctrine\ORM\Proxy\Autoloader;
|
||||
|
||||
$proxyDir = "/path/to/proxies";
|
||||
$proxyNamespace = "MyProxies";
|
||||
|
||||
@@ -24,28 +24,34 @@ performance it is also recommended that you use APC with PHP.
|
||||
Doctrine ORM Packages
|
||||
-------------------
|
||||
|
||||
Doctrine ORM is divided into three main packages.
|
||||
Doctrine ORM is divided into four main packages.
|
||||
|
||||
- Common
|
||||
- DBAL (includes Common)
|
||||
- ORM (includes DBAL+Common)
|
||||
- `Collections <https://www.doctrine-project.org/projects/doctrine-collections/en/stable/index.html>`_
|
||||
- `Event Manager <https://www.doctrine-project.org/projects/doctrine-event-manager/en/stable/index.html>`_
|
||||
- `Persistence <https://www.doctrine-project.org/projects/doctrine-persistence/en/stable/index.html>`_
|
||||
- `DBAL <https://www.doctrine-project.org/projects/doctrine-dbal/en/stable/index.html>`_
|
||||
- ORM (depends on DBAL+Persistence+Collections)
|
||||
|
||||
This manual mainly covers the ORM package, sometimes touching parts
|
||||
of the underlying DBAL and Common packages. The Doctrine code base
|
||||
of the underlying DBAL and Persistence packages. The Doctrine code base
|
||||
is split in to these packages for a few reasons and they are to...
|
||||
|
||||
|
||||
- ...make things more maintainable and decoupled
|
||||
- ...allow you to use the code in Doctrine Common without the ORM
|
||||
or DBAL
|
||||
- ...allow you to use the code in Doctrine Persistence and Collections
|
||||
without the ORM or DBAL
|
||||
- ...allow you to use the DBAL without the ORM
|
||||
|
||||
The Common Package
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Collection, Event Manager and Persistence
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The Common package contains highly reusable components that have no
|
||||
dependencies beyond the package itself (and PHP, of course). The
|
||||
root namespace of the Common package is ``Doctrine\Common``.
|
||||
The Collection, Event Manager and Persistence packages contain highly
|
||||
reusable components that have no dependencies beyond the packages
|
||||
themselves (and PHP, of course). The root namespace of the Persistence
|
||||
package is ``Doctrine\Persistence``. The root namespace of the
|
||||
Collection package is ``Doctrine\Common\Collections``, for historical
|
||||
reasons. The root namespace of the Event Manager package is just
|
||||
``Doctrine\Common``, also for historical reasons.
|
||||
|
||||
The DBAL Package
|
||||
~~~~~~~~~~~~~~~~
|
||||
@@ -199,5 +205,3 @@ typical implementation of the
|
||||
to keep track of all the things that need to be done the next time
|
||||
``flush`` is invoked. You usually do not directly interact with a
|
||||
``UnitOfWork`` but with the ``EntityManager`` instead.
|
||||
|
||||
|
||||
|
||||
@@ -422,9 +422,11 @@ the field that serves as the identifier with the ``#[Id]`` attribute.
|
||||
# fields here
|
||||
|
||||
In most cases using the automatic generator strategy (``#[GeneratedValue]``) is
|
||||
what you want. It defaults to the identifier generation mechanism your current
|
||||
database vendor prefers: AUTO_INCREMENT with MySQL, sequences with PostgreSQL
|
||||
and Oracle and so on.
|
||||
what you want, but for backwards-compatibility reasons it might not. It
|
||||
defaults to the identifier generation mechanism your current database
|
||||
vendor preferred at the time that strategy was introduced:
|
||||
``AUTO_INCREMENT`` with MySQL, sequences with PostgreSQL and Oracle and
|
||||
so on.
|
||||
|
||||
.. _identifier-generation-strategies:
|
||||
|
||||
@@ -441,17 +443,18 @@ Here is the list of possible generation strategies:
|
||||
|
||||
- ``AUTO`` (default): Tells Doctrine to pick the strategy that is
|
||||
preferred by the used database platform. The preferred strategies
|
||||
are IDENTITY for MySQL, SQLite, MsSQL and SQL Anywhere and SEQUENCE
|
||||
for Oracle and PostgreSQL. This strategy provides full portability.
|
||||
- ``SEQUENCE``: Tells Doctrine to use a database sequence for ID
|
||||
generation. This strategy does currently not provide full
|
||||
portability. Sequences are supported by Oracle, PostgreSql and
|
||||
SQL Anywhere.
|
||||
are ``IDENTITY`` for MySQL, SQLite, MsSQL and SQL Anywhere and, for
|
||||
historical reasons, ``SEQUENCE`` for Oracle and PostgreSQL. This
|
||||
strategy provides full portability.
|
||||
- ``IDENTITY``: Tells Doctrine to use special identity columns in
|
||||
the database that generate a value on insertion of a row. This
|
||||
strategy does currently not provide full portability and is
|
||||
supported by the following platforms: MySQL/SQLite/SQL Anywhere
|
||||
(AUTO\_INCREMENT), MSSQL (IDENTITY) and PostgreSQL (SERIAL).
|
||||
(``AUTO_INCREMENT``), MSSQL (``IDENTITY``) and PostgreSQL (``SERIAL``).
|
||||
- ``SEQUENCE``: Tells Doctrine to use a database sequence for ID
|
||||
generation. This strategy does currently not provide full
|
||||
portability. Sequences are supported by Oracle, PostgreSql and
|
||||
SQL Anywhere.
|
||||
- ``UUID`` (deprecated): Tells Doctrine to use the built-in Universally
|
||||
Unique Identifier generator. This strategy provides full portability.
|
||||
- ``NONE``: Tells Doctrine that the identifiers are assigned (and
|
||||
|
||||
@@ -464,6 +464,11 @@ hierarchies:
|
||||
$query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyEmployee');
|
||||
$query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1');
|
||||
$query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u NOT INSTANCE OF ?1');
|
||||
$query->setParameter(0, $em->getClassMetadata(CompanyEmployee::class));
|
||||
|
||||
.. note::
|
||||
To use a class as parameter, you have to bind its class metadata:
|
||||
``$query->setParameter(0, $em->getClassMetadata(CompanyEmployee::class);``.
|
||||
|
||||
Get all users visible on a given website that have chosen certain gender:
|
||||
|
||||
@@ -1381,7 +1386,7 @@ Result Cache API:
|
||||
$query->setResultCacheDriver(new ApcCache());
|
||||
|
||||
$query->useResultCache(true)
|
||||
->setResultCacheLifeTime($seconds = 3600);
|
||||
->setResultCacheLifeTime(3600);
|
||||
|
||||
$result = $query->getResult(); // cache miss
|
||||
|
||||
@@ -1392,7 +1397,7 @@ Result Cache API:
|
||||
$result = $query->getResult(); // saved in given result cache id.
|
||||
|
||||
// or call useResultCache() with all parameters:
|
||||
$query->useResultCache(true, $seconds = 3600, 'my_query_result');
|
||||
$query->useResultCache(true, 3600, 'my_query_result');
|
||||
$result = $query->getResult(); // cache hit!
|
||||
|
||||
// Introspection
|
||||
@@ -1457,7 +1462,7 @@ several methods to interact with it:
|
||||
|
||||
- ``Query::setQueryCacheDriver($driver)`` - Allows to set a Cache
|
||||
instance
|
||||
- ``Query::setQueryCacheLifeTime($seconds = 3600)`` - Set lifetime
|
||||
- ``Query::setQueryCacheLifeTime($seconds)`` - Set lifetime
|
||||
of the query caching.
|
||||
- ``Query::expireQueryCache($bool)`` - Enforce the expiring of the
|
||||
query cache if set to true.
|
||||
|
||||
@@ -457,13 +457,12 @@ prePersist
|
||||
|
||||
There are two ways for the ``prePersist`` event to be triggered:
|
||||
|
||||
- One is obviously when you call ``EntityManager::persist()``. The
|
||||
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
|
||||
during this operation is also persisted and ``prePersist`` called
|
||||
on it. This is called :ref:`persistence by reachability<persistence-by-reachability>`.
|
||||
- One is when you call ``EntityManager::persist()``. The
|
||||
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
|
||||
during this operation is also persisted and ``prePersist`` called
|
||||
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.
|
||||
|
||||
@@ -253,7 +253,7 @@ Calling ``setParameter()`` automatically infers which type you are setting as
|
||||
value. This works for integers, arrays of strings/integers, DateTime instances
|
||||
and for managed entities. If you want to set a type explicitly you can call
|
||||
the third argument to ``setParameter()`` explicitly. It accepts either a DBAL
|
||||
Doctrine\DBAL\ParameterType::* or a DBAL Type name for conversion.
|
||||
``Doctrine\DBAL\ParameterType::*`` or a DBAL Type name for conversion.
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
@@ -782,6 +782,23 @@ and these associations are mapped as EAGER, they will automatically
|
||||
be loaded together with the entity being queried and is thus
|
||||
immediately available to your application.
|
||||
|
||||
Eager Loading can also be configured at runtime through
|
||||
``AbstractQuery::setFetchMode`` in DQL or Native Queries.
|
||||
|
||||
Eager loading for many-to-one and one-to-one associations is using either a
|
||||
LEFT JOIN or a second query for fetching the related entity eagerly.
|
||||
|
||||
Eager loading for many-to-one associations uses a second query to load
|
||||
the collections for several entities at the same time.
|
||||
|
||||
When many-to-many, one-to-one or one-to-many associations are eagerly loaded,
|
||||
then the global batch size configuration is used to avoid IN(?) queries with
|
||||
too many arguments. The default batch size is 100 and can be changed with
|
||||
``Configuration::setEagerFetchBatchSize()``.
|
||||
|
||||
For eagerly loaded Many-To-Many associations one query has to be made for each
|
||||
collection.
|
||||
|
||||
By Lazy Loading
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -735,7 +735,7 @@ classes. We'll store them in ``src/Bug.php`` and ``src/User.php``, respectively.
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
#[ORM\GeneratedValue]
|
||||
private int $id;
|
||||
private int|null $id;
|
||||
|
||||
#[ORM\Column(type: 'string')]
|
||||
private string $description;
|
||||
@@ -1199,21 +1199,21 @@ which translates the YYYY-mm-dd HH:mm:ss database format
|
||||
into a PHP DateTime instance and back.
|
||||
|
||||
After the field definitions, the two qualified references to the
|
||||
user entity are defined. They are created by the ``many-to-one``
|
||||
tag. The class name of the related entity has to be specified with
|
||||
the ``target-entity`` attribute, which is enough information for
|
||||
the database mapper to access the foreign-table. Since
|
||||
user entity are defined. They are created by the ``ManyToOne``
|
||||
attribute. The class name of the related entity has to be specified with
|
||||
the ``targetEntity`` parameter, which is enough information for
|
||||
the database mapper to access the foreign table. Since
|
||||
``reporter`` and ``engineer`` are on the owning side of a
|
||||
bi-directional relation, we also have to specify the ``inversed-by``
|
||||
attribute. They have to point to the field names on the inverse
|
||||
side of the relationship. We will see in the next example that the ``inversed-by``
|
||||
attribute has a counterpart ``mapped-by`` which makes that
|
||||
bi-directional relation, we also have to specify the ``inversedBy``
|
||||
parameter. They have to point to the field names on the inverse
|
||||
side of the relationship. We will see in the next example that the ``inversedBy``
|
||||
parameter has a counterpart ``mappedBy`` which makes that
|
||||
the inverse side.
|
||||
|
||||
The last definition is for the ``Bug#products`` collection. It
|
||||
holds all products where the specific bug occurs. Again
|
||||
you have to define the ``target-entity`` and ``field`` attributes
|
||||
on the ``many-to-many`` tag.
|
||||
you have to define the ``targetEntity`` and ``field`` parameters
|
||||
on the ``ManyToMany`` attribute.
|
||||
|
||||
Finally, we'll add metadata mappings for the ``User`` entity.
|
||||
|
||||
@@ -1336,7 +1336,7 @@ Finally, we'll add metadata mappings for the ``User`` entity.
|
||||
targetEntity: Bug
|
||||
mappedBy: engineer
|
||||
|
||||
Here are some new things to mention about the ``one-to-many`` tags.
|
||||
Here are some new things to mention about the ``OneToMany`` attribute.
|
||||
Remember that we discussed about the inverse and owning side. Now
|
||||
both reportedBugs and assignedBugs are inverse relations, which
|
||||
means the join details have already been defined on the owning
|
||||
|
||||
@@ -15,7 +15,7 @@ has a very simple API and implements the SPL interfaces ``Countable`` and
|
||||
->setFirstResult(0)
|
||||
->setMaxResults(100);
|
||||
|
||||
$paginator = new Paginator($query, $fetchJoinCollection = true);
|
||||
$paginator = new Paginator($query, fetchJoinCollection: true);
|
||||
|
||||
$c = count($paginator);
|
||||
foreach ($paginator as $post) {
|
||||
@@ -36,10 +36,25 @@ correct result:
|
||||
|
||||
This behavior is only necessary if you actually fetch join a to-many
|
||||
collection. You can disable this behavior by setting the
|
||||
``$fetchJoinCollection`` flag to ``false``; in that case only 2 instead of the 3 queries
|
||||
``fetchJoinCollection`` argument to ``false``; in that case only 2 instead of the 3 queries
|
||||
described are executed. We hope to automate the detection for this in
|
||||
the future.
|
||||
|
||||
.. note::
|
||||
|
||||
``$fetchJoinCollection`` flag set to ``true`` might affect results if you use aggregations in your query.
|
||||
``fetchJoinCollection`` argument set to ``true`` might affect results if you use aggregations in your query.
|
||||
|
||||
By using the ``Paginator::HINT_ENABLE_DISTINCT`` you can instruct doctrine that the query to be executed
|
||||
will not produce "duplicate" rows (only to-one relations are joined), thus the SQL limit will work as expected.
|
||||
In this way the `DISTINCT` keyword will be omitted and can bring important performance improvements.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Tools\Pagination\Paginator;
|
||||
|
||||
$dql = "SELECT u, p FROM User u JOIN u.mainPicture p";
|
||||
$query = $entityManager->createQuery($dql)
|
||||
->setHint(Paginator::HINT_ENABLE_DISTINCT, false)
|
||||
->setFirstResult(0)
|
||||
->setMaxResults(100);
|
||||
|
||||
@@ -10,7 +10,6 @@ use Doctrine\Common\Cache\Psr6\CacheAdapter;
|
||||
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
use Doctrine\DBAL\Cache\QueryCacheProfile;
|
||||
use Doctrine\DBAL\Result;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
@@ -20,6 +19,7 @@ use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
use Doctrine\ORM\Cache\TimestampCacheKey;
|
||||
use Doctrine\ORM\Internal\Hydration\IterableResult;
|
||||
use Doctrine\ORM\Mapping\MappingException as ORMMappingException;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Query\Parameter;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
@@ -430,7 +430,7 @@ abstract class AbstractQuery
|
||||
}
|
||||
|
||||
try {
|
||||
$class = ClassUtils::getClass($value);
|
||||
$class = DefaultProxyClassNameResolver::getClass($value);
|
||||
$value = $this->_em->getUnitOfWork()->getSingleIdentifierValue($value);
|
||||
|
||||
if ($value === null) {
|
||||
|
||||
@@ -4,12 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Cache\Persister\CachedPersister;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\ORMInvalidArgumentException;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
|
||||
use function is_array;
|
||||
@@ -293,7 +293,7 @@ class DefaultCache implements Cache
|
||||
private function toIdentifierArray(ClassMetadata $metadata, $identifier): array
|
||||
{
|
||||
if (is_object($identifier)) {
|
||||
$class = ClassUtils::getClass($identifier);
|
||||
$class = DefaultProxyClassNameResolver::getClass($identifier);
|
||||
if ($this->em->getMetadataFactory()->hasMetadataFor($class)) {
|
||||
$identifier = $this->uow->getSingleIdentifierValue($identifier);
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Doctrine\ORM\Utility\IdentifierFlattener;
|
||||
@@ -112,7 +112,7 @@ class DefaultEntityHydrator implements EntityHydrator
|
||||
}
|
||||
|
||||
if (! isset($assoc['id'])) {
|
||||
$targetClass = ClassUtils::getClass($data[$name]);
|
||||
$targetClass = DefaultProxyClassNameResolver::getClass($data[$name]);
|
||||
$targetId = $this->uow->getEntityIdentifier($data[$name]);
|
||||
$data[$name] = new AssociationCacheEntry($targetClass, $targetId);
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ namespace Doctrine\ORM\Cache\Persister\Collection;
|
||||
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Cache\CollectionCacheKey;
|
||||
use Doctrine\ORM\Cache\CollectionHydrator;
|
||||
@@ -19,6 +18,7 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
|
||||
use function array_values;
|
||||
@@ -148,7 +148,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
}
|
||||
|
||||
$class = $this->targetEntity;
|
||||
$className = ClassUtils::getClass($elements[$index]);
|
||||
$className = DefaultProxyClassNameResolver::getClass($elements[$index]);
|
||||
|
||||
if ($className !== $this->targetEntity->name) {
|
||||
$class = $this->metadataFactory->getMetadataFor($className);
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache\Persister\Collection;
|
||||
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
use Doctrine\ORM\Cache\Exception\CannotUpdateReadOnlyCollection;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
|
||||
class ReadOnlyCachedCollectionPersister extends NonStrictReadWriteCachedCollectionPersister
|
||||
{
|
||||
@@ -17,7 +17,7 @@ class ReadOnlyCachedCollectionPersister extends NonStrictReadWriteCachedCollecti
|
||||
{
|
||||
if ($collection->isDirty() && $collection->getSnapshot()) {
|
||||
throw CannotUpdateReadOnlyCollection::fromEntityAndField(
|
||||
ClassUtils::getClass($collection->getOwner()),
|
||||
DefaultProxyClassNameResolver::getClass($collection->getOwner()),
|
||||
$this->association['fieldName']
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Cache\Persister\Entity;
|
||||
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Cache\CollectionCacheKey;
|
||||
use Doctrine\ORM\Cache\EntityCacheKey;
|
||||
@@ -21,6 +20,7 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
|
||||
use function array_merge;
|
||||
@@ -190,7 +190,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
public function storeEntityCache($entity, EntityCacheKey $key)
|
||||
{
|
||||
$class = $this->class;
|
||||
$className = ClassUtils::getClass($entity);
|
||||
$className = DefaultProxyClassNameResolver::getClass($entity);
|
||||
|
||||
if ($className !== $this->class->name) {
|
||||
$class = $this->metadataFactory->getMetadataFor($className);
|
||||
@@ -438,7 +438,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
}
|
||||
|
||||
$class = $this->class;
|
||||
$className = ClassUtils::getClass($entity);
|
||||
$className = DefaultProxyClassNameResolver::getClass($entity);
|
||||
|
||||
if ($className !== $this->class->name) {
|
||||
$class = $this->metadataFactory->getMetadataFor($className);
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache\Persister\Entity;
|
||||
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
use Doctrine\ORM\Cache\Exception\CannotUpdateReadOnlyEntity;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
|
||||
/**
|
||||
* Specific read-only region entity persister
|
||||
@@ -17,6 +17,6 @@ class ReadOnlyCachedEntityPersister extends NonStrictReadWriteCachedEntityPersis
|
||||
*/
|
||||
public function update($entity)
|
||||
{
|
||||
throw CannotUpdateReadOnlyEntity::fromEntity(ClassUtils::getClass($entity));
|
||||
throw CannotUpdateReadOnlyEntity::fromEntity(DefaultProxyClassNameResolver::getClass($entity));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use Doctrine\Common\Cache\Cache as CacheDriver;
|
||||
use Doctrine\Common\Cache\Psr6\CacheAdapter;
|
||||
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
|
||||
use Doctrine\Common\Persistence\PersistentObject;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Cache\CacheConfiguration;
|
||||
use Doctrine\ORM\Cache\Exception\CacheException;
|
||||
@@ -27,6 +28,7 @@ use Doctrine\ORM\Exception\NotSupported;
|
||||
use Doctrine\ORM\Exception\ProxyClassesAlwaysRegenerating;
|
||||
use Doctrine\ORM\Exception\UnknownEntityNamespace;
|
||||
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
|
||||
use Doctrine\ORM\Mapping\DefaultNamingStrategy;
|
||||
@@ -68,6 +70,21 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
/** @var mixed[] */
|
||||
protected $_attributes = [];
|
||||
|
||||
/** @psalm-var array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> */
|
||||
private $identityGenerationPreferences = [];
|
||||
|
||||
/** @psalm-param array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> $value */
|
||||
public function setIdentityGenerationPreferences(array $value): void
|
||||
{
|
||||
$this->identityGenerationPreferences = $value;
|
||||
}
|
||||
|
||||
/** @psalm-return array<class-string<AbstractPlatform>, ClassMetadata::GENERATOR_TYPE_*> $value */
|
||||
public function getIdentityGenerationPreferences(): array
|
||||
{
|
||||
return $this->identityGenerationPreferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the directory where Doctrine generates any necessary proxy class files.
|
||||
*
|
||||
@@ -1127,4 +1144,14 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
{
|
||||
return $this->_attributes['rejectIdCollisionInIdentityMap'] ?? false;
|
||||
}
|
||||
|
||||
public function setEagerFetchBatchSize(int $batchSize = 100): void
|
||||
{
|
||||
$this->_attributes['fetchModeSubselectBatchSize'] = $batchSize;
|
||||
}
|
||||
|
||||
public function getEagerFetchBatchSize(): int
|
||||
{
|
||||
return $this->_attributes['fetchModeSubselectBatchSize'] ?? 100;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Decorator;
|
||||
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
@@ -170,6 +171,13 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements
|
||||
*/
|
||||
public function getPartialReference($entityName, $identifier)
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/10987',
|
||||
'Method %s is deprecated and will be removed in 3.0.',
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
return $this->wrapped->getPartialReference($entityName, $identifier);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ use BadMethodCallException;
|
||||
use Doctrine\Common\Cache\Psr6\CacheAdapter;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\Common\Persistence\PersistentObject;
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
@@ -24,6 +23,7 @@ use Doctrine\ORM\Exception\ORMException;
|
||||
use Doctrine\ORM\Exception\UnrecognizedIdentifierFields;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\Query\FilterCollection;
|
||||
@@ -444,7 +444,7 @@ class EntityManager implements EntityManagerInterface
|
||||
|
||||
foreach ($id as $i => $value) {
|
||||
if (is_object($value)) {
|
||||
$className = ClassUtils::getClass($value);
|
||||
$className = DefaultProxyClassNameResolver::getClass($value);
|
||||
if ($this->metadataFactory->hasMetadataFor($className)) {
|
||||
$id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
|
||||
|
||||
@@ -571,6 +571,12 @@ class EntityManager implements EntityManagerInterface
|
||||
*/
|
||||
public function getPartialReference($entityName, $identifier)
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/10987',
|
||||
'Method %s is deprecated and will be removed in 3.0.',
|
||||
__METHOD__
|
||||
);
|
||||
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
|
||||
|
||||
$entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName);
|
||||
@@ -981,7 +987,7 @@ class EntityManager implements EntityManagerInterface
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/9961',
|
||||
'%s() is deprecated. To boostrap a DBAL connection, call %s::getConnection() instead. Use the constructor to create an instance of %s.',
|
||||
'%s() is deprecated. To bootstrap a DBAL connection, call %s::getConnection() instead. Use the constructor to create an instance of %s.',
|
||||
__METHOD__,
|
||||
DriverManager::class,
|
||||
self::class
|
||||
|
||||
@@ -200,6 +200,8 @@ interface EntityManagerInterface extends ObjectManager
|
||||
* never be visible to the application (especially not event listeners) as it will
|
||||
* never be loaded in the first place.
|
||||
*
|
||||
* @deprecated 2.7 This method is being removed from the ORM and won't have any replacement
|
||||
*
|
||||
* @param string $entityName The name of the entity type.
|
||||
* @param mixed $identifier The entity identifier.
|
||||
* @psalm-param class-string<T> $entityName
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Internal\CommitOrder;
|
||||
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
final class Edge
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @readonly
|
||||
*/
|
||||
public $from;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @readonly
|
||||
*/
|
||||
public $to;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @readonly
|
||||
*/
|
||||
public $weight;
|
||||
|
||||
public function __construct(string $from, string $to, int $weight)
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/10547',
|
||||
'The %s class is deprecated and will be removed in ORM 3.0',
|
||||
self::class
|
||||
);
|
||||
|
||||
$this->from = $from;
|
||||
$this->to = $to;
|
||||
$this->weight = $weight;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Internal\CommitOrder;
|
||||
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
final class Vertex
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @readonly
|
||||
*/
|
||||
public $hash;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @psalm-var VertexState::*
|
||||
*/
|
||||
public $state = VertexState::NOT_VISITED;
|
||||
|
||||
/**
|
||||
* @var ClassMetadata
|
||||
* @readonly
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/** @var array<string, Edge> */
|
||||
public $dependencyList = [];
|
||||
|
||||
public function __construct(string $hash, ClassMetadata $value)
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/10547',
|
||||
'The %s class is deprecated and will be removed in ORM 3.0',
|
||||
self::class
|
||||
);
|
||||
|
||||
$this->hash = $hash;
|
||||
$this->value = $value;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Internal\CommitOrder;
|
||||
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
final class VertexState
|
||||
{
|
||||
public const NOT_VISITED = 0;
|
||||
public const IN_PROGRESS = 1;
|
||||
public const VISITED = 2;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/10547',
|
||||
'The %s class is deprecated and will be removed in ORM 3.0',
|
||||
self::class
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Internal;
|
||||
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Internal\CommitOrder\Edge;
|
||||
use Doctrine\ORM\Internal\CommitOrder\Vertex;
|
||||
use Doctrine\ORM\Internal\CommitOrder\VertexState;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
use function array_reverse;
|
||||
|
||||
/**
|
||||
* CommitOrderCalculator implements topological sorting, which is an ordering
|
||||
* algorithm for directed graphs (DG) and/or directed acyclic graphs (DAG) by
|
||||
* using a depth-first searching (DFS) to traverse the graph built in memory.
|
||||
* This algorithm have a linear running time based on nodes (V) and dependency
|
||||
* between the nodes (E), resulting in a computational complexity of O(V + E).
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
class CommitOrderCalculator
|
||||
{
|
||||
/** @deprecated */
|
||||
public const NOT_VISITED = VertexState::NOT_VISITED;
|
||||
|
||||
/** @deprecated */
|
||||
public const IN_PROGRESS = VertexState::IN_PROGRESS;
|
||||
|
||||
/** @deprecated */
|
||||
public const VISITED = VertexState::VISITED;
|
||||
|
||||
/**
|
||||
* Matrix of nodes (aka. vertex).
|
||||
*
|
||||
* Keys are provided hashes and values are the node definition objects.
|
||||
*
|
||||
* @var array<string, Vertex>
|
||||
*/
|
||||
private $nodeList = [];
|
||||
|
||||
/**
|
||||
* Volatile variable holding calculated nodes during sorting process.
|
||||
*
|
||||
* @psalm-var list<ClassMetadata>
|
||||
*/
|
||||
private $sortedNodeList = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/10547',
|
||||
'The %s class is deprecated and will be removed in ORM 3.0',
|
||||
self::class
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for node (vertex) existence in graph.
|
||||
*
|
||||
* @param string $hash
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNode($hash)
|
||||
{
|
||||
return isset($this->nodeList[$hash]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new node (vertex) to the graph, assigning its hash and value.
|
||||
*
|
||||
* @param string $hash
|
||||
* @param ClassMetadata $node
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addNode($hash, $node)
|
||||
{
|
||||
$this->nodeList[$hash] = new Vertex($hash, $node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new dependency (edge) to the graph using their hashes.
|
||||
*
|
||||
* @param string $fromHash
|
||||
* @param string $toHash
|
||||
* @param int $weight
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addDependency($fromHash, $toHash, $weight)
|
||||
{
|
||||
$this->nodeList[$fromHash]->dependencyList[$toHash]
|
||||
= new Edge($fromHash, $toHash, $weight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a valid order list of all current nodes.
|
||||
* The desired topological sorting is the reverse post order of these searches.
|
||||
*
|
||||
* {@internal Highly performance-sensitive method.}
|
||||
*
|
||||
* @psalm-return list<ClassMetadata>
|
||||
*/
|
||||
public function sort()
|
||||
{
|
||||
foreach ($this->nodeList as $vertex) {
|
||||
if ($vertex->state !== VertexState::NOT_VISITED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->visit($vertex);
|
||||
}
|
||||
|
||||
$sortedList = $this->sortedNodeList;
|
||||
|
||||
$this->nodeList = [];
|
||||
$this->sortedNodeList = [];
|
||||
|
||||
return array_reverse($sortedList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit a given node definition for reordering.
|
||||
*
|
||||
* {@internal Highly performance-sensitive method.}
|
||||
*/
|
||||
private function visit(Vertex $vertex): void
|
||||
{
|
||||
$vertex->state = VertexState::IN_PROGRESS;
|
||||
|
||||
foreach ($vertex->dependencyList as $edge) {
|
||||
$adjacentVertex = $this->nodeList[$edge->to];
|
||||
|
||||
switch ($adjacentVertex->state) {
|
||||
case VertexState::VISITED:
|
||||
// Do nothing, since node was already visited
|
||||
break;
|
||||
|
||||
case VertexState::IN_PROGRESS:
|
||||
if (
|
||||
isset($adjacentVertex->dependencyList[$vertex->hash]) &&
|
||||
$adjacentVertex->dependencyList[$vertex->hash]->weight < $edge->weight
|
||||
) {
|
||||
// If we have some non-visited dependencies in the in-progress dependency, we
|
||||
// need to visit them before adding the node.
|
||||
foreach ($adjacentVertex->dependencyList as $adjacentEdge) {
|
||||
$adjacentEdgeVertex = $this->nodeList[$adjacentEdge->to];
|
||||
|
||||
if ($adjacentEdgeVertex->state === VertexState::NOT_VISITED) {
|
||||
$this->visit($adjacentEdgeVertex);
|
||||
}
|
||||
}
|
||||
|
||||
$adjacentVertex->state = VertexState::VISITED;
|
||||
|
||||
$this->sortedNodeList[] = $adjacentVertex->value;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case VertexState::NOT_VISITED:
|
||||
$this->visit($adjacentVertex);
|
||||
}
|
||||
}
|
||||
|
||||
if ($vertex->state !== VertexState::VISITED) {
|
||||
$vertex->state = VertexState::VISITED;
|
||||
|
||||
$this->sortedNodeList[] = $vertex->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ use Doctrine\ORM\Id\UuidGenerator;
|
||||
use Doctrine\ORM\Mapping\Exception\CannotGenerateIds;
|
||||
use Doctrine\ORM\Mapping\Exception\InvalidCustomGenerator;
|
||||
use Doctrine\ORM\Mapping\Exception\UnknownGeneratorType;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
|
||||
use Doctrine\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
@@ -38,6 +39,7 @@ use function end;
|
||||
use function explode;
|
||||
use function get_class;
|
||||
use function in_array;
|
||||
use function is_a;
|
||||
use function is_subclass_of;
|
||||
use function str_contains;
|
||||
use function strlen;
|
||||
@@ -71,9 +73,17 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
/** @var mixed[] */
|
||||
private $embeddablesActiveNesting = [];
|
||||
|
||||
private const NON_IDENTITY_DEFAULT_STRATEGY = [
|
||||
'Doctrine\DBAL\Platforms\PostgreSqlPlatform' => ClassMetadata::GENERATOR_TYPE_SEQUENCE,
|
||||
Platforms\OraclePlatform::class => ClassMetadata::GENERATOR_TYPE_SEQUENCE,
|
||||
Platforms\PostgreSQLPlatform::class => ClassMetadata::GENERATOR_TYPE_SEQUENCE,
|
||||
];
|
||||
|
||||
/** @return void */
|
||||
public function setEntityManager(EntityManagerInterface $em)
|
||||
{
|
||||
parent::setProxyClassNameResolver(new DefaultProxyClassNameResolver());
|
||||
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
@@ -630,7 +640,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
'https://github.com/doctrine/orm/issues/8850',
|
||||
<<<'DEPRECATION'
|
||||
Context: Loading metadata for class %s
|
||||
Problem: Using the IDENTITY generator strategy with platform "%s" is deprecated and will not be possible in Doctrine ORM 3.0.
|
||||
Problem: Using identity columns emulated with a sequence is deprecated and will not be possible in Doctrine ORM 3.0.
|
||||
Solution: Use the SEQUENCE generator strategy instead.
|
||||
DEPRECATION
|
||||
,
|
||||
@@ -725,14 +735,40 @@ DEPRECATION
|
||||
}
|
||||
}
|
||||
|
||||
/** @psalm-return ClassMetadata::GENERATOR_TYPE_SEQUENCE|ClassMetadata::GENERATOR_TYPE_IDENTITY */
|
||||
/** @psalm-return ClassMetadata::GENERATOR_TYPE_* */
|
||||
private function determineIdGeneratorStrategy(AbstractPlatform $platform): int
|
||||
{
|
||||
if (
|
||||
$platform instanceof Platforms\OraclePlatform
|
||||
|| $platform instanceof Platforms\PostgreSQLPlatform
|
||||
) {
|
||||
return ClassMetadata::GENERATOR_TYPE_SEQUENCE;
|
||||
assert($this->em !== null);
|
||||
foreach ($this->em->getConfiguration()->getIdentityGenerationPreferences() as $platformFamily => $strategy) {
|
||||
if (is_a($platform, $platformFamily)) {
|
||||
return $strategy;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::NON_IDENTITY_DEFAULT_STRATEGY as $platformFamily => $strategy) {
|
||||
if (is_a($platform, $platformFamily)) {
|
||||
if ($platform instanceof Platforms\PostgreSQLPlatform || is_a($platform, 'Doctrine\DBAL\Platforms\PostgreSqlPlatform')) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8893',
|
||||
<<<'DEPRECATION'
|
||||
Relying on non-optimal defaults for ID generation is deprecated, and IDENTITY
|
||||
results in SERIAL, which is not recommended.
|
||||
Instead, configure identifier generation strategies explicitly through
|
||||
configuration.
|
||||
We currently recommend "SEQUENCE" for "%s", so you should use
|
||||
$configuration->setIdentityGenerationPreferences([
|
||||
"%s" => ClassMetadata::GENERATOR_TYPE_SEQUENCE,
|
||||
]);
|
||||
DEPRECATION
|
||||
,
|
||||
$platformFamily,
|
||||
$platformFamily
|
||||
);
|
||||
}
|
||||
|
||||
return $strategy;
|
||||
}
|
||||
}
|
||||
|
||||
if ($platform->supportsIdentityColumns()) {
|
||||
|
||||
@@ -30,6 +30,8 @@ use function is_numeric;
|
||||
|
||||
/**
|
||||
* The AnnotationDriver reads the mapping metadata from docblock annotations.
|
||||
*
|
||||
* @deprecated This class will be removed in 3.0 without replacement.
|
||||
*/
|
||||
class AnnotationDriver extends CompatibilityAnnotationDriver
|
||||
{
|
||||
|
||||
@@ -69,8 +69,7 @@ final class AttributeReader
|
||||
));
|
||||
}
|
||||
|
||||
return $this->getPropertyAttributes($property)[$attributeName]
|
||||
?? ($this->isRepeatable($attributeName) ? new RepeatableAttributeCollection() : null);
|
||||
return $this->getPropertyAttributes($property)[$attributeName] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -128,7 +128,7 @@ class XmlDriver extends FileDriver
|
||||
|
||||
// Evaluate named queries
|
||||
if (isset($xmlRoot->{'named-queries'})) {
|
||||
foreach ($xmlRoot->{'named-queries'}->{'named-query'} as $namedQueryElement) {
|
||||
foreach ($xmlRoot->{'named-queries'}->{'named-query'} ?? [] as $namedQueryElement) {
|
||||
$metadata->addNamedQuery(
|
||||
[
|
||||
'name' => (string) $namedQueryElement['name'],
|
||||
@@ -140,7 +140,7 @@ class XmlDriver extends FileDriver
|
||||
|
||||
// Evaluate native named queries
|
||||
if (isset($xmlRoot->{'named-native-queries'})) {
|
||||
foreach ($xmlRoot->{'named-native-queries'}->{'named-native-query'} as $nativeQueryElement) {
|
||||
foreach ($xmlRoot->{'named-native-queries'}->{'named-native-query'} ?? [] as $nativeQueryElement) {
|
||||
$metadata->addNamedNativeQuery(
|
||||
[
|
||||
'name' => isset($nativeQueryElement['name']) ? (string) $nativeQueryElement['name'] : null,
|
||||
@@ -154,7 +154,7 @@ class XmlDriver extends FileDriver
|
||||
|
||||
// Evaluate sql result set mapping
|
||||
if (isset($xmlRoot->{'sql-result-set-mappings'})) {
|
||||
foreach ($xmlRoot->{'sql-result-set-mappings'}->{'sql-result-set-mapping'} as $rsmElement) {
|
||||
foreach ($xmlRoot->{'sql-result-set-mappings'}->{'sql-result-set-mapping'} ?? [] as $rsmElement) {
|
||||
$entities = [];
|
||||
$columns = [];
|
||||
foreach ($rsmElement as $entityElement) {
|
||||
@@ -240,7 +240,7 @@ class XmlDriver extends FileDriver
|
||||
// Evaluate <indexes...>
|
||||
if (isset($xmlRoot->indexes)) {
|
||||
$metadata->table['indexes'] = [];
|
||||
foreach ($xmlRoot->indexes->index as $indexXml) {
|
||||
foreach ($xmlRoot->indexes->index ?? [] as $indexXml) {
|
||||
$index = [];
|
||||
|
||||
if (isset($indexXml['columns']) && ! empty($indexXml['columns'])) {
|
||||
@@ -283,7 +283,7 @@ class XmlDriver extends FileDriver
|
||||
// Evaluate <unique-constraints..>
|
||||
if (isset($xmlRoot->{'unique-constraints'})) {
|
||||
$metadata->table['uniqueConstraints'] = [];
|
||||
foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $uniqueXml) {
|
||||
foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} ?? [] as $uniqueXml) {
|
||||
$unique = [];
|
||||
|
||||
if (isset($uniqueXml['columns']) && ! empty($uniqueXml['columns'])) {
|
||||
@@ -370,7 +370,7 @@ class XmlDriver extends FileDriver
|
||||
|
||||
// Evaluate <id ...> mappings
|
||||
$associationIds = [];
|
||||
foreach ($xmlRoot->id as $idElement) {
|
||||
foreach ($xmlRoot->id ?? [] as $idElement) {
|
||||
if (isset($idElement['association-key']) && $this->evaluateBoolean($idElement['association-key'])) {
|
||||
$associationIds[(string) $idElement['name']] = true;
|
||||
continue;
|
||||
@@ -439,7 +439,7 @@ class XmlDriver extends FileDriver
|
||||
if (isset($oneToOneElement->{'join-column'})) {
|
||||
$joinColumns[] = $this->joinColumnToArray($oneToOneElement->{'join-column'});
|
||||
} elseif (isset($oneToOneElement->{'join-columns'})) {
|
||||
foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
|
||||
foreach ($oneToOneElement->{'join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {
|
||||
$joinColumns[] = $this->joinColumnToArray($joinColumnElement);
|
||||
}
|
||||
}
|
||||
@@ -490,7 +490,7 @@ class XmlDriver extends FileDriver
|
||||
|
||||
if (isset($oneToManyElement->{'order-by'})) {
|
||||
$orderBy = [];
|
||||
foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
|
||||
foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {
|
||||
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
|
||||
? (string) $orderByField['direction']
|
||||
: Criteria::ASC;
|
||||
@@ -542,7 +542,7 @@ class XmlDriver extends FileDriver
|
||||
if (isset($manyToOneElement->{'join-column'})) {
|
||||
$joinColumns[] = $this->joinColumnToArray($manyToOneElement->{'join-column'});
|
||||
} elseif (isset($manyToOneElement->{'join-columns'})) {
|
||||
foreach ($manyToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
|
||||
foreach ($manyToOneElement->{'join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {
|
||||
$joinColumns[] = $this->joinColumnToArray($joinColumnElement);
|
||||
}
|
||||
}
|
||||
@@ -601,11 +601,11 @@ class XmlDriver extends FileDriver
|
||||
$joinTable['options'] = $this->parseOptions($joinTableElement->options->children());
|
||||
}
|
||||
|
||||
foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
|
||||
foreach ($joinTableElement->{'join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {
|
||||
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
|
||||
}
|
||||
|
||||
foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
|
||||
foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {
|
||||
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
|
||||
}
|
||||
|
||||
@@ -618,7 +618,7 @@ class XmlDriver extends FileDriver
|
||||
|
||||
if (isset($manyToManyElement->{'order-by'})) {
|
||||
$orderBy = [];
|
||||
foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
|
||||
foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {
|
||||
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
|
||||
? (string) $orderByField['direction']
|
||||
: Criteria::ASC;
|
||||
@@ -644,9 +644,9 @@ class XmlDriver extends FileDriver
|
||||
|
||||
// Evaluate association-overrides
|
||||
if (isset($xmlRoot->{'attribute-overrides'})) {
|
||||
foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} as $overrideElement) {
|
||||
foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} ?? [] as $overrideElement) {
|
||||
$fieldName = (string) $overrideElement['name'];
|
||||
foreach ($overrideElement->field as $field) {
|
||||
foreach ($overrideElement->field ?? [] as $field) {
|
||||
$mapping = $this->columnToArray($field);
|
||||
$mapping['fieldName'] = $fieldName;
|
||||
$metadata->setAttributeOverride($fieldName, $mapping);
|
||||
@@ -656,14 +656,14 @@ class XmlDriver extends FileDriver
|
||||
|
||||
// Evaluate association-overrides
|
||||
if (isset($xmlRoot->{'association-overrides'})) {
|
||||
foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $overrideElement) {
|
||||
foreach ($xmlRoot->{'association-overrides'}->{'association-override'} ?? [] as $overrideElement) {
|
||||
$fieldName = (string) $overrideElement['name'];
|
||||
$override = [];
|
||||
|
||||
// Check for join-columns
|
||||
if (isset($overrideElement->{'join-columns'})) {
|
||||
$joinColumns = [];
|
||||
foreach ($overrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
|
||||
foreach ($overrideElement->{'join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {
|
||||
$joinColumns[] = $this->joinColumnToArray($joinColumnElement);
|
||||
}
|
||||
|
||||
@@ -685,13 +685,13 @@ class XmlDriver extends FileDriver
|
||||
}
|
||||
|
||||
if (isset($joinTableElement->{'join-columns'})) {
|
||||
foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
|
||||
foreach ($joinTableElement->{'join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {
|
||||
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($joinTableElement->{'inverse-join-columns'})) {
|
||||
foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
|
||||
foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} ?? [] as $joinColumnElement) {
|
||||
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
|
||||
}
|
||||
}
|
||||
@@ -715,14 +715,14 @@ class XmlDriver extends FileDriver
|
||||
|
||||
// Evaluate <lifecycle-callbacks...>
|
||||
if (isset($xmlRoot->{'lifecycle-callbacks'})) {
|
||||
foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) {
|
||||
foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} ?? [] as $lifecycleCallback) {
|
||||
$metadata->addLifecycleCallback((string) $lifecycleCallback['method'], constant('Doctrine\ORM\Events::' . (string) $lifecycleCallback['type']));
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate entity listener
|
||||
if (isset($xmlRoot->{'entity-listeners'})) {
|
||||
foreach ($xmlRoot->{'entity-listeners'}->{'entity-listener'} as $listenerElement) {
|
||||
foreach ($xmlRoot->{'entity-listeners'}->{'entity-listener'} ?? [] as $listenerElement) {
|
||||
$className = (string) $listenerElement['class'];
|
||||
// Evaluate the listener using naming convention.
|
||||
if ($listenerElement->count() === 0) {
|
||||
@@ -744,16 +744,14 @@ class XmlDriver extends FileDriver
|
||||
/**
|
||||
* Parses (nested) option elements.
|
||||
*
|
||||
* @param SimpleXMLElement $options The XML element.
|
||||
*
|
||||
* @return mixed[] The options array.
|
||||
* @psalm-return array<int|string, array<int|string, mixed|string>|bool|string>
|
||||
*/
|
||||
private function parseOptions(SimpleXMLElement $options): array
|
||||
private function parseOptions(?SimpleXMLElement $options): array
|
||||
{
|
||||
$array = [];
|
||||
|
||||
foreach ($options as $option) {
|
||||
foreach ($options ?? [] as $option) {
|
||||
if ($option->count()) {
|
||||
$value = $this->parseOptions($option->children());
|
||||
} else {
|
||||
@@ -816,7 +814,7 @@ class XmlDriver extends FileDriver
|
||||
}
|
||||
|
||||
if (isset($joinColumnElement['options'])) {
|
||||
$joinColumn['options'] = $this->parseOptions($joinColumnElement['options']->children());
|
||||
$joinColumn['options'] = $this->parseOptions($joinColumnElement['options'] ? $joinColumnElement['options']->children() : null);
|
||||
}
|
||||
|
||||
return $joinColumn;
|
||||
@@ -972,19 +970,19 @@ class XmlDriver extends FileDriver
|
||||
|
||||
if (isset($xmlElement->entity)) {
|
||||
foreach ($xmlElement->entity as $entityElement) {
|
||||
/** @psalm-var class-string */
|
||||
/** @psalm-var class-string $entityName */
|
||||
$entityName = (string) $entityElement['name'];
|
||||
$result[$entityName] = $entityElement;
|
||||
}
|
||||
} elseif (isset($xmlElement->{'mapped-superclass'})) {
|
||||
foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) {
|
||||
/** @psalm-var class-string */
|
||||
/** @psalm-var class-string $className */
|
||||
$className = (string) $mappedSuperClass['name'];
|
||||
$result[$className] = $mappedSuperClass;
|
||||
}
|
||||
} elseif (isset($xmlElement->embeddable)) {
|
||||
foreach ($xmlElement->embeddable as $embeddableElement) {
|
||||
/** @psalm-var class-string */
|
||||
/** @psalm-var class-string $embeddableName */
|
||||
$embeddableName = (string) $embeddableElement['name'];
|
||||
$result[$embeddableName] = $embeddableElement;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Doctrine\ORM\Mapping;
|
||||
* Is used to specify an array of native SQL named queries.
|
||||
* The NamedNativeQueries annotation can be applied to an entity or mapped superclass.
|
||||
*
|
||||
* @deprecated Named queries won't be supported in ORM 3.
|
||||
*
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Doctrine\ORM\Mapping;
|
||||
* Is used to specify a native SQL named query.
|
||||
* The NamedNativeQuery annotation can be applied to an entity or mapped superclass.
|
||||
*
|
||||
* @deprecated Named queries won't be supported in ORM 3.
|
||||
*
|
||||
* @Annotation
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,8 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @deprecated Named queries won't be supported in ORM 3.
|
||||
*
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,8 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @deprecated Named queries won't be supported in ORM 3.
|
||||
*
|
||||
* @Annotation
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
|
||||
@@ -11,8 +11,10 @@ use function ksort;
|
||||
|
||||
/**
|
||||
* Represents a native SQL query.
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
final class NativeQuery extends AbstractQuery
|
||||
class NativeQuery extends AbstractQuery
|
||||
{
|
||||
/** @var string */
|
||||
private $sql;
|
||||
|
||||
@@ -15,6 +15,7 @@ use function func_num_args;
|
||||
use function get_debug_type;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function is_scalar;
|
||||
use function method_exists;
|
||||
use function reset;
|
||||
use function spl_object_id;
|
||||
@@ -261,6 +262,32 @@ EXCEPTION
|
||||
return new self(sprintf('Entity name must be a string, %s given', get_debug_type($entityName)));
|
||||
}
|
||||
|
||||
/** @param mixed $value */
|
||||
public static function invalidAutoGenerateMode($value): self
|
||||
{
|
||||
return new self(sprintf('Invalid auto generate mode "%s" given.', is_scalar($value) ? (string) $value : get_debug_type($value)));
|
||||
}
|
||||
|
||||
public static function missingPrimaryKeyValue(string $className, string $idField): self
|
||||
{
|
||||
return new self(sprintf('Missing value for primary key %s on %s', $idField, $className));
|
||||
}
|
||||
|
||||
public static function proxyDirectoryRequired(): self
|
||||
{
|
||||
return new self('You must configure a proxy directory. See docs for details');
|
||||
}
|
||||
|
||||
public static function proxyNamespaceRequired(): self
|
||||
{
|
||||
return new self('You must configure a proxy namespace');
|
||||
}
|
||||
|
||||
public static function proxyDirectoryNotWritable(string $proxyDirectory): self
|
||||
{
|
||||
return new self(sprintf('Your proxy directory "%s" must be writable', $proxyDirectory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to show an object as string.
|
||||
*
|
||||
|
||||
@@ -7,7 +7,6 @@ namespace Doctrine\ORM\Persisters\Entity;
|
||||
use BackedEnum;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Expr\Comparison;
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
@@ -26,6 +25,7 @@ use Doctrine\ORM\Persisters\Exception\InvalidOrientation;
|
||||
use Doctrine\ORM\Persisters\Exception\UnrecognizedField;
|
||||
use Doctrine\ORM\Persisters\SqlExpressionVisitor;
|
||||
use Doctrine\ORM\Persisters\SqlValueVisitor;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\ORM\Repository\Exception\InvalidFindByCall;
|
||||
@@ -1264,7 +1264,7 @@ class BasicEntityPersister implements EntityPersister
|
||||
}
|
||||
|
||||
$isAssocToOneInverseSide = $assoc['type'] & ClassMetadata::TO_ONE && ! $assoc['isOwningSide'];
|
||||
$isAssocFromOneEager = $assoc['type'] !== ClassMetadata::MANY_TO_MANY && $assoc['fetch'] === ClassMetadata::FETCH_EAGER;
|
||||
$isAssocFromOneEager = $assoc['type'] & ClassMetadata::TO_ONE && $assoc['fetch'] === ClassMetadata::FETCH_EAGER;
|
||||
|
||||
if (! ($isAssocFromOneEager || $isAssocToOneInverseSide)) {
|
||||
continue;
|
||||
@@ -2028,7 +2028,7 @@ class BasicEntityPersister implements EntityPersister
|
||||
return [$value->value];
|
||||
}
|
||||
|
||||
$valueClass = ClassUtils::getClass($value);
|
||||
$valueClass = DefaultProxyClassNameResolver::getClass($value);
|
||||
|
||||
if ($this->em->getMetadataFactory()->isTransient($valueClass)) {
|
||||
return [$value];
|
||||
|
||||
@@ -6,7 +6,6 @@ namespace Doctrine\ORM\Proxy;
|
||||
|
||||
use Doctrine\Common\Proxy\Autoloader as BaseAutoloader;
|
||||
|
||||
/** @deprecated use \Doctrine\Common\Proxy\Autoloader instead */
|
||||
class Autoloader extends BaseAutoloader
|
||||
{
|
||||
}
|
||||
|
||||
40
lib/Doctrine/ORM/Proxy/DefaultProxyClassNameResolver.php
Normal file
40
lib/Doctrine/ORM/Proxy/DefaultProxyClassNameResolver.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Proxy;
|
||||
|
||||
use Doctrine\Persistence\Mapping\ProxyClassNameResolver;
|
||||
use Doctrine\Persistence\Proxy;
|
||||
|
||||
use function get_class;
|
||||
use function strrpos;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* Class-related functionality for objects that might or not be proxy objects
|
||||
* at the moment.
|
||||
*/
|
||||
final class DefaultProxyClassNameResolver implements ProxyClassNameResolver
|
||||
{
|
||||
public function resolveClassName(string $className): string
|
||||
{
|
||||
$pos = strrpos($className, '\\' . Proxy::MARKER . '\\');
|
||||
|
||||
if ($pos === false) {
|
||||
return $className;
|
||||
}
|
||||
|
||||
return substr($className, $pos + Proxy::MARKER_LENGTH + 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $object
|
||||
*
|
||||
* @return class-string
|
||||
*/
|
||||
public static function getClass($object): string
|
||||
{
|
||||
return (new self())->resolveClassName(get_class($object));
|
||||
}
|
||||
}
|
||||
@@ -9,30 +9,94 @@ use Doctrine\Common\Proxy\AbstractProxyFactory;
|
||||
use Doctrine\Common\Proxy\Proxy as CommonProxy;
|
||||
use Doctrine\Common\Proxy\ProxyDefinition;
|
||||
use Doctrine\Common\Proxy\ProxyGenerator;
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityNotFoundException;
|
||||
use Doctrine\ORM\ORMInvalidArgumentException;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use Doctrine\ORM\Proxy\Proxy as LegacyProxy;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Doctrine\ORM\Utility\IdentifierFlattener;
|
||||
use Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\Persistence\Proxy;
|
||||
use ReflectionProperty;
|
||||
use Symfony\Component\VarExporter\ProxyHelper;
|
||||
use Symfony\Component\VarExporter\VarExporter;
|
||||
use Throwable;
|
||||
|
||||
use function array_combine;
|
||||
use function array_flip;
|
||||
use function array_intersect_key;
|
||||
use function bin2hex;
|
||||
use function chmod;
|
||||
use function class_exists;
|
||||
use function dirname;
|
||||
use function file_exists;
|
||||
use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function is_bool;
|
||||
use function is_dir;
|
||||
use function is_int;
|
||||
use function is_writable;
|
||||
use function ltrim;
|
||||
use function mkdir;
|
||||
use function preg_match_all;
|
||||
use function random_bytes;
|
||||
use function rename;
|
||||
use function rtrim;
|
||||
use function str_replace;
|
||||
use function strpos;
|
||||
use function strrpos;
|
||||
use function strtr;
|
||||
use function substr;
|
||||
use function uksort;
|
||||
use function ucfirst;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
/**
|
||||
* This factory is used to create proxy objects for entities at runtime.
|
||||
*/
|
||||
class ProxyFactory extends AbstractProxyFactory
|
||||
{
|
||||
/**
|
||||
* Never autogenerate a proxy and rely that it was generated by some
|
||||
* process before deployment.
|
||||
*/
|
||||
public const AUTOGENERATE_NEVER = 0;
|
||||
|
||||
/**
|
||||
* Always generates a new proxy in every request.
|
||||
*
|
||||
* This is only sane during development.
|
||||
*/
|
||||
public const AUTOGENERATE_ALWAYS = 1;
|
||||
|
||||
/**
|
||||
* Autogenerate the proxy class when the proxy file does not exist.
|
||||
*
|
||||
* This strategy causes a file_exists() call whenever any proxy is used the
|
||||
* first time in a request.
|
||||
*/
|
||||
public const AUTOGENERATE_FILE_NOT_EXISTS = 2;
|
||||
|
||||
/**
|
||||
* Generate the proxy classes using eval().
|
||||
*
|
||||
* This strategy is only sane for development, and even then it gives me
|
||||
* the creeps a little.
|
||||
*/
|
||||
public const AUTOGENERATE_EVAL = 3;
|
||||
|
||||
/**
|
||||
* Autogenerate the proxy class when the proxy file does not exist or
|
||||
* when the proxied file changed.
|
||||
*
|
||||
* This strategy causes a file_exists() call whenever any proxy is used the
|
||||
* first time in a request. When the proxied file is changed, the proxy will
|
||||
* be updated.
|
||||
*/
|
||||
public const AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED = 4;
|
||||
|
||||
private const PROXY_CLASS_TEMPLATE = <<<'EOPHP'
|
||||
<?php
|
||||
|
||||
@@ -45,15 +109,6 @@ class <proxyShortClassName> extends \<className> implements \<baseProxyInterface
|
||||
{
|
||||
<useLazyGhostTrait>
|
||||
|
||||
public function __construct(?\Closure $initializer = null, ?\Closure $cloner = null)
|
||||
{
|
||||
if ($cloner !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::createLazyGhost($initializer, <skippedProperties>, $this);
|
||||
}
|
||||
|
||||
public function __isInitialized(): bool
|
||||
{
|
||||
return isset($this->lazyObjectState) && $this->isLazyObjectInitialized();
|
||||
@@ -73,9 +128,15 @@ EOPHP;
|
||||
/** @var UnitOfWork The UnitOfWork this factory uses to retrieve persisters */
|
||||
private $uow;
|
||||
|
||||
/** @var string */
|
||||
private $proxyDir;
|
||||
|
||||
/** @var string */
|
||||
private $proxyNs;
|
||||
|
||||
/** @var self::AUTOGENERATE_* */
|
||||
private $autoGenerate;
|
||||
|
||||
/**
|
||||
* The IdentifierFlattener used for manipulating identifiers
|
||||
*
|
||||
@@ -83,8 +144,11 @@ EOPHP;
|
||||
*/
|
||||
private $identifierFlattener;
|
||||
|
||||
/** @var ProxyDefinition[] */
|
||||
private $definitions = [];
|
||||
/** @var array<class-string, Closure> */
|
||||
private $proxyFactories = [];
|
||||
|
||||
/** @var bool */
|
||||
private $isLazyGhostObjectEnabled = true;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the <tt>ProxyFactory</tt> class that is
|
||||
@@ -97,23 +161,40 @@ EOPHP;
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $em, $proxyDir, $proxyNs, $autoGenerate = self::AUTOGENERATE_NEVER)
|
||||
{
|
||||
$proxyGenerator = new ProxyGenerator($proxyDir, $proxyNs);
|
||||
if (! $em->getConfiguration()->isLazyGhostObjectEnabled()) {
|
||||
if (PHP_VERSION_ID >= 80100) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/10837/',
|
||||
'Not enabling lazy ghost objects is deprecated and will not be supported in Doctrine ORM 3.0. Ensure Doctrine\ORM\Configuration::setLazyGhostObjectEnabled(true) is called to enable them.'
|
||||
);
|
||||
}
|
||||
|
||||
if ($em->getConfiguration()->isLazyGhostObjectEnabled()) {
|
||||
$proxyGenerator->setPlaceholder('baseProxyInterface', InternalProxy::class);
|
||||
$proxyGenerator->setPlaceholder('useLazyGhostTrait', Closure::fromCallable([$this, 'generateUseLazyGhostTrait']));
|
||||
$proxyGenerator->setPlaceholder('skippedProperties', Closure::fromCallable([$this, 'generateSkippedProperties']));
|
||||
$proxyGenerator->setPlaceholder('serializeImpl', Closure::fromCallable([$this, 'generateSerializeImpl']));
|
||||
$proxyGenerator->setProxyClassTemplate(self::PROXY_CLASS_TEMPLATE);
|
||||
} else {
|
||||
$this->isLazyGhostObjectEnabled = false;
|
||||
|
||||
$proxyGenerator = new ProxyGenerator($proxyDir, $proxyNs);
|
||||
$proxyGenerator->setPlaceholder('baseProxyInterface', LegacyProxy::class);
|
||||
|
||||
parent::__construct($proxyGenerator, $em->getMetadataFactory(), $autoGenerate);
|
||||
}
|
||||
|
||||
parent::__construct($proxyGenerator, $em->getMetadataFactory(), $autoGenerate);
|
||||
if (! $proxyDir) {
|
||||
throw ORMInvalidArgumentException::proxyDirectoryRequired();
|
||||
}
|
||||
|
||||
if (! $proxyNs) {
|
||||
throw ORMInvalidArgumentException::proxyNamespaceRequired();
|
||||
}
|
||||
|
||||
if (is_int($autoGenerate) ? $autoGenerate < 0 || $autoGenerate > 4 : ! is_bool($autoGenerate)) {
|
||||
throw ORMInvalidArgumentException::invalidAutoGenerateMode($autoGenerate);
|
||||
}
|
||||
|
||||
$this->em = $em;
|
||||
$this->uow = $em->getUnitOfWork();
|
||||
$this->proxyDir = $proxyDir;
|
||||
$this->proxyNs = $proxyNs;
|
||||
$this->autoGenerate = (int) $autoGenerate;
|
||||
$this->identifierFlattener = new IdentifierFlattener($this->uow, $em->getMetadataFactory());
|
||||
}
|
||||
|
||||
@@ -122,19 +203,57 @@ EOPHP;
|
||||
*/
|
||||
public function getProxy($className, array $identifier)
|
||||
{
|
||||
$proxy = parent::getProxy($className, $identifier);
|
||||
|
||||
if (! $this->em->getConfiguration()->isLazyGhostObjectEnabled()) {
|
||||
return $proxy;
|
||||
if (! $this->isLazyGhostObjectEnabled) {
|
||||
return parent::getProxy($className, $identifier);
|
||||
}
|
||||
|
||||
$initializer = $this->definitions[$className]->initializer;
|
||||
$proxyFactory = $this->proxyFactories[$className] ?? $this->getProxyFactory($className);
|
||||
|
||||
$proxy->__construct(static function (InternalProxy $object) use ($initializer, $proxy): void {
|
||||
$initializer($object, $proxy);
|
||||
});
|
||||
return $proxyFactory($identifier);
|
||||
}
|
||||
|
||||
return $proxy;
|
||||
/**
|
||||
* Generates proxy classes for all given classes.
|
||||
*
|
||||
* @param ClassMetadata[] $classes The classes (ClassMetadata instances) for which to generate proxies.
|
||||
* @param string|null $proxyDir The target directory of the proxy classes. If not specified, the
|
||||
* directory configured on the Configuration of the EntityManager used
|
||||
* by this factory is used.
|
||||
*
|
||||
* @return int Number of generated proxies.
|
||||
*/
|
||||
public function generateProxyClasses(array $classes, $proxyDir = null)
|
||||
{
|
||||
if (! $this->isLazyGhostObjectEnabled) {
|
||||
return parent::generateProxyClasses($classes, $proxyDir);
|
||||
}
|
||||
|
||||
$generated = 0;
|
||||
|
||||
foreach ($classes as $class) {
|
||||
if ($this->skipClass($class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$proxyFileName = $this->getProxyFileName($class->getName(), $proxyDir ?: $this->proxyDir);
|
||||
$proxyClassName = self::generateProxyClassName($class->getName(), $this->proxyNs);
|
||||
|
||||
$this->generateProxyClass($class, $proxyFileName, $proxyClassName);
|
||||
|
||||
++$generated;
|
||||
}
|
||||
|
||||
return $generated;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @deprecated ProxyFactory::resetUninitializedProxy() is deprecated and will be removed in version 3.0 of doctrine/orm.
|
||||
*/
|
||||
public function resetUninitializedProxy(CommonProxy $proxy)
|
||||
{
|
||||
return parent::resetUninitializedProxy($proxy);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,23 +268,19 @@ EOPHP;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @deprecated ProxyFactory::createProxyDefinition() is deprecated and will be removed in version 3.0 of doctrine/orm.
|
||||
*/
|
||||
protected function createProxyDefinition($className)
|
||||
{
|
||||
$classMetadata = $this->em->getClassMetadata($className);
|
||||
$entityPersister = $this->uow->getEntityPersister($className);
|
||||
|
||||
if ($this->em->getConfiguration()->isLazyGhostObjectEnabled()) {
|
||||
$initializer = $this->createLazyInitializer($classMetadata, $entityPersister);
|
||||
$cloner = static function (): void {
|
||||
};
|
||||
} else {
|
||||
$initializer = $this->createInitializer($classMetadata, $entityPersister);
|
||||
$cloner = $this->createCloner($classMetadata, $entityPersister);
|
||||
}
|
||||
$initializer = $this->createInitializer($classMetadata, $entityPersister);
|
||||
$cloner = $this->createCloner($classMetadata, $entityPersister);
|
||||
|
||||
return $this->definitions[$className] = new ProxyDefinition(
|
||||
ClassUtils::generateProxyClassName($className, $this->proxyNs),
|
||||
return new ProxyDefinition(
|
||||
self::generateProxyClassName($className, $this->proxyNs),
|
||||
$classMetadata->getIdentifierFieldNames(),
|
||||
$classMetadata->getReflectionProperties(),
|
||||
$initializer,
|
||||
@@ -176,6 +291,8 @@ EOPHP;
|
||||
/**
|
||||
* Creates a closure capable of initializing a proxy
|
||||
*
|
||||
* @deprecated ProxyFactory::createInitializer() is deprecated and will be removed in version 3.0 of doctrine/orm.
|
||||
*
|
||||
* @psalm-return Closure(CommonProxy):void
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
@@ -241,16 +358,16 @@ EOPHP;
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
*/
|
||||
private function createLazyInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister): Closure
|
||||
private function createLazyInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister, IdentifierFlattener $identifierFlattener): Closure
|
||||
{
|
||||
return function (InternalProxy $proxy, InternalProxy $original) use ($entityPersister, $classMetadata): void {
|
||||
return static function (InternalProxy $proxy, InternalProxy $original) use ($entityPersister, $classMetadata, $identifierFlattener): void {
|
||||
$identifier = $classMetadata->getIdentifierValues($original);
|
||||
$entity = $entityPersister->loadById($identifier, $original);
|
||||
|
||||
if ($entity === null) {
|
||||
throw EntityNotFoundException::fromClassNameAndIdentifier(
|
||||
$classMetadata->getName(),
|
||||
$this->identifierFlattener->flattenIdentifier($classMetadata, $identifier)
|
||||
$identifierFlattener->flattenIdentifier($classMetadata, $identifier)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -265,7 +382,6 @@ EOPHP;
|
||||
continue;
|
||||
}
|
||||
|
||||
$property->setAccessible(true);
|
||||
$property->setValue($proxy, $property->getValue($entity));
|
||||
}
|
||||
};
|
||||
@@ -274,6 +390,8 @@ EOPHP;
|
||||
/**
|
||||
* Creates a closure capable of finalizing state a cloned proxy
|
||||
*
|
||||
* @deprecated ProxyFactory::createCloner() is deprecated and will be removed in version 3.0 of doctrine/orm.
|
||||
*
|
||||
* @psalm-return Closure(CommonProxy):void
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
@@ -310,25 +428,18 @@ EOPHP;
|
||||
};
|
||||
}
|
||||
|
||||
private function generateUseLazyGhostTrait(ClassMetadata $class): string
|
||||
private function getProxyFileName(string $className, string $baseDirectory): string
|
||||
{
|
||||
$code = ProxyHelper::generateLazyGhost($class->getReflectionClass());
|
||||
$code = substr($code, 7 + (int) strpos($code, "\n{"));
|
||||
$code = substr($code, 0, (int) strpos($code, "\n}"));
|
||||
$code = str_replace('LazyGhostTrait;', str_replace("\n ", "\n", 'LazyGhostTrait {
|
||||
initializeLazyObject as __load;
|
||||
setLazyObjectAsInitialized as public __setInitialized;
|
||||
isLazyObjectInitialized as private;
|
||||
createLazyGhost as private;
|
||||
resetLazyObject as private;
|
||||
}'), $code);
|
||||
$baseDirectory = $baseDirectory ?: $this->proxyDir;
|
||||
|
||||
return $code;
|
||||
return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . InternalProxy::MARKER
|
||||
. str_replace('\\', '', $className) . '.php';
|
||||
}
|
||||
|
||||
private function generateSkippedProperties(ClassMetadata $class): string
|
||||
private function getProxyFactory(string $className): Closure
|
||||
{
|
||||
$skippedProperties = [];
|
||||
$class = $this->em->getClassMetadata($className);
|
||||
$identifiers = array_flip($class->getIdentifierFieldNames());
|
||||
$filter = ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE;
|
||||
$reflector = $class->getReflectionClass();
|
||||
@@ -350,11 +461,123 @@ EOPHP;
|
||||
$reflector = $reflector->getParentClass();
|
||||
}
|
||||
|
||||
uksort($skippedProperties, 'strnatcmp');
|
||||
$className = $class->getName(); // aliases and case sensitivity
|
||||
$entityPersister = $this->uow->getEntityPersister($className);
|
||||
$initializer = $this->createLazyInitializer($class, $entityPersister, $this->identifierFlattener);
|
||||
$proxyClassName = $this->loadProxyClass($class);
|
||||
$identifierFields = array_intersect_key($class->getReflectionProperties(), $identifiers);
|
||||
|
||||
$code = VarExporter::export($skippedProperties);
|
||||
$code = str_replace(VarExporter::export($class->getName()), 'parent::class', $code);
|
||||
$code = str_replace("\n", "\n ", $code);
|
||||
$proxyFactory = Closure::bind(static function (array $identifier) use ($initializer, $skippedProperties, $identifierFields, $className): InternalProxy {
|
||||
$proxy = self::createLazyGhost(static function (InternalProxy $object) use ($initializer, &$proxy): void {
|
||||
$initializer($object, $proxy);
|
||||
}, $skippedProperties);
|
||||
|
||||
foreach ($identifierFields as $idField => $reflector) {
|
||||
if (! isset($identifier[$idField])) {
|
||||
throw ORMInvalidArgumentException::missingPrimaryKeyValue($className, $idField);
|
||||
}
|
||||
|
||||
$reflector->setValue($proxy, $identifier[$idField]);
|
||||
}
|
||||
|
||||
return $proxy;
|
||||
}, null, $proxyClassName);
|
||||
|
||||
return $this->proxyFactories[$className] = $proxyFactory;
|
||||
}
|
||||
|
||||
private function loadProxyClass(ClassMetadata $class): string
|
||||
{
|
||||
$proxyClassName = self::generateProxyClassName($class->getName(), $this->proxyNs);
|
||||
|
||||
if (class_exists($proxyClassName, false)) {
|
||||
return $proxyClassName;
|
||||
}
|
||||
|
||||
if ($this->autoGenerate === self::AUTOGENERATE_EVAL) {
|
||||
$this->generateProxyClass($class, null, $proxyClassName);
|
||||
|
||||
return $proxyClassName;
|
||||
}
|
||||
|
||||
$fileName = $this->getProxyFileName($class->getName(), $this->proxyDir);
|
||||
|
||||
switch ($this->autoGenerate) {
|
||||
case self::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED:
|
||||
if (file_exists($fileName) && filemtime($fileName) >= filemtime($class->getReflectionClass()->getFileName())) {
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
case self::AUTOGENERATE_FILE_NOT_EXISTS:
|
||||
if (file_exists($fileName)) {
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
case self::AUTOGENERATE_ALWAYS:
|
||||
$this->generateProxyClass($class, $fileName, $proxyClassName);
|
||||
break;
|
||||
}
|
||||
|
||||
require $fileName;
|
||||
|
||||
return $proxyClassName;
|
||||
}
|
||||
|
||||
private function generateProxyClass(ClassMetadata $class, ?string $fileName, string $proxyClassName): void
|
||||
{
|
||||
$i = strrpos($proxyClassName, '\\');
|
||||
$placeholders = [
|
||||
'<className>' => $class->getName(),
|
||||
'<namespace>' => substr($proxyClassName, 0, $i),
|
||||
'<proxyShortClassName>' => substr($proxyClassName, 1 + $i),
|
||||
'<baseProxyInterface>' => InternalProxy::class,
|
||||
];
|
||||
|
||||
preg_match_all('(<([a-zA-Z]+)>)', self::PROXY_CLASS_TEMPLATE, $placeholderMatches);
|
||||
|
||||
foreach (array_combine($placeholderMatches[0], $placeholderMatches[1]) as $placeholder => $name) {
|
||||
$placeholders[$placeholder] ?? $placeholders[$placeholder] = $this->{'generate' . ucfirst($name)}($class);
|
||||
}
|
||||
|
||||
$proxyCode = strtr(self::PROXY_CLASS_TEMPLATE, $placeholders);
|
||||
|
||||
if (! $fileName) {
|
||||
if (! class_exists($proxyClassName)) {
|
||||
eval(substr($proxyCode, 5));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$parentDirectory = dirname($fileName);
|
||||
|
||||
if (! is_dir($parentDirectory) && ! @mkdir($parentDirectory, 0775, true)) {
|
||||
throw ORMInvalidArgumentException::proxyDirectoryNotWritable($this->proxyDir);
|
||||
}
|
||||
|
||||
if (! is_writable($parentDirectory)) {
|
||||
throw ORMInvalidArgumentException::proxyDirectoryNotWritable($this->proxyDir);
|
||||
}
|
||||
|
||||
$tmpFileName = $fileName . '.' . bin2hex(random_bytes(12));
|
||||
|
||||
file_put_contents($tmpFileName, $proxyCode);
|
||||
@chmod($tmpFileName, 0664);
|
||||
rename($tmpFileName, $fileName);
|
||||
}
|
||||
|
||||
private function generateUseLazyGhostTrait(ClassMetadata $class): string
|
||||
{
|
||||
$code = ProxyHelper::generateLazyGhost($class->getReflectionClass());
|
||||
$code = substr($code, 7 + (int) strpos($code, "\n{"));
|
||||
$code = substr($code, 0, (int) strpos($code, "\n}"));
|
||||
$code = str_replace('LazyGhostTrait;', str_replace("\n ", "\n", 'LazyGhostTrait {
|
||||
initializeLazyObject as __load;
|
||||
setLazyObjectAsInitialized as public __setInitialized;
|
||||
isLazyObjectInitialized as private;
|
||||
createLazyGhost as private;
|
||||
resetLazyObject as private;
|
||||
}'), $code);
|
||||
|
||||
return $code;
|
||||
}
|
||||
@@ -365,7 +588,7 @@ EOPHP;
|
||||
$properties = $reflector->hasMethod('__serialize') ? 'parent::__serialize()' : '(array) $this';
|
||||
|
||||
$code = '$properties = ' . $properties . ';
|
||||
unset($properties["\0" . self::class . "\0lazyObjectState"], $properties[\'__isCloning\']);
|
||||
unset($properties["\0" . self::class . "\0lazyObjectState"]);
|
||||
|
||||
';
|
||||
|
||||
@@ -387,4 +610,9 @@ EOPHP;
|
||||
|
||||
return $data;';
|
||||
}
|
||||
|
||||
private static function generateProxyClassName(string $className, string $proxyNamespace): string
|
||||
{
|
||||
return rtrim($proxyNamespace, '\\') . '\\' . Proxy::MARKER . '\\' . ltrim($className, '\\');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,10 @@ use function stripos;
|
||||
|
||||
/**
|
||||
* A Query object represents a DQL query.
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
final class Query extends AbstractQuery
|
||||
class Query extends AbstractQuery
|
||||
{
|
||||
/**
|
||||
* A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Doctrine\ORM\Query\AST;
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*/
|
||||
class ConditionalFactor extends Node
|
||||
class ConditionalFactor extends Node implements Phase2OptimizableConditional
|
||||
{
|
||||
/** @var bool */
|
||||
public $not = false;
|
||||
|
||||
@@ -9,12 +9,12 @@ namespace Doctrine\ORM\Query\AST;
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*/
|
||||
class ConditionalPrimary extends Node
|
||||
class ConditionalPrimary extends Node implements Phase2OptimizableConditional
|
||||
{
|
||||
/** @var Node|null */
|
||||
public $simpleConditionalExpression;
|
||||
|
||||
/** @var ConditionalExpression|null */
|
||||
/** @var ConditionalExpression|Phase2OptimizableConditional|null */
|
||||
public $conditionalExpression;
|
||||
|
||||
/** @return bool */
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Doctrine\ORM\Query\AST;
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*/
|
||||
class ConditionalTerm extends Node
|
||||
class ConditionalTerm extends Node implements Phase2OptimizableConditional
|
||||
{
|
||||
/** @var mixed[] */
|
||||
public $conditionalFactors = [];
|
||||
|
||||
@@ -6,10 +6,10 @@ namespace Doctrine\ORM\Query\AST;
|
||||
|
||||
class HavingClause extends Node
|
||||
{
|
||||
/** @var ConditionalExpression */
|
||||
/** @var ConditionalExpression|Phase2OptimizableConditional */
|
||||
public $conditionalExpression;
|
||||
|
||||
/** @param ConditionalExpression $conditionalExpression */
|
||||
/** @param ConditionalExpression|Phase2OptimizableConditional $conditionalExpression */
|
||||
public function __construct($conditionalExpression)
|
||||
{
|
||||
$this->conditionalExpression = $conditionalExpression;
|
||||
|
||||
@@ -25,7 +25,7 @@ class Join extends Node
|
||||
/** @var Node|null */
|
||||
public $joinAssociationDeclaration = null;
|
||||
|
||||
/** @var ConditionalExpression|null */
|
||||
/** @var ConditionalExpression|Phase2OptimizableConditional|null */
|
||||
public $conditionalExpression = null;
|
||||
|
||||
/**
|
||||
|
||||
17
lib/Doctrine/ORM/Query/AST/Phase2OptimizableConditional.php
Normal file
17
lib/Doctrine/ORM/Query/AST/Phase2OptimizableConditional.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Query\AST;
|
||||
|
||||
/**
|
||||
* Marks types that can be used in place of a ConditionalExpression as a phase
|
||||
* 2 optimization.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @psalm-inheritors ConditionalPrimary|ConditionalFactor|ConditionalTerm
|
||||
*/
|
||||
interface Phase2OptimizableConditional
|
||||
{
|
||||
}
|
||||
@@ -11,15 +11,15 @@ namespace Doctrine\ORM\Query\AST;
|
||||
*/
|
||||
class WhenClause extends Node
|
||||
{
|
||||
/** @var ConditionalExpression */
|
||||
/** @var ConditionalExpression|Phase2OptimizableConditional */
|
||||
public $caseConditionExpression;
|
||||
|
||||
/** @var mixed */
|
||||
public $thenScalarExpression = null;
|
||||
|
||||
/**
|
||||
* @param ConditionalExpression $caseConditionExpression
|
||||
* @param mixed $thenScalarExpression
|
||||
* @param ConditionalExpression|Phase2OptimizableConditional $caseConditionExpression
|
||||
* @param mixed $thenScalarExpression
|
||||
*/
|
||||
public function __construct($caseConditionExpression, $thenScalarExpression)
|
||||
{
|
||||
|
||||
@@ -11,10 +11,10 @@ namespace Doctrine\ORM\Query\AST;
|
||||
*/
|
||||
class WhereClause extends Node
|
||||
{
|
||||
/** @var ConditionalExpression|ConditionalTerm */
|
||||
/** @var ConditionalExpression|Phase2OptimizableConditional */
|
||||
public $conditionalExpression;
|
||||
|
||||
/** @param ConditionalExpression $conditionalExpression */
|
||||
/** @param ConditionalExpression|Phase2OptimizableConditional $conditionalExpression */
|
||||
public function __construct($conditionalExpression)
|
||||
{
|
||||
$this->conditionalExpression = $conditionalExpression;
|
||||
|
||||
@@ -9,6 +9,12 @@ use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Result;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
|
||||
use function array_diff;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
use function str_replace;
|
||||
|
||||
/**
|
||||
* Base class for SQL statement executors.
|
||||
*
|
||||
@@ -18,12 +24,24 @@ use Doctrine\DBAL\Types\Type;
|
||||
*/
|
||||
abstract class AbstractSqlExecutor
|
||||
{
|
||||
/** @var list<string>|string */
|
||||
/**
|
||||
* @deprecated use $sqlStatements instead
|
||||
*
|
||||
* @var list<string>|string
|
||||
*/
|
||||
protected $_sqlStatements;
|
||||
|
||||
/** @var list<string>|string */
|
||||
protected $sqlStatements;
|
||||
|
||||
/** @var QueryCacheProfile */
|
||||
protected $queryCacheProfile;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->_sqlStatements = &$this->sqlStatements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL statements that are executed by the executor.
|
||||
*
|
||||
@@ -31,21 +49,18 @@ abstract class AbstractSqlExecutor
|
||||
*/
|
||||
public function getSqlStatements()
|
||||
{
|
||||
return $this->_sqlStatements;
|
||||
return $this->sqlStatements;
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function setQueryCacheProfile(QueryCacheProfile $qcp)
|
||||
public function setQueryCacheProfile(QueryCacheProfile $qcp): void
|
||||
{
|
||||
$this->queryCacheProfile = $qcp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use query cache
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeQueryCacheProfile()
|
||||
public function removeQueryCacheProfile(): void
|
||||
{
|
||||
$this->queryCacheProfile = null;
|
||||
}
|
||||
@@ -60,4 +75,28 @@ abstract class AbstractSqlExecutor
|
||||
* @return Result|int
|
||||
*/
|
||||
abstract public function execute(Connection $conn, array $params, array $types);
|
||||
|
||||
/** @return list<string> */
|
||||
public function __sleep(): array
|
||||
{
|
||||
/* Two reasons for this:
|
||||
- we do not need to serialize the deprecated property, we can
|
||||
rebuild the reference to the new property in __wakeup()
|
||||
- not having the legacy property in the serialized data means the
|
||||
serialized representation becomes compatible with 3.0.x, meaning
|
||||
there will not be a deprecation warning about a missing property
|
||||
when unserializing data */
|
||||
return array_values(array_diff(array_map(static function (string $prop): string {
|
||||
return str_replace("\0*\0", '', $prop);
|
||||
}, array_keys((array) $this)), ['_sqlStatements']));
|
||||
}
|
||||
|
||||
public function __wakeup(): void
|
||||
{
|
||||
if ($this->_sqlStatements !== null && $this->sqlStatements === null) {
|
||||
$this->sqlStatements = $this->_sqlStatements;
|
||||
}
|
||||
|
||||
$this->_sqlStatements = &$this->sqlStatements;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
|
||||
*/
|
||||
public function __construct(AST\Node $AST, $sqlWalker)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$em = $sqlWalker->getEntityManager();
|
||||
$conn = $em->getConnection();
|
||||
$platform = $conn->getDatabasePlatform();
|
||||
@@ -83,8 +85,8 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
|
||||
// 3. Create and store DELETE statements
|
||||
$classNames = array_merge($primaryClass->parentClasses, [$primaryClass->name], $primaryClass->subClasses);
|
||||
foreach (array_reverse($classNames) as $className) {
|
||||
$tableName = $quoteStrategy->getTableName($em->getClassMetadata($className), $platform);
|
||||
$this->_sqlStatements[] = 'DELETE FROM ' . $tableName
|
||||
$tableName = $quoteStrategy->getTableName($em->getClassMetadata($className), $platform);
|
||||
$this->sqlStatements[] = 'DELETE FROM ' . $tableName
|
||||
. ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')';
|
||||
}
|
||||
|
||||
@@ -117,7 +119,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
|
||||
$numDeleted = $conn->executeStatement($this->insertSql, $params, $types);
|
||||
|
||||
// Execute DELETE statements
|
||||
foreach ($this->_sqlStatements as $sql) {
|
||||
foreach ($this->sqlStatements as $sql) {
|
||||
$conn->executeStatement($sql);
|
||||
}
|
||||
} catch (Throwable $exception) {
|
||||
|
||||
@@ -50,6 +50,8 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
*/
|
||||
public function __construct(AST\Node $AST, $sqlWalker)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$em = $sqlWalker->getEntityManager();
|
||||
$conn = $em->getConnection();
|
||||
$platform = $conn->getDatabasePlatform();
|
||||
@@ -119,7 +121,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
}
|
||||
|
||||
if ($affected) {
|
||||
$this->_sqlStatements[$i] = $updateSql . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')';
|
||||
$this->sqlStatements[$i] = $updateSql . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +165,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
);
|
||||
|
||||
// Execute UPDATE statements
|
||||
foreach ($this->_sqlStatements as $key => $statement) {
|
||||
foreach ($this->sqlStatements as $key => $statement) {
|
||||
$paramValues = [];
|
||||
$paramTypes = [];
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@ class SingleSelectExecutor extends AbstractSqlExecutor
|
||||
{
|
||||
public function __construct(SelectStatement $AST, SqlWalker $sqlWalker)
|
||||
{
|
||||
$this->_sqlStatements = $sqlWalker->walkSelectStatement($AST);
|
||||
parent::__construct();
|
||||
|
||||
$this->sqlStatements = $sqlWalker->walkSelectStatement($AST);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,6 +30,6 @@ class SingleSelectExecutor extends AbstractSqlExecutor
|
||||
*/
|
||||
public function execute(Connection $conn, array $params, array $types)
|
||||
{
|
||||
return $conn->executeQuery($this->_sqlStatements, $params, $types, $this->queryCacheProfile);
|
||||
return $conn->executeQuery($this->sqlStatements, $params, $types, $this->queryCacheProfile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,12 @@ class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor
|
||||
/** @param SqlWalker $sqlWalker */
|
||||
public function __construct(AST\Node $AST, $sqlWalker)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if ($AST instanceof AST\UpdateStatement) {
|
||||
$this->_sqlStatements = $sqlWalker->walkUpdateStatement($AST);
|
||||
$this->sqlStatements = $sqlWalker->walkUpdateStatement($AST);
|
||||
} elseif ($AST instanceof AST\DeleteStatement) {
|
||||
$this->_sqlStatements = $sqlWalker->walkDeleteStatement($AST);
|
||||
$this->sqlStatements = $sqlWalker->walkDeleteStatement($AST);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +42,6 @@ class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor
|
||||
$conn->ensureConnectedToPrimary();
|
||||
}
|
||||
|
||||
return $conn->executeStatement($this->_sqlStatements, $params, $types);
|
||||
return $conn->executeStatement($this->sqlStatements, $params, $types);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,9 @@ class ParserResult
|
||||
{
|
||||
foreach (self::LEGACY_PROPERTY_MAPPING as $property => $legacyProperty) {
|
||||
$this->$property = $data[sprintf("\0%s\0%s", self::class, $legacyProperty)]
|
||||
?? $data[self::class][$legacyProperty]
|
||||
?? $data[sprintf("\0%s\0%s", self::class, $property)]
|
||||
?? $data[self::class][$property]
|
||||
?? $this->$property
|
||||
?? null;
|
||||
}
|
||||
|
||||
@@ -204,6 +204,14 @@ class QueryException extends ORMException
|
||||
);
|
||||
}
|
||||
|
||||
public static function eagerFetchJoinWithNotAllowed(string $sourceEntity, string $fieldName): QueryException
|
||||
{
|
||||
return new self(
|
||||
'Associations with fetch-mode=EAGER may not be using WITH conditions in
|
||||
"' . $sourceEntity . '#' . $fieldName . '".'
|
||||
);
|
||||
}
|
||||
|
||||
public static function iterateWithMixedResultNotAllowed(): QueryException
|
||||
{
|
||||
return new self('Iterating a query with mixed results (using scalars) is not supported.');
|
||||
|
||||
@@ -154,6 +154,11 @@ class ResultSetMappingBuilder extends ResultSetMapping
|
||||
}
|
||||
|
||||
$this->addFieldResult($alias, $columnAlias, $propertyName);
|
||||
|
||||
$enumType = $classMetadata->getFieldMapping($propertyName)['enumType'] ?? null;
|
||||
if (! empty($enumType)) {
|
||||
$this->addEnumResult($columnAlias, $enumType);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($classMetadata->associationMappings as $associationMapping) {
|
||||
|
||||
@@ -1010,9 +1010,9 @@ class SqlWalker implements TreeWalker
|
||||
/**
|
||||
* Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param AST\JoinAssociationDeclaration $joinAssociationDeclaration
|
||||
* @param int $joinType
|
||||
* @param AST\ConditionalExpression $condExpr
|
||||
* @param AST\JoinAssociationDeclaration $joinAssociationDeclaration
|
||||
* @param int $joinType
|
||||
* @param AST\ConditionalExpression|AST\Phase2OptimizableConditional $condExpr
|
||||
* @psalm-param AST\Join::JOIN_TYPE_* $joinType
|
||||
*
|
||||
* @return string
|
||||
@@ -1047,7 +1047,9 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
}
|
||||
|
||||
$targetTableJoin = null;
|
||||
if ($relation['fetch'] === ClassMetadata::FETCH_EAGER && $condExpr !== null) {
|
||||
throw QueryException::eagerFetchJoinWithNotAllowed($assoc['sourceEntity'], $assoc['fieldName']);
|
||||
}
|
||||
|
||||
// This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot
|
||||
// be the owning side and previously we ensured that $assoc is always the owning side of the associations.
|
||||
@@ -2048,7 +2050,7 @@ class SqlWalker implements TreeWalker
|
||||
/**
|
||||
* Walk down a ConditionalExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param AST\ConditionalExpression $condExpr
|
||||
* @param AST\ConditionalExpression|AST\Phase2OptimizableConditional $condExpr
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
@@ -2068,7 +2070,7 @@ class SqlWalker implements TreeWalker
|
||||
/**
|
||||
* Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param AST\ConditionalTerm $condTerm
|
||||
* @param AST\ConditionalTerm|AST\ConditionalFactor|AST\ConditionalPrimary $condTerm
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
@@ -2088,7 +2090,7 @@ class SqlWalker implements TreeWalker
|
||||
/**
|
||||
* Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param AST\ConditionalFactor $factor
|
||||
* @param AST\ConditionalFactor|AST\ConditionalPrimary $factor
|
||||
*
|
||||
* @return string The SQL.
|
||||
*
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
|
||||
|
||||
use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand;
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
@@ -20,6 +21,8 @@ use function sprintf;
|
||||
*/
|
||||
class CollectionRegionCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -58,12 +61,7 @@ EOT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
|
||||
|
||||
use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand;
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
@@ -20,6 +21,8 @@ use function sprintf;
|
||||
*/
|
||||
class EntityRegionCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -57,12 +60,7 @@ EOT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand;
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
@@ -18,6 +19,8 @@ use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
*/
|
||||
class MetadataCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -31,12 +34,7 @@ EOT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ use Doctrine\Common\Cache\ClearableCache;
|
||||
use Doctrine\Common\Cache\FlushableCache;
|
||||
use Doctrine\Common\Cache\XcacheCache;
|
||||
use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand;
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use Symfony\Component\Cache\Adapter\ApcuAdapter;
|
||||
@@ -28,6 +29,8 @@ use function sprintf;
|
||||
*/
|
||||
class QueryCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -55,12 +58,7 @@ EOT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
|
||||
|
||||
use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand;
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
@@ -20,6 +21,8 @@ use function sprintf;
|
||||
*/
|
||||
class QueryRegionCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -56,12 +59,7 @@ EOT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ use Doctrine\Common\Cache\FlushableCache;
|
||||
use Doctrine\Common\Cache\XcacheCache;
|
||||
use Doctrine\ORM\Configuration;
|
||||
use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand;
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use Symfony\Component\Cache\Adapter\ApcuAdapter;
|
||||
@@ -29,6 +30,8 @@ use function sprintf;
|
||||
*/
|
||||
class ResultCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -56,12 +59,7 @@ EOT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console\Command;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use Doctrine\ORM\Tools\ConvertDoctrine1Schema;
|
||||
use Doctrine\ORM\Tools\EntityGenerator;
|
||||
use Doctrine\ORM\Tools\Export\ClassMetadataExporter;
|
||||
@@ -34,6 +35,8 @@ use const PHP_EOL;
|
||||
*/
|
||||
class ConvertDoctrine1SchemaCommand extends Command
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @var EntityGenerator|null */
|
||||
private $entityGenerator = null;
|
||||
|
||||
@@ -87,12 +90,7 @@ class ConvertDoctrine1SchemaCommand extends Command
|
||||
->setHelp('Converts Doctrine 1.x schema into a Doctrine 2.x schema.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = new SymfonyStyle($input, $output);
|
||||
$ui->getErrorStyle()->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.');
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Tools\Console\Command;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\ORM\Mapping\Driver\DatabaseDriver;
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use Doctrine\ORM\Tools\Console\MetadataFilter;
|
||||
use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
|
||||
use Doctrine\ORM\Tools\EntityGenerator;
|
||||
@@ -37,6 +38,8 @@ use function strtolower;
|
||||
*/
|
||||
class ConvertMappingCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -84,12 +87,7 @@ EOT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = new SymfonyStyle($input, $output);
|
||||
$ui->getErrorStyle()->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.');
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console\Command;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
@@ -19,6 +20,8 @@ use Throwable;
|
||||
*/
|
||||
class EnsureProductionSettingsCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -29,12 +32,7 @@ class EnsureProductionSettingsCommand extends AbstractEntityManagerCommand
|
||||
->setHelp('Verify that Doctrine is properly configured for a production environment.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
$ui->warning('This console command has been deprecated and will be removed in a future version of Doctrine ORM.');
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console\Command;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use Doctrine\ORM\Tools\Console\MetadataFilter;
|
||||
use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
|
||||
use Doctrine\ORM\Tools\EntityGenerator;
|
||||
@@ -28,6 +29,8 @@ use function sprintf;
|
||||
*/
|
||||
class GenerateEntitiesCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -67,12 +70,7 @@ EOT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
$ui->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.');
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console\Command;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use Doctrine\ORM\Tools\Console\MetadataFilter;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
@@ -26,6 +27,8 @@ use function sprintf;
|
||||
*/
|
||||
class GenerateProxiesCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -38,12 +41,7 @@ class GenerateProxiesCommand extends AbstractEntityManagerCommand
|
||||
->setHelp('Generates proxy classes for entity classes.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console\Command;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use Doctrine\ORM\Tools\Console\MetadataFilter;
|
||||
use Doctrine\ORM\Tools\EntityRepositoryGenerator;
|
||||
use InvalidArgumentException;
|
||||
@@ -27,6 +28,8 @@ use function sprintf;
|
||||
*/
|
||||
class GenerateRepositoriesCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -39,12 +42,7 @@ class GenerateRepositoriesCommand extends AbstractEntityManagerCommand
|
||||
->setHelp('Generate repository classes from your mapping information.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
$ui->warning('Command ' . $this->getName() . ' is deprecated and will be removed in Doctrine ORM 3.0.');
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Tools\Console\Command;
|
||||
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
@@ -20,6 +21,8 @@ use function sprintf;
|
||||
*/
|
||||
class InfoCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -34,12 +37,7 @@ EOT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console\Command;
|
||||
|
||||
use Doctrine\Common\Util\Debug;
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use Doctrine\ORM\Tools\Debug;
|
||||
use LogicException;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
@@ -27,6 +28,8 @@ use function strtoupper;
|
||||
*/
|
||||
class RunDqlCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -58,12 +61,7 @@ EOT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = new SymfonyStyle($input, $output);
|
||||
|
||||
@@ -118,7 +116,7 @@ EOT
|
||||
|
||||
$resultSet = $query->execute([], constant($hydrationMode));
|
||||
|
||||
$ui->text(Debug::dump($resultSet, (int) $input->getOption('depth'), true, false));
|
||||
$ui->text(Debug::dump($resultSet, (int) $input->getOption('depth')));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Tools\Console\Command\SchemaTool;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\Command\AbstractEntityManagerCommand;
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
@@ -17,6 +18,8 @@ use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
*/
|
||||
abstract class AbstractCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/**
|
||||
* @param mixed[] $metadatas
|
||||
*
|
||||
@@ -24,12 +27,7 @@ abstract class AbstractCommand extends AbstractEntityManagerCommand
|
||||
*/
|
||||
abstract protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = new SymfonyStyle($input, $output);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console\Command;
|
||||
|
||||
use Doctrine\ORM\Tools\Console\CommandCompatibility;
|
||||
use Doctrine\ORM\Tools\SchemaValidator;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
@@ -20,6 +21,8 @@ use function sprintf;
|
||||
*/
|
||||
class ValidateSchemaCommand extends AbstractEntityManagerCommand
|
||||
{
|
||||
use CommandCompatibility;
|
||||
|
||||
/** @return void */
|
||||
protected function configure()
|
||||
{
|
||||
@@ -28,20 +31,16 @@ class ValidateSchemaCommand extends AbstractEntityManagerCommand
|
||||
->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')
|
||||
->addOption('skip-mapping', null, InputOption::VALUE_NONE, 'Skip the mapping validation check')
|
||||
->addOption('skip-sync', null, InputOption::VALUE_NONE, 'Skip checking if the mapping is in sync with the database')
|
||||
->addOption('skip-property-types', null, InputOption::VALUE_NONE, 'Skip checking if property types match the Doctrine types')
|
||||
->setHelp('Validate that the mapping files are correct and in sync with the database.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
private function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
|
||||
|
||||
$em = $this->getEntityManager($input);
|
||||
$validator = new SchemaValidator($em);
|
||||
$validator = new SchemaValidator($em, ! $input->getOption('skip-property-types'));
|
||||
$exit = 0;
|
||||
|
||||
$ui->section('Mapping');
|
||||
|
||||
35
lib/Doctrine/ORM/Tools/Console/CommandCompatibility.php
Normal file
35
lib/Doctrine/ORM/Tools/Console/CommandCompatibility.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools\Console;
|
||||
|
||||
use ReflectionMethod;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
if ((new ReflectionMethod(Command::class, 'execute'))->hasReturnType()) {
|
||||
/** @internal */
|
||||
trait CommandCompatibility
|
||||
{
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
return $this->doExecute($input, $output);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/** @internal */
|
||||
trait CommandCompatibility
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
return $this->doExecute($input, $output);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,34 @@ namespace Doctrine\ORM\Tools\Console\Helper;
|
||||
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use ReflectionMethod;
|
||||
use Symfony\Component\Console\Helper\Helper;
|
||||
use Symfony\Component\Console\Helper\HelperInterface;
|
||||
|
||||
if ((new ReflectionMethod(HelperInterface::class, 'getName'))->hasReturnType()) {
|
||||
/** @internal */
|
||||
trait EntityManagerHelperCompatibility
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'entityManager';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/** @internal */
|
||||
trait EntityManagerHelperCompatibility
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'entityManager';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Doctrine CLI Connection Helper.
|
||||
@@ -15,6 +42,8 @@ use Symfony\Component\Console\Helper\Helper;
|
||||
*/
|
||||
class EntityManagerHelper extends Helper
|
||||
{
|
||||
use EntityManagerHelperCompatibility;
|
||||
|
||||
/**
|
||||
* Doctrine ORM EntityManagerInterface.
|
||||
*
|
||||
@@ -43,14 +72,4 @@ class EntityManagerHelper extends Helper
|
||||
{
|
||||
return $this->_em;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'entityManager';
|
||||
}
|
||||
}
|
||||
|
||||
168
lib/Doctrine/ORM/Tools/Debug.php
Normal file
168
lib/Doctrine/ORM/Tools/Debug.php
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools;
|
||||
|
||||
use ArrayIterator;
|
||||
use ArrayObject;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\Persistence\Proxy;
|
||||
use stdClass;
|
||||
|
||||
use function array_keys;
|
||||
use function count;
|
||||
use function end;
|
||||
use function explode;
|
||||
use function extension_loaded;
|
||||
use function get_class;
|
||||
use function html_entity_decode;
|
||||
use function ini_get;
|
||||
use function ini_set;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function ob_end_clean;
|
||||
use function ob_get_contents;
|
||||
use function ob_start;
|
||||
use function strip_tags;
|
||||
use function var_dump;
|
||||
|
||||
/**
|
||||
* Static class containing most used debug methods.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*/
|
||||
final class Debug
|
||||
{
|
||||
/**
|
||||
* Private constructor (prevents instantiation).
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a dump of the public, protected and private properties of $var.
|
||||
*
|
||||
* @link https://xdebug.org/
|
||||
*
|
||||
* @param mixed $var The variable to dump.
|
||||
* @param int $maxDepth The maximum nesting level for object properties.
|
||||
*/
|
||||
public static function dump($var, int $maxDepth = 2): string
|
||||
{
|
||||
$html = ini_get('html_errors');
|
||||
|
||||
if ($html !== '1') {
|
||||
ini_set('html_errors', 'on');
|
||||
}
|
||||
|
||||
if (extension_loaded('xdebug')) {
|
||||
$previousDepth = ini_get('xdebug.var_display_max_depth');
|
||||
ini_set('xdebug.var_display_max_depth', (string) $maxDepth);
|
||||
}
|
||||
|
||||
try {
|
||||
$var = self::export($var, $maxDepth);
|
||||
|
||||
ob_start();
|
||||
var_dump($var);
|
||||
|
||||
$dump = ob_get_contents();
|
||||
|
||||
ob_end_clean();
|
||||
|
||||
$dumpText = strip_tags(html_entity_decode($dump));
|
||||
} finally {
|
||||
ini_set('html_errors', $html);
|
||||
|
||||
if (isset($previousDepth)) {
|
||||
ini_set('xdebug.var_display_max_depth', $previousDepth);
|
||||
}
|
||||
}
|
||||
|
||||
return $dumpText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $var
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function export($var, int $maxDepth)
|
||||
{
|
||||
if ($var instanceof Collection) {
|
||||
$var = $var->toArray();
|
||||
}
|
||||
|
||||
if (! $maxDepth) {
|
||||
return is_object($var) ? get_class($var)
|
||||
: (is_array($var) ? 'Array(' . count($var) . ')' : $var);
|
||||
}
|
||||
|
||||
if (is_array($var)) {
|
||||
$return = [];
|
||||
|
||||
foreach ($var as $k => $v) {
|
||||
$return[$k] = self::export($v, $maxDepth - 1);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
if (! is_object($var)) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
$return = new stdClass();
|
||||
if ($var instanceof DateTimeInterface) {
|
||||
$return->__CLASS__ = get_class($var);
|
||||
$return->date = $var->format('c');
|
||||
$return->timezone = $var->getTimezone()->getName();
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
$return->__CLASS__ = DefaultProxyClassNameResolver::getClass($var);
|
||||
|
||||
if ($var instanceof Proxy) {
|
||||
$return->__IS_PROXY__ = true;
|
||||
$return->__PROXY_INITIALIZED__ = $var->__isInitialized();
|
||||
}
|
||||
|
||||
if ($var instanceof ArrayObject || $var instanceof ArrayIterator) {
|
||||
$return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1);
|
||||
}
|
||||
|
||||
return self::fillReturnWithClassAttributes($var, $return, $maxDepth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the $return variable with class attributes
|
||||
* Based on obj2array function from {@see https://secure.php.net/manual/en/function.get-object-vars.php#47075}
|
||||
*
|
||||
* @param object $var
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private static function fillReturnWithClassAttributes($var, stdClass $return, int $maxDepth)
|
||||
{
|
||||
$clone = (array) $var;
|
||||
|
||||
foreach (array_keys($clone) as $key) {
|
||||
$aux = explode("\0", (string) $key);
|
||||
$name = end($aux);
|
||||
if ($aux[0] === '') {
|
||||
$name .= ':' . ($aux[1] === '*' ? 'protected' : $aux[1] . ':private');
|
||||
}
|
||||
|
||||
$return->$name = self::export($clone[$key], $maxDepth - 1);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ use Doctrine\ORM\Mapping\QuoteStrategy;
|
||||
use Doctrine\ORM\OptimisticLockException;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\AST\OrderByClause;
|
||||
use Doctrine\ORM\Query\AST\PartialObjectExpression;
|
||||
use Doctrine\ORM\Query\AST\PathExpression;
|
||||
use Doctrine\ORM\Query\AST\SelectExpression;
|
||||
use Doctrine\ORM\Query\AST\SelectStatement;
|
||||
@@ -335,7 +334,7 @@ class LimitSubqueryOutputWalker extends SqlWalker
|
||||
|
||||
// Add select items which were not excluded to the AST's select clause.
|
||||
foreach ($selects as $idVar => $fields) {
|
||||
$AST->selectClause->selectExpressions[] = new SelectExpression(new PartialObjectExpression($idVar, array_keys($fields)), null, true);
|
||||
$AST->selectClause->selectExpressions[] = new SelectExpression($idVar, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,8 +373,7 @@ class LimitSubqueryOutputWalker extends SqlWalker
|
||||
string $innerSql
|
||||
): string {
|
||||
[$searchPatterns, $replacements] = $this->generateSqlAliasReplacements();
|
||||
|
||||
$orderByItems = [];
|
||||
$orderByItems = [];
|
||||
|
||||
foreach ($orderByClause->orderByItems as $orderByItem) {
|
||||
// Walk order by item to get string representation of it and
|
||||
|
||||
@@ -50,12 +50,14 @@ class LimitSubqueryWalker extends TreeWalkerAdapter
|
||||
throw new RuntimeException('Paginating an entity with foreign key as identifier only works when using the Output Walkers. Call Paginator#setUseOutputWalkers(true) before iterating the paginator.');
|
||||
}
|
||||
|
||||
$this->_getQuery()->setHint(
|
||||
$query = $this->_getQuery();
|
||||
|
||||
$query->setHint(
|
||||
self::IDENTIFIER_TYPE,
|
||||
Type::getType($rootClass->fieldMappings[$identifier]['type'])
|
||||
);
|
||||
|
||||
$this->_getQuery()->setHint(self::FORCE_DBAL_TYPE_CONVERSION, true);
|
||||
$query->setHint(self::FORCE_DBAL_TYPE_CONVERSION, true);
|
||||
|
||||
$pathExpression = new PathExpression(
|
||||
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
|
||||
@@ -66,7 +68,7 @@ class LimitSubqueryWalker extends TreeWalkerAdapter
|
||||
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
|
||||
|
||||
$AST->selectClause->selectExpressions = [new SelectExpression($pathExpression, '_dctrn_id')];
|
||||
$AST->selectClause->isDistinct = true;
|
||||
$AST->selectClause->isDistinct = ($query->getHints()[Paginator::HINT_ENABLE_DISTINCT] ?? true) === true;
|
||||
|
||||
if (! isset($AST->orderByClause)) {
|
||||
return;
|
||||
|
||||
@@ -34,6 +34,8 @@ class Paginator implements Countable, IteratorAggregate
|
||||
{
|
||||
use SQLResultCasing;
|
||||
|
||||
public const HINT_ENABLE_DISTINCT = 'paginator.distinct.enable';
|
||||
|
||||
/** @var Query */
|
||||
private $query;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ namespace Doctrine\ORM\Tools\Pagination;
|
||||
|
||||
use Doctrine\ORM\Query\AST\ArithmeticExpression;
|
||||
use Doctrine\ORM\Query\AST\ConditionalExpression;
|
||||
use Doctrine\ORM\Query\AST\ConditionalFactor;
|
||||
use Doctrine\ORM\Query\AST\ConditionalPrimary;
|
||||
use Doctrine\ORM\Query\AST\ConditionalTerm;
|
||||
use Doctrine\ORM\Query\AST\InListExpression;
|
||||
@@ -96,10 +95,7 @@ class WhereInWalker extends TreeWalkerAdapter
|
||||
),
|
||||
]
|
||||
);
|
||||
} elseif (
|
||||
$AST->whereClause->conditionalExpression instanceof ConditionalExpression
|
||||
|| $AST->whereClause->conditionalExpression instanceof ConditionalFactor
|
||||
) {
|
||||
} else {
|
||||
$tmpPrimary = new ConditionalPrimary();
|
||||
$tmpPrimary->conditionalExpression = $AST->whereClause->conditionalExpression;
|
||||
$AST->whereClause->conditionalExpression = new ConditionalTerm(
|
||||
|
||||
@@ -4,36 +4,84 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools;
|
||||
|
||||
use BackedEnum;
|
||||
use Doctrine\DBAL\Types\AsciiStringType;
|
||||
use Doctrine\DBAL\Types\BigIntType;
|
||||
use Doctrine\DBAL\Types\BooleanType;
|
||||
use Doctrine\DBAL\Types\DecimalType;
|
||||
use Doctrine\DBAL\Types\FloatType;
|
||||
use Doctrine\DBAL\Types\GuidType;
|
||||
use Doctrine\DBAL\Types\IntegerType;
|
||||
use Doctrine\DBAL\Types\JsonType;
|
||||
use Doctrine\DBAL\Types\SimpleArrayType;
|
||||
use Doctrine\DBAL\Types\SmallIntType;
|
||||
use Doctrine\DBAL\Types\StringType;
|
||||
use Doctrine\DBAL\Types\TextType;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
use ReflectionEnum;
|
||||
use ReflectionNamedType;
|
||||
|
||||
use function array_diff;
|
||||
use function array_filter;
|
||||
use function array_key_exists;
|
||||
use function array_map;
|
||||
use function array_push;
|
||||
use function array_search;
|
||||
use function array_values;
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
use function class_parents;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function interface_exists;
|
||||
use function is_a;
|
||||
use function sprintf;
|
||||
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
/**
|
||||
* Performs strict validation of the mapping schema
|
||||
*
|
||||
* @link www.doctrine-project.com
|
||||
*
|
||||
* @psalm-import-type FieldMapping from ClassMetadata
|
||||
*/
|
||||
class SchemaValidator
|
||||
{
|
||||
/** @var EntityManagerInterface */
|
||||
private $em;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
/** @var bool */
|
||||
private $validatePropertyTypes;
|
||||
|
||||
/**
|
||||
* It maps built-in Doctrine types to PHP types
|
||||
*/
|
||||
private const BUILTIN_TYPES_MAP = [
|
||||
AsciiStringType::class => 'string',
|
||||
BigIntType::class => 'string',
|
||||
BooleanType::class => 'bool',
|
||||
DecimalType::class => 'string',
|
||||
FloatType::class => 'float',
|
||||
GuidType::class => 'string',
|
||||
IntegerType::class => 'int',
|
||||
JsonType::class => 'array',
|
||||
SimpleArrayType::class => 'array',
|
||||
SmallIntType::class => 'int',
|
||||
StringType::class => 'string',
|
||||
TextType::class => 'string',
|
||||
];
|
||||
|
||||
public function __construct(EntityManagerInterface $em, bool $validatePropertyTypes = true)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->em = $em;
|
||||
$this->validatePropertyTypes = $validatePropertyTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,6 +140,11 @@ class SchemaValidator
|
||||
}
|
||||
}
|
||||
|
||||
// PHP 7.4 introduces the ability to type properties, so we can't validate them in previous versions
|
||||
if (PHP_VERSION_ID >= 70400 && $this->validatePropertyTypes) {
|
||||
array_push($ce, ...$this->validatePropertiesTypes($class));
|
||||
}
|
||||
|
||||
if ($class->isEmbeddedClass && count($class->associationMappings) > 0) {
|
||||
$ce[] = "Embeddable '" . $class->name . "' does not support associations";
|
||||
|
||||
@@ -304,4 +357,122 @@ class SchemaValidator
|
||||
|
||||
return $schemaTool->getUpdateSchemaSql($allMetadata, true);
|
||||
}
|
||||
|
||||
/** @return list<string> containing the found issues */
|
||||
private function validatePropertiesTypes(ClassMetadataInfo $class): array
|
||||
{
|
||||
return array_values(
|
||||
array_filter(
|
||||
array_map(
|
||||
/** @param FieldMapping $fieldMapping */
|
||||
function (array $fieldMapping) use ($class): ?string {
|
||||
$fieldName = $fieldMapping['fieldName'];
|
||||
assert(isset($class->reflFields[$fieldName]));
|
||||
$propertyType = $class->reflFields[$fieldName]->getType();
|
||||
|
||||
// If the field type is not a built-in type, we cannot check it
|
||||
if (! Type::hasType($fieldMapping['type'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the property type is not a named type, we cannot check it
|
||||
if (! ($propertyType instanceof ReflectionNamedType) || $propertyType->getName() === 'mixed') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$metadataFieldType = $this->findBuiltInType(Type::getType($fieldMapping['type']));
|
||||
|
||||
//If the metadata field type is not a mapped built-in type, we cannot check it
|
||||
if ($metadataFieldType === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$propertyType = $propertyType->getName();
|
||||
|
||||
// If the property type is the same as the metadata field type, we are ok
|
||||
if ($propertyType === $metadataFieldType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_a($propertyType, BackedEnum::class, true)) {
|
||||
$backingType = (string) (new ReflectionEnum($propertyType))->getBackingType();
|
||||
|
||||
if ($metadataFieldType !== $backingType) {
|
||||
return sprintf(
|
||||
"The field '%s#%s' has the property type '%s' with a backing type of '%s' that differs from the metadata field type '%s'.",
|
||||
$class->name,
|
||||
$fieldName,
|
||||
$propertyType,
|
||||
$backingType,
|
||||
$metadataFieldType
|
||||
);
|
||||
}
|
||||
|
||||
if (! isset($fieldMapping['enumType']) || $propertyType === $fieldMapping['enumType']) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
"The field '%s#%s' has the property type '%s' that differs from the metadata enumType '%s'.",
|
||||
$class->name,
|
||||
$fieldName,
|
||||
$propertyType,
|
||||
$fieldMapping['enumType']
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
isset($fieldMapping['enumType'])
|
||||
&& $propertyType !== $fieldMapping['enumType']
|
||||
&& interface_exists($propertyType)
|
||||
&& is_a($fieldMapping['enumType'], $propertyType, true)
|
||||
) {
|
||||
$backingType = (string) (new ReflectionEnum($fieldMapping['enumType']))->getBackingType();
|
||||
|
||||
if ($metadataFieldType === $backingType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
"The field '%s#%s' has the metadata enumType '%s' with a backing type of '%s' that differs from the metadata field type '%s'.",
|
||||
$class->name,
|
||||
$fieldName,
|
||||
$fieldMapping['enumType'],
|
||||
$backingType,
|
||||
$metadataFieldType
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
$fieldMapping['type'] === 'json'
|
||||
&& in_array($propertyType, ['string', 'int', 'float', 'bool', 'true', 'false', 'null'], true)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
"The field '%s#%s' has the property type '%s' that differs from the metadata field type '%s' returned by the '%s' DBAL type.",
|
||||
$class->name,
|
||||
$fieldName,
|
||||
$propertyType,
|
||||
$metadataFieldType,
|
||||
$fieldMapping['type']
|
||||
);
|
||||
},
|
||||
$class->fieldMappings
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The exact DBAL type must be used (no subclasses), since consumers of doctrine/orm may have their own
|
||||
* customization around field types.
|
||||
*/
|
||||
private function findBuiltInType(Type $type): ?string
|
||||
{
|
||||
$typeName = get_class($type);
|
||||
|
||||
return self::BUILTIN_TYPES_MAP[$typeName] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ use Doctrine\ORM\Exception\EntityIdentityCollisionException;
|
||||
use Doctrine\ORM\Exception\ORMException;
|
||||
use Doctrine\ORM\Exception\UnexpectedAssociationValue;
|
||||
use Doctrine\ORM\Id\AssignedGenerator;
|
||||
use Doctrine\ORM\Internal\CommitOrderCalculator;
|
||||
use Doctrine\ORM\Internal\HydrationCompleteHandler;
|
||||
use Doctrine\ORM\Internal\TopologicalSort;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
@@ -52,6 +51,7 @@ use RuntimeException;
|
||||
use Throwable;
|
||||
use UnexpectedValueException;
|
||||
|
||||
use function array_chunk;
|
||||
use function array_combine;
|
||||
use function array_diff_key;
|
||||
use function array_filter;
|
||||
@@ -315,6 +315,9 @@ class UnitOfWork implements PropertyChangedListener
|
||||
*/
|
||||
private $eagerLoadingEntities = [];
|
||||
|
||||
/** @var array<string, array<string, mixed>> */
|
||||
private $eagerLoadingCollections = [];
|
||||
|
||||
/** @var bool */
|
||||
protected $hasCache = false;
|
||||
|
||||
@@ -1682,11 +1685,11 @@ IDs should uniquely map to entity object instances. This problem may occur if:
|
||||
clearing the EntityManager;
|
||||
- you might have been using EntityManager#getReference() to create a reference
|
||||
for a nonexistent ID that was subsequently (by the RDBMS) assigned to another
|
||||
entity.
|
||||
entity.
|
||||
|
||||
Otherwise, it might be an ORM-internal inconsistency, please report it.
|
||||
|
||||
To opt-in to the new exception, call
|
||||
To opt-in to the new exception, call
|
||||
\Doctrine\ORM\Configuration::setRejectIdCollisionInIdentityMap on the entity
|
||||
manager's configuration.
|
||||
EXCEPTION
|
||||
@@ -2721,16 +2724,6 @@ EXCEPTION
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CommitOrderCalculator used by the UnitOfWork to order commits.
|
||||
*
|
||||
* @return CommitOrderCalculator
|
||||
*/
|
||||
public function getCommitOrderCalculator()
|
||||
{
|
||||
return new Internal\CommitOrderCalculator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the UnitOfWork.
|
||||
*
|
||||
@@ -2760,6 +2753,7 @@ EXCEPTION
|
||||
$this->pendingCollectionElementRemovals =
|
||||
$this->visitedCollections =
|
||||
$this->eagerLoadingEntities =
|
||||
$this->eagerLoadingCollections =
|
||||
$this->orphanRemovals = [];
|
||||
} else {
|
||||
Deprecation::triggerIfCalledFromOutside(
|
||||
@@ -2949,6 +2943,10 @@ EXCEPTION
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! isset($hints['fetchMode'][$class->name][$field])) {
|
||||
$hints['fetchMode'][$class->name][$field] = $assoc['fetch'];
|
||||
}
|
||||
|
||||
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
|
||||
|
||||
switch (true) {
|
||||
@@ -3012,10 +3010,6 @@ EXCEPTION
|
||||
break;
|
||||
}
|
||||
|
||||
if (! isset($hints['fetchMode'][$class->name][$field])) {
|
||||
$hints['fetchMode'][$class->name][$field] = $assoc['fetch'];
|
||||
}
|
||||
|
||||
// Foreign key is set
|
||||
// Check identity map first
|
||||
// FIXME: Can break easily with composite keys if join column values are in
|
||||
@@ -3058,7 +3052,9 @@ EXCEPTION
|
||||
break;
|
||||
|
||||
// Deferred eager load only works for single identifier classes
|
||||
case isset($hints[self::HINT_DEFEREAGERLOAD]) && ! $targetClass->isIdentifierComposite:
|
||||
case isset($hints[self::HINT_DEFEREAGERLOAD]) &&
|
||||
$hints[self::HINT_DEFEREAGERLOAD] &&
|
||||
! $targetClass->isIdentifierComposite:
|
||||
// TODO: Is there a faster approach?
|
||||
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($normalizedAssociatedId);
|
||||
|
||||
@@ -3107,9 +3103,13 @@ EXCEPTION
|
||||
$reflField = $class->reflFields[$field];
|
||||
$reflField->setValue($entity, $pColl);
|
||||
|
||||
if ($assoc['fetch'] === ClassMetadata::FETCH_EAGER) {
|
||||
$this->loadCollection($pColl);
|
||||
$pColl->takeSnapshot();
|
||||
if ($hints['fetchMode'][$class->name][$field] === ClassMetadata::FETCH_EAGER) {
|
||||
if ($assoc['type'] === ClassMetadata::ONE_TO_MANY) {
|
||||
$this->scheduleCollectionForBatchLoading($pColl, $class);
|
||||
} elseif ($assoc['type'] === ClassMetadata::MANY_TO_MANY) {
|
||||
$this->loadCollection($pColl);
|
||||
$pColl->takeSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
$this->originalEntityData[$oid][$field] = $pColl;
|
||||
@@ -3126,7 +3126,7 @@ EXCEPTION
|
||||
/** @return void */
|
||||
public function triggerEagerLoads()
|
||||
{
|
||||
if (! $this->eagerLoadingEntities) {
|
||||
if (! $this->eagerLoadingEntities && ! $this->eagerLoadingCollections) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3139,11 +3139,69 @@ EXCEPTION
|
||||
continue;
|
||||
}
|
||||
|
||||
$class = $this->em->getClassMetadata($entityName);
|
||||
$class = $this->em->getClassMetadata($entityName);
|
||||
$batches = array_chunk($ids, $this->em->getConfiguration()->getEagerFetchBatchSize());
|
||||
|
||||
$this->getEntityPersister($entityName)->loadAll(
|
||||
array_combine($class->identifier, [array_values($ids)])
|
||||
);
|
||||
foreach ($batches as $batchedIds) {
|
||||
$this->getEntityPersister($entityName)->loadAll(
|
||||
array_combine($class->identifier, [$batchedIds])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$eagerLoadingCollections = $this->eagerLoadingCollections; // avoid recursion
|
||||
$this->eagerLoadingCollections = [];
|
||||
|
||||
foreach ($eagerLoadingCollections as $group) {
|
||||
$this->eagerLoadCollections($group['items'], $group['mapping']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all data into the given collections, according to the specified mapping
|
||||
*
|
||||
* @param PersistentCollection[] $collections
|
||||
* @param array<string, mixed> $mapping
|
||||
* @psalm-param array{targetEntity: class-string, sourceEntity: class-string, mappedBy: string, indexBy: string|null} $mapping
|
||||
*/
|
||||
private function eagerLoadCollections(array $collections, array $mapping): void
|
||||
{
|
||||
$targetEntity = $mapping['targetEntity'];
|
||||
$class = $this->em->getClassMetadata($mapping['sourceEntity']);
|
||||
$mappedBy = $mapping['mappedBy'];
|
||||
|
||||
$batches = array_chunk($collections, $this->em->getConfiguration()->getEagerFetchBatchSize(), true);
|
||||
|
||||
foreach ($batches as $collectionBatch) {
|
||||
$entities = [];
|
||||
|
||||
foreach ($collectionBatch as $collection) {
|
||||
$entities[] = $collection->getOwner();
|
||||
}
|
||||
|
||||
$found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities]);
|
||||
|
||||
$targetClass = $this->em->getClassMetadata($targetEntity);
|
||||
$targetProperty = $targetClass->getReflectionProperty($mappedBy);
|
||||
|
||||
foreach ($found as $targetValue) {
|
||||
$sourceEntity = $targetProperty->getValue($targetValue);
|
||||
|
||||
$id = $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($sourceEntity));
|
||||
$idHash = implode(' ', $id);
|
||||
|
||||
if (isset($mapping['indexBy'])) {
|
||||
$indexByProperty = $targetClass->getReflectionProperty($mapping['indexBy']);
|
||||
$collectionBatch[$idHash]->hydrateSet($indexByProperty->getValue($targetValue), $targetValue);
|
||||
} else {
|
||||
$collectionBatch[$idHash]->add($targetValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($collections as $association) {
|
||||
$association->setInitialized(true);
|
||||
$association->takeSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3174,6 +3232,33 @@ EXCEPTION
|
||||
$collection->setInitialized(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule this collection for batch loading at the end of the UnitOfWork
|
||||
*/
|
||||
private function scheduleCollectionForBatchLoading(PersistentCollection $collection, ClassMetadata $sourceClass): void
|
||||
{
|
||||
$mapping = $collection->getMapping();
|
||||
$name = $mapping['sourceEntity'] . '#' . $mapping['fieldName'];
|
||||
|
||||
if (! isset($this->eagerLoadingCollections[$name])) {
|
||||
$this->eagerLoadingCollections[$name] = [
|
||||
'items' => [],
|
||||
'mapping' => $mapping,
|
||||
];
|
||||
}
|
||||
|
||||
$owner = $collection->getOwner();
|
||||
assert($owner !== null);
|
||||
|
||||
$id = $this->identifierFlattener->flattenIdentifier(
|
||||
$sourceClass,
|
||||
$sourceClass->getIdentifierValues($owner)
|
||||
);
|
||||
$idHash = implode(' ', $id);
|
||||
|
||||
$this->eagerLoadingCollections[$name]['items'][$idHash] = $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the identity map of the UnitOfWork.
|
||||
*
|
||||
|
||||
@@ -49,11 +49,19 @@
|
||||
|
||||
<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
|
||||
<exclude-pattern>lib/Doctrine/ORM/Mapping/Driver/CompatibilityAnnotationDriver.php</exclude-pattern>
|
||||
<exclude-pattern>lib/Doctrine/ORM/Tools/Console/CommandCompatibility.php</exclude-pattern>
|
||||
<exclude-pattern>lib/Doctrine/ORM/Tools/Console/Helper/EntityManagerHelper.php</exclude-pattern>
|
||||
<exclude-pattern>tests/*</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="Squiz.Classes.ClassFileName.NoMatch">
|
||||
<exclude-pattern>*/tests/*</exclude-pattern>
|
||||
<exclude-pattern>lib/Doctrine/ORM/Tools/Console/Helper/EntityManagerHelper.php</exclude-pattern>
|
||||
<exclude-pattern>tests/*</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps">
|
||||
<exclude-pattern>lib/Doctrine/ORM/Tools/Debug.php</exclude-pattern>
|
||||
<exclude-pattern>tests/Doctrine/Tests/ORM/Tools/DebugTest.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase">
|
||||
@@ -192,6 +200,11 @@
|
||||
<exclude-pattern>tests/Doctrine/Tests/ORM/Functional/Ticket/DDC832Test.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="Squiz.Classes.ValidClassName.NotCamelCaps">
|
||||
<!-- we need to test what happens with an stdClass proxy -->
|
||||
<exclude-pattern>tests/Doctrine/Tests/Proxy/DefaultProxyClassNameResolverTest.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="Squiz.Commenting.FunctionComment.WrongStyle">
|
||||
<!-- https://github.com/squizlabs/PHP_CodeSniffer/issues/1961 -->
|
||||
<exclude-pattern>tests/Doctrine/Tests/Mocks/DatabasePlatformMock.php</exclude-pattern>
|
||||
|
||||
@@ -286,12 +286,22 @@ parameters:
|
||||
path: lib/Doctrine/ORM/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method Doctrine\\\\Common\\\\Proxy\\\\Proxy\\:\\:__construct\\(\\)\\.$#"
|
||||
message: "#^Call to an undefined method Doctrine\\\\Common\\\\Proxy\\\\Proxy\\:\\:__wakeup\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method Doctrine\\\\Common\\\\Proxy\\\\Proxy\\:\\:__wakeup\\(\\)\\.$#"
|
||||
message: "#^Call to an undefined static method Doctrine\\\\ORM\\\\Proxy\\\\ProxyFactory\\:\\:createLazyGhost\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: "#^Comparison operation \"\\<\" between 0\\|1\\|2\\|3\\|4 and 0 is always false\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: "#^Comparison operation \"\\>\" between 0\\|1\\|2\\|3\\|4 and 4 is always false\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Proxy/ProxyFactory.php
|
||||
|
||||
@@ -300,6 +310,11 @@ parameters:
|
||||
count: 3
|
||||
path: lib/Doctrine/ORM/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: "#^Result of \\|\\| is always false\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$sqlParams of method Doctrine\\\\ORM\\\\Query\\:\\:evictResultSetCache\\(\\) expects array\\<string, mixed\\>, array\\<int, mixed\\> given\\.$#"
|
||||
count: 1
|
||||
@@ -440,6 +455,11 @@ parameters:
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Query/SqlWalker.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$condTerm of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkConditionalTerm\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalFactor\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalPrimary\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalTerm, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Phase2OptimizableConditional given\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Query/SqlWalker.php
|
||||
|
||||
-
|
||||
message: "#^Result of && is always false\\.$#"
|
||||
count: 1
|
||||
@@ -595,16 +615,6 @@ parameters:
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php
|
||||
|
||||
-
|
||||
message: "#^Instanceof between \\*NEVER\\* and Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalFactor will always evaluate to false\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php
|
||||
|
||||
-
|
||||
message: "#^Instanceof between Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalExpression and Doctrine\\\\ORM\\\\Query\\\\AST\\\\ConditionalPrimary will always evaluate to false\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php
|
||||
|
||||
-
|
||||
message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#"
|
||||
count: 1
|
||||
|
||||
@@ -54,6 +54,11 @@ parameters:
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php
|
||||
|
||||
-
|
||||
message: '#^Class Doctrine\\DBAL\\Platforms\\MySQLPlatform not found\.$#'
|
||||
count: 2
|
||||
path: lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
|
||||
|
||||
# Symfony cache supports passing a key prefix to the clear method.
|
||||
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<files psalm-version="5.14.1@b9d355e0829c397b9b3b47d0c0ed042a8a70284d">
|
||||
<files psalm-version="5.16.0@2897ba636551a8cb61601cc26f6ccfbba6c36591">
|
||||
<file src="lib/Doctrine/ORM/AbstractQuery.php">
|
||||
<DeprecatedClass>
|
||||
<code>IterableResult</code>
|
||||
@@ -400,12 +400,6 @@
|
||||
<code>getTableHiLoUpdateNextValSql</code>
|
||||
</UndefinedMethod>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Internal/CommitOrderCalculator.php">
|
||||
<RedundantCondition>
|
||||
<code><![CDATA[$vertex->state !== VertexState::VISITED]]></code>
|
||||
<code><![CDATA[$vertex->state !== VertexState::VISITED]]></code>
|
||||
</RedundantCondition>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php">
|
||||
<DeprecatedClass>
|
||||
<code>IterableResult</code>
|
||||
@@ -542,6 +536,8 @@
|
||||
<ArgumentTypeCoercion>
|
||||
<code>$class</code>
|
||||
<code>$class</code>
|
||||
<code>$platformFamily</code>
|
||||
<code><![CDATA['Doctrine\DBAL\Platforms\PostgreSqlPlatform']]></code>
|
||||
<code><![CDATA[new $definition['class']()]]></code>
|
||||
</ArgumentTypeCoercion>
|
||||
<DeprecatedClass>
|
||||
@@ -594,7 +590,6 @@
|
||||
<DocblockTypeContradiction>
|
||||
<code><![CDATA[! $mapping['isOwningSide']]]></code>
|
||||
<code><![CDATA[! $this->table]]></code>
|
||||
<code><![CDATA[! class_exists($mapping['targetEntity'])]]></code>
|
||||
<code><![CDATA[$this->table]]></code>
|
||||
<code><![CDATA[isset($mapping['id']) && $mapping['id'] === true && ! $mapping['isOwningSide']]]></code>
|
||||
<code><![CDATA[isset($mapping['orderBy']) && ! is_array($mapping['orderBy'])]]></code>
|
||||
@@ -630,9 +625,6 @@
|
||||
<code>$quotedColumnNames</code>
|
||||
<code><![CDATA[$this->namespace . '\\' . $className]]></code>
|
||||
</LessSpecificReturnStatement>
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<MoreSpecificReturnType>
|
||||
<code>FieldMapping</code>
|
||||
<code>class-string|null</code>
|
||||
@@ -873,6 +865,9 @@
|
||||
<MoreSpecificReturnType>
|
||||
<code>class-string</code>
|
||||
</MoreSpecificReturnType>
|
||||
<NoValue>
|
||||
<code>$metadata</code>
|
||||
</NoValue>
|
||||
<PossiblyNullArrayAccess>
|
||||
<code><![CDATA[$this->tables[$tableName]]]></code>
|
||||
<code><![CDATA[$this->tables[$tableName]]]></code>
|
||||
@@ -916,13 +911,12 @@
|
||||
<code><![CDATA[$metadata->table]]></code>
|
||||
</InvalidPropertyAssignmentValue>
|
||||
<InvalidPropertyFetch>
|
||||
<code><![CDATA[$indexXml->options]]></code>
|
||||
<code><![CDATA[$uniqueXml->options]]></code>
|
||||
<code><![CDATA[$xmlRoot->{'discriminator-column'}]]></code>
|
||||
<code><![CDATA[$xmlRoot->{'discriminator-map'}]]></code>
|
||||
</InvalidPropertyFetch>
|
||||
<InvalidReturnStatement>
|
||||
<code>$mapping</code>
|
||||
<code>$result</code>
|
||||
<code><![CDATA[[
|
||||
'usage' => $usage,
|
||||
'region' => $region,
|
||||
@@ -946,6 +940,7 @@
|
||||
* options?: array
|
||||
* }</code>
|
||||
<code>array{usage: int|null, region?: string}</code>
|
||||
<code>loadMappingFile</code>
|
||||
</InvalidReturnType>
|
||||
<MissingParamType>
|
||||
<code>$fileExtension</code>
|
||||
@@ -955,15 +950,9 @@
|
||||
<code>$metadata</code>
|
||||
</MoreSpecificImplementedParamType>
|
||||
<NoInterfaceProperties>
|
||||
<code><![CDATA[$indexXml->options]]></code>
|
||||
<code><![CDATA[$uniqueXml->options]]></code>
|
||||
<code><![CDATA[$xmlRoot->{'discriminator-column'}]]></code>
|
||||
<code><![CDATA[$xmlRoot->{'discriminator-map'}]]></code>
|
||||
</NoInterfaceProperties>
|
||||
<PossiblyNullArgument>
|
||||
<code><![CDATA[$joinColumnElement['options']->children()]]></code>
|
||||
<code><![CDATA[$option->children()]]></code>
|
||||
</PossiblyNullArgument>
|
||||
<TypeDoesNotContainType>
|
||||
<code><![CDATA[$xmlRoot->getName() === 'embeddable']]></code>
|
||||
<code><![CDATA[$xmlRoot->getName() === 'entity']]></code>
|
||||
@@ -1377,38 +1366,74 @@
|
||||
<code>$columnList</code>
|
||||
</PossiblyUndefinedVariable>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Proxy/DefaultProxyClassNameResolver.php">
|
||||
<LessSpecificReturnStatement>
|
||||
<code>$className</code>
|
||||
<code>substr($className, $pos + Proxy::MARKER_LENGTH + 2)</code>
|
||||
</LessSpecificReturnStatement>
|
||||
<MoreSpecificReturnType>
|
||||
<code>string</code>
|
||||
</MoreSpecificReturnType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Proxy/ProxyFactory.php">
|
||||
<ArgumentTypeCoercion>
|
||||
<code>$classMetadata</code>
|
||||
<code>$classMetadata</code>
|
||||
<code>$classMetadata</code>
|
||||
</ArgumentTypeCoercion>
|
||||
<DirectConstructorCall>
|
||||
<code><![CDATA[$proxy->__construct(static function (InternalProxy $object) use ($initializer, $proxy): void {
|
||||
$initializer($object, $proxy);
|
||||
})]]></code>
|
||||
</DirectConstructorCall>
|
||||
<DeprecatedMethod>
|
||||
<code>createCloner</code>
|
||||
<code>createInitializer</code>
|
||||
</DeprecatedMethod>
|
||||
<InvalidArgument>
|
||||
<code><![CDATA[$classMetadata->getReflectionProperties()]]></code>
|
||||
<code><![CDATA[$em->getMetadataFactory()]]></code>
|
||||
<code><![CDATA[$em->getMetadataFactory()]]></code>
|
||||
</InvalidArgument>
|
||||
<InvalidNullableReturnType>
|
||||
<code>Closure</code>
|
||||
</InvalidNullableReturnType>
|
||||
<InvalidPropertyAssignmentValue>
|
||||
<code><![CDATA[$this->proxyFactories]]></code>
|
||||
</InvalidPropertyAssignmentValue>
|
||||
<NoInterfaceProperties>
|
||||
<code><![CDATA[$metadata->isEmbeddedClass]]></code>
|
||||
<code><![CDATA[$metadata->isMappedSuperclass]]></code>
|
||||
</NoInterfaceProperties>
|
||||
<NullableReturnStatement>
|
||||
<code><![CDATA[$this->proxyFactories[$className] = $proxyFactory]]></code>
|
||||
</NullableReturnStatement>
|
||||
<PossiblyFalseArgument>
|
||||
<code>$i</code>
|
||||
</PossiblyFalseArgument>
|
||||
<PossiblyFalseOperand>
|
||||
<code>$i</code>
|
||||
</PossiblyFalseOperand>
|
||||
<PossiblyNullPropertyFetch>
|
||||
<code><![CDATA[$property->name]]></code>
|
||||
<code><![CDATA[$property->name]]></code>
|
||||
</PossiblyNullPropertyFetch>
|
||||
<PossiblyNullReference>
|
||||
<code>getValue</code>
|
||||
<code>setAccessible</code>
|
||||
<code>setAccessible</code>
|
||||
<code>setValue</code>
|
||||
<code>setValue</code>
|
||||
</PossiblyNullReference>
|
||||
<TypeDoesNotContainType>
|
||||
<code><![CDATA[$autoGenerate < 0]]></code>
|
||||
<code><![CDATA[$autoGenerate > 4]]></code>
|
||||
</TypeDoesNotContainType>
|
||||
<UndefinedInterfaceMethod>
|
||||
<code>__construct</code>
|
||||
<code>__wakeup</code>
|
||||
</UndefinedInterfaceMethod>
|
||||
<UndefinedMethod>
|
||||
<code><![CDATA[self::createLazyGhost(static function (InternalProxy $object) use ($initializer, &$proxy): void {
|
||||
$initializer($object, $proxy);
|
||||
}, $skippedProperties)]]></code>
|
||||
</UndefinedMethod>
|
||||
<UnresolvableInclude>
|
||||
<code>require $fileName</code>
|
||||
</UnresolvableInclude>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query.php">
|
||||
<DeprecatedClass>
|
||||
@@ -1704,11 +1729,6 @@
|
||||
<code>$sqlWalker</code>
|
||||
</ParamNameMismatch>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/AST/Node.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php">
|
||||
<ParamNameMismatch>
|
||||
<code>$sqlWalker</code>
|
||||
@@ -1819,9 +1839,32 @@
|
||||
</ParamNameMismatch>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php">
|
||||
<DeprecatedProperty>
|
||||
<code><![CDATA[$this->_sqlStatements]]></code>
|
||||
<code><![CDATA[$this->_sqlStatements]]></code>
|
||||
</DeprecatedProperty>
|
||||
<DocblockTypeContradiction>
|
||||
<code><![CDATA[$this->_sqlStatements !== null && $this->sqlStatements === null]]></code>
|
||||
<code><![CDATA[$this->sqlStatements === null]]></code>
|
||||
</DocblockTypeContradiction>
|
||||
<PossiblyNullPropertyAssignmentValue>
|
||||
<code>null</code>
|
||||
</PossiblyNullPropertyAssignmentValue>
|
||||
<PropertyNotSetInConstructor>
|
||||
<code>$_sqlStatements</code>
|
||||
<code>$queryCacheProfile</code>
|
||||
<code>$sqlStatements</code>
|
||||
</PropertyNotSetInConstructor>
|
||||
<RedundantConditionGivenDocblockType>
|
||||
<code><![CDATA[$this->_sqlStatements !== null]]></code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
<UninitializedProperty>
|
||||
<code><![CDATA[$this->sqlStatements]]></code>
|
||||
</UninitializedProperty>
|
||||
<UnsupportedPropertyReferenceUsage>
|
||||
<code><![CDATA[$this->_sqlStatements = &$this->sqlStatements]]></code>
|
||||
<code><![CDATA[$this->_sqlStatements = &$this->sqlStatements]]></code>
|
||||
</UnsupportedPropertyReferenceUsage>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php">
|
||||
<InvalidReturnStatement>
|
||||
@@ -1831,15 +1874,12 @@
|
||||
<code>int</code>
|
||||
</InvalidReturnType>
|
||||
<PossiblyInvalidIterator>
|
||||
<code><![CDATA[$this->_sqlStatements]]></code>
|
||||
<code><![CDATA[$this->sqlStatements]]></code>
|
||||
</PossiblyInvalidIterator>
|
||||
<PropertyNotSetInConstructor>
|
||||
<code>MultiTableDeleteExecutor</code>
|
||||
<code>MultiTableDeleteExecutor</code>
|
||||
</PropertyNotSetInConstructor>
|
||||
<UninitializedProperty>
|
||||
<code><![CDATA[$this->_sqlStatements]]></code>
|
||||
</UninitializedProperty>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php">
|
||||
<InvalidReturnStatement>
|
||||
@@ -1849,40 +1889,40 @@
|
||||
<code>int</code>
|
||||
</InvalidReturnType>
|
||||
<PossiblyInvalidIterator>
|
||||
<code><![CDATA[$this->_sqlStatements]]></code>
|
||||
<code><![CDATA[$this->sqlStatements]]></code>
|
||||
</PossiblyInvalidIterator>
|
||||
<PropertyNotSetInConstructor>
|
||||
<code>MultiTableUpdateExecutor</code>
|
||||
<code>MultiTableUpdateExecutor</code>
|
||||
<code>MultiTableUpdateExecutor</code>
|
||||
</PropertyNotSetInConstructor>
|
||||
<PropertyTypeCoercion>
|
||||
<code><![CDATA[$this->_sqlStatements]]></code>
|
||||
<code><![CDATA[$this->sqlStatements]]></code>
|
||||
</PropertyTypeCoercion>
|
||||
<UninitializedProperty>
|
||||
<code><![CDATA[$this->_sqlStatements]]></code>
|
||||
</UninitializedProperty>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php">
|
||||
<PossiblyInvalidArgument>
|
||||
<code><![CDATA[$this->_sqlStatements]]></code>
|
||||
<code><![CDATA[$this->sqlStatements]]></code>
|
||||
</PossiblyInvalidArgument>
|
||||
<PropertyNotSetInConstructor>
|
||||
<code>SingleSelectExecutor</code>
|
||||
<code>SingleSelectExecutor</code>
|
||||
</PropertyNotSetInConstructor>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php">
|
||||
<InvalidReturnStatement>
|
||||
<code><![CDATA[$conn->executeStatement($this->_sqlStatements, $params, $types)]]></code>
|
||||
<code><![CDATA[$conn->executeStatement($this->sqlStatements, $params, $types)]]></code>
|
||||
</InvalidReturnStatement>
|
||||
<InvalidReturnType>
|
||||
<code>int</code>
|
||||
</InvalidReturnType>
|
||||
<PossiblyInvalidArgument>
|
||||
<code><![CDATA[$this->_sqlStatements]]></code>
|
||||
<code><![CDATA[$this->sqlStatements]]></code>
|
||||
</PossiblyInvalidArgument>
|
||||
<PropertyNotSetInConstructor>
|
||||
<code>SingleTableDeleteUpdateExecutor</code>
|
||||
<code>SingleTableDeleteUpdateExecutor</code>
|
||||
<code>SingleTableDeleteUpdateExecutor</code>
|
||||
</PropertyNotSetInConstructor>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr.php">
|
||||
@@ -1896,36 +1936,15 @@
|
||||
<code>$parts</code>
|
||||
</NonInvariantDocblockPropertyType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Base.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Comparison.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Composite.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<PossiblyInvalidCast>
|
||||
<code>$part</code>
|
||||
</PossiblyInvalidCast>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/From.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Func.php">
|
||||
<LessSpecificReturnStatement>
|
||||
<code><![CDATA[$this->arguments]]></code>
|
||||
</LessSpecificReturnStatement>
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<MoreSpecificReturnType>
|
||||
<code><![CDATA[list<mixed>]]></code>
|
||||
</MoreSpecificReturnType>
|
||||
@@ -1936,9 +1955,6 @@
|
||||
</NonInvariantDocblockPropertyType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Join.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<PossiblyNullArgument>
|
||||
<code><![CDATA[$this->conditionType]]></code>
|
||||
</PossiblyNullArgument>
|
||||
@@ -1948,16 +1964,6 @@
|
||||
<code>$parts</code>
|
||||
</NonInvariantDocblockPropertyType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Math.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/OrderBy.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Orx.php">
|
||||
<NonInvariantDocblockPropertyType>
|
||||
<code>$allowedClasses</code>
|
||||
@@ -1971,9 +1977,6 @@
|
||||
</NonInvariantDocblockPropertyType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Filter/SQLFilter.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<MissingClosureParamType>
|
||||
<code>$value</code>
|
||||
</MissingClosureParamType>
|
||||
@@ -2028,18 +2031,13 @@
|
||||
</PossiblyFalseArgument>
|
||||
<PossiblyInvalidArgument>
|
||||
<code>$AST</code>
|
||||
<code>$conditionalExpression</code>
|
||||
<code>$expr</code>
|
||||
<code>$pathExp</code>
|
||||
<code><![CDATA[$this->ConditionalExpression()]]></code>
|
||||
<code><![CDATA[$this->ConditionalExpression()]]></code>
|
||||
<code><![CDATA[$this->lexer->getLiteral($token)]]></code>
|
||||
<code><![CDATA[$this->lexer->getLiteral($token)]]></code>
|
||||
<code><![CDATA[$this->lexer->getLiteral($token)]]></code>
|
||||
</PossiblyInvalidArgument>
|
||||
<PossiblyInvalidPropertyAssignmentValue>
|
||||
<code><![CDATA[$this->ConditionalExpression()]]></code>
|
||||
<code><![CDATA[$this->ConditionalExpression()]]></code>
|
||||
<code><![CDATA[$this->SimpleArithmeticExpression()]]></code>
|
||||
</PossiblyInvalidPropertyAssignmentValue>
|
||||
<PossiblyNullArgument>
|
||||
@@ -2086,9 +2084,6 @@
|
||||
<code>addNamedNativeQueryResultClassMapping</code>
|
||||
<code>addNamedNativeQueryResultSetMapping</code>
|
||||
</DeprecatedMethod>
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<PossiblyUndefinedArrayOffset>
|
||||
<code><![CDATA[$associationMapping['joinColumns']]]></code>
|
||||
<code><![CDATA[$associationMapping['joinColumns']]]></code>
|
||||
@@ -2151,20 +2146,20 @@
|
||||
<ImplicitToStringCast>
|
||||
<code>$expr</code>
|
||||
</ImplicitToStringCast>
|
||||
<InvalidArgument>
|
||||
<code>$condExpr</code>
|
||||
<code>$condTerm</code>
|
||||
<code>$factor</code>
|
||||
</InvalidArgument>
|
||||
<InvalidArrayOffset>
|
||||
<code><![CDATA[$this->queryComponents[$expression]]]></code>
|
||||
</InvalidArrayOffset>
|
||||
<InvalidNullableReturnType>
|
||||
<code>string</code>
|
||||
</InvalidNullableReturnType>
|
||||
<MoreSpecificImplementedParamType>
|
||||
<code>$query</code>
|
||||
</MoreSpecificImplementedParamType>
|
||||
<NoValue>
|
||||
<code>$expression</code>
|
||||
</NoValue>
|
||||
<PossiblyInvalidArgument>
|
||||
<code><![CDATA[$aggExpression->pathExpression]]></code>
|
||||
<code><![CDATA[$whereClause->conditionalExpression]]></code>
|
||||
</PossiblyInvalidArgument>
|
||||
<PossiblyNullArgument>
|
||||
<code><![CDATA[$AST->whereClause]]></code>
|
||||
@@ -2200,7 +2195,6 @@
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
<RedundantConditionGivenDocblockType>
|
||||
<code>$whereClause !== null</code>
|
||||
<code><![CDATA[($factor->not ? 'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary)]]></code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/TreeWalkerAdapter.php">
|
||||
@@ -2259,9 +2253,6 @@
|
||||
<InvalidPropertyAssignmentValue>
|
||||
<code>new ArrayCollection($parameters)</code>
|
||||
</InvalidPropertyAssignmentValue>
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<PossiblyFalseArgument>
|
||||
<code>$spacePos</code>
|
||||
<code>$spacePos</code>
|
||||
@@ -2390,11 +2381,6 @@
|
||||
<code>getAllClassNames</code>
|
||||
</PossiblyNullReference>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php">
|
||||
<DeprecatedClass>
|
||||
<code><![CDATA[Debug::dump($resultSet, (int) $input->getOption('depth'), true, false)]]></code>
|
||||
</DeprecatedClass>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php">
|
||||
<InvalidNullableReturnType>
|
||||
<code>int</code>
|
||||
@@ -2557,9 +2543,6 @@
|
||||
<NonInvariantDocblockPropertyType>
|
||||
<code>$_extension</code>
|
||||
</NonInvariantDocblockPropertyType>
|
||||
<PossiblyFalseArgument>
|
||||
<code><![CDATA[$simpleXml->asXML()]]></code>
|
||||
</PossiblyFalseArgument>
|
||||
<RedundantCondition>
|
||||
<code><![CDATA[$field['associationKey']]]></code>
|
||||
<code><![CDATA[isset($field['associationKey']) && $field['associationKey']]]></code>
|
||||
@@ -2642,21 +2625,6 @@
|
||||
<code>$orderByClause</code>
|
||||
</PropertyNotSetInConstructor>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php">
|
||||
<DocblockTypeContradiction>
|
||||
<code><![CDATA[$AST->whereClause->conditionalExpression instanceof ConditionalExpression
|
||||
|| $AST->whereClause->conditionalExpression instanceof ConditionalFactor]]></code>
|
||||
<code><![CDATA[$AST->whereClause->conditionalExpression instanceof ConditionalFactor]]></code>
|
||||
<code><![CDATA[$AST->whereClause->conditionalExpression instanceof ConditionalPrimary]]></code>
|
||||
</DocblockTypeContradiction>
|
||||
<PossiblyInvalidPropertyAssignmentValue>
|
||||
<code><![CDATA[$AST->whereClause->conditionalExpression]]></code>
|
||||
</PossiblyInvalidPropertyAssignmentValue>
|
||||
<RedundantConditionGivenDocblockType>
|
||||
<code><![CDATA[$AST->whereClause->conditionalExpression instanceof ConditionalExpression
|
||||
|| $AST->whereClause->conditionalExpression instanceof ConditionalFactor]]></code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Tools/SchemaTool.php">
|
||||
<ArgumentTypeCoercion>
|
||||
<code>$classes</code>
|
||||
@@ -2737,6 +2705,7 @@
|
||||
<NoValue>
|
||||
<code>$entityState</code>
|
||||
<code>$entityState</code>
|
||||
<code>$object</code>
|
||||
</NoValue>
|
||||
<PossiblyInvalidArgument>
|
||||
<code>$value</code>
|
||||
|
||||
21
psalm.xml
21
psalm.xml
@@ -36,7 +36,12 @@
|
||||
<referencedClass name="Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets"/>
|
||||
<referencedClass name="Doctrine\ORM\Event\LifecycleEventArgs"/>
|
||||
<referencedClass name="Doctrine\ORM\Exception\UnknownEntityNamespace"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\Driver\AnnotationDriver"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\Driver\YamlDriver"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\NamedNativeQueries"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\NamedNativeQuery"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\NamedQueries"/>
|
||||
<referencedClass name="Doctrine\ORM\Mapping\NamedQuery"/>
|
||||
<referencedClass name="Doctrine\ORM\Query\AST\InExpression"/>
|
||||
<referencedClass name="Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand"/>
|
||||
<referencedClass name="Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand"/>
|
||||
@@ -45,10 +50,6 @@
|
||||
<referencedClass name="Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand"/>
|
||||
<referencedClass name="Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper"/>
|
||||
<referencedClass name="Doctrine\ORM\Tools\Console\EntityManagerProvider\HelperSetManagerProvider"/>
|
||||
<referencedClass name="Doctrine\ORM\Internal\CommitOrder\Edge"/>
|
||||
<referencedClass name="Doctrine\ORM\Internal\CommitOrder\Vertex"/>
|
||||
<referencedClass name="Doctrine\ORM\Internal\CommitOrder\VertexState"/>
|
||||
<referencedClass name="Doctrine\ORM\Internal\CommitOrderCalculator"/>
|
||||
</errorLevel>
|
||||
</DeprecatedClass>
|
||||
<DeprecatedConstant>
|
||||
@@ -94,6 +95,7 @@
|
||||
<referencedMethod name="Doctrine\ORM\Configuration::ensureProductionSettings"/>
|
||||
<referencedMethod name="Doctrine\ORM\Configuration::newDefaultAnnotationDriver"/>
|
||||
<referencedMethod name="Doctrine\ORM\EntityManager::createConnection"/>
|
||||
<referencedMethod name="Doctrine\ORM\EntityManagerInterface::getPartialReference"/>
|
||||
<referencedMethod name="Doctrine\ORM\Id\AbstractIdGenerator::generate"/>
|
||||
<referencedMethod name="Doctrine\ORM\ORMInvalidArgumentException::invalidEntityName"/>
|
||||
<referencedMethod name="Doctrine\ORM\ORMSetup::createDefaultAnnotationDriver"/>
|
||||
@@ -121,6 +123,17 @@
|
||||
<file name="lib/Doctrine/ORM/PersistentCollection.php"/>
|
||||
</errorLevel>
|
||||
</DocblockTypeContradiction>
|
||||
<DuplicateClass>
|
||||
<errorLevel type="suppress">
|
||||
<file name="lib/Doctrine/ORM/Tools/Console/CommandCompatibility.php"/>
|
||||
<file name="lib/Doctrine/ORM/Tools/Console/Helper/EntityManagerHelper.php"/>
|
||||
</errorLevel>
|
||||
</DuplicateClass>
|
||||
<ForbiddenCode>
|
||||
<errorLevel type="suppress">
|
||||
<file name="lib/Doctrine/ORM/Tools/Debug.php"/>
|
||||
</errorLevel>
|
||||
</ForbiddenCode>
|
||||
<InvalidArgument>
|
||||
<errorLevel type="suppress">
|
||||
<!-- Argument type changes in DBAL 3.2 -->
|
||||
|
||||
@@ -87,12 +87,18 @@ class ConnectionMock extends Connection
|
||||
*/
|
||||
public function fetchColumn($statement, array $params = [], $colunm = 0, array $types = [])
|
||||
{
|
||||
throw new BadMethodCallException('Call to deprecated method.');
|
||||
throw new BadMethodCallException(sprintf(
|
||||
'Call to deprecated method %s().',
|
||||
__METHOD__
|
||||
));
|
||||
}
|
||||
|
||||
public function query(?string $sql = null): Result
|
||||
{
|
||||
throw new BadMethodCallException('Call to deprecated method.');
|
||||
throw new BadMethodCallException(sprintf(
|
||||
'Call to deprecated method %s().',
|
||||
__METHOD__
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,8 @@ use BadMethodCallException;
|
||||
use Doctrine\DBAL\Exception as DBALException;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Mock class for DatabasePlatform.
|
||||
*/
|
||||
@@ -15,7 +17,10 @@ class DatabasePlatformMock extends AbstractPlatform
|
||||
{
|
||||
public function prefersIdentityColumns(): bool
|
||||
{
|
||||
throw new BadMethodCallException('Call to deprecated method.');
|
||||
throw new BadMethodCallException(sprintf(
|
||||
'Call to deprecated method %s().',
|
||||
__METHOD__
|
||||
));
|
||||
}
|
||||
|
||||
public function supportsIdentityColumns(): bool
|
||||
@@ -25,7 +30,10 @@ class DatabasePlatformMock extends AbstractPlatform
|
||||
|
||||
public function prefersSequences(): bool
|
||||
{
|
||||
throw new BadMethodCallException('Call to deprecated method.');
|
||||
throw new BadMethodCallException(sprintf(
|
||||
'Call to deprecated method %s().',
|
||||
__METHOD__
|
||||
));
|
||||
}
|
||||
|
||||
public function supportsSequences(): bool
|
||||
@@ -94,7 +102,10 @@ class DatabasePlatformMock extends AbstractPlatform
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
throw new BadMethodCallException('Call to deprecated method.');
|
||||
throw new BadMethodCallException(sprintf(
|
||||
'Call to deprecated method %s().',
|
||||
__METHOD__
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,8 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Schema\AbstractSchemaManager;
|
||||
use Exception;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Mock class for Driver.
|
||||
*/
|
||||
@@ -70,7 +72,10 @@ class DriverMock implements Driver
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
throw new BadMethodCallException('Call to deprecated method.');
|
||||
throw new BadMethodCallException(sprintf(
|
||||
'Call to deprecated method %s().',
|
||||
__METHOD__
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM;
|
||||
|
||||
use Doctrine\ORM\Internal\CommitOrderCalculator;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
|
||||
/**
|
||||
* Tests of the commit order calculation.
|
||||
*
|
||||
* IMPORTANT: When writing tests here consider that a lot of graph constellations
|
||||
* can have many valid orderings, so you may want to build a graph that has only
|
||||
* 1 valid order to simplify your tests.
|
||||
*/
|
||||
class CommitOrderCalculatorTest extends OrmTestCase
|
||||
{
|
||||
/** @var CommitOrderCalculator */
|
||||
private $_calc;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->_calc = new CommitOrderCalculator();
|
||||
}
|
||||
|
||||
public function testCommitOrdering1(): void
|
||||
{
|
||||
$class1 = new ClassMetadata(NodeClass1::class);
|
||||
$class2 = new ClassMetadata(NodeClass2::class);
|
||||
$class3 = new ClassMetadata(NodeClass3::class);
|
||||
$class4 = new ClassMetadata(NodeClass4::class);
|
||||
$class5 = new ClassMetadata(NodeClass5::class);
|
||||
|
||||
$this->_calc->addNode($class1->name, $class1);
|
||||
$this->_calc->addNode($class2->name, $class2);
|
||||
$this->_calc->addNode($class3->name, $class3);
|
||||
$this->_calc->addNode($class4->name, $class4);
|
||||
$this->_calc->addNode($class5->name, $class5);
|
||||
|
||||
$this->_calc->addDependency($class1->name, $class2->name, 1);
|
||||
$this->_calc->addDependency($class2->name, $class3->name, 1);
|
||||
$this->_calc->addDependency($class3->name, $class4->name, 1);
|
||||
$this->_calc->addDependency($class5->name, $class1->name, 1);
|
||||
|
||||
$sorted = $this->_calc->sort();
|
||||
|
||||
// There is only 1 valid ordering for this constellation
|
||||
$correctOrder = [$class5, $class1, $class2, $class3, $class4];
|
||||
|
||||
self::assertSame($correctOrder, $sorted);
|
||||
}
|
||||
|
||||
public function testCommitOrdering2(): void
|
||||
{
|
||||
$class1 = new ClassMetadata(NodeClass1::class);
|
||||
$class2 = new ClassMetadata(NodeClass2::class);
|
||||
|
||||
$this->_calc->addNode($class1->name, $class1);
|
||||
$this->_calc->addNode($class2->name, $class2);
|
||||
|
||||
$this->_calc->addDependency($class1->name, $class2->name, 0);
|
||||
$this->_calc->addDependency($class2->name, $class1->name, 1);
|
||||
|
||||
$sorted = $this->_calc->sort();
|
||||
|
||||
// There is only 1 valid ordering for this constellation
|
||||
$correctOrder = [$class2, $class1];
|
||||
|
||||
self::assertSame($correctOrder, $sorted);
|
||||
}
|
||||
|
||||
public function testCommitOrdering3(): void
|
||||
{
|
||||
// this test corresponds to the GH7259Test::testPersistFileBeforeVersion functional test
|
||||
$class1 = new ClassMetadata(NodeClass1::class);
|
||||
$class2 = new ClassMetadata(NodeClass2::class);
|
||||
$class3 = new ClassMetadata(NodeClass3::class);
|
||||
$class4 = new ClassMetadata(NodeClass4::class);
|
||||
|
||||
$this->_calc->addNode($class1->name, $class1);
|
||||
$this->_calc->addNode($class2->name, $class2);
|
||||
$this->_calc->addNode($class3->name, $class3);
|
||||
$this->_calc->addNode($class4->name, $class4);
|
||||
|
||||
$this->_calc->addDependency($class4->name, $class1->name, 1);
|
||||
$this->_calc->addDependency($class1->name, $class2->name, 1);
|
||||
$this->_calc->addDependency($class4->name, $class3->name, 1);
|
||||
$this->_calc->addDependency($class1->name, $class4->name, 0);
|
||||
|
||||
$sorted = $this->_calc->sort();
|
||||
|
||||
// There is only multiple valid ordering for this constellation, but
|
||||
// the class4, class1, class2 ordering is important to break the cycle
|
||||
// on the nullable link.
|
||||
$correctOrders = [
|
||||
[$class4, $class1, $class2, $class3],
|
||||
[$class4, $class1, $class3, $class2],
|
||||
[$class4, $class3, $class1, $class2],
|
||||
];
|
||||
|
||||
// We want to perform a strict comparison of the array
|
||||
self::assertContains($sorted, $correctOrders, '', false, true);
|
||||
}
|
||||
}
|
||||
|
||||
class NodeClass1
|
||||
{
|
||||
}
|
||||
class NodeClass2
|
||||
{
|
||||
}
|
||||
class NodeClass3
|
||||
{
|
||||
}
|
||||
class NodeClass4
|
||||
{
|
||||
}
|
||||
class NodeClass5
|
||||
{
|
||||
}
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Decorator;
|
||||
|
||||
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
|
||||
use Doctrine\ORM\Decorator\EntityManagerDecorator;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
@@ -22,6 +23,8 @@ use function sprintf;
|
||||
|
||||
class EntityManagerDecoratorTest extends TestCase
|
||||
{
|
||||
use VerifyDeprecations;
|
||||
|
||||
public const VOID_METHODS = [
|
||||
'persist',
|
||||
'remove',
|
||||
@@ -122,4 +125,12 @@ class EntityManagerDecoratorTest extends TestCase
|
||||
|
||||
self::assertSame($return, $decorator->$method(...$parameters));
|
||||
}
|
||||
|
||||
public function testGetPartialReferenceIsDeprecated(): void
|
||||
{
|
||||
$this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/10987');
|
||||
$decorator = new class ($this->wrapped) extends EntityManagerDecorator {
|
||||
};
|
||||
$decorator->getPartialReference(stdClass::class, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user