mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 06:52:09 +01:00
Compare commits
164 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c351e0444 | ||
|
|
5ed5383338 | ||
|
|
eb1d54871b | ||
|
|
e148c838b0 | ||
|
|
c73df2a7b4 | ||
|
|
38682e93db | ||
|
|
84df37de97 | ||
|
|
856c3143f8 | ||
|
|
79f73a23f3 | ||
|
|
4af912f712 | ||
|
|
65f48e0ecd | ||
|
|
193c3abf0e | ||
|
|
5b8263e8fb | ||
|
|
26e85b8c88 | ||
|
|
152c04c03d | ||
|
|
12ab6fa43f | ||
|
|
e8e61cbbd5 | ||
|
|
8f847cb5aa | ||
|
|
599832cb81 | ||
|
|
530f515556 | ||
|
|
1c55025b12 | ||
|
|
0900d4bc97 | ||
|
|
be2518d784 | ||
|
|
bdd8883d12 | ||
|
|
536b65f02b | ||
|
|
103c42cdb7 | ||
|
|
cdaf7b5308 | ||
|
|
4b88ce787d | ||
|
|
f9c3470a8d | ||
|
|
c1b131b67e | ||
|
|
16b82ea061 | ||
|
|
f8f370ace6 | ||
|
|
d5c69fb73f | ||
|
|
93f9eb7af2 | ||
|
|
6d5da83c68 | ||
|
|
5f01dd8d09 | ||
|
|
b596e6a665 | ||
|
|
79d3cf5880 | ||
|
|
d7b7c28ae5 | ||
|
|
d6fd510c49 | ||
|
|
a2a7d5bb01 | ||
|
|
223b2650c4 | ||
|
|
01c1644d9c | ||
|
|
3eff2d4b3f | ||
|
|
9ddf8b96f8 | ||
|
|
3d00fa817a | ||
|
|
c0a1404e4c | ||
|
|
bfed8cb6ed | ||
|
|
09a2648f7e | ||
|
|
ee591195cf | ||
|
|
e974313523 | ||
|
|
1e972b6e0e | ||
|
|
e369cb6e73 | ||
|
|
ec391be4f2 | ||
|
|
697e23422f | ||
|
|
e487b6fe2b | ||
|
|
656f881756 | ||
|
|
cd2aa487a5 | ||
|
|
b7d822972e | ||
|
|
ec63f5d32a | ||
|
|
952ccc5fc8 | ||
|
|
9a2f1f380d | ||
|
|
580b9196e6 | ||
|
|
0d911b9381 | ||
|
|
c6d8aecc0f | ||
|
|
fdd3d112b0 | ||
|
|
2fecb3cb1a | ||
|
|
f3630ea16b | ||
|
|
fd19444761 | ||
|
|
4b1afb41b3 | ||
|
|
f9f453f4d7 | ||
|
|
f508a4bb71 | ||
|
|
5d0fbc47d0 | ||
|
|
1e977426eb | ||
|
|
2640f88f8a | ||
|
|
fa731b10ec | ||
|
|
4117ca349f | ||
|
|
2d475c9bb3 | ||
|
|
6f54011e7b | ||
|
|
760397c429 | ||
|
|
7190ac5127 | ||
|
|
ceaefcb18d | ||
|
|
844ce77cae | ||
|
|
cf3a185b62 | ||
|
|
efc982a48d | ||
|
|
96bc214acd | ||
|
|
15999758a7 | ||
|
|
44aa8c2c5b | ||
|
|
8c6fc5ae52 | ||
|
|
c4561571aa | ||
|
|
40a203843d | ||
|
|
8b5ee54c6a | ||
|
|
03fa495fbc | ||
|
|
5901848944 | ||
|
|
d40f9e57ff | ||
|
|
133cc95f33 | ||
|
|
d30e748e64 | ||
|
|
98d77043d8 | ||
|
|
40d1e7bbfc | ||
|
|
e8275f6e4d | ||
|
|
70dcffa025 | ||
|
|
c94a9b1d8b | ||
|
|
6a9393e8ed | ||
|
|
ab98d0ffc6 | ||
|
|
2ddeb79431 | ||
|
|
92ff9c9108 | ||
|
|
7c58dc89c3 | ||
|
|
b513f7c935 | ||
|
|
f1483f848c | ||
|
|
ea4c9b21b7 | ||
|
|
72edfbc270 | ||
|
|
5ccf2eac40 | ||
|
|
6696b0dfbf | ||
|
|
aead77d597 | ||
|
|
130c27c1da | ||
|
|
f6e1dd44f0 | ||
|
|
1e9973a0c0 | ||
|
|
91761738fd | ||
|
|
cccb2e2fdf | ||
|
|
18138d895e | ||
|
|
95d434d003 | ||
|
|
70c651ebb7 | ||
|
|
8cb62a616a | ||
|
|
fa2b52c974 | ||
|
|
6d306c1946 | ||
|
|
5bf814032f | ||
|
|
bea5e7166c | ||
|
|
003090b70c | ||
|
|
02a4e4099d | ||
|
|
56e0ac02af | ||
|
|
12a70bbefb | ||
|
|
5a4ddb2870 | ||
|
|
42195060e6 | ||
|
|
68fa55f310 | ||
|
|
0b0c3e7e58 | ||
|
|
92434f91c7 | ||
|
|
6414ad4cbb | ||
|
|
ac5aea1c81 | ||
|
|
a75605b8c3 | ||
|
|
66c95a65c5 | ||
|
|
62a0d7359b | ||
|
|
2c7d7ebb48 | ||
|
|
8b6fe52f74 | ||
|
|
eabb7f84e9 | ||
|
|
f0a20dbc9c | ||
|
|
32cd2106d0 | ||
|
|
6857a2e8d4 | ||
|
|
5e8b34ae30 | ||
|
|
a9b682b7c0 | ||
|
|
0aadc456dc | ||
|
|
77b7107d05 | ||
|
|
2488b4c50c | ||
|
|
ed642c72c9 | ||
|
|
2dde65c4ba | ||
|
|
1e971d85c4 | ||
|
|
2074fc3ff9 | ||
|
|
8fef44333b | ||
|
|
3361691d0a | ||
|
|
b6a2257758 | ||
|
|
06d9c584a3 | ||
|
|
b0381b3705 | ||
|
|
3984f74eb4 | ||
|
|
ebdced6175 | ||
|
|
1a702075ba |
@@ -12,21 +12,27 @@
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.11",
|
||||
"branchName": "2.11.x",
|
||||
"slug": "2.11",
|
||||
"name": "2.12",
|
||||
"branchName": "2.12.x",
|
||||
"slug": "2.12",
|
||||
"upcoming": true
|
||||
},
|
||||
{
|
||||
"name": "2.10",
|
||||
"branchName": "2.10.x",
|
||||
"slug": "2.10",
|
||||
"name": "2.11",
|
||||
"branchName": "2.11.x",
|
||||
"slug": "2.11",
|
||||
"current": true,
|
||||
"aliases": [
|
||||
"current",
|
||||
"stable"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "2.10",
|
||||
"branchName": "2.10.x",
|
||||
"slug": "2.10",
|
||||
"maintained": false
|
||||
},
|
||||
{
|
||||
"name": "2.9",
|
||||
"branchName": "2.9.x",
|
||||
|
||||
4
.github/workflows/coding-standard.yml
vendored
4
.github/workflows/coding-standard.yml
vendored
@@ -10,6 +10,6 @@ on:
|
||||
|
||||
jobs:
|
||||
coding-standards:
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@1.1.1"
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@1.4.1"
|
||||
with:
|
||||
php-version: "7.4"
|
||||
php-version: "8.1"
|
||||
|
||||
16
.github/workflows/continuous-integration.yml
vendored
16
.github/workflows/continuous-integration.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
- php-version: "8.0"
|
||||
dbal-version: "2.13"
|
||||
- php-version: "8.1"
|
||||
dbal-version: "3.2@dev"
|
||||
dbal-version: "3@dev"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
@@ -78,16 +78,16 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
- "8.1"
|
||||
dbal-version:
|
||||
- "default"
|
||||
postgres-version:
|
||||
- "9.6"
|
||||
- "13"
|
||||
- "14"
|
||||
include:
|
||||
- php-version: "8.0"
|
||||
dbal-version: "2.13"
|
||||
postgres-version: "13"
|
||||
postgres-version: "14"
|
||||
|
||||
services:
|
||||
postgres:
|
||||
@@ -139,18 +139,18 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
- "8.1"
|
||||
dbal-version:
|
||||
- "default"
|
||||
mariadb-version:
|
||||
- "10.5"
|
||||
- "10.6"
|
||||
extension:
|
||||
- "mysqli"
|
||||
- "pdo_mysql"
|
||||
include:
|
||||
- php-version: "8.0"
|
||||
dbal-version: "2.13"
|
||||
mariadb-version: "10.5"
|
||||
mariadb-version: "10.6"
|
||||
extension: "pdo_mysql"
|
||||
|
||||
services:
|
||||
@@ -205,7 +205,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.4"
|
||||
- "8.1"
|
||||
dbal-version:
|
||||
- "default"
|
||||
mysql-version:
|
||||
|
||||
@@ -7,9 +7,8 @@ on:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@1.1.1"
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@1.4.1"
|
||||
secrets:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
|
||||
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}
|
||||
ORGANIZATION_ADMIN_TOKEN: ${{ secrets.ORGANIZATION_ADMIN_TOKEN }}
|
||||
|
||||
11
.github/workflows/static-analysis.yml
vendored
11
.github/workflows/static-analysis.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.0"
|
||||
- "8.1"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "2.13"
|
||||
@@ -58,10 +58,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.0"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "2.13"
|
||||
- "8.1"
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
@@ -73,10 +70,6 @@ jobs:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
|
||||
- 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@v1"
|
||||
with:
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -15,5 +15,5 @@ vendor/
|
||||
/tests/Doctrine/Performance/history.db
|
||||
/.phpcs-cache
|
||||
composer.lock
|
||||
/.phpunit.result.cache
|
||||
.phpunit.result.cache
|
||||
/*.phpunit.xml
|
||||
|
||||
14
README.md
14
README.md
@@ -1,7 +1,7 @@
|
||||
| [3.0.x][3.0] | [2.11.x][2.11] | [2.10.x][2.10] |
|
||||
| [3.0.x][3.0] | [2.12.x][2.12] | [2.11.x][2.11] |
|
||||
|:----------------:|:----------------:|:----------:|
|
||||
| [![Build status][3.0 image]][3.0] | [![Build status][2.11 image]][2.11] | [![Build status][2.10 image]][2.10] |
|
||||
| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.11 coverage image]][2.11 coverage] | [![Coverage Status][2.10 coverage image]][2.10 coverage] |
|
||||
| [![Build status][3.0 image]][3.0] | [![Build status][2.12 image]][2.12] | [![Build status][2.11 image]][2.11] |
|
||||
| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.12 coverage image]][2.12 coverage] | [![Coverage Status][2.11 coverage image]][2.11 coverage] |
|
||||
|
||||
Doctrine 2 is an object-relational mapper (ORM) for PHP 7.1+ that provides transparent persistence
|
||||
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
|
||||
@@ -20,10 +20,10 @@ without requiring unnecessary code duplication.
|
||||
[3.0]: https://github.com/doctrine/orm/tree/3.0.x
|
||||
[3.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.0.x/graph/badge.svg
|
||||
[3.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.0.x
|
||||
[2.10 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.10.x
|
||||
[2.10]: https://github.com/doctrine/orm/tree/2.10.x
|
||||
[2.10 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.10.x/graph/badge.svg
|
||||
[2.10 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.10.x
|
||||
[2.12 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.12.x
|
||||
[2.12]: https://github.com/doctrine/orm/tree/2.12.x
|
||||
[2.12 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.12.x/graph/badge.svg
|
||||
[2.12 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.12.x
|
||||
[2.11 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.11.x
|
||||
[2.11]: https://github.com/doctrine/orm/tree/2.11.x
|
||||
[2.11 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.11.x/graph/badge.svg
|
||||
|
||||
56
UPGRADE.md
56
UPGRADE.md
@@ -1,3 +1,56 @@
|
||||
# Upgrade to 2.11
|
||||
|
||||
## Rename `AbstractIdGenerator::generate()` to `generateId()`
|
||||
|
||||
Implementations of `AbstractIdGenerator` have to override the method
|
||||
`generateId()` without calling the parent implementation. Not doing so is
|
||||
deprecated. Calling `generate()` on any `AbstractIdGenerator` implementation
|
||||
is deprecated.
|
||||
|
||||
## PSR-6-based second level cache
|
||||
|
||||
The second level cache has been reworked to consume a PSR-6 cache. Using a
|
||||
Doctrine Cache instance is deprecated.
|
||||
|
||||
* `DefaultCacheFactory`: The constructor expects a PSR-6 cache item pool as
|
||||
second argument now.
|
||||
* `DefaultMultiGetRegion`: This class is deprecated in favor of `DefaultRegion`.
|
||||
* `DefaultRegion`:
|
||||
* The constructor expects a PSR-6 cache item pool as second argument now.
|
||||
* The protected `$cache` property is deprecated.
|
||||
* The properties `$name` and `$lifetime` as well as the constant
|
||||
`REGION_KEY_SEPARATOR` and the method `getCacheEntryKey()` are flagged as
|
||||
`@internal` now. They all will become `private` in 3.0.
|
||||
* The method `getCache()` is deprecated without replacement.
|
||||
|
||||
## Deprecated: `Doctrine\ORM\Mapping\Driver\PHPDriver`
|
||||
|
||||
Use `StaticPHPDriver` instead when you want to programmatically configure
|
||||
entity metadata.
|
||||
|
||||
You can convert mappings with the `orm:convert-mapping` command or more simply
|
||||
in this case, `include` the metadata file from the `loadMetadata` static method
|
||||
used by the `StaticPHPDriver`.
|
||||
|
||||
## Deprecated: `Setup::registerAutoloadDirectory()`
|
||||
|
||||
Use Composer's autoloader instead.
|
||||
|
||||
## Deprecated: `AbstractHydrator::hydrateRow()`
|
||||
|
||||
Following the deprecation of the method `AbstractHydrator::iterate()`, the
|
||||
method `hydrateRow()` has been deprecated as well.
|
||||
|
||||
## Deprecate cache settings inspection
|
||||
|
||||
Doctrine does not provide its own cache implementation anymore and relies on
|
||||
the PSR-6 standard instead. As a consequence, we cannot determine anymore
|
||||
whether a given cache adapter is suitable for a production environment.
|
||||
Because of that, functionality that aims to do so has been deprecated:
|
||||
|
||||
* `Configuration::ensureProductionSettings()`
|
||||
* the `orm:ensure-production-settings` console command
|
||||
|
||||
# Upgrade to 2.10
|
||||
|
||||
## BC Break: `UnitOfWork` now relies on SPL object IDs, not hashes
|
||||
@@ -182,8 +235,7 @@ These methods have been deprecated:
|
||||
## Deprecated `Doctrine\ORM\Version`
|
||||
|
||||
The `Doctrine\ORM\Version` class is now deprecated and will be removed in Doctrine ORM 3.0:
|
||||
please refrain from checking the ORM version at runtime or use
|
||||
[ocramius/package-versions](https://github.com/Ocramius/PackageVersions/).
|
||||
please refrain from checking the ORM version at runtime or use Composer's [runtime API](https://getcomposer.org/doc/07-runtime.md#knowing-whether-package-x-is-installed-in-version-y).
|
||||
|
||||
## Deprecated `EntityManager#merge()` method
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
<var name="db_driver" value="mysqli"/>
|
||||
<var name="db_host" value="127.0.0.1" />
|
||||
<var name="db_port" value="3306"/>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
<var name="db_driver" value="pdo_mysql"/>
|
||||
<var name="db_host" value="127.0.0.1" />
|
||||
<var name="db_port" value="3306"/>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
<var name="db_driver" value="pdo_pgsql"/>
|
||||
<var name="db_host" value="localhost" />
|
||||
<var name="db_user" value="postgres" />
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
|
||||
colors="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
<!-- use an in-memory sqlite database -->
|
||||
<var name="db_driver" value="pdo_sqlite"/>
|
||||
<var name="db_memory" value="true"/>
|
||||
|
||||
@@ -13,17 +13,20 @@
|
||||
{"name": "Marco Pivetta", "email": "ocramius@gmail.com"}
|
||||
],
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 ||^8.0",
|
||||
"php": "^7.1 || ^8.0",
|
||||
"composer-runtime-api": "^2",
|
||||
"ext-ctype": "*",
|
||||
"ext-pdo": "*",
|
||||
"composer/package-versions-deprecated": "^1.8",
|
||||
"doctrine/cache": "^1.12.1 || ^2.1.1",
|
||||
"doctrine/collections": "^1.5",
|
||||
"doctrine/common": "^3.0.3",
|
||||
"doctrine/dbal": "^2.13.1 || ^3.1.1",
|
||||
"doctrine/dbal": "^2.13.1 || ^3.2",
|
||||
"doctrine/deprecations": "^0.5.3",
|
||||
"doctrine/event-manager": "^1.1",
|
||||
"doctrine/inflector": "^1.4 || ^2.0",
|
||||
@@ -39,12 +42,12 @@
|
||||
"doctrine/annotations": "^1.13",
|
||||
"doctrine/coding-standard": "^9.0",
|
||||
"phpbench/phpbench": "^0.16.10 || ^1.0",
|
||||
"phpstan/phpstan": "1.2.0",
|
||||
"phpstan/phpstan": "1.4.6",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
|
||||
"squizlabs/php_codesniffer": "3.6.1",
|
||||
"symfony/cache": "^4.4 || ^5.2",
|
||||
"squizlabs/php_codesniffer": "3.6.2",
|
||||
"symfony/cache": "^4.4 || ^5.4 || ^6.0",
|
||||
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
|
||||
"vimeo/psalm": "4.13.1"
|
||||
"vimeo/psalm": "4.22.0"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/annotations": "<1.13 || >= 2.0"
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
Accessing private/protected properties/methods of the same class from different instance
|
||||
========================================================================================
|
||||
|
||||
.. sectionauthor:: Michael Olsavsky (olsavmic)
|
||||
|
||||
As explained in the :doc:`restrictions for entity classes in the manual <../reference/architecture>`,
|
||||
it is dangerous to access private/protected properties of different entity instance of the same class because of lazy loading.
|
||||
|
||||
The proxy instance that's injected instead of the real entity may not be initialized yet
|
||||
and therefore not contain expected data which may result in unexpected behavior.
|
||||
That's a limitation of current proxy implementation - only public methods automatically initialize proxies.
|
||||
|
||||
It is usually preferable to use a public interface to manipulate the object from outside the `$this`
|
||||
context but it may not be convenient in some cases. The following example shows how to do it safely.
|
||||
|
||||
Safely accessing private properties from different instance of the same class
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
To safely access private property of different instance of the same class, make sure to initialise
|
||||
the proxy before use manually as follows:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\Common\Proxy\Proxy;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Entity
|
||||
{
|
||||
// ...
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Entity")
|
||||
* @ORM\JoinColumn(nullable=false)
|
||||
*/
|
||||
private self $parent;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", nullable=false)
|
||||
*/
|
||||
private string $name;
|
||||
|
||||
// ...
|
||||
|
||||
public function doSomethingWithParent()
|
||||
{
|
||||
// Always initializing the proxy before use
|
||||
if ($this->parent instanceof Proxy) {
|
||||
$this->parent->__load();
|
||||
}
|
||||
|
||||
// Accessing the `$this->parent->name` property without loading the proxy first
|
||||
// may throw error in case the Proxy has not been initialized yet.
|
||||
$this->parent->name;
|
||||
}
|
||||
|
||||
public function doSomethingWithAnotherInstance(self $instance)
|
||||
{
|
||||
// Always initializing the proxy before use
|
||||
if ($instance instanceof Proxy) {
|
||||
$instance->__load();
|
||||
}
|
||||
|
||||
// Accessing the `$instance->name` property without loading the proxy first
|
||||
// may throw error in case the Proxy has not been initialized yet.
|
||||
$instance->name;
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -4,7 +4,7 @@ Advanced field value conversion using custom mapping types
|
||||
.. sectionauthor:: Jan Sorgalla <jsorgalla@googlemail.com>
|
||||
|
||||
When creating entities, you sometimes have the need to transform field values
|
||||
before they are saved to the database. In Doctrine you can use Custom Mapping
|
||||
before they are saved to the database. In Doctrine you can use Custom Mapping
|
||||
Types to solve this (see: :ref:`reference-basic-mapping-custom-mapping-types`).
|
||||
|
||||
There are several ways to achieve this: converting the value inside the Type
|
||||
@@ -15,7 +15,7 @@ type `Point <https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html>`_.
|
||||
|
||||
The ``Point`` type is part of the `Spatial extension <https://dev.mysql.com/doc/refman/8.0/en/spatial-extensions.html>`_
|
||||
of MySQL and enables you to store a single location in a coordinate space by
|
||||
using x and y coordinates. You can use the Point type to store a
|
||||
using x and y coordinates. You can use the Point type to store a
|
||||
longitude/latitude pair to represent a geographic location.
|
||||
|
||||
The entity
|
||||
@@ -29,9 +29,9 @@ The entity class:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
namespace Geo\Entity;
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
@@ -84,7 +84,7 @@ The entity class:
|
||||
}
|
||||
}
|
||||
|
||||
We use the custom type ``point`` in the ``@Column`` docblock annotation of the
|
||||
We use the custom type ``point`` in the ``@Column`` docblock annotation of the
|
||||
``$point`` field. We will create this custom mapping type in the next chapter.
|
||||
|
||||
The point class:
|
||||
@@ -92,7 +92,7 @@ The point class:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
namespace Geo\ValueObject;
|
||||
|
||||
class Point
|
||||
@@ -196,7 +196,7 @@ The format of the string representation format is called
|
||||
`Well-known text (WKT) <https://en.wikipedia.org/wiki/Well-known_text>`_.
|
||||
The advantage of this format is, that it is both human readable and parsable by MySQL.
|
||||
|
||||
Internally, MySQL stores geometry values in a binary format that is not
|
||||
Internally, MySQL stores geometry values in a binary format that is not
|
||||
identical to the WKT format. So, we need to let MySQL transform the WKT
|
||||
representation into its internal format.
|
||||
|
||||
@@ -210,13 +210,13 @@ which convert WKT strings to and from the internal format of MySQL.
|
||||
|
||||
.. note::
|
||||
|
||||
When using DQL queries, the ``convertToPHPValueSQL`` and
|
||||
When using DQL queries, the ``convertToPHPValueSQL`` and
|
||||
``convertToDatabaseValueSQL`` methods only apply to identification variables
|
||||
and path expressions in SELECT clauses. Expressions in WHERE clauses are
|
||||
and path expressions in SELECT clauses. Expressions in WHERE clauses are
|
||||
**not** wrapped!
|
||||
|
||||
If you want to use Point values in WHERE clauses, you have to implement a
|
||||
:doc:`user defined function <dql-user-defined-functions>` for
|
||||
:doc:`user defined function <dql-user-defined-functions>` for
|
||||
``PointFromText``.
|
||||
|
||||
Example usage
|
||||
@@ -252,5 +252,5 @@ Example usage
|
||||
$query = $em->createQuery("SELECT l FROM Geo\Entity\Location l WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'");
|
||||
$location = $query->getSingleResult();
|
||||
|
||||
/* @var Geo\ValueObject\Point */
|
||||
/** @var Geo\ValueObject\Point */
|
||||
$point = $location->getPoint();
|
||||
|
||||
@@ -88,7 +88,7 @@ API would look for this use-case:
|
||||
$pageNum = 1;
|
||||
$query = $em->createQuery($dql);
|
||||
$query->setFirstResult( ($pageNum-1) * 20)->setMaxResults(20);
|
||||
|
||||
|
||||
$totalResults = Paginate::count($query);
|
||||
$results = $query->getResult();
|
||||
|
||||
@@ -101,12 +101,12 @@ The ``Paginate::count(Query $query)`` looks like:
|
||||
{
|
||||
static public function count(Query $query)
|
||||
{
|
||||
/* @var $countQuery Query */
|
||||
/** @var Query $countQuery */
|
||||
$countQuery = clone $query;
|
||||
|
||||
|
||||
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('DoctrineExtensions\Paginate\CountSqlWalker'));
|
||||
$countQuery->setFirstResult(null)->setMaxResults(null);
|
||||
|
||||
|
||||
return $countQuery->getSingleScalarResult();
|
||||
}
|
||||
}
|
||||
@@ -137,13 +137,13 @@ implementation is:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$pathExpression = new PathExpression(
|
||||
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,
|
||||
$parent['metadata']->getSingleIdentifierFieldName()
|
||||
);
|
||||
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
|
||||
|
||||
|
||||
$AST->selectClause->selectExpressions = array(
|
||||
new SelectExpression(
|
||||
new AggregateExpression('count', $pathExpression, true), null
|
||||
@@ -196,7 +196,7 @@ modify the generation of the SELECT clause, adding the
|
||||
public function walkSelectClause($selectClause)
|
||||
{
|
||||
$sql = parent::walkSelectClause($selectClause);
|
||||
|
||||
|
||||
if ($this->getQuery()->getHint('mysqlWalker.sqlNoCache') === true) {
|
||||
if ($selectClause->isDistinct) {
|
||||
$sql = str_replace('SELECT DISTINCT', 'SELECT DISTINCT SQL_NO_CACHE', $sql);
|
||||
@@ -204,7 +204,7 @@ modify the generation of the SELECT clause, adding the
|
||||
$sql = str_replace('SELECT', 'SELECT SQL_NO_CACHE', $sql);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ configuration:
|
||||
$config->addCustomStringFunction($name, $class);
|
||||
$config->addCustomNumericFunction($name, $class);
|
||||
$config->addCustomDatetimeFunction($name, $class);
|
||||
|
||||
|
||||
$em = EntityManager::create($dbParams, $config);
|
||||
|
||||
The ``$name`` is the name the function will be referred to in the
|
||||
@@ -96,7 +96,7 @@ discuss it step by step:
|
||||
// (1)
|
||||
public $firstDateExpression = null;
|
||||
public $secondDateExpression = null;
|
||||
|
||||
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER); // (2)
|
||||
@@ -106,7 +106,7 @@ discuss it step by step:
|
||||
$this->secondDateExpression = $parser->ArithmeticPrimary(); // (6)
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS); // (3)
|
||||
}
|
||||
|
||||
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
return 'DATEDIFF(' .
|
||||
@@ -180,28 +180,28 @@ I'll skip the blah and show the code for this function:
|
||||
public $firstDateExpression = null;
|
||||
public $intervalExpression = null;
|
||||
public $unit = null;
|
||||
|
||||
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
|
||||
$this->firstDateExpression = $parser->ArithmeticPrimary();
|
||||
|
||||
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
|
||||
|
||||
$this->intervalExpression = $parser->ArithmeticPrimary();
|
||||
|
||||
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
|
||||
/* @var $lexer Lexer */
|
||||
|
||||
/** @var Lexer $lexer */
|
||||
$lexer = $parser->getLexer();
|
||||
$this->unit = $lexer->token['value'];
|
||||
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
|
||||
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
return 'DATE_ADD(' .
|
||||
|
||||
@@ -195,10 +195,7 @@ SQL Logger (***Optional***)
|
||||
|
||||
Gets or sets the logger to use for logging all SQL statements
|
||||
executed by Doctrine. The logger class must implement the
|
||||
``Doctrine\DBAL\Logging\SQLLogger`` interface. A simple default
|
||||
implementation that logs to the standard output using ``echo`` and
|
||||
``var_dump`` can be found at
|
||||
``Doctrine\DBAL\Logging\EchoSQLLogger``.
|
||||
deprecated ``Doctrine\DBAL\Logging\SQLLogger`` interface.
|
||||
|
||||
Auto-generating Proxy Classes (***OPTIONAL***)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -440,6 +437,19 @@ That will be available for all entities without a custom repository class.
|
||||
The default value is ``Doctrine\ORM\EntityRepository``.
|
||||
Any repository class must be a subclass of EntityRepository otherwise you got an ORMException
|
||||
|
||||
Ignoring entities (***OPTIONAL***)
|
||||
-----------------------------------
|
||||
|
||||
Specifies the Entity FQCNs to ignore.
|
||||
SchemaTool will then skip these (e.g. when comparing schemas).
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$config->setSchemaIgnoreClasses([$fqcn]);
|
||||
$config->getSchemaIgnoreClasses();
|
||||
|
||||
|
||||
Setting up the Console
|
||||
----------------------
|
||||
|
||||
|
||||
@@ -123,6 +123,18 @@ Optional attributes:
|
||||
|
||||
- **nullable**: Determines if NULL values allowed for this column. If not specified, default value is false.
|
||||
|
||||
- **insertable**: Boolean value to determine if the column should be
|
||||
included when inserting a new row into the underlying entities table.
|
||||
If not specified, default value is true.
|
||||
|
||||
- **updatable**: Boolean value to determine if the column should be
|
||||
included when updating the row of the underlying entities table.
|
||||
If not specified, default value is true.
|
||||
|
||||
- **generated**: An enum with the possible values ALWAYS, INSERT, NEVER. Is
|
||||
used after an INSERT or UPDATE statement to determine if the database
|
||||
generated this value and it needs to be fetched using a SELECT statement.
|
||||
|
||||
- **options**: Array of additional options:
|
||||
|
||||
- ``default``: The default value to set for the column if no value
|
||||
@@ -193,6 +205,13 @@ Examples:
|
||||
*/
|
||||
protected $loginCount;
|
||||
|
||||
/**
|
||||
* Generated column
|
||||
* @Column(type="string", name="user_fullname", insertable=false, updatable=false)
|
||||
* MySQL example: full_name char(41) GENERATED ALWAYS AS (concat(firstname,' ',lastname)),
|
||||
*/
|
||||
protected $fullname;
|
||||
|
||||
.. _annref_column_result:
|
||||
|
||||
@ColumnResult
|
||||
|
||||
@@ -82,9 +82,13 @@ be any regular PHP class observing the following restrictions:
|
||||
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
|
||||
- An entity class must not implement ``__wakeup`` or
|
||||
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
|
||||
Also consider implementing
|
||||
`Serializable <https://php.net/manual/en/class.serializable.php>`_
|
||||
instead.
|
||||
You can also consider implementing
|
||||
`Serializable <https://php.net/manual/en/class.serializable.php>`_,
|
||||
but be aware that it is deprecated since PHP 8.1. We do not recommend its usage.
|
||||
- PHP 7.4 introduces :doc:`the new magic method <https://php.net/manual/en/language.oop5.magic.php#object.unserialize>`
|
||||
``__unserialize``, which changes the execution priority between
|
||||
``__wakeup`` and itself when used. This can cause unexpected behaviour in
|
||||
an Entity.
|
||||
- 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
|
||||
@@ -93,6 +97,7 @@ be any regular PHP class observing the following restrictions:
|
||||
- An entity cannot make use of func_get_args() to implement variable parameters.
|
||||
Generated proxies do not support this for performance reasons and your code might
|
||||
actually fail to work when violating this restriction.
|
||||
- Entity cannot access private/protected properties/methods of another entity of the same class or :doc:`do so safely <../cookbook/accessing-private-properties-of-the-same-class-from-different-instance>`.
|
||||
|
||||
Entities support inheritance, polymorphic associations, and
|
||||
polymorphic queries. Both abstract and concrete classes can be
|
||||
@@ -161,7 +166,8 @@ possible for ``__sleep`` to return names of private properties in
|
||||
parent classes. On the other hand it is not a solution for proxy
|
||||
objects to implement ``Serializable`` because Serializable does not
|
||||
work well with any potential cyclic object references (at least we
|
||||
did not find a way yet, if you did, please contact us).
|
||||
did not find a way yet, if you did, please contact us). The
|
||||
``Serializable`` interface is also deprecated beginning with PHP 8.1.
|
||||
|
||||
The EntityManager
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -10,6 +10,8 @@ annotation metadata supported since the first version 2.0.
|
||||
Index
|
||||
-----
|
||||
|
||||
- :ref:`#[AssociationOverride] <attrref_associationoverride]`
|
||||
- :ref:`#[AttributeOverride] <attrref_attributeoverride]`
|
||||
- :ref:`#[Column] <attrref_column>`
|
||||
- :ref:`#[Cache] <attrref_cache>`
|
||||
- :ref:`#[ChangeTrackingPolicy <attrref_changetrackingpolicy>`
|
||||
@@ -49,6 +51,93 @@ Index
|
||||
Reference
|
||||
---------
|
||||
|
||||
.. _attrref_associationoverride:
|
||||
|
||||
#[AssociationOverride]
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In an inheritance hierarchy this attribute allows to override the
|
||||
assocation mapping definitions of the parent mappings. It needs to be nested
|
||||
within a ``#[AssociationOverrides]`` on the class level.
|
||||
|
||||
Required parameters:
|
||||
|
||||
- **name**: Name of the association mapping to overwrite.
|
||||
|
||||
Optional parameters:
|
||||
|
||||
- **joinColumns**: A list of nested ``#[JoinColumn]`` declarations.
|
||||
- **joinTable**: A nested ``#[JoinTable]`` declaration in case of a many-to-many overwrite.
|
||||
- **inversedBy**: The name of the inversedBy field on the target entity side.
|
||||
- **fetch**: The fetch strategy, one of: EAGER, LAZY, EXTRA_LAZY.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\AssociationOverride;
|
||||
use Doctrine\ORM\Mapping\AssociationOverrides;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
|
||||
#[AssociationOverrides([
|
||||
new AssociationOverride(
|
||||
name: "groups",
|
||||
joinTable: new JoinTable(
|
||||
name: "ddc964_users_admingroups",
|
||||
),
|
||||
joinColumns: [new JoinColumn(name: "adminuser_id")],
|
||||
inverseJoinColumns: [new JoinColumn(name: "admingroup_id")]
|
||||
),
|
||||
new AssociationOverride(
|
||||
name: "address",
|
||||
joinColumns: [new JoinColumn(name: "adminaddress_id", referencedColumnName: "id")]
|
||||
)
|
||||
])]
|
||||
class DDC964Admin extends DDC964User
|
||||
{
|
||||
}
|
||||
|
||||
.. _attrref_attributeoverride:
|
||||
|
||||
#[AttributeOverride]
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In an inheritance hierarchy this attribute allows to override the
|
||||
field mapping definitions of the parent mappings. It needs to be nested
|
||||
within a ``#[AttributeOverrides]`` on the class level.
|
||||
|
||||
Required parameters:
|
||||
|
||||
- **name**: Name of the association mapping to overwrite.
|
||||
- **column**: A nested ``#[Column]`` attribute with the overwritten field settings.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\AttributeOverride;
|
||||
use Doctrine\ORM\Mapping\AttributeOverrides;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
|
||||
#[Entity]
|
||||
#[AttributeOverrides([
|
||||
new AttributeOverride(
|
||||
name: "id",
|
||||
column: new Column(name: "guest_id", type: "integer", length: 140)
|
||||
),
|
||||
new AttributeOverride(
|
||||
name: "name",
|
||||
column: new Column(name: "guest_name", nullable: false, unique: true, length: 240)
|
||||
)]
|
||||
)]
|
||||
class DDC964Guest extends DDC964User
|
||||
{
|
||||
}
|
||||
|
||||
.. _attrref_column:
|
||||
|
||||
#[Column]
|
||||
@@ -59,12 +148,12 @@ inside the instance variables PHP DocBlock comment. Any value hold
|
||||
inside this variable will be saved to and loaded from the database
|
||||
as part of the lifecycle of the instance variables entity-class.
|
||||
|
||||
Required attributes:
|
||||
Required parameters:
|
||||
|
||||
- **type**: Name of the DBAL Type which does the conversion between PHP
|
||||
and Database representation.
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
- **name**: By default the property name is used for the database
|
||||
column name also, however the ``name`` attribute allows you to
|
||||
@@ -89,6 +178,18 @@ Optional attributes:
|
||||
- **nullable**: Determines if NULL values allowed for this column.
|
||||
If not specified, default value is ``false``.
|
||||
|
||||
- **insertable**: Boolean value to determine if the column should be
|
||||
included when inserting a new row into the underlying entities table.
|
||||
If not specified, default value is true.
|
||||
|
||||
- **updatable**: Boolean value to determine if the column should be
|
||||
included when updating the row of the underlying entities table.
|
||||
If not specified, default value is true.
|
||||
|
||||
- **generated**: An enum with the possible values ALWAYS, INSERT, NEVER. Is
|
||||
used after an INSERT or UPDATE statement to determine if the database
|
||||
generated this value and it needs to be fetched using a SELECT statement.
|
||||
|
||||
- **options**: Array of additional options:
|
||||
|
||||
- ``default``: The default value to set for the column if no value
|
||||
@@ -159,13 +260,22 @@ Examples:
|
||||
)]
|
||||
protected $loginCount;
|
||||
|
||||
// MySQL example: full_name char(41) GENERATED ALWAYS AS (concat(firstname,' ',lastname)),
|
||||
#[Column(
|
||||
type: "string",
|
||||
name: "user_fullname",
|
||||
insertable: false,
|
||||
updatable: false
|
||||
)]
|
||||
protected $fullname;
|
||||
|
||||
.. _attrref_cache:
|
||||
|
||||
#[Cache]
|
||||
~~~~~~~~
|
||||
Add caching strategy to a root entity or a collection.
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
- **usage**: One of ``READ_ONLY``, ``READ_WRITE`` or ``NONSTRICT_READ_WRITE``, By default this is ``READ_ONLY``.
|
||||
- **region**: An specific region name
|
||||
@@ -210,7 +320,7 @@ Example:
|
||||
|
||||
This attribute allows you to specify a user-provided class to generate identifiers. This attribute only works when both :ref:`#[Id] <attrref_id>` and :ref:`#[GeneratedValue(strategy: "CUSTOM")] <attrref_generatedvalue>` are specified.
|
||||
|
||||
Required attributes:
|
||||
Required parameters:
|
||||
|
||||
- **class**: name of the class which should extend Doctrine\ORM\Id\AbstractIdGenerator
|
||||
|
||||
@@ -244,13 +354,13 @@ actually instantiated as.
|
||||
If this attribute is not specified, the discriminator column defaults
|
||||
to a string column of length 255 called ``dtype``.
|
||||
|
||||
Required attributes:
|
||||
Required parameters:
|
||||
|
||||
|
||||
- **name**: The column name of the discriminator. This name is also
|
||||
used during Array hydration as key to specify the class-name.
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
|
||||
- **type**: By default this is string.
|
||||
@@ -319,7 +429,7 @@ attribute to establish the relationship between the two classes.
|
||||
The embedded attribute is required on an entity's member variable,
|
||||
in order to specify that it is an embedded class.
|
||||
|
||||
Required attributes:
|
||||
Required parameters:
|
||||
|
||||
- **class**: The embeddable class
|
||||
|
||||
@@ -331,7 +441,7 @@ Required attributes:
|
||||
Required attribute to mark a PHP class as an entity. Doctrine manages
|
||||
the persistence of all classes marked as entities.
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
- **repositoryClass**: Specifies the FQCN of a subclass of the
|
||||
``EntityRepository``. Use of repositories for entities is encouraged to keep
|
||||
@@ -368,7 +478,7 @@ conjunction with #[Id].
|
||||
If this attribute is not specified with ``#[Id]`` the ``NONE`` strategy is
|
||||
used as default.
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
- **strategy**: Set the name of the identifier generation strategy.
|
||||
Valid values are ``AUTO``, ``SEQUENCE``, ``IDENTITY``, ``UUID``
|
||||
@@ -424,14 +534,14 @@ Attribute is used on the entity-class level. It provides a hint to the SchemaToo
|
||||
generate a database index on the specified table columns. It only
|
||||
has meaning in the ``SchemaTool`` schema generation context.
|
||||
|
||||
Required attributes:
|
||||
Required parameters:
|
||||
|
||||
- **name**: Name of the Index
|
||||
- **fields**: Array of fields. Exactly one of **fields, columns** is required.
|
||||
- **columns**: Array of columns. Exactly one of **fields, columns** is required.
|
||||
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
- **options**: Array of platform specific options:
|
||||
|
||||
@@ -460,7 +570,7 @@ Example with partial indexes:
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Index;
|
||||
|
||||
#[Index(name: "search_idx", columns: {"category"},
|
||||
#[Index(name: "search_idx", columns: ["category"],
|
||||
options: [
|
||||
"where": "((category IS NOT NULL))"
|
||||
]
|
||||
@@ -518,7 +628,7 @@ Examples:
|
||||
#[Entity]
|
||||
#[InheritanceType("SINGLE_TABLE")]
|
||||
#[DiscriminatorColumn(name: "discr", type: "string")]
|
||||
#[DiscriminatorMap({"person" = "Person", "employee" = "Employee"})]
|
||||
#[DiscriminatorMap(["person" => "Person", "employee" => "Employee"])]
|
||||
class Person
|
||||
{
|
||||
// ...
|
||||
@@ -527,7 +637,7 @@ Examples:
|
||||
#[Entity]
|
||||
#[InheritanceType("JOINED")]
|
||||
#[DiscriminatorColumn(name: "discr", type: "string")]
|
||||
#[DiscriminatorMap({"person" = "Person", "employee" = "Employee"})]
|
||||
#[DiscriminatorMap(["person" => "Person", "employee" => "Employee"])]
|
||||
class Person
|
||||
{
|
||||
// ...
|
||||
@@ -546,9 +656,10 @@ are missing they will be computed considering the field's name and the current
|
||||
|
||||
The ``#[InverseJoinColumn]`` is the same as ``#[JoinColumn]`` and is used in the context
|
||||
of a ``#[ManyToMany]`` attribute declaration to specifiy the details of the join table's
|
||||
column information used for the join to the inverse entity.
|
||||
column information used for the join to the inverse entity. This is only required
|
||||
on PHP 8.0, where nested attributes are not yet supported.
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
- **name**: Column name that holds the foreign key identifier for
|
||||
this relation. In the context of ``#[JoinTable]`` it specifies the column
|
||||
@@ -596,7 +707,7 @@ details of the database join table. If you do not specify
|
||||
using the affected table and the column names.
|
||||
|
||||
A notable difference to the annotation metadata support, ``#[JoinColumn]``
|
||||
and ``#[InverseJoinColumn]`` are specified at the property level and are not
|
||||
and ``#[InverseJoinColumn]`` can be specified at the property level and are not
|
||||
nested within the ``#[JoinTable]`` attribute.
|
||||
|
||||
Required attribute:
|
||||
@@ -623,14 +734,14 @@ Example:
|
||||
Defines that the annotated instance variable holds a reference that
|
||||
describes a many-to-one relationship between two entities.
|
||||
|
||||
Required attributes:
|
||||
Required parameters:
|
||||
|
||||
|
||||
- **targetEntity**: FQCN of the referenced target entity. Can be the
|
||||
unqualified class name if both classes are in the same namespace.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
|
||||
- **cascade**: Cascade Option
|
||||
@@ -659,14 +770,14 @@ additional, optional attribute that has reasonable default
|
||||
configuration values using the table and names of the two related
|
||||
entities.
|
||||
|
||||
Required attributes:
|
||||
Required parameters:
|
||||
|
||||
|
||||
- **targetEntity**: FQCN of the referenced target entity. Can be the
|
||||
unqualified class name if both classes are in the same namespace.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
|
||||
- **mappedBy**: This option specifies the property name on the
|
||||
@@ -720,7 +831,7 @@ The ``#[MappedSuperclass]`` attribute cannot be used in conjunction with
|
||||
``#[Entity]``. See the Inheritance Mapping section for
|
||||
:doc:`more details on the restrictions of mapped superclasses <inheritance-mapping>`.
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
- **repositoryClass**: Specifies the FQCN of a subclass of the EntityRepository.
|
||||
That will be inherited for all subclasses of that Mapped Superclass.
|
||||
@@ -756,13 +867,13 @@ be specified. When no
|
||||
:ref:`#[JoinColumn] <attrref_joincolumn>` is specified it defaults to using the target entity table and
|
||||
primary key column names and the current naming strategy to determine a name for the join column.
|
||||
|
||||
Required attributes:
|
||||
Required parameters:
|
||||
|
||||
- **targetEntity**: FQCN of the referenced target entity. Can be the
|
||||
unqualified class name if both classes are in the same namespace.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
- **cascade**: Cascade Option
|
||||
- **fetch**: One of LAZY or EAGER
|
||||
@@ -786,13 +897,13 @@ Example:
|
||||
#[OneToMany]
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Required attributes:
|
||||
Required parameters:
|
||||
|
||||
- **targetEntity**: FQCN of the referenced target entity. Can be the
|
||||
unqualified class name if both classes are in the same namespace.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
- **cascade**: Cascade Option
|
||||
- **orphanRemoval**: Boolean that specifies if orphans, inverse
|
||||
@@ -916,11 +1027,11 @@ For use with ``#[GeneratedValue(strategy: "SEQUENCE")]`` this
|
||||
attribute allows to specify details about the sequence, such as
|
||||
the increment size and initial values of the sequence.
|
||||
|
||||
Required attributes:
|
||||
Required parameters:
|
||||
|
||||
- **sequenceName**: Name of the sequence
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
- **allocationSize**: Increment the sequence by the allocation size
|
||||
when its fetched. A value larger than 1 allows optimization for
|
||||
@@ -954,11 +1065,11 @@ placed on the entity-class level and is optional. If it is
|
||||
not specified the table name will default to the entity's
|
||||
unqualified classname.
|
||||
|
||||
Required attributes:
|
||||
Required parameters:
|
||||
|
||||
- **name**: Name of the table
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
- **schema**: Name of the schema the table lies in.
|
||||
|
||||
@@ -985,12 +1096,12 @@ generate a database unique constraint on the specified table
|
||||
columns. It only has meaning in the SchemaTool schema generation
|
||||
context.
|
||||
|
||||
Required attributes:
|
||||
Required parameters:
|
||||
|
||||
- **name**: Name of the Index
|
||||
- **columns**: Array of columns.
|
||||
|
||||
Optional attributes:
|
||||
Optional parameters:
|
||||
|
||||
- **options**: Array of platform specific options:
|
||||
|
||||
|
||||
@@ -14,17 +14,11 @@ After working through this guide you should know:
|
||||
Mapping of associations will be covered in the next chapter on
|
||||
:doc:`Association Mapping <association-mapping>`.
|
||||
|
||||
Guide Assumptions
|
||||
-----------------
|
||||
|
||||
You should have already :doc:`installed and configure <configuration>`
|
||||
Doctrine.
|
||||
|
||||
Creating Classes for the Database
|
||||
---------------------------------
|
||||
|
||||
Every PHP object that you want to save in the database using Doctrine
|
||||
is called an "Entity". The term "Entity" describes objects
|
||||
is called an *Entity*. The term "Entity" describes objects
|
||||
that have an identity over many independent requests. This identity is
|
||||
usually achieved by assigning a unique identifier to an entity.
|
||||
In this tutorial the following ``Message`` PHP class will serve as the
|
||||
@@ -50,11 +44,11 @@ that describes your entity.
|
||||
Doctrine provides several different ways to specify object-relational
|
||||
mapping metadata:
|
||||
|
||||
- :doc:`Docblock Annotations <annotations-reference>`
|
||||
- :doc:`Attributes <attributes-reference>`
|
||||
- :doc:`Docblock Annotations <annotations-reference>`
|
||||
- :doc:`XML <xml-mapping>`
|
||||
- :doc:`YAML <yaml-mapping>`
|
||||
- :doc:`PHP code <php-mapping>`
|
||||
- :doc:`YAML <yaml-mapping>` (deprecated and will be removed in ``doctrine/orm`` 3.0.)
|
||||
|
||||
This manual will usually show mapping metadata via docblock annotations, though
|
||||
many examples also show the equivalent configuration in YAML and XML.
|
||||
@@ -62,8 +56,8 @@ many examples also show the equivalent configuration in YAML and XML.
|
||||
.. note::
|
||||
|
||||
All metadata drivers perform equally. Once the metadata of a class has been
|
||||
read from the source (annotations, xml or yaml) it is stored in an instance
|
||||
of the ``Doctrine\ORM\Mapping\ClassMetadata`` class and these instances are
|
||||
read from the source (attributes, annotations, XML, etc.) it is stored in an instance
|
||||
of the ``Doctrine\ORM\Mapping\ClassMetadata`` class which are
|
||||
stored in the metadata cache. If you're not using a metadata cache (not
|
||||
recommended!) then the XML driver is the fastest.
|
||||
|
||||
@@ -71,9 +65,22 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
.. code-block:: attribute
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
|
||||
#[Entity]
|
||||
class Message
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
|
||||
/** @Entity */
|
||||
class Message
|
||||
{
|
||||
@@ -100,9 +107,25 @@ You can change this by configuring information about the table:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
.. code-block:: attribute
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\Table;
|
||||
|
||||
#[Entity]
|
||||
#[Table(name: 'message')]
|
||||
class Message
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\Table;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="message")
|
||||
@@ -132,19 +155,38 @@ Now the class ``Message`` will be saved and fetched from the table ``message``.
|
||||
Property Mapping
|
||||
----------------
|
||||
|
||||
The next step after marking a PHP class as an entity is mapping its properties
|
||||
to columns in a table.
|
||||
The next step is mapping its properties to columns in the table.
|
||||
|
||||
To configure a property use the ``@Column`` docblock annotation. The ``type``
|
||||
To configure a property use the ``Column`` docblock annotation. The ``type``
|
||||
attribute specifies the :ref:`Doctrine Mapping Type <reference-mapping-types>`
|
||||
to use for the field. If the type is not specified, ``string`` is used as the
|
||||
default.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
.. code-block:: attribute
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
|
||||
#[Entity]
|
||||
class Message
|
||||
{
|
||||
#[Column(type: Types::INTEGER)]
|
||||
private $id;
|
||||
#[Column(length: 140)]
|
||||
private $text;
|
||||
#[Column(name: 'posted_at', type: Types::DATETIME)]
|
||||
private $postedAt;
|
||||
}
|
||||
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
|
||||
/** @Entity */
|
||||
class Message
|
||||
{
|
||||
@@ -180,36 +222,34 @@ default.
|
||||
column: posted_at
|
||||
|
||||
When we don't explicitly specify a column name via the ``name`` option, Doctrine
|
||||
assumes the field name is also the column name. This means that:
|
||||
assumes the field name is also the column name. So in this example:
|
||||
|
||||
* the ``id`` property will map to the column ``id`` using the type ``integer``;
|
||||
* the ``text`` property will map to the column ``text`` with the default mapping type ``string``;
|
||||
* the ``postedAt`` property will map to the ``posted_at`` column with the ``datetime`` type.
|
||||
|
||||
The Column annotation has some more attributes. Here is a complete
|
||||
list:
|
||||
Here is a complete list of ``Column``s attributes (all optional):
|
||||
|
||||
- ``type``: (optional, defaults to 'string') The mapping type to
|
||||
use for the column.
|
||||
- ``name``: (optional, defaults to field name) The name of the
|
||||
column in the database.
|
||||
- ``length``: (optional, default 255) The length of the column in
|
||||
the database. (Applies only if a string-valued column is used).
|
||||
- ``unique``: (optional, default FALSE) Whether the column is a
|
||||
unique key.
|
||||
- ``nullable``: (optional, default FALSE) Whether the database
|
||||
column is nullable.
|
||||
- ``precision``: (optional, default 0) The precision for a decimal
|
||||
(exact numeric) column (applies only for decimal column),
|
||||
- ``type`` (default: 'string'): The mapping type to use for the column.
|
||||
- ``name`` (default: name of property): The name of the column in the database.
|
||||
- ``length`` (default: 255): The length of the column in the database.
|
||||
Applies only if a string-valued column is used.
|
||||
- ``unique`` (default: ``false``): Whether the column is a unique key.
|
||||
- ``nullable`` (default: ``false``): Whether the column is nullable.
|
||||
- ``insertable`` (default: ``true``): Whether the column should be inserted.
|
||||
- ``updatable`` (default: ``true``): Whether the column should be updated.
|
||||
- ``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.
|
||||
- ``scale``: (optional, default 0) The scale for a decimal (exact
|
||||
- ``scale`` (default: 0): The scale for a decimal (exact
|
||||
numeric) column (applies only for decimal column), which represents
|
||||
the number of digits to the right of the decimal point and must
|
||||
not be greater than *precision*.
|
||||
- ``columnDefinition``: (optional) Allows to define a custom
|
||||
not be greater than ``precision``.
|
||||
- ``columnDefinition``: Allows to define a custom
|
||||
DDL snippet that is used to create the column. Warning: This normally
|
||||
confuses the SchemaTool to always detect the column as changed.
|
||||
- ``options``: (optional) Key-value pairs of options that get passed
|
||||
confuses the :doc:`SchemaTool <tools>` to always detect the column as changed.
|
||||
- ``options``: Key-value pairs of options that get passed
|
||||
to the underlying database platform when generating DDL statements.
|
||||
|
||||
.. _reference-php-mapping-types:
|
||||
@@ -217,21 +257,35 @@ list:
|
||||
PHP Types Mapping
|
||||
_________________
|
||||
|
||||
Since version 2.9 Doctrine can determine usable defaults from property types
|
||||
on entity classes. When property type is nullable this has no effect on
|
||||
``nullable`` Column attribute at the moment for backwards compatibility
|
||||
reasons.
|
||||
.. versionadded:: 2.9
|
||||
|
||||
Additionally, Doctrine will map PHP types to ``type`` attribute as follows:
|
||||
The column types can be inferred automatically from PHP's property types.
|
||||
However, when the property type is nullable this has no effect on the ``nullable`` Column attribute.
|
||||
|
||||
- ``DateInterval``: ``dateinterval``
|
||||
- ``DateTime``: ``datetime``
|
||||
- ``DateTimeImmutable``: ``datetime_immutable``
|
||||
- ``array``: ``json``
|
||||
- ``bool``: ``boolean``
|
||||
- ``float``: ``float``
|
||||
- ``int``: ``integer``
|
||||
- ``string`` or any other type: ``string``
|
||||
These are the "automatic" mapping rules:
|
||||
|
||||
+-----------------------+-------------------------------+
|
||||
| PHP property type | Doctrine column type |
|
||||
+=======================+===============================+
|
||||
| ``DateInterval`` | ``Types::DATEINTERVAL`` |
|
||||
+-----------------------+-------------------------------+
|
||||
| ``DateTime`` | ``Types::DATETIME_MUTABLE`` |
|
||||
+-----------------------+-------------------------------+
|
||||
| ``DateTimeImmutable`` | ``Types::DATETIME_IMMUTABLE`` |
|
||||
+-----------------------+-------------------------------+
|
||||
| ``array`` | ``Types::JSON`` |
|
||||
+-----------------------+-------------------------------+
|
||||
| ``bool`` | ``Types::BOOLEAN`` |
|
||||
+-----------------------+-------------------------------+
|
||||
| ``float`` | ``Types::FLOAT`` |
|
||||
+-----------------------+-------------------------------+
|
||||
| ``int`` | ``Types::INTEGER`` |
|
||||
+-----------------------+-------------------------------+
|
||||
| Any other type | ``Types::STRING`` |
|
||||
+-----------------------+-------------------------------+
|
||||
|
||||
As of version 2.11 Doctrine can also automatically map typed properties using a
|
||||
PHP 8.1 enum to set the right ``type`` and ``enumType``.
|
||||
|
||||
.. _reference-mapping-types:
|
||||
|
||||
|
||||
@@ -670,11 +670,23 @@ The same restrictions apply for the reference of related entities.
|
||||
|
||||
.. warning::
|
||||
|
||||
DQL DELETE statements are ported directly into a
|
||||
Database DELETE statement and therefore bypass any events and checks for the
|
||||
version column if they are not explicitly added to the WHERE clause
|
||||
of the query. Additionally Deletes of specified entities are *NOT*
|
||||
cascaded to related entities even if specified in the metadata.
|
||||
DQL DELETE statements are ported directly into an SQL DELETE statement.
|
||||
Therefore, some limitations apply:
|
||||
|
||||
- Lifecycle events for the affected entities are not executed.
|
||||
- A cascading ``remove`` operation (as indicated e. g. by ``cascade={"remove"}``
|
||||
or ``cascade={"all"}`` in the mapping configuration) is not being performed
|
||||
for associated entities. You can rely on database level cascade operations by
|
||||
configuring each join column with the ``onDelete`` option.
|
||||
- Checks for the version column are bypassed if they are not explicitly added
|
||||
to the WHERE clause of the query.
|
||||
|
||||
When you rely on one of these features, one option is to use the
|
||||
``EntityManager#remove($entity)`` method. This, however, is costly performance-wise:
|
||||
It means collections and related entities are fetched into memory
|
||||
(even if they are marked as lazy). Pulling object graphs into memory on cascade
|
||||
can cause considerable performance overhead, especially when the cascaded collections
|
||||
are large. Make sure to weigh the benefits and downsides.
|
||||
|
||||
Comments in queries
|
||||
-------------------
|
||||
@@ -718,7 +730,7 @@ clauses:
|
||||
- ``SQRT(q)`` - Return the square-root of q.
|
||||
- ``SUBSTRING(str, start [, length])`` - Return substring of given
|
||||
string.
|
||||
- ``TRIM([LEADING \| TRAILING \| BOTH] ['trchar' FROM] str)`` - Trim
|
||||
- ``TRIM([LEADING | TRAILING | BOTH] ['trchar' FROM] str)`` - Trim
|
||||
the string by the given trim char, defaults to whitespaces.
|
||||
- ``UPPER(str)`` - Return the upper-case of the given string.
|
||||
- ``DATE_ADD(date, value, unit)`` - Add the given time to a given date.
|
||||
@@ -1712,7 +1724,7 @@ Literal Values
|
||||
.. code-block:: php
|
||||
|
||||
Literal ::= string | char | integer | float | boolean
|
||||
InParameter ::= Literal | InputParameter
|
||||
InParameter ::= ArithmeticExpression | InputParameter
|
||||
|
||||
Input Parameter
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -121,52 +121,55 @@ Now you can test the ``$eventSubscriber`` instance to see if the
|
||||
echo 'pre foo invoked!';
|
||||
}
|
||||
|
||||
Registering Events
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Registering Event Handlers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are two ways to register an event:
|
||||
There are two ways to set up an event handler:
|
||||
|
||||
* *All events* can be registered by calling ``$eventManager->addEventListener()``
|
||||
or ``eventManager->addEventSubscriber()``, see
|
||||
* For *all events* you can create a Lifecycle Event Listener or Subscriber class and register
|
||||
it by calling ``$eventManager->addEventListener()`` or ``eventManager->addEventSubscriber()``,
|
||||
see
|
||||
:ref:`Listening and subscribing to Lifecycle Events<listening-and-subscribing-to-lifecycle-events>`
|
||||
* *Lifecycle Callbacks* can also be registered in the entity mapping (annotation, attribute, etc.),
|
||||
see :ref:`Lifecycle Callbacks<lifecycle-callbacks>`
|
||||
* For *some events* (see table below), you can create a *Lifecycle Callback* method in the
|
||||
entity, see :ref:`Lifecycle Callbacks<lifecycle-callbacks>`.
|
||||
|
||||
.. _reference-events-lifecycle-events:
|
||||
|
||||
Events Overview
|
||||
---------------
|
||||
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
| Event | Dispatched by | Lifecycle |
|
||||
| | | Callback |
|
||||
+=================================================================+=======================+===========+
|
||||
| :ref:`preRemove<reference-events-pre-remove>` | ``$em->remove()`` | Yes |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
| :ref:`postRemove<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
| :ref:`prePersist<reference-events-pre-persist>` | ``$em->persist()`` | Yes |
|
||||
| | on *initial* persist | |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
| :ref:`postPersist<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
| :ref:`preUpdate<reference-events-pre-update>` | ``$em->flush()`` | Yes |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
| :ref:`postUpdate<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
| :ref:`postLoad<reference-events-post-load>` | Loading from database | Yes |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
| :ref:`loadClassMetadata<reference-events-load-class-metadata>` | Loading of mapping | No |
|
||||
| | metadata | |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
| ``onClassMetadataNotFound`` | ``MappingException`` | No |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
| :ref:`preFlush<reference-events-pre-flush>` | ``$em->flush()`` | Yes |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
| :ref:`onFlush<reference-events-on-flush>` | ``$em->flush()`` | No |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
| :ref:`postFlush<reference-events-post-flush>` | ``$em->flush()`` | No |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
| ``onClear`` | ``$em->clear()`` | No |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| Event | Dispatched by | Lifecycle | Passed |
|
||||
| | | Callback | Argument |
|
||||
+=================================================================+=======================+===========+=====================================+
|
||||
| :ref:`preRemove<reference-events-pre-remove>` | ``$em->remove()`` | Yes | `_LifecycleEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postRemove<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `_LifecycleEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`prePersist<reference-events-pre-persist>` | ``$em->persist()`` | Yes | `_LifecycleEventArgs`_ |
|
||||
| | on *initial* persist | | |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postPersist<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `_LifecycleEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`preUpdate<reference-events-pre-update>` | ``$em->flush()`` | Yes | `_PreUpdateEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postUpdate<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `_LifecycleEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postLoad<reference-events-post-load>` | Loading from database | Yes | `_LifecycleEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`loadClassMetadata<reference-events-load-class-metadata>` | Loading of mapping | No | `_LoadClassMetadataEventArgs` |
|
||||
| | metadata | | |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| ``onClassMetadataNotFound`` | ``MappingException`` | No | `_OnClassMetadataNotFoundEventArgs` |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`preFlush<reference-events-pre-flush>` | ``$em->flush()`` | Yes | `_PreFlushEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`onFlush<reference-events-on-flush>` | ``$em->flush()`` | No | `_OnFlushEventArgs` |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postFlush<reference-events-post-flush>` | ``$em->flush()`` | No | `_PostFlushEventArgs` |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`onClear<reference-events-on-clear>` | ``$em->clear()`` | No | `_OnClearEventArgs` |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
|
||||
Naming convention
|
||||
~~~~~~~~~~~~~~~~~
|
||||
@@ -187,94 +190,6 @@ several reasons:
|
||||
An example for a correct notation can be found in the example
|
||||
``TestEvent`` above.
|
||||
|
||||
.. _reference-events-lifecycle-events:
|
||||
|
||||
Lifecycle Events
|
||||
----------------
|
||||
|
||||
The ``EntityManager`` and ``UnitOfWork`` classes trigger a bunch of
|
||||
events during the life-time of their registered entities.
|
||||
|
||||
|
||||
|
||||
- ``preRemove`` - The ``preRemove`` event occurs for a given entity
|
||||
before the respective ``EntityManager`` remove operation for that
|
||||
entity is executed. It is not called for a DQL ``DELETE`` statement.
|
||||
- ``postRemove`` - The ``postRemove`` event occurs for an entity after the
|
||||
entity has been deleted. It will be invoked after the database
|
||||
delete operations. It is not called for a DQL ``DELETE`` statement.
|
||||
- ``prePersist`` - The ``prePersist`` event occurs for a given entity
|
||||
before the respective ``EntityManager`` persist operation for that
|
||||
entity is executed. It should be noted that this event is only triggered on
|
||||
*initial* persist of an entity (i.e. it does not trigger on future updates).
|
||||
- ``postPersist`` - The ``postPersist`` event occurs for an entity after
|
||||
the entity has been made persistent. It will be invoked after the
|
||||
database insert operations. Generated primary key values are
|
||||
available in the postPersist event.
|
||||
- ``preUpdate`` - The ``preUpdate`` event occurs before the database
|
||||
update operations to entity data. It is not called for a DQL
|
||||
``UPDATE`` statement nor when the computed changeset is empty.
|
||||
- ``postUpdate`` - The ``postUpdate`` event occurs after the database
|
||||
update operations to entity data. It is not called for a DQL
|
||||
``UPDATE`` statement.
|
||||
- ``postLoad`` - The postLoad event occurs for an entity after the
|
||||
entity has been loaded into the current ``EntityManager`` from the
|
||||
database or after the refresh operation has been applied to it.
|
||||
- ``loadClassMetadata`` - The ``loadClassMetadata`` event occurs after the
|
||||
mapping metadata for a class has been loaded from a mapping source
|
||||
(annotations/xml/yaml). This event is not a lifecycle callback.
|
||||
- ``onClassMetadataNotFound`` - Loading class metadata for a particular
|
||||
requested class name failed. Manipulating the given event args instance
|
||||
allows providing fallback metadata even when no actual metadata exists
|
||||
or could be found. This event is not a lifecycle callback.
|
||||
- ``preFlush`` - The ``preFlush`` event occurs at the very beginning of
|
||||
a flush operation.
|
||||
- ``onFlush`` - The ``onFlush`` event occurs after the change-sets of all
|
||||
managed entities are computed. This event is not a lifecycle
|
||||
callback.
|
||||
- ``postFlush`` - The ``postFlush`` event occurs at the end of a flush operation. This
|
||||
event is not a lifecycle callback.
|
||||
- ``onClear`` - The ``onClear`` event occurs when the
|
||||
``EntityManager#clear()`` operation is invoked, after all references
|
||||
to entities have been removed from the unit of work. This event is not
|
||||
a lifecycle callback.
|
||||
|
||||
|
||||
.. warning::
|
||||
|
||||
Note that, when using ``Doctrine\ORM\AbstractQuery#toIterable()``, ``postLoad``
|
||||
events will be executed immediately after objects are being hydrated, and therefore
|
||||
associations are not guaranteed to be initialized. It is not safe to combine
|
||||
usage of ``Doctrine\ORM\AbstractQuery#toIterable()`` and ``postLoad`` event
|
||||
handlers.
|
||||
|
||||
.. warning::
|
||||
|
||||
Note that the ``postRemove`` event or any events triggered after an entity removal
|
||||
can receive an uninitializable proxy in case you have configured an entity to
|
||||
cascade remove relations. In this case, you should load yourself the proxy in
|
||||
the associated pre event.
|
||||
|
||||
These can be hooked into by two different types of event
|
||||
listeners:
|
||||
|
||||
- Lifecycle Callbacks are methods on the entity classes that are
|
||||
called when the event is triggered. They receive some kind
|
||||
of ``EventArgs`` instance.
|
||||
- Lifecycle Event Listeners and Subscribers are classes with specific callback
|
||||
methods that receives some kind of ``EventArgs`` instance.
|
||||
|
||||
The ``EventArgs`` instance received by the listener gives access to the entity,
|
||||
``EntityManager`` instance and other relevant data.
|
||||
|
||||
.. note::
|
||||
|
||||
All Lifecycle events that happen during the ``flush()`` of
|
||||
an ``EntityManager`` have very specific constraints on the allowed
|
||||
operations that can be executed. Please read the
|
||||
:ref:`reference-events-implementing-listeners` section very carefully
|
||||
to understand which operations are allowed in which lifecycle event.
|
||||
|
||||
.. _lifecycle-callbacks:
|
||||
|
||||
Lifecycle Callbacks
|
||||
@@ -296,20 +211,21 @@ specific to a particular entity class's lifecycle.
|
||||
.. code-block:: attribute
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Persistence\Event\LifecycleEventArgs;
|
||||
|
||||
/**
|
||||
* #[Entity]
|
||||
* #[HasLifecycleCallbacks]
|
||||
*/
|
||||
#[Entity]
|
||||
#[HasLifecycleCallbacks]
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
|
||||
#[Column(type: 'string', length: 255)]
|
||||
#[Column(type: Types::STRING, length: 255)]
|
||||
public $value;
|
||||
|
||||
#[PrePersist]
|
||||
public function doStuffOnPrePersist()
|
||||
public function doStuffOnPrePersist(LifecycleEventArgs $eventArgs)
|
||||
{
|
||||
$this->createdAt = date('Y-m-d H:i:s');
|
||||
}
|
||||
@@ -320,15 +236,17 @@ specific to a particular entity class's lifecycle.
|
||||
$this->value = 'changed from prePersist callback!';
|
||||
}
|
||||
|
||||
#[PostLoad]
|
||||
public function doStuffOnPostLoad()
|
||||
#[PreUpdate]
|
||||
public function doStuffOnPreUpdate(PreUpdateEventArgs $eventArgs)
|
||||
{
|
||||
$this->value = 'changed from postLoad callback!';
|
||||
$this->value = 'changed from preUpdate callback!';
|
||||
}
|
||||
}
|
||||
.. code-block:: annotation
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\Persistence\Event\LifecycleEventArgs;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
@@ -342,7 +260,7 @@ specific to a particular entity class's lifecycle.
|
||||
public $value;
|
||||
|
||||
/** @PrePersist */
|
||||
public function doStuffOnPrePersist()
|
||||
public function doStuffOnPrePersist(LifecycleEventArgs $eventArgs)
|
||||
{
|
||||
$this->createdAt = date('Y-m-d H:i:s');
|
||||
}
|
||||
@@ -353,10 +271,10 @@ specific to a particular entity class's lifecycle.
|
||||
$this->value = 'changed from prePersist callback!';
|
||||
}
|
||||
|
||||
/** @PostLoad */
|
||||
public function doStuffOnPostLoad()
|
||||
/** @PreUpdate */
|
||||
public function doStuffOnPreUpdate(PreUpdateEventArgs $eventArgs)
|
||||
{
|
||||
$this->value = 'changed from postLoad callback!';
|
||||
$this->value = 'changed from preUpdate callback!';
|
||||
}
|
||||
}
|
||||
.. code-block:: xml
|
||||
@@ -372,7 +290,7 @@ specific to a particular entity class's lifecycle.
|
||||
<lifecycle-callbacks>
|
||||
<lifecycle-callback type="prePersist" method="doStuffOnPrePersist"/>
|
||||
<lifecycle-callback type="prePersist" method="doOtherStuffOnPrePersist"/>
|
||||
<lifecycle-callback type="postLoad" method="doStuffOnPostLoad"/>
|
||||
<lifecycle-callback type="preUpdate" method="doStuffOnPreUpdate"/>
|
||||
</lifecycle-callbacks>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@@ -386,7 +304,7 @@ specific to a particular entity class's lifecycle.
|
||||
type: string(255)
|
||||
lifecycleCallbacks:
|
||||
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ]
|
||||
postLoad: [ doStuffOnPostLoad ]
|
||||
preUpdate: [ doStuffOnPreUpdate ]
|
||||
|
||||
Lifecycle Callbacks Event Argument
|
||||
----------------------------------
|
||||
@@ -533,21 +451,23 @@ that (prior to version 2.4) you do not have access to the
|
||||
prePersist
|
||||
~~~~~~~~~~
|
||||
|
||||
There are two ways for the ``prePersist`` event to be triggered.
|
||||
One is obviously when you call ``EntityManager#persist()``. The
|
||||
event is also called for all cascaded associations.
|
||||
There are two ways for the ``prePersist`` event to be triggered:
|
||||
|
||||
There is another way for ``prePersist`` to be called, inside the
|
||||
- One is obviously when you call ``EntityManager::persist()``. The
|
||||
event is also called for all :ref:`cascaded associations<transitive-persistence>`.
|
||||
- The other is inside the
|
||||
``flush()`` method when changes to associations are computed and
|
||||
this association is marked as cascade persist. Any new entity found
|
||||
this association is marked as :ref:`cascade: persist<transitive-persistence>`. Any new entity found
|
||||
during this operation is also persisted and ``prePersist`` called
|
||||
on it. This is called "persistence by reachability".
|
||||
on it. This is called :ref:`persistence by reachability<persistence-by-reachability>`.
|
||||
|
||||
In both cases you get passed a ``LifecycleEventArgs`` instance
|
||||
which has access to the entity and the entity manager.
|
||||
|
||||
The following restrictions apply to ``prePersist``:
|
||||
This event is only triggered on *initial* persist of an entity
|
||||
(i.e. it does not trigger on future updates).
|
||||
|
||||
The following restrictions apply to ``prePersist``:
|
||||
|
||||
- If you are using a PrePersist Identity Generator such as
|
||||
sequences the ID value will *NOT* be available within any
|
||||
@@ -555,15 +475,17 @@ The following restrictions apply to ``prePersist``:
|
||||
- Doctrine will not recognize changes made to relations in a prePersist
|
||||
event. This includes modifications to
|
||||
collections such as additions, removals or replacement.
|
||||
|
||||
|
||||
.. _reference-events-pre-remove:
|
||||
|
||||
preRemove
|
||||
~~~~~~~~~
|
||||
|
||||
The ``preRemove`` event is called on every entity when its passed
|
||||
to the ``EntityManager#remove()`` method. It is cascaded for all
|
||||
associations that are marked as cascade delete.
|
||||
The ``preRemove`` event is called on every entity immediately when it is passed
|
||||
to the ``EntityManager::remove()`` method. It is cascaded for all
|
||||
associations that are marked as :ref:`cascade: remove<transitive-persistence>`
|
||||
|
||||
It is not called for a DQL ``DELETE`` statement.
|
||||
|
||||
There are no restrictions to what methods can be called inside the
|
||||
``preRemove`` event, except when the remove method itself was
|
||||
@@ -574,10 +496,10 @@ called during a flush operation.
|
||||
preFlush
|
||||
~~~~~~~~
|
||||
|
||||
``preFlush`` is called at ``EntityManager#flush()`` before
|
||||
anything else. ``EntityManager#flush()`` should not be called inside
|
||||
its listeners, since `preFlush` event is dispatched in it, which would
|
||||
result in infinite loop.
|
||||
``preFlush`` is called inside ``EntityManager::flush()`` before
|
||||
anything else. ``EntityManager::flush()`` must not be called inside
|
||||
its listeners, since it would fire the ``preFlush`` event again, which would
|
||||
result in an infinite loop.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -598,12 +520,11 @@ result in infinite loop.
|
||||
onFlush
|
||||
~~~~~~~
|
||||
|
||||
OnFlush is a very powerful event. It is called inside
|
||||
``EntityManager#flush()`` after the changes to all the managed
|
||||
``onFlush`` is a very powerful event. It is called inside
|
||||
``EntityManager::flush()`` after the changes to all the managed
|
||||
entities and their associations have been computed. This means, the
|
||||
``onFlush`` event has access to the sets of:
|
||||
|
||||
|
||||
- Entities scheduled for insert
|
||||
- Entities scheduled for update
|
||||
- Entities scheduled for removal
|
||||
@@ -611,7 +532,7 @@ entities and their associations have been computed. This means, the
|
||||
- Collections scheduled for removal
|
||||
|
||||
To make use of the ``onFlush`` event you have to be familiar with the
|
||||
internal ``UnitOfWork`` API, which grants you access to the previously
|
||||
internal :ref:`UnitOfWork<unit-of-work>` API, which grants you access to the previously
|
||||
mentioned sets. See this example:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -646,11 +567,10 @@ mentioned sets. See this example:
|
||||
}
|
||||
}
|
||||
|
||||
The following restrictions apply to the onFlush event:
|
||||
|
||||
The following restrictions apply to the ``onFlush`` event:
|
||||
|
||||
- If you create and persist a new entity in ``onFlush``, then
|
||||
calling ``EntityManager#persist()`` is not enough.
|
||||
calling ``EntityManager::persist()`` is not enough.
|
||||
You have to execute an additional call to
|
||||
``$unitOfWork->computeChangeSet($classMetadata, $entity)``.
|
||||
- Changing primitive fields or associations requires you to
|
||||
@@ -663,8 +583,9 @@ The following restrictions apply to the onFlush event:
|
||||
postFlush
|
||||
~~~~~~~~~
|
||||
|
||||
``postFlush`` is called at the end of ``EntityManager#flush()``.
|
||||
``EntityManager#flush()`` can **NOT** be called safely inside its listeners.
|
||||
``postFlush`` is called at the end of ``EntityManager::flush()``.
|
||||
``EntityManager::flush()`` can **NOT** be called safely inside its listeners.
|
||||
This event is not a lifecycle callback.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -685,22 +606,22 @@ postFlush
|
||||
preUpdate
|
||||
~~~~~~~~~
|
||||
|
||||
PreUpdate is called inside the ``EntityManager#flush()`` method,
|
||||
PreUpdate is called inside the ``EntityManager::flush()`` method,
|
||||
right before an SQL ``UPDATE`` statement. This event is not
|
||||
triggered when the computed changeset is empty.
|
||||
triggered when the computed changeset is empty, nor for a DQL
|
||||
``UPDATE`` statement.
|
||||
|
||||
Changes to associations of the updated entity are never allowed in
|
||||
this event, since Doctrine cannot guarantee to correctly handle
|
||||
referential integrity at this point of the flush operation. This
|
||||
event has a powerful feature however, it is executed with a
|
||||
``PreUpdateEventArgs`` instance, which contains a reference to the
|
||||
`_PreUpdateEventArgs`_ instance, which contains a reference to the
|
||||
computed change-set of this entity.
|
||||
|
||||
This means you have access to all the fields that have changed for
|
||||
this entity with their old and new value. The following methods are
|
||||
available on the ``PreUpdateEventArgs``:
|
||||
|
||||
|
||||
- ``getEntity()`` to get access to the actual entity.
|
||||
- ``getEntityChangeSet()`` to get a copy of the changeset array.
|
||||
Changes to this returned array do not affect updating.
|
||||
@@ -754,15 +675,14 @@ lifecycle callback when there are expensive validations to call:
|
||||
|
||||
Restrictions for this event:
|
||||
|
||||
|
||||
- Changes to associations of the passed entities are not
|
||||
recognized by the flush operation anymore.
|
||||
- Changes to fields of the passed entities are not recognized by
|
||||
the flush operation anymore, use the computed change-set passed to
|
||||
the event to modify primitive field values, e.g. use
|
||||
``$eventArgs->setNewValue($field, $value);`` as in the Alice to Bob example above.
|
||||
- Any calls to ``EntityManager#persist()`` or
|
||||
``EntityManager#remove()``, even in combination with the ``UnitOfWork``
|
||||
- Any calls to ``EntityManager::persist()`` or
|
||||
``EntityManager::remove()``, even in combination with the ``UnitOfWork``
|
||||
API are strongly discouraged and don't work as expected outside the
|
||||
flush operation.
|
||||
|
||||
@@ -771,19 +691,54 @@ Restrictions for this event:
|
||||
postUpdate, postRemove, postPersist
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The three post events are called inside ``EntityManager#flush()``.
|
||||
These three post* events are called inside ``EntityManager::flush()``.
|
||||
Changes in here are not relevant to the persistence in the
|
||||
database, but you can use these events to alter non-persistable items,
|
||||
like non-mapped fields, logging or even associated classes that are
|
||||
not directly mapped by Doctrine.
|
||||
|
||||
- The ``postUpdate`` event occurs after the database
|
||||
update operations to entity data. It is not called for a DQL
|
||||
``UPDATE`` statement.
|
||||
- The ``postPersist`` event occurs for an entity after
|
||||
the entity has been made persistent. It will be invoked after the
|
||||
database insert operations. Generated primary key values are
|
||||
available in the postPersist event.
|
||||
- The ``postRemove`` event occurs for an entity after the
|
||||
entity has been deleted. It will be invoked after the database
|
||||
delete operations. It is not called for a DQL ``DELETE`` statement.
|
||||
|
||||
.. warning::
|
||||
|
||||
The ``postRemove`` event or any events triggered after an entity removal
|
||||
can receive an uninitializable proxy in case you have configured an entity to
|
||||
cascade remove relations. In this case, you should load yourself the proxy in
|
||||
the associated ``pre*`` event.
|
||||
|
||||
.. _reference-events-post-load:
|
||||
|
||||
postLoad
|
||||
~~~~~~~~
|
||||
|
||||
This event is called after an entity is constructed by the
|
||||
EntityManager.
|
||||
The postLoad event occurs after the entity has been loaded into the current
|
||||
``EntityManager`` from the database or after ``refresh()`` has been applied to it.
|
||||
|
||||
.. warning::
|
||||
|
||||
When using ``Doctrine\ORM\AbstractQuery::toIterable()``, ``postLoad``
|
||||
events will be executed immediately after objects are being hydrated, and therefore
|
||||
associations are not guaranteed to be initialized. It is not safe to combine
|
||||
usage of ``Doctrine\ORM\AbstractQuery::toIterable()`` and ``postLoad`` event
|
||||
handlers.
|
||||
|
||||
.. _reference-events-on-clear:
|
||||
|
||||
onClear
|
||||
~~~~~~~~
|
||||
|
||||
The ``onClear`` event occurs when the ``EntityManager::clear()`` operation is invoked,
|
||||
after all references to entities have been removed from the unit of work.
|
||||
This event is not a lifecycle callback.
|
||||
|
||||
Entity listeners
|
||||
----------------
|
||||
@@ -1008,9 +963,11 @@ Implementing your own resolver :
|
||||
Load ClassMetadata Event
|
||||
------------------------
|
||||
|
||||
When the mapping information for an entity is read, it is populated
|
||||
in to a ``Doctrine\ORM\Mapping\ClassMetadata`` instance. You can hook in to this
|
||||
process and manipulate the instance.
|
||||
``loadClassMetadata`` - The ``loadClassMetadata`` event occurs after the
|
||||
mapping metadata for a class has been loaded from a mapping source
|
||||
(annotations/xml/yaml) in to a ``Doctrine\ORM\Mapping\ClassMetadata`` instance.
|
||||
You can hook in to this process and manipulate the instance.
|
||||
This event is not a lifecycle callback.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -1033,6 +990,11 @@ process and manipulate the instance.
|
||||
}
|
||||
}
|
||||
|
||||
If not class metadata can be found, an ``onClassMetadataNotFound`` event is dispatched.
|
||||
Manipulating the given event args instance
|
||||
allows providing fallback metadata even when no actual metadata exists
|
||||
or could be found. This event is not a lifecycle callback.
|
||||
|
||||
SchemaTool Events
|
||||
-----------------
|
||||
|
||||
@@ -1085,3 +1047,12 @@ and the EntityManager.
|
||||
$em = $eventArgs->getEntityManager();
|
||||
}
|
||||
}
|
||||
|
||||
.. _LifecycleEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/LifecycleEventArgs.php
|
||||
.. _PreUpdateEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php
|
||||
.. _PreFlushEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/PreFlushEventArgs.php
|
||||
.. _PostFlushEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/PostFlushEventArgs.php
|
||||
.. _OnFlushEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/OnFlushEventArgs.php
|
||||
.. _OnClearEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/OnClearEventArgs.php
|
||||
.. _LoadClassMetadataEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php
|
||||
.. _OnClassMetadataNotFoundEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php
|
||||
|
||||
@@ -9,6 +9,11 @@ the code in PHP files or inside of a static function named
|
||||
PHP Files
|
||||
---------
|
||||
|
||||
.. note::
|
||||
|
||||
PHPDriver is deprecated and will be removed in 3.0, use StaticPHPDriver
|
||||
instead.
|
||||
|
||||
If you wish to write your mapping information inside PHP files that
|
||||
are named after the entity and included to populate the metadata
|
||||
for an entity you can do so by using the ``PHPDriver``:
|
||||
|
||||
@@ -13,7 +13,7 @@ as you want, or just pick a preferred one.
|
||||
|
||||
The ``QueryBuilder`` is not an abstraction of DQL, but merely a tool to dynamically build it.
|
||||
You should still use plain DQL when you can, as it is simpler and more readable.
|
||||
More about this in the :doc:`FAQ <faq>`_.
|
||||
More about this in the :doc:`FAQ <faq>`.
|
||||
|
||||
Constructing a new QueryBuilder object
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -31,31 +31,31 @@ Each cache region resides in a specific cache namespace and has its own lifetime
|
||||
Notice that when caching collection and queries only identifiers are stored.
|
||||
The entity values will be stored in its own region
|
||||
|
||||
Something like below for an entity region :
|
||||
Something like below for an entity region:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
[
|
||||
'region_name:entity_1_hash' => ['id'=> 1, 'name' => 'FooBar', 'associationName'=>null],
|
||||
'region_name:entity_2_hash' => ['id'=> 2, 'name' => 'Foo', 'associationName'=>['id'=>11]],
|
||||
'region_name:entity_3_hash' => ['id'=> 3, 'name' => 'Bar', 'associationName'=>['id'=>22]]
|
||||
'region_name:entity_1_hash' => ['id' => 1, 'name' => 'FooBar', 'associationName' => null],
|
||||
'region_name:entity_2_hash' => ['id' => 2, 'name' => 'Foo', 'associationName' => ['id' => 11]],
|
||||
'region_name:entity_3_hash' => ['id' => 3, 'name' => 'Bar', 'associationName' => ['id' => 22]]
|
||||
];
|
||||
|
||||
|
||||
If the entity holds a collection that also needs to be cached.
|
||||
An collection region could look something like :
|
||||
An collection region could look something like:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
[
|
||||
'region_name:entity_1_coll_assoc_name_hash' => ['ownerId'=> 1, 'list' => [1, 2, 3]],
|
||||
'region_name:entity_2_coll_assoc_name_hash' => ['ownerId'=> 2, 'list' => [2, 3]],
|
||||
'region_name:entity_3_coll_assoc_name_hash' => ['ownerId'=> 3, 'list' => [2, 4]]
|
||||
'region_name:entity_1_coll_assoc_name_hash' => ['ownerId' => 1, 'list' => [1, 2, 3]],
|
||||
'region_name:entity_2_coll_assoc_name_hash' => ['ownerId' => 2, 'list' => [2, 3]],
|
||||
'region_name:entity_3_coll_assoc_name_hash' => ['ownerId' => 3, 'list' => [2, 4]]
|
||||
];
|
||||
|
||||
A query region might be something like :
|
||||
A query region might be something like:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -93,8 +93,6 @@ Cache region
|
||||
``Doctrine\ORM\Cache\Region`` defines a contract for accessing a particular
|
||||
cache region.
|
||||
|
||||
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Region.html>`_.
|
||||
|
||||
Concurrent cache region
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -105,8 +103,6 @@ If you want to use an ``READ_WRITE`` cache, you should consider providing your o
|
||||
|
||||
``Doctrine\ORM\Cache\ConcurrentRegion`` defines a contract for concurrently managed data region.
|
||||
|
||||
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/ConcurrentRegion.html>`_.
|
||||
|
||||
Timestamp region
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -114,8 +110,6 @@ Timestamp region
|
||||
|
||||
Tracks the timestamps of the most recent updates to particular entity.
|
||||
|
||||
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/TimestampRegion.html>`_.
|
||||
|
||||
.. _reference-second-level-cache-mode:
|
||||
|
||||
Caching mode
|
||||
@@ -132,7 +126,7 @@ Caching mode
|
||||
|
||||
* Read Write Cache doesn’t employ any locks but can do reads, inserts, updates and deletes.
|
||||
* Good if the application needs to update data rarely.
|
||||
|
||||
|
||||
|
||||
* ``READ_WRITE``
|
||||
|
||||
@@ -147,21 +141,21 @@ Built-in cached persisters
|
||||
|
||||
Cached persisters are responsible to access cache regions.
|
||||
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
| Cache Usage | Persister |
|
||||
+=======================+===========================================================================================+
|
||||
| READ_ONLY | Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadOnlyCachedEntityPersister |
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
| READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadWriteCachedEntityPersister |
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
| NONSTRICT_READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\Entity\\NonStrictReadWriteCachedEntityPersister |
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
| READ_ONLY | Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadOnlyCachedCollectionPersister |
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
| READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadWriteCachedCollectionPersister |
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
| NONSTRICT_READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\Collection\\NonStrictReadWriteCachedCollectionPersister |
|
||||
+-----------------------+-------------------------------------------------------------------------------------------+
|
||||
+-----------------------+------------------------------------------------------------------------------------------+
|
||||
| Cache Usage | Persister |
|
||||
+=======================+==========================================================================================+
|
||||
| READ_ONLY | ``Doctrine\ORM\Cache\Persister\Entity\ReadOnlyCachedEntityPersister`` |
|
||||
+-----------------------+------------------------------------------------------------------------------------------+
|
||||
| READ_WRITE | ``Doctrine\ORM\Cache\Persister\Entity\ReadWriteCachedEntityPersister`` |
|
||||
+-----------------------+------------------------------------------------------------------------------------------+
|
||||
| NONSTRICT_READ_WRITE | ``Doctrine\ORM\Cache\Persister\Entity\NonStrictReadWriteCachedEntityPersister`` |
|
||||
+-----------------------+------------------------------------------------------------------------------------------+
|
||||
| READ_ONLY | ``Doctrine\ORM\Cache\Persister\Collection\ReadOnlyCachedCollectionPersister`` |
|
||||
+-----------------------+------------------------------------------------------------------------------------------+
|
||||
| READ_WRITE | ``Doctrine\ORM\Cache\Persister\Collection\ReadWriteCachedCollectionPersister`` |
|
||||
+-----------------------+------------------------------------------------------------------------------------------+
|
||||
| NONSTRICT_READ_WRITE | ``Doctrine\ORM\Cache\Persister\Collection\NonStrictReadWriteCachedCollectionPersister`` |
|
||||
+-----------------------+------------------------------------------------------------------------------------------+
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
@@ -172,13 +166,13 @@ Enable Second Level Cache
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To enable the second-level-cache, you should provide a cache factory.
|
||||
``\Doctrine\ORM\Cache\DefaultCacheFactory`` is the default implementation.
|
||||
``Doctrine\ORM\Cache\DefaultCacheFactory`` is the default implementation.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @var \Doctrine\ORM\Cache\RegionsConfiguration $cacheConfig */
|
||||
/** @var \Doctrine\Common\Cache\Cache $cache */
|
||||
/** @var \Psr\Cache\CacheItemPoolInterface $cache */
|
||||
/** @var \Doctrine\ORM\Configuration $config */
|
||||
|
||||
$factory = new \Doctrine\ORM\Cache\DefaultCacheFactory($cacheConfig, $cache);
|
||||
@@ -196,7 +190,7 @@ Cache Factory
|
||||
|
||||
Cache Factory is the main point of extension.
|
||||
|
||||
It allows you to provide a specific implementation of the following components :
|
||||
It allows you to provide a specific implementation of the following components:
|
||||
|
||||
``QueryCache``
|
||||
stores and retrieves query cache results.
|
||||
@@ -209,8 +203,6 @@ It allows you to provide a specific implementation of the following components :
|
||||
``CollectionHydrator``
|
||||
transforms collections into cache entries and cache entries into collections
|
||||
|
||||
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/DefaultCacheFactory.html>`_.
|
||||
|
||||
Region Lifetime
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -234,12 +226,12 @@ Cache Log
|
||||
~~~~~~~~~
|
||||
By providing a cache logger you should be able to get information about all cache operations such as hits, misses and puts.
|
||||
|
||||
``\Doctrine\ORM\Cache\Logging\StatisticsCacheLogger`` is a built-in implementation that provides basic statistics.
|
||||
``Doctrine\ORM\Cache\Logging\StatisticsCacheLogger`` is a built-in implementation that provides basic statistics.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $config \Doctrine\ORM\Configuration */
|
||||
/** @var \Doctrine\ORM\Configuration $config */
|
||||
$logger = new \Doctrine\ORM\Cache\Logging\StatisticsCacheLogger();
|
||||
|
||||
// Cache logger
|
||||
@@ -269,12 +261,9 @@ By providing a cache logger you should be able to get information about all cach
|
||||
$logger->getMissCount();
|
||||
|
||||
If you want to get more information you should implement
|
||||
``\Doctrine\ORM\Cache\Logging\CacheLogger`` and collect
|
||||
``Doctrine\ORM\Cache\Logging\CacheLogger`` and collect
|
||||
all the information you want.
|
||||
|
||||
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Logging/CacheLogger.html>`_.
|
||||
|
||||
|
||||
Entity cache definition
|
||||
-----------------------
|
||||
* Entity cache configuration allows you to define the caching strategy and region for an entity.
|
||||
@@ -330,8 +319,8 @@ level cache region.
|
||||
Country:
|
||||
type: entity
|
||||
cache:
|
||||
usage : READ_ONLY
|
||||
region : my_entity_region
|
||||
usage: READ_ONLY
|
||||
region: my_entity_region
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
@@ -401,7 +390,7 @@ It caches the primary keys of association and cache each element will be cached
|
||||
</id>
|
||||
|
||||
<field name="name" type="string" column="name"/>
|
||||
|
||||
|
||||
<many-to-one field="country" target-entity="Country">
|
||||
<cache usage="NONSTRICT_READ_WRITE" />
|
||||
|
||||
@@ -421,7 +410,7 @@ It caches the primary keys of association and cache each element will be cached
|
||||
State:
|
||||
type: entity
|
||||
cache:
|
||||
usage : NONSTRICT_READ_WRITE
|
||||
usage: NONSTRICT_READ_WRITE
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
@@ -439,17 +428,18 @@ It caches the primary keys of association and cache each element will be cached
|
||||
country_id:
|
||||
referencedColumnName: id
|
||||
cache:
|
||||
usage : NONSTRICT_READ_WRITE
|
||||
usage: NONSTRICT_READ_WRITE
|
||||
|
||||
oneToMany:
|
||||
cities:
|
||||
targetEntity:City
|
||||
mappedBy: state
|
||||
cache:
|
||||
usage : NONSTRICT_READ_WRITE
|
||||
usage: NONSTRICT_READ_WRITE
|
||||
|
||||
.. note::
|
||||
|
||||
> Note: for this to work, the target entity must also be marked as cacheable.
|
||||
for this to work, the target entity must also be marked as cacheable.
|
||||
|
||||
Cache usage
|
||||
~~~~~~~~~~~
|
||||
@@ -466,8 +456,8 @@ Basic entity cache
|
||||
|
||||
$country1 = $em->find('Country', 1); // Retrieve item from cache
|
||||
|
||||
$country1->setName("New Name");
|
||||
|
||||
$country1->setName('New Name');
|
||||
|
||||
$em->flush(); // Hit database to update the row and update cache
|
||||
|
||||
$em->clear(); // Clear entity manager
|
||||
@@ -492,7 +482,7 @@ Association cache
|
||||
$state = $em->find('State', 1);
|
||||
|
||||
// Hit database to update the row and update cache entry
|
||||
$state->setName("New Name");
|
||||
$state->setName('New Name');
|
||||
$em->persist($state);
|
||||
$em->flush();
|
||||
|
||||
@@ -543,14 +533,14 @@ The query cache stores the results of the query but as identifiers, entity value
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $em \Doctrine\ORM\EntityManager */
|
||||
/** @var \Doctrine\ORM\EntityManager $em */
|
||||
|
||||
// Execute database query, store query cache and entity cache
|
||||
$result1 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
|
||||
->setCacheable(true)
|
||||
->getResult();
|
||||
|
||||
$em->clear()
|
||||
$em->clear();
|
||||
|
||||
// Check if query result is valid and load entities from cache
|
||||
$result2 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
|
||||
@@ -570,10 +560,10 @@ The Cache Mode controls how a particular query interacts with the second-level c
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $em \Doctrine\ORM\EntityManager */
|
||||
/** @var \Doctrine\ORM\EntityManager $em */
|
||||
// Will refresh the query cache and all entities the cache as it reads from the database.
|
||||
$result1 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
|
||||
->setCacheMode(Cache::MODE_GET)
|
||||
->setCacheMode(\Doctrine\ORM\Cache::MODE_GET)
|
||||
->setCacheable(true)
|
||||
->getResult();
|
||||
|
||||
@@ -597,7 +587,7 @@ Execute the ``UPDATE`` and invalidate ``all cache entries`` using ``Query::HINT_
|
||||
<?php
|
||||
// Execute and invalidate
|
||||
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
|
||||
->setHint(Query::HINT_CACHE_EVICT, true)
|
||||
->setHint(\Doctrine\ORM\Query::HINT_CACHE_EVICT, true)
|
||||
->execute();
|
||||
|
||||
|
||||
@@ -659,7 +649,7 @@ However, you can use the cache API to check / invalidate cache entries.
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/* @var $cache \Doctrine\ORM\Cache */
|
||||
/** @var \Doctrine\ORM\Cache $cache */
|
||||
$cache = $em->getCache();
|
||||
|
||||
$cache->containsEntity('Entity\State', 1) // Check if the cache exists
|
||||
@@ -704,19 +694,19 @@ For performance reasons the cache API does not extract from composite primary ke
|
||||
}
|
||||
|
||||
// Supported
|
||||
/* @var $article Article */
|
||||
/** @var Article $article */
|
||||
$article = $em->find('Article', 1);
|
||||
|
||||
// Supported
|
||||
/* @var $article Article */
|
||||
/** @var Article $article */
|
||||
$article = $em->find('Article', $article);
|
||||
|
||||
// Supported
|
||||
$id = array('source' => 1, 'target' => 2);
|
||||
$id = ['source' => 1, 'target' => 2];
|
||||
$reference = $em->find('Reference', $id);
|
||||
|
||||
// NOT Supported
|
||||
$id = array('source' => new Article(1), 'target' => new Article(2));
|
||||
$id = ['source' => new Article(1), 'target' => new Article(2)];
|
||||
$reference = $em->find('Reference', $id);
|
||||
|
||||
Distributed environments
|
||||
|
||||
@@ -521,6 +521,8 @@ For each cascade operation that gets activated, Doctrine also
|
||||
applies that operation to the association, be it single or
|
||||
collection valued.
|
||||
|
||||
.. _persistence-by-reachability:
|
||||
|
||||
Persistence by Reachability: Cascade Persist
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -256,6 +256,11 @@ Optional attributes:
|
||||
table? Defaults to false.
|
||||
- nullable - Should this field allow NULL as a value? Defaults to
|
||||
false.
|
||||
- insertable - Should this field be inserted? Defaults to true.
|
||||
- updatable - Should this field be updated? Defaults to true.
|
||||
- generated - Enum of the values ALWAYS, INSERT, NEVER that determines if
|
||||
generated value must be fetched from database after INSERT or UPDATE.
|
||||
Defaults to "NEVER".
|
||||
- version - Should this field be used for optimistic locking? Only
|
||||
works on fields with type integer or datetime.
|
||||
- scale - Scale of a decimal type.
|
||||
@@ -689,6 +694,7 @@ specified by their respective tags:
|
||||
- ``<cascade-merge />``
|
||||
- ``<cascade-remove />``
|
||||
- ``<cascade-refresh />``
|
||||
- ``<cascade-detach />``
|
||||
|
||||
Join Column Element
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -81,10 +81,11 @@ that directory with the following contents:
|
||||
|
||||
{
|
||||
"require": {
|
||||
"doctrine/orm": "^2.10.2",
|
||||
"doctrine/dbal": "^3.1.1",
|
||||
"symfony/yaml": "2.*",
|
||||
"symfony/cache": "^5.3"
|
||||
"doctrine/orm": "^2.11.0",
|
||||
"doctrine/dbal": "^3.2",
|
||||
"doctrine/annotations": "1.13.2",
|
||||
"symfony/yaml": "^5.4",
|
||||
"symfony/cache": "^5.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {"": "src/"}
|
||||
|
||||
@@ -288,6 +288,14 @@
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="generated-type">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="NEVER"/>
|
||||
<xs:enumeration value="INSERT"/>
|
||||
<xs:enumeration value="ALWAYS"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="field">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="options" type="orm:options" minOccurs="0" />
|
||||
@@ -299,6 +307,10 @@
|
||||
<xs:attribute name="length" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="unique" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="nullable" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="insertable" type="xs:boolean" default="true" />
|
||||
<xs:attribute name="updatable" type="xs:boolean" default="true" />
|
||||
<xs:attribute name="generated" type="orm:generated-type" default="NEVER" />
|
||||
<xs:attribute name="enum-type" type="xs:string" />
|
||||
<xs:attribute name="version" type="xs:boolean" />
|
||||
<xs:attribute name="column-definition" type="xs:string" />
|
||||
<xs:attribute name="precision" type="xs:integer" use="optional" />
|
||||
@@ -353,7 +365,7 @@
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="optional"/>
|
||||
<xs:attribute name="columns" type="xs:string" use="required"/>
|
||||
<xs:attribute name="columns" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="fields" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="flags" type="xs:string" use="optional"/>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
@@ -622,6 +634,8 @@
|
||||
<xs:attribute name="length" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="unique" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="nullable" type="xs:boolean" default="false" />
|
||||
<xs:attribute name="insertable" type="xs:boolean" default="true" />
|
||||
<xs:attribute name="updateable" type="xs:boolean" default="true" />
|
||||
<xs:attribute name="version" type="xs:boolean" />
|
||||
<xs:attribute name="column-definition" type="xs:string" />
|
||||
<xs:attribute name="precision" type="xs:integer" use="optional" />
|
||||
|
||||
@@ -22,6 +22,7 @@ use Doctrine\ORM\Query\Parameter;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\Persistence\Mapping\MappingException;
|
||||
use LogicException;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Traversable;
|
||||
|
||||
@@ -91,7 +92,7 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* The user-specified ResultSetMapping to use.
|
||||
*
|
||||
* @var ResultSetMapping
|
||||
* @var ResultSetMapping|null
|
||||
*/
|
||||
protected $_resultSetMapping;
|
||||
|
||||
@@ -113,6 +114,7 @@ abstract class AbstractQuery
|
||||
* The hydration mode.
|
||||
*
|
||||
* @var string|int
|
||||
* @psalm-var string|AbstractQuery::HYDRATE_*
|
||||
*/
|
||||
protected $_hydrationMode = self::HYDRATE_OBJECT;
|
||||
|
||||
@@ -150,6 +152,7 @@ abstract class AbstractQuery
|
||||
* Second level query cache mode.
|
||||
*
|
||||
* @var int|null
|
||||
* @psalm-var Cache::MODE_*|null
|
||||
*/
|
||||
protected $cacheMode;
|
||||
|
||||
@@ -251,7 +254,8 @@ abstract class AbstractQuery
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @return int|null
|
||||
* @psalm-return Cache::MODE_*|null
|
||||
*/
|
||||
public function getCacheMode()
|
||||
{
|
||||
@@ -260,6 +264,7 @@ abstract class AbstractQuery
|
||||
|
||||
/**
|
||||
* @param int $cacheMode
|
||||
* @psalm-param Cache::MODE_* $cacheMode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
@@ -366,11 +371,11 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Sets a query parameter.
|
||||
*
|
||||
* @param string|int $key The parameter position or name.
|
||||
* @param mixed $value The parameter value.
|
||||
* @param 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.
|
||||
* @param string|int $key The parameter position or name.
|
||||
* @param mixed $value The parameter value.
|
||||
* @param 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.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
@@ -492,7 +497,7 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Gets the ResultSetMapping used for hydration.
|
||||
*
|
||||
* @return ResultSetMapping
|
||||
* @return ResultSetMapping|null
|
||||
*/
|
||||
protected function getResultSetMapping()
|
||||
{
|
||||
@@ -540,7 +545,7 @@ abstract class AbstractQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
// DBAL < 3.2
|
||||
// DBAL 2
|
||||
if (! method_exists(QueryCacheProfile::class, 'setResultCache')) {
|
||||
if (! $profile->getResultCacheDriver()) {
|
||||
$defaultHydrationCacheImpl = $this->_em->getConfiguration()->getHydrationCache();
|
||||
@@ -584,7 +589,7 @@ abstract class AbstractQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
// DBAL < 3.2
|
||||
// DBAL 2
|
||||
if (! method_exists(QueryCacheProfile::class, 'setResultCache')) {
|
||||
if (! $profile->getResultCacheDriver()) {
|
||||
$defaultResultCacheDriver = $this->_em->getConfiguration()->getResultCache();
|
||||
@@ -640,7 +645,7 @@ abstract class AbstractQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
// DBAL < 3.2
|
||||
// DBAL 2
|
||||
if (! method_exists(QueryCacheProfile::class, 'setResultCache')) {
|
||||
$resultCacheDriver = DoctrineProvider::wrap($resultCache);
|
||||
|
||||
@@ -746,7 +751,7 @@ abstract class AbstractQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Compatibility for DBAL < 3.2
|
||||
// Compatibility for DBAL 2
|
||||
if (! method_exists($this->_queryCacheProfile, 'setResultCache')) {
|
||||
$this->_queryCacheProfile = $this->_queryCacheProfile->setResultCacheDriver(DoctrineProvider::wrap($cache));
|
||||
|
||||
@@ -829,6 +834,7 @@ abstract class AbstractQuery
|
||||
*
|
||||
* @param string|int $hydrationMode Doctrine processing mode to be used during hydration process.
|
||||
* One of the Query::HYDRATE_* constants.
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_* $hydrationMode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
@@ -843,6 +849,7 @@ abstract class AbstractQuery
|
||||
* Gets the hydration mode currently used by the query.
|
||||
*
|
||||
* @return string|int
|
||||
* @psalm-return string|AbstractQuery::HYDRATE_*
|
||||
*/
|
||||
public function getHydrationMode()
|
||||
{
|
||||
@@ -855,6 +862,7 @@ abstract class AbstractQuery
|
||||
* Alias for execute(null, $hydrationMode = HYDRATE_OBJECT).
|
||||
*
|
||||
* @param string|int $hydrationMode
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_* $hydrationMode
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -902,7 +910,8 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Get exactly one result or null.
|
||||
*
|
||||
* @param string|int $hydrationMode
|
||||
* @param string|int|null $hydrationMode
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
@@ -939,7 +948,8 @@ abstract class AbstractQuery
|
||||
* If the result is not unique, a NonUniqueResultException is thrown.
|
||||
* If there is no result, a NoResultException is thrown.
|
||||
*
|
||||
* @param string|int $hydrationMode
|
||||
* @param string|int|null $hydrationMode
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
@@ -1037,6 +1047,7 @@ abstract class AbstractQuery
|
||||
*
|
||||
* @param ArrayCollection|mixed[]|null $parameters The query parameters.
|
||||
* @param string|int|null $hydrationMode The hydration mode to use.
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode The hydration mode to use.
|
||||
*
|
||||
* @return IterableResult
|
||||
*/
|
||||
@@ -1057,7 +1068,11 @@ abstract class AbstractQuery
|
||||
$this->setParameters($parameters);
|
||||
}
|
||||
|
||||
$rsm = $this->getResultSetMapping();
|
||||
$rsm = $this->getResultSetMapping();
|
||||
if ($rsm === null) {
|
||||
throw new LogicException('Uninitialized result set mapping.');
|
||||
}
|
||||
|
||||
$stmt = $this->_doExecute();
|
||||
|
||||
return $this->_em->newHydrator($this->_hydrationMode)->iterate($stmt, $rsm, $this->_hints);
|
||||
@@ -1070,6 +1085,7 @@ abstract class AbstractQuery
|
||||
* @param ArrayCollection|array|mixed[] $parameters The query parameters.
|
||||
* @param string|int|null $hydrationMode The hydration mode to use.
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
*
|
||||
* @return iterable<mixed>
|
||||
*/
|
||||
@@ -1087,6 +1103,9 @@ abstract class AbstractQuery
|
||||
}
|
||||
|
||||
$rsm = $this->getResultSetMapping();
|
||||
if ($rsm === null) {
|
||||
throw new LogicException('Uninitialized result set mapping.');
|
||||
}
|
||||
|
||||
if ($rsm->isMixed && count($rsm->scalarMappings) > 0) {
|
||||
throw QueryException::iterateWithMixedResultNotAllowed();
|
||||
@@ -1103,6 +1122,7 @@ abstract class AbstractQuery
|
||||
* @param ArrayCollection|mixed[]|null $parameters Query parameters.
|
||||
* @param string|int|null $hydrationMode Processing mode to be used during the hydration process.
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -1121,6 +1141,7 @@ abstract class AbstractQuery
|
||||
* @param ArrayCollection|mixed[]|null $parameters
|
||||
* @param string|int|null $hydrationMode
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -1165,7 +1186,11 @@ abstract class AbstractQuery
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
$rsm = $this->getResultSetMapping();
|
||||
$rsm = $this->getResultSetMapping();
|
||||
if ($rsm === null) {
|
||||
throw new LogicException('Uninitialized result set mapping.');
|
||||
}
|
||||
|
||||
$data = $this->_em->newHydrator($this->_hydrationMode)->hydrateAll($stmt, $rsm, $this->_hints);
|
||||
|
||||
$setCacheEntry($data);
|
||||
@@ -1177,7 +1202,7 @@ abstract class AbstractQuery
|
||||
{
|
||||
assert($this->_hydrationCacheProfile !== null);
|
||||
|
||||
// Support for DBAL < 3.2
|
||||
// Support for DBAL 2
|
||||
if (! method_exists($this->_hydrationCacheProfile, 'getResultCache')) {
|
||||
$cacheDriver = $this->_hydrationCacheProfile->getResultCacheDriver();
|
||||
assert($cacheDriver !== null);
|
||||
@@ -1197,12 +1222,17 @@ abstract class AbstractQuery
|
||||
* @param ArrayCollection|mixed[]|null $parameters
|
||||
* @param string|int|null $hydrationMode
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_*|null $hydrationMode
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function executeUsingQueryCache($parameters = null, $hydrationMode = null)
|
||||
{
|
||||
$rsm = $this->getResultSetMapping();
|
||||
$rsm = $this->getResultSetMapping();
|
||||
if ($rsm === null) {
|
||||
throw new LogicException('Uninitialized result set mapping.');
|
||||
}
|
||||
|
||||
$queryCache = $this->_em->getCache()->getQueryCache($this->cacheRegion);
|
||||
$queryKey = new QueryCacheKey(
|
||||
$this->getHash(),
|
||||
@@ -1237,6 +1267,7 @@ abstract class AbstractQuery
|
||||
|
||||
private function getTimestampKey(): ?TimestampCacheKey
|
||||
{
|
||||
assert($this->_resultSetMapping !== null);
|
||||
$entityName = reset($this->_resultSetMapping->aliasMap);
|
||||
|
||||
if (empty($entityName)) {
|
||||
|
||||
@@ -10,15 +10,13 @@ namespace Doctrine\ORM\Cache;
|
||||
class AssociationCacheEntry implements CacheEntry
|
||||
{
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var array<string, mixed> The entity identifier
|
||||
*/
|
||||
public $identifier;
|
||||
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var string The entity class name
|
||||
*/
|
||||
public $class;
|
||||
|
||||
@@ -11,8 +11,7 @@ namespace Doctrine\ORM\Cache;
|
||||
abstract class CacheKey
|
||||
{
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var string Unique identifier
|
||||
*/
|
||||
public $hash;
|
||||
|
||||
@@ -10,8 +10,7 @@ namespace Doctrine\ORM\Cache;
|
||||
class CollectionCacheEntry implements CacheEntry
|
||||
{
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var CacheKey[] The list of entity identifiers hold by the collection
|
||||
*/
|
||||
public $identifiers;
|
||||
|
||||
@@ -15,22 +15,19 @@ use function strtolower;
|
||||
class CollectionCacheKey extends CacheKey
|
||||
{
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var array<string, mixed> The owner entity identifier
|
||||
*/
|
||||
public $ownerIdentifier;
|
||||
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var string The owner entity class
|
||||
*/
|
||||
public $entityClass;
|
||||
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var string The association name
|
||||
*/
|
||||
public $association;
|
||||
|
||||
@@ -28,7 +28,7 @@ interface CollectionHydrator
|
||||
* @param CollectionCacheEntry $entry The cached collection entry.
|
||||
* @param PersistentCollection $collection The collection to load the cache into.
|
||||
*
|
||||
* @return mixed[]
|
||||
* @return mixed[]|null
|
||||
*/
|
||||
public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ interface ConcurrentRegion extends Region
|
||||
*
|
||||
* @param CacheKey $key The key of the item to lock.
|
||||
*
|
||||
* @return Lock A lock instance or NULL if the lock already exists.
|
||||
* @return Lock|null A lock instance or NULL if the lock already exists.
|
||||
*
|
||||
* @throws LockException Indicates a problem accessing the region.
|
||||
*/
|
||||
@@ -30,7 +30,7 @@ interface ConcurrentRegion extends Region
|
||||
* @param CacheKey $key The key of the item to unlock.
|
||||
* @param Lock $lock The lock previously obtained from {@link readLock}
|
||||
*
|
||||
* @return void
|
||||
* @return bool
|
||||
*
|
||||
* @throws LockException Indicates a problem accessing the region.
|
||||
*/
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
use Doctrine\Common\Cache\Cache as CacheAdapter;
|
||||
use Doctrine\Common\Cache\CacheProvider;
|
||||
use Doctrine\Common\Cache\MultiGetCache;
|
||||
use Doctrine\Common\Cache\Cache as LegacyCache;
|
||||
use Doctrine\Common\Cache\Psr6\CacheAdapter;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Cache\Persister\Collection\NonStrictReadWriteCachedCollectionPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Collection\ReadOnlyCachedCollectionPersister;
|
||||
@@ -14,7 +14,6 @@ use Doctrine\ORM\Cache\Persister\Collection\ReadWriteCachedCollectionPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\NonStrictReadWriteCachedEntityPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\ReadOnlyCachedEntityPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\ReadWriteCachedEntityPersister;
|
||||
use Doctrine\ORM\Cache\Region\DefaultMultiGetRegion;
|
||||
use Doctrine\ORM\Cache\Region\DefaultRegion;
|
||||
use Doctrine\ORM\Cache\Region\FileLockRegion;
|
||||
use Doctrine\ORM\Cache\Region\UpdateTimestampCache;
|
||||
@@ -24,15 +23,19 @@ use Doctrine\ORM\Persisters\Collection\CollectionPersister;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use TypeError;
|
||||
|
||||
use function assert;
|
||||
use function get_debug_type;
|
||||
use function sprintf;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
class DefaultCacheFactory implements CacheFactory
|
||||
{
|
||||
/** @var CacheAdapter */
|
||||
private $cache;
|
||||
/** @var CacheItemPoolInterface */
|
||||
private $cacheItemPool;
|
||||
|
||||
/** @var RegionsConfiguration */
|
||||
private $regionsConfig;
|
||||
@@ -46,9 +49,33 @@ class DefaultCacheFactory implements CacheFactory
|
||||
/** @var string|null */
|
||||
private $fileLockRegionDirectory;
|
||||
|
||||
public function __construct(RegionsConfiguration $cacheConfig, CacheAdapter $cache)
|
||||
/**
|
||||
* @param CacheItemPoolInterface $cacheItemPool
|
||||
*/
|
||||
public function __construct(RegionsConfiguration $cacheConfig, $cacheItemPool)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
if ($cacheItemPool instanceof LegacyCache) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/9322',
|
||||
'Passing an instance of %s to %s is deprecated, pass a %s instead.',
|
||||
get_debug_type($cacheItemPool),
|
||||
__METHOD__,
|
||||
CacheItemPoolInterface::class
|
||||
);
|
||||
|
||||
$this->cacheItemPool = CacheAdapter::wrap($cacheItemPool);
|
||||
} elseif (! $cacheItemPool instanceof CacheItemPoolInterface) {
|
||||
throw new TypeError(sprintf(
|
||||
'%s: Parameter #2 is expected to be an instance of %s, got %s.',
|
||||
__METHOD__,
|
||||
CacheItemPoolInterface::class,
|
||||
get_debug_type($cacheItemPool)
|
||||
));
|
||||
} else {
|
||||
$this->cacheItemPool = $cacheItemPool;
|
||||
}
|
||||
|
||||
$this->regionsConfig = $cacheConfig;
|
||||
}
|
||||
|
||||
@@ -91,6 +118,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
*/
|
||||
public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata)
|
||||
{
|
||||
assert($metadata->cache !== null);
|
||||
$region = $this->getRegion($metadata->cache);
|
||||
$usage = $metadata->cache['usage'];
|
||||
|
||||
@@ -181,13 +209,9 @@ class DefaultCacheFactory implements CacheFactory
|
||||
return $this->regions[$cache['region']];
|
||||
}
|
||||
|
||||
$name = $cache['region'];
|
||||
$cacheAdapter = $this->createRegionCache($name);
|
||||
$lifetime = $this->regionsConfig->getLifetime($cache['region']);
|
||||
|
||||
$region = $cacheAdapter instanceof MultiGetCache
|
||||
? new DefaultMultiGetRegion($name, $cacheAdapter, $lifetime)
|
||||
: new DefaultRegion($name, $cacheAdapter, $lifetime);
|
||||
$name = $cache['region'];
|
||||
$lifetime = $this->regionsConfig->getLifetime($cache['region']);
|
||||
$region = new DefaultRegion($name, $this->cacheItemPool, $lifetime);
|
||||
|
||||
if ($cache['usage'] === ClassMetadata::CACHE_USAGE_READ_WRITE) {
|
||||
if (
|
||||
@@ -207,25 +231,6 @@ class DefaultCacheFactory implements CacheFactory
|
||||
return $this->regions[$cache['region']] = $region;
|
||||
}
|
||||
|
||||
private function createRegionCache(string $name): CacheAdapter
|
||||
{
|
||||
$cacheAdapter = clone $this->cache;
|
||||
|
||||
if (! $cacheAdapter instanceof CacheProvider) {
|
||||
return $cacheAdapter;
|
||||
}
|
||||
|
||||
$namespace = $cacheAdapter->getNamespace();
|
||||
|
||||
if ($namespace !== '') {
|
||||
$namespace .= ':';
|
||||
}
|
||||
|
||||
$cacheAdapter->setNamespace($namespace . $name);
|
||||
|
||||
return $cacheAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -235,7 +240,7 @@ class DefaultCacheFactory implements CacheFactory
|
||||
$name = Cache::DEFAULT_TIMESTAMP_REGION_NAME;
|
||||
$lifetime = $this->regionsConfig->getLifetime($name);
|
||||
|
||||
$this->timestampRegion = new UpdateTimestampCache($name, clone $this->cache, $lifetime);
|
||||
$this->timestampRegion = new UpdateTimestampCache($name, $this->cacheItemPool, $lifetime);
|
||||
}
|
||||
|
||||
return $this->timestampRegion;
|
||||
@@ -244,8 +249,8 @@ class DefaultCacheFactory implements CacheFactory
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createCache(EntityManagerInterface $em)
|
||||
public function createCache(EntityManagerInterface $entityManager)
|
||||
{
|
||||
return new DefaultCache($em);
|
||||
return new DefaultCache($entityManager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
|
||||
use function array_walk;
|
||||
use function assert;
|
||||
|
||||
/**
|
||||
|
||||
@@ -55,8 +55,16 @@ class DefaultEntityHydrator implements EntityHydrator
|
||||
$data = $this->uow->getOriginalEntityData($entity);
|
||||
$data = array_merge($data, $metadata->getIdentifierValues($entity)); // why update has no identifier values ?
|
||||
|
||||
if ($metadata->isVersioned) {
|
||||
$data[$metadata->versionField] = $metadata->getFieldValue($entity, $metadata->versionField);
|
||||
if ($metadata->requiresFetchAfterChange) {
|
||||
if ($metadata->isVersioned) {
|
||||
$data[$metadata->versionField] = $metadata->getFieldValue($entity, $metadata->versionField);
|
||||
}
|
||||
|
||||
foreach ($metadata->fieldMappings as $name => $fieldMapping) {
|
||||
if (isset($fieldMapping['generated'])) {
|
||||
$data[$name] = $metadata->getFieldValue($entity, $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($metadata->associationMappings as $name => $assoc) {
|
||||
|
||||
@@ -18,7 +18,6 @@ use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
|
||||
use function array_key_exists;
|
||||
use function array_map;
|
||||
use function array_shift;
|
||||
use function array_unshift;
|
||||
@@ -45,7 +44,7 @@ class DefaultQueryCache implements QueryCache
|
||||
/** @var QueryCacheValidator */
|
||||
private $validator;
|
||||
|
||||
/** @var CacheLogger */
|
||||
/** @var CacheLogger|null */
|
||||
protected $cacheLogger;
|
||||
|
||||
/** @var array<string,mixed> */
|
||||
|
||||
@@ -14,15 +14,13 @@ use function array_map;
|
||||
class EntityCacheEntry implements CacheEntry
|
||||
{
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var array<string,mixed> The entity map data
|
||||
*/
|
||||
public $data;
|
||||
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var string The entity class name
|
||||
* @psalm-var class-string
|
||||
*/
|
||||
|
||||
@@ -15,15 +15,13 @@ use function strtolower;
|
||||
class EntityCacheKey extends CacheKey
|
||||
{
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var array<string, mixed> The entity identifier
|
||||
*/
|
||||
public $identifier;
|
||||
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var string The entity class name
|
||||
*/
|
||||
public $entityClass;
|
||||
|
||||
@@ -24,7 +24,9 @@ interface EntityHydrator
|
||||
* @param ClassMetadata $metadata The entity metadata.
|
||||
* @param EntityCacheKey $key The entity cache key.
|
||||
* @param EntityCacheEntry $entry The entity cache entry.
|
||||
* @param object $entity The entity to load the cache into. If not specified, a new entity is created.
|
||||
* @param object|null $entity The entity to load the cache into. If not specified, a new entity is created.
|
||||
*
|
||||
* @return object|null
|
||||
*/
|
||||
public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, $entity = null);
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ namespace Doctrine\ORM\Cache\Exception;
|
||||
|
||||
use Doctrine\ORM\Cache\CacheException as BaseCacheException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Exception for cache.
|
||||
*/
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
class CannotUpdateReadOnlyCollection extends CacheException
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
class CannotUpdateReadOnlyEntity extends CacheException
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
class FeatureNotImplemented extends CacheException
|
||||
{
|
||||
public static function scalarResults(): self
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
final class MetadataCacheNotConfigured extends CacheException
|
||||
{
|
||||
public static function create(): self
|
||||
|
||||
@@ -5,16 +5,15 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Cache\Exception;
|
||||
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use LogicException;
|
||||
|
||||
use function get_class;
|
||||
use function get_debug_type;
|
||||
|
||||
final class MetadataCacheUsesNonPersistentCache extends CacheException
|
||||
{
|
||||
public static function fromDriver(Cache $cache): self
|
||||
{
|
||||
return new self(
|
||||
'Metadata Cache uses a non-persistent cache driver, ' . get_class($cache) . '.'
|
||||
'Metadata Cache uses a non-persistent cache driver, ' . get_debug_type($cache) . '.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
class NonCacheableEntity extends CacheException
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
final class QueryCacheNotConfigured extends CacheException
|
||||
{
|
||||
public static function create(): self
|
||||
|
||||
@@ -5,16 +5,15 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Cache\Exception;
|
||||
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use LogicException;
|
||||
|
||||
use function get_class;
|
||||
use function get_debug_type;
|
||||
|
||||
final class QueryCacheUsesNonPersistentCache extends CacheException
|
||||
{
|
||||
public static function fromDriver(Cache $cache): self
|
||||
{
|
||||
return new self(
|
||||
'Query Cache uses a non-persistent cache driver, ' . get_class($cache) . '.'
|
||||
'Query Cache uses a non-persistent cache driver, ' . get_debug_type($cache) . '.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Cache\Persister\Collection;
|
||||
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
use Doctrine\ORM\Cache\CacheException;
|
||||
use Doctrine\ORM\Cache\Exception\CannotUpdateReadOnlyCollection;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
|
||||
|
||||
@@ -12,15 +12,13 @@ use function microtime;
|
||||
class QueryCacheEntry implements CacheEntry
|
||||
{
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var array<string, mixed> List of entity identifiers
|
||||
*/
|
||||
public $result;
|
||||
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var float Time creation of this cache entry
|
||||
*/
|
||||
public $time;
|
||||
|
||||
@@ -12,26 +12,29 @@ use Doctrine\ORM\Cache;
|
||||
class QueryCacheKey extends CacheKey
|
||||
{
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var int Cache key lifetime
|
||||
*/
|
||||
public $lifetime;
|
||||
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
* Cache mode
|
||||
*
|
||||
* @var int Cache mode (Doctrine\ORM\Cache::MODE_*)
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var int
|
||||
* @psalm-var Cache::MODE_*
|
||||
*/
|
||||
public $cacheMode;
|
||||
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var TimestampCacheKey|null
|
||||
*/
|
||||
public $timestampKey;
|
||||
|
||||
/**
|
||||
* @psalm-param Cache::MODE_* $cacheMode
|
||||
*/
|
||||
public function __construct(
|
||||
string $cacheId,
|
||||
int $lifetime = 0,
|
||||
|
||||
@@ -45,6 +45,8 @@ interface Region extends MultiGetRegion
|
||||
* @param CacheEntry $entry The entry to cache.
|
||||
* @param Lock|null $lock The lock previously obtained.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws CacheException Indicates a problem accessing the region.
|
||||
*/
|
||||
public function put(CacheKey $key, CacheEntry $entry, ?Lock $lock = null);
|
||||
@@ -54,6 +56,8 @@ interface Region extends MultiGetRegion
|
||||
*
|
||||
* @param CacheKey $key The key under which to cache the item.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws CacheException Indicates a problem accessing the region.
|
||||
*/
|
||||
public function evict(CacheKey $key);
|
||||
@@ -61,6 +65,8 @@ interface Region extends MultiGetRegion
|
||||
/**
|
||||
* Remove all contents of this particular cache region.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws CacheException Indicates problem accessing the region.
|
||||
*/
|
||||
public function evictAll();
|
||||
|
||||
@@ -4,64 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache\Region;
|
||||
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use Doctrine\Common\Cache\MultiGetCache;
|
||||
use Doctrine\ORM\Cache\CacheEntry;
|
||||
use Doctrine\ORM\Cache\CollectionCacheEntry;
|
||||
|
||||
use function assert;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* A cache region that enables the retrieval of multiple elements with one call
|
||||
*
|
||||
* @deprecated Use {@link DefaultRegion} instead.
|
||||
*/
|
||||
class DefaultMultiGetRegion extends DefaultRegion
|
||||
{
|
||||
/**
|
||||
* Note that the multiple type is due to doctrine/cache not integrating the MultiGetCache interface
|
||||
* in its signature due to BC in 1.x
|
||||
*
|
||||
* @var MultiGetCache|Cache
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @param MultiGetCache $cache
|
||||
*/
|
||||
public function __construct($name, MultiGetCache $cache, $lifetime = 0)
|
||||
{
|
||||
assert($cache instanceof Cache);
|
||||
parent::__construct($name, $cache, $lifetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMultiple(CollectionCacheEntry $collection)
|
||||
{
|
||||
$keysToRetrieve = [];
|
||||
|
||||
foreach ($collection->identifiers as $index => $key) {
|
||||
$keysToRetrieve[$index] = $this->getCacheEntryKey($key);
|
||||
}
|
||||
|
||||
$items = $this->cache->fetchMultiple($keysToRetrieve);
|
||||
if (count($items) !== count($keysToRetrieve)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$returnableItems = [];
|
||||
|
||||
foreach ($keysToRetrieve as $index => $key) {
|
||||
if (! $items[$key] instanceof CacheEntry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$returnableItems[$index] = $items[$key];
|
||||
}
|
||||
|
||||
return $returnableItems;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,44 +4,94 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Cache\Region;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Doctrine\Common\Cache\Cache as CacheAdapter;
|
||||
use Closure;
|
||||
use Doctrine\Common\Cache\Cache as LegacyCache;
|
||||
use Doctrine\Common\Cache\CacheProvider;
|
||||
use Doctrine\Common\Cache\ClearableCache;
|
||||
use Doctrine\Common\Cache\Psr6\CacheAdapter;
|
||||
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Cache\CacheEntry;
|
||||
use Doctrine\ORM\Cache\CacheKey;
|
||||
use Doctrine\ORM\Cache\CollectionCacheEntry;
|
||||
use Doctrine\ORM\Cache\Lock;
|
||||
use Doctrine\ORM\Cache\Region;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Traversable;
|
||||
use TypeError;
|
||||
|
||||
use function get_class;
|
||||
use function array_map;
|
||||
use function get_debug_type;
|
||||
use function iterator_to_array;
|
||||
use function sprintf;
|
||||
use function strtr;
|
||||
|
||||
/**
|
||||
* The simplest cache region compatible with all doctrine-cache drivers.
|
||||
*/
|
||||
class DefaultRegion implements Region
|
||||
{
|
||||
/**
|
||||
* @internal since 2.11, this constant will be private in 3.0.
|
||||
*/
|
||||
public const REGION_KEY_SEPARATOR = '_';
|
||||
|
||||
/** @var CacheAdapter */
|
||||
protected $cache;
|
||||
|
||||
/** @var string */
|
||||
protected $name;
|
||||
|
||||
/** @var int */
|
||||
protected $lifetime = 0;
|
||||
private const REGION_PREFIX = 'DC2_REGION_';
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param int $lifetime
|
||||
* @deprecated since 2.11, this property will be removed in 3.0.
|
||||
*
|
||||
* @var LegacyCache
|
||||
*/
|
||||
public function __construct($name, CacheAdapter $cache, $lifetime = 0)
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @internal since 2.11, this property will be private in 3.0.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @internal since 2.11, this property will be private in 3.0.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $lifetime = 0;
|
||||
|
||||
/** @var CacheItemPoolInterface */
|
||||
private $cacheItemPool;
|
||||
|
||||
/**
|
||||
* @param CacheItemPoolInterface $cacheItemPool
|
||||
*/
|
||||
public function __construct(string $name, $cacheItemPool, int $lifetime = 0)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
$this->name = (string) $name;
|
||||
$this->lifetime = (int) $lifetime;
|
||||
if ($cacheItemPool instanceof LegacyCache) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/9322',
|
||||
'Passing an instance of %s to %s is deprecated, pass a %s instead.',
|
||||
get_debug_type($cacheItemPool),
|
||||
__METHOD__,
|
||||
CacheItemPoolInterface::class
|
||||
);
|
||||
|
||||
$this->cache = $cacheItemPool;
|
||||
$this->cacheItemPool = CacheAdapter::wrap($cacheItemPool);
|
||||
} elseif (! $cacheItemPool instanceof CacheItemPoolInterface) {
|
||||
throw new TypeError(sprintf(
|
||||
'%s: Parameter #2 is expected to be an instance of %s, got %s.',
|
||||
__METHOD__,
|
||||
CacheItemPoolInterface::class,
|
||||
get_debug_type($cacheItemPool)
|
||||
));
|
||||
} else {
|
||||
$this->cache = DoctrineProvider::wrap($cacheItemPool);
|
||||
$this->cacheItemPool = $cacheItemPool;
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
$this->lifetime = $lifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,6 +103,8 @@ class DefaultRegion implements Region
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @return CacheProvider
|
||||
*/
|
||||
public function getCache()
|
||||
@@ -65,7 +117,7 @@ class DefaultRegion implements Region
|
||||
*/
|
||||
public function contains(CacheKey $key)
|
||||
{
|
||||
return $this->cache->contains($this->getCacheEntryKey($key));
|
||||
return $this->cacheItemPool->hasItem($this->getCacheEntryKey($key));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,7 +125,8 @@ class DefaultRegion implements Region
|
||||
*/
|
||||
public function get(CacheKey $key)
|
||||
{
|
||||
$entry = $this->cache->fetch($this->getCacheEntryKey($key));
|
||||
$item = $this->cacheItemPool->getItem($this->getCacheEntryKey($key));
|
||||
$entry = $item->isHit() ? $item->get() : null;
|
||||
|
||||
if (! $entry instanceof CacheEntry) {
|
||||
return null;
|
||||
@@ -87,30 +140,33 @@ class DefaultRegion implements Region
|
||||
*/
|
||||
public function getMultiple(CollectionCacheEntry $collection)
|
||||
{
|
||||
$keys = array_map(
|
||||
Closure::fromCallable([$this, 'getCacheEntryKey']),
|
||||
$collection->identifiers
|
||||
);
|
||||
/** @var iterable<string, CacheItemInterface> $items */
|
||||
$items = $this->cacheItemPool->getItems($keys);
|
||||
if ($items instanceof Traversable) {
|
||||
$items = iterator_to_array($items);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($collection->identifiers as $key) {
|
||||
$entryKey = $this->getCacheEntryKey($key);
|
||||
$entryValue = $this->cache->fetch($entryKey);
|
||||
|
||||
if (! $entryValue instanceof CacheEntry) {
|
||||
foreach ($keys as $arrayKey => $cacheKey) {
|
||||
if (! isset($items[$cacheKey]) || ! $items[$cacheKey]->isHit()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result[] = $entryValue;
|
||||
$entry = $items[$cacheKey]->get();
|
||||
if (! $entry instanceof CacheEntry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result[$arrayKey] = $entry;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheEntryKey(CacheKey $key)
|
||||
{
|
||||
return $this->name . self::REGION_KEY_SEPARATOR . $key->hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
@@ -118,7 +174,15 @@ class DefaultRegion implements Region
|
||||
*/
|
||||
public function put(CacheKey $key, CacheEntry $entry, ?Lock $lock = null)
|
||||
{
|
||||
return $this->cache->save($this->getCacheEntryKey($key), $entry, $this->lifetime);
|
||||
$item = $this->cacheItemPool
|
||||
->getItem($this->getCacheEntryKey($key))
|
||||
->set($entry);
|
||||
|
||||
if ($this->lifetime > 0) {
|
||||
$item->expiresAfter($this->lifetime);
|
||||
}
|
||||
|
||||
return $this->cacheItemPool->save($item);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,7 +192,7 @@ class DefaultRegion implements Region
|
||||
*/
|
||||
public function evict(CacheKey $key)
|
||||
{
|
||||
return $this->cache->delete($this->getCacheEntryKey($key));
|
||||
return $this->cacheItemPool->deleteItem($this->getCacheEntryKey($key));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,13 +202,16 @@ class DefaultRegion implements Region
|
||||
*/
|
||||
public function evictAll()
|
||||
{
|
||||
if (! $this->cache instanceof ClearableCache) {
|
||||
throw new BadMethodCallException(sprintf(
|
||||
'Clearing all cache entries is not supported by the supplied cache adapter of type %s',
|
||||
get_class($this->cache)
|
||||
));
|
||||
}
|
||||
return $this->cacheItemPool->clear(self::REGION_PREFIX . $this->name);
|
||||
}
|
||||
|
||||
return $this->cache->deleteAll();
|
||||
/**
|
||||
* @internal since 2.11, this method will be private in 3.0.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheEntryKey(CacheKey $key)
|
||||
{
|
||||
return self::REGION_PREFIX . $this->name . self::REGION_KEY_SEPARATOR . strtr($key->hash, '{}()/\@:', '________');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,8 +228,6 @@ class FileLockRegion implements ConcurrentRegion
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock(CacheKey $key, Lock $lock)
|
||||
{
|
||||
|
||||
@@ -12,8 +12,7 @@ use function microtime;
|
||||
class TimestampCacheEntry implements CacheEntry
|
||||
{
|
||||
/**
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @readonly Public only for performance reasons, it should be considered immutable.
|
||||
* @var float
|
||||
*/
|
||||
public $time;
|
||||
|
||||
@@ -25,6 +25,7 @@ use Doctrine\ORM\Exception\NamedNativeQueryNotFound;
|
||||
use Doctrine\ORM\Exception\NamedQueryNotFound;
|
||||
use Doctrine\ORM\Exception\ProxyClassesAlwaysRegenerating;
|
||||
use Doctrine\ORM\Exception\UnknownEntityNamespace;
|
||||
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
|
||||
use Doctrine\ORM\Mapping\DefaultNamingStrategy;
|
||||
@@ -38,11 +39,13 @@ use Doctrine\ORM\Repository\DefaultRepositoryFactory;
|
||||
use Doctrine\ORM\Repository\RepositoryFactory;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use LogicException;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use ReflectionClass;
|
||||
|
||||
use function class_exists;
|
||||
use function method_exists;
|
||||
use function sprintf;
|
||||
use function strtolower;
|
||||
use function trim;
|
||||
|
||||
@@ -161,6 +164,14 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*/
|
||||
public function newDefaultAnnotationDriver($paths = [], $useSimpleAnnotationReader = true)
|
||||
{
|
||||
if (! class_exists(AnnotationReader::class)) {
|
||||
throw new LogicException(sprintf(
|
||||
'The annotation metadata driver cannot be enabled because the "doctrine/annotations" library'
|
||||
. ' is not installed. Please run "composer require doctrine/annotations" or choose a different'
|
||||
. ' metadata driver.'
|
||||
));
|
||||
}
|
||||
|
||||
AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php');
|
||||
|
||||
if ($useSimpleAnnotationReader) {
|
||||
@@ -256,7 +267,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*/
|
||||
public function getResultCache(): ?CacheItemPoolInterface
|
||||
{
|
||||
// Compatibility with DBAL < 3.2
|
||||
// Compatibility with DBAL 2
|
||||
if (! method_exists(parent::class, 'getResultCache')) {
|
||||
$cacheImpl = $this->getResultCacheImpl();
|
||||
|
||||
@@ -271,7 +282,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*/
|
||||
public function setResultCache(CacheItemPoolInterface $cache): void
|
||||
{
|
||||
// Compatibility with DBAL < 3.2
|
||||
// Compatibility with DBAL 2
|
||||
if (! method_exists(parent::class, 'setResultCache')) {
|
||||
$this->setResultCacheImpl(DoctrineProvider::wrap($cache));
|
||||
|
||||
@@ -510,6 +521,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* Ensures that this Configuration instance contains settings that are
|
||||
* suitable for a production environment.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws ProxyClassesAlwaysRegenerating
|
||||
@@ -518,6 +531,13 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*/
|
||||
public function ensureProductionSettings()
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/9074',
|
||||
'%s is deprecated',
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
$queryCacheImpl = $this->getQueryCacheImpl();
|
||||
|
||||
if (! $queryCacheImpl) {
|
||||
@@ -703,7 +723,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
/**
|
||||
* Sets the custom hydrator modes in one pass.
|
||||
*
|
||||
* @param array<string, class-string> $modes An array of ($modeName => $hydrator).
|
||||
* @param array<string, class-string<AbstractHydrator>> $modes An array of ($modeName => $hydrator).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -722,7 +742,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* @param string $modeName The hydration mode name.
|
||||
*
|
||||
* @return string|null The hydrator class name.
|
||||
* @psalm-return ?class-string
|
||||
* @psalm-return class-string<AbstractHydrator>|null
|
||||
*/
|
||||
public function getCustomHydrationMode($modeName)
|
||||
{
|
||||
@@ -734,7 +754,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*
|
||||
* @param string $modeName The hydration mode name.
|
||||
* @param string $hydrator The hydrator class name.
|
||||
* @psalm-param class-string $hydrator
|
||||
* @psalm-param class-string<AbstractHydrator> $hydrator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -1003,4 +1023,24 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
{
|
||||
$this->_attributes['defaultQueryHints'][$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of entity class names to be ignored by the SchemaTool
|
||||
*
|
||||
* @return list<class-string>
|
||||
*/
|
||||
public function getSchemaIgnoreClasses(): array
|
||||
{
|
||||
return $this->_attributes['schemaIgnoreClasses'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a list of entity class names to be ignored by the SchemaTool
|
||||
*
|
||||
* @param list<class-string> $schemaIgnoreClasses List of entity class names
|
||||
*/
|
||||
public function setSchemaIgnoreClasses(array $schemaIgnoreClasses): void
|
||||
{
|
||||
$this->_attributes['schemaIgnoreClasses'] = $schemaIgnoreClasses;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\Persistence\ObjectManagerDecorator;
|
||||
|
||||
use function get_class;
|
||||
use function get_debug_type;
|
||||
use function method_exists;
|
||||
use function sprintf;
|
||||
use function trigger_error;
|
||||
@@ -17,12 +17,11 @@ use const E_USER_NOTICE;
|
||||
|
||||
/**
|
||||
* Base class for EntityManager decorators
|
||||
*
|
||||
* @extends ObjectManagerDecorator<EntityManagerInterface>
|
||||
*/
|
||||
abstract class EntityManagerDecorator extends ObjectManagerDecorator implements EntityManagerInterface
|
||||
{
|
||||
/** @var EntityManagerInterface */
|
||||
protected $wrapped;
|
||||
|
||||
public function __construct(EntityManagerInterface $wrapped)
|
||||
{
|
||||
$this->wrapped = $wrapped;
|
||||
@@ -67,7 +66,7 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements
|
||||
{
|
||||
if (! method_exists($this->wrapped, 'wrapInTransaction')) {
|
||||
trigger_error(
|
||||
sprintf('Calling `transactional()` instead of `wrapInTransaction()` which is not implemented on %s', get_class($this->wrapped)),
|
||||
sprintf('Calling `transactional()` instead of `wrapInTransaction()` which is not implemented on %s', get_debug_type($this->wrapped)),
|
||||
E_USER_NOTICE
|
||||
);
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ use Throwable;
|
||||
|
||||
use function array_keys;
|
||||
use function call_user_func;
|
||||
use function get_class;
|
||||
use function get_debug_type;
|
||||
use function gettype;
|
||||
use function is_array;
|
||||
use function is_callable;
|
||||
@@ -120,7 +120,7 @@ use function sprintf;
|
||||
/**
|
||||
* The expression builder instance used to generate query expressions.
|
||||
*
|
||||
* @var Expr
|
||||
* @var Expr|null
|
||||
*/
|
||||
private $expressionBuilder;
|
||||
|
||||
@@ -134,11 +134,15 @@ use function sprintf;
|
||||
/**
|
||||
* Collection of query filters.
|
||||
*
|
||||
* @var FilterCollection
|
||||
* @var FilterCollection|null
|
||||
*/
|
||||
private $filterCollection;
|
||||
|
||||
/** @var Cache The second level cache regions API. */
|
||||
/**
|
||||
* The second level cache regions API.
|
||||
*
|
||||
* @var Cache|null
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
@@ -399,6 +403,7 @@ use function sprintf;
|
||||
* @param int|null $lockVersion The version of the entity to find when using
|
||||
* optimistic locking.
|
||||
* @psalm-param class-string<T> $className
|
||||
* @psalm-param LockMode::*|null $lockMode
|
||||
*
|
||||
* @return object|null The entity instance or NULL if the entity can not be found.
|
||||
* @psalm-return ?T
|
||||
@@ -944,7 +949,7 @@ use function sprintf;
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
'Invalid $connection argument of type %s given%s.',
|
||||
is_object($connection) ? get_class($connection) : gettype($connection),
|
||||
get_debug_type($connection),
|
||||
is_object($connection) ? '' : ': "' . $connection . '"'
|
||||
)
|
||||
);
|
||||
@@ -986,6 +991,8 @@ use function sprintf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param LockMode::* $lockMode
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
* @throws TransactionRequiredException
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,7 @@ use BadMethodCallException;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
|
||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
@@ -237,6 +238,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
* @param object $entity
|
||||
* @param int $lockMode
|
||||
* @param int|DateTimeInterface|null $lockVersion
|
||||
* @psalm-param LockMode::* $lockMode
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
@@ -282,6 +284,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
* @deprecated
|
||||
*
|
||||
* @param string|int $hydrationMode
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_* $hydrationMode
|
||||
*
|
||||
* @return AbstractHydrator
|
||||
*/
|
||||
@@ -291,6 +294,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
* Create a new instance for the given hydration mode.
|
||||
*
|
||||
* @param string|int $hydrationMode
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_* $hydrationMode
|
||||
*
|
||||
* @return AbstractHydrator
|
||||
*
|
||||
|
||||
@@ -37,8 +37,6 @@ class EntityNotFoundException extends ORMException
|
||||
|
||||
/**
|
||||
* Instance for which no identifier can be found
|
||||
*
|
||||
* @psalm-param class-string $className
|
||||
*/
|
||||
public static function noIdentifierFound(string $className): self
|
||||
{
|
||||
|
||||
@@ -8,13 +8,13 @@ use BadMethodCallException;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\Inflector\Inflector;
|
||||
use Doctrine\Inflector\InflectorFactory;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
use Doctrine\ORM\Repository\Exception\InvalidMagicMethodCall;
|
||||
use Doctrine\ORM\Repository\InvalidFindByCall;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
use function array_slice;
|
||||
@@ -39,7 +39,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
/** @var string */
|
||||
protected $_entityName;
|
||||
|
||||
/** @var EntityManager */
|
||||
/** @var EntityManagerInterface */
|
||||
protected $_em;
|
||||
|
||||
/** @var ClassMetadata */
|
||||
@@ -165,6 +165,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
* or NULL if no specific lock mode should be used
|
||||
* during the search.
|
||||
* @param int|null $lockVersion The lock version.
|
||||
* @psalm-param LockMode::*|null $lockMode
|
||||
*
|
||||
* @return object|null The entity instance or NULL if the entity can not be found.
|
||||
* @psalm-return ?T
|
||||
@@ -281,7 +282,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
* @return EntityManagerInterface
|
||||
*/
|
||||
protected function getEntityManager()
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\Persistence\Event\LifecycleEventArgs as BaseLifecycleEventArgs;
|
||||
|
||||
/**
|
||||
@@ -28,7 +28,7 @@ class LifecycleEventArgs extends BaseLifecycleEventArgs
|
||||
/**
|
||||
* Retrieves associated EntityManager.
|
||||
*
|
||||
* @return EntityManager
|
||||
* @return EntityManagerInterface
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
|
||||
@@ -4,14 +4,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\Persistence\Event\LoadClassMetadataEventArgs as BaseLoadClassMetadataEventArgs;
|
||||
|
||||
/**
|
||||
* Class that holds event arguments for a loadMetadata event.
|
||||
*
|
||||
* @method __construct(ClassMetadata $classMetadata, EntityManager $objectManager)
|
||||
* @method __construct(ClassMetadata $classMetadata, EntityManagerInterface $objectManager)
|
||||
* @method ClassMetadata getClassMetadata()
|
||||
*/
|
||||
class LoadClassMetadataEventArgs extends BaseLoadClassMetadataEventArgs
|
||||
@@ -19,7 +19,7 @@ class LoadClassMetadataEventArgs extends BaseLoadClassMetadataEventArgs
|
||||
/**
|
||||
* Retrieve associated EntityManager.
|
||||
*
|
||||
* @return EntityManager
|
||||
* @return EntityManagerInterface
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
|
||||
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
@@ -15,7 +14,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
*/
|
||||
class PostFlushEventArgs extends EventArgs
|
||||
{
|
||||
/** @var EntityManager */
|
||||
/** @var EntityManagerInterface */
|
||||
private $em;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
@@ -26,7 +25,7 @@ class PostFlushEventArgs extends EventArgs
|
||||
/**
|
||||
* Retrieves associated EntityManager.
|
||||
*
|
||||
* @return EntityManager
|
||||
* @return EntityManagerInterface
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
|
||||
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
@@ -15,7 +14,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
*/
|
||||
class PreFlushEventArgs extends EventArgs
|
||||
{
|
||||
/** @var EntityManager */
|
||||
/** @var EntityManagerInterface */
|
||||
private $em;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
@@ -24,7 +23,7 @@ class PreFlushEventArgs extends EventArgs
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
* @return EntityManagerInterface
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function get_class;
|
||||
use function get_debug_type;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
@@ -108,7 +108,7 @@ class PreUpdateEventArgs extends LifecycleEventArgs
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Field "%s" is not a valid field of the entity "%s" in PreUpdateEventArgs.',
|
||||
$field,
|
||||
get_class($this->getEntity())
|
||||
get_debug_type($this->getEntity())
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
final class EntityManagerClosed extends ORMException implements ManagerException
|
||||
{
|
||||
public static function create(): self
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Exception;
|
||||
|
||||
use function get_class;
|
||||
use function get_debug_type;
|
||||
|
||||
final class EntityMissingAssignedId extends ORMException
|
||||
{
|
||||
@@ -13,7 +13,7 @@ final class EntityMissingAssignedId extends ORMException
|
||||
*/
|
||||
public static function forField($entity, string $field): self
|
||||
{
|
||||
return new self('Entity of type ' . get_class($entity) . " is missing an assigned ID for field '" . $field . "'. " .
|
||||
return new self('Entity of type ' . get_debug_type($entity) . " is missing an assigned ID for field '" . $field . "'. " .
|
||||
'The identifier generation strategy for this entity requires the ID field to be populated before ' .
|
||||
'EntityManager#persist() is called. If you want automatically generated identifiers instead ' .
|
||||
'you need to adjust the metadata mapping accordingly.');
|
||||
|
||||
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Exception;
|
||||
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use LogicException;
|
||||
|
||||
final class InvalidEntityRepository extends ORMException implements ConfigurationException
|
||||
{
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
final class MissingIdentifierField extends ORMException implements ManagerException
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
final class MissingMappingDriverImplementation extends ORMException implements ManagerException
|
||||
{
|
||||
public static function create(): self
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
final class NamedQueryNotFound extends ORMException implements ConfigurationException
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
final class ProxyClassesAlwaysRegenerating extends ORMException implements ConfigurationException
|
||||
{
|
||||
public static function create(): self
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
final class UnknownEntityNamespace extends ORMException implements ConfigurationException
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Exception;
|
||||
|
||||
use LogicException;
|
||||
|
||||
use function implode;
|
||||
use function sprintf;
|
||||
|
||||
|
||||
@@ -4,10 +4,54 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
|
||||
use function get_debug_type;
|
||||
use function sprintf;
|
||||
|
||||
abstract class AbstractIdGenerator
|
||||
{
|
||||
/** @var bool */
|
||||
private $alreadyDelegatedToGenerateId = false;
|
||||
|
||||
/**
|
||||
* Generates an identifier for an entity.
|
||||
*
|
||||
* @deprecated Call {@see generateId()} instead.
|
||||
*
|
||||
* @param object|null $entity
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
{
|
||||
if ($this->alreadyDelegatedToGenerateId) {
|
||||
throw new LogicException(sprintf(
|
||||
'Endless recursion detected in %s. Please implement generateId() without calling the parent implementation.',
|
||||
get_debug_type($this)
|
||||
));
|
||||
}
|
||||
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/9325',
|
||||
'%s::generate() is deprecated, call generateId() instead.',
|
||||
get_debug_type($this)
|
||||
);
|
||||
|
||||
$this->alreadyDelegatedToGenerateId = true;
|
||||
|
||||
try {
|
||||
return $this->generateId($em, $entity);
|
||||
} finally {
|
||||
$this->alreadyDelegatedToGenerateId = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an identifier for an entity.
|
||||
*
|
||||
@@ -15,11 +59,26 @@ abstract class AbstractIdGenerator
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function generate(EntityManager $em, $entity);
|
||||
public function generateId(EntityManagerInterface $em, $entity)
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/9325',
|
||||
'Not implementing %s in %s is deprecated.',
|
||||
__FUNCTION__,
|
||||
get_debug_type($this)
|
||||
);
|
||||
|
||||
if (! $em instanceof EntityManager) {
|
||||
throw new InvalidArgumentException('Unsupported entity manager implementation.');
|
||||
}
|
||||
|
||||
return $this->generate($em, $entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this generator is a post-insert generator which means that
|
||||
* {@link generate()} must be called after the entity has been inserted
|
||||
* {@link generateId()} must be called after the entity has been inserted
|
||||
* into the database.
|
||||
*
|
||||
* By default, this method returns FALSE. Generators that have this requirement
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Exception\EntityMissingAssignedId;
|
||||
|
||||
use function get_class;
|
||||
@@ -17,11 +17,11 @@ class AssignedGenerator extends AbstractIdGenerator
|
||||
/**
|
||||
* Returns the identifier assigned to the given entity.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws EntityMissingAssignedId
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
public function generateId(EntityManagerInterface $em, $entity)
|
||||
{
|
||||
$class = $em->getClassMetadata(get_class($entity));
|
||||
$idFields = $class->getIdentifierFieldNames();
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* Id generator that obtains IDs from special "identity" columns. These are columns
|
||||
@@ -16,7 +16,7 @@ class BigIntegerIdentityGenerator extends AbstractIdGenerator
|
||||
/**
|
||||
* The name of the sequence to pass to lastInsertId(), if any.
|
||||
*
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
private $sequenceName;
|
||||
|
||||
@@ -33,7 +33,7 @@ class BigIntegerIdentityGenerator extends AbstractIdGenerator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
public function generateId(EntityManagerInterface $em, $entity)
|
||||
{
|
||||
return (string) $em->getConnection()->lastInsertId($this->sequenceName);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* Id generator that obtains IDs from special "identity" columns. These are columns
|
||||
@@ -33,7 +33,7 @@ class IdentityGenerator extends AbstractIdGenerator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
public function generateId(EntityManagerInterface $em, $entity)
|
||||
{
|
||||
return (int) $em->getConnection()->lastInsertId($this->sequenceName);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Serializable;
|
||||
|
||||
use function serialize;
|
||||
@@ -50,15 +51,18 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
public function generateId(EntityManagerInterface $em, $entity)
|
||||
{
|
||||
if ($this->_maxValue === null || $this->_nextValue === $this->_maxValue) {
|
||||
// Allocate new values
|
||||
$conn = $em->getConnection();
|
||||
$sql = $conn->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName);
|
||||
$connection = $em->getConnection();
|
||||
$sql = $connection->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName);
|
||||
|
||||
// Using `query` to force usage of the master server in MasterSlaveConnection
|
||||
$this->_nextValue = (int) $conn->executeQuery($sql)->fetchOne();
|
||||
if ($connection instanceof PrimaryReadReplicaConnection) {
|
||||
$connection->ensureConnectedToPrimary();
|
||||
}
|
||||
|
||||
$this->_nextValue = (int) $connection->executeQuery($sql)->fetchOne();
|
||||
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* Id generator that uses a single-row database table and a hi/lo algorithm.
|
||||
@@ -43,8 +43,8 @@ class TableGenerator extends AbstractIdGenerator
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function generate(
|
||||
EntityManager $em,
|
||||
public function generateId(
|
||||
EntityManagerInterface $em,
|
||||
$entity
|
||||
) {
|
||||
if ($this->_maxValue === null || $this->_nextValue === $this->_maxValue) {
|
||||
|
||||
@@ -4,9 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Exception\NotSupported;
|
||||
|
||||
use function method_exists;
|
||||
@@ -37,11 +38,15 @@ class UuidGenerator extends AbstractIdGenerator
|
||||
*
|
||||
* @throws NotSupported
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
public function generateId(EntityManagerInterface $em, $entity)
|
||||
{
|
||||
$conn = $em->getConnection();
|
||||
$sql = 'SELECT ' . $conn->getDatabasePlatform()->getGuidExpression();
|
||||
$connection = $em->getConnection();
|
||||
$sql = 'SELECT ' . $connection->getDatabasePlatform()->getGuidExpression();
|
||||
|
||||
return $conn->executeQuery($sql)->fetchOne();
|
||||
if ($connection instanceof PrimaryReadReplicaConnection) {
|
||||
$connection->ensureConnectedToPrimary();
|
||||
}
|
||||
|
||||
return $connection->executeQuery($sql)->fetchOne();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ abstract class AbstractHydrator
|
||||
* Hydrates all rows returned by the passed statement instance at once.
|
||||
*
|
||||
* @param Result|ResultStatement $stmt
|
||||
* @param object $resultSetMapping
|
||||
* @param ResultSetMapping $resultSetMapping
|
||||
* @psalm-param array<string, string> $hints
|
||||
*
|
||||
* @return mixed[]
|
||||
@@ -277,10 +277,19 @@ abstract class AbstractHydrator
|
||||
* Hydrates a single row returned by the current statement instance during
|
||||
* row-by-row hydration with {@link iterate()} or {@link toIterable()}.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @return mixed[]|false
|
||||
*/
|
||||
public function hydrateRow()
|
||||
{
|
||||
Deprecation::triggerIfCalledFromOutside(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/9072',
|
||||
'%s is deprecated.',
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
$row = $this->statement()->fetchAssociative();
|
||||
|
||||
if ($row === false) {
|
||||
|
||||
@@ -32,12 +32,12 @@ class SingleScalarHydrator extends AbstractHydrator
|
||||
throw new NonUniqueResultException('The query returned multiple rows. Change the query or use a different result function like getScalarResult().');
|
||||
}
|
||||
|
||||
if (count($data[key($data)]) > 1) {
|
||||
$result = $this->gatherScalarRowData($data[key($data)]);
|
||||
|
||||
if (count($result) > 1) {
|
||||
throw new NonUniqueResultException('The query returned a row containing multiple columns. Change the query or use a different result function like getScalarResult().');
|
||||
}
|
||||
|
||||
$result = $this->gatherScalarRowData($data[key($data)]);
|
||||
|
||||
return array_shift($result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ namespace Doctrine\ORM\Internal;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Platforms\DB2Platform;
|
||||
use Doctrine\DBAL\Platforms\OraclePlatform;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
|
||||
use function method_exists;
|
||||
@@ -25,7 +24,7 @@ trait SQLResultCasing
|
||||
return strtoupper($column);
|
||||
}
|
||||
|
||||
if ($platform instanceof PostgreSQL94Platform || $platform instanceof PostgreSQLPlatform) {
|
||||
if ($platform instanceof PostgreSQLPlatform) {
|
||||
return strtolower($column);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
/**
|
||||
* A lazy collection that allows a fast count when using criteria object
|
||||
@@ -38,6 +39,7 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
if ($this->isInitialized()) {
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Doctrine\ORM\Mapping;
|
||||
* This annotation is used to override association mapping of property for an entity relationship.
|
||||
*
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
final class AssociationOverride implements Annotation
|
||||
@@ -22,29 +23,64 @@ final class AssociationOverride implements Annotation
|
||||
/**
|
||||
* The join column that is being mapped to the persistent attribute.
|
||||
*
|
||||
* @var array<\Doctrine\ORM\Mapping\JoinColumn>
|
||||
* @var array<\Doctrine\ORM\Mapping\JoinColumn>|null
|
||||
*/
|
||||
public $joinColumns;
|
||||
|
||||
/**
|
||||
* The join column that is being mapped to the persistent attribute.
|
||||
*
|
||||
* @var array<\Doctrine\ORM\Mapping\JoinColumn>|null
|
||||
*/
|
||||
public $inverseJoinColumns;
|
||||
|
||||
/**
|
||||
* The join table that maps the relationship.
|
||||
*
|
||||
* @var \Doctrine\ORM\Mapping\JoinTable
|
||||
* @var \Doctrine\ORM\Mapping\JoinTable|null
|
||||
*/
|
||||
public $joinTable;
|
||||
|
||||
/**
|
||||
* The name of the association-field on the inverse-side.
|
||||
*
|
||||
* @var string
|
||||
* @var ?string
|
||||
*/
|
||||
public $inversedBy;
|
||||
|
||||
/**
|
||||
* The fetching strategy to use for the association.
|
||||
*
|
||||
* @var string
|
||||
* @var ?string
|
||||
* @Enum({"LAZY", "EAGER", "EXTRA_LAZY"})
|
||||
*/
|
||||
public $fetch;
|
||||
|
||||
/**
|
||||
* @param JoinColumn|array<JoinColumn> $joinColumns
|
||||
* @param JoinColumn|array<JoinColumn> $inverseJoinColumns
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
$joinColumns = null,
|
||||
$inverseJoinColumns = null,
|
||||
?JoinTable $joinTable = null,
|
||||
?string $inversedBy = null,
|
||||
?string $fetch = null
|
||||
) {
|
||||
if ($joinColumns instanceof JoinColumn) {
|
||||
$joinColumns = [$joinColumns];
|
||||
}
|
||||
|
||||
if ($inverseJoinColumns instanceof JoinColumn) {
|
||||
$inverseJoinColumns = [$inverseJoinColumns];
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
$this->joinColumns = $joinColumns;
|
||||
$this->inverseJoinColumns = $inverseJoinColumns;
|
||||
$this->joinTable = $joinTable;
|
||||
$this->inversedBy = $inversedBy;
|
||||
$this->fetch = $fetch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,18 +4,42 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* This annotation is used to override association mappings of relationship properties.
|
||||
*
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class AssociationOverrides implements Annotation
|
||||
{
|
||||
/**
|
||||
* Mapping overrides of relationship properties.
|
||||
*
|
||||
* @var array<\Doctrine\ORM\Mapping\AssociationOverride>
|
||||
* @var array<AssociationOverride>
|
||||
*/
|
||||
public $value;
|
||||
public $overrides = [];
|
||||
|
||||
/**
|
||||
* @param array<AssociationOverride>|AssociationOverride $overrides
|
||||
*/
|
||||
public function __construct($overrides)
|
||||
{
|
||||
if (! is_array($overrides)) {
|
||||
$overrides = [$overrides];
|
||||
}
|
||||
|
||||
foreach ($overrides as $override) {
|
||||
if (! ($override instanceof AssociationOverride)) {
|
||||
throw MappingException::invalidOverrideType('AssociationOverride', $override);
|
||||
}
|
||||
|
||||
$this->overrides[] = $override;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* This annotation is used to override the mapping of a entity property.
|
||||
*
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
|
||||
final class AttributeOverride implements Annotation
|
||||
{
|
||||
/**
|
||||
@@ -28,4 +26,10 @@ final class AttributeOverride implements Annotation
|
||||
* @var \Doctrine\ORM\Mapping\Column
|
||||
*/
|
||||
public $column;
|
||||
|
||||
public function __construct(string $name, Column $column)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->column = $column;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,18 +4,42 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* This annotation is used to override the mapping of a entity property.
|
||||
*
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class AttributeOverrides implements Annotation
|
||||
{
|
||||
/**
|
||||
* One or more field or property mapping overrides.
|
||||
*
|
||||
* @var array<\Doctrine\ORM\Mapping\AttributeOverride>
|
||||
* @var array<AttributeOverride>
|
||||
*/
|
||||
public $value;
|
||||
public $overrides = [];
|
||||
|
||||
/**
|
||||
* @param array<AttributeOverride>|AttributeOverride $overrides
|
||||
*/
|
||||
public function __construct($overrides)
|
||||
{
|
||||
if (! is_array($overrides)) {
|
||||
$overrides = [$overrides];
|
||||
}
|
||||
|
||||
foreach ($overrides as $override) {
|
||||
if (! ($override instanceof AttributeOverride)) {
|
||||
throw MappingException::invalidOverrideType('AttributeOverride', $override);
|
||||
}
|
||||
|
||||
$this->overrides[] = $override;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,9 +59,9 @@ class ClassMetadataBuilder
|
||||
/**
|
||||
* Adds and embedded class
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @param string $class
|
||||
* @param string|null $columnPrefix
|
||||
* @param string $fieldName
|
||||
* @param string $class
|
||||
* @param string|false|null $columnPrefix
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
|
||||
@@ -110,6 +110,34 @@ class FieldBuilder
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets insertable.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function insertable(bool $flag = true): self
|
||||
{
|
||||
if (! $flag) {
|
||||
$this->mapping['notInsertable'] = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets updatable.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function updatable(bool $flag = true): self
|
||||
{
|
||||
if (! $flag) {
|
||||
$this->mapping['notUpdatable'] = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets scale.
|
||||
*
|
||||
|
||||
@@ -20,7 +20,6 @@ use Doctrine\ORM\Id\SequenceGenerator;
|
||||
use Doctrine\ORM\Id\UuidGenerator;
|
||||
use Doctrine\ORM\Mapping\Exception\CannotGenerateIds;
|
||||
use Doctrine\ORM\Mapping\Exception\InvalidCustomGenerator;
|
||||
use Doctrine\ORM\Mapping\Exception\TableGeneratorNotImplementedYet;
|
||||
use Doctrine\ORM\Mapping\Exception\UnknownGeneratorType;
|
||||
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
|
||||
use Doctrine\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
|
||||
@@ -46,9 +45,7 @@ use function substr;
|
||||
* metadata mapping information of a class which describes how a class should be mapped
|
||||
* to a relational database.
|
||||
*
|
||||
* @method ClassMetadata[] getAllMetadata()
|
||||
* @method ClassMetadata[] getLoadedMetadata()
|
||||
* @method ClassMetadata getMetadataFor($className)
|
||||
* @extends AbstractClassMetadataFactory<ClassMetadata>
|
||||
*/
|
||||
class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
{
|
||||
@@ -91,14 +88,16 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
protected function onNotFoundMetadata($className)
|
||||
{
|
||||
if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$eventArgs = new OnClassMetadataNotFoundEventArgs($className, $this->em);
|
||||
|
||||
$this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
|
||||
$classMetadata = $eventArgs->getFoundMetadata();
|
||||
assert($classMetadata instanceof ClassMetadata || $classMetadata === null);
|
||||
|
||||
return $eventArgs->getFoundMetadata();
|
||||
return $classMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -643,11 +642,13 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-return ClassMetadata::GENERATOR_TYPE_SEQUENCE|ClassMetadata::GENERATOR_TYPE_IDENTITY
|
||||
*/
|
||||
private function determineIdGeneratorStrategy(AbstractPlatform $platform): int
|
||||
{
|
||||
if (
|
||||
$platform instanceof Platforms\OraclePlatform
|
||||
|| $platform instanceof Platforms\PostgreSQL94Platform
|
||||
|| $platform instanceof Platforms\PostgreSQLPlatform
|
||||
) {
|
||||
return ClassMetadata::GENERATOR_TYPE_SEQUENCE;
|
||||
@@ -667,7 +668,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
private function truncateSequenceName(string $schemaElementName): string
|
||||
{
|
||||
$platform = $this->getTargetPlatform();
|
||||
if (! in_array($platform->getName(), ['oracle', 'sqlanywhere'], true)) {
|
||||
if (! $platform instanceof Platforms\OraclePlatform && ! $platform instanceof Platforms\SQLAnywherePlatform) {
|
||||
return $schemaElementName;
|
||||
}
|
||||
|
||||
@@ -738,7 +739,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
*/
|
||||
protected function isEntity(ClassMetadataInterface $class)
|
||||
{
|
||||
return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false;
|
||||
return ! $class->isMappedSuperclass;
|
||||
}
|
||||
|
||||
private function getTargetPlatform(): Platforms\AbstractPlatform
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user