mirror of
https://github.com/doctrine/orm.git
synced 2026-04-02 20:32:19 +02:00
Compare commits
149 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79bff0f072 | ||
|
|
77b579287c | ||
|
|
44c92377a0 | ||
|
|
255612a1ff | ||
|
|
6bb30e1cee | ||
|
|
6336eb5d5d | ||
|
|
b8f404abcf | ||
|
|
3e8490a792 | ||
|
|
14bb034fe4 | ||
|
|
afc0aab61a | ||
|
|
3e784c1d9a | ||
|
|
e1d7a13a5e | ||
|
|
ca49533d3e | ||
|
|
8275471110 | ||
|
|
afd683bdb8 | ||
|
|
fe6e5a67f8 | ||
|
|
b20a66dcdd | ||
|
|
0db02df3aa | ||
|
|
dc46af27ed | ||
|
|
05ab22710b | ||
|
|
6af7de38e1 | ||
|
|
0780b5a6b1 | ||
|
|
71b1831351 | ||
|
|
63d9a898ec | ||
|
|
7376adede8 | ||
|
|
59f7f6793d | ||
|
|
0fc9208d71 | ||
|
|
fd9e572424 | ||
|
|
76490f2c99 | ||
|
|
f8bbdc40b0 | ||
|
|
765a9c43e6 | ||
|
|
fcd9dede2e | ||
|
|
262c396639 | ||
|
|
e767d2dbeb | ||
|
|
d65b6a6a10 | ||
|
|
3d8afbb7b2 | ||
|
|
736f6bc97f | ||
|
|
0ed46303cc | ||
|
|
6e351b699c | ||
|
|
758de75b45 | ||
|
|
1ff63be779 | ||
|
|
2d5d129ee1 | ||
|
|
cdc52b2a9e | ||
|
|
c5e2f3fb23 | ||
|
|
c91c3a3b1d | ||
|
|
42a693c0d3 | ||
|
|
03cc07e4d7 | ||
|
|
78fee8ea5c | ||
|
|
0c73cf93fa | ||
|
|
c03ed691d4 | ||
|
|
b521e89b20 | ||
|
|
a6a94cdefd | ||
|
|
c5e5742744 | ||
|
|
8b3a6dfbbb | ||
|
|
5634c3ccee | ||
|
|
a93d7d22d8 | ||
|
|
677e0a4ed3 | ||
|
|
224ff9710d | ||
|
|
68c71521e7 | ||
|
|
a2ea72c763 | ||
|
|
1492b01093 | ||
|
|
68b797161e | ||
|
|
e17b31ee68 | ||
|
|
ac87e6e7dc | ||
|
|
016d9d2074 | ||
|
|
a96697e436 | ||
|
|
b1ec1da8e6 | ||
|
|
6cfe0883f0 | ||
|
|
cd56fe54bb | ||
|
|
982ef7ac56 | ||
|
|
bc904f6a0c | ||
|
|
cddf985a7d | ||
|
|
2e9ca50007 | ||
|
|
275975a444 | ||
|
|
2088a469cb | ||
|
|
3f13f119e2 | ||
|
|
d444a79bb0 | ||
|
|
01dae555bf | ||
|
|
d9049d8ef5 | ||
|
|
1ad46527b6 | ||
|
|
a4b794db4f | ||
|
|
7eeea7d66c | ||
|
|
f96e812438 | ||
|
|
d1b4aa013a | ||
|
|
dda313871d | ||
|
|
f7584df83c | ||
|
|
28b2591694 | ||
|
|
615566507c | ||
|
|
9b1cb60cbc | ||
|
|
1d0e365401 | ||
|
|
c12daf5206 | ||
|
|
f7030d1844 | ||
|
|
8d7b4d8d17 | ||
|
|
f53d35b74a | ||
|
|
350597beb0 | ||
|
|
8dc17b2851 | ||
|
|
ee9dd474de | ||
|
|
0a4a11b6e4 | ||
|
|
a7406cc26d | ||
|
|
f11adcb622 | ||
|
|
1e4b88d456 | ||
|
|
eb5dd3f34f | ||
|
|
25066f801c | ||
|
|
a0f5b4c9a3 | ||
|
|
8b5148a7a8 | ||
|
|
22198f7c19 | ||
|
|
9cf5593759 | ||
|
|
b5c0e0154f | ||
|
|
dbf26dbf90 | ||
|
|
9b09cd03c0 | ||
|
|
2c489fb8b3 | ||
|
|
42a36e19a5 | ||
|
|
f46d8a1742 | ||
|
|
1eb67b7ca1 | ||
|
|
00b29a13f2 | ||
|
|
a22e0fda8e | ||
|
|
b58946a11f | ||
|
|
d973867dc0 | ||
|
|
ac5a17da2e | ||
|
|
f4bbf8e1d0 | ||
|
|
95a0000446 | ||
|
|
b2a09b9c4e | ||
|
|
3695d5793d | ||
|
|
9674168572 | ||
|
|
80aec02fd7 | ||
|
|
637cd6e405 | ||
|
|
9430d56d78 | ||
|
|
a4e71b8df7 | ||
|
|
f20955901a | ||
|
|
54c36d2a55 | ||
|
|
ea7cc64911 | ||
|
|
22947da46b | ||
|
|
2928b9331a | ||
|
|
fdd84a2290 | ||
|
|
8c1ad6f0c5 | ||
|
|
a5cb7c26c5 | ||
|
|
f8b5ef03d5 | ||
|
|
e80f03c5d8 | ||
|
|
c1d61802cd | ||
|
|
ca8631c172 | ||
|
|
518d16b9cb | ||
|
|
7679fc0f4f | ||
|
|
c5ef72d060 | ||
|
|
cbab4d6a14 | ||
|
|
5733eced42 | ||
|
|
0917109c50 | ||
|
|
615eb926f4 | ||
|
|
d734ab38fa | ||
|
|
0446f5b4b5 |
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -7,7 +7,3 @@ updates:
|
||||
labels:
|
||||
- "CI"
|
||||
target-branch: "2.20.x"
|
||||
groups:
|
||||
doctrine:
|
||||
patterns:
|
||||
- "doctrine/*"
|
||||
|
||||
2
.github/workflows/coding-standards.yml
vendored
2
.github/workflows/coding-standards.yml
vendored
@@ -24,4 +24,4 @@ on:
|
||||
|
||||
jobs:
|
||||
coding-standards:
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@14.0.0"
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@13.1.0"
|
||||
|
||||
2
.github/workflows/composer-lint.yml
vendored
2
.github/workflows/composer-lint.yml
vendored
@@ -17,4 +17,4 @@ on:
|
||||
jobs:
|
||||
composer-lint:
|
||||
name: "Composer Lint"
|
||||
uses: "doctrine/.github/.github/workflows/composer-lint.yml@14.0.0"
|
||||
uses: "doctrine/.github/.github/workflows/composer-lint.yml@13.1.0"
|
||||
|
||||
56
.github/workflows/continuous-integration.yml
vendored
56
.github/workflows/continuous-integration.yml
vendored
@@ -38,14 +38,10 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.1"
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3.7"
|
||||
extension:
|
||||
- "sqlite3"
|
||||
- "pdo_sqlite"
|
||||
@@ -53,37 +49,30 @@ jobs:
|
||||
- "highest"
|
||||
stability:
|
||||
- "stable"
|
||||
native_lazy:
|
||||
- "0"
|
||||
include:
|
||||
- php-version: "8.2"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "4@dev"
|
||||
extension: "pdo_sqlite"
|
||||
stability: "stable"
|
||||
native_lazy: "0"
|
||||
- php-version: "8.2"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "4@dev"
|
||||
extension: "sqlite3"
|
||||
stability: "stable"
|
||||
native_lazy: "0"
|
||||
- php-version: "8.1"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "default"
|
||||
deps: "lowest"
|
||||
extension: "pdo_sqlite"
|
||||
stability: "stable"
|
||||
native_lazy: "0"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "default"
|
||||
deps: "highest"
|
||||
extension: "pdo_sqlite"
|
||||
stability: "stable"
|
||||
native_lazy: "1"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "default"
|
||||
deps: "highest"
|
||||
extension: "sqlite3"
|
||||
stability: "dev"
|
||||
native_lazy: "1"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
@@ -111,12 +100,8 @@ jobs:
|
||||
run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update"
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Downgrade VarExporter"
|
||||
run: 'composer require --no-update "symfony/var-exporter:^6.4 || ^7.4"'
|
||||
if: "${{ matrix.native_lazy == '0' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v4"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
dependency-versions: "${{ matrix.deps }}"
|
||||
@@ -125,7 +110,6 @@ jobs:
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage-no-cache.xml"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 0
|
||||
ENABLE_NATIVE_LAZY_OBJECTS: ${{ matrix.native_lazy }}
|
||||
|
||||
- name: "Run PHPUnit with Second Level Cache and PHPUnit 10"
|
||||
run: |
|
||||
@@ -147,12 +131,11 @@ jobs:
|
||||
if: "${{ matrix.php-version != '8.1' }}"
|
||||
env:
|
||||
ENABLE_SECOND_LEVEL_CACHE: 1
|
||||
ENABLE_NATIVE_LAZY_OBJECTS: ${{ matrix.native_lazy }}
|
||||
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v7"
|
||||
with:
|
||||
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.deps }}-${{ matrix.stability }}-${{ matrix.native_lazy }}-coverage"
|
||||
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.deps }}-${{ matrix.stability }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
|
||||
|
||||
@@ -204,26 +187,23 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3.7"
|
||||
postgres-version:
|
||||
- "17"
|
||||
extension:
|
||||
- pdo_pgsql
|
||||
- pgsql
|
||||
include:
|
||||
- php-version: "8.2"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "4@dev"
|
||||
postgres-version: "14"
|
||||
extension: pdo_pgsql
|
||||
- php-version: "8.2"
|
||||
dbal-version: "3.7"
|
||||
postgres-version: "9.6"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "default"
|
||||
postgres-version: "10"
|
||||
extension: pdo_pgsql
|
||||
|
||||
services:
|
||||
@@ -257,7 +237,7 @@ jobs:
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v4"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
@@ -285,13 +265,10 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3.7"
|
||||
- "4@dev"
|
||||
mariadb-version:
|
||||
- "11.4"
|
||||
@@ -331,7 +308,7 @@ jobs:
|
||||
extensions: "${{ matrix.extension }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v4"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
@@ -359,13 +336,10 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "8.5"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "3.7"
|
||||
mysql-version:
|
||||
- "5.7"
|
||||
- "8.0"
|
||||
@@ -373,11 +347,11 @@ jobs:
|
||||
- "mysqli"
|
||||
- "pdo_mysql"
|
||||
include:
|
||||
- php-version: "8.2"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "4@dev"
|
||||
mysql-version: "8.0"
|
||||
extension: "mysqli"
|
||||
- php-version: "8.2"
|
||||
- php-version: "8.4"
|
||||
dbal-version: "4@dev"
|
||||
mysql-version: "8.0"
|
||||
extension: "pdo_mysql"
|
||||
@@ -413,7 +387,7 @@ jobs:
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v4"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
with:
|
||||
composer-options: "--ignore-platform-req=php+"
|
||||
|
||||
@@ -470,7 +444,7 @@ jobs:
|
||||
path: "reports"
|
||||
|
||||
- name: "Upload to Codecov"
|
||||
uses: "codecov/codecov-action@v6"
|
||||
uses: "codecov/codecov-action@v5"
|
||||
with:
|
||||
directory: reports
|
||||
env:
|
||||
|
||||
2
.github/workflows/documentation.yml
vendored
2
.github/workflows/documentation.yml
vendored
@@ -17,4 +17,4 @@ on:
|
||||
jobs:
|
||||
documentation:
|
||||
name: "Documentation"
|
||||
uses: "doctrine/.github/.github/workflows/documentation.yml@14.0.0"
|
||||
uses: "doctrine/.github/.github/workflows/documentation.yml@13.1.0"
|
||||
|
||||
4
.github/workflows/phpbench.yml
vendored
4
.github/workflows/phpbench.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.1"
|
||||
- "8.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
ini-values: "zend.assertions=1, apc.enable_cli=1"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v4"
|
||||
uses: "ramsey/composer-install@v3"
|
||||
|
||||
- name: "Run PHPBench"
|
||||
run: "vendor/bin/phpbench run --report=default"
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@14.0.0"
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@13.1.0"
|
||||
secrets:
|
||||
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
|
||||
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}
|
||||
|
||||
17
.github/workflows/static-analysis.yml
vendored
17
.github/workflows/static-analysis.yml
vendored
@@ -25,14 +25,6 @@ jobs:
|
||||
name: Static Analysis with PHPStan
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- dbal-version: default
|
||||
config: phpstan.neon
|
||||
- dbal-version: 3.8.2
|
||||
config: phpstan-dbal3.neon
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v6"
|
||||
@@ -44,13 +36,8 @@ jobs:
|
||||
php-version: "8.4"
|
||||
tools: cs2pr
|
||||
|
||||
- name: Require specific DBAL version
|
||||
run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update"
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
|
||||
- name: Install dependencies with Composer
|
||||
uses: ramsey/composer-install@v4
|
||||
uses: ramsey/composer-install@v2
|
||||
|
||||
- name: Run static analysis with phpstan/phpstan
|
||||
run: "vendor/bin/phpstan analyse -c ${{ matrix.config }} --error-format=checkstyle | cs2pr"
|
||||
run: "vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
| [![Build status][4.0 image]][4.0 workflow] | [![Build status][3.7 image]][3.7 workflow] | [![Build status][3.6 image]][3.6 workflow] | [![Build status][2.21 image]][2.21 workflow] | [![Build status][2.20 image]][2.20 workflow] |
|
||||
| [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.7 coverage image]][3.7 coverage] | [![Coverage Status][3.6 coverage image]][3.6 coverage] | [![Coverage Status][2.21 coverage image]][2.21 coverage] | [![Coverage Status][2.20 coverage image]][2.20 coverage] |
|
||||
|
||||
Doctrine ORM is an object-relational mapper for PHP 8.1+ that provides transparent persistence
|
||||
Doctrine ORM is an object-relational mapper for PHP 8.4+ that provides transparent persistence
|
||||
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
|
||||
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
|
||||
inspired by Hibernate's HQL. This provides developers with a powerful alternative to SQL that maintains flexibility
|
||||
|
||||
177
UPGRADE.md
177
UPGRADE.md
@@ -6,6 +6,145 @@ awareness about deprecated code.
|
||||
- Use of our low-overhead runtime deprecation API, details:
|
||||
https://github.com/doctrine/deprecations/
|
||||
|
||||
# Upgrade to 4.0
|
||||
|
||||
## BC BREAK: EventManager to EventManagerInterface migration
|
||||
|
||||
The following methods used to return an instance of `Doctrine\Common\EventManager`,
|
||||
and now return an instance of `Doctrine\Common\EventManagerInterface`:
|
||||
|
||||
- `Doctrine\ORM\EntityManager::getEventManager()`
|
||||
- `Doctrine\ORM\EntityManagerDecorator::getEventManager()`
|
||||
- `Doctrine\ORM\EntityManagerInterface::getEventManager()`
|
||||
|
||||
## BC BREAK: Remove `FieldMapping::$default`
|
||||
|
||||
The `default` property of `Doctrine\ORM\Mapping\FieldMapping` has been removed.
|
||||
Use `FieldMapping::$options['default']` instead.
|
||||
|
||||
## BC BREAK: throw on `nullable` on columns that end up being used in a primary key
|
||||
|
||||
Specifying `nullable` on join columns that are part of a primary key is
|
||||
an error and will cause an exception to be thrown.
|
||||
|
||||
## BC BREAK: `Doctrine\ORM\Mapping\LegacyReflectionFields` is removed
|
||||
|
||||
The `Doctrine\ORM\Mapping\LegacyReflectionFields` class has been removed.
|
||||
Also, `Doctrine\ORM\Mapping\ClassMetadata::$reflFields` has been removed, as
|
||||
well as methods depending on it.
|
||||
|
||||
## BC BREAK: Userland lazy objects are no longer supported
|
||||
|
||||
Userland lazy objects are no longer supported.
|
||||
[Native lazy objects](https://www.php.net/manual/en/language.oop5.lazy-objects.php)
|
||||
are used instead.
|
||||
|
||||
## BC BREAK: type declarations on constants
|
||||
|
||||
All constants in the ORM now have type declarations and are final. You may no
|
||||
longer override them in extending types.
|
||||
|
||||
## Remove methods for configuring no longer configurable features
|
||||
|
||||
Since 3.0, lazy ghosts are enabled unconditionally, and so is rejecting ID
|
||||
collisions in the identity map.
|
||||
|
||||
As a consequence, the following methods are removed:
|
||||
|
||||
* `Doctrine\ORM\Configuration::setLazyGhostObjectEnabled()`
|
||||
* `Doctrine\ORM\Configuration::isLazyGhostObjectEnabled()`
|
||||
* `Doctrine\ORM\Configuration::setRejectIdCollisionInIdentityMap()`
|
||||
* `Doctrine\ORM\Configuration::isRejectIdCollisionInIdentityMapEnabled()`
|
||||
|
||||
## BC BREAK: New argument to `NewObjectExpression::dispatch()`
|
||||
|
||||
```diff
|
||||
<?php
|
||||
class NewObjectExpression extends Node
|
||||
{
|
||||
// …
|
||||
|
||||
- public function dispatch(SqlWalker $walker): string
|
||||
+ public function dispatch(SqlWalker $walker, string|null $parentAlias = null): string
|
||||
{
|
||||
// …
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## BC BREAK: New argument to `AbstractEntityPersister::buildCollectionCacheKey()`
|
||||
|
||||
|
||||
```diff
|
||||
<?php
|
||||
abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
{
|
||||
// …
|
||||
|
||||
protected function buildCollectionCacheKey(
|
||||
AssociationMapping $association,
|
||||
array $ownerId,
|
||||
+ string $filterHash
|
||||
): CollectionCacheKey {
|
||||
// …
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Require implementation of `OutputWalker`, remove `SqlWalker::getExecutor()`
|
||||
|
||||
The `SqlWalker::getExecutor()` method is removed. Output walkers should
|
||||
implement the `\Doctrine\ORM\Query\OutputWalker` interface and create
|
||||
`Doctrine\ORM\Query\Exec\SqlFinalizer` instances.
|
||||
|
||||
## Remove `DatabaseDriver`
|
||||
|
||||
The class `Doctrine\ORM\Mapping\Driver\DatabaseDriver` is removed.
|
||||
|
||||
## Remove the `NotSupported` exception
|
||||
|
||||
The class `Doctrine\ORM\Exception\NotSupported` has been removed without replacement.
|
||||
|
||||
## Remove remaining `Serializable` implementation
|
||||
|
||||
`SequenceGenerator` does not implement the `Serializable` interface anymore.
|
||||
|
||||
The following methods have been removed:
|
||||
|
||||
* `SequenceGenerator::serialize()`
|
||||
* `SequenceGenerator::unserialize()`
|
||||
|
||||
## Remove `orm:schema-tool:update` option `--complete`
|
||||
|
||||
That option was a no-op.
|
||||
|
||||
## Remove `Doctrine\ORM\Mapping\ReflectionEnumProperty`
|
||||
|
||||
This class has been removed.
|
||||
Instead, use `Doctrine\Persistence\Reflection\EnumReflectionProperty` from
|
||||
`doctrine/persistence`.
|
||||
|
||||
## Forbid passing null to `ClassMetadata::fullyQualifiedClassName()`
|
||||
|
||||
Passing `null` to `Doctrine\ORM\ClassMetadata::fullyQualifiedClassName()` is
|
||||
no longer possible.
|
||||
|
||||
## Remove array access
|
||||
|
||||
Using array access on instances of the following classes is no longer possible:
|
||||
|
||||
- `Doctrine\ORM\Mapping\DiscriminatorColumnMapping`
|
||||
- `Doctrine\ORM\Mapping\EmbedClassMapping`
|
||||
- `Doctrine\ORM\Mapping\FieldMapping`
|
||||
- `Doctrine\ORM\Mapping\JoinColumnMapping`
|
||||
- `Doctrine\ORM\Mapping\JoinTableMapping`
|
||||
|
||||
## Remove properties `$indexes` and `$uniqueConstraints` from `Doctrine\ORM\Mapping\Table`
|
||||
|
||||
The properties `$indexes` and `$uniqueConstraints` have been removed since they had no effect at all.
|
||||
The preferred way of defining indices and unique constraints is by
|
||||
using the `\Doctrine\ORM\Mapping\UniqueConstraint` and `\Doctrine\ORM\Mapping\Index` attributes.
|
||||
|
||||
# Upgrade to 3.x General Notes
|
||||
|
||||
We recommend you upgrade to DBAL 3 first before upgrading to ORM 3. See
|
||||
@@ -26,6 +165,44 @@ to run with ORM 3.
|
||||
At this point, we recommend upgrading to PHP 8.4 first and then directly from
|
||||
ORM 2.19 to 3.5 and up so that you can skip the lazy ghost proxy generation
|
||||
and directly start using native lazy objects.
|
||||
>>>>>>> origin/3.6.x
|
||||
|
||||
# Upgrade to 3.7
|
||||
|
||||
## Conditional breaking changes
|
||||
|
||||
3.7 adds support for `doctrine/collections` 3. If you upgrade to that version
|
||||
of `doctrine/collections`, there are breaking changes in `doctrine/orm` as well,
|
||||
because of cross-package inheritance and type declarations.
|
||||
|
||||
Most notably, `Doctrine\ORM\PersistentCollection::add` no longer returns a boolean:
|
||||
|
||||
```diff
|
||||
- public function add(mixed $value): bool
|
||||
+ public function add(mixed $value): void
|
||||
```
|
||||
|
||||
That method always returned `true`, so you can safely stop using the return
|
||||
value before upgrading.
|
||||
|
||||
Also, if you extend `Doctrine\ORM\Persisters\SqlValueVisitor`, you need to
|
||||
ensure the following methods have a return type in your subclasses:
|
||||
|
||||
- `walkComparison()`
|
||||
- `walkCompositeExpression()`
|
||||
- `walkValue()`
|
||||
|
||||
## Deprecate `EventManager` return type in `EntityManager` methods
|
||||
|
||||
The return type of the following methods has been changed from
|
||||
`Doctrine\Common\EventManager` to `Doctrine\Common\EventManagerInterface`:
|
||||
|
||||
- `Doctrine\ORM\Decorator\EntityManagerDecorator::getEventManager()`
|
||||
- `Doctrine\ORM\EntityManager::getEventManager()`
|
||||
- `Doctrine\ORM\EntityManagerInterface::getEventManager()`
|
||||
|
||||
All three methods continue to return an instance of `EventManager`, however
|
||||
relying on that is deprecated and will no longer be the guaranteed in 4.0.
|
||||
|
||||
# Upgrade to 3.6
|
||||
|
||||
|
||||
@@ -31,20 +31,19 @@
|
||||
],
|
||||
"homepage": "https://www.doctrine-project.org/projects/orm.html",
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"php": "^8.4",
|
||||
"ext-ctype": "*",
|
||||
"composer-runtime-api": "^2",
|
||||
"doctrine/collections": "^2.2",
|
||||
"doctrine/dbal": "^3.8.2 || ^4",
|
||||
"doctrine/deprecations": "^0.5.3 || ^1",
|
||||
"doctrine/event-manager": "^1.2 || ^2",
|
||||
"doctrine/collections": "^3",
|
||||
"doctrine/dbal": "^4.2.1",
|
||||
"doctrine/deprecations": "^1.1",
|
||||
"doctrine/event-manager": "^2.1.1",
|
||||
"doctrine/inflector": "^1.4 || ^2.0",
|
||||
"doctrine/instantiator": "^1.3 || ^2",
|
||||
"doctrine/lexer": "^3",
|
||||
"doctrine/persistence": "^3.3.1 || ^4",
|
||||
"doctrine/persistence": "^4",
|
||||
"psr/cache": "^1 || ^2 || ^3",
|
||||
"symfony/console": "^5.4 || ^6.0 || ^7.0 || ^8.0",
|
||||
"symfony/var-exporter": "^6.3.9 || ^7.0 || ^8.0"
|
||||
"symfony/console": "^5.4 || ^6.0 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^14.0",
|
||||
@@ -52,9 +51,10 @@
|
||||
"phpstan/extension-installer": "^1.4",
|
||||
"phpstan/phpstan": "2.1.23",
|
||||
"phpstan/phpstan-deprecation-rules": "^2",
|
||||
"phpunit/phpunit": "^10.5.0 || ^11.5",
|
||||
"phpunit/phpunit": "^11.5.42",
|
||||
"psr/log": "^1 || ^2 || ^3",
|
||||
"symfony/cache": "^5.4 || ^6.2 || ^7.0 || ^8.0"
|
||||
"symfony/cache": "^5.4 || ^6.2 || ^7.0 || ^8.0",
|
||||
"symfony/var-exporter": "^6.3.9 || ^7.0 || ^8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-dom": "Provides support for XSD validation for XML mapping files",
|
||||
|
||||
@@ -33,18 +33,7 @@ steps of configuration.
|
||||
$config->setMetadataDriverImpl($driverImpl);
|
||||
$config->setQueryCache($queryCache);
|
||||
|
||||
if (PHP_VERSION_ID > 80400) {
|
||||
$config->enableNativeLazyObjects(true);
|
||||
} else {
|
||||
$config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies');
|
||||
$config->setProxyNamespace('MyProject\Proxies');
|
||||
|
||||
if ($applicationMode === "development") {
|
||||
$config->setAutoGenerateProxyClasses(true);
|
||||
} else {
|
||||
$config->setAutoGenerateProxyClasses(false);
|
||||
}
|
||||
}
|
||||
$config->enableNativeLazyObjects(true);
|
||||
|
||||
$connection = DriverManager::getConnection([
|
||||
'driver' => 'pdo_sqlite',
|
||||
@@ -76,55 +65,6 @@ Configuration Options
|
||||
The following sections describe all the configuration options
|
||||
available on a ``Doctrine\ORM\Configuration`` instance.
|
||||
|
||||
.. _reference-native-lazy-objects:
|
||||
|
||||
Native Lazy Objects (**OPTIONAL**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
With PHP 8.4 we recommend that you use native lazy objects instead of
|
||||
the code generation approach using the ``symfony/var-exporter`` Ghost trait.
|
||||
|
||||
With Doctrine 4, the minimal requirement will become PHP 8.4 and native lazy objects
|
||||
will become the only approach to lazy loading.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->enableNativeLazyObjects(true);
|
||||
|
||||
Proxy Directory
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Required except if you use native lazy objects with PHP 8.4.
|
||||
This setting will be removed in the future.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setProxyDir($dir);
|
||||
$config->getProxyDir();
|
||||
|
||||
Gets or sets the directory where Doctrine generates any proxy
|
||||
classes. For a detailed explanation on proxy classes and how they
|
||||
are used in Doctrine, refer to the "Proxy Objects" section further
|
||||
down.
|
||||
|
||||
Proxy Namespace
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Required except if you use native lazy objects with PHP 8.4.
|
||||
This setting will be removed in the future.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setProxyNamespace($namespace);
|
||||
$config->getProxyNamespace();
|
||||
|
||||
Gets or sets the namespace to use for generated proxy classes. For
|
||||
a detailed explanation on proxy classes and how they are used in
|
||||
Doctrine, refer to the "Proxy Objects" section further down.
|
||||
|
||||
Metadata Driver (**REQUIRED**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -268,63 +208,6 @@ Gets or sets the logger to use for logging all SQL statements
|
||||
executed by Doctrine. The logger class must implement the
|
||||
deprecated ``Doctrine\DBAL\Logging\SQLLogger`` interface.
|
||||
|
||||
Auto-generating Proxy Classes (**OPTIONAL**)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This setting is not required if you use native lazy objects with PHP 8.4
|
||||
and will be removed in the future.
|
||||
|
||||
Proxy classes can either be generated manually through the Doctrine
|
||||
Console or automatically at runtime by Doctrine. The configuration
|
||||
option that controls this behavior is:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setAutoGenerateProxyClasses($mode);
|
||||
|
||||
Possible values for ``$mode`` are:
|
||||
|
||||
- ``Doctrine\ORM\Proxy\ProxyFactory::AUTOGENERATE_NEVER``
|
||||
|
||||
Never autogenerate a proxy. You will need to generate the proxies
|
||||
manually, for this use the Doctrine Console like so:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine orm:generate-proxies
|
||||
|
||||
When you do this in a development environment,
|
||||
be aware that you may get class/file not found errors if certain proxies
|
||||
are not yet generated. You may also get failing lazy-loads if new
|
||||
methods were added to the entity class that are not yet in the proxy class.
|
||||
In such a case, simply use the Doctrine Console to (re)generate the
|
||||
proxy classes.
|
||||
|
||||
- ``Doctrine\ORM\Proxy\ProxyFactory::AUTOGENERATE_ALWAYS``
|
||||
|
||||
Always generates a new proxy in every request and writes it to disk.
|
||||
|
||||
- ``Doctrine\ORM\Proxy\ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS``
|
||||
|
||||
Generate 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.
|
||||
|
||||
- ``Doctrine\ORM\Proxy\ProxyFactory::AUTOGENERATE_EVAL``
|
||||
|
||||
Generate the proxy classes and evaluate them on the fly via eval(),
|
||||
avoiding writing the proxies to disk.
|
||||
This strategy is only sane for development.
|
||||
|
||||
In a production environment, it is highly recommended to use
|
||||
AUTOGENERATE_NEVER to allow for optimal performances. The other
|
||||
options are interesting in development environment.
|
||||
|
||||
``setAutoGenerateProxyClasses`` can accept a boolean
|
||||
value. This is still possible, ``FALSE`` being equivalent to
|
||||
AUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS.
|
||||
|
||||
Development vs Production Configuration
|
||||
---------------------------------------
|
||||
|
||||
@@ -440,55 +323,6 @@ transparently initialize itself on first access.
|
||||
This will override the 'fetch' option specified in the mapping for
|
||||
that association, but only for that query.
|
||||
|
||||
|
||||
Generating Proxy classes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In a production environment, it is highly recommended to use
|
||||
``AUTOGENERATE_NEVER`` to allow for optimal performances.
|
||||
However you will be required to generate the proxies manually
|
||||
using the Doctrine Console:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$ ./doctrine orm:generate-proxies
|
||||
|
||||
The other options are interesting in development environment:
|
||||
|
||||
- ``AUTOGENERATE_ALWAYS`` will require you to create and configure
|
||||
a proxy directory. Proxies will be generated and written to file
|
||||
on each request, so any modification to your code will be acknowledged.
|
||||
|
||||
- ``AUTOGENERATE_FILE_NOT_EXISTS`` will not overwrite an existing
|
||||
proxy file. If your code changes, you will need to regenerate the
|
||||
proxies manually.
|
||||
|
||||
- ``AUTOGENERATE_EVAL`` will regenerate each proxy on each request,
|
||||
but without writing them to disk.
|
||||
|
||||
Autoloading Proxies
|
||||
-------------------
|
||||
|
||||
When you deserialize proxy objects from the session or any other storage
|
||||
it is necessary to have an autoloading mechanism in place for these classes.
|
||||
For implementation reasons Proxy class names are not PSR-0 compliant. This
|
||||
means that you have to register a special autoloader for these classes:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Proxy\Autoloader;
|
||||
|
||||
$proxyDir = "/path/to/proxies";
|
||||
$proxyNamespace = "MyProxies";
|
||||
|
||||
Autoloader::register($proxyDir, $proxyNamespace);
|
||||
|
||||
If you want to execute additional logic to intercept the proxy file not found
|
||||
state you can pass a closure as the third argument. It will be called with
|
||||
the arguments proxydir, namespace and className when the proxy file could not
|
||||
be found.
|
||||
|
||||
Multiple Metadata Sources
|
||||
-------------------------
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ well.
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Doctrine ORM requires a minimum of PHP 8.1. For greatly improved
|
||||
Doctrine ORM requires a minimum of PHP 8.4. For greatly improved
|
||||
performance it is also recommended that you use APC with PHP.
|
||||
|
||||
Doctrine ORM Packages
|
||||
@@ -79,9 +79,8 @@ Entities
|
||||
An entity is a lightweight, persistent domain object. An entity can
|
||||
be any regular PHP class observing the following restrictions:
|
||||
|
||||
- An entity class can be final or read-only when
|
||||
you use :ref:`native lazy objects <reference-native-lazy-objects>`.
|
||||
It may contain final methods or read-only properties too.
|
||||
- An entity class can be final or read-only. It may contain final
|
||||
methods or read-only properties too.
|
||||
- Any two entity classes in a class hierarchy that inherit
|
||||
directly or indirectly from one another must not have a mapped
|
||||
property with the same name. That is, if B inherits from A then B
|
||||
|
||||
@@ -168,7 +168,7 @@ Here is a complete list of ``Column``s attributes (all optional):
|
||||
- ``insertable`` (default: ``true``): Whether the column should be inserted.
|
||||
- ``updatable`` (default: ``true``): Whether the column should be updated.
|
||||
- ``generated`` (default: ``null``): Whether the generated strategy should be ``'NEVER'``, ``'INSERT'`` and ``ALWAYS``.
|
||||
- ``enumType`` (requires PHP 8.1 and ``doctrine/orm`` 2.11): The PHP enum class name to convert the database value into. See :ref:`reference-enum-mapping`.
|
||||
- ``enumType`` (requires PHP 8.1 and ``doctrine/orm`` 2.11): The PHP enum class name to convert the database value into.
|
||||
- ``precision`` (default: 0): The precision for a decimal (exact numeric) column
|
||||
(applies only for decimal column),
|
||||
which is the maximum number of digits that are stored for the values.
|
||||
@@ -321,160 +321,6 @@ that value and raw value are different, you have to use the raw value representa
|
||||
$messageRepository = $entityManager->getRepository(Message::class);
|
||||
$deMessages = $messageRepository->findBy(['language' => 'de']); // Use lower case here for raw value representation
|
||||
|
||||
.. _reference-enum-mapping:
|
||||
|
||||
Mapping PHP Enums
|
||||
-----------------
|
||||
|
||||
.. versionadded:: 2.11
|
||||
|
||||
Doctrine natively supports mapping PHP backed enums to database columns.
|
||||
A backed enum is a PHP enum that the same scalar type (``string`` or ``int``)
|
||||
assigned to each case. Doctrine stores the scalar value in the database and
|
||||
converts it back to the enum instance when hydrating the entity.
|
||||
|
||||
Using ``enumType`` provides three main benefits:
|
||||
|
||||
- **Automatic conversion**: Doctrine handles the conversion in both directions
|
||||
transparently. When loading an entity, scalar values from the database are
|
||||
converted into enum instances. When persisting, enum instances are reduced
|
||||
to their scalar ``->value`` before being sent to the database.
|
||||
- **Type-safety**: Entity properties contain enum instances directly. Your
|
||||
getters return ``Suit`` instead of ``string``, removing the need to call
|
||||
``Suit::from()`` manually.
|
||||
- **Validation**: When a database value does not match any enum case, Doctrine
|
||||
throws a ``MappingException`` during hydration instead of silently returning
|
||||
an invalid value.
|
||||
|
||||
This feature works with all database platforms supported by Doctrine (MySQL,
|
||||
PostgreSQL, SQLite, etc.) as it relies on standard column types (``string``,
|
||||
``integer``, ``json``, ``simple_array``) rather than any vendor-specific enum
|
||||
type.
|
||||
|
||||
.. note::
|
||||
|
||||
This is unrelated to the MySQL-specific ``ENUM`` column type covered in
|
||||
:doc:`the MySQL Enums cookbook entry </cookbook/mysql-enums>`.
|
||||
|
||||
Defining an Enum
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. literalinclude:: basic-mapping/Suit.php
|
||||
:language: php
|
||||
|
||||
Only backed enums (``string`` or ``int``) are supported. Unit enums (without
|
||||
a scalar value) cannot be mapped.
|
||||
|
||||
Single-Value Columns
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Use the ``enumType`` option on ``#[Column]`` to map a property to a backed enum.
|
||||
The underlying database column stores the enum's scalar value (``string`` or ``int``).
|
||||
|
||||
.. literalinclude:: basic-mapping/EnumMapping.php
|
||||
:language: php
|
||||
|
||||
When the PHP property is typed with the enum class, Doctrine automatically
|
||||
infers the appropriate column type (``string`` for string-backed enums,
|
||||
``integer`` for int-backed enums) and sets ``enumType``. You can also specify
|
||||
the column ``type`` explicitly.
|
||||
|
||||
Storing Collections of Enums
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can store multiple enum values in a single column by combining ``enumType``
|
||||
with a collection column type: ``json`` or ``simple_array``.
|
||||
|
||||
.. note::
|
||||
|
||||
Automatic type inference does not apply to collection columns. When the
|
||||
PHP property is typed as ``array``, Doctrine cannot detect the enum class.
|
||||
You must specify both ``type`` and ``enumType`` explicitly.
|
||||
|
||||
.. literalinclude:: basic-mapping/EnumCollectionMapping.php
|
||||
:language: php
|
||||
|
||||
With ``json``, the values are stored as a JSON array (e.g. ``["hearts","spades"]``).
|
||||
With ``simple_array``, the values are stored as a comma-separated string
|
||||
(e.g. ``hearts,spades``).
|
||||
|
||||
In both cases, Doctrine converts each element to and from the enum
|
||||
automatically during hydration and persistence.
|
||||
|
||||
.. tip::
|
||||
|
||||
Use ``json`` when enum values may contain commas, when you need to store
|
||||
int-backed enums (as it preserves value types), when the column also
|
||||
stores complex/nested data structures, or when you want to query individual
|
||||
values using database-native JSON operators (e.g. PostgreSQL ``jsonb``).
|
||||
Prefer ``simple_array`` for a compact, human-readable storage of
|
||||
string-backed enums whose values do not contain commas.
|
||||
|
||||
+-------------------+-----------------------------+-------------------------------+
|
||||
| Column type | Database storage | PHP type |
|
||||
+===================+=============================+===============================+
|
||||
| ``string`` | ``hearts`` | ``Suit`` |
|
||||
+-------------------+-----------------------------+-------------------------------+
|
||||
| ``integer`` | ``1`` | ``Priority`` |
|
||||
+-------------------+-----------------------------+-------------------------------+
|
||||
| ``json`` | ``["hearts","spades"]`` | ``array<Suit>`` |
|
||||
+-------------------+-----------------------------+-------------------------------+
|
||||
| ``simple_array`` | ``hearts,spades`` | ``array<Suit>`` |
|
||||
+-------------------+-----------------------------+-------------------------------+
|
||||
|
||||
Nullable Enums
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Enum columns can be nullable. When the database value is ``NULL``, Doctrine
|
||||
preserves it as ``null`` without triggering any validation error.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
#[ORM\Column(type: 'string', nullable: true, enumType: Suit::class)]
|
||||
private Suit|null $suit = null;
|
||||
|
||||
Default Values
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
You can specify a database-level default using an enum case directly in the
|
||||
column options:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
#[ORM\Column(options: ['default' => Suit::Hearts])]
|
||||
public Suit $suit;
|
||||
|
||||
Using Enums in Queries
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Enum instances can be used directly as parameters in DQL, QueryBuilder, and
|
||||
repository methods. Doctrine converts them to their scalar value automatically.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// QueryBuilder
|
||||
$qb = $em->createQueryBuilder();
|
||||
$qb->select('c')
|
||||
->from(Card::class, 'c')
|
||||
->where('c.suit = :suit')
|
||||
->setParameter('suit', Suit::Clubs);
|
||||
|
||||
// Repository
|
||||
$cards = $em->getRepository(Card::class)->findBy(['suit' => Suit::Clubs]);
|
||||
|
||||
XML Mapping
|
||||
~~~~~~~~~~~
|
||||
|
||||
When using XML mapping, the ``enum-type`` attribute is used on ``<field>``
|
||||
elements:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<field name="suit" type="string" enum-type="App\Entity\Suit" />
|
||||
|
||||
.. _reference-mapping-types:
|
||||
|
||||
Doctrine Mapping Types
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity]
|
||||
class Player
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private int $id;
|
||||
|
||||
/** @var list<Suit> */
|
||||
#[ORM\Column(type: 'json', enumType: Suit::class)]
|
||||
private array $favouriteSuits = [];
|
||||
|
||||
/** @var list<Suit> */
|
||||
#[ORM\Column(type: 'simple_array', enumType: Suit::class)]
|
||||
private array $allowedSuits = [];
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity]
|
||||
class Card
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private int $id;
|
||||
|
||||
#[ORM\Column(enumType: Suit::class)]
|
||||
private Suit $suit;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
enum Suit: string
|
||||
{
|
||||
case Hearts = 'hearts';
|
||||
case Diamonds = 'diamonds';
|
||||
case Clubs = 'clubs';
|
||||
case Spades = 'spades';
|
||||
}
|
||||
@@ -83,8 +83,6 @@ The following Commands are currently available:
|
||||
cache drivers.
|
||||
- ``orm:clear-cache:result`` Clear result cache of the various
|
||||
cache drivers.
|
||||
- ``orm:generate-proxies`` Generates proxy classes for entity
|
||||
classes. Deprecated in favor of using native lazy objects.
|
||||
- ``orm:run-dql`` Executes arbitrary DQL directly from the command
|
||||
line.
|
||||
- ``orm:schema-tool:create`` Processes the schema and either
|
||||
|
||||
@@ -49,9 +49,7 @@ An entity contains persistable properties. A persistable property
|
||||
is an instance variable of the entity that is saved into and retrieved from the database
|
||||
by Doctrine's data mapping capabilities.
|
||||
|
||||
An entity class can be final or read-only when you use
|
||||
:ref:`native lazy objects <reference-native-lazy-objects>`.
|
||||
It may contain final methods or read-only properties too.
|
||||
An entity class can be final or read-only. It may contain final methods or read-only properties too.
|
||||
|
||||
An Example Model: Bug Tracker
|
||||
-----------------------------
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<!-- Ignore warnings, show progress of the run and show sniff names -->
|
||||
<arg value="nps"/>
|
||||
|
||||
<config name="php_version" value="80100"/>
|
||||
<config name="php_version" value="80400"/>
|
||||
|
||||
<file>src</file>
|
||||
<file>tests</file>
|
||||
@@ -50,8 +50,6 @@
|
||||
</rule>
|
||||
|
||||
<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
|
||||
<exclude-pattern>src/Mapping/Driver/LoadMappingFileImplementation.php</exclude-pattern>
|
||||
<exclude-pattern>src/Mapping/GetReflectionClassImplementation.php</exclude-pattern>
|
||||
<exclude-pattern>tests/*</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
@@ -210,12 +208,6 @@
|
||||
<exclude-pattern>tests/Tests/Models/DDC1590/DDC1590User.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
|
||||
<rule ref="Squiz.Classes.ValidClassName.NotCamelCaps">
|
||||
<!-- we need to test what happens with an stdClass proxy -->
|
||||
<exclude-pattern>tests/Tests/Proxy/DefaultProxyClassNameResolverTest.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="Squiz.Commenting.FunctionComment.WrongStyle">
|
||||
<!-- https://github.com/squizlabs/PHP_CodeSniffer/issues/1961 -->
|
||||
<exclude-pattern>tests/Tests/Mocks/DatabasePlatformMock.php</exclude-pattern>
|
||||
|
||||
@@ -942,12 +942,6 @@ parameters:
|
||||
count: 4
|
||||
path: src/Mapping/ClassMetadata.php
|
||||
|
||||
-
|
||||
message: '#^If condition is always true\.$#'
|
||||
identifier: if.alwaysTrue
|
||||
count: 1
|
||||
path: src/Mapping/ClassMetadata.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\ClassMetadata\:\:inlineEmbeddable\(\) has parameter \$embeddable with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -1308,6 +1302,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Mapping/DefaultQuoteStrategy.php
|
||||
|
||||
-
|
||||
message: '#^Expression on left side of \?\? is not nullable\.$#'
|
||||
identifier: nullCoalesce.expr
|
||||
count: 1
|
||||
path: src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\AttributeDriver\:\:isRepeatedPropertyDeclaration\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -1326,96 +1326,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Schema\\\\ForeignKeyConstraint'' and ''getReferencedColumn…'' will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Schema\\\\ForeignKeyConstraint'' and ''getReferencedTableN…'' will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Schema\\\\ForeignKeyConstraint'' and ''getReferencingColum…'' will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Schema\\\\Index'' and ''getIndexedColumns'' will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to function method_exists\(\) with ''Doctrine\\\\DBAL\\\\Schema\\\\Table'' and ''getPrimaryKeyConstr…'' will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to function method_exists\(\) with Doctrine\\DBAL\\Schema\\Index and ''getType'' will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Call to function method_exists\(\) with Doctrine\\DBAL\\Schema\\Table and ''getPrimaryKeyConstr…'' will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 2
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Instanceof between Doctrine\\ORM\\Mapping\\ClassMetadata\<T of object\> and Doctrine\\ORM\\Mapping\\ClassMetadata will always evaluate to true\.$#'
|
||||
identifier: instanceof.alwaysTrue
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:__construct\(\) has parameter \$sm with generic class Doctrine\\DBAL\\Schema\\AbstractSchemaManager but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:buildFieldMappings\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:buildIndexes\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:buildToOneAssociationMappings\(\) has parameter \$metadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:getAssetName\(\) has parameter \$asset with generic class Doctrine\\DBAL\\Schema\\AbstractAsset but does not specify its types\: N$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:getClassNameForTable\(\) should return class\-string but returns string\.$#'
|
||||
identifier: return.type
|
||||
count: 2
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$columnName of method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:getFieldNameForColumn\(\) expects string, string\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 4
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\Driver\\SimplifiedXmlDriver\:\:__construct\(\) has parameter \$fileExtension with no type specified\.$#'
|
||||
identifier: missingType.parameter
|
||||
@@ -1518,18 +1428,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Mapping/JoinTableMapping.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\LegacyReflectionFields\:\:__construct\(\) has parameter \$classMetadata with generic class Doctrine\\ORM\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Mapping/LegacyReflectionFields.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$class of method Doctrine\\Persistence\\Mapping\\ReflectionService\:\:getAccessibleProperty\(\) expects class\-string, string given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Mapping/LegacyReflectionFields.php
|
||||
|
||||
-
|
||||
message: '#^Strict comparison using \!\=\= between array\<string, string\> and null will always evaluate to true\.$#'
|
||||
identifier: notIdentical.alwaysTrue
|
||||
@@ -1602,12 +1500,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Mapping/QuoteStrategy.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\ReflectionEnumProperty\:\:getValue\(\) return type has no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: src/Mapping/ReflectionEnumProperty.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Mapping\\ToOneOwningSideMapping\:\:fromMappingArray\(\) should return static\(Doctrine\\ORM\\Mapping\\ToOneOwningSideMapping\) but returns Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\.$#'
|
||||
identifier: return.type
|
||||
@@ -1681,7 +1573,7 @@ parameters:
|
||||
path: src/PersistentCollection.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:matching\(\) should return Doctrine\\Common\\Collections\\Collection\<TKey of \(int\|string\), T\> but returns Doctrine\\Common\\Collections\\ReadableCollection\<TKey of \(int\|string\), T\>&Doctrine\\Common\\Collections\\Selectable\<TKey of \(int\|string\), T\>\.$#'
|
||||
message: '#^Method Doctrine\\ORM\\PersistentCollection\:\:matching\(\) should return Doctrine\\Common\\Collections\\Collection\<TKey of \(int\|string\), T\> but returns Doctrine\\Common\\Collections\\ReadableCollection\<TKey of \(int\|string\), T\>\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/PersistentCollection.php
|
||||
@@ -1693,7 +1585,7 @@ parameters:
|
||||
path: src/PersistentCollection.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$callback of function array_walk expects callable\(object, int\)\: mixed, array\{Doctrine\\Common\\Collections\\Collection\<TKey of \(int\|string\), T\>&Doctrine\\Common\\Collections\\Selectable\<TKey of \(int\|string\), T\>, ''add''\} given\.$#'
|
||||
message: '#^Parameter \#2 \$callback of function array_walk expects callable\(object, int\)\: mixed, array\{Doctrine\\Common\\Collections\\Collection\<TKey of \(int\|string\), T\>, ''add''\} given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/PersistentCollection.php
|
||||
@@ -1818,6 +1710,12 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Collection/ManyToManyPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:count\(\) should return int\<0, max\> but returns int\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Persisters/Collection/ManyToManyPersister.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Persisters\\Collection\\ManyToManyPersister\:\:delete\(\) has parameter \$collection with generic class Doctrine\\ORM\\PersistentCollection but does not specify its types\: TKey, T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -2106,12 +2004,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$lockMode of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:appendLockHint\(\) expects Doctrine\\DBAL\\LockMode, Doctrine\\DBAL\\LockMode\:\:NONE\|Doctrine\\DBAL\\LockMode\:\:OPTIMISTIC\|Doctrine\\DBAL\\LockMode\:\:PESSIMISTIC_READ\|Doctrine\\DBAL\\LockMode\:\:PESSIMISTIC_WRITE\|int given\.$#'
|
||||
identifier: argument.type
|
||||
count: 2
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$hints of method Doctrine\\ORM\\Internal\\Hydration\\AbstractHydrator\:\:hydrateAll\(\) expects array\<string, string\>, array\<string, Doctrine\\ORM\\PersistentCollection\|true\> given\.$#'
|
||||
identifier: argument.type
|
||||
@@ -2184,12 +2076,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/Entity/JoinedSubclassPersister.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$lockMode of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:appendLockHint\(\) expects Doctrine\\DBAL\\LockMode, Doctrine\\DBAL\\LockMode\:\:NONE\|Doctrine\\DBAL\\LockMode\:\:OPTIMISTIC\|Doctrine\\DBAL\\LockMode\:\:PESSIMISTIC_READ\|Doctrine\\DBAL\\LockMode\:\:PESSIMISTIC_WRITE\|int given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Persisters/Entity/JoinedSubclassPersister.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping\:\:\$joinColumns\.$#'
|
||||
identifier: property.notFound
|
||||
@@ -2214,156 +2100,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Persisters/SqlValueVisitor.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$className of static method Doctrine\\ORM\\Proxy\\Autoloader\:\:resolveFile\(\) expects class\-string, string given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Proxy/Autoloader.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 of closure expects class\-string, string given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Proxy/Autoloader.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver\:\:resolveClassName\(\) should return class\-string\<T of object\> but returns class\-string\<Doctrine\\Persistence\\Proxy\<T of object\>\>\|class\-string\<T of object\>\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Proxy/DefaultProxyClassNameResolver.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\DefaultProxyClassNameResolver\:\:resolveClassName\(\) should return class\-string\<T of object\> but returns string\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Proxy/DefaultProxyClassNameResolver.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$isEmbeddedClass\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$isMappedSuperclass\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined static method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyGhost\(\)\.$#'
|
||||
identifier: staticMethod.notFound
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Call to function is_bool\(\) with bool will always evaluate to true\.$#'
|
||||
identifier: function.alreadyNarrowedType
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Comparison operation "\<" between 0\|1\|2\|3\|4 and 0 is always false\.$#'
|
||||
identifier: smaller.alwaysFalse
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Comparison operation "\>" between 0\|1\|2\|3\|4 and 4 is always false\.$#'
|
||||
identifier: greater.alwaysFalse
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) has Doctrine\\ORM\\EntityNotFoundException in PHPDoc @throws tag but it''s not thrown\.$#'
|
||||
identifier: throws.unusedType
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) has parameter \$classMetadata with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) return type has no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:createLazyInitializer\(\) return type with generic interface Doctrine\\ORM\\Proxy\\InternalProxy does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateProxyClass\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateProxyClasses\(\) has parameter \$classes with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateSerializeImpl\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateUseLazyGhostTrait\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:loadProxyClass\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:skipClass\(\) has parameter \$metadata with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$class of method Doctrine\\ORM\\Utility\\IdentifierFlattener\:\:flattenIdentifier\(\) expects Doctrine\\ORM\\Mapping\\ClassMetadata, Doctrine\\Persistence\\Mapping\\ClassMetadata given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$filename of function filemtime expects string, string\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$length of function substr expects int\|null, int\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$newScope of static method Closure\:\:bind\(\) expects ''static''\|class\-string\|object\|null, string given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Result of \|\| is always false\.$#'
|
||||
identifier: booleanOr.alwaysFalse
|
||||
count: 1
|
||||
path: src/Proxy/ProxyFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Query\:\:processParameterMappings\(\) return type has no value type specified in iterable type array\.$#'
|
||||
identifier: missingType.iterableValue
|
||||
@@ -2484,12 +2220,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Query/AST/Functions/SizeFunction.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#2 \$mode of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:getTrimExpression\(\) expects Doctrine\\DBAL\\Platforms\\TrimMode, Doctrine\\DBAL\\Platforms\\TrimMode\:\:BOTH\|Doctrine\\DBAL\\Platforms\\TrimMode\:\:LEADING\|Doctrine\\DBAL\\Platforms\\TrimMode\:\:TRAILING\|Doctrine\\DBAL\\Platforms\\TrimMode\:\:UNSPECIFIED\|int given\.$#'
|
||||
identifier: argument.type
|
||||
count: 2
|
||||
path: src/Query/AST/Functions/TrimFunction.php
|
||||
|
||||
-
|
||||
message: '#^Call to an undefined method Doctrine\\ORM\\Query\\SqlWalker\:\:walkJoinPathExpression\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
@@ -2556,12 +2286,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Query/Exec/MultiTableUpdateExecutor.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$types of method Doctrine\\DBAL\\Connection\:\:executeStatement\(\) expects array\<int\<0, max\>\|string, Doctrine\\DBAL\\ArrayParameterType\|Doctrine\\DBAL\\ParameterType\|Doctrine\\DBAL\\Types\\Type\|string\>, list\<Doctrine\\DBAL\\ArrayParameterType\:\:ASCII\|Doctrine\\DBAL\\ArrayParameterType\:\:BINARY\|Doctrine\\DBAL\\ArrayParameterType\:\:INTEGER\|Doctrine\\DBAL\\ArrayParameterType\:\:STRING\|Doctrine\\DBAL\\ParameterType\:\:ASCII\|Doctrine\\DBAL\\ParameterType\:\:BINARY\|Doctrine\\DBAL\\ParameterType\:\:BOOLEAN\|Doctrine\\DBAL\\ParameterType\:\:INTEGER\|Doctrine\\DBAL\\ParameterType\:\:LARGE_OBJECT\|Doctrine\\DBAL\\ParameterType\:\:NULL\|Doctrine\\DBAL\\ParameterType\:\:STRING\|Doctrine\\DBAL\\Types\\Type\|int\|string\> given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Query/Exec/MultiTableUpdateExecutor.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$sql of method Doctrine\\DBAL\\Connection\:\:executeQuery\(\) expects string, list\<string\>\|string given\.$#'
|
||||
identifier: argument.type
|
||||
@@ -2586,12 +2310,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Query/Expr/Func.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Query\\ParameterTypeInferer\:\:inferType\(\) never returns int so it can be removed from the return type\.$#'
|
||||
identifier: return.unusedType
|
||||
count: 1
|
||||
path: src/Query/ParameterTypeInferer.php
|
||||
|
||||
-
|
||||
message: '#^@readonly property cannot have a default value\.$#'
|
||||
identifier: property.readOnlyByPhpDocDefaultValue
|
||||
@@ -2826,6 +2544,12 @@ parameters:
|
||||
count: 2
|
||||
path: src/QueryBuilder.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$type of method Doctrine\\ORM\\QueryBuilder\:\:setParameter\(\) expects Doctrine\\DBAL\\ArrayParameterType\|Doctrine\\DBAL\\ParameterType\|string\|null, Doctrine\\DBAL\\ArrayParameterType\|Doctrine\\DBAL\\ParameterType\|int\|string\|null given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/QueryBuilder.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Repository\\DefaultRepositoryFactory\:\:createRepository\(\) return type with generic class Doctrine\\ORM\\EntityRepository does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -2838,24 +2562,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Repository/DefaultRepositoryFactory.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$name\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateProxiesCommand.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$filename of function file_exists expects string, string\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateProxiesCommand.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$filename of function is_writable expects string, string\|false given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/Tools/Console/Command/GenerateProxiesCommand.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Tools\\Console\\Command\\MappingDescribeCommand\:\:getClassMetadata\(\) return type with generic class Doctrine\\ORM\\Mapping\\ClassMetadata does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -3429,12 +3135,6 @@ parameters:
|
||||
count: 2
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#3 \$collection of class Doctrine\\ORM\\PersistentCollection constructor expects Doctrine\\Common\\Collections\\Collection\<\(int\|string\), mixed\>&Doctrine\\Common\\Collections\\Selectable\<\(int\|string\), mixed\>, Doctrine\\Common\\Collections\\Collection given\.$#'
|
||||
identifier: argument.type
|
||||
count: 1
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#5 \$invoke of method Doctrine\\ORM\\Event\\ListenersInvoker\:\:invoke\(\) expects int\<0, 7\>, int\<min, \-1\>\|int\<1, max\> given\.$#'
|
||||
identifier: argument.type
|
||||
@@ -3453,12 +3153,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '#^Unable to resolve the template type T in call to method static method Symfony\\Component\\VarExporter\\Hydrator\:\:hydrate\(\)$#'
|
||||
identifier: argument.templateType
|
||||
count: 1
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$name\.$#'
|
||||
identifier: property.notFound
|
||||
@@ -3548,9 +3242,3 @@ parameters:
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ORM\\Utility\\PersisterHelper\:\:inferParameterTypes\(\) should return list\<Doctrine\\DBAL\\ArrayParameterType\:\:ASCII\|Doctrine\\DBAL\\ArrayParameterType\:\:BINARY\|Doctrine\\DBAL\\ArrayParameterType\:\:INTEGER\|Doctrine\\DBAL\\ArrayParameterType\:\:STRING\|Doctrine\\DBAL\\ParameterType\:\:ASCII\|Doctrine\\DBAL\\ParameterType\:\:BINARY\|Doctrine\\DBAL\\ParameterType\:\:BOOLEAN\|Doctrine\\DBAL\\ParameterType\:\:INTEGER\|Doctrine\\DBAL\\ParameterType\:\:LARGE_OBJECT\|Doctrine\\DBAL\\ParameterType\:\:NULL\|Doctrine\\DBAL\\ParameterType\:\:STRING\|string\> but returns list\<Doctrine\\DBAL\\ArrayParameterType\:\:ASCII\|Doctrine\\DBAL\\ArrayParameterType\:\:BINARY\|Doctrine\\DBAL\\ArrayParameterType\:\:INTEGER\|Doctrine\\DBAL\\ArrayParameterType\:\:STRING\|int\>\.$#'
|
||||
identifier: return.type
|
||||
count: 1
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
includes:
|
||||
- phpstan-baseline.neon
|
||||
- phpstan-params.neon
|
||||
|
||||
parameters:
|
||||
reportUnmatchedIgnoredErrors: false # Some errors in the baseline only apply to DBAL 4
|
||||
ignoreErrors:
|
||||
# Symfony cache supports passing a key prefix to the clear method.
|
||||
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
|
||||
|
||||
# We can be certain that those values are not matched.
|
||||
-
|
||||
message: '~^Match expression does not handle remaining values:~'
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
# DBAL 4 compatibility
|
||||
-
|
||||
message: '~^Method Doctrine\\ORM\\Query\\AST\\Functions\\TrimFunction::getTrimMode\(\) never returns .* so it can be removed from the return type\.$~'
|
||||
path: src/Query/AST/Functions/TrimFunction.php
|
||||
|
||||
-
|
||||
message: '~.*getTrimExpression.*expects int.*~'
|
||||
path: src/Query/AST/Functions/TrimFunction.php
|
||||
|
||||
-
|
||||
message: '~^Call to static method unquoted\(\) on an unknown class Doctrine\\DBAL\\Schema\\Name\\Identifier\.$~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '~^Instantiated class Doctrine\\DBAL\\Schema\\Name\\UnqualifiedName not found\.$~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '~^Call to an undefined method Doctrine\\DBAL\\Schema\\Table::addPrimaryKeyConstraint\(\)\.$~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '~^Call to static method quoted\(\) on an unknown class Doctrine\\DBAL\\Schema\\Name\\Identifier\.$~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '~^Call to an undefined method Doctrine\\DBAL\\Schema\\Table\:\:getObjectName\(\)\.$~'
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '~^Call to an undefined method Doctrine\\DBAL\\Schema\\ForeignKeyConstraint::get.*\.$~'
|
||||
identifier: method.notFound
|
||||
|
||||
-
|
||||
message: '~createComparator~'
|
||||
identifier: arguments.count
|
||||
|
||||
-
|
||||
message: '~UnqualifiedName~'
|
||||
identifier: class.notFound
|
||||
|
||||
-
|
||||
message: '~IndexedColumn~'
|
||||
identifier: class.notFound
|
||||
|
||||
-
|
||||
message: '~PrimaryKeyConstraint~'
|
||||
identifier: class.notFound
|
||||
|
||||
-
|
||||
message: '~IndexType~'
|
||||
identifier: class.notFound
|
||||
|
||||
-
|
||||
message: '~dropForeignKey~'
|
||||
identifier: method.notFound
|
||||
|
||||
-
|
||||
message: '~getIndexedColumns~'
|
||||
identifier: method.notFound
|
||||
|
||||
-
|
||||
message: '~getPrimaryKeyConstraint~'
|
||||
identifier: method.notFound
|
||||
|
||||
-
|
||||
message: '~PrimaryKeyConstraint~'
|
||||
identifier: class.notFound
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
-
|
||||
message: '~^Call to method toString.*UnqualifiedName\.$~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
- '~^Call to method getObjectName\(\) on an unknown class Doctrine\\DBAL\\Schema\\NamedObject\.$~'
|
||||
|
||||
- '~^Class Doctrine\\DBAL\\Platforms\\SQLitePlatform not found\.$~'
|
||||
|
||||
- '~^Class Doctrine\\DBAL\\Schema\\NamedObject not found\.$~'
|
||||
|
||||
-
|
||||
message: '~sort~'
|
||||
identifier: argument.unresolvableType
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Parameter \#1 \$asset of static method Doctrine\\ORM\\Mapping\\Driver\\DatabaseDriver\:\:getAssetName\(\) expects Doctrine\\DBAL\\Schema\\AbstractAsset, Doctrine\\DBAL\\Schema\\Column\|false given\.$#'
|
||||
identifier: argument.type
|
||||
path: src/Mapping/Driver/DatabaseDriver.php
|
||||
|
||||
-
|
||||
message: '#^Instantiated class Doctrine\\DBAL\\Schema\\DefaultExpression\\\w+ not found\.$#'
|
||||
identifier: class.notFound
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
|
||||
# To be removed in 4.0
|
||||
-
|
||||
message: '#Negated boolean expression is always false\.#'
|
||||
paths:
|
||||
- src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
-
|
||||
message: '~^Call to deprecated method getEventManager\(\) of class Doctrine\\DBAL\\Connection\.$~'
|
||||
path: src/EntityManager.php
|
||||
-
|
||||
message: '~deprecated class Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand\:~'
|
||||
path: src/Tools/Console/ConsoleRunner.php
|
||||
|
||||
# Compatibility with Persistence 3
|
||||
-
|
||||
message: '#Expression on left side of \?\? is not nullable.#'
|
||||
path: src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
-
|
||||
message: '~^Method Doctrine\\ORM\\Utility\\PersisterHelper\:\:getArrayBindingType\(\) never returns .* so it can be removed from the return type\.$~'
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
-
|
||||
message: '~inferParameterTypes.*should return~'
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
-
|
||||
message: '~.*appendLockHint.*expects.*LockMode given~'
|
||||
paths:
|
||||
- src/Persisters/Entity/BasicEntityPersister.php
|
||||
- src/Persisters/Entity/JoinedSubclassPersister.php
|
||||
|
||||
-
|
||||
message: '~.*executeStatement.*expects~'
|
||||
path: src/Query/Exec/MultiTableUpdateExecutor.php
|
||||
|
||||
-
|
||||
message: '~method_exists.*getEventManager~'
|
||||
path: src/EntityManager.php
|
||||
|
||||
-
|
||||
message: '~method_exists.*getIdentitySequence~'
|
||||
path: src/Mapping/ClassMetadataFactory.php
|
||||
|
||||
-
|
||||
message: '~expand(Criteria)?Parameters.*should return array~'
|
||||
path: src/Persisters/Entity/BasicEntityPersister.php
|
||||
|
||||
-
|
||||
message: '~inferType.*never returns~'
|
||||
path: src/Query/ParameterTypeInferer.php
|
||||
45
phpstan.neon
45
phpstan.neon
@@ -3,6 +3,8 @@ includes:
|
||||
- phpstan-params.neon
|
||||
|
||||
parameters:
|
||||
checkMissingOverrideMethodAttribute: true
|
||||
|
||||
ignoreErrors:
|
||||
# Symfony cache supports passing a key prefix to the clear method.
|
||||
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
|
||||
@@ -11,46 +13,3 @@ parameters:
|
||||
-
|
||||
message: '~^Match expression does not handle remaining values:~'
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
# The return type is already narrow enough.
|
||||
- '~^Method Doctrine\\ORM\\Query\\ParameterTypeInferer\:\:inferType\(\) never returns ''[a-z_]+'' so it can be removed from the return type\.$~'
|
||||
- '~^Method Doctrine\\ORM\\Query\\ParameterTypeInferer\:\:inferType\(\) never returns Doctrine\\DBAL\\(?:Array)?ParameterType\:\:[A-Z_]+ so it can be removed from the return type\.$~'
|
||||
|
||||
# DBAL 4 compatibility
|
||||
-
|
||||
message: '~^Method Doctrine\\ORM\\Query\\AST\\Functions\\TrimFunction::getTrimMode\(\) never returns .* so it can be removed from the return type\.$~'
|
||||
path: src/Query/AST/Functions/TrimFunction.php
|
||||
-
|
||||
message: '~^Method Doctrine\\ORM\\Utility\\PersisterHelper\:\:getArrayBindingType\(\) never returns .* so it can be removed from the return type\.$~'
|
||||
path: src/Utility/PersisterHelper.php
|
||||
|
||||
# Compatibility with DBAL 3
|
||||
# See https://github.com/doctrine/dbal/pull/3480
|
||||
-
|
||||
message: '~^Result of method Doctrine\\DBAL\\Connection::commit\(\) \(void\) is used\.$~'
|
||||
path: src/UnitOfWork.php
|
||||
-
|
||||
message: '~^Strict comparison using === between null and false will always evaluate to false\.$~'
|
||||
path: src/UnitOfWork.php
|
||||
-
|
||||
message: '~^Variable \$e on left side of \?\? always exists and is not nullable\.$~'
|
||||
path: src/UnitOfWork.php
|
||||
|
||||
-
|
||||
message: '~^Parameter #2 \$command of static method Doctrine\\ORM\\Tools\\Console\\ConsoleRunner::addCommandToApplication\(\) expects Symfony\\Component\\Console\\Command\\Command, Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand given\.$~'
|
||||
path: src/Tools/Console/ConsoleRunner.php
|
||||
|
||||
-
|
||||
message: '~Strict comparison using \=\=\= between callable\(\)\: mixed and null will always evaluate to false\.~'
|
||||
path: src/Tools/SchemaTool.php
|
||||
|
||||
# To be removed in 4.0
|
||||
-
|
||||
message: '#Negated boolean expression is always false\.#'
|
||||
paths:
|
||||
- src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
# Compatibility with Persistence 3
|
||||
-
|
||||
message: '#Expression on left side of \?\? is not nullable.#'
|
||||
path: src/Mapping/Driver/AttributeDriver.php
|
||||
|
||||
@@ -16,7 +16,6 @@ use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
use Doctrine\ORM\Cache\TimestampCacheKey;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\MappingException as ORMMappingException;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Doctrine\ORM\Query\Parameter;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\Persistence\Mapping\MappingException;
|
||||
@@ -51,32 +50,32 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Hydrates an object graph. This is the default behavior.
|
||||
*/
|
||||
public const HYDRATE_OBJECT = 1;
|
||||
final public const int HYDRATE_OBJECT = 1;
|
||||
|
||||
/**
|
||||
* Hydrates an array graph.
|
||||
*/
|
||||
public const HYDRATE_ARRAY = 2;
|
||||
final public const int HYDRATE_ARRAY = 2;
|
||||
|
||||
/**
|
||||
* Hydrates a flat, rectangular result set with scalar values.
|
||||
*/
|
||||
public const HYDRATE_SCALAR = 3;
|
||||
final public const int HYDRATE_SCALAR = 3;
|
||||
|
||||
/**
|
||||
* Hydrates a single scalar value.
|
||||
*/
|
||||
public const HYDRATE_SINGLE_SCALAR = 4;
|
||||
final public const int HYDRATE_SINGLE_SCALAR = 4;
|
||||
|
||||
/**
|
||||
* Very simple object hydrator (optimized for performance).
|
||||
*/
|
||||
public const HYDRATE_SIMPLEOBJECT = 5;
|
||||
final public const int HYDRATE_SIMPLEOBJECT = 5;
|
||||
|
||||
/**
|
||||
* Hydrates scalar column value.
|
||||
*/
|
||||
public const HYDRATE_SCALAR_COLUMN = 6;
|
||||
final public const int HYDRATE_SCALAR_COLUMN = 6;
|
||||
|
||||
/**
|
||||
* The parameter map of this query.
|
||||
@@ -320,16 +319,16 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Sets a query parameter.
|
||||
*
|
||||
* @param string|int $key The parameter position or name.
|
||||
* @param mixed $value The parameter value.
|
||||
* @param ParameterType|ArrayParameterType|string|int|null $type The parameter type. If specified, the given value
|
||||
* will be run through the type conversion of this
|
||||
* type. This is usually not needed for strings and
|
||||
* numeric types.
|
||||
* @param string|int $key The parameter position or name.
|
||||
* @param mixed $value The parameter value.
|
||||
* @param ParameterType|ArrayParameterType|string|null $type The parameter type. If specified, the given value
|
||||
* will be run through the type conversion of this
|
||||
* type. This is usually not needed for strings and
|
||||
* numeric types.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParameter(string|int $key, mixed $value, ParameterType|ArrayParameterType|string|int|null $type = null): static
|
||||
public function setParameter(string|int $key, mixed $value, ParameterType|ArrayParameterType|string|null $type = null): static
|
||||
{
|
||||
$existingParameter = $this->getParameter($key);
|
||||
|
||||
@@ -378,7 +377,7 @@ abstract class AbstractQuery
|
||||
}
|
||||
|
||||
try {
|
||||
$class = DefaultProxyClassNameResolver::getClass($value);
|
||||
$class = $value::class;
|
||||
$value = $this->em->getUnitOfWork()->getSingleIdentifierValue($value);
|
||||
|
||||
if ($value === null) {
|
||||
|
||||
@@ -12,31 +12,31 @@ use Doctrine\ORM\Cache\Region;
|
||||
*/
|
||||
interface Cache
|
||||
{
|
||||
public const DEFAULT_QUERY_REGION_NAME = 'query_cache_region';
|
||||
final public const string DEFAULT_QUERY_REGION_NAME = 'query_cache_region';
|
||||
|
||||
public const DEFAULT_TIMESTAMP_REGION_NAME = 'timestamp_cache_region';
|
||||
final public const string DEFAULT_TIMESTAMP_REGION_NAME = 'timestamp_cache_region';
|
||||
|
||||
/**
|
||||
* May read items from the cache, but will not add items.
|
||||
*/
|
||||
public const MODE_GET = 1;
|
||||
final public const int MODE_GET = 1;
|
||||
|
||||
/**
|
||||
* Will never read items from the cache,
|
||||
* but will add items to the cache as it reads them from the database.
|
||||
*/
|
||||
public const MODE_PUT = 2;
|
||||
final public const int MODE_PUT = 2;
|
||||
|
||||
/**
|
||||
* May read items from the cache, and add items to the cache.
|
||||
*/
|
||||
public const MODE_NORMAL = 3;
|
||||
final public const int MODE_NORMAL = 3;
|
||||
|
||||
/**
|
||||
* The query will never read items from the cache,
|
||||
* but will refresh items to the cache as it reads them from the database.
|
||||
*/
|
||||
public const MODE_REFRESH = 4;
|
||||
final public const int MODE_REFRESH = 4;
|
||||
|
||||
public function getEntityCacheRegion(string $className): Region|null;
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ 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 Override;
|
||||
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
@@ -40,6 +40,7 @@ class DefaultCache implements Cache
|
||||
->getCacheFactory();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getEntityCacheRegion(string $className): Region|null
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -52,6 +53,7 @@ class DefaultCache implements Cache
|
||||
return $persister->getCacheRegion();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getCollectionCacheRegion(string $className, string $association): Region|null
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -64,6 +66,7 @@ class DefaultCache implements Cache
|
||||
return $persister->getCacheRegion();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function containsEntity(string $className, mixed $identifier): bool
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -76,6 +79,7 @@ class DefaultCache implements Cache
|
||||
return $persister->getCacheRegion()->contains($this->buildEntityCacheKey($metadata, $identifier));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictEntity(string $className, mixed $identifier): void
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -88,6 +92,7 @@ class DefaultCache implements Cache
|
||||
$persister->getCacheRegion()->evict($this->buildEntityCacheKey($metadata, $identifier));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictEntityRegion(string $className): void
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -100,6 +105,7 @@ class DefaultCache implements Cache
|
||||
$persister->getCacheRegion()->evictAll();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictEntityRegions(): void
|
||||
{
|
||||
$metadatas = $this->em->getMetadataFactory()->getAllMetadata();
|
||||
@@ -115,6 +121,7 @@ class DefaultCache implements Cache
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function containsCollection(string $className, string $association, mixed $ownerIdentifier): bool
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -127,6 +134,7 @@ class DefaultCache implements Cache
|
||||
return $persister->getCacheRegion()->contains($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictCollection(string $className, string $association, mixed $ownerIdentifier): void
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -139,6 +147,7 @@ class DefaultCache implements Cache
|
||||
$persister->getCacheRegion()->evict($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictCollectionRegion(string $className, string $association): void
|
||||
{
|
||||
$metadata = $this->em->getClassMetadata($className);
|
||||
@@ -151,6 +160,7 @@ class DefaultCache implements Cache
|
||||
$persister->getCacheRegion()->evictAll();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictCollectionRegions(): void
|
||||
{
|
||||
$metadatas = $this->em->getMetadataFactory()->getAllMetadata();
|
||||
@@ -172,11 +182,13 @@ class DefaultCache implements Cache
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function containsQuery(string $regionName): bool
|
||||
{
|
||||
return isset($this->queryCaches[$regionName]);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictQueryRegion(string|null $regionName = null): void
|
||||
{
|
||||
if ($regionName === null && $this->defaultQueryCache !== null) {
|
||||
@@ -190,6 +202,7 @@ class DefaultCache implements Cache
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictQueryRegions(): void
|
||||
{
|
||||
$this->getQueryCache()->clear();
|
||||
@@ -199,6 +212,7 @@ class DefaultCache implements Cache
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getQueryCache(string|null $regionName = null): QueryCache
|
||||
{
|
||||
if ($regionName === null) {
|
||||
@@ -233,7 +247,7 @@ class DefaultCache implements Cache
|
||||
private function toIdentifierArray(ClassMetadata $metadata, mixed $identifier): array
|
||||
{
|
||||
if (is_object($identifier)) {
|
||||
$class = DefaultProxyClassNameResolver::getClass($identifier);
|
||||
$class = $identifier::class;
|
||||
if ($this->em->getMetadataFactory()->hasMetadataFor($class)) {
|
||||
$identifier = $this->uow->getSingleIdentifierValue($identifier)
|
||||
?? throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($class);
|
||||
|
||||
@@ -23,6 +23,7 @@ use Doctrine\ORM\Persisters\Collection\CollectionPersister;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use Override;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
|
||||
use function assert;
|
||||
@@ -63,6 +64,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
$this->timestampRegion = $region;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata): CachedEntityPersister
|
||||
{
|
||||
assert($metadata->cache !== null);
|
||||
@@ -88,6 +90,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildCachedCollectionPersister(
|
||||
EntityManagerInterface $em,
|
||||
CollectionPersister $persister,
|
||||
@@ -116,6 +119,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildQueryCache(EntityManagerInterface $em, string|null $regionName = null): QueryCache
|
||||
{
|
||||
return new DefaultQueryCache(
|
||||
@@ -129,11 +133,13 @@ class DefaultCacheFactory implements CacheFactory
|
||||
);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildCollectionHydrator(EntityManagerInterface $em, AssociationMapping $mapping): CollectionHydrator
|
||||
{
|
||||
return new DefaultCollectionHydrator($em);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata): EntityHydrator
|
||||
{
|
||||
return new DefaultEntityHydrator($em);
|
||||
@@ -142,6 +148,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getRegion(array $cache): Region
|
||||
{
|
||||
if (isset($this->regions[$cache['region']])) {
|
||||
@@ -170,6 +177,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
return $this->regions[$cache['region']] = $region;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getTimestampRegion(): TimestampRegion
|
||||
{
|
||||
if ($this->timestampRegion === null) {
|
||||
@@ -182,6 +190,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
return $this->timestampRegion;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createCache(EntityManagerInterface $entityManager): Cache
|
||||
{
|
||||
return new DefaultCache($entityManager);
|
||||
|
||||
@@ -11,6 +11,7 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Override;
|
||||
|
||||
use function assert;
|
||||
|
||||
@@ -30,6 +31,7 @@ class DefaultCollectionHydrator implements CollectionHydrator
|
||||
$this->uow = $em->getUnitOfWork();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, array|Collection $collection): CollectionCacheEntry
|
||||
{
|
||||
$data = [];
|
||||
@@ -41,6 +43,7 @@ class DefaultCollectionHydrator implements CollectionHydrator
|
||||
return new CollectionCacheEntry($data);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection): array|null
|
||||
{
|
||||
$assoc = $metadata->associationMappings[$key->association];
|
||||
|
||||
@@ -6,10 +6,10 @@ namespace Doctrine\ORM\Cache;
|
||||
|
||||
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;
|
||||
use Override;
|
||||
|
||||
use function assert;
|
||||
use function is_array;
|
||||
@@ -34,6 +34,7 @@ class DefaultEntityHydrator implements EntityHydrator
|
||||
$this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory());
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, object $entity): EntityCacheEntry
|
||||
{
|
||||
$data = $this->uow->getOriginalEntityData($entity);
|
||||
@@ -97,7 +98,7 @@ class DefaultEntityHydrator implements EntityHydrator
|
||||
}
|
||||
|
||||
if (! isset($assoc->id)) {
|
||||
$targetClass = DefaultProxyClassNameResolver::getClass($data[$name]);
|
||||
$targetClass = $data[$name]::class;
|
||||
$targetId = $this->uow->getEntityIdentifier($data[$name]);
|
||||
$data[$name] = new AssociationCacheEntry($targetClass, $targetId);
|
||||
|
||||
@@ -125,6 +126,7 @@ class DefaultEntityHydrator implements EntityHydrator
|
||||
return new EntityCacheEntry($metadata->name, $data);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, object|null $entity = null): object|null
|
||||
{
|
||||
$data = $entry->data;
|
||||
|
||||
@@ -18,6 +18,7 @@ use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Override;
|
||||
|
||||
use function array_map;
|
||||
use function array_shift;
|
||||
@@ -54,6 +55,7 @@ class DefaultQueryCache implements QueryCache
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = []): array|null
|
||||
{
|
||||
if (! ($key->cacheMode & Cache::MODE_GET)) {
|
||||
@@ -197,6 +199,7 @@ class DefaultQueryCache implements QueryCache
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function put(QueryCacheKey $key, ResultSetMapping $rsm, mixed $result, array $hints = []): bool
|
||||
{
|
||||
if ($rsm->scalarMappings) {
|
||||
@@ -406,11 +409,13 @@ class DefaultQueryCache implements QueryCache
|
||||
return $values;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function clear(): bool
|
||||
{
|
||||
return $this->region->evictAll();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getRegion(): Region
|
||||
{
|
||||
return $this->region;
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Doctrine\ORM\Cache\Logging;
|
||||
use Doctrine\ORM\Cache\CollectionCacheKey;
|
||||
use Doctrine\ORM\Cache\EntityCacheKey;
|
||||
use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
use Override;
|
||||
|
||||
class CacheLoggerChain implements CacheLogger
|
||||
{
|
||||
@@ -29,6 +30,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
return $this->loggers;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function collectionCacheHit(string $regionName, CollectionCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -36,6 +38,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function collectionCacheMiss(string $regionName, CollectionCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -43,6 +46,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function collectionCachePut(string $regionName, CollectionCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -50,6 +54,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function entityCacheHit(string $regionName, EntityCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -57,6 +62,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function entityCacheMiss(string $regionName, EntityCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -64,6 +70,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function entityCachePut(string $regionName, EntityCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -71,6 +78,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function queryCacheHit(string $regionName, QueryCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -78,6 +86,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function queryCacheMiss(string $regionName, QueryCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
@@ -85,6 +94,7 @@ class CacheLoggerChain implements CacheLogger
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function queryCachePut(string $regionName, QueryCacheKey $key): void
|
||||
{
|
||||
foreach ($this->loggers as $logger) {
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Doctrine\ORM\Cache\Logging;
|
||||
use Doctrine\ORM\Cache\CollectionCacheKey;
|
||||
use Doctrine\ORM\Cache\EntityCacheKey;
|
||||
use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
use Override;
|
||||
|
||||
use function array_sum;
|
||||
|
||||
@@ -24,54 +25,63 @@ class StatisticsCacheLogger implements CacheLogger
|
||||
/** @var array<string, int> */
|
||||
private array $cachePutCountMap = [];
|
||||
|
||||
#[Override]
|
||||
public function collectionCacheMiss(string $regionName, CollectionCacheKey $key): void
|
||||
{
|
||||
$this->cacheMissCountMap[$regionName]
|
||||
= ($this->cacheMissCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function collectionCacheHit(string $regionName, CollectionCacheKey $key): void
|
||||
{
|
||||
$this->cacheHitCountMap[$regionName]
|
||||
= ($this->cacheHitCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function collectionCachePut(string $regionName, CollectionCacheKey $key): void
|
||||
{
|
||||
$this->cachePutCountMap[$regionName]
|
||||
= ($this->cachePutCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function entityCacheMiss(string $regionName, EntityCacheKey $key): void
|
||||
{
|
||||
$this->cacheMissCountMap[$regionName]
|
||||
= ($this->cacheMissCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function entityCacheHit(string $regionName, EntityCacheKey $key): void
|
||||
{
|
||||
$this->cacheHitCountMap[$regionName]
|
||||
= ($this->cacheHitCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function entityCachePut(string $regionName, EntityCacheKey $key): void
|
||||
{
|
||||
$this->cachePutCountMap[$regionName]
|
||||
= ($this->cachePutCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function queryCacheHit(string $regionName, QueryCacheKey $key): void
|
||||
{
|
||||
$this->cacheHitCountMap[$regionName]
|
||||
= ($this->cacheHitCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function queryCacheMiss(string $regionName, QueryCacheKey $key): void
|
||||
{
|
||||
$this->cacheMissCountMap[$regionName]
|
||||
= ($this->cacheMissCountMap[$regionName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function queryCachePut(string $regionName, QueryCacheKey $key): void
|
||||
{
|
||||
$this->cachePutCountMap[$regionName]
|
||||
|
||||
@@ -17,9 +17,9 @@ 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\Query\FilterCollection;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Override;
|
||||
|
||||
use function array_values;
|
||||
use function assert;
|
||||
@@ -63,21 +63,25 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
$this->targetEntity = $em->getClassMetadata($association->targetEntity);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getCacheRegion(): Region
|
||||
{
|
||||
return $this->region;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getSourceEntityMetadata(): ClassMetadata
|
||||
{
|
||||
return $this->sourceEntity;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getTargetEntityMetadata(): ClassMetadata
|
||||
{
|
||||
return $this->targetEntity;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key): array|null
|
||||
{
|
||||
$cache = $this->region->get($key);
|
||||
@@ -89,6 +93,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
return $this->hydrator->loadCacheEntry($this->sourceEntity, $key, $cache, $collection);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function storeCollectionCache(CollectionCacheKey $key, Collection|array $elements): void
|
||||
{
|
||||
$associationMapping = $this->sourceEntity->associationMappings[$key->association];
|
||||
@@ -111,7 +116,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
}
|
||||
|
||||
$class = $this->targetEntity;
|
||||
$className = DefaultProxyClassNameResolver::getClass($elements[$index]);
|
||||
$className = $elements[$index]::class;
|
||||
|
||||
if ($className !== $this->targetEntity->name) {
|
||||
$class = $this->metadataFactory->getMetadataFor($className);
|
||||
@@ -128,16 +133,19 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function contains(PersistentCollection $collection, object $element): bool
|
||||
{
|
||||
return $this->persister->contains($collection, $element);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function containsKey(PersistentCollection $collection, mixed $key): bool
|
||||
{
|
||||
return $this->persister->containsKey($collection, $key);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function count(PersistentCollection $collection): int
|
||||
{
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
@@ -151,6 +159,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
return $this->persister->count($collection);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function get(PersistentCollection $collection, mixed $index): mixed
|
||||
{
|
||||
return $this->persister->get($collection, $index);
|
||||
@@ -159,6 +168,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function slice(PersistentCollection $collection, int $offset, int|null $length = null): array
|
||||
{
|
||||
return $this->persister->slice($collection, $offset, $length);
|
||||
@@ -167,6 +177,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function loadCriteria(PersistentCollection $collection, Criteria $criteria): array
|
||||
{
|
||||
return $this->persister->loadCriteria($collection, $criteria);
|
||||
|
||||
@@ -6,11 +6,13 @@ namespace Doctrine\ORM\Cache\Persister\Collection;
|
||||
|
||||
use Doctrine\ORM\Cache\CollectionCacheKey;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Override;
|
||||
|
||||
use function spl_object_id;
|
||||
|
||||
class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPersister
|
||||
{
|
||||
#[Override]
|
||||
public function afterTransactionComplete(): void
|
||||
{
|
||||
if (isset($this->queuedCache['update'])) {
|
||||
@@ -28,11 +30,13 @@ class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPers
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function afterTransactionRolledBack(): void
|
||||
{
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function delete(PersistentCollection $collection): void
|
||||
{
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
@@ -43,6 +47,7 @@ class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPers
|
||||
$this->queuedCache['delete'][spl_object_id($collection)] = $key;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function update(PersistentCollection $collection): void
|
||||
{
|
||||
$isInitialized = $collection->isInitialized();
|
||||
|
||||
@@ -6,15 +6,16 @@ namespace Doctrine\ORM\Cache\Persister\Collection;
|
||||
|
||||
use Doctrine\ORM\Cache\Exception\CannotUpdateReadOnlyCollection;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Override;
|
||||
|
||||
class ReadOnlyCachedCollectionPersister extends NonStrictReadWriteCachedCollectionPersister
|
||||
{
|
||||
#[Override]
|
||||
public function update(PersistentCollection $collection): void
|
||||
{
|
||||
if ($collection->isDirty() && $collection->getSnapshot()) {
|
||||
throw CannotUpdateReadOnlyCollection::fromEntityAndField(
|
||||
DefaultProxyClassNameResolver::getClass($collection->getOwner()),
|
||||
$collection->getOwner()::class,
|
||||
$this->association->fieldName,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
|
||||
use Override;
|
||||
|
||||
use function spl_object_id;
|
||||
|
||||
@@ -24,6 +25,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
|
||||
parent::__construct($persister, $region, $em, $association);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function afterTransactionComplete(): void
|
||||
{
|
||||
if (isset($this->queuedCache['update'])) {
|
||||
@@ -41,6 +43,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function afterTransactionRolledBack(): void
|
||||
{
|
||||
if (isset($this->queuedCache['update'])) {
|
||||
@@ -58,6 +61,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function delete(PersistentCollection $collection): void
|
||||
{
|
||||
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
|
||||
@@ -76,6 +80,7 @@ class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
|
||||
];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function update(PersistentCollection $collection): void
|
||||
{
|
||||
$isInitialized = $collection->isInitialized();
|
||||
|
||||
@@ -23,13 +23,12 @@ 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\Query\FilterCollection;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Override;
|
||||
|
||||
use function array_merge;
|
||||
use function func_get_args;
|
||||
use function serialize;
|
||||
use function sha1;
|
||||
|
||||
@@ -77,6 +76,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
$this->timestampKey = new TimestampCacheKey($this->class->rootEntityName);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function addInsert(object $entity): void
|
||||
{
|
||||
$this->persister->addInsert($entity);
|
||||
@@ -85,15 +85,17 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getInserts(): array
|
||||
{
|
||||
return $this->persister->getInserts();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getSelectSQL(
|
||||
array|Criteria $criteria,
|
||||
AssociationMapping|null $assoc = null,
|
||||
LockMode|int|null $lockMode = null,
|
||||
LockMode|null $lockMode = null,
|
||||
int|null $limit = null,
|
||||
int|null $offset = null,
|
||||
array|null $orderBy = null,
|
||||
@@ -101,21 +103,25 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $this->persister->getSelectSQL($criteria, $assoc, $lockMode, $limit, $offset, $orderBy);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getCountSQL(array|Criteria $criteria = []): string
|
||||
{
|
||||
return $this->persister->getCountSQL($criteria);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getInsertSQL(): string
|
||||
{
|
||||
return $this->persister->getInsertSQL();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getResultSetMapping(): ResultSetMapping
|
||||
{
|
||||
return $this->persister->getResultSetMapping();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getSelectConditionStatementSQL(
|
||||
string $field,
|
||||
mixed $value,
|
||||
@@ -125,6 +131,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $this->persister->getSelectConditionStatementSQL($field, $value, $assoc, $comparison);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function exists(object $entity, Criteria|null $extraConditions = null): bool
|
||||
{
|
||||
if ($extraConditions === null) {
|
||||
@@ -138,20 +145,23 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $this->persister->exists($entity, $extraConditions);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getCacheRegion(): Region
|
||||
{
|
||||
return $this->region;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getEntityHydrator(): EntityHydrator
|
||||
{
|
||||
return $this->hydrator;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function storeEntityCache(object $entity, EntityCacheKey $key): bool
|
||||
{
|
||||
$class = $this->class;
|
||||
$className = DefaultProxyClassNameResolver::getClass($entity);
|
||||
$className = $entity::class;
|
||||
|
||||
if ($className !== $this->class->name) {
|
||||
$class = $this->metadataFactory->getMetadataFor($className);
|
||||
@@ -225,6 +235,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function expandParameters(array $criteria): array
|
||||
{
|
||||
return $this->persister->expandParameters($criteria);
|
||||
@@ -233,11 +244,13 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function expandCriteriaParameters(Criteria $criteria): array
|
||||
{
|
||||
return $this->persister->expandCriteriaParameters($criteria);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getClassMetadata(): ClassMetadata
|
||||
{
|
||||
return $this->persister->getClassMetadata();
|
||||
@@ -246,6 +259,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getManyToManyCollection(
|
||||
AssociationMapping $assoc,
|
||||
object $sourceEntity,
|
||||
@@ -258,6 +272,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getOneToManyCollection(
|
||||
AssociationMapping $assoc,
|
||||
object $sourceEntity,
|
||||
@@ -267,11 +282,13 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $this->persister->getOneToManyCollection($assoc, $sourceEntity, $offset, $limit);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getOwningTable(string $fieldName): string
|
||||
{
|
||||
return $this->persister->getOwningTable($fieldName);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function executeInserts(): void
|
||||
{
|
||||
// The commit order/foreign key relationships may make it necessary that multiple calls to executeInsert()
|
||||
@@ -288,12 +305,13 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function load(
|
||||
array $criteria,
|
||||
object|null $entity = null,
|
||||
AssociationMapping|null $assoc = null,
|
||||
array $hints = [],
|
||||
LockMode|int|null $lockMode = null,
|
||||
LockMode|null $lockMode = null,
|
||||
int|null $limit = null,
|
||||
array|null $orderBy = null,
|
||||
): object|null {
|
||||
@@ -335,6 +353,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function loadAll(
|
||||
array $criteria = [],
|
||||
array|null $orderBy = null,
|
||||
@@ -371,6 +390,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function loadById(array $identifier, object|null $entity = null): object|null
|
||||
{
|
||||
$cacheKey = new EntityCacheKey($this->class->rootEntityName, $identifier);
|
||||
@@ -398,7 +418,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
}
|
||||
|
||||
$class = $this->class;
|
||||
$className = DefaultProxyClassNameResolver::getClass($entity);
|
||||
$className = $entity::class;
|
||||
|
||||
if ($className !== $this->class->name) {
|
||||
$class = $this->metadataFactory->getMetadataFor($className);
|
||||
@@ -420,6 +440,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $entity;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function count(array|Criteria $criteria = []): int
|
||||
{
|
||||
return $this->persister->count($criteria);
|
||||
@@ -428,6 +449,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function loadCriteria(Criteria $criteria): array
|
||||
{
|
||||
$orderBy = $criteria->orderings();
|
||||
@@ -463,6 +485,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function loadManyToManyCollection(
|
||||
AssociationMapping $assoc,
|
||||
object $sourceEntity,
|
||||
@@ -494,6 +517,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
return $list;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function loadOneToManyCollection(
|
||||
AssociationMapping $assoc,
|
||||
object $sourceEntity,
|
||||
@@ -528,6 +552,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function loadOneToOneEntity(AssociationMapping $assoc, object $sourceEntity, array $identifier = []): object|null
|
||||
{
|
||||
return $this->persister->loadOneToOneEntity($assoc, $sourceEntity, $identifier);
|
||||
@@ -536,7 +561,8 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function lock(array $criteria, LockMode|int $lockMode): void
|
||||
#[Override]
|
||||
public function lock(array $criteria, LockMode $lockMode): void
|
||||
{
|
||||
$this->persister->lock($criteria, $lockMode);
|
||||
}
|
||||
@@ -544,16 +570,15 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function refresh(array $id, object $entity, LockMode|int|null $lockMode = null): void
|
||||
#[Override]
|
||||
public function refresh(array $id, object $entity, LockMode|null $lockMode = null): void
|
||||
{
|
||||
$this->persister->refresh($id, $entity, $lockMode);
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $ownerId */
|
||||
protected function buildCollectionCacheKey(AssociationMapping $association, array $ownerId, /* string $filterHash */): CollectionCacheKey
|
||||
protected function buildCollectionCacheKey(AssociationMapping $association, array $ownerId, string $filterHash): CollectionCacheKey
|
||||
{
|
||||
$filterHash = (string) (func_get_args()[2] ?? ''); // todo: move to argument in next major release
|
||||
|
||||
return new CollectionCacheKey(
|
||||
$this->metadataFactory->getMetadataFor($association->sourceEntity)->rootEntityName,
|
||||
$association->fieldName,
|
||||
|
||||
@@ -5,12 +5,14 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Cache\Persister\Entity;
|
||||
|
||||
use Doctrine\ORM\Cache\EntityCacheKey;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Specific non-strict read/write cached entity persister
|
||||
*/
|
||||
class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
{
|
||||
#[Override]
|
||||
public function afterTransactionComplete(): void
|
||||
{
|
||||
$isChanged = false;
|
||||
@@ -42,11 +44,13 @@ class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function afterTransactionRolledBack(): void
|
||||
{
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function delete(object $entity): bool
|
||||
{
|
||||
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
|
||||
@@ -61,6 +65,7 @@ class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function update(object $entity): void
|
||||
{
|
||||
$this->persister->update($entity);
|
||||
|
||||
@@ -5,15 +5,16 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Cache\Persister\Entity;
|
||||
|
||||
use Doctrine\ORM\Cache\Exception\CannotUpdateReadOnlyEntity;
|
||||
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Specific read-only region entity persister
|
||||
*/
|
||||
class ReadOnlyCachedEntityPersister extends NonStrictReadWriteCachedEntityPersister
|
||||
{
|
||||
#[Override]
|
||||
public function update(object $entity): void
|
||||
{
|
||||
throw CannotUpdateReadOnlyEntity::fromEntity(DefaultProxyClassNameResolver::getClass($entity));
|
||||
throw CannotUpdateReadOnlyEntity::fromEntity($entity::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use Doctrine\ORM\Cache\EntityCacheKey;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Specific read-write entity persister
|
||||
@@ -20,6 +21,7 @@ class ReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
parent::__construct($persister, $region, $em, $class);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function afterTransactionComplete(): void
|
||||
{
|
||||
$isChanged = true;
|
||||
@@ -47,6 +49,7 @@ class ReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function afterTransactionRolledBack(): void
|
||||
{
|
||||
if (isset($this->queuedCache['update'])) {
|
||||
@@ -64,6 +67,7 @@ class ReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
$this->queuedCache = [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function delete(object $entity): bool
|
||||
{
|
||||
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
|
||||
@@ -86,6 +90,7 @@ class ReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function update(object $entity): void
|
||||
{
|
||||
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
|
||||
|
||||
@@ -9,6 +9,7 @@ use Doctrine\ORM\Cache\CacheKey;
|
||||
use Doctrine\ORM\Cache\CollectionCacheEntry;
|
||||
use Doctrine\ORM\Cache\Lock;
|
||||
use Doctrine\ORM\Cache\Region;
|
||||
use Override;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Traversable;
|
||||
@@ -22,8 +23,8 @@ use function strtr;
|
||||
*/
|
||||
class DefaultRegion implements Region
|
||||
{
|
||||
private const REGION_KEY_SEPARATOR = '_';
|
||||
private const REGION_PREFIX = 'DC2_REGION_';
|
||||
private const string REGION_KEY_SEPARATOR = '_';
|
||||
private const string REGION_PREFIX = 'DC2_REGION_';
|
||||
|
||||
public function __construct(
|
||||
private readonly string $name,
|
||||
@@ -32,16 +33,19 @@ class DefaultRegion implements Region
|
||||
) {
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function contains(CacheKey $key): bool
|
||||
{
|
||||
return $this->cacheItemPool->hasItem($this->getCacheEntryKey($key));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function get(CacheKey $key): CacheEntry|null
|
||||
{
|
||||
$item = $this->cacheItemPool->getItem($this->getCacheEntryKey($key));
|
||||
@@ -54,6 +58,7 @@ class DefaultRegion implements Region
|
||||
return $entry;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getMultiple(CollectionCacheEntry $collection): array|null
|
||||
{
|
||||
$keys = array_map(
|
||||
@@ -83,6 +88,7 @@ class DefaultRegion implements Region
|
||||
return $result;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool
|
||||
{
|
||||
$item = $this->cacheItemPool
|
||||
@@ -96,11 +102,13 @@ class DefaultRegion implements Region
|
||||
return $this->cacheItemPool->save($item);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evict(CacheKey $key): bool
|
||||
{
|
||||
return $this->cacheItemPool->deleteItem($this->getCacheEntryKey($key));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictAll(): bool
|
||||
{
|
||||
return $this->cacheItemPool->clear(self::REGION_PREFIX . $this->name);
|
||||
|
||||
@@ -11,6 +11,7 @@ use Doctrine\ORM\Cache\ConcurrentRegion;
|
||||
use Doctrine\ORM\Cache\Lock;
|
||||
use Doctrine\ORM\Cache\Region;
|
||||
use InvalidArgumentException;
|
||||
use Override;
|
||||
|
||||
use function array_filter;
|
||||
use function array_map;
|
||||
@@ -35,7 +36,7 @@ use const LOCK_EX;
|
||||
*/
|
||||
class FileLockRegion implements ConcurrentRegion
|
||||
{
|
||||
final public const LOCK_EXTENSION = 'lock';
|
||||
final public const string LOCK_EXTENSION = 'lock';
|
||||
|
||||
/**
|
||||
* @param numeric-string|int $lockLifetime
|
||||
@@ -102,11 +103,13 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return @fileatime($filename);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->region->getName();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function contains(CacheKey $key): bool
|
||||
{
|
||||
if ($this->isLocked($key)) {
|
||||
@@ -116,6 +119,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $this->region->contains($key);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function get(CacheKey $key): CacheEntry|null
|
||||
{
|
||||
if ($this->isLocked($key)) {
|
||||
@@ -125,6 +129,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $this->region->get($key);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getMultiple(CollectionCacheEntry $collection): array|null
|
||||
{
|
||||
if (array_filter(array_map($this->isLocked(...), $collection->identifiers))) {
|
||||
@@ -134,6 +139,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $this->region->getMultiple($collection);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function put(CacheKey $key, CacheEntry $entry, Lock|null $lock = null): bool
|
||||
{
|
||||
if ($this->isLocked($key, $lock)) {
|
||||
@@ -143,6 +149,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $this->region->put($key, $entry);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evict(CacheKey $key): bool
|
||||
{
|
||||
if ($this->isLocked($key)) {
|
||||
@@ -152,6 +159,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $this->region->evict($key);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function evictAll(): bool
|
||||
{
|
||||
// The check below is necessary because on some platforms glob returns false
|
||||
@@ -165,6 +173,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $this->region->evictAll();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function lock(CacheKey $key): Lock|null
|
||||
{
|
||||
if ($this->isLocked($key)) {
|
||||
@@ -183,6 +192,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return $lock;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function unlock(CacheKey $key, Lock $lock): bool
|
||||
{
|
||||
if ($this->isLocked($key, $lock)) {
|
||||
|
||||
@@ -7,12 +7,14 @@ namespace Doctrine\ORM\Cache\Region;
|
||||
use Doctrine\ORM\Cache\CacheKey;
|
||||
use Doctrine\ORM\Cache\TimestampCacheEntry;
|
||||
use Doctrine\ORM\Cache\TimestampRegion;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Tracks the timestamps of the most recent updates to particular keys.
|
||||
*/
|
||||
class UpdateTimestampCache extends DefaultRegion implements TimestampRegion
|
||||
{
|
||||
#[Override]
|
||||
public function update(CacheKey $key): void
|
||||
{
|
||||
$this->put($key, new TimestampCacheEntry());
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
use Override;
|
||||
|
||||
use function microtime;
|
||||
|
||||
class TimestampQueryCacheValidator implements QueryCacheValidator
|
||||
@@ -12,6 +14,7 @@ class TimestampQueryCacheValidator implements QueryCacheValidator
|
||||
{
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isValid(QueryCacheKey $key, QueryCacheEntry $entry): bool
|
||||
{
|
||||
if ($this->regionUpdated($key, $entry)) {
|
||||
|
||||
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Cache\CacheConfiguration;
|
||||
use Doctrine\ORM\Exception\InvalidEntityRepository;
|
||||
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
|
||||
@@ -18,21 +17,17 @@ use Doctrine\ORM\Mapping\EntityListenerResolver;
|
||||
use Doctrine\ORM\Mapping\NamingStrategy;
|
||||
use Doctrine\ORM\Mapping\QuoteStrategy;
|
||||
use Doctrine\ORM\Mapping\TypedFieldMapper;
|
||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
||||
use Doctrine\ORM\Query\Filter\SQLFilter;
|
||||
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
|
||||
use Doctrine\ORM\Repository\RepositoryFactory;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
use LogicException;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
|
||||
use function class_exists;
|
||||
use function is_a;
|
||||
use function strtolower;
|
||||
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
/**
|
||||
* Configuration container for all configuration options of Doctrine.
|
||||
* It combines all configuration options from DBAL & ORM.
|
||||
@@ -59,112 +54,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
return $this->identityGenerationPreferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the directory where Doctrine generates any necessary proxy class files.
|
||||
*/
|
||||
public function setProxyDir(string $dir): void
|
||||
{
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
Deprecation::triggerIfCalledFromOutside(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/12005',
|
||||
'Calling %s is deprecated and will not be possible in Doctrine ORM 4.0.',
|
||||
__METHOD__,
|
||||
);
|
||||
}
|
||||
|
||||
$this->attributes['proxyDir'] = $dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the directory where Doctrine generates any necessary proxy class files.
|
||||
*/
|
||||
public function getProxyDir(): string|null
|
||||
{
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/12005',
|
||||
'Calling %s is deprecated and will not be possible in Doctrine ORM 4.0.',
|
||||
__METHOD__,
|
||||
);
|
||||
}
|
||||
|
||||
return $this->attributes['proxyDir'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the strategy for automatically generating proxy classes.
|
||||
*
|
||||
* @return ProxyFactory::AUTOGENERATE_*
|
||||
*/
|
||||
public function getAutoGenerateProxyClasses(): int
|
||||
{
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/12005',
|
||||
'Calling %s is deprecated and will not be possible in Doctrine ORM 4.0.',
|
||||
__METHOD__,
|
||||
);
|
||||
}
|
||||
|
||||
return $this->attributes['autoGenerateProxyClasses'] ?? ProxyFactory::AUTOGENERATE_ALWAYS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the strategy for automatically generating proxy classes.
|
||||
*
|
||||
* @param bool|ProxyFactory::AUTOGENERATE_* $autoGenerate True is converted to AUTOGENERATE_ALWAYS, false to AUTOGENERATE_NEVER.
|
||||
*/
|
||||
public function setAutoGenerateProxyClasses(bool|int $autoGenerate): void
|
||||
{
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
Deprecation::triggerIfCalledFromOutside(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/12005',
|
||||
'Calling %s is deprecated and will not be possible in Doctrine ORM 4.0.',
|
||||
__METHOD__,
|
||||
);
|
||||
}
|
||||
|
||||
$this->attributes['autoGenerateProxyClasses'] = (int) $autoGenerate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the namespace where proxy classes reside.
|
||||
*/
|
||||
public function getProxyNamespace(): string|null
|
||||
{
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/12005',
|
||||
'Calling %s is deprecated and will not be possible in Doctrine ORM 4.0.',
|
||||
__METHOD__,
|
||||
);
|
||||
}
|
||||
|
||||
return $this->attributes['proxyNamespace'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the namespace where proxy classes reside.
|
||||
*/
|
||||
public function setProxyNamespace(string $ns): void
|
||||
{
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
Deprecation::triggerIfCalledFromOutside(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/12005',
|
||||
'Calling %s is deprecated and will not be possible in Doctrine ORM 4.0.',
|
||||
__METHOD__,
|
||||
);
|
||||
}
|
||||
|
||||
$this->attributes['proxyNamespace'] = $ns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver implementation that is used for metadata caching.
|
||||
*
|
||||
@@ -650,70 +539,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
$this->attributes['schemaIgnoreClasses'] = $schemaIgnoreClasses;
|
||||
}
|
||||
|
||||
public function isNativeLazyObjectsEnabled(): bool
|
||||
{
|
||||
return $this->attributes['nativeLazyObjects'] ?? false;
|
||||
}
|
||||
|
||||
public function enableNativeLazyObjects(bool $nativeLazyObjects): void
|
||||
{
|
||||
if (PHP_VERSION_ID >= 80400 && ! $nativeLazyObjects) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/12005',
|
||||
'Disabling native lazy objects is deprecated and will be impossible in Doctrine ORM 4.0.',
|
||||
);
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID < 80400 && $nativeLazyObjects) {
|
||||
throw new LogicException('Lazy loading proxies require PHP 8.4 or higher.');
|
||||
}
|
||||
|
||||
$this->attributes['nativeLazyObjects'] = $nativeLazyObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated lazy ghost objects are always enabled
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function isLazyGhostObjectEnabled(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @deprecated lazy ghost objects cannot be disabled */
|
||||
public function setLazyGhostObjectEnabled(bool $flag): void
|
||||
{
|
||||
if (! $flag) {
|
||||
throw new LogicException(<<<'EXCEPTION'
|
||||
The lazy ghost object feature cannot be disabled anymore.
|
||||
Please remove the call to setLazyGhostObjectEnabled(false).
|
||||
EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated rejecting ID collisions in the identity map cannot be disabled */
|
||||
public function setRejectIdCollisionInIdentityMap(bool $flag): void
|
||||
{
|
||||
if (! $flag) {
|
||||
throw new LogicException(<<<'EXCEPTION'
|
||||
Rejecting ID collisions in the identity map cannot be disabled anymore.
|
||||
Please remove the call to setRejectIdCollisionInIdentityMap(false).
|
||||
EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated rejecting ID collisions in the identity map is always enabled
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function isRejectIdCollisionInIdentityMapEnabled(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setEagerFetchBatchSize(int $batchSize = 100): void
|
||||
{
|
||||
$this->attributes['fetchModeSubselectBatchSize'] = $batchSize;
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Decorator;
|
||||
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\Common\EventManagerInterface;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\Cache;
|
||||
@@ -24,6 +24,7 @@ use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Doctrine\Persistence\ObjectManagerDecorator;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Base class for EntityManager decorators
|
||||
@@ -37,136 +38,163 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements
|
||||
$this->wrapped = $wrapped;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getRepository(string $className): EntityRepository
|
||||
{
|
||||
return $this->wrapped->getRepository($className);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getMetadataFactory(): ClassMetadataFactory
|
||||
{
|
||||
return $this->wrapped->getMetadataFactory();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getClassMetadata(string $className): ClassMetadata
|
||||
{
|
||||
return $this->wrapped->getClassMetadata($className);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getConnection(): Connection
|
||||
{
|
||||
return $this->wrapped->getConnection();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getExpressionBuilder(): Expr
|
||||
{
|
||||
return $this->wrapped->getExpressionBuilder();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function beginTransaction(): void
|
||||
{
|
||||
$this->wrapped->beginTransaction();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function wrapInTransaction(callable $func): mixed
|
||||
{
|
||||
return $this->wrapped->wrapInTransaction($func);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function commit(): void
|
||||
{
|
||||
$this->wrapped->commit();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function rollback(): void
|
||||
{
|
||||
$this->wrapped->rollback();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createQuery(string $dql = ''): Query
|
||||
{
|
||||
return $this->wrapped->createQuery($dql);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createNativeQuery(string $sql, ResultSetMapping $rsm): NativeQuery
|
||||
{
|
||||
return $this->wrapped->createNativeQuery($sql, $rsm);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createQueryBuilder(): QueryBuilder
|
||||
{
|
||||
return $this->wrapped->createQueryBuilder();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getReference(string $entityName, mixed $id): object|null
|
||||
{
|
||||
return $this->wrapped->getReference($entityName, $id);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function close(): void
|
||||
{
|
||||
$this->wrapped->close();
|
||||
}
|
||||
|
||||
public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void
|
||||
#[Override]
|
||||
public function lock(object $entity, LockMode $lockMode, DateTimeInterface|int|null $lockVersion = null): void
|
||||
{
|
||||
$this->wrapped->lock($entity, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
public function find(string $className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
#[Override]
|
||||
public function find(string $className, mixed $id, LockMode|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
{
|
||||
return $this->wrapped->find($className, $id, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
public function refresh(object $object, LockMode|int|null $lockMode = null): void
|
||||
#[Override]
|
||||
public function refresh(object $object, LockMode|null $lockMode = null): void
|
||||
{
|
||||
$this->wrapped->refresh($object, $lockMode);
|
||||
}
|
||||
|
||||
public function getEventManager(): EventManager
|
||||
#[Override]
|
||||
public function getEventManager(): EventManagerInterface
|
||||
{
|
||||
return $this->wrapped->getEventManager();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getConfiguration(): Configuration
|
||||
{
|
||||
return $this->wrapped->getConfiguration();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isOpen(): bool
|
||||
{
|
||||
return $this->wrapped->isOpen();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getUnitOfWork(): UnitOfWork
|
||||
{
|
||||
return $this->wrapped->getUnitOfWork();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function newHydrator(string|int $hydrationMode): AbstractHydrator
|
||||
{
|
||||
return $this->wrapped->newHydrator($hydrationMode);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getProxyFactory(): ProxyFactory
|
||||
{
|
||||
return $this->wrapped->getProxyFactory();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getFilters(): FilterCollection
|
||||
{
|
||||
return $this->wrapped->getFilters();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isFiltersStateClean(): bool
|
||||
{
|
||||
return $this->wrapped->isFiltersStateClean();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function hasFilters(): bool
|
||||
{
|
||||
return $this->wrapped->hasFilters();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getCache(): Cache|null
|
||||
{
|
||||
return $this->wrapped->getCache();
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Doctrine\ORM;
|
||||
use BackedEnum;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\Common\EventManagerInterface;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\Exception\EntityManagerClosed;
|
||||
@@ -18,18 +19,17 @@ use Doctrine\ORM\Exception\UnrecognizedIdentifierFields;
|
||||
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
|
||||
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;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\Repository\RepositoryFactory;
|
||||
use Override;
|
||||
|
||||
use function array_keys;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function ltrim;
|
||||
use function method_exists;
|
||||
|
||||
/**
|
||||
* The EntityManager is the central access point to ORM functionality.
|
||||
@@ -72,7 +72,7 @@ class EntityManager implements EntityManagerInterface
|
||||
/**
|
||||
* The event manager that is the central point of the event system.
|
||||
*/
|
||||
private EventManager $eventManager;
|
||||
private EventManagerInterface $eventManager;
|
||||
|
||||
/**
|
||||
* The proxy factory used to create dynamic proxies.
|
||||
@@ -113,17 +113,13 @@ class EntityManager implements EntityManagerInterface
|
||||
public function __construct(
|
||||
private Connection $conn,
|
||||
private Configuration $config,
|
||||
EventManager|null $eventManager = null,
|
||||
EventManagerInterface|null $eventManager = null,
|
||||
) {
|
||||
if (! $config->getMetadataDriverImpl()) {
|
||||
throw MissingMappingDriverImplementation::create();
|
||||
}
|
||||
|
||||
$this->eventManager = $eventManager
|
||||
?? (method_exists($conn, 'getEventManager')
|
||||
? $conn->getEventManager()
|
||||
: new EventManager()
|
||||
);
|
||||
$this->eventManager = $eventManager ?? new EventManager();
|
||||
|
||||
$metadataFactoryClassName = $config->getClassMetadataFactoryName();
|
||||
|
||||
@@ -134,16 +130,7 @@ class EntityManager implements EntityManagerInterface
|
||||
|
||||
$this->repositoryFactory = $config->getRepositoryFactory();
|
||||
$this->unitOfWork = new UnitOfWork($this);
|
||||
if ($config->isNativeLazyObjectsEnabled()) {
|
||||
$this->proxyFactory = new ProxyFactory($this);
|
||||
} else {
|
||||
$this->proxyFactory = new ProxyFactory(
|
||||
$this,
|
||||
$config->getProxyDir(),
|
||||
$config->getProxyNamespace(),
|
||||
$config->getAutoGenerateProxyClasses(),
|
||||
);
|
||||
}
|
||||
$this->proxyFactory = new ProxyFactory($this);
|
||||
|
||||
if ($config->isSecondLevelCacheEnabled()) {
|
||||
$cacheConfig = $config->getSecondLevelCacheConfiguration();
|
||||
@@ -152,31 +139,37 @@ class EntityManager implements EntityManagerInterface
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getConnection(): Connection
|
||||
{
|
||||
return $this->conn;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getMetadataFactory(): ClassMetadataFactory
|
||||
{
|
||||
return $this->metadataFactory;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getExpressionBuilder(): Expr
|
||||
{
|
||||
return $this->expressionBuilder ??= new Expr();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function beginTransaction(): void
|
||||
{
|
||||
$this->conn->beginTransaction();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getCache(): Cache|null
|
||||
{
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function wrapInTransaction(callable $func): mixed
|
||||
{
|
||||
$this->conn->beginTransaction();
|
||||
@@ -202,11 +195,13 @@ class EntityManager implements EntityManagerInterface
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function commit(): void
|
||||
{
|
||||
$this->conn->commit();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function rollback(): void
|
||||
{
|
||||
$this->conn->rollBack();
|
||||
@@ -219,11 +214,13 @@ class EntityManager implements EntityManagerInterface
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getClassMetadata(string $className): Mapping\ClassMetadata
|
||||
{
|
||||
return $this->metadataFactory->getMetadataFor($className);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createQuery(string $dql = ''): Query
|
||||
{
|
||||
$query = new Query($this);
|
||||
@@ -235,6 +232,7 @@ class EntityManager implements EntityManagerInterface
|
||||
return $query;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createNativeQuery(string $sql, ResultSetMapping $rsm): NativeQuery
|
||||
{
|
||||
$query = new NativeQuery($this);
|
||||
@@ -245,6 +243,7 @@ class EntityManager implements EntityManagerInterface
|
||||
return $query;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function createQueryBuilder(): QueryBuilder
|
||||
{
|
||||
return new QueryBuilder($this);
|
||||
@@ -262,6 +261,7 @@ class EntityManager implements EntityManagerInterface
|
||||
* makes use of optimistic locking fails.
|
||||
* @throws ORMException
|
||||
*/
|
||||
#[Override]
|
||||
public function flush(): void
|
||||
{
|
||||
$this->errorIfClosed();
|
||||
@@ -271,7 +271,8 @@ class EntityManager implements EntityManagerInterface
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function find($className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
#[Override]
|
||||
public function find($className, mixed $id, LockMode|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
{
|
||||
$class = $this->metadataFactory->getMetadataFor(ltrim($className, '\\'));
|
||||
|
||||
@@ -289,7 +290,7 @@ class EntityManager implements EntityManagerInterface
|
||||
|
||||
foreach ($id as $i => $value) {
|
||||
if (is_object($value)) {
|
||||
$className = DefaultProxyClassNameResolver::getClass($value);
|
||||
$className = $value::class;
|
||||
if ($this->metadataFactory->hasMetadataFor($className)) {
|
||||
$id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
|
||||
|
||||
@@ -367,6 +368,7 @@ class EntityManager implements EntityManagerInterface
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getReference(string $entityName, mixed $id): object|null
|
||||
{
|
||||
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
|
||||
@@ -412,11 +414,13 @@ class EntityManager implements EntityManagerInterface
|
||||
* Clears the EntityManager. All entities that are currently managed
|
||||
* by this EntityManager become detached.
|
||||
*/
|
||||
#[Override]
|
||||
public function clear(): void
|
||||
{
|
||||
$this->unitOfWork->clear();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function close(): void
|
||||
{
|
||||
$this->clear();
|
||||
@@ -436,6 +440,7 @@ class EntityManager implements EntityManagerInterface
|
||||
* @throws ORMInvalidArgumentException
|
||||
* @throws ORMException
|
||||
*/
|
||||
#[Override]
|
||||
public function persist(object $object): void
|
||||
{
|
||||
$this->errorIfClosed();
|
||||
@@ -452,6 +457,7 @@ class EntityManager implements EntityManagerInterface
|
||||
* @throws ORMInvalidArgumentException
|
||||
* @throws ORMException
|
||||
*/
|
||||
#[Override]
|
||||
public function remove(object $object): void
|
||||
{
|
||||
$this->errorIfClosed();
|
||||
@@ -459,7 +465,8 @@ class EntityManager implements EntityManagerInterface
|
||||
$this->unitOfWork->remove($object);
|
||||
}
|
||||
|
||||
public function refresh(object $object, LockMode|int|null $lockMode = null): void
|
||||
#[Override]
|
||||
public function refresh(object $object, LockMode|null $lockMode = null): void
|
||||
{
|
||||
$this->errorIfClosed();
|
||||
|
||||
@@ -475,12 +482,14 @@ class EntityManager implements EntityManagerInterface
|
||||
*
|
||||
* @throws ORMInvalidArgumentException
|
||||
*/
|
||||
#[Override]
|
||||
public function detach(object $object): void
|
||||
{
|
||||
$this->unitOfWork->detach($object);
|
||||
}
|
||||
|
||||
public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void
|
||||
#[Override]
|
||||
public function lock(object $entity, LockMode $lockMode, DateTimeInterface|int|null $lockVersion = null): void
|
||||
{
|
||||
$this->unitOfWork->lock($entity, $lockMode, $lockVersion);
|
||||
}
|
||||
@@ -494,6 +503,7 @@ class EntityManager implements EntityManagerInterface
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
#[Override]
|
||||
public function getRepository(string $className): EntityRepository
|
||||
{
|
||||
return $this->repositoryFactory->getRepository($this, $className);
|
||||
@@ -504,6 +514,7 @@ class EntityManager implements EntityManagerInterface
|
||||
*
|
||||
* @return bool TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
|
||||
*/
|
||||
#[Override]
|
||||
public function contains(object $object): bool
|
||||
{
|
||||
return $this->unitOfWork->isScheduledForInsert($object)
|
||||
@@ -511,11 +522,13 @@ class EntityManager implements EntityManagerInterface
|
||||
&& ! $this->unitOfWork->isScheduledForDelete($object);
|
||||
}
|
||||
|
||||
public function getEventManager(): EventManager
|
||||
#[Override]
|
||||
public function getEventManager(): EventManagerInterface
|
||||
{
|
||||
return $this->eventManager;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getConfiguration(): Configuration
|
||||
{
|
||||
return $this->config;
|
||||
@@ -533,16 +546,19 @@ class EntityManager implements EntityManagerInterface
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isOpen(): bool
|
||||
{
|
||||
return ! $this->closed;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getUnitOfWork(): UnitOfWork
|
||||
{
|
||||
return $this->unitOfWork;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function newHydrator(string|int $hydrationMode): AbstractHydrator
|
||||
{
|
||||
return match ($hydrationMode) {
|
||||
@@ -556,11 +572,13 @@ class EntityManager implements EntityManagerInterface
|
||||
};
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getProxyFactory(): ProxyFactory
|
||||
{
|
||||
return $this->proxyFactory;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function initializeObject(object $obj): void
|
||||
{
|
||||
$this->unitOfWork->initializeObject($obj);
|
||||
@@ -569,33 +587,35 @@ class EntityManager implements EntityManagerInterface
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function isUninitializedObject($value): bool
|
||||
{
|
||||
return $this->unitOfWork->isUninitializedObject($value);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getFilters(): FilterCollection
|
||||
{
|
||||
return $this->filterCollection ??= new FilterCollection($this);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isFiltersStateClean(): bool
|
||||
{
|
||||
return $this->filterCollection === null || $this->filterCollection->isClean();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function hasFilters(): bool
|
||||
{
|
||||
return $this->filterCollection !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-param LockMode::* $lockMode
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
* @throws TransactionRequiredException
|
||||
*/
|
||||
private function checkLockRequirements(LockMode|int $lockMode, ClassMetadata $class): void
|
||||
private function checkLockRequirements(LockMode $lockMode, ClassMetadata $class): void
|
||||
{
|
||||
switch ($lockMode) {
|
||||
case LockMode::OPTIMISTIC:
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\Common\EventManagerInterface;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\Exception\ORMException;
|
||||
@@ -16,6 +16,7 @@ use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\Query\FilterCollection;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Override;
|
||||
|
||||
interface EntityManagerInterface extends ObjectManager
|
||||
{
|
||||
@@ -28,6 +29,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
#[Override]
|
||||
public function getRepository(string $className): EntityRepository;
|
||||
|
||||
/**
|
||||
@@ -40,6 +42,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
*/
|
||||
public function getConnection(): Connection;
|
||||
|
||||
#[Override]
|
||||
public function getMetadataFactory(): ClassMetadataFactory;
|
||||
|
||||
/**
|
||||
@@ -110,15 +113,13 @@ interface EntityManagerInterface extends ObjectManager
|
||||
/**
|
||||
* Finds an Entity by its identifier.
|
||||
*
|
||||
* @param string $className The class name of the entity to find.
|
||||
* @param mixed $id The identity of the entity to find.
|
||||
* @param LockMode|int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
|
||||
* or NULL if no specific lock mode should be used
|
||||
* during the search.
|
||||
* @param int|null $lockVersion The version of the entity to find when using
|
||||
* optimistic locking.
|
||||
* @param string $className The class name of the entity to find.
|
||||
* @param mixed $id The identity of the entity to find.
|
||||
* @param LockMode|null $lockMode The lock mode or NULL if no specific lock mode
|
||||
* should be used during the search.
|
||||
* @param int|null $lockVersion The version of the entity to find when using
|
||||
* optimistic locking.
|
||||
* @phpstan-param class-string<T> $className
|
||||
* @phpstan-param LockMode::*|null $lockMode
|
||||
*
|
||||
* @return object|null The entity instance or NULL if the entity can not be found.
|
||||
* @phpstan-return T|null
|
||||
@@ -130,22 +131,22 @@ interface EntityManagerInterface extends ObjectManager
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
public function find(string $className, mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null;
|
||||
#[Override]
|
||||
public function find(string $className, mixed $id, LockMode|null $lockMode = null, int|null $lockVersion = null): object|null;
|
||||
|
||||
/**
|
||||
* Refreshes the persistent state of an object from the database,
|
||||
* overriding any local changes that have not yet been persisted.
|
||||
*
|
||||
* @param LockMode|int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
|
||||
* or NULL if no specific lock mode should be used
|
||||
* during the search.
|
||||
* @phpstan-param LockMode::*|null $lockMode
|
||||
* @param LockMode|null $lockMode The lock mode or NULL if no specific lock mode
|
||||
* should be used during the search.
|
||||
*
|
||||
* @throws ORMInvalidArgumentException
|
||||
* @throws ORMException
|
||||
* @throws TransactionRequiredException
|
||||
*/
|
||||
public function refresh(object $object, LockMode|int|null $lockMode = null): void;
|
||||
#[Override]
|
||||
public function refresh(object $object, LockMode|null $lockMode = null): void;
|
||||
|
||||
/**
|
||||
* Gets a reference to the entity identified by the given type and identifier
|
||||
@@ -172,17 +173,15 @@ interface EntityManagerInterface extends ObjectManager
|
||||
/**
|
||||
* Acquire a lock on the given entity.
|
||||
*
|
||||
* @phpstan-param LockMode::* $lockMode
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
* @throws PessimisticLockException
|
||||
*/
|
||||
public function lock(object $entity, LockMode|int $lockMode, DateTimeInterface|int|null $lockVersion = null): void;
|
||||
public function lock(object $entity, LockMode $lockMode, DateTimeInterface|int|null $lockVersion = null): void;
|
||||
|
||||
/**
|
||||
* Gets the EventManager used by the EntityManager.
|
||||
* Gets the EventManagerInterface used by the EntityManager.
|
||||
*/
|
||||
public function getEventManager(): EventManager;
|
||||
public function getEventManager(): EventManagerInterface;
|
||||
|
||||
/**
|
||||
* Gets the Configuration used by the EntityManager.
|
||||
@@ -237,5 +236,6 @@ interface EntityManagerInterface extends ObjectManager
|
||||
*
|
||||
* @phpstan-template T of object
|
||||
*/
|
||||
#[Override]
|
||||
public function getClassMetadata(string $className): Mapping\ClassMetadata;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
use Doctrine\ORM\Repository\Exception\InvalidMagicMethodCall;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use Override;
|
||||
|
||||
use function array_slice;
|
||||
use function lcfirst;
|
||||
@@ -73,15 +74,14 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
/**
|
||||
* Finds an entity by its primary key / identifier.
|
||||
*
|
||||
* @param LockMode|int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
|
||||
* or NULL if no specific lock mode should be used
|
||||
* during the search.
|
||||
* @phpstan-param LockMode::*|null $lockMode
|
||||
* @param LockMode|null $lockMode The lock mode or NULL if no specific lock mode
|
||||
* should be used during the search.
|
||||
*
|
||||
* @return object|null The entity instance or NULL if the entity can not be found.
|
||||
* @phpstan-return ?T
|
||||
*/
|
||||
public function find(mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
#[Override]
|
||||
public function find(mixed $id, LockMode|null $lockMode = null, int|null $lockVersion = null): object|null
|
||||
{
|
||||
return $this->em->find($this->entityName, $id, $lockMode, $lockVersion);
|
||||
}
|
||||
@@ -91,6 +91,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
*
|
||||
* @phpstan-return list<T> The entities.
|
||||
*/
|
||||
#[Override]
|
||||
public function findAll(): array
|
||||
{
|
||||
return $this->findBy([]);
|
||||
@@ -103,6 +104,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
*
|
||||
* @phpstan-return list<T>
|
||||
*/
|
||||
#[Override]
|
||||
public function findBy(array $criteria, array|null $orderBy = null, int|null $limit = null, int|null $offset = null): array
|
||||
{
|
||||
$persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
|
||||
@@ -118,6 +120,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
*
|
||||
* @phpstan-return T|null
|
||||
*/
|
||||
#[Override]
|
||||
public function findOneBy(array $criteria, array|null $orderBy = null): object|null
|
||||
{
|
||||
$persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
|
||||
@@ -175,6 +178,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
return $this->entityName;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getClassName(): string
|
||||
{
|
||||
return $this->getEntityName();
|
||||
@@ -197,6 +201,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
*
|
||||
* @phpstan-return AbstractLazyCollection<int, T>&Selectable<int, T>
|
||||
*/
|
||||
#[Override]
|
||||
public function matching(Criteria $criteria): AbstractLazyCollection&Selectable
|
||||
{
|
||||
$persister = $this->em->getUnitOfWork()->getEntityPersister($this->entityName);
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\Common\EventDispatcher;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\EntityListenerResolver;
|
||||
@@ -15,21 +15,21 @@ use Doctrine\ORM\Mapping\EntityListenerResolver;
|
||||
*/
|
||||
class ListenersInvoker
|
||||
{
|
||||
final public const INVOKE_NONE = 0;
|
||||
final public const INVOKE_LISTENERS = 1;
|
||||
final public const INVOKE_CALLBACKS = 2;
|
||||
final public const INVOKE_MANAGER = 4;
|
||||
final public const int INVOKE_NONE = 0;
|
||||
final public const int INVOKE_LISTENERS = 1;
|
||||
final public const int INVOKE_CALLBACKS = 2;
|
||||
final public const int INVOKE_MANAGER = 4;
|
||||
|
||||
/** The Entity listener resolver. */
|
||||
private readonly EntityListenerResolver $resolver;
|
||||
|
||||
/** The EventManager used for dispatching events. */
|
||||
private readonly EventManager $eventManager;
|
||||
/** The EventDispatcher used for dispatching events. */
|
||||
private readonly EventDispatcher $eventDispatcher;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->eventManager = $em->getEventManager();
|
||||
$this->resolver = $em->getConfiguration()->getEntityListenerResolver();
|
||||
$this->eventDispatcher = $em->getEventManager();
|
||||
$this->resolver = $em->getConfiguration()->getEntityListenerResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +52,7 @@ class ListenersInvoker
|
||||
$invoke |= self::INVOKE_LISTENERS;
|
||||
}
|
||||
|
||||
if ($this->eventManager->hasListeners($eventName)) {
|
||||
if ($this->eventDispatcher->hasListeners($eventName)) {
|
||||
$invoke |= self::INVOKE_MANAGER;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class ListenersInvoker
|
||||
}
|
||||
|
||||
if ($invoke & self::INVOKE_MANAGER) {
|
||||
$this->eventManager->dispatchEvent($eventName, $event);
|
||||
$this->eventDispatcher->dispatchEvent($eventName, $event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const preRemove = 'preRemove';
|
||||
final public const string preRemove = 'preRemove';
|
||||
|
||||
/**
|
||||
* The postRemove event occurs for an entity after the entity has
|
||||
@@ -32,7 +32,7 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const postRemove = 'postRemove';
|
||||
final public const string postRemove = 'postRemove';
|
||||
|
||||
/**
|
||||
* The prePersist event occurs for a given entity before the respective
|
||||
@@ -40,7 +40,7 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const prePersist = 'prePersist';
|
||||
final public const string prePersist = 'prePersist';
|
||||
|
||||
/**
|
||||
* The postPersist event occurs for an entity after the entity has
|
||||
@@ -49,7 +49,7 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const postPersist = 'postPersist';
|
||||
final public const string postPersist = 'postPersist';
|
||||
|
||||
/**
|
||||
* The preUpdate event occurs before the database update operations to
|
||||
@@ -57,7 +57,7 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const preUpdate = 'preUpdate';
|
||||
final public const string preUpdate = 'preUpdate';
|
||||
|
||||
/**
|
||||
* The postUpdate event occurs after the database update operations to
|
||||
@@ -65,7 +65,7 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const postUpdate = 'postUpdate';
|
||||
final public const string postUpdate = 'postUpdate';
|
||||
|
||||
/**
|
||||
* The postLoad event occurs for an entity after the entity has been loaded
|
||||
@@ -78,26 +78,26 @@ final class Events
|
||||
*
|
||||
* This is an entity lifecycle event.
|
||||
*/
|
||||
public const postLoad = 'postLoad';
|
||||
final public const string postLoad = 'postLoad';
|
||||
|
||||
/**
|
||||
* The loadClassMetadata event occurs after the mapping metadata for a class
|
||||
* has been loaded from a mapping source (attributes/xml).
|
||||
*/
|
||||
public const loadClassMetadata = 'loadClassMetadata';
|
||||
final public const string loadClassMetadata = 'loadClassMetadata';
|
||||
|
||||
/**
|
||||
* The onClassMetadataNotFound event occurs whenever loading metadata for a class
|
||||
* failed.
|
||||
*/
|
||||
public const onClassMetadataNotFound = 'onClassMetadataNotFound';
|
||||
final public const string onClassMetadataNotFound = 'onClassMetadataNotFound';
|
||||
|
||||
/**
|
||||
* The preFlush event occurs when the EntityManager#flush() operation is invoked,
|
||||
* but before any changes to managed entities have been calculated. This event is
|
||||
* always raised right after EntityManager#flush() call.
|
||||
*/
|
||||
public const preFlush = 'preFlush';
|
||||
final public const string preFlush = 'preFlush';
|
||||
|
||||
/**
|
||||
* The onFlush event occurs when the EntityManager#flush() operation is invoked,
|
||||
@@ -105,7 +105,7 @@ final class Events
|
||||
* actual database operations are executed. The event is only raised if there is
|
||||
* actually something to do for the underlying UnitOfWork.
|
||||
*/
|
||||
public const onFlush = 'onFlush';
|
||||
final public const string onFlush = 'onFlush';
|
||||
|
||||
/**
|
||||
* The postFlush event occurs when the EntityManager#flush() operation is invoked and
|
||||
@@ -113,11 +113,11 @@ final class Events
|
||||
* actually something to do for the underlying UnitOfWork. The event won't be raised if an error occurs during the
|
||||
* flush operation.
|
||||
*/
|
||||
public const postFlush = 'postFlush';
|
||||
final public const string postFlush = 'postFlush';
|
||||
|
||||
/**
|
||||
* The onClear event occurs when the EntityManager#clear() operation is invoked,
|
||||
* after all references to entities have been removed from the unit of work.
|
||||
*/
|
||||
public const onClear = 'onClear';
|
||||
final public const string onClear = 'onClear';
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use function sprintf;
|
||||
|
||||
final class MultipleSelectorsFoundException extends LogicException implements ORMException
|
||||
{
|
||||
public const MULTIPLE_SELECTORS_FOUND_EXCEPTION = 'Multiple selectors found: %s. Please select only one.';
|
||||
final public const string MULTIPLE_SELECTORS_FOUND_EXCEPTION = 'Multiple selectors found: %s. Please select only one.';
|
||||
|
||||
/** @param string[] $selectors */
|
||||
public static function create(array $selectors): self
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
/** @deprecated */
|
||||
final class NotSupported extends LogicException implements ORMException
|
||||
{
|
||||
public static function create(): self
|
||||
{
|
||||
return new self('This behaviour is (currently) not supported by Doctrine 2');
|
||||
}
|
||||
|
||||
public static function createForDbal3(string $context): self
|
||||
{
|
||||
return new self(sprintf(
|
||||
<<<'EXCEPTION'
|
||||
Context: %s
|
||||
Problem: Feature was deprecated in doctrine/dbal 2.x and is not supported by installed doctrine/dbal:3.x
|
||||
Solution: See the doctrine/deprecations logs for new alternative approaches.
|
||||
EXCEPTION
|
||||
,
|
||||
$context,
|
||||
));
|
||||
}
|
||||
|
||||
public static function createForPersistence3(string $context): self
|
||||
{
|
||||
return new self(sprintf(
|
||||
<<<'EXCEPTION'
|
||||
Context: %s
|
||||
Problem: Feature was deprecated in doctrine/persistence 2.x and is not supported by installed doctrine/persistence:3.x
|
||||
Solution: See the doctrine/deprecations logs for new alternative approaches.
|
||||
EXCEPTION
|
||||
,
|
||||
$context,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Exception\EntityMissingAssignedId;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Special generator for application-assigned identifiers (doesn't really generate anything).
|
||||
@@ -19,6 +20,7 @@ class AssignedGenerator extends AbstractIdGenerator
|
||||
*
|
||||
* @throws EntityMissingAssignedId
|
||||
*/
|
||||
#[Override]
|
||||
public function generateId(EntityManagerInterface $em, object|null $entity): array
|
||||
{
|
||||
$class = $em->getClassMetadata($entity::class);
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Id generator that obtains IDs from special "identity" columns. These are columns
|
||||
@@ -13,11 +14,13 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
*/
|
||||
class BigIntegerIdentityGenerator extends AbstractIdGenerator
|
||||
{
|
||||
#[Override]
|
||||
public function generateId(EntityManagerInterface $em, object|null $entity): string
|
||||
{
|
||||
return (string) $em->getConnection()->lastInsertId();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isPostInsertGenerator(): bool
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Id generator that obtains IDs from special "identity" columns. These are columns
|
||||
@@ -13,11 +14,13 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
*/
|
||||
class IdentityGenerator extends AbstractIdGenerator
|
||||
{
|
||||
#[Override]
|
||||
public function generateId(EntityManagerInterface $em, object|null $entity): int
|
||||
{
|
||||
return (int) $em->getConnection()->lastInsertId();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isPostInsertGenerator(): bool
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -5,17 +5,13 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Serializable;
|
||||
|
||||
use function serialize;
|
||||
use function unserialize;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Represents an ID generator that uses a database sequence.
|
||||
*/
|
||||
class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
class SequenceGenerator extends AbstractIdGenerator
|
||||
{
|
||||
private int $nextValue = 0;
|
||||
private int|null $maxValue = null;
|
||||
@@ -32,6 +28,7 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
) {
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function generateId(EntityManagerInterface $em, object|null $entity): int
|
||||
{
|
||||
if ($this->maxValue === null || $this->nextValue === $this->maxValue) {
|
||||
@@ -66,20 +63,6 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
return $this->nextValue;
|
||||
}
|
||||
|
||||
/** @deprecated without replacement. */
|
||||
final public function serialize(): string
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/11468',
|
||||
'%s() is deprecated, use __serialize() instead. %s won\'t implement the Serializable interface anymore in ORM 4.',
|
||||
__METHOD__,
|
||||
self::class,
|
||||
);
|
||||
|
||||
return serialize($this->__serialize());
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function __serialize(): array
|
||||
{
|
||||
@@ -89,20 +72,6 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
];
|
||||
}
|
||||
|
||||
/** @deprecated without replacement. */
|
||||
final public function unserialize(string $serialized): void
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/11468',
|
||||
'%s() is deprecated, use __unserialize() instead. %s won\'t implement the Serializable interface anymore in ORM 4.',
|
||||
__METHOD__,
|
||||
self::class,
|
||||
);
|
||||
|
||||
$this->__unserialize(unserialize($serialized));
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $data */
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use Override;
|
||||
|
||||
use function array_key_last;
|
||||
use function count;
|
||||
use function is_array;
|
||||
@@ -32,6 +34,7 @@ class ArrayHydrator extends AbstractHydrator
|
||||
|
||||
private int $resultCounter = 0;
|
||||
|
||||
#[Override]
|
||||
protected function prepare(): void
|
||||
{
|
||||
$this->isSimpleQuery = count($this->resultSetMapping()->aliasMap) <= 1;
|
||||
@@ -46,6 +49,7 @@ class ArrayHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateAllData(): array
|
||||
{
|
||||
$result = [];
|
||||
@@ -60,6 +64,7 @@ class ArrayHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateRowData(array $row, array &$result): void
|
||||
{
|
||||
// 1) Initialize
|
||||
|
||||
@@ -10,6 +10,7 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Override;
|
||||
|
||||
use function array_fill_keys;
|
||||
use function array_keys;
|
||||
@@ -51,6 +52,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
/** @var mixed[] */
|
||||
private array $existingCollections = [];
|
||||
|
||||
#[Override]
|
||||
protected function prepare(): void
|
||||
{
|
||||
if (! isset($this->hints[UnitOfWork::HINT_DEFEREAGERLOAD])) {
|
||||
@@ -108,6 +110,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function cleanup(): void
|
||||
{
|
||||
$eagerLoad = isset($this->hints[UnitOfWork::HINT_DEFEREAGERLOAD]) && $this->hints[UnitOfWork::HINT_DEFEREAGERLOAD] === true;
|
||||
@@ -127,6 +130,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$this->uow->hydrationComplete();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function cleanupAfterRowIteration(): void
|
||||
{
|
||||
$this->identifierMap =
|
||||
@@ -139,6 +143,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateAllData(): array
|
||||
{
|
||||
$result = [];
|
||||
@@ -318,6 +323,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
* @param mixed[] $row The data of the row to process.
|
||||
* @param mixed[] $result The result array to fill.
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateRowData(array $row, array &$result): void
|
||||
{
|
||||
// Initialize
|
||||
@@ -574,6 +580,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
|
||||
/** @param mixed[] $data pre-hydrated SQL Result Row. */
|
||||
#[Override]
|
||||
protected function hydrateNestedEntity(array $data, string $dqlAlias): mixed
|
||||
{
|
||||
if (isset($this->resultSetMapping()->nestedEntities[$dqlAlias])) {
|
||||
@@ -587,6 +594,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
* When executed in a hydrate() loop we may have to clear internal state to
|
||||
* decrease memory consumption.
|
||||
*/
|
||||
#[Override]
|
||||
public function onClear(mixed $eventArgs): void
|
||||
{
|
||||
parent::onClear($eventArgs);
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use Doctrine\DBAL\Driver\Exception;
|
||||
use Doctrine\ORM\Exception\MultipleSelectorsFoundException;
|
||||
use Override;
|
||||
|
||||
use function count;
|
||||
|
||||
@@ -20,6 +21,7 @@ final class ScalarColumnHydrator extends AbstractHydrator
|
||||
* @throws MultipleSelectorsFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateAllData(): array
|
||||
{
|
||||
if (count($this->resultSetMapping()->fieldMappings) > 1) {
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Hydrator that produces flat, rectangular results of scalar data.
|
||||
* The created result is almost the same as a regular SQL result set, except
|
||||
@@ -14,6 +16,7 @@ class ScalarHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateAllData(): array
|
||||
{
|
||||
$result = [];
|
||||
@@ -28,6 +31,7 @@ class ScalarHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateRowData(array $row, array &$result): void
|
||||
{
|
||||
$result[] = $this->gatherScalarRowData($row);
|
||||
|
||||
@@ -9,6 +9,7 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Doctrine\ORM\Query;
|
||||
use Exception;
|
||||
use Override;
|
||||
use RuntimeException;
|
||||
use ValueError;
|
||||
|
||||
@@ -28,6 +29,7 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
|
||||
private ClassMetadata|null $class = null;
|
||||
|
||||
#[Override]
|
||||
protected function prepare(): void
|
||||
{
|
||||
if (count($this->resultSetMapping()->aliasMap) !== 1) {
|
||||
@@ -41,6 +43,7 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
$this->class = $this->getClassMetadata(reset($this->resultSetMapping()->aliasMap));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function cleanup(): void
|
||||
{
|
||||
parent::cleanup();
|
||||
@@ -52,6 +55,7 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateAllData(): array
|
||||
{
|
||||
$result = [];
|
||||
@@ -68,6 +72,7 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function hydrateRowData(array $row, array &$result): void
|
||||
{
|
||||
assert($this->class !== null);
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use Doctrine\ORM\NonUniqueResultException;
|
||||
use Doctrine\ORM\NoResultException;
|
||||
use Override;
|
||||
|
||||
use function array_shift;
|
||||
use function count;
|
||||
@@ -16,6 +17,7 @@ use function key;
|
||||
*/
|
||||
class SingleScalarHydrator extends AbstractHydrator
|
||||
{
|
||||
#[Override]
|
||||
protected function hydrateAllData(): mixed
|
||||
{
|
||||
$data = $this->statement()->fetchAllAssociative();
|
||||
|
||||
@@ -25,9 +25,9 @@ use function spl_object_id;
|
||||
*/
|
||||
final class StronglyConnectedComponents
|
||||
{
|
||||
private const NOT_VISITED = 1;
|
||||
private const IN_PROGRESS = 2;
|
||||
private const VISITED = 3;
|
||||
private const int NOT_VISITED = 1;
|
||||
private const int IN_PROGRESS = 2;
|
||||
private const int VISITED = 3;
|
||||
|
||||
/**
|
||||
* Array of all nodes, indexed by object ids.
|
||||
|
||||
@@ -20,9 +20,9 @@ use function spl_object_id;
|
||||
*/
|
||||
final class TopologicalSort
|
||||
{
|
||||
private const NOT_VISITED = 1;
|
||||
private const IN_PROGRESS = 2;
|
||||
private const VISITED = 3;
|
||||
private const int NOT_VISITED = 1;
|
||||
private const int IN_PROGRESS = 2;
|
||||
private const int VISITED = 3;
|
||||
|
||||
/**
|
||||
* Array of all nodes, indexed by object ids.
|
||||
|
||||
@@ -10,8 +10,7 @@ use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\ReadableCollection;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
|
||||
use function assert;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* A lazy collection that allows a fast count when using criteria object
|
||||
@@ -26,6 +25,7 @@ use function assert;
|
||||
*/
|
||||
class LazyCriteriaCollection extends AbstractLazyCollection implements Selectable
|
||||
{
|
||||
/** @var non-negative-int|null */
|
||||
private int|null $count = null;
|
||||
|
||||
public function __construct(
|
||||
@@ -37,6 +37,7 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
|
||||
/**
|
||||
* Do an efficient count on the collection
|
||||
*/
|
||||
#[Override]
|
||||
public function count(): int
|
||||
{
|
||||
if ($this->isInitialized()) {
|
||||
@@ -54,6 +55,7 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
|
||||
/**
|
||||
* check if collection is empty without loading it
|
||||
*/
|
||||
#[Override]
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
if ($this->isInitialized()) {
|
||||
@@ -70,6 +72,7 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
|
||||
*
|
||||
* @return bool TRUE if the collection contains $element, FALSE otherwise.
|
||||
*/
|
||||
#[Override]
|
||||
public function contains(mixed $element): bool
|
||||
{
|
||||
if ($this->isInitialized()) {
|
||||
@@ -80,14 +83,15 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
|
||||
}
|
||||
|
||||
/** @return ReadableCollection<TKey, TValue>&Selectable<TKey, TValue> */
|
||||
#[Override]
|
||||
public function matching(Criteria $criteria): ReadableCollection&Selectable
|
||||
{
|
||||
$this->initialize();
|
||||
assert($this->collection instanceof Selectable);
|
||||
|
||||
return $this->collection->matching($criteria);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function doInitialize(): void
|
||||
{
|
||||
$elements = $this->entityPersister->loadCriteria($this->criteria);
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\ORM\Internal\SQLResultCasing;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* ANSI compliant quote strategy, this strategy does not apply any quote.
|
||||
@@ -15,6 +16,7 @@ class AnsiQuoteStrategy implements QuoteStrategy
|
||||
{
|
||||
use SQLResultCasing;
|
||||
|
||||
#[Override]
|
||||
public function getColumnName(
|
||||
string $fieldName,
|
||||
ClassMetadata $class,
|
||||
@@ -23,6 +25,7 @@ class AnsiQuoteStrategy implements QuoteStrategy
|
||||
return $class->fieldMappings[$fieldName]->columnName;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getTableName(ClassMetadata $class, AbstractPlatform $platform): string
|
||||
{
|
||||
return $class->table['name'];
|
||||
@@ -31,16 +34,19 @@ class AnsiQuoteStrategy implements QuoteStrategy
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string
|
||||
{
|
||||
return $definition['sequenceName'];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getJoinColumnName(JoinColumnMapping $joinColumn, ClassMetadata $class, AbstractPlatform $platform): string
|
||||
{
|
||||
return $joinColumn->name;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getReferencedJoinColumnName(
|
||||
JoinColumnMapping $joinColumn,
|
||||
ClassMetadata $class,
|
||||
@@ -49,6 +55,7 @@ class AnsiQuoteStrategy implements QuoteStrategy
|
||||
return $joinColumn->referencedColumnName;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getJoinTableName(
|
||||
ManyToManyOwningSideMapping $association,
|
||||
ClassMetadata $class,
|
||||
@@ -60,11 +67,13 @@ class AnsiQuoteStrategy implements QuoteStrategy
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform): array
|
||||
{
|
||||
return $class->identifier;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getColumnAlias(
|
||||
string $columnName,
|
||||
int $counter,
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function property_exists;
|
||||
|
||||
/** @internal */
|
||||
trait ArrayAccessImplementation
|
||||
{
|
||||
/** @param string $offset */
|
||||
public function offsetExists(mixed $offset): bool
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/11211',
|
||||
'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.',
|
||||
static::class,
|
||||
);
|
||||
|
||||
return isset($this->$offset);
|
||||
}
|
||||
|
||||
/** @param string $offset */
|
||||
public function offsetGet(mixed $offset): mixed
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/11211',
|
||||
'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.',
|
||||
static::class,
|
||||
);
|
||||
|
||||
if (! property_exists($this, $offset)) {
|
||||
throw new InvalidArgumentException('Undefined property: ' . $offset);
|
||||
}
|
||||
|
||||
return $this->$offset;
|
||||
}
|
||||
|
||||
/** @param string $offset */
|
||||
public function offsetSet(mixed $offset, mixed $value): void
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/11211',
|
||||
'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.',
|
||||
static::class,
|
||||
);
|
||||
|
||||
$this->$offset = $value;
|
||||
}
|
||||
|
||||
/** @param string $offset */
|
||||
public function offsetUnset(mixed $offset): void
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/11211',
|
||||
'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.',
|
||||
static::class,
|
||||
);
|
||||
|
||||
$this->$offset = null;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use ArrayAccess;
|
||||
use Exception;
|
||||
use OutOfRangeException;
|
||||
|
||||
@@ -14,8 +13,7 @@ use function in_array;
|
||||
use function property_exists;
|
||||
use function sprintf;
|
||||
|
||||
/** @template-implements ArrayAccess<string, mixed> */
|
||||
abstract class AssociationMapping implements ArrayAccess
|
||||
abstract class AssociationMapping
|
||||
{
|
||||
/**
|
||||
* The names of persistence operations to cascade on the association.
|
||||
|
||||
@@ -17,7 +17,7 @@ use function get_class_methods;
|
||||
class EntityListenerBuilder
|
||||
{
|
||||
/** Hash-map to handle event names. */
|
||||
private const EVENTS = [
|
||||
private const array EVENTS = [
|
||||
Events::preRemove => true,
|
||||
Events::postRemove => true,
|
||||
Events::prePersist => true,
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Builder;
|
||||
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* ManyToMany Association Builder
|
||||
*
|
||||
@@ -29,6 +31,7 @@ class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
#[Override]
|
||||
public function addJoinColumn(
|
||||
string $columnName,
|
||||
string $referencedColumnName,
|
||||
@@ -72,6 +75,7 @@ class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function build(): ClassMetadataBuilder
|
||||
{
|
||||
$mapping = $this->mapping;
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Builder;
|
||||
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* OneToMany Association Builder
|
||||
*
|
||||
@@ -31,6 +33,7 @@ class OneToManyAssociationBuilder extends AssociationBuilder
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function build(): ClassMetadataBuilder
|
||||
{
|
||||
$mapping = $this->mapping;
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Doctrine\ORM\Internal\NoUnknownNamedArguments;
|
||||
use Override;
|
||||
use ReflectionProperty;
|
||||
|
||||
final class ChainTypedFieldMapper implements TypedFieldMapper
|
||||
@@ -24,6 +25,7 @@ final class ChainTypedFieldMapper implements TypedFieldMapper
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function validateAndComplete(array $mapping, ReflectionProperty $field): array
|
||||
{
|
||||
foreach ($this->typedFieldMappers as $typedFieldMapper) {
|
||||
|
||||
@@ -22,9 +22,9 @@ use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
|
||||
use Doctrine\Persistence\Mapping\ReflectionService;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use Override;
|
||||
use ReflectionClass;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionProperty;
|
||||
use Stringable;
|
||||
|
||||
use function array_column;
|
||||
@@ -80,40 +80,38 @@ use function trim;
|
||||
*/
|
||||
class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
{
|
||||
use GetReflectionClassImplementation;
|
||||
|
||||
/* The inheritance mapping types */
|
||||
/**
|
||||
* NONE means the class does not participate in an inheritance hierarchy
|
||||
* and therefore does not need an inheritance mapping type.
|
||||
*/
|
||||
public const INHERITANCE_TYPE_NONE = 1;
|
||||
final public const int INHERITANCE_TYPE_NONE = 1;
|
||||
|
||||
/**
|
||||
* JOINED means the class will be persisted according to the rules of
|
||||
* <tt>Class Table Inheritance</tt>.
|
||||
*/
|
||||
public const INHERITANCE_TYPE_JOINED = 2;
|
||||
final public const int INHERITANCE_TYPE_JOINED = 2;
|
||||
|
||||
/**
|
||||
* SINGLE_TABLE means the class will be persisted according to the rules of
|
||||
* <tt>Single Table Inheritance</tt>.
|
||||
*/
|
||||
public const INHERITANCE_TYPE_SINGLE_TABLE = 3;
|
||||
final public const int INHERITANCE_TYPE_SINGLE_TABLE = 3;
|
||||
|
||||
/* The Id generator types. */
|
||||
/**
|
||||
* AUTO means the generator type will depend on what the used platform prefers.
|
||||
* Offers full portability.
|
||||
*/
|
||||
public const GENERATOR_TYPE_AUTO = 1;
|
||||
final public const int GENERATOR_TYPE_AUTO = 1;
|
||||
|
||||
/**
|
||||
* SEQUENCE means a separate sequence object will be used. Platforms that do
|
||||
* not have native sequence support may emulate it. Full portability is currently
|
||||
* not guaranteed.
|
||||
*/
|
||||
public const GENERATOR_TYPE_SEQUENCE = 2;
|
||||
final public const int GENERATOR_TYPE_SEQUENCE = 2;
|
||||
|
||||
/**
|
||||
* IDENTITY means an identity column is used for id generation. The database
|
||||
@@ -121,18 +119,18 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* native identity columns may emulate them. Full portability is currently
|
||||
* not guaranteed.
|
||||
*/
|
||||
public const GENERATOR_TYPE_IDENTITY = 4;
|
||||
final public const int GENERATOR_TYPE_IDENTITY = 4;
|
||||
|
||||
/**
|
||||
* NONE means the class does not have a generated id. That means the class
|
||||
* must have a natural, manually assigned id.
|
||||
*/
|
||||
public const GENERATOR_TYPE_NONE = 5;
|
||||
final public const int GENERATOR_TYPE_NONE = 5;
|
||||
|
||||
/**
|
||||
* CUSTOM means that customer will use own ID generator that supposedly work
|
||||
*/
|
||||
public const GENERATOR_TYPE_CUSTOM = 7;
|
||||
final public const int GENERATOR_TYPE_CUSTOM = 7;
|
||||
|
||||
/**
|
||||
* DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
|
||||
@@ -141,92 +139,92 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
*
|
||||
* This is the default change tracking policy.
|
||||
*/
|
||||
public const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
|
||||
final public const int CHANGETRACKING_DEFERRED_IMPLICIT = 1;
|
||||
|
||||
/**
|
||||
* DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
|
||||
* by doing a property-by-property comparison with the original data. This will
|
||||
* be done only for entities that were explicitly saved (through persist() or a cascade).
|
||||
*/
|
||||
public const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
|
||||
final public const int CHANGETRACKING_DEFERRED_EXPLICIT = 2;
|
||||
|
||||
/**
|
||||
* Specifies that an association is to be fetched when it is first accessed.
|
||||
*/
|
||||
public const FETCH_LAZY = 2;
|
||||
final public const int FETCH_LAZY = 2;
|
||||
|
||||
/**
|
||||
* Specifies that an association is to be fetched when the owner of the
|
||||
* association is fetched.
|
||||
*/
|
||||
public const FETCH_EAGER = 3;
|
||||
final public const int FETCH_EAGER = 3;
|
||||
|
||||
/**
|
||||
* Specifies that an association is to be fetched lazy (on first access) and that
|
||||
* commands such as Collection#count, Collection#slice are issued directly against
|
||||
* the database if the collection is not yet initialized.
|
||||
*/
|
||||
public const FETCH_EXTRA_LAZY = 4;
|
||||
final public const int FETCH_EXTRA_LAZY = 4;
|
||||
|
||||
/**
|
||||
* Identifies a one-to-one association.
|
||||
*/
|
||||
public const ONE_TO_ONE = 1;
|
||||
final public const int ONE_TO_ONE = 1;
|
||||
|
||||
/**
|
||||
* Identifies a many-to-one association.
|
||||
*/
|
||||
public const MANY_TO_ONE = 2;
|
||||
final public const int MANY_TO_ONE = 2;
|
||||
|
||||
/**
|
||||
* Identifies a one-to-many association.
|
||||
*/
|
||||
public const ONE_TO_MANY = 4;
|
||||
final public const int ONE_TO_MANY = 4;
|
||||
|
||||
/**
|
||||
* Identifies a many-to-many association.
|
||||
*/
|
||||
public const MANY_TO_MANY = 8;
|
||||
final public const int MANY_TO_MANY = 8;
|
||||
|
||||
/**
|
||||
* Combined bitmask for to-one (single-valued) associations.
|
||||
*/
|
||||
public const TO_ONE = 3;
|
||||
final public const int TO_ONE = 3;
|
||||
|
||||
/**
|
||||
* Combined bitmask for to-many (collection-valued) associations.
|
||||
*/
|
||||
public const TO_MANY = 12;
|
||||
final public const int TO_MANY = 12;
|
||||
|
||||
/**
|
||||
* ReadOnly cache can do reads, inserts and deletes, cannot perform updates or employ any locks,
|
||||
*/
|
||||
public const CACHE_USAGE_READ_ONLY = 1;
|
||||
final public const int CACHE_USAGE_READ_ONLY = 1;
|
||||
|
||||
/**
|
||||
* Nonstrict Read Write Cache doesn’t employ any locks but can do inserts, update and deletes.
|
||||
*/
|
||||
public const CACHE_USAGE_NONSTRICT_READ_WRITE = 2;
|
||||
final public const int CACHE_USAGE_NONSTRICT_READ_WRITE = 2;
|
||||
|
||||
/**
|
||||
* Read Write Attempts to lock the entity before update/delete.
|
||||
*/
|
||||
public const CACHE_USAGE_READ_WRITE = 3;
|
||||
final public const int CACHE_USAGE_READ_WRITE = 3;
|
||||
|
||||
/**
|
||||
* The value of this column is never generated by the database.
|
||||
*/
|
||||
public const GENERATED_NEVER = 0;
|
||||
final public const int GENERATED_NEVER = 0;
|
||||
|
||||
/**
|
||||
* The value of this column is generated by the database on INSERT, but not on UPDATE.
|
||||
*/
|
||||
public const GENERATED_INSERT = 1;
|
||||
final public const int GENERATED_INSERT = 1;
|
||||
|
||||
/**
|
||||
* The value of this column is generated by the database on both INSERT and UDPATE statements.
|
||||
*/
|
||||
public const GENERATED_ALWAYS = 2;
|
||||
final public const int GENERATED_ALWAYS = 2;
|
||||
|
||||
/**
|
||||
* READ-ONLY: The namespace the entity class is contained in.
|
||||
@@ -519,9 +517,9 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* The ReflectionClass instance of the mapped class.
|
||||
*
|
||||
* @var ReflectionClass<T>|null
|
||||
* @var ReflectionClass<T>
|
||||
*/
|
||||
public ReflectionClass|null $reflClass = null;
|
||||
public ReflectionClass $reflClass;
|
||||
|
||||
/**
|
||||
* Is this entity marked as "read-only"?
|
||||
@@ -537,15 +535,6 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
*/
|
||||
protected NamingStrategy $namingStrategy;
|
||||
|
||||
/**
|
||||
* The ReflectionProperty instances of the mapped class.
|
||||
*
|
||||
* @deprecated Use $propertyAccessors instead.
|
||||
*
|
||||
* @var LegacyReflectionFields|array<string, ReflectionProperty>
|
||||
*/
|
||||
public LegacyReflectionFields|array $reflFields = [];
|
||||
|
||||
/** @var array<string, PropertyAccessor> */
|
||||
public array $propertyAccessors = [];
|
||||
|
||||
@@ -568,19 +557,6 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
$this->typedFieldMapper = $typedFieldMapper ?? new DefaultTypedFieldMapper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ReflectionProperties of the mapped class.
|
||||
*
|
||||
* @deprecated Use getPropertyAccessors() instead.
|
||||
*
|
||||
* @return LegacyReflectionFields|ReflectionProperty[] An array of ReflectionProperty instances.
|
||||
* @phpstan-return LegacyReflectionFields|array<string, ReflectionProperty>
|
||||
*/
|
||||
public function getReflectionProperties(): array|LegacyReflectionFields
|
||||
{
|
||||
return $this->reflFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ReflectionProperties of the mapped class.
|
||||
*
|
||||
@@ -591,35 +567,11 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
return $this->propertyAccessors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a ReflectionProperty for a specific field of the mapped class.
|
||||
*
|
||||
* @deprecated Use getPropertyAccessor() instead.
|
||||
*/
|
||||
public function getReflectionProperty(string $name): ReflectionProperty|null
|
||||
{
|
||||
return $this->reflFields[$name];
|
||||
}
|
||||
|
||||
public function getPropertyAccessor(string $name): PropertyAccessor|null
|
||||
{
|
||||
return $this->propertyAccessors[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use getPropertyAccessor() instead.
|
||||
*
|
||||
* @throws BadMethodCallException If the class has a composite identifier.
|
||||
*/
|
||||
public function getSingleIdReflectionProperty(): ReflectionProperty|null
|
||||
{
|
||||
if ($this->isIdentifierComposite) {
|
||||
throw new BadMethodCallException('Class ' . $this->name . ' has a composite identifier.');
|
||||
}
|
||||
|
||||
return $this->reflFields[$this->identifier[0]];
|
||||
}
|
||||
|
||||
/** @throws BadMethodCallException If the class has a composite identifier. */
|
||||
public function getSingleIdPropertyAccessor(): PropertyAccessor|null
|
||||
{
|
||||
@@ -638,6 +590,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
#[Override]
|
||||
public function getIdentifierValues(object $entity): array
|
||||
{
|
||||
if ($this->isIdentifierComposite) {
|
||||
@@ -701,6 +654,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
*
|
||||
* @todo Construct meaningful string representation.
|
||||
*/
|
||||
#[Override]
|
||||
public function __toString(): string
|
||||
{
|
||||
return self::class . '@' . spl_object_id($this);
|
||||
@@ -713,9 +667,8 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
* That means any metadata properties that are not set or empty or simply have
|
||||
* their default value are NOT serialized.
|
||||
*
|
||||
* Parts that are also NOT serialized because they can not be properly unserialized:
|
||||
* - reflClass (ReflectionClass)
|
||||
* - reflFields (ReflectionProperty array)
|
||||
* Parts that are also NOT serialized because they can not be properly
|
||||
* unserialized, e.g. reflClass (ReflectionClass)
|
||||
*
|
||||
* @return string[] The names of all the fields that should be serialized.
|
||||
*/
|
||||
@@ -824,9 +777,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
public function wakeupReflection(ReflectionService $reflService): void
|
||||
{
|
||||
// Restore ReflectionClass and properties
|
||||
$this->reflClass = $reflService->getClass($this->name);
|
||||
/** @phpstan-ignore property.deprecated */
|
||||
$this->reflFields = new LegacyReflectionFields($this, $reflService);
|
||||
$this->reflClass = $reflService->getClass($this->name);
|
||||
$this->instantiator = $this->instantiator ?: new Instantiator();
|
||||
|
||||
$parentAccessors = [];
|
||||
@@ -907,10 +858,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
{
|
||||
$this->reflClass = $reflService->getClass($this->name);
|
||||
$this->namespace = $reflService->getClassNamespace($this->name);
|
||||
|
||||
if ($this->reflClass) {
|
||||
$this->name = $this->rootEntityName = $this->reflClass->name;
|
||||
}
|
||||
$this->name = $this->rootEntityName = $this->reflClass->name;
|
||||
|
||||
$this->table['name'] = $this->namingStrategy->classToTableName($this->name);
|
||||
}
|
||||
@@ -970,6 +918,12 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getReflectionClass(): ReflectionClass
|
||||
{
|
||||
return $this->reflClass;
|
||||
}
|
||||
|
||||
/** @phpstan-param array{usage?: mixed, region?: mixed} $cache */
|
||||
public function enableCache(array $cache): void
|
||||
{
|
||||
@@ -1036,6 +990,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* Checks whether a field is part of the identifier/primary key field(s).
|
||||
*/
|
||||
#[Override]
|
||||
public function isIdentifier(string $fieldName): bool
|
||||
{
|
||||
if (! $this->identifier) {
|
||||
@@ -1141,8 +1096,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
*/
|
||||
private function isTypedProperty(string $name): bool
|
||||
{
|
||||
return isset($this->reflClass)
|
||||
&& $this->reflClass->hasProperty($name)
|
||||
return $this->reflClass->hasProperty($name)
|
||||
&& $this->reflClass->getProperty($name)->hasType();
|
||||
}
|
||||
|
||||
@@ -1269,11 +1223,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
$this->containsEnumIdentifier = true;
|
||||
}
|
||||
|
||||
if (
|
||||
defined('Doctrine\DBAL\Types\Types::ENUM')
|
||||
&& $mapping->type === Types::ENUM
|
||||
&& ! isset($mapping->options['values'])
|
||||
) {
|
||||
if ($mapping->type === Types::ENUM && ! isset($mapping->options['values'])) {
|
||||
$mapping->options['values'] = array_column($mapping->enumType::cases(), 'value');
|
||||
}
|
||||
}
|
||||
@@ -1460,6 +1410,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getIdentifierFieldNames(): array
|
||||
{
|
||||
return $this->identifier;
|
||||
@@ -1511,11 +1462,13 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getIdentifier(): array
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function hasField(string $fieldName): bool
|
||||
{
|
||||
return isset($this->fieldMappings[$fieldName]) || isset($this->embeddedClasses[$fieldName]);
|
||||
@@ -1642,6 +1595,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
*
|
||||
* @todo 3.0 Remove this. PersisterHelper should fix it somehow
|
||||
*/
|
||||
#[Override]
|
||||
public function getTypeOfField(string $fieldName): string|null
|
||||
{
|
||||
return isset($this->fieldMappings[$fieldName])
|
||||
@@ -2315,17 +2269,20 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function hasAssociation(string $fieldName): bool
|
||||
{
|
||||
return isset($this->associationMappings[$fieldName]);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isSingleValuedAssociation(string $fieldName): bool
|
||||
{
|
||||
return isset($this->associationMappings[$fieldName])
|
||||
&& ($this->associationMappings[$fieldName]->isToOne());
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isCollectionValuedAssociation(string $fieldName): bool
|
||||
{
|
||||
return isset($this->associationMappings[$fieldName])
|
||||
@@ -2521,6 +2478,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getFieldNames(): array
|
||||
{
|
||||
return array_keys($this->fieldMappings);
|
||||
@@ -2529,6 +2487,7 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getAssociationNames(): array
|
||||
{
|
||||
return array_keys($this->associationMappings);
|
||||
@@ -2541,23 +2500,27 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
#[Override]
|
||||
public function getAssociationTargetClass(string $assocName): string
|
||||
{
|
||||
return $this->associationMappings[$assocName]->targetEntity
|
||||
?? throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isAssociationInverseSide(string $assocName): bool
|
||||
{
|
||||
return isset($this->associationMappings[$assocName])
|
||||
&& ! $this->associationMappings[$assocName]->isOwningSide();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getAssociationMappedByTargetField(string $assocName): string
|
||||
{
|
||||
$assoc = $this->getAssociationMapping($assocName);
|
||||
@@ -2581,24 +2544,12 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
/**
|
||||
* @param C $className
|
||||
*
|
||||
* @return string|null null if and only if the input value is null
|
||||
* @phpstan-return (C is class-string ? class-string : (C is string ? string : null))
|
||||
* @phpstan-return (C is class-string ? class-string : string)
|
||||
*
|
||||
* @template C of string|null
|
||||
* @template C of string
|
||||
*/
|
||||
public function fullyQualifiedClassName(string|null $className): string|null
|
||||
public function fullyQualifiedClassName(string $className): string
|
||||
{
|
||||
if ($className === null) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/11294',
|
||||
'Passing null to %s is deprecated and will not be supported in Doctrine ORM 4.0',
|
||||
__METHOD__,
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! str_contains($className, '\\') && $this->namespace) {
|
||||
return $this->namespace . '\\' . $className;
|
||||
}
|
||||
@@ -2664,8 +2615,6 @@ class ClassMetadata implements PersistenceClassMetadata, Stringable
|
||||
if (! empty($this->embeddedClasses[$property]->columnPrefix)) {
|
||||
$fieldMapping['columnName'] = $this->embeddedClasses[$property]->columnPrefix . $fieldMapping['columnName'];
|
||||
} elseif ($this->embeddedClasses[$property]->columnPrefix !== false) {
|
||||
assert($this->reflClass !== null);
|
||||
assert($embeddable->reflClass !== null);
|
||||
$fieldMapping['columnName'] = $this->namingStrategy
|
||||
->embeddedFieldToColumnName(
|
||||
$property,
|
||||
|
||||
@@ -4,10 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\Common\EventDispatcher;
|
||||
use Doctrine\DBAL\Platforms;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
|
||||
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
|
||||
@@ -19,11 +18,11 @@ use Doctrine\ORM\Id\IdentityGenerator;
|
||||
use Doctrine\ORM\Id\SequenceGenerator;
|
||||
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;
|
||||
use Doctrine\Persistence\Mapping\ReflectionService;
|
||||
use Override;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
|
||||
@@ -35,14 +34,11 @@ use function explode;
|
||||
use function in_array;
|
||||
use function is_a;
|
||||
use function is_subclass_of;
|
||||
use function method_exists;
|
||||
use function str_contains;
|
||||
use function strlen;
|
||||
use function strtolower;
|
||||
use function substr;
|
||||
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
/**
|
||||
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
|
||||
* metadata mapping information of a class which describes how a class should be mapped
|
||||
@@ -55,21 +51,17 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
private EntityManagerInterface|null $em = null;
|
||||
private AbstractPlatform|null $targetPlatform = null;
|
||||
private MappingDriver|null $driver = null;
|
||||
private EventManager|null $evm = null;
|
||||
private EventDispatcher|null $eventDispatcher = null;
|
||||
|
||||
/** @var mixed[] */
|
||||
private array $embeddablesActiveNesting = [];
|
||||
|
||||
private const NON_IDENTITY_DEFAULT_STRATEGY = [
|
||||
private const array NON_IDENTITY_DEFAULT_STRATEGY = [
|
||||
Platforms\OraclePlatform::class => ClassMetadata::GENERATOR_TYPE_SEQUENCE,
|
||||
];
|
||||
|
||||
public function setEntityManager(EntityManagerInterface $em): void
|
||||
{
|
||||
if (! $em->getConfiguration()->isNativeLazyObjectsEnabled()) {
|
||||
parent::setProxyClassNameResolver(new DefaultProxyClassNameResolver());
|
||||
}
|
||||
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
@@ -107,22 +99,20 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
return $owningSide;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function initialize(): void
|
||||
{
|
||||
$this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
|
||||
$this->evm = $this->em->getEventManager();
|
||||
$this->initialized = true;
|
||||
$this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
|
||||
$this->eventDispatcher = $this->em->getEventManager();
|
||||
$this->initialized = true;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function onNotFoundMetadata(string $className): ClassMetadata|null
|
||||
{
|
||||
if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$eventArgs = new OnClassMetadataNotFoundEventArgs($className, $this->em);
|
||||
|
||||
$this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
|
||||
$this->eventDispatcher->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
|
||||
$classMetadata = $eventArgs->getFoundMetadata();
|
||||
assert($classMetadata instanceof ClassMetadata || $classMetadata === null);
|
||||
|
||||
@@ -132,6 +122,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
protected function doLoadMetadata(
|
||||
ClassMetadataInterface $class,
|
||||
ClassMetadataInterface|null $parent,
|
||||
@@ -245,10 +236,10 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
// During the following event, there may also be updates to the discriminator map as per GH-1257/GH-8402.
|
||||
// So, we must not discover the missing subclasses before that.
|
||||
|
||||
if ($this->evm->hasListeners(Events::loadClassMetadata)) {
|
||||
$eventArgs = new LoadClassMetadataEventArgs($class, $this->em);
|
||||
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
|
||||
}
|
||||
$this->eventDispatcher->dispatchEvent(
|
||||
Events::loadClassMetadata,
|
||||
new LoadClassMetadataEventArgs($class, $this->em),
|
||||
);
|
||||
|
||||
$this->findAbstractEntityClassesNotListedInDiscriminatorMap($class);
|
||||
|
||||
@@ -262,11 +253,6 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
*/
|
||||
protected function validateRuntimeMetadata(ClassMetadata $class, ClassMetadataInterface|null $parent): void
|
||||
{
|
||||
if (! $class->reflClass) {
|
||||
// only validate if there is a reflection class instance
|
||||
return;
|
||||
}
|
||||
|
||||
$class->validateIdentifier();
|
||||
$class->validateAssociations();
|
||||
$class->validateLifecycleCallbacks($this->getReflectionService());
|
||||
@@ -302,6 +288,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function newClassMetadataInstance(string $className): ClassMetadata
|
||||
{
|
||||
return new ClassMetadata(
|
||||
@@ -626,39 +613,8 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
}
|
||||
}
|
||||
|
||||
$nonIdentityDefaultStrategy = self::NON_IDENTITY_DEFAULT_STRATEGY;
|
||||
|
||||
// DBAL 3
|
||||
if (method_exists($platform, 'getIdentitySequenceName')) {
|
||||
$nonIdentityDefaultStrategy[Platforms\PostgreSQLPlatform::class] = ClassMetadata::GENERATOR_TYPE_SEQUENCE;
|
||||
}
|
||||
|
||||
foreach ($nonIdentityDefaultStrategy as $platformFamily => $strategy) {
|
||||
foreach (self::NON_IDENTITY_DEFAULT_STRATEGY as $platformFamily => $strategy) {
|
||||
if (is_a($platform, $platformFamily)) {
|
||||
if ($platform instanceof 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", when using DBAL 3,
|
||||
and "IDENTITY" when using DBAL 4,
|
||||
so you should probably use the following configuration before upgrading to DBAL 4,
|
||||
and remove it after deploying that upgrade:
|
||||
|
||||
$configuration->setIdentityGenerationPreferences([
|
||||
"%s" => ClassMetadata::GENERATOR_TYPE_SEQUENCE,
|
||||
]);
|
||||
|
||||
DEPRECATION,
|
||||
$platformFamily,
|
||||
$platformFamily,
|
||||
);
|
||||
}
|
||||
|
||||
return $strategy;
|
||||
}
|
||||
}
|
||||
@@ -700,14 +656,11 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService): void
|
||||
{
|
||||
$class->wakeupReflection($reflService);
|
||||
|
||||
if (PHP_VERSION_ID < 80400) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($class->propertyAccessors as $propertyAccessor) {
|
||||
$property = $propertyAccessor->getUnderlyingReflector();
|
||||
|
||||
@@ -717,11 +670,13 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService): void
|
||||
{
|
||||
$class->initializeReflection($reflService);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function getDriver(): MappingDriver
|
||||
{
|
||||
assert($this->driver !== null);
|
||||
@@ -729,6 +684,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
return $this->driver;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function isEntity(ClassMetadataInterface $class): bool
|
||||
{
|
||||
return ! $class->isMappedSuperclass;
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Override;
|
||||
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
@@ -14,6 +16,7 @@ class DefaultEntityListenerResolver implements EntityListenerResolver
|
||||
/** @var array<class-string, object> Map to store entity listener instances. */
|
||||
private array $instances = [];
|
||||
|
||||
#[Override]
|
||||
public function clear(string|null $className = null): void
|
||||
{
|
||||
if ($className === null) {
|
||||
@@ -26,11 +29,13 @@ class DefaultEntityListenerResolver implements EntityListenerResolver
|
||||
unset($this->instances[$className]);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function register(object $object): void
|
||||
{
|
||||
$this->instances[$object::class] = $object;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function resolve(string $className): object
|
||||
{
|
||||
$className = trim($className, '\\');
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Override;
|
||||
|
||||
use function str_contains;
|
||||
use function strrpos;
|
||||
use function strtolower;
|
||||
@@ -16,6 +18,7 @@ use function substr;
|
||||
*/
|
||||
class DefaultNamingStrategy implements NamingStrategy
|
||||
{
|
||||
#[Override]
|
||||
public function classToTableName(string $className): string
|
||||
{
|
||||
if (str_contains($className, '\\')) {
|
||||
@@ -25,11 +28,13 @@ class DefaultNamingStrategy implements NamingStrategy
|
||||
return $className;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function propertyToColumnName(string $propertyName, string $className): string
|
||||
{
|
||||
return $propertyName;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function embeddedFieldToColumnName(
|
||||
string $propertyName,
|
||||
string $embeddedColumnName,
|
||||
@@ -39,16 +44,19 @@ class DefaultNamingStrategy implements NamingStrategy
|
||||
return $propertyName . '_' . $embeddedColumnName;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function referenceColumnName(): string
|
||||
{
|
||||
return 'id';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function joinColumnName(string $propertyName, string $className): string
|
||||
{
|
||||
return $propertyName . '_' . $this->referenceColumnName();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function joinTableName(
|
||||
string $sourceEntity,
|
||||
string $targetEntity,
|
||||
@@ -58,6 +66,7 @@ class DefaultNamingStrategy implements NamingStrategy
|
||||
$this->classToTableName($targetEntity));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function joinKeyColumnName(
|
||||
string $entityName,
|
||||
string|null $referencedColumnName,
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\ORM\Internal\SQLResultCasing;
|
||||
use Override;
|
||||
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
@@ -24,6 +25,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
{
|
||||
use SQLResultCasing;
|
||||
|
||||
#[Override]
|
||||
public function getColumnName(string $fieldName, ClassMetadata $class, AbstractPlatform $platform): string
|
||||
{
|
||||
return isset($class->fieldMappings[$fieldName]->quoted)
|
||||
@@ -36,6 +38,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
*
|
||||
* @todo Table names should be computed in DBAL depending on the platform
|
||||
*/
|
||||
#[Override]
|
||||
public function getTableName(ClassMetadata $class, AbstractPlatform $platform): string
|
||||
{
|
||||
$tableName = $class->table['name'];
|
||||
@@ -58,6 +61,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string
|
||||
{
|
||||
return isset($definition['quoted'])
|
||||
@@ -68,6 +72,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
: $definition['sequenceName'];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getJoinColumnName(JoinColumnMapping $joinColumn, ClassMetadata $class, AbstractPlatform $platform): string
|
||||
{
|
||||
return isset($joinColumn->quoted)
|
||||
@@ -75,6 +80,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
: $joinColumn->name;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getReferencedJoinColumnName(
|
||||
JoinColumnMapping $joinColumn,
|
||||
ClassMetadata $class,
|
||||
@@ -85,6 +91,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
: $joinColumn->referencedColumnName;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getJoinTableName(
|
||||
ManyToManyOwningSideMapping $association,
|
||||
ClassMetadata $class,
|
||||
@@ -108,6 +115,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform): array
|
||||
{
|
||||
$quotedColumnNames = [];
|
||||
@@ -136,6 +144,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
return $quotedColumnNames;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getColumnAlias(
|
||||
string $columnName,
|
||||
int $counter,
|
||||
|
||||
@@ -11,6 +11,7 @@ use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Override;
|
||||
use ReflectionEnum;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionProperty;
|
||||
@@ -27,7 +28,7 @@ final class DefaultTypedFieldMapper implements TypedFieldMapper
|
||||
/** @var array<class-string|ScalarName, class-string<Type>|string> $typedFieldMappings */
|
||||
private array $typedFieldMappings;
|
||||
|
||||
private const DEFAULT_TYPED_FIELD_MAPPINGS = [
|
||||
private const array DEFAULT_TYPED_FIELD_MAPPINGS = [
|
||||
DateInterval::class => Types::DATEINTERVAL,
|
||||
DateTime::class => Types::DATETIME_MUTABLE,
|
||||
DateTimeImmutable::class => Types::DATETIME_IMMUTABLE,
|
||||
@@ -52,6 +53,7 @@ final class DefaultTypedFieldMapper implements TypedFieldMapper
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
#[Override]
|
||||
public function validateAndComplete(array $mapping, ReflectionProperty $field): array
|
||||
{
|
||||
$type = $field->getType();
|
||||
@@ -63,10 +65,7 @@ final class DefaultTypedFieldMapper implements TypedFieldMapper
|
||||
if (
|
||||
! $type->isBuiltin()
|
||||
&& enum_exists($type->getName())
|
||||
&& (! isset($mapping['type']) || (
|
||||
defined('Doctrine\DBAL\Types\Types::ENUM')
|
||||
&& $mapping['type'] === Types::ENUM
|
||||
))
|
||||
&& (! isset($mapping['type']) || $mapping['type'] === Types::ENUM)
|
||||
) {
|
||||
$reflection = new ReflectionEnum($type->getName());
|
||||
if (! $reflection->isBacked()) {
|
||||
|
||||
@@ -4,18 +4,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use ArrayAccess;
|
||||
use BackedEnum;
|
||||
use Exception;
|
||||
|
||||
use function in_array;
|
||||
use function property_exists;
|
||||
|
||||
/** @template-implements ArrayAccess<string, mixed> */
|
||||
final class DiscriminatorColumnMapping implements ArrayAccess
|
||||
final class DiscriminatorColumnMapping
|
||||
{
|
||||
use ArrayAccessImplementation;
|
||||
|
||||
/** The database length of the column. Optional. Default value taken from the type. */
|
||||
public int|null $length = null;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
|
||||
use Doctrine\Persistence\Mapping\Driver\ClassLocator;
|
||||
use Doctrine\Persistence\Mapping\Driver\ColocatedMappingDriver;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
use InvalidArgumentException;
|
||||
use Override;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
|
||||
@@ -21,33 +21,22 @@ use function assert;
|
||||
use function class_exists;
|
||||
use function constant;
|
||||
use function defined;
|
||||
use function sprintf;
|
||||
|
||||
class AttributeDriver implements MappingDriver
|
||||
{
|
||||
use ColocatedMappingDriver;
|
||||
use ReflectionBasedDriver;
|
||||
|
||||
private const ENTITY_ATTRIBUTE_CLASSES = [
|
||||
private const array ENTITY_ATTRIBUTE_CLASSES = [
|
||||
Mapping\Entity::class => 1,
|
||||
Mapping\MappedSuperclass::class => 2,
|
||||
];
|
||||
|
||||
private readonly AttributeReader $reader;
|
||||
|
||||
/**
|
||||
* @param string[]|ClassLocator $paths a ClassLocator, or an array of directories.
|
||||
* @param true $reportFieldsWhereDeclared no-op, to be removed in 4.0
|
||||
*/
|
||||
public function __construct(array|ClassLocator $paths, bool $reportFieldsWhereDeclared = true)
|
||||
/** @param string[]|ClassLocator $paths a ClassLocator, or an array of directories. */
|
||||
public function __construct(array|ClassLocator $paths)
|
||||
{
|
||||
if (! $reportFieldsWhereDeclared) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'The $reportFieldsWhereDeclared argument is no longer supported, make sure to omit it when calling %s.',
|
||||
__METHOD__,
|
||||
));
|
||||
}
|
||||
|
||||
$this->reader = new AttributeReader();
|
||||
|
||||
if ($paths instanceof ClassLocator) {
|
||||
@@ -57,6 +46,7 @@ class AttributeDriver implements MappingDriver
|
||||
}
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function isTransient(string $className): bool
|
||||
{
|
||||
$classAttributes = $this->reader->getClassAttributes(new ReflectionClass($className));
|
||||
@@ -79,6 +69,7 @@ class AttributeDriver implements MappingDriver
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
#[Override]
|
||||
public function loadMetadataForClass(string $className, PersistenceClassMetadata $metadata): void
|
||||
{
|
||||
$reflectionClass = $metadata->getReflectionClass()
|
||||
|
||||
@@ -1,639 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Driver;
|
||||
|
||||
use Doctrine\DBAL\Schema\AbstractAsset;
|
||||
use Doctrine\DBAL\Schema\AbstractSchemaManager;
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
|
||||
use Doctrine\DBAL\Schema\Index;
|
||||
use Doctrine\DBAL\Schema\Index\IndexedColumn;
|
||||
use Doctrine\DBAL\Schema\Index\IndexType;
|
||||
use Doctrine\DBAL\Schema\Name\UnqualifiedName;
|
||||
use Doctrine\DBAL\Schema\NamedObject;
|
||||
use Doctrine\DBAL\Schema\PrimaryKeyConstraint;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\DBAL\Schema\Table;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Inflector\Inflector;
|
||||
use Doctrine\Inflector\InflectorFactory;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
use InvalidArgumentException;
|
||||
use TypeError;
|
||||
|
||||
use function array_diff;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function current;
|
||||
use function enum_exists;
|
||||
use function get_debug_type;
|
||||
use function in_array;
|
||||
use function method_exists;
|
||||
use function preg_replace;
|
||||
use function sort;
|
||||
use function sprintf;
|
||||
use function strtolower;
|
||||
|
||||
/**
|
||||
* The DatabaseDriver reverse engineers the mapping metadata from a database.
|
||||
*
|
||||
* @deprecated No replacement planned
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
*/
|
||||
class DatabaseDriver implements MappingDriver
|
||||
{
|
||||
/**
|
||||
* Replacement for {@see Types::ARRAY}.
|
||||
*
|
||||
* To be removed as soon as support for DBAL 3 is dropped.
|
||||
*/
|
||||
private const ARRAY = 'array';
|
||||
|
||||
/**
|
||||
* Replacement for {@see Types::OBJECT}.
|
||||
*
|
||||
* To be removed as soon as support for DBAL 3 is dropped.
|
||||
*/
|
||||
private const OBJECT = 'object';
|
||||
|
||||
/** @var array<string,Table>|null */
|
||||
private array|null $tables = null;
|
||||
|
||||
/** @var array<class-string, string> */
|
||||
private array $classToTableNames = [];
|
||||
|
||||
/** @phpstan-var array<string, Table> */
|
||||
private array $manyToManyTables = [];
|
||||
|
||||
/** @var mixed[] */
|
||||
private array $classNamesForTables = [];
|
||||
|
||||
/** @var mixed[] */
|
||||
private array $fieldNamesForColumns = [];
|
||||
|
||||
/**
|
||||
* The namespace for the generated entities.
|
||||
*/
|
||||
private string|null $namespace = null;
|
||||
|
||||
private Inflector $inflector;
|
||||
|
||||
public function __construct(private readonly AbstractSchemaManager $sm)
|
||||
{
|
||||
$this->inflector = InflectorFactory::create()->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the namespace for the generated entities.
|
||||
*/
|
||||
public function setNamespace(string $namespace): void
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
|
||||
public function isTransient(string $className): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getAllClassNames(): array
|
||||
{
|
||||
$this->reverseEngineerMappingFromDatabase();
|
||||
|
||||
return array_keys($this->classToTableNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets class name for a table.
|
||||
*/
|
||||
public function setClassNameForTable(string $tableName, string $className): void
|
||||
{
|
||||
$this->classNamesForTables[$tableName] = $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets field name for a column on a specific table.
|
||||
*/
|
||||
public function setFieldNameForColumn(string $tableName, string $columnName, string $fieldName): void
|
||||
{
|
||||
$this->fieldNamesForColumns[$tableName][$columnName] = $fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets tables manually instead of relying on the reverse engineering capabilities of SchemaManager.
|
||||
*
|
||||
* @param Table[] $entityTables
|
||||
* @param Table[] $manyToManyTables
|
||||
* @phpstan-param list<Table> $entityTables
|
||||
* @phpstan-param list<Table> $manyToManyTables
|
||||
*/
|
||||
public function setTables(array $entityTables, array $manyToManyTables): void
|
||||
{
|
||||
$this->tables = $this->manyToManyTables = $this->classToTableNames = [];
|
||||
|
||||
foreach ($entityTables as $table) {
|
||||
$className = $this->getClassNameForTable(self::getAssetName($table));
|
||||
|
||||
$this->classToTableNames[$className] = self::getAssetName($table);
|
||||
$this->tables[self::getAssetName($table)] = $table;
|
||||
}
|
||||
|
||||
foreach ($manyToManyTables as $table) {
|
||||
$this->manyToManyTables[self::getAssetName($table)] = $table;
|
||||
}
|
||||
}
|
||||
|
||||
public function setInflector(Inflector $inflector): void
|
||||
{
|
||||
$this->inflector = $inflector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @param class-string<T> $className
|
||||
* @param ClassMetadata<T> $metadata
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
public function loadMetadataForClass(string $className, PersistenceClassMetadata $metadata): void
|
||||
{
|
||||
if (! $metadata instanceof ClassMetadata) {
|
||||
throw new TypeError(sprintf(
|
||||
'Argument #2 passed to %s() must be an instance of %s, %s given.',
|
||||
__METHOD__,
|
||||
ClassMetadata::class,
|
||||
get_debug_type($metadata),
|
||||
));
|
||||
}
|
||||
|
||||
$this->reverseEngineerMappingFromDatabase();
|
||||
|
||||
if (! isset($this->classToTableNames[$className])) {
|
||||
throw new InvalidArgumentException('Unknown class ' . $className);
|
||||
}
|
||||
|
||||
$tableName = $this->classToTableNames[$className];
|
||||
|
||||
$metadata->name = $className;
|
||||
$metadata->table['name'] = $tableName;
|
||||
|
||||
$this->buildIndexes($metadata);
|
||||
$this->buildFieldMappings($metadata);
|
||||
$this->buildToOneAssociationMappings($metadata);
|
||||
|
||||
foreach ($this->manyToManyTables as $manyTable) {
|
||||
foreach ($manyTable->getForeignKeys() as $foreignKey) {
|
||||
// foreign key maps to the table of the current entity, many to many association probably exists
|
||||
if (! (strtolower($tableName) === strtolower(self::getReferencedTableName($foreignKey)))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$myFk = $foreignKey;
|
||||
$otherFk = null;
|
||||
|
||||
foreach ($manyTable->getForeignKeys() as $foreignKey) {
|
||||
if ($foreignKey !== $myFk) {
|
||||
$otherFk = $foreignKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! $otherFk) {
|
||||
// the definition of this many to many table does not contain
|
||||
// enough foreign key information to continue reverse engineering.
|
||||
continue;
|
||||
}
|
||||
|
||||
$localColumn = current(self::getReferencingColumnNames($myFk));
|
||||
|
||||
$associationMapping = [];
|
||||
$associationMapping['fieldName'] = $this->getFieldNameForColumn(self::getAssetName($manyTable), current(self::getReferencingColumnNames($otherFk)), true);
|
||||
$associationMapping['targetEntity'] = $this->getClassNameForTable(self::getReferencedTableName($otherFk));
|
||||
|
||||
if (self::getAssetName(current($manyTable->getColumns())) === $localColumn) {
|
||||
$associationMapping['inversedBy'] = $this->getFieldNameForColumn(self::getAssetName($manyTable), current(self::getReferencingColumnNames($myFk)), true);
|
||||
$associationMapping['joinTable'] = [
|
||||
'name' => strtolower(self::getAssetName($manyTable)),
|
||||
'joinColumns' => [],
|
||||
'inverseJoinColumns' => [],
|
||||
];
|
||||
|
||||
$fkCols = self::getReferencedColumnNames($myFk);
|
||||
$cols = self::getReferencingColumnNames($myFk);
|
||||
|
||||
for ($i = 0, $colsCount = count($cols); $i < $colsCount; $i++) {
|
||||
$associationMapping['joinTable']['joinColumns'][] = [
|
||||
'name' => $cols[$i],
|
||||
'referencedColumnName' => $fkCols[$i],
|
||||
];
|
||||
}
|
||||
|
||||
$fkCols = self::getReferencedColumnNames($otherFk);
|
||||
$cols = self::getReferencingColumnNames($otherFk);
|
||||
|
||||
for ($i = 0, $colsCount = count($cols); $i < $colsCount; $i++) {
|
||||
$associationMapping['joinTable']['inverseJoinColumns'][] = [
|
||||
'name' => $cols[$i],
|
||||
'referencedColumnName' => $fkCols[$i],
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$associationMapping['mappedBy'] = $this->getFieldNameForColumn(
|
||||
// @phpstan-ignore function.alreadyNarrowedType (DBAL 3 compatibility)
|
||||
method_exists(Table::class, 'getObjectName')
|
||||
? $manyTable->getObjectName()->toString()
|
||||
: $manyTable->getName(), // DBAL < 4.4
|
||||
current(self::getReferencingColumnNames($myFk)),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
$metadata->mapManyToMany($associationMapping);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @throws MappingException */
|
||||
private function reverseEngineerMappingFromDatabase(): void
|
||||
{
|
||||
if ($this->tables !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->tables = $this->manyToManyTables = $this->classToTableNames = [];
|
||||
|
||||
foreach ($this->sm->listTables() as $table) {
|
||||
$tableName = self::getAssetName($table);
|
||||
$foreignKeys = $table->getForeignKeys();
|
||||
|
||||
$allForeignKeyColumns = [];
|
||||
|
||||
foreach ($foreignKeys as $foreignKey) {
|
||||
$allForeignKeyColumns = array_merge($allForeignKeyColumns, self::getReferencingColumnNames($foreignKey));
|
||||
}
|
||||
|
||||
if (method_exists($table, 'getPrimaryKeyConstraint')) {
|
||||
$primaryKey = $table->getPrimaryKeyConstraint();
|
||||
} else {
|
||||
$primaryKey = $table->getPrimaryKey();
|
||||
}
|
||||
|
||||
if ($primaryKey === null) {
|
||||
throw new MappingException(
|
||||
'Table ' . $tableName . ' has no primary key. Doctrine does not ' .
|
||||
"support reverse engineering from tables that don't have a primary key.",
|
||||
);
|
||||
}
|
||||
|
||||
if ($primaryKey instanceof PrimaryKeyConstraint) {
|
||||
$pkColumns = array_map(static fn (UnqualifiedName $name) => $name->toString(), $primaryKey->getColumnNames());
|
||||
} else {
|
||||
$pkColumns = self::getIndexedColumns($primaryKey);
|
||||
}
|
||||
|
||||
sort($pkColumns);
|
||||
sort($allForeignKeyColumns);
|
||||
|
||||
if ($pkColumns === $allForeignKeyColumns && count($foreignKeys) === 2) {
|
||||
$this->manyToManyTables[$tableName] = $table;
|
||||
} else {
|
||||
// lower-casing is necessary because of Oracle Uppercase Tablenames,
|
||||
// assumption is lower-case + underscore separated.
|
||||
$className = $this->getClassNameForTable($tableName);
|
||||
|
||||
$this->tables[$tableName] = $table;
|
||||
$this->classToTableNames[$className] = $tableName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build indexes from a class metadata.
|
||||
*/
|
||||
private function buildIndexes(ClassMetadata $metadata): void
|
||||
{
|
||||
$tableName = $metadata->table['name'];
|
||||
$table = $this->tables[$tableName];
|
||||
$primaryKey = self::getPrimaryKey($table);
|
||||
$indexes = $table->getIndexes();
|
||||
|
||||
foreach ($indexes as $index) {
|
||||
if ($index === $primaryKey) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (enum_exists(IndexType::class) && method_exists($index, 'getType')) {
|
||||
$isUnique = $index->getType() === IndexType::UNIQUE;
|
||||
} else {
|
||||
$isUnique = $index->isUnique();
|
||||
}
|
||||
|
||||
$indexName = self::getAssetName($index);
|
||||
$indexColumns = self::getIndexedColumns($index);
|
||||
$constraintType = $isUnique
|
||||
? 'uniqueConstraints'
|
||||
: 'indexes';
|
||||
|
||||
$metadata->table[$constraintType][$indexName]['columns'] = $indexColumns;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build field mapping from class metadata.
|
||||
*/
|
||||
private function buildFieldMappings(ClassMetadata $metadata): void
|
||||
{
|
||||
$tableName = $metadata->table['name'];
|
||||
$columns = $this->tables[$tableName]->getColumns();
|
||||
$primaryKeys = $this->getTablePrimaryKeys($this->tables[$tableName]);
|
||||
$foreignKeys = $this->tables[$tableName]->getForeignKeys();
|
||||
$allForeignKeys = [];
|
||||
|
||||
foreach ($foreignKeys as $foreignKey) {
|
||||
$allForeignKeys = array_merge($allForeignKeys, self::getReferencingColumnNames($foreignKey));
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
$fieldMappings = [];
|
||||
|
||||
foreach ($columns as $column) {
|
||||
if (in_array(self::getAssetName($column), $allForeignKeys, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldMapping = $this->buildFieldMapping($tableName, $column);
|
||||
|
||||
if ($primaryKeys && in_array(self::getAssetName($column), $primaryKeys, true)) {
|
||||
$fieldMapping['id'] = true;
|
||||
$ids[] = $fieldMapping;
|
||||
}
|
||||
|
||||
$fieldMappings[] = $fieldMapping;
|
||||
}
|
||||
|
||||
// We need to check for the columns here, because we might have associations as id as well.
|
||||
if ($ids && count($primaryKeys) === 1) {
|
||||
$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);
|
||||
}
|
||||
|
||||
foreach ($fieldMappings as $fieldMapping) {
|
||||
$metadata->mapField($fieldMapping);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build field mapping from a schema column definition
|
||||
*
|
||||
* @return mixed[]
|
||||
* @phpstan-return array{
|
||||
* fieldName: string,
|
||||
* columnName: string,
|
||||
* type: string,
|
||||
* nullable: bool,
|
||||
* options: array{
|
||||
* unsigned?: bool,
|
||||
* fixed?: bool,
|
||||
* comment: string|null,
|
||||
* default?: mixed
|
||||
* },
|
||||
* precision?: int,
|
||||
* scale?: int,
|
||||
* length?: int|null
|
||||
* }
|
||||
*/
|
||||
private function buildFieldMapping(string $tableName, Column $column): array
|
||||
{
|
||||
$fieldMapping = [
|
||||
'fieldName' => $this->getFieldNameForColumn($tableName, self::getAssetName($column), false),
|
||||
'columnName' => self::getAssetName($column),
|
||||
'type' => Type::getTypeRegistry()->lookupName($column->getType()),
|
||||
'nullable' => ! $column->getNotnull(),
|
||||
'options' => [
|
||||
'comment' => $column->getComment(),
|
||||
],
|
||||
];
|
||||
|
||||
// Type specific elements
|
||||
switch ($fieldMapping['type']) {
|
||||
case self::ARRAY:
|
||||
case Types::BLOB:
|
||||
case Types::GUID:
|
||||
case self::OBJECT:
|
||||
case Types::SIMPLE_ARRAY:
|
||||
case Types::STRING:
|
||||
case Types::TEXT:
|
||||
$fieldMapping['length'] = $column->getLength();
|
||||
$fieldMapping['options']['fixed'] = $column->getFixed();
|
||||
break;
|
||||
|
||||
case Types::DECIMAL:
|
||||
case Types::FLOAT:
|
||||
$fieldMapping['precision'] = $column->getPrecision();
|
||||
$fieldMapping['scale'] = $column->getScale();
|
||||
break;
|
||||
|
||||
case Types::INTEGER:
|
||||
case Types::BIGINT:
|
||||
case Types::SMALLINT:
|
||||
$fieldMapping['options']['unsigned'] = $column->getUnsigned();
|
||||
break;
|
||||
}
|
||||
|
||||
// Default
|
||||
$default = $column->getDefault();
|
||||
if ($default !== null) {
|
||||
$fieldMapping['options']['default'] = $default;
|
||||
}
|
||||
|
||||
return $fieldMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build to one (one to one, many to one) association mapping from class metadata.
|
||||
*/
|
||||
private function buildToOneAssociationMappings(ClassMetadata $metadata): void
|
||||
{
|
||||
assert($this->tables !== null);
|
||||
|
||||
$tableName = $metadata->table['name'];
|
||||
$primaryKeys = $this->getTablePrimaryKeys($this->tables[$tableName]);
|
||||
$foreignKeys = $this->tables[$tableName]->getForeignKeys();
|
||||
|
||||
foreach ($foreignKeys as $foreignKey) {
|
||||
$foreignTableName = self::getReferencedTableName($foreignKey);
|
||||
$fkColumns = self::getReferencingColumnNames($foreignKey);
|
||||
$fkForeignColumns = self::getReferencedColumnNames($foreignKey);
|
||||
$localColumn = current($fkColumns);
|
||||
$associationMapping = [
|
||||
'fieldName' => $this->getFieldNameForColumn($tableName, $localColumn, true),
|
||||
'targetEntity' => $this->getClassNameForTable($foreignTableName),
|
||||
];
|
||||
|
||||
if (isset($metadata->fieldMappings[$associationMapping['fieldName']])) {
|
||||
$associationMapping['fieldName'] .= '2'; // "foo" => "foo2"
|
||||
}
|
||||
|
||||
if ($primaryKeys && in_array($localColumn, $primaryKeys, true)) {
|
||||
$associationMapping['id'] = true;
|
||||
}
|
||||
|
||||
for ($i = 0, $fkColumnsCount = count($fkColumns); $i < $fkColumnsCount; $i++) {
|
||||
$associationMapping['joinColumns'][] = [
|
||||
'name' => $fkColumns[$i],
|
||||
'referencedColumnName' => $fkForeignColumns[$i],
|
||||
];
|
||||
}
|
||||
|
||||
// Here we need to check if $fkColumns are the same as $primaryKeys
|
||||
if (! array_diff($fkColumns, $primaryKeys)) {
|
||||
$metadata->mapOneToOne($associationMapping);
|
||||
} else {
|
||||
$metadata->mapManyToOne($associationMapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve schema table definition primary keys.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function getTablePrimaryKeys(Table $table): array
|
||||
{
|
||||
try {
|
||||
if (method_exists($table, 'getPrimaryKeyConstraint')) {
|
||||
return array_map(static fn (UnqualifiedName $name) => $name->toString(), $table->getPrimaryKeyConstraint()->getColumnNames());
|
||||
}
|
||||
|
||||
return self::getIndexedColumns($table->getPrimaryKey());
|
||||
} catch (SchemaException) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mapped class name for a table if it exists. Otherwise return "classified" version.
|
||||
*
|
||||
* @return class-string
|
||||
*/
|
||||
private function getClassNameForTable(string $tableName): string
|
||||
{
|
||||
if (isset($this->classNamesForTables[$tableName])) {
|
||||
return $this->namespace . $this->classNamesForTables[$tableName];
|
||||
}
|
||||
|
||||
return $this->namespace . $this->inflector->classify(strtolower($tableName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mapped field name for a column, if it exists. Otherwise return camelized version.
|
||||
*
|
||||
* @param bool $fk Whether the column is a foreignkey or not.
|
||||
*/
|
||||
private function getFieldNameForColumn(
|
||||
string $tableName,
|
||||
string $columnName,
|
||||
bool $fk = false,
|
||||
): string {
|
||||
if (isset($this->fieldNamesForColumns[$tableName], $this->fieldNamesForColumns[$tableName][$columnName])) {
|
||||
return $this->fieldNamesForColumns[$tableName][$columnName];
|
||||
}
|
||||
|
||||
$columnName = strtolower($columnName);
|
||||
|
||||
// Replace _id if it is a foreignkey column
|
||||
if ($fk) {
|
||||
$columnName = preg_replace('/_id$/', '', $columnName);
|
||||
}
|
||||
|
||||
return $this->inflector->camelize($columnName);
|
||||
}
|
||||
|
||||
private static function getReferencedTableName(ForeignKeyConstraint $foreignKey): string
|
||||
{
|
||||
if (method_exists(ForeignKeyConstraint::class, 'getReferencedTableName')) {
|
||||
return $foreignKey->getReferencedTableName()->toString();
|
||||
}
|
||||
|
||||
return $foreignKey->getForeignTableName();
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
private static function getReferencingColumnNames(ForeignKeyConstraint $foreignKey): array
|
||||
{
|
||||
if (method_exists(ForeignKeyConstraint::class, 'getReferencingColumnNames')) {
|
||||
return array_map(static fn (UnqualifiedName $name) => $name->toString(), $foreignKey->getReferencingColumnNames());
|
||||
}
|
||||
|
||||
return $foreignKey->getLocalColumns();
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
private static function getReferencedColumnNames(ForeignKeyConstraint $foreignKey): array
|
||||
{
|
||||
if (method_exists(ForeignKeyConstraint::class, 'getReferencedColumnNames')) {
|
||||
return array_map(static fn (UnqualifiedName $name) => $name->toString(), $foreignKey->getReferencedColumnNames());
|
||||
}
|
||||
|
||||
return $foreignKey->getForeignColumns();
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
private static function getIndexedColumns(Index $index): array
|
||||
{
|
||||
if (method_exists(Index::class, 'getIndexedColumns')) {
|
||||
return array_map(static fn (IndexedColumn $indexedColumn) => $indexedColumn->getColumnName()->toString(), $index->getIndexedColumns());
|
||||
}
|
||||
|
||||
return $index->getColumns();
|
||||
}
|
||||
|
||||
private static function getPrimaryKey(Table $table): Index|null
|
||||
{
|
||||
$primaryKeyConstraint = null;
|
||||
|
||||
if (method_exists(Table::class, 'getPrimaryKeyConstraint')) {
|
||||
$primaryKeyConstraint = $table->getPrimaryKeyConstraint();
|
||||
}
|
||||
|
||||
foreach ($table->getIndexes() as $index) {
|
||||
if ($primaryKeyConstraint !== null) {
|
||||
$primaryKeyConstraintColumns = array_map(static fn (UnqualifiedName $name) => $name->toString(), $primaryKeyConstraint->getColumnNames());
|
||||
|
||||
if ($primaryKeyConstraintColumns === self::getIndexedColumns($index)) {
|
||||
return $index;
|
||||
}
|
||||
} elseif ($index->isPrimary()) {
|
||||
return $index;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function getAssetName(AbstractAsset $asset): string
|
||||
{
|
||||
return $asset instanceof NamedObject
|
||||
? $asset->getObjectName()->toString()
|
||||
// DBAL < 4.4
|
||||
: $asset->getName();
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Driver;
|
||||
|
||||
use Doctrine\Persistence\Mapping\StaticReflectionService;
|
||||
|
||||
use function class_exists;
|
||||
|
||||
if (! class_exists(StaticReflectionService::class)) {
|
||||
/** @internal */
|
||||
trait LoadMappingFileImplementation
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function loadMappingFile($file): array
|
||||
{
|
||||
return $this->doLoadMappingFile($file);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/** @internal */
|
||||
trait LoadMappingFileImplementation
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function loadMappingFile($file)
|
||||
{
|
||||
return $this->doLoadMappingFile($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ use Doctrine\Persistence\Mapping\Driver\SymfonyFileLocator;
|
||||
*/
|
||||
class SimplifiedXmlDriver extends XmlDriver
|
||||
{
|
||||
public const DEFAULT_FILE_EXTENSION = '.orm.xml';
|
||||
final public const string DEFAULT_FILE_EXTENSION = '.orm.xml';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
|
||||
@@ -13,6 +13,7 @@ use Doctrine\Persistence\Mapping\Driver\FileLocator;
|
||||
use DOMDocument;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use Override;
|
||||
use SimpleXMLElement;
|
||||
|
||||
use function assert;
|
||||
@@ -41,9 +42,7 @@ use function strtoupper;
|
||||
*/
|
||||
class XmlDriver extends FileDriver
|
||||
{
|
||||
use LoadMappingFileImplementation;
|
||||
|
||||
public const DEFAULT_FILE_EXTENSION = '.dcm.xml';
|
||||
public const string DEFAULT_FILE_EXTENSION = '.dcm.xml';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
@@ -77,6 +76,7 @@ class XmlDriver extends FileDriver
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
#[Override]
|
||||
public function loadMetadataForClass($className, PersistenceClassMetadata $metadata): void
|
||||
{
|
||||
$xmlRoot = $this->getElement($className);
|
||||
@@ -916,8 +916,13 @@ class XmlDriver extends FileDriver
|
||||
return $cascades;
|
||||
}
|
||||
|
||||
/** @return array<class-string, SimpleXMLElement> */
|
||||
private function doLoadMappingFile(string $file): array
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return array<class-string, SimpleXMLElement>
|
||||
*/
|
||||
#[Override]
|
||||
protected function loadMappingFile(string $file): array
|
||||
{
|
||||
$this->validateMapping($file);
|
||||
$result = [];
|
||||
|
||||
@@ -4,15 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use ArrayAccess;
|
||||
|
||||
use function property_exists;
|
||||
|
||||
/** @template-implements ArrayAccess<string, mixed> */
|
||||
final class EmbeddedClassMapping implements ArrayAccess
|
||||
final class EmbeddedClassMapping
|
||||
{
|
||||
use ArrayAccessImplementation;
|
||||
|
||||
public string|false|null $columnPrefix = null;
|
||||
public string|null $declaredField = null;
|
||||
public string|null $originalField = null;
|
||||
|
||||
@@ -4,17 +4,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use ArrayAccess;
|
||||
use BackedEnum;
|
||||
|
||||
use function in_array;
|
||||
use function property_exists;
|
||||
|
||||
/** @template-implements ArrayAccess<string, mixed> */
|
||||
final class FieldMapping implements ArrayAccess
|
||||
final class FieldMapping
|
||||
{
|
||||
use ArrayAccessImplementation;
|
||||
|
||||
/** The database length of the column. Optional. Default value taken from the type. */
|
||||
public int|null $length = null;
|
||||
/**
|
||||
@@ -72,9 +68,6 @@ final class FieldMapping implements ArrayAccess
|
||||
public array|null $options = null;
|
||||
public bool|null $version = null;
|
||||
|
||||
/** @deprecated Use options with 'default' key instead */
|
||||
public string|int|null $default = null;
|
||||
|
||||
/**
|
||||
* @param string $type The type name of the mapped field. Can be one of
|
||||
* Doctrine's mapping types or a custom mapping type.
|
||||
@@ -162,7 +155,6 @@ final class FieldMapping implements ArrayAccess
|
||||
'declared',
|
||||
'declaredField',
|
||||
'options',
|
||||
'default',
|
||||
] as $key
|
||||
) {
|
||||
if ($this->$key !== null) {
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Doctrine\Persistence\Mapping\StaticReflectionService;
|
||||
use ReflectionClass;
|
||||
|
||||
use function class_exists;
|
||||
|
||||
if (! class_exists(StaticReflectionService::class)) {
|
||||
trait GetReflectionClassImplementation
|
||||
{
|
||||
public function getReflectionClass(): ReflectionClass
|
||||
{
|
||||
return $this->reflClass;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trait GetReflectionClassImplementation
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Can return null when using static reflection, in violation of the LSP
|
||||
*/
|
||||
public function getReflectionClass(): ReflectionClass|null
|
||||
{
|
||||
return $this->reflClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Override;
|
||||
|
||||
abstract class InverseSideMapping extends AssociationMapping
|
||||
{
|
||||
/**
|
||||
@@ -20,6 +22,7 @@ abstract class InverseSideMapping extends AssociationMapping
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
#[Override]
|
||||
public function __sleep(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -4,15 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use ArrayAccess;
|
||||
|
||||
use function property_exists;
|
||||
|
||||
/** @template-implements ArrayAccess<string, mixed> */
|
||||
final class JoinColumnMapping implements ArrayAccess
|
||||
final class JoinColumnMapping
|
||||
{
|
||||
use ArrayAccessImplementation;
|
||||
|
||||
public bool|null $deferrable = null;
|
||||
public bool|null $unique = null;
|
||||
public bool|null $quoted = null;
|
||||
|
||||
@@ -4,16 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use ArrayAccess;
|
||||
|
||||
use function array_map;
|
||||
use function in_array;
|
||||
|
||||
/** @template-implements ArrayAccess<string, mixed> */
|
||||
final class JoinTableMapping implements ArrayAccess
|
||||
final class JoinTableMapping
|
||||
{
|
||||
use ArrayAccessImplementation;
|
||||
|
||||
public bool|null $quoted = null;
|
||||
|
||||
/** @var list<JoinColumnMapping> */
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use ArrayAccess;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\Persistence\Mapping\ReflectionService;
|
||||
use Doctrine\Persistence\Reflection\EnumReflectionProperty;
|
||||
use Generator;
|
||||
use IteratorAggregate;
|
||||
use OutOfBoundsException;
|
||||
use ReflectionProperty;
|
||||
use Traversable;
|
||||
|
||||
use function array_keys;
|
||||
use function assert;
|
||||
use function is_string;
|
||||
use function str_contains;
|
||||
use function str_replace;
|
||||
|
||||
/**
|
||||
* @template-implements ArrayAccess<string, ReflectionProperty|null>
|
||||
* @template-implements IteratorAggregate<string, ReflectionProperty|null>
|
||||
*/
|
||||
class LegacyReflectionFields implements ArrayAccess, IteratorAggregate
|
||||
{
|
||||
/** @var array<string, ReflectionProperty|null> */
|
||||
private array $reflFields = [];
|
||||
|
||||
public function __construct(private ClassMetadata $classMetadata, private ReflectionService $reflectionService)
|
||||
{
|
||||
}
|
||||
|
||||
/** @param string $offset */
|
||||
public function offsetExists($offset): bool // phpcs:ignore
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/11659',
|
||||
'Access to ClassMetadata::$reflFields is deprecated and will be removed in Doctrine ORM 4.0.',
|
||||
);
|
||||
|
||||
return isset($this->classMetadata->propertyAccessors[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
*
|
||||
* @psalm-suppress LessSpecificImplementedReturnType
|
||||
*/
|
||||
public function offsetGet($field): mixed // phpcs:ignore
|
||||
{
|
||||
if (isset($this->reflFields[$field])) {
|
||||
return $this->reflFields[$field];
|
||||
}
|
||||
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/11659',
|
||||
'Access to ClassMetadata::$reflFields is deprecated and will be removed in Doctrine ORM 4.0.',
|
||||
);
|
||||
|
||||
if (isset($this->classMetadata->propertyAccessors[$field])) {
|
||||
$fieldName = str_contains($field, '.') ? $this->classMetadata->fieldMappings[$field]->originalField : $field;
|
||||
$className = $this->classMetadata->name;
|
||||
|
||||
assert(is_string($fieldName));
|
||||
|
||||
if (isset($this->classMetadata->fieldMappings[$field]) && $this->classMetadata->fieldMappings[$field]->originalClass !== null) {
|
||||
$className = $this->classMetadata->fieldMappings[$field]->originalClass;
|
||||
} elseif (isset($this->classMetadata->fieldMappings[$field]) && $this->classMetadata->fieldMappings[$field]->declared !== null) {
|
||||
$className = $this->classMetadata->fieldMappings[$field]->declared;
|
||||
} elseif (isset($this->classMetadata->associationMappings[$field]) && $this->classMetadata->associationMappings[$field]->declared !== null) {
|
||||
$className = $this->classMetadata->associationMappings[$field]->declared;
|
||||
} elseif (isset($this->classMetadata->embeddedClasses[$field]) && $this->classMetadata->embeddedClasses[$field]->declared !== null) {
|
||||
$className = $this->classMetadata->embeddedClasses[$field]->declared;
|
||||
}
|
||||
|
||||
/** @psalm-suppress ArgumentTypeCoercion */
|
||||
$this->reflFields[$field] = $this->getAccessibleProperty($className, $fieldName);
|
||||
|
||||
if (isset($this->classMetadata->fieldMappings[$field])) {
|
||||
if ($this->classMetadata->fieldMappings[$field]->enumType !== null) {
|
||||
$this->reflFields[$field] = new EnumReflectionProperty(
|
||||
$this->reflFields[$field],
|
||||
$this->classMetadata->fieldMappings[$field]->enumType,
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->classMetadata->fieldMappings[$field]->originalField !== null) {
|
||||
$parentField = str_replace('.' . $fieldName, '', $field);
|
||||
$originalClass = $this->classMetadata->fieldMappings[$field]->originalClass;
|
||||
|
||||
if (! str_contains($parentField, '.')) {
|
||||
$parentClass = $this->classMetadata->name;
|
||||
} else {
|
||||
$parentClass = $this->classMetadata->fieldMappings[$parentField]->originalClass;
|
||||
}
|
||||
|
||||
/** @psalm-var class-string $parentClass */
|
||||
/** @psalm-var class-string $originalClass */
|
||||
|
||||
$this->reflFields[$field] = new ReflectionEmbeddedProperty(
|
||||
$this->getAccessibleProperty($parentClass, $parentField),
|
||||
$this->reflFields[$field],
|
||||
$originalClass,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->reflFields[$field];
|
||||
}
|
||||
|
||||
throw new OutOfBoundsException('Unknown field: ' . $this->classMetadata->name . ' ::$' . $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $offset
|
||||
* @param ReflectionProperty $value
|
||||
*/
|
||||
public function offsetSet($offset, $value): void // phpcs:ignore
|
||||
{
|
||||
$this->reflFields[$offset] = $value;
|
||||
}
|
||||
|
||||
/** @param string $offset */
|
||||
public function offsetUnset($offset): void // phpcs:ignore
|
||||
{
|
||||
unset($this->reflFields[$offset]);
|
||||
}
|
||||
|
||||
/** @psalm-param class-string $class */
|
||||
private function getAccessibleProperty(string $class, string $field): ReflectionProperty
|
||||
{
|
||||
$reflectionProperty = $this->reflectionService->getAccessibleProperty($class, $field);
|
||||
|
||||
assert($reflectionProperty !== null);
|
||||
|
||||
if ($reflectionProperty->isReadOnly()) {
|
||||
$declaringClass = $reflectionProperty->class;
|
||||
if ($declaringClass !== $class) {
|
||||
$reflectionProperty = $this->reflectionService->getAccessibleProperty($declaringClass, $field);
|
||||
|
||||
assert($reflectionProperty !== null);
|
||||
}
|
||||
|
||||
$reflectionProperty = new ReflectionReadonlyProperty($reflectionProperty);
|
||||
}
|
||||
|
||||
return $reflectionProperty;
|
||||
}
|
||||
|
||||
/** @return Generator<string, ReflectionProperty> */
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/11659',
|
||||
'Access to ClassMetadata::$reflFields is deprecated and will be removed in Doctrine ORM 4.0.',
|
||||
);
|
||||
|
||||
$keys = array_keys($this->classMetadata->propertyAccessors);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
yield $key => $this->offsetGet($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Override;
|
||||
|
||||
use function strtolower;
|
||||
use function trim;
|
||||
@@ -28,6 +28,7 @@ final class ManyToManyOwningSideMapping extends ToManyOwningSideMapping implemen
|
||||
public array $relationToTargetKeyColumns = [];
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
#[Override]
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = parent::toArray();
|
||||
@@ -130,14 +131,7 @@ final class ManyToManyOwningSideMapping extends ToManyOwningSideMapping implemen
|
||||
|
||||
foreach ($mapping->joinTable->joinColumns as $joinColumn) {
|
||||
if ($joinColumn->nullable !== null) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/12126',
|
||||
<<<'DEPRECATION'
|
||||
Specifying the "nullable" attribute for join columns in many-to-many associations (here, %s::$%s) is a no-op.
|
||||
The ORM will always set it to false.
|
||||
Doing so is deprecated and will be an error in 4.0.
|
||||
DEPRECATION,
|
||||
throw MappingException::cannotSetNullableOnManyToManyJoinColumns(
|
||||
$mapping->sourceEntity,
|
||||
$mapping->fieldName,
|
||||
);
|
||||
@@ -169,14 +163,7 @@ final class ManyToManyOwningSideMapping extends ToManyOwningSideMapping implemen
|
||||
|
||||
foreach ($mapping->joinTable->inverseJoinColumns as $inverseJoinColumn) {
|
||||
if ($inverseJoinColumn->nullable !== null) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/12126',
|
||||
<<<'DEPRECATION'
|
||||
Specifying the "nullable" attribute for join columns in many-to-many associations (here, %s::$%s) is a no-op.
|
||||
The ORM will always set it to false.
|
||||
Doing so is deprecated and will be an error in 4.0.
|
||||
DEPRECATION,
|
||||
throw MappingException::cannotSetNullableOnManyToManyJoinColumns(
|
||||
$mapping->targetEntity,
|
||||
$mapping->fieldName,
|
||||
);
|
||||
@@ -210,6 +197,7 @@ final class ManyToManyOwningSideMapping extends ToManyOwningSideMapping implemen
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
#[Override]
|
||||
public function __sleep(): array
|
||||
{
|
||||
$serialized = parent::__sleep();
|
||||
|
||||
@@ -8,6 +8,7 @@ use BackedEnum;
|
||||
use Doctrine\ORM\Exception\ORMException;
|
||||
use Doctrine\Persistence\Mapping\MappingException as PersistenceMappingException;
|
||||
use LibXMLError;
|
||||
use Override;
|
||||
use ReflectionException;
|
||||
use ValueError;
|
||||
|
||||
@@ -87,6 +88,7 @@ class MappingException extends PersistenceMappingException implements ORMExcepti
|
||||
return new self(sprintf("The embed mapping '%s' misses the 'class' attribute.", $fieldName));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public static function mappingFileNotFound(string $entityName, string $fileName): self
|
||||
{
|
||||
return new self(sprintf("No mapping file found named '%s' for class '%s'.", $fileName, $entityName));
|
||||
@@ -317,6 +319,7 @@ class MappingException extends PersistenceMappingException implements ORMExcepti
|
||||
));
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public static function fileMappingDriversRequireConfiguredDirectoryPath(string|null $path = null): self
|
||||
{
|
||||
if (! empty($path)) {
|
||||
@@ -424,6 +427,28 @@ class MappingException extends PersistenceMappingException implements ORMExcepti
|
||||
return new self(sprintf("Discriminator column name on entity class '%s' is not defined.", $className));
|
||||
}
|
||||
|
||||
public static function cannotSetNullableOnToOneIdentifierJoinColumns(
|
||||
string $className,
|
||||
string $fieldName,
|
||||
): self {
|
||||
return new self(sprintf(
|
||||
'Cannot specify the "nullable" attribute for join columns in to-one associations (%s::$%s) that are part of the identifier',
|
||||
$className,
|
||||
$fieldName,
|
||||
));
|
||||
}
|
||||
|
||||
public static function cannotSetNullableOnManyToManyJoinColumns(
|
||||
string $className,
|
||||
string $fieldName,
|
||||
): self {
|
||||
return new self(sprintf(
|
||||
'Cannot specify the "nullable" attribute for join columns in many-to-many associations (%s::$%s)',
|
||||
$className,
|
||||
$fieldName,
|
||||
));
|
||||
}
|
||||
|
||||
public static function cannotVersionIdField(string $className, string $fieldName): self
|
||||
{
|
||||
return new self(sprintf(
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Override;
|
||||
|
||||
final class OneToManyAssociationMapping extends ToManyInverseSideMapping
|
||||
{
|
||||
/**
|
||||
@@ -28,6 +30,7 @@ final class OneToManyAssociationMapping extends ToManyInverseSideMapping
|
||||
* isOwningSide: bool,
|
||||
* } $mappingArray
|
||||
*/
|
||||
#[Override]
|
||||
public static function fromMappingArray(array $mappingArray): static
|
||||
{
|
||||
$mapping = parent::fromMappingArray($mappingArray);
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Override;
|
||||
|
||||
abstract class OwningSideMapping extends AssociationMapping
|
||||
{
|
||||
/**
|
||||
@@ -15,6 +17,7 @@ abstract class OwningSideMapping extends AssociationMapping
|
||||
public string|null $inversedBy = null;
|
||||
|
||||
/** @return list<string> */
|
||||
#[Override]
|
||||
public function __sleep(): array
|
||||
{
|
||||
$serialized = parent::__sleep();
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Mapping\PropertyAccessors;
|
||||
|
||||
use Doctrine\Instantiator\Instantiator;
|
||||
use Override;
|
||||
use ReflectionProperty;
|
||||
|
||||
/** @internal */
|
||||
@@ -20,6 +21,7 @@ class EmbeddablePropertyAccessor implements PropertyAccessor
|
||||
) {
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function setValue(object $object, mixed $value): void
|
||||
{
|
||||
$embeddedObject = $this->parent->getValue($object);
|
||||
@@ -35,6 +37,7 @@ class EmbeddablePropertyAccessor implements PropertyAccessor
|
||||
$this->child->setValue($embeddedObject, $value);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getValue(object $object): mixed
|
||||
{
|
||||
$embeddedObject = $this->parent->getValue($object);
|
||||
@@ -46,6 +49,7 @@ class EmbeddablePropertyAccessor implements PropertyAccessor
|
||||
return $this->child->getValue($embeddedObject);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getUnderlyingReflector(): ReflectionProperty
|
||||
{
|
||||
return $this->child->getUnderlyingReflector();
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Mapping\PropertyAccessors;
|
||||
|
||||
use BackedEnum;
|
||||
use Override;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function array_map;
|
||||
@@ -19,6 +20,7 @@ class EnumPropertyAccessor implements PropertyAccessor
|
||||
{
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function setValue(object $object, mixed $value): void
|
||||
{
|
||||
if ($value !== null) {
|
||||
@@ -28,6 +30,7 @@ class EnumPropertyAccessor implements PropertyAccessor
|
||||
$this->parent->setValue($object, $value);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getValue(object $object): mixed
|
||||
{
|
||||
$enum = $this->parent->getValue($object);
|
||||
@@ -78,6 +81,7 @@ class EnumPropertyAccessor implements PropertyAccessor
|
||||
return $this->enumType::from($value);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getUnderlyingReflector(): ReflectionProperty
|
||||
{
|
||||
return $this->parent->getUnderlyingReflector();
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping\PropertyAccessors;
|
||||
|
||||
use Doctrine\ORM\Proxy\InternalProxy;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function ltrim;
|
||||
|
||||
/** @internal */
|
||||
class ObjectCastPropertyAccessor implements PropertyAccessor
|
||||
{
|
||||
/** @param class-string $class */
|
||||
public static function fromNames(string $class, string $name): self
|
||||
{
|
||||
$reflectionProperty = new ReflectionProperty($class, $name);
|
||||
|
||||
$key = $reflectionProperty->isPrivate() ? "\0" . ltrim($class, '\\') . "\0" . $name : ($reflectionProperty->isProtected() ? "\0*\0" . $name : $name);
|
||||
|
||||
return new self($reflectionProperty, $key);
|
||||
}
|
||||
|
||||
public static function fromReflectionProperty(ReflectionProperty $reflectionProperty): self
|
||||
{
|
||||
$name = $reflectionProperty->getName();
|
||||
$key = $reflectionProperty->isPrivate() ? "\0" . ltrim($reflectionProperty->getDeclaringClass()->getName(), '\\') . "\0" . $name : ($reflectionProperty->isProtected() ? "\0*\0" . $name : $name);
|
||||
|
||||
return new self($reflectionProperty, $key);
|
||||
}
|
||||
|
||||
private function __construct(private ReflectionProperty $reflectionProperty, private string $key)
|
||||
{
|
||||
}
|
||||
|
||||
public function setValue(object $object, mixed $value): void
|
||||
{
|
||||
if (! ($object instanceof InternalProxy && ! $object->__isInitialized())) {
|
||||
$this->reflectionProperty->setValue($object, $value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$object->__setInitialized(true);
|
||||
|
||||
$this->reflectionProperty->setValue($object, $value);
|
||||
|
||||
$object->__setInitialized(false);
|
||||
}
|
||||
|
||||
public function getValue(object $object): mixed
|
||||
{
|
||||
return ((array) $object)[$this->key] ?? null;
|
||||
}
|
||||
|
||||
public function getUnderlyingReflector(): ReflectionProperty
|
||||
{
|
||||
return $this->reflectionProperty;
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@ namespace Doctrine\ORM\Mapping\PropertyAccessors;
|
||||
|
||||
use ReflectionProperty;
|
||||
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
class PropertyAccessorFactory
|
||||
{
|
||||
/** @phpstan-param class-string $className */
|
||||
@@ -15,9 +13,7 @@ class PropertyAccessorFactory
|
||||
{
|
||||
$reflectionProperty = new ReflectionProperty($className, $propertyName);
|
||||
|
||||
$accessor = PHP_VERSION_ID >= 80400
|
||||
? RawValuePropertyAccessor::fromReflectionProperty($reflectionProperty)
|
||||
: ObjectCastPropertyAccessor::fromReflectionProperty($reflectionProperty);
|
||||
$accessor = RawValuePropertyAccessor::fromReflectionProperty($reflectionProperty);
|
||||
|
||||
if ($reflectionProperty->hasType() && ! $reflectionProperty->getType()->allowsNull()) {
|
||||
$accessor = new TypedNoDefaultPropertyAccessor($accessor, $reflectionProperty);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user