mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 06:52:09 +01:00
Compare commits
136 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4af1aa3177 | ||
|
|
f4d5283f70 | ||
|
|
976fe5bc0d | ||
|
|
582b222b00 | ||
|
|
1ffb9152f7 | ||
|
|
51faa6ddb7 | ||
|
|
692c3e1b45 | ||
|
|
e410180c6e | ||
|
|
4476b05d59 | ||
|
|
343b0ae576 | ||
|
|
de69f60c6a | ||
|
|
2a653b05a0 | ||
|
|
c6831c6b07 | ||
|
|
33da4d84eb | ||
|
|
c1b373b931 | ||
|
|
b6cff1aa1c | ||
|
|
d2206152bb | ||
|
|
a34dc0a0e3 | ||
|
|
881a7b3b69 | ||
|
|
82bbb1dc4a | ||
|
|
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 | ||
|
|
e369cb6e73 | ||
|
|
ec391be4f2 | ||
|
|
697e23422f | ||
|
|
e487b6fe2b | ||
|
|
656f881756 | ||
|
|
cd2aa487a5 | ||
|
|
ec63f5d32a | ||
|
|
952ccc5fc8 | ||
|
|
580b9196e6 | ||
|
|
0d911b9381 | ||
|
|
c6d8aecc0f | ||
|
|
fdd3d112b0 | ||
|
|
f9f453f4d7 | ||
|
|
f508a4bb71 | ||
|
|
5d0fbc47d0 | ||
|
|
1e977426eb | ||
|
|
4117ca349f | ||
|
|
2d475c9bb3 | ||
|
|
6f54011e7b | ||
|
|
7190ac5127 | ||
|
|
ceaefcb18d | ||
|
|
844ce77cae | ||
|
|
cf3a185b62 | ||
|
|
15999758a7 | ||
|
|
8c6fc5ae52 | ||
|
|
40a203843d | ||
|
|
5901848944 | ||
|
|
133cc95f33 | ||
|
|
98d77043d8 | ||
|
|
70dcffa025 | ||
|
|
c94a9b1d8b | ||
|
|
92ff9c9108 | ||
|
|
f1483f848c | ||
|
|
ea4c9b21b7 | ||
|
|
130c27c1da | ||
|
|
1e9973a0c0 | ||
|
|
91761738fd | ||
|
|
95d434d003 | ||
|
|
70c651ebb7 | ||
|
|
8cb62a616a | ||
|
|
6d306c1946 | ||
|
|
bea5e7166c | ||
|
|
003090b70c | ||
|
|
56e0ac02af | ||
|
|
42195060e6 | ||
|
|
ac5aea1c81 | ||
|
|
a75605b8c3 | ||
|
|
eabb7f84e9 | ||
|
|
f0a20dbc9c | ||
|
|
6857a2e8d4 | ||
|
|
5e8b34ae30 | ||
|
|
a9b682b7c0 | ||
|
|
0aadc456dc | ||
|
|
2488b4c50c | ||
|
|
ed642c72c9 | ||
|
|
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",
|
||||
|
||||
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:
|
||||
|
||||
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
|
||||
|
||||
@@ -37,8 +37,7 @@ will have to run a composer installation in the project:
|
||||
```sh
|
||||
git clone git@github.com:doctrine/orm.git
|
||||
cd orm
|
||||
curl -sS https://getcomposer.org/installer | php --
|
||||
./composer.phar install
|
||||
composer install
|
||||
```
|
||||
|
||||
To run the testsuite against another database, copy the ``phpunit.xml.dist``
|
||||
|
||||
18
README.md
18
README.md
@@ -1,7 +1,9 @@
|
||||
| [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] |
|
||||
|
||||
[<h1 align="center">🇺🇦 UKRAINE NEEDS YOUR HELP NOW!</h1>](https://www.doctrine-project.org/stop-war.html)
|
||||
|
||||
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
|
||||
@@ -13,17 +15,17 @@ without requiring unnecessary code duplication.
|
||||
## More resources:
|
||||
|
||||
* [Website](http://www.doctrine-project.org)
|
||||
* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
|
||||
* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/stable/index.html)
|
||||
|
||||
|
||||
[3.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.0.x
|
||||
[3.0]: https://github.com/doctrine/orm/tree/3.0.x
|
||||
[3.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.0.x/graph/badge.svg
|
||||
[3.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.0.x
|
||||
[2.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
|
||||
|
||||
@@ -10,8 +10,8 @@ we cannot protect you from SQL injection.
|
||||
Please read the documentation chapter on Security in Doctrine DBAL and ORM to
|
||||
understand the assumptions we make.
|
||||
|
||||
- [DBAL Security Page](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/security.html)
|
||||
- [ORM Security Page](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/security.html)
|
||||
- [DBAL Security Page](https://www.doctrine-project.org/projects/doctrine-dbal/en/stable/reference/security.html)
|
||||
- [ORM Security Page](https://www.doctrine-project.org/projects/doctrine-orm/en/stable/reference/security.html)
|
||||
|
||||
If you find a Security bug in Doctrine, please report it on Jira and change the
|
||||
Security Level to "Security Issues". It will be visible to Doctrine Core
|
||||
|
||||
60
UPGRADE.md
60
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
|
||||
|
||||
@@ -237,8 +289,8 @@ If you would still like to perform batching operations over small `UnitOfWork`
|
||||
instances, it is suggested to follow these paths instead:
|
||||
|
||||
* eagerly use `EntityManager#clear()` in conjunction with a specific second level
|
||||
cache configuration (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/second-level-cache.html)
|
||||
* use an explicit change tracking policy (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/change-tracking-policies.html)
|
||||
cache configuration (see http://docs.doctrine-project.org/projects/doctrine-orm/en/stable/reference/second-level-cache.html)
|
||||
* use an explicit change tracking policy (see http://docs.doctrine-project.org/projects/doctrine-orm/en/stable/reference/change-tracking-policies.html)
|
||||
|
||||
## Deprecated `YAML` mapping drivers.
|
||||
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -20,14 +20,13 @@
|
||||
"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",
|
||||
@@ -43,12 +42,12 @@
|
||||
"doctrine/annotations": "^1.13",
|
||||
"doctrine/coding-standard": "^9.0",
|
||||
"phpbench/phpbench": "^0.16.10 || ^1.0",
|
||||
"phpstan/phpstan": "1.3.3",
|
||||
"phpstan/phpstan": "~1.4.10 || 1.5.0",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
|
||||
"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.18.1"
|
||||
"vimeo/psalm": "4.22.0"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/annotations": "<1.13 || >= 2.0"
|
||||
|
||||
201
docs/en/conf.py
201
docs/en/conf.py
@@ -1,201 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Doctrine 2 ORM documentation build configuration file, created by
|
||||
# sphinx-quickstart on Fri Dec 3 18:10:24 2010.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os, datetime
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
sys.path.append(os.path.abspath('_exts'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['configurationblock']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Doctrine 2 ORM'
|
||||
copyright = u'2010-%y, Doctrine Project Team'.format(datetime.date.today)
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '2'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '2'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
language = 'en'
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of documents that shouldn't be included in the build.
|
||||
#unused_docs = []
|
||||
|
||||
# List of directories, relative to source directory, that shouldn't be searched
|
||||
# for source files.
|
||||
exclude_trees = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
show_authors = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
html_theme = 'doctrine'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = ['_theme']
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_use_modindex = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = ''
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Doctrine2ORMdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'Doctrine2ORM.tex', u'Doctrine 2 ORM Documentation',
|
||||
u'Doctrine Project Team', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_use_modindex = True
|
||||
|
||||
primary_domain = "dcorm"
|
||||
|
||||
def linkcode_resolve(domain, info):
|
||||
if domain == 'dcorm':
|
||||
return 'http://'
|
||||
return None
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -131,8 +131,8 @@ generation of a DateDiff FunctionNode somewhere in the AST of the
|
||||
dql statement.
|
||||
|
||||
The ``ArithmeticPrimary`` method call is the most common
|
||||
denominator of valid EBNF tokens taken from the
|
||||
`DQL EBNF grammar <https://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#ebnf>`_
|
||||
denominator of valid EBNF tokens taken from the :ref:`DQL EBNF grammar
|
||||
<dql_ebnf_grammar>`
|
||||
that matches our requirements for valid input into the DateDiff Dql
|
||||
function. Picking the right tokens for your methods is a tricky
|
||||
business, but the EBNF grammar is pretty helpful finding it, as is
|
||||
|
||||
@@ -3,8 +3,8 @@ Implementing Wakeup or Clone
|
||||
|
||||
.. sectionauthor:: Roman Borschel (roman@code-factory.org)
|
||||
|
||||
As explained in the
|
||||
`restrictions for entity classes in the manual <https://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/architecture.html#entities>`_,
|
||||
As explained in the :ref:`restrictions for entity classes in the manual
|
||||
<terminology_entities>`,
|
||||
it is usually not allowed for an entity to implement ``__wakeup``
|
||||
or ``__clone``, because Doctrine makes special use of them.
|
||||
However, it is quite easy to make use of these methods in a safe
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -66,6 +66,8 @@ The root namespace of the ORM package is ``Doctrine\ORM``.
|
||||
Terminology
|
||||
-----------
|
||||
|
||||
.. _terminology_entities:
|
||||
|
||||
Entities
|
||||
~~~~~~~~
|
||||
|
||||
@@ -82,9 +84,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 +99,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 +168,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]
|
||||
@@ -89,6 +178,18 @@ Optional parameters:
|
||||
- **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,6 +260,15 @@ 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]
|
||||
@@ -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,7 +656,8 @@ 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 parameters:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ If you want to configure Doctrine in more detail, take a look at the :doc:`Advan
|
||||
.. note::
|
||||
|
||||
You can learn more about the database connection configuration in the
|
||||
`Doctrine DBAL connection configuration reference <https://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html>`_.
|
||||
`Doctrine DBAL connection configuration reference <https://docs.doctrine-project.org/projects/doctrine-dbal/en/stable/reference/configuration.html>`_.
|
||||
|
||||
Setting up the Commandline Tool
|
||||
-------------------------------
|
||||
|
||||
@@ -1531,6 +1531,8 @@ Given that there are 10 users and corresponding addresses in the database the ex
|
||||
a one-by-one basis once they are accessed.
|
||||
|
||||
|
||||
.. _dql_ebnf_grammar:
|
||||
|
||||
EBNF
|
||||
----
|
||||
|
||||
@@ -1724,7 +1726,7 @@ Literal Values
|
||||
.. code-block:: php
|
||||
|
||||
Literal ::= string | char | integer | float | boolean
|
||||
InParameter ::= Literal | InputParameter
|
||||
InParameter ::= ArithmeticExpression | InputParameter
|
||||
|
||||
Input Parameter
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -121,16 +121,17 @@ 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:
|
||||
|
||||
@@ -141,33 +142,33 @@ Events Overview
|
||||
| Event | Dispatched by | Lifecycle | Passed |
|
||||
| | | Callback | Argument |
|
||||
+=================================================================+=======================+===========+=====================================+
|
||||
| :ref:`preRemove<reference-events-pre-remove>` | ``$em->remove()`` | Yes | `_LifecycleEventArgs`_ |
|
||||
| :ref:`preRemove<reference-events-pre-remove>` | ``$em->remove()`` | Yes | `LifecycleEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postRemove<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `_LifecycleEventArgs`_ |
|
||||
| :ref:`postRemove<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `LifecycleEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`prePersist<reference-events-pre-persist>` | ``$em->persist()`` | 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:`postPersist<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `LifecycleEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`preUpdate<reference-events-pre-update>` | ``$em->flush()`` | Yes | `_PreUpdateEventArgs`_ |
|
||||
| :ref:`preUpdate<reference-events-pre-update>` | ``$em->flush()`` | Yes | `PreUpdateEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postUpdate<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `_LifecycleEventArgs`_ |
|
||||
| :ref:`postUpdate<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `LifecycleEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postLoad<reference-events-post-load>` | Loading from database | 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` |
|
||||
| :ref:`loadClassMetadata<reference-events-load-class-metadata>` | Loading of mapping | No | `LoadClassMetadataEventArgs`_ |
|
||||
| | metadata | | |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| ``onClassMetadataNotFound`` | ``MappingException`` | No | `_OnClassMetadataNotFoundEventArgs` |
|
||||
| ``onClassMetadataNotFound`` | ``MappingException`` | No | `OnClassMetadataNotFoundEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`preFlush<reference-events-pre-flush>` | ``$em->flush()`` | Yes | `_PreFlushEventArgs`_ |
|
||||
| :ref:`preFlush<reference-events-pre-flush>` | ``$em->flush()`` | Yes | `PreFlushEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`onFlush<reference-events-on-flush>` | ``$em->flush()`` | No | `_OnFlushEventArgs` |
|
||||
| :ref:`onFlush<reference-events-on-flush>` | ``$em->flush()`` | No | `OnFlushEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`postFlush<reference-events-post-flush>` | ``$em->flush()`` | No | `_PostFlushEventArgs` |
|
||||
| :ref:`postFlush<reference-events-post-flush>` | ``$em->flush()`` | No | `PostFlushEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`onClear<reference-events-on-clear>` | ``$em->clear()`` | No | `_OnClearEventArgs` |
|
||||
| :ref:`onClear<reference-events-on-clear>` | ``$em->clear()`` | No | `OnClearEventArgs`_ |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
|
||||
Naming convention
|
||||
|
||||
@@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -172,7 +172,7 @@ To enable the second-level-cache, you should provide a cache factory.
|
||||
|
||||
<?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);
|
||||
|
||||
@@ -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" />
|
||||
@@ -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
|
||||
);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ use Doctrine\ORM\Exception\InvalidHydrationMode;
|
||||
use Doctrine\ORM\Exception\MismatchedEventManager;
|
||||
use Doctrine\ORM\Exception\MissingIdentifierField;
|
||||
use Doctrine\ORM\Exception\MissingMappingDriverImplementation;
|
||||
use Doctrine\ORM\Exception\ORMException;
|
||||
use Doctrine\ORM\Exception\UnrecognizedIdentifierFields;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
@@ -32,7 +33,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;
|
||||
@@ -59,7 +60,7 @@ use function sprintf;
|
||||
* $entityManager = EntityManager::create($dbParams, $config);
|
||||
*
|
||||
* For more information see
|
||||
* {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html}
|
||||
* {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/stable/reference/configuration.html}
|
||||
*
|
||||
* You should never attempt to inherit from the EntityManager: Inheritance
|
||||
* is not a valid extension point for the EntityManager. Instead you
|
||||
@@ -120,7 +121,7 @@ use function sprintf;
|
||||
/**
|
||||
* The expression builder instance used to generate query expressions.
|
||||
*
|
||||
* @var Expr
|
||||
* @var Expr|null
|
||||
*/
|
||||
private $expressionBuilder;
|
||||
|
||||
@@ -134,11 +135,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 +404,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
|
||||
@@ -773,7 +779,7 @@ use function sprintf;
|
||||
* @return ObjectRepository|EntityRepository The repository class.
|
||||
* @psalm-return EntityRepository<T>
|
||||
*
|
||||
* @template T
|
||||
* @template T of object
|
||||
*/
|
||||
public function getRepository($entityName)
|
||||
{
|
||||
@@ -944,7 +950,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 +992,8 @@ use function sprintf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param LockMode::* $lockMode
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
* @throws TransactionRequiredException
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,8 @@ use BadMethodCallException;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\Exception\ORMException;
|
||||
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
|
||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
@@ -30,7 +32,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
*
|
||||
* @psalm-return EntityRepository<T>
|
||||
*
|
||||
* @template T
|
||||
* @template T of object
|
||||
*/
|
||||
public function getRepository($className);
|
||||
|
||||
@@ -237,6 +239,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
* @param object $entity
|
||||
* @param int $lockMode
|
||||
* @param int|DateTimeInterface|null $lockVersion
|
||||
* @psalm-param LockMode::* $lockMode
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
@@ -282,6 +285,7 @@ interface EntityManagerInterface extends ObjectManager
|
||||
* @deprecated
|
||||
*
|
||||
* @param string|int $hydrationMode
|
||||
* @psalm-param string|AbstractQuery::HYDRATE_* $hydrationMode
|
||||
*
|
||||
* @return AbstractHydrator
|
||||
*/
|
||||
@@ -291,6 +295,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;
|
||||
@@ -30,7 +30,7 @@ use function substr;
|
||||
* This class is designed for inheritance and users can subclass this class to
|
||||
* write their own repositories with business-specific methods to locate entities.
|
||||
*
|
||||
* @template T
|
||||
* @template T of object
|
||||
* @template-implements Selectable<int,T>
|
||||
* @template-implements ObjectRepository<T>
|
||||
*/
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Serializable;
|
||||
|
||||
use function serialize;
|
||||
@@ -51,7 +51,7 @@ 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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -7,7 +7,7 @@ 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;
|
||||
@@ -38,7 +38,7 @@ class UuidGenerator extends AbstractIdGenerator
|
||||
*
|
||||
* @throws NotSupported
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
public function generateId(EntityManagerInterface $em, $entity)
|
||||
{
|
||||
$connection = $em->getConnection();
|
||||
$sql = 'SELECT ' . $connection->getDatabasePlatform()->getGuidExpression();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Doctrine\ORM\Internal\Hydration;
|
||||
use Doctrine\DBAL\Driver\Exception;
|
||||
use Doctrine\ORM\Exception\MultipleSelectorsFoundException;
|
||||
|
||||
use function array_column;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
@@ -26,12 +27,8 @@ final class ScalarColumnHydrator extends AbstractHydrator
|
||||
throw MultipleSelectorsFoundException::create($this->resultSetMapping()->fieldMappings);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
$result = $this->statement()->fetchAllNumeric();
|
||||
|
||||
while ($row = $this->statement()->fetchOne()) {
|
||||
$result[] = $row;
|
||||
}
|
||||
|
||||
return $result;
|
||||
return array_column($result, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use BackedEnum;
|
||||
use BadMethodCallException;
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
@@ -14,14 +15,15 @@ use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\Instantiator\Instantiator;
|
||||
use Doctrine\Instantiator\InstantiatorInterface;
|
||||
use Doctrine\ORM\Cache\Exception\CacheException;
|
||||
use Doctrine\ORM\Cache\Exception\NonCacheableEntityAssociation;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Id\AbstractIdGenerator;
|
||||
use Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\Persistence\Mapping\ReflectionService;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use ReflectionClass;
|
||||
use ReflectionEnum;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionProperty;
|
||||
use RuntimeException;
|
||||
@@ -37,6 +39,7 @@ use function array_values;
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
use function count;
|
||||
use function enum_exists;
|
||||
use function explode;
|
||||
use function gettype;
|
||||
use function in_array;
|
||||
@@ -77,6 +80,10 @@ use const PHP_VERSION_ID;
|
||||
* length?: int,
|
||||
* id?: bool,
|
||||
* nullable?: bool,
|
||||
* notInsertable?: bool,
|
||||
* notUpdatable?: bool,
|
||||
* generated?: string,
|
||||
* enumType?: class-string<BackedEnum>,
|
||||
* columnDefinition?: string,
|
||||
* precision?: int,
|
||||
* scale?: int,
|
||||
@@ -254,6 +261,21 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public const CACHE_USAGE_READ_WRITE = 3;
|
||||
|
||||
/**
|
||||
* The value of this column is never generated by the database.
|
||||
*/
|
||||
public const GENERATED_NEVER = 0;
|
||||
|
||||
/**
|
||||
* The value of this column is generated by the database on INSERT, but not on UPDATE.
|
||||
*/
|
||||
public const GENERATED_INSERT = 1;
|
||||
|
||||
/**
|
||||
* The value of this column is generated by the database on both INSERT and UDPATE statements.
|
||||
*/
|
||||
public const GENERATED_ALWAYS = 2;
|
||||
|
||||
/**
|
||||
* READ-ONLY: The name of the entity class.
|
||||
*
|
||||
@@ -301,7 +323,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* (Optional).
|
||||
*
|
||||
* @var string|null
|
||||
* @psalm-var ?class-string
|
||||
* @psalm-var ?class-string<EntityRepository>
|
||||
*/
|
||||
public $customRepositoryClassName;
|
||||
|
||||
@@ -396,7 +418,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* READ-ONLY: The inheritance mapping type used by the class.
|
||||
*
|
||||
* @var int
|
||||
* @psalm-var self::$INHERITANCE_TYPE_*
|
||||
* @psalm-var self::INHERITANCE_TYPE_*
|
||||
*/
|
||||
public $inheritanceType = self::INHERITANCE_TYPE_NONE;
|
||||
|
||||
@@ -404,6 +426,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* READ-ONLY: The Id generator type used by the class.
|
||||
*
|
||||
* @var int
|
||||
* @psalm-var self::GENERATOR_TYPE_*
|
||||
*/
|
||||
public $generatorType = self::GENERATOR_TYPE_NONE;
|
||||
|
||||
@@ -434,6 +457,12 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* - <b>nullable</b> (boolean, optional)
|
||||
* Whether the column is nullable. Defaults to FALSE.
|
||||
*
|
||||
* - <b>'notInsertable'</b> (boolean, optional)
|
||||
* Whether the column is not insertable. Optional. Is only set if value is TRUE.
|
||||
*
|
||||
* - <b>'notUpdatable'</b> (boolean, optional)
|
||||
* Whether the column is updatable. Optional. Is only set if value is TRUE.
|
||||
*
|
||||
* - <b>columnDefinition</b> (string, optional, schema-only)
|
||||
* The SQL fragment that is used when generating the DDL for the column.
|
||||
*
|
||||
@@ -654,13 +683,21 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
|
||||
|
||||
/**
|
||||
* READ-ONLY: A Flag indicating whether one or more columns of this class
|
||||
* have to be reloaded after insert / update operations.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $requiresFetchAfterChange = false;
|
||||
|
||||
/**
|
||||
* READ-ONLY: A flag for whether or not instances of this class are to be versioned
|
||||
* with optimistic locking.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $isVersioned;
|
||||
public $isVersioned = false;
|
||||
|
||||
/**
|
||||
* READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
|
||||
@@ -669,8 +706,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public $versionField;
|
||||
|
||||
/** @var mixed[] */
|
||||
public $cache = null;
|
||||
/** @var mixed[]|null */
|
||||
public $cache;
|
||||
|
||||
/**
|
||||
* The ReflectionClass instance of the mapped class.
|
||||
@@ -700,7 +737,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* The ReflectionProperty instances of the mapped class.
|
||||
*
|
||||
* @var ReflectionProperty[]|null[]
|
||||
* @var array<string, ReflectionProperty|null>
|
||||
*/
|
||||
public $reflFields = [];
|
||||
|
||||
@@ -958,6 +995,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$serialized[] = 'cache';
|
||||
}
|
||||
|
||||
if ($this->requiresFetchAfterChange) {
|
||||
$serialized[] = 'requiresFetchAfterChange';
|
||||
}
|
||||
|
||||
return $serialized;
|
||||
}
|
||||
|
||||
@@ -988,7 +1029,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
|
||||
foreach ($this->embeddedClasses as $property => $embeddedClass) {
|
||||
if (isset($embeddedClass['declaredField'])) {
|
||||
$childProperty = $reflService->getAccessibleProperty(
|
||||
$childProperty = $this->getAccessibleProperty(
|
||||
$reflService,
|
||||
$this->embeddedClasses[$embeddedClass['declaredField']]['class'],
|
||||
$embeddedClass['originalField']
|
||||
);
|
||||
@@ -1002,7 +1044,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldRefl = $reflService->getAccessibleProperty(
|
||||
$fieldRefl = $this->getAccessibleProperty(
|
||||
$reflService,
|
||||
$embeddedClass['declared'] ?? $this->name,
|
||||
$property
|
||||
);
|
||||
@@ -1013,23 +1056,40 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
|
||||
foreach ($this->fieldMappings as $field => $mapping) {
|
||||
if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) {
|
||||
$childProperty = $this->getAccessibleProperty($reflService, $mapping['originalClass'], $mapping['originalField']);
|
||||
assert($childProperty !== null);
|
||||
|
||||
if (isset($mapping['enumType'])) {
|
||||
$childProperty = new ReflectionEnumProperty(
|
||||
$childProperty,
|
||||
$mapping['enumType']
|
||||
);
|
||||
}
|
||||
|
||||
$this->reflFields[$field] = new ReflectionEmbeddedProperty(
|
||||
$parentReflFields[$mapping['declaredField']],
|
||||
$reflService->getAccessibleProperty($mapping['originalClass'], $mapping['originalField']),
|
||||
$childProperty,
|
||||
$mapping['originalClass']
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->reflFields[$field] = isset($mapping['declared'])
|
||||
? $reflService->getAccessibleProperty($mapping['declared'], $field)
|
||||
: $reflService->getAccessibleProperty($this->name, $field);
|
||||
? $this->getAccessibleProperty($reflService, $mapping['declared'], $field)
|
||||
: $this->getAccessibleProperty($reflService, $this->name, $field);
|
||||
|
||||
if (isset($mapping['enumType']) && $this->reflFields[$field] !== null) {
|
||||
$this->reflFields[$field] = new ReflectionEnumProperty(
|
||||
$this->reflFields[$field],
|
||||
$mapping['enumType']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->associationMappings as $field => $mapping) {
|
||||
$this->reflFields[$field] = isset($mapping['declared'])
|
||||
? $reflService->getAccessibleProperty($mapping['declared'], $field)
|
||||
: $reflService->getAccessibleProperty($this->name, $field);
|
||||
? $this->getAccessibleProperty($reflService, $mapping['declared'], $field)
|
||||
: $this->getAccessibleProperty($reflService, $this->name, $field);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1467,6 +1527,15 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
! isset($mapping['type'])
|
||||
&& ($type instanceof ReflectionNamedType)
|
||||
) {
|
||||
if (PHP_VERSION_ID >= 80100 && ! $type->isBuiltin() && enum_exists($type->getName())) {
|
||||
$mapping['enumType'] = $type->getName();
|
||||
|
||||
$reflection = new ReflectionEnum($type->getName());
|
||||
$type = $reflection->getBackingType();
|
||||
|
||||
assert($type instanceof ReflectionNamedType);
|
||||
}
|
||||
|
||||
switch ($type->getName()) {
|
||||
case DateInterval::class:
|
||||
$mapping['type'] = Types::DATEINTERVAL;
|
||||
@@ -1588,6 +1657,26 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$mapping['requireSQLConversion'] = true;
|
||||
}
|
||||
|
||||
if (isset($mapping['generated'])) {
|
||||
if (! in_array($mapping['generated'], [self::GENERATED_NEVER, self::GENERATED_INSERT, self::GENERATED_ALWAYS])) {
|
||||
throw MappingException::invalidGeneratedMode($mapping['generated']);
|
||||
}
|
||||
|
||||
if ($mapping['generated'] === self::GENERATED_NEVER) {
|
||||
unset($mapping['generated']);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($mapping['enumType'])) {
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
throw MappingException::enumsRequirePhp81($this->name, $mapping['fieldName']);
|
||||
}
|
||||
|
||||
if (! enum_exists($mapping['enumType'])) {
|
||||
throw MappingException::nonEnumTypeMapped($this->name, $mapping['fieldName'], $mapping['enumType']);
|
||||
}
|
||||
}
|
||||
|
||||
return $mapping;
|
||||
}
|
||||
|
||||
@@ -2151,6 +2240,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Sets the type of Id generator to use for the mapped class.
|
||||
*
|
||||
* @param int $generatorType
|
||||
* @psalm-param self::GENERATOR_TYPE_* $generatorType
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -2377,6 +2467,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Sets the inheritance type used by the class and its subclasses.
|
||||
*
|
||||
* @param int $type
|
||||
* @psalm-param self::INHERITANCE_TYPE_* $type
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
@@ -2640,6 +2731,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$mapping = $this->validateAndCompleteFieldMapping($mapping);
|
||||
$this->assertFieldNotMapped($mapping['fieldName']);
|
||||
|
||||
if (isset($mapping['generated'])) {
|
||||
$this->requiresFetchAfterChange = true;
|
||||
}
|
||||
|
||||
$this->fieldMappings[$mapping['fieldName']] = $mapping;
|
||||
}
|
||||
|
||||
@@ -2922,8 +3017,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Registers a custom repository class for the entity class.
|
||||
*
|
||||
* @param string $repositoryClassName The class name of the custom mapper.
|
||||
* @psalm-param class-string $repositoryClassName
|
||||
* @param string|null $repositoryClassName The class name of the custom mapper.
|
||||
* @psalm-param class-string<EntityRepository>|null $repositoryClassName
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -3370,8 +3465,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public function setVersionMapping(array &$mapping)
|
||||
{
|
||||
$this->isVersioned = true;
|
||||
$this->versionField = $mapping['fieldName'];
|
||||
$this->isVersioned = true;
|
||||
$this->versionField = $mapping['fieldName'];
|
||||
$this->requiresFetchAfterChange = true;
|
||||
|
||||
if (! isset($mapping['default'])) {
|
||||
if (in_array($mapping['type'], ['integer', 'bigint', 'smallint'], true)) {
|
||||
@@ -3394,6 +3490,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
public function setVersioned($bool)
|
||||
{
|
||||
$this->isVersioned = $bool;
|
||||
|
||||
if ($bool) {
|
||||
$this->requiresFetchAfterChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3590,9 +3690,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
|
||||
/**
|
||||
* @param string|null $className
|
||||
* @psalm-param ?class-string $className
|
||||
* @psalm-param string|class-string|null $className
|
||||
*
|
||||
* @return string|null null if the input value is null
|
||||
* @psalm-return class-string|null
|
||||
*/
|
||||
public function fullyQualifiedClassName($className)
|
||||
{
|
||||
@@ -3600,7 +3701,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
return $className;
|
||||
}
|
||||
|
||||
if ($className !== null && strpos($className, '\\') === false && $this->namespace) {
|
||||
if (strpos($className, '\\') === false && $this->namespace) {
|
||||
return $this->namespace . '\\' . $className;
|
||||
}
|
||||
|
||||
@@ -3745,4 +3846,17 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
throw new InvalidArgumentException("'orderBy' is expected to be an array, not " . gettype($mapping['orderBy']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param class-string $class
|
||||
*/
|
||||
private function getAccessibleProperty(ReflectionService $reflService, string $class, string $field): ?ReflectionProperty
|
||||
{
|
||||
$reflectionProperty = $reflService->getAccessibleProperty($class, $field);
|
||||
if ($reflectionProperty !== null && PHP_VERSION_ID >= 80100 && $reflectionProperty->isReadOnly()) {
|
||||
$reflectionProperty = new ReflectionReadonlyProperty($reflectionProperty);
|
||||
}
|
||||
|
||||
return $reflectionProperty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,15 @@ final class Column implements Annotation
|
||||
/** @var bool */
|
||||
public $nullable = false;
|
||||
|
||||
/** @var bool */
|
||||
public $insertable = true;
|
||||
|
||||
/** @var bool */
|
||||
public $updatable = true;
|
||||
|
||||
/** @var class-string<\BackedEnum>|null */
|
||||
public $enumType = null;
|
||||
|
||||
/** @var array<string,mixed> */
|
||||
public $options = [];
|
||||
|
||||
@@ -51,7 +60,16 @@ final class Column implements Annotation
|
||||
public $columnDefinition;
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $options
|
||||
* @var string|null
|
||||
* @psalm-var 'NEVER'|'INSERT'|'ALWAYS'|null
|
||||
* @Enum({"NEVER", "INSERT", "ALWAYS"})
|
||||
*/
|
||||
public $generated;
|
||||
|
||||
/**
|
||||
* @param class-string<\BackedEnum>|null $enumType
|
||||
* @param array<string,mixed> $options
|
||||
* @psalm-param 'NEVER'|'INSERT'|'ALWAYS'|null $generated
|
||||
*/
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
@@ -61,8 +79,12 @@ final class Column implements Annotation
|
||||
?int $scale = null,
|
||||
bool $unique = false,
|
||||
bool $nullable = false,
|
||||
bool $insertable = true,
|
||||
bool $updatable = true,
|
||||
?string $enumType = null,
|
||||
array $options = [],
|
||||
?string $columnDefinition = null
|
||||
?string $columnDefinition = null,
|
||||
?string $generated = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
@@ -71,7 +93,11 @@ final class Column implements Annotation
|
||||
$this->scale = $scale;
|
||||
$this->unique = $unique;
|
||||
$this->nullable = $nullable;
|
||||
$this->insertable = $insertable;
|
||||
$this->updatable = $updatable;
|
||||
$this->enumType = $enumType;
|
||||
$this->options = $options;
|
||||
$this->columnDefinition = $columnDefinition;
|
||||
$this->generated = $generated;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user