mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 06:52:09 +01:00
Compare commits
148 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99d67cb77d | ||
|
|
43f66d5808 | ||
|
|
a6577b89a2 | ||
|
|
0ca87566a9 | ||
|
|
5d01f94a36 | ||
|
|
3d02b02636 | ||
|
|
6de321cb09 | ||
|
|
535bc92dc8 | ||
|
|
ebb5d03f7a | ||
|
|
8e13369621 | ||
|
|
8eff4b775a | ||
|
|
b85403d0a2 | ||
|
|
22ce3adfce | ||
|
|
3a194ad699 | ||
|
|
d52dab54dd | ||
|
|
b5ac7714bc | ||
|
|
590551d5c3 | ||
|
|
c9fb9fdb40 | ||
|
|
965926dcc8 | ||
|
|
a6e30c5f4c | ||
|
|
30ab6f4cea | ||
|
|
5e5a44dce2 | ||
|
|
d7bf30b291 | ||
|
|
ce8da6623f | ||
|
|
2ecec0c5d6 | ||
|
|
6f128e4515 | ||
|
|
e24b0f0be7 | ||
|
|
6753b26f73 | ||
|
|
4ccc4e19fc | ||
|
|
4e2009433b | ||
|
|
c25b822217 | ||
|
|
c3dcc5af91 | ||
|
|
b2f404b25f | ||
|
|
d141f27875 | ||
|
|
4691839201 | ||
|
|
91387382b7 | ||
|
|
f634c64b7a | ||
|
|
7ba9c980b5 | ||
|
|
dacdcf2c7b | ||
|
|
f296fee9e4 | ||
|
|
8555fc1d34 | ||
|
|
b0826fd746 | ||
|
|
fe93c2e9d5 | ||
|
|
850d57827f | ||
|
|
e1388fa986 | ||
|
|
9a48450481 | ||
|
|
cff8b96dd6 | ||
|
|
996c1c74b3 | ||
|
|
48612e6dc6 | ||
|
|
ddfee26f80 | ||
|
|
eb860a704e | ||
|
|
51ffcb4891 | ||
|
|
72f500318a | ||
|
|
55f030f66b | ||
|
|
95af30eb72 | ||
|
|
9ea0769d78 | ||
|
|
22413453da | ||
|
|
06fadcdd8c | ||
|
|
7c56aa2141 | ||
|
|
4cdcb5f760 | ||
|
|
b542b36e45 | ||
|
|
e5a7a13e1e | ||
|
|
8336dd3779 | ||
|
|
b04d7a62ae | ||
|
|
a959a474fd | ||
|
|
ce128e742b | ||
|
|
dac87dae06 | ||
|
|
a2230485b2 | ||
|
|
a68aa580c5 | ||
|
|
5ee71c54d4 | ||
|
|
dc37c2cd2f | ||
|
|
261a405970 | ||
|
|
1ea51d88c4 | ||
|
|
da3a9fa361 | ||
|
|
4fd81d26ff | ||
|
|
f8e06ad31e | ||
|
|
559c1ba806 | ||
|
|
4665758c44 | ||
|
|
e2e9f8fa97 | ||
|
|
f7249ec709 | ||
|
|
87dbcca454 | ||
|
|
ceeea8ccd1 | ||
|
|
6e16ef8c31 | ||
|
|
305e0d6664 | ||
|
|
199be94e6d | ||
|
|
09a7d9f18a | ||
|
|
f57f33b67f | ||
|
|
e86cddb360 | ||
|
|
fa588af3b1 | ||
|
|
d4741720fa | ||
|
|
343385d060 | ||
|
|
6d04dced03 | ||
|
|
22fa3a8556 | ||
|
|
eb05756dc3 | ||
|
|
5bb7e20708 | ||
|
|
a9076313c7 | ||
|
|
2a87821b28 | ||
|
|
da5877d60c | ||
|
|
67dfe8e1af | ||
|
|
2dfe51b396 | ||
|
|
5ac036de02 | ||
|
|
fda0d7b440 | ||
|
|
23e1fd8ad6 | ||
|
|
f8fa0fe069 | ||
|
|
a588555ecd | ||
|
|
501057da83 | ||
|
|
7de84537f6 | ||
|
|
97f8325dad | ||
|
|
0ebd7052d7 | ||
|
|
5d73378b92 | ||
|
|
10572ec441 | ||
|
|
76278d801d | ||
|
|
ca80830b26 | ||
|
|
1ed89c756a | ||
|
|
bb078b5cb7 | ||
|
|
bcb4889a2c | ||
|
|
961da8b0cc | ||
|
|
c3f8996af5 | ||
|
|
0655083e50 | ||
|
|
b6b3c97436 | ||
|
|
8f6d146bc4 | ||
|
|
1f4e6ebeeb | ||
|
|
a94db4f5c0 | ||
|
|
4fccec1322 | ||
|
|
0177133385 | ||
|
|
8df5cb84fa | ||
|
|
3b7275e183 | ||
|
|
2685b65c2b | ||
|
|
3902a4eb6e | ||
|
|
b3ed525d4d | ||
|
|
4cdc6b1a71 | ||
|
|
38ccbd8638 | ||
|
|
b4e6530d2d | ||
|
|
0ae53a6703 | ||
|
|
49864a7f57 | ||
|
|
e2e59e94f5 | ||
|
|
6fe388a705 | ||
|
|
172a8d9414 | ||
|
|
d00dbf7e2d | ||
|
|
17012f1fea | ||
|
|
324ac3972f | ||
|
|
323469cdb7 | ||
|
|
835030297a | ||
|
|
4cc78d9478 | ||
|
|
68d24288ce | ||
|
|
5abad7c0af | ||
|
|
f0ad5f72b2 | ||
|
|
cbc252f3b7 |
5
.gitattributes
vendored
5
.gitattributes
vendored
@@ -2,10 +2,9 @@
|
||||
/tools export-ignore
|
||||
/docs export-ignore
|
||||
/.github export-ignore
|
||||
.doctrine-project.json export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.gitmodules export-ignore
|
||||
.travis.yml export-ignore
|
||||
build.properties export-ignore
|
||||
build.properties.dev export-ignore
|
||||
build.xml export-ignore
|
||||
@@ -15,4 +14,6 @@ run-all.sh export-ignore
|
||||
phpcs.xml.dist export-ignore
|
||||
phpbench.json export-ignore
|
||||
phpstan.neon export-ignore
|
||||
phpstan-baseline.neon export-ignore
|
||||
psalm.xml export-ignore
|
||||
psalm-baseline.xml export-ignore
|
||||
|
||||
41
.github/workflows/continuous-integration.yml
vendored
41
.github/workflows/continuous-integration.yml
vendored
@@ -19,11 +19,6 @@ jobs:
|
||||
- "7.3"
|
||||
- "7.4"
|
||||
- "8.0"
|
||||
deps:
|
||||
- "highest"
|
||||
include:
|
||||
- deps: "lowest"
|
||||
php-version: "7.3"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
@@ -41,8 +36,6 @@ jobs:
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
dependency-versions: "${{ matrix.deps }}"
|
||||
|
||||
- name: "Run PHPUnit"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/sqlite.xml --coverage-clover=coverage-no-cache.xml"
|
||||
@@ -57,7 +50,7 @@ jobs:
|
||||
- name: "Upload coverage file"
|
||||
uses: "actions/upload-artifact@v2"
|
||||
with:
|
||||
name: "phpunit-sqlite-${{ matrix.deps }}-${{ matrix.php-version }}-coverage"
|
||||
name: "phpunit-sqlite-${{ matrix.php-version }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
|
||||
|
||||
@@ -228,6 +221,38 @@ jobs:
|
||||
name: "${{ github.job }}-${{ matrix.mysql-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-coverage"
|
||||
path: "coverage*.xml"
|
||||
|
||||
phpunit-lower-php-versions:
|
||||
name: "PHPUnit with SQLite"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.1"
|
||||
deps:
|
||||
- "highest"
|
||||
- "lowest"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
ini-values: "zend.assertions=1"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
dependency-versions: "${{ matrix.deps }}"
|
||||
|
||||
- name: "Run PHPUnit"
|
||||
run: "vendor/bin/phpunit -c ci/github/phpunit/sqlite.xml"
|
||||
|
||||
upload_coverage:
|
||||
name: "Upload coverage to Codecov"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
23
.github/workflows/static-analysis.yml
vendored
23
.github/workflows/static-analysis.yml
vendored
@@ -1,12 +1,18 @@
|
||||
name: Static Analysis
|
||||
|
||||
name: "Static Analysis"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*.x"
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
|
||||
jobs:
|
||||
static-analysis-phpstan:
|
||||
name: "PHPStan"
|
||||
runs-on: "ubuntu-latest"
|
||||
name: "Static Analysis with PHPStan"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -22,17 +28,18 @@ jobs:
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
tools: cs2pr
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
- name: "Run a static analysis with phpstan/phpstan"
|
||||
run: "php vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr"
|
||||
run: "vendor/bin/phpstan analyse"
|
||||
|
||||
static-analysis-psalm:
|
||||
name: "Psalm"
|
||||
runs-on: "ubuntu-latest"
|
||||
name: "Static Analysis with Psalm"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -51,6 +58,8 @@ jobs:
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
dependency-versions: "highest"
|
||||
|
||||
- name: "Run a static analysis with vimeo/psalm"
|
||||
run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)"
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -16,3 +16,4 @@ vendor/
|
||||
/.phpcs-cache
|
||||
composer.lock
|
||||
/.phpunit.result.cache
|
||||
/*.phpunit.xml
|
||||
|
||||
28
UPGRADE.md
28
UPGRADE.md
@@ -1,3 +1,31 @@
|
||||
# Upgrade to 2.9
|
||||
|
||||
## Minor BC BREAK: Setup tool needs cache implementation
|
||||
|
||||
With the deprecation of doctrine/cache, the setup tool might no longer work as expected without a different cache
|
||||
implementation. To work around this:
|
||||
* Install symfony/cache: `composer require symfony/cache`. This will keep previous behaviour without any changes
|
||||
* Instantiate caches yourself: to use a different cache implementation, pass a cache instance when calling any
|
||||
configuration factory in the setup tool:
|
||||
```diff
|
||||
- $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, $proxyDir);
|
||||
+ $cache = \Doctrine\Common\Cache\Psr6\DoctrineProvider::wrap($anyPsr6Implementation);
|
||||
+ $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, $proxyDir, $cache);
|
||||
```
|
||||
* As a quick workaround, you can lock the doctrine/cache dependency to work around this: `composer require doctrine/cache ^1.11`.
|
||||
Note that this is only recommended as a bandaid fix, as future versions of ORM will no longer work with doctrine/cache
|
||||
1.11.
|
||||
|
||||
## Deprecated: doctrine/cache for metadata caching
|
||||
|
||||
The `Doctrine\ORM\Configuration#setMetadataCacheImpl()` method is deprecated and should no longer be used. Please use
|
||||
`Doctrine\ORM\Configuration#setMetadataCache()` with any PSR-6 cache adapter instead.
|
||||
|
||||
## Removed: flushing metadata cache
|
||||
|
||||
To support PSR-6 caches, the `--flush` option for the `orm:clear-cache:metadata` command is ignored. Metadata cache is
|
||||
now always cleared regardless of the cache adapter being used.
|
||||
|
||||
# Upgrade to 2.8
|
||||
|
||||
## Minor BC BREAK: Failed commit now throw OptimisticLockException
|
||||
|
||||
@@ -16,29 +16,34 @@
|
||||
"sort-packages": true
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2|^8.0",
|
||||
"php": "^7.1|^8.0",
|
||||
"ext-pdo": "*",
|
||||
"composer/package-versions-deprecated": "^1.8",
|
||||
"doctrine/annotations": "^1.11.1",
|
||||
"doctrine/cache": "^1.9.1",
|
||||
"doctrine/annotations": "^1.13",
|
||||
"doctrine/cache": "^1.11|^2.0",
|
||||
"doctrine/collections": "^1.5",
|
||||
"doctrine/common": "^3.0.3",
|
||||
"doctrine/dbal": "^2.10.0",
|
||||
"doctrine/dbal": "^2.13.0",
|
||||
"doctrine/deprecations": "^0.5.3",
|
||||
"doctrine/event-manager": "^1.1",
|
||||
"doctrine/inflector": "^1.4|^2.0",
|
||||
"doctrine/instantiator": "^1.3",
|
||||
"doctrine/lexer": "^1.0",
|
||||
"doctrine/persistence": "^2.0",
|
||||
"symfony/console": "^3.0|^4.0|^5.0"
|
||||
"doctrine/persistence": "^2.2",
|
||||
"psr/cache": "^1 || ^2 || ^3",
|
||||
"symfony/console": "^3.0|^4.0|^5.0|^6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^8.0",
|
||||
"phpstan/phpstan": "^0.12.18",
|
||||
"phpunit/phpunit": "^8.5|^9.4",
|
||||
"symfony/yaml": "^3.4|^4.0|^5.0",
|
||||
"vimeo/psalm": "4.1.1"
|
||||
"doctrine/coding-standard": "^9.0",
|
||||
"phpstan/phpstan": "^0.12.83",
|
||||
"phpunit/phpunit": "^7.5|^8.5|^9.4",
|
||||
"squizlabs/php_codesniffer": "3.6.0",
|
||||
"symfony/cache": "^4.4|^5.2",
|
||||
"symfony/yaml": "^3.4|^4.0|^5.0|^6.0",
|
||||
"vimeo/psalm": "4.7.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0",
|
||||
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
|
||||
},
|
||||
"autoload": {
|
||||
|
||||
@@ -10,6 +10,11 @@ code should look like. We will implement it on a
|
||||
`Layer Supertype <http://martinfowler.com/eaaCatalog/layerSupertype.html>`_
|
||||
for all our domain objects.
|
||||
|
||||
.. warning::
|
||||
|
||||
The notify change tracking policy is deprecated and will be removed in ORM 3.0.
|
||||
(`Details <https://github.com/doctrine/orm/issues/8383>`_)
|
||||
|
||||
Implementing NotifyPropertyChanged
|
||||
----------------------------------
|
||||
|
||||
|
||||
@@ -101,10 +101,11 @@ Gets or sets the metadata driver implementation that is used by
|
||||
Doctrine to acquire the object-relational metadata for your
|
||||
classes.
|
||||
|
||||
There are currently 4 available implementations:
|
||||
There are currently 5 available implementations:
|
||||
|
||||
|
||||
- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\AttributeDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\XmlDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\YamlDriver``
|
||||
- ``Doctrine\ORM\Mapping\Driver\DriverChain``
|
||||
|
||||
@@ -89,7 +89,7 @@ as part of the lifecycle of the instance variables entity-class.
|
||||
Required attributes:
|
||||
|
||||
- **type**: Name of the Doctrine Type which is converted between PHP
|
||||
and Database representation.
|
||||
and Database representation. Default to ``string`` or :ref:`Type from PHP property type <reference-php-mapping-types>`
|
||||
|
||||
Optional attributes:
|
||||
|
||||
@@ -113,7 +113,7 @@ Optional attributes:
|
||||
- **unique**: Boolean value to determine if the value of the column
|
||||
should be unique across all rows of the underlying entities table.
|
||||
|
||||
- **nullable**: Determines if NULL values allowed for this column. If not specified, default value is false.
|
||||
- **nullable**: Determines if NULL values allowed for this column. If not specified, default value is false. When using typed properties on entity class defaults to true when property is nullable.
|
||||
|
||||
- **options**: Array of additional options:
|
||||
|
||||
@@ -513,7 +513,8 @@ Required attributes:
|
||||
|
||||
|
||||
- **name**: Name of the Index
|
||||
- **columns**: Array of columns.
|
||||
- **fields**: Array of fields. Exactly one of **fields**, **columns** is required.
|
||||
- **columns**: Array of columns. Exactly one of **fields**, **columns** is required.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
@@ -535,6 +536,19 @@ Basic example:
|
||||
{
|
||||
}
|
||||
|
||||
Basic example using fields:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="ecommerce_products",indexes={@Index(name="search_idx", fields={"name", "email"})})
|
||||
*/
|
||||
class ECommerceProduct
|
||||
{
|
||||
}
|
||||
|
||||
Example with partial indexes:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -635,6 +649,8 @@ Optional attributes:
|
||||
constraint level. Defaults to false.
|
||||
- **nullable**: Determine whether the related entity is required, or if
|
||||
null is an allowed state for the relation. Defaults to true.
|
||||
When using typed properties on entity class defaults to false when
|
||||
property is not nullable.
|
||||
- **onDelete**: Cascade Action (Database-level)
|
||||
- **columnDefinition**: DDL SQL snippet that starts after the column
|
||||
name and specifies the complete (non-portable!) column definition.
|
||||
@@ -715,6 +731,7 @@ Required attributes:
|
||||
|
||||
- **targetEntity**: FQCN of the referenced target entity. Can be the
|
||||
unqualified class name if both classes are in the same namespace.
|
||||
You can omit this value if you use a PHP property type instead.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
@@ -840,6 +857,11 @@ Example:
|
||||
|
||||
@NamedNativeQuery
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. note::
|
||||
|
||||
Named Native Queries are deprecated as of version 2.9 and will be removed in ORM 3.0
|
||||
|
||||
Is used to specify a native SQL named query.
|
||||
The NamedNativeQuery annotation can be applied to an entity or mapped superclass.
|
||||
|
||||
@@ -923,6 +945,7 @@ Required attributes:
|
||||
|
||||
- **targetEntity**: FQCN of the referenced target entity. Can be the
|
||||
unqualified class name if both classes are in the same namespace.
|
||||
When typed properties are used it is inherited from PHP type.
|
||||
*IMPORTANT:* No leading backslash!
|
||||
|
||||
Optional attributes:
|
||||
@@ -1263,7 +1286,8 @@ Required attributes:
|
||||
|
||||
|
||||
- **name**: Name of the Index
|
||||
- **columns**: Array of columns.
|
||||
- **fields**: Array of fields. Exactly one of **fields**, **columns** is required.
|
||||
- **columns**: Array of columns. Exactly one of **fields**, **columns** is required.
|
||||
|
||||
Optional attributes:
|
||||
|
||||
@@ -1285,6 +1309,19 @@ Basic example:
|
||||
{
|
||||
}
|
||||
|
||||
Basic example using fields:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="ecommerce_products",uniqueConstraints={@UniqueConstraint(name="search_idx", fields={"name", "email"})})
|
||||
*/
|
||||
class ECommerceProduct
|
||||
{
|
||||
}
|
||||
|
||||
Example with partial indexes:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -22,9 +22,9 @@ One tip for working with relations is to read the relation from left to right, w
|
||||
- ManyToOne - Many instances of the current Entity refer to One instance of the referred Entity.
|
||||
- OneToOne - One instance of the current Entity refers to One instance of the referred Entity.
|
||||
|
||||
See below for all the possible relations.
|
||||
See below for all the possible relations.
|
||||
|
||||
An association is considered to be unidirectional if only one side of the association has
|
||||
An association is considered to be unidirectional if only one side of the association has
|
||||
a property referring to the other side.
|
||||
|
||||
To gain a full understanding of associations you should also read about :doc:`owning and
|
||||
@@ -1061,6 +1061,70 @@ join columns default to the simple, unqualified class name of the
|
||||
targeted class followed by "\_id". The referencedColumnName always
|
||||
defaults to "id", just as in one-to-one or many-to-one mappings.
|
||||
|
||||
Additionally, when using typed properties with Doctrine 2.9 or newer
|
||||
you can skip ``targetEntity`` in ``ManyToOne`` and ``OneToOne``
|
||||
associations as they will be set based on type. Also ``nullable``
|
||||
attribute on ``JoinColumn`` will be inherited from PHP type. So that:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @OneToOne */
|
||||
private Shipment $shipment;
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity class="Product">
|
||||
<one-to-one field="shipment" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Product:
|
||||
type: entity
|
||||
oneToOne:
|
||||
shipment: ~
|
||||
|
||||
Is essentially the same as following:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* One Product has One Shipment.
|
||||
* @OneToOne(targetEntity="Shipment")
|
||||
* @JoinColumn(name="shipment_id", referencedColumnName="id", nullable=false)
|
||||
*/
|
||||
private Shipment $shipment;
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity class="Product">
|
||||
<one-to-one field="shipment" target-entity="Shipment">
|
||||
<join-column name="shipment_id" referenced-column-name="id" nulable=false />
|
||||
</one-to-one>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Product:
|
||||
type: entity
|
||||
oneToOne:
|
||||
shipment:
|
||||
targetEntity: Shipment
|
||||
joinColumn:
|
||||
name: shipment_id
|
||||
referencedColumnName: id
|
||||
nullable: false
|
||||
|
||||
If you accept these defaults, you can reduce the mapping code to a
|
||||
minimum.
|
||||
|
||||
|
||||
1032
docs/en/reference/attributes-reference.rst
Normal file
1032
docs/en/reference/attributes-reference.rst
Normal file
File diff suppressed because it is too large
Load Diff
@@ -211,6 +211,25 @@ list:
|
||||
- ``options``: (optional) Key-value pairs of options that get passed
|
||||
to the underlying database platform when generating DDL statements.
|
||||
|
||||
.. _reference-php-mapping-types:
|
||||
|
||||
PHP Types Mapping
|
||||
_________________
|
||||
|
||||
Since version 2.9 Doctrine can determine usable defaults from property types
|
||||
on entity classes. When property type is nullable the default for ``nullable``
|
||||
Column attribute is set to TRUE. Additionally, Doctrine will map PHP types
|
||||
to ``type`` attribute as follows:
|
||||
|
||||
- ``DateInterval``: ``dateinterval``
|
||||
- ``DateTime``: ``datetime``
|
||||
- ``DateTimeImmutable``: ``datetime_immutable``
|
||||
- ``array``: ``json``
|
||||
- ``bool``: ``boolean``
|
||||
- ``float``: ``float``
|
||||
- ``int``: ``integer``
|
||||
- ``string`` or any other type: ``string``
|
||||
|
||||
.. _reference-mapping-types:
|
||||
|
||||
Doctrine Mapping Types
|
||||
@@ -328,7 +347,7 @@ annotation.
|
||||
|
||||
In most cases using the automatic generator strategy (``@GeneratedValue``) is
|
||||
what you want. It defaults to the identifier generation mechanism your current
|
||||
database vendor prefers: AUTO_INCREMENT with MySQL, sequences with PostgreSQL
|
||||
database vendor prefers: AUTO_INCREMENT with MySQL, sequences with PostgreSQL
|
||||
and Oracle and so on.
|
||||
|
||||
Identifier Generation Strategies
|
||||
|
||||
@@ -89,11 +89,11 @@ with the batching strategy that was already used for bulk inserts:
|
||||
foreach ($q->toIterable() as $user) {
|
||||
$user->increaseCredit();
|
||||
$user->calculateNewBonuses();
|
||||
++$i;
|
||||
if (($i % $batchSize) === 0) {
|
||||
$em->flush(); // Executes all updates.
|
||||
$em->clear(); // Detaches all objects from Doctrine!
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
$em->flush();
|
||||
|
||||
@@ -147,11 +147,11 @@ The following example shows how to do this:
|
||||
$q = $em->createQuery('select u from MyProject\Model\User u');
|
||||
foreach($q->toIterable() as $row) {
|
||||
$em->remove($row);
|
||||
++$i;
|
||||
if (($i % $batchSize) === 0) {
|
||||
$em->flush(); // Executes all deletions.
|
||||
$em->clear(); // Detaches all objects from Doctrine!
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
$em->flush();
|
||||
|
||||
|
||||
@@ -61,6 +61,11 @@ This policy can be configured as follows:
|
||||
Notify
|
||||
~~~~~~
|
||||
|
||||
.. warning::
|
||||
|
||||
The notify change tracking policy is deprecated and will be removed in ORM 3.0.
|
||||
(`Details <https://github.com/doctrine/orm/issues/8383>`_)
|
||||
|
||||
This policy is based on the assumption that the entities notify
|
||||
interested listeners of changes to their properties. For that
|
||||
purpose, a class that wants to use this policy needs to implement
|
||||
|
||||
@@ -603,6 +603,13 @@ then phonenumber-id:
|
||||
...
|
||||
'nameUpper' => string 'JWAGE' (length=5)
|
||||
|
||||
You can also index by a to-one association, which will use the id of
|
||||
the associated entity (the join column) as the key in the result set:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT p, u FROM Participant INDEX BY p.user JOIN p.user u WHERE p.event = 3
|
||||
|
||||
UPDATE queries
|
||||
--------------
|
||||
|
||||
@@ -1370,7 +1377,8 @@ userland:
|
||||
that contain char or binary data. Doctrine has no way of implicitly
|
||||
reloading this data. Partially loaded objects have to be passed to
|
||||
``EntityManager::refresh()`` if they are to be reloaded fully from
|
||||
the database.
|
||||
the database. This query hint is deprecated and will be removed
|
||||
in the future (`Details <https://github.com/doctrine/orm/issues/8471>`_)
|
||||
- Query::HINT\_REFRESH - This query is used internally by
|
||||
``EntityManager::refresh()`` and can be used in userland as well.
|
||||
If you specify this hint and a query returns the data for an entity
|
||||
@@ -1615,7 +1623,7 @@ From, Join and Index by
|
||||
RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
|
||||
JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy]
|
||||
Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" (JoinAssociationDeclaration | RangeVariableDeclaration) ["WITH" ConditionalExpression]
|
||||
IndexBy ::= "INDEX" "BY" StateFieldPathExpression
|
||||
IndexBy ::= "INDEX" "BY" SingleValuedPathExpression
|
||||
|
||||
Select Expressions
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -53,6 +53,9 @@ proper quoting of parameters.
|
||||
}
|
||||
}
|
||||
|
||||
If the parameter is an array and should be quoted as a list of values for an IN query
|
||||
this is possible with the alternative ``SQLFilter#setParameterList()`` and
|
||||
``SQLFilter#getParameterList()`` functions.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
@@ -44,13 +44,35 @@ Read-Only Entities
|
||||
------------------
|
||||
|
||||
You can mark entities as read only (See metadata mapping
|
||||
references for details). This means that the entity marked as read only is never considered
|
||||
for updates, which means when you call flush on the EntityManager these entities are skipped
|
||||
even if properties changed. Read-Only allows to persist new entities of a kind and remove existing
|
||||
ones, they are just not considered for updates.
|
||||
references for details).
|
||||
|
||||
This means that the entity marked as read only is never considered for updates.
|
||||
During flush on the EntityManager these entities are skipped even if properties
|
||||
changed.
|
||||
|
||||
Read-Only allows to persist new entities of a kind and remove existing ones,
|
||||
they are just not considered for updates.
|
||||
|
||||
See :ref:`annref_entity`
|
||||
|
||||
You can also explicitly mark individual entities read only directly on the
|
||||
UnitOfWork via a call to ``markReadOnly()``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$user = $entityManager->find(User::class, $id);
|
||||
$entityManager->getUnitOfWork()->markReadOnly($user);
|
||||
|
||||
Or you can set all objects that are the result of a query hydration to be
|
||||
marked as read only with the following query hint:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$query = $entityManager->createQuery('SELECT u FROM App\\Entity\\User u');
|
||||
$query->setHint(Query::HINT_READ_ONLY, true);
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
Extra-Lazy Collections
|
||||
----------------------
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ metadata:
|
||||
|
||||
- **XML files** (XmlDriver)
|
||||
- **Class DocBlock Annotations** (AnnotationDriver)
|
||||
- **Attributes** (AttributeDriver)
|
||||
- **YAML files** (YamlDriver)
|
||||
- **PHP Code in files or static functions** (PhpDriver)
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ with inheritance hierarchies.
|
||||
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
|
||||
$sql = "SELECT u.id, u.name, a.id AS address_id, a.street, a.city " .
|
||||
$sql = "SELECT u.id, u.name, a.id AS address_id, a.street, a.city " .
|
||||
"FROM users u INNER JOIN address a ON u.address_id = a.id";
|
||||
|
||||
$rsm = new ResultSetMappingBuilder($entityManager);
|
||||
@@ -265,7 +265,7 @@ detail:
|
||||
<?php
|
||||
/**
|
||||
* Adds a meta column (foreign key or discriminator column) to the result set.
|
||||
*
|
||||
*
|
||||
* @param string $alias
|
||||
* @param string $columnAlias
|
||||
* @param string $columnName
|
||||
@@ -320,10 +320,10 @@ entity.
|
||||
$rsm->addEntityResult('User', 'u');
|
||||
$rsm->addFieldResult('u', 'id', 'id');
|
||||
$rsm->addFieldResult('u', 'name', 'name');
|
||||
|
||||
|
||||
$query = $this->_em->createNativeQuery('SELECT id, name FROM users WHERE name = ?', $rsm);
|
||||
$query->setParameter(1, 'romanb');
|
||||
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
The result would look like this:
|
||||
@@ -356,10 +356,10 @@ thus owns the foreign key.
|
||||
$rsm->addFieldResult('u', 'id', 'id');
|
||||
$rsm->addFieldResult('u', 'name', 'name');
|
||||
$rsm->addMetaResult('u', 'address_id', 'address_id');
|
||||
|
||||
|
||||
$query = $this->_em->createNativeQuery('SELECT id, name, address_id FROM users WHERE name = ?', $rsm);
|
||||
$query->setParameter(1, 'romanb');
|
||||
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
Foreign keys are used by Doctrine for lazy-loading purposes when
|
||||
@@ -385,12 +385,12 @@ associations that are lazy.
|
||||
$rsm->addFieldResult('a', 'address_id', 'id');
|
||||
$rsm->addFieldResult('a', 'street', 'street');
|
||||
$rsm->addFieldResult('a', 'city', 'city');
|
||||
|
||||
|
||||
$sql = 'SELECT u.id, u.name, a.id AS address_id, a.street, a.city FROM users u ' .
|
||||
'INNER JOIN address a ON u.address_id = a.id WHERE u.name = ?';
|
||||
$query = $this->_em->createNativeQuery($sql, $rsm);
|
||||
$query->setParameter(1, 'romanb');
|
||||
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
In this case the nested entity ``Address`` is registered with the
|
||||
@@ -420,10 +420,10 @@ to map the hierarchy (both use a discriminator column).
|
||||
$rsm->addFieldResult('u', 'name', 'name');
|
||||
$rsm->addMetaResult('u', 'discr', 'discr'); // discriminator column
|
||||
$rsm->setDiscriminatorColumn('u', 'discr');
|
||||
|
||||
|
||||
$query = $this->_em->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm);
|
||||
$query->setParameter(1, 'romanb');
|
||||
|
||||
|
||||
$users = $query->getResult();
|
||||
|
||||
Note that in the case of Class Table Inheritance, an example as
|
||||
@@ -435,6 +435,10 @@ strategy but with native SQL it is your responsibility.
|
||||
Named Native Query
|
||||
------------------
|
||||
|
||||
.. note::
|
||||
|
||||
Named Native Queries are deprecated as of version 2.9 and will be removed in ORM 3.0
|
||||
|
||||
You can also map a native query using a named native query mapping.
|
||||
|
||||
To achieve that, you must describe the SQL resultset structure
|
||||
@@ -789,7 +793,7 @@ followed by a dot ("."), followed by the name or the field or property of the pr
|
||||
6:
|
||||
name: address.country
|
||||
column: a_country
|
||||
|
||||
|
||||
|
||||
|
||||
If you retrieve a single entity and if you use the default mapping,
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
Partial Objects
|
||||
===============
|
||||
|
||||
|
||||
.. warning::
|
||||
|
||||
Creating Partial Objects through DQL is deprecated and
|
||||
will be removed in the future, use data transfer object
|
||||
support in DQL instead. (`Details
|
||||
<https://github.com/doctrine/orm/issues/8471>`_)
|
||||
|
||||
A partial object is an object whose state is not fully initialized
|
||||
after being reconstituted from the database and that is
|
||||
disconnected from the rest of its data. The following section will
|
||||
|
||||
@@ -520,6 +520,9 @@ complete list of supported helper methods available:
|
||||
// Example - $qb->expr()->sqrt('u.currentBalance')
|
||||
public function sqrt($x); // Returns Expr\Func
|
||||
|
||||
// Example - $qb->expr()->mod('u.currentBalance', '10')
|
||||
public function mod($x); // Returns Expr\Func
|
||||
|
||||
// Example - $qb->expr()->count('u.firstname')
|
||||
public function count($x); // Returns Expr\Func
|
||||
|
||||
|
||||
@@ -332,7 +332,8 @@
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="optional"/>
|
||||
<xs:attribute name="columns" type="xs:string" use="required"/>
|
||||
<xs:attribute name="columns" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="fields" type="xs:string" use="optional"/>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
@@ -351,6 +352,7 @@
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="optional"/>
|
||||
<xs:attribute name="columns" type="xs:string" use="required"/>
|
||||
<xs:attribute name="fields" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="flags" type="xs:string" use="optional"/>
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
@@ -539,7 +541,7 @@
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="target-entity" type="xs:string" use="required" />
|
||||
<xs:attribute name="target-entity" type="xs:string" />
|
||||
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
@@ -557,7 +559,7 @@
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="target-entity" type="xs:string" use="required" />
|
||||
<xs:attribute name="target-entity" type="xs:string" />
|
||||
<xs:attribute name="mapped-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
|
||||
|
||||
@@ -24,7 +24,8 @@ use Countable;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\DBAL\Cache\QueryCacheProfile;
|
||||
use Doctrine\DBAL\Driver\Statement;
|
||||
use Doctrine\DBAL\Driver\ResultStatement;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Cache\Logging\CacheLogger;
|
||||
use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
use Doctrine\ORM\Cache\TimestampCacheKey;
|
||||
@@ -49,9 +50,6 @@ use function ksort;
|
||||
use function reset;
|
||||
use function serialize;
|
||||
use function sha1;
|
||||
use function trigger_error;
|
||||
|
||||
use const E_USER_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Base contract for ORM queries. Base class for Query and NativeQuery.
|
||||
@@ -123,7 +121,7 @@ abstract class AbstractQuery
|
||||
*/
|
||||
protected $_hydrationMode = self::HYDRATE_OBJECT;
|
||||
|
||||
/** @var QueryCacheProfile */
|
||||
/** @var QueryCacheProfile|null */
|
||||
protected $_queryCacheProfile;
|
||||
|
||||
/**
|
||||
@@ -289,7 +287,7 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Retrieves the associated EntityManager of this Query instance.
|
||||
*
|
||||
* @return EntityManager
|
||||
* @return EntityManagerInterface
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
@@ -314,6 +312,7 @@ abstract class AbstractQuery
|
||||
* Get all defined parameters.
|
||||
*
|
||||
* @return ArrayCollection The defined query parameters.
|
||||
* @psalm-return ArrayCollection<int, Parameter>
|
||||
*/
|
||||
public function getParameters()
|
||||
{
|
||||
@@ -325,7 +324,7 @@ abstract class AbstractQuery
|
||||
*
|
||||
* @param mixed $key The key (index or name) of the bound parameter.
|
||||
*
|
||||
* @return Query\Parameter|null The value of the bound parameter, or NULL if not available.
|
||||
* @return Parameter|null The value of the bound parameter, or NULL if not available.
|
||||
*/
|
||||
public function getParameter($key)
|
||||
{
|
||||
@@ -346,10 +345,9 @@ abstract class AbstractQuery
|
||||
* Sets a collection of query parameters.
|
||||
*
|
||||
* @param ArrayCollection|mixed[] $parameters
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
|
||||
*
|
||||
* @return static This query instance.
|
||||
*
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
|
||||
*/
|
||||
public function setParameters($parameters)
|
||||
{
|
||||
@@ -402,10 +400,9 @@ abstract class AbstractQuery
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed[]|string|int|float|bool
|
||||
* @psalm-return array|scalar
|
||||
*
|
||||
* @throws ORMInvalidArgumentException
|
||||
*
|
||||
* @psalm-return array|scalar
|
||||
*/
|
||||
public function processParameterValue($value)
|
||||
{
|
||||
@@ -509,10 +506,8 @@ abstract class AbstractQuery
|
||||
|
||||
/**
|
||||
* Allows to translate entity namespaces to full qualified names.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function translateNamespaces(Query\ResultSetMapping $rsm)
|
||||
private function translateNamespaces(Query\ResultSetMapping $rsm): void
|
||||
{
|
||||
$translate = function ($alias): string {
|
||||
return $this->_em->getClassMetadata($alias)->getName();
|
||||
@@ -593,6 +588,7 @@ abstract class AbstractQuery
|
||||
*/
|
||||
public function setResultCacheDriver($resultCacheDriver = null)
|
||||
{
|
||||
/** @phpstan-ignore-next-line */
|
||||
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
|
||||
throw ORMException::invalidResultCacheDriver();
|
||||
}
|
||||
@@ -723,7 +719,7 @@ abstract class AbstractQuery
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QueryCacheProfile
|
||||
* @return QueryCacheProfile|null
|
||||
*/
|
||||
public function getQueryCacheProfile()
|
||||
{
|
||||
@@ -949,7 +945,7 @@ abstract class AbstractQuery
|
||||
* Executes the query and returns an IterableResult that can be used to incrementally
|
||||
* iterate over the result.
|
||||
*
|
||||
* @deprecated
|
||||
* @deprecated 2.8 Use {@see toIterable} instead. See https://github.com/doctrine/orm/issues/8463
|
||||
*
|
||||
* @param ArrayCollection|mixed[]|null $parameters The query parameters.
|
||||
* @param string|int|null $hydrationMode The hydration mode to use.
|
||||
@@ -958,9 +954,11 @@ abstract class AbstractQuery
|
||||
*/
|
||||
public function iterate($parameters = null, $hydrationMode = null)
|
||||
{
|
||||
@trigger_error(
|
||||
'Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0. Use toIterable() instead.',
|
||||
E_USER_DEPRECATED
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8463',
|
||||
'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use toIterable() instead.',
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
if ($hydrationMode !== null) {
|
||||
@@ -981,8 +979,9 @@ abstract class AbstractQuery
|
||||
* Executes the query and returns an iterable that can be used to incrementally
|
||||
* iterate over the result.
|
||||
*
|
||||
* @param ArrayCollection|mixed[] $parameters The query parameters.
|
||||
* @param string|int|null $hydrationMode The hydration mode to use.
|
||||
* @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
|
||||
*
|
||||
* @return iterable<mixed>
|
||||
*/
|
||||
@@ -1015,6 +1014,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
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -1032,6 +1032,7 @@ abstract class AbstractQuery
|
||||
*
|
||||
* @param ArrayCollection|mixed[]|null $parameters
|
||||
* @param string|int|null $hydrationMode
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -1045,7 +1046,7 @@ abstract class AbstractQuery
|
||||
$this->setParameters($parameters);
|
||||
}
|
||||
|
||||
$setCacheEntry = static function (): void {
|
||||
$setCacheEntry = static function ($data): void {
|
||||
};
|
||||
|
||||
if ($this->_hydrationCacheProfile !== null) {
|
||||
@@ -1091,6 +1092,7 @@ abstract class AbstractQuery
|
||||
*
|
||||
* @param ArrayCollection|mixed[]|null $parameters
|
||||
* @param string|int|null $hydrationMode
|
||||
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -1129,10 +1131,7 @@ abstract class AbstractQuery
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TimestampCacheKey|null
|
||||
*/
|
||||
private function getTimestampKey()
|
||||
private function getTimestampKey(): ?TimestampCacheKey
|
||||
{
|
||||
$entityName = reset($this->_resultSetMapping->aliasMap);
|
||||
|
||||
@@ -1203,7 +1202,9 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Executes the query and returns a the resulting Statement object.
|
||||
*
|
||||
* @return Statement The executed database statement that holds the results.
|
||||
* @return ResultStatement|int The executed database statement that holds
|
||||
* the results, or an integer indicating how
|
||||
* many rows were affected.
|
||||
*/
|
||||
abstract protected function _doExecute();
|
||||
|
||||
|
||||
@@ -63,6 +63,9 @@ class CacheConfiguration
|
||||
return $this->cacheLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setCacheLogger(CacheLogger $logger)
|
||||
{
|
||||
$this->cacheLogger = $logger;
|
||||
@@ -80,6 +83,9 @@ class CacheConfiguration
|
||||
return $this->regionsConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setRegionsConfiguration(RegionsConfiguration $regionsConfig)
|
||||
{
|
||||
$this->regionsConfig = $regionsConfig;
|
||||
@@ -99,6 +105,9 @@ class CacheConfiguration
|
||||
return $this->queryValidator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setQueryValidator(QueryCacheValidator $validator)
|
||||
{
|
||||
$this->queryValidator = $validator;
|
||||
|
||||
@@ -30,9 +30,9 @@ use Doctrine\ORM\PersistentCollection;
|
||||
interface CollectionHydrator
|
||||
{
|
||||
/**
|
||||
* @param ClassMetadata $metadata The entity metadata.
|
||||
* @param CollectionCacheKey $key The cached collection key.
|
||||
* @param mixed[]|Collection $collection The collection.
|
||||
* @param ClassMetadata $metadata The entity metadata.
|
||||
* @param CollectionCacheKey $key The cached collection key.
|
||||
* @param array|mixed[]|Collection $collection The collection.
|
||||
*
|
||||
* @return CollectionCacheEntry
|
||||
*/
|
||||
|
||||
@@ -48,7 +48,7 @@ class DefaultCache implements Cache
|
||||
/** @var QueryCache[] */
|
||||
private $queryCaches = [];
|
||||
|
||||
/** @var QueryCache */
|
||||
/** @var QueryCache|null */
|
||||
private $defaultQueryCache;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
@@ -278,10 +278,8 @@ class DefaultCache implements Cache
|
||||
/**
|
||||
* @param ClassMetadata $metadata The entity metadata.
|
||||
* @param mixed $identifier The entity identifier.
|
||||
*
|
||||
* @return EntityCacheKey
|
||||
*/
|
||||
private function buildEntityCacheKey(ClassMetadata $metadata, $identifier)
|
||||
private function buildEntityCacheKey(ClassMetadata $metadata, $identifier): EntityCacheKey
|
||||
{
|
||||
if (! is_array($identifier)) {
|
||||
$identifier = $this->toIdentifierArray($metadata, $identifier);
|
||||
@@ -294,11 +292,12 @@ class DefaultCache implements Cache
|
||||
* @param ClassMetadata $metadata The entity metadata.
|
||||
* @param string $association The field name that represents the association.
|
||||
* @param mixed $ownerIdentifier The identifier of the owning entity.
|
||||
*
|
||||
* @return CollectionCacheKey
|
||||
*/
|
||||
private function buildCollectionCacheKey(ClassMetadata $metadata, $association, $ownerIdentifier)
|
||||
{
|
||||
private function buildCollectionCacheKey(
|
||||
ClassMetadata $metadata,
|
||||
string $association,
|
||||
$ownerIdentifier
|
||||
): CollectionCacheKey {
|
||||
if (! is_array($ownerIdentifier)) {
|
||||
$ownerIdentifier = $this->toIdentifierArray($metadata, $ownerIdentifier);
|
||||
}
|
||||
@@ -312,7 +311,7 @@ class DefaultCache implements Cache
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function toIdentifierArray(ClassMetadata $metadata, $identifier)
|
||||
private function toIdentifierArray(ClassMetadata $metadata, $identifier): array
|
||||
{
|
||||
if (is_object($identifier) && $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($identifier))) {
|
||||
$identifier = $this->uow->getSingleIdentifierValue($identifier);
|
||||
|
||||
@@ -70,6 +70,8 @@ class DefaultCacheFactory implements CacheFactory
|
||||
|
||||
/**
|
||||
* @param string $fileLockRegionDirectory
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setFileLockRegionDirectory($fileLockRegionDirectory)
|
||||
{
|
||||
@@ -84,11 +86,17 @@ class DefaultCacheFactory implements CacheFactory
|
||||
return $this->fileLockRegionDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setRegion(Region $region)
|
||||
{
|
||||
$this->regions[$region->getName()] = $region;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setTimestampRegion(TimestampRegion $region)
|
||||
{
|
||||
$this->timestampRegion = $region;
|
||||
@@ -111,6 +119,10 @@ class DefaultCacheFactory implements CacheFactory
|
||||
}
|
||||
|
||||
if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) {
|
||||
if (! $region instanceof ConcurrentRegion) {
|
||||
throw new InvalidArgumentException(sprintf('Unable to use access strategy type of [%s] without a ConcurrentRegion', $usage));
|
||||
}
|
||||
|
||||
return new ReadWriteCachedEntityPersister($persister, $region, $em, $metadata);
|
||||
}
|
||||
|
||||
@@ -134,6 +146,10 @@ class DefaultCacheFactory implements CacheFactory
|
||||
}
|
||||
|
||||
if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) {
|
||||
if (! $region instanceof ConcurrentRegion) {
|
||||
throw new InvalidArgumentException(sprintf('Unable to use access strategy type of [%s] without a ConcurrentRegion', $usage));
|
||||
}
|
||||
|
||||
return new ReadWriteCachedCollectionPersister($persister, $region, $em, $mapping);
|
||||
}
|
||||
|
||||
@@ -201,18 +217,13 @@ class DefaultCacheFactory implements CacheFactory
|
||||
}
|
||||
|
||||
$directory = $this->fileLockRegionDirectory . DIRECTORY_SEPARATOR . $cache['region'];
|
||||
$region = new FileLockRegion($region, $directory, $this->regionsConfig->getLockLifetime($cache['region']));
|
||||
$region = new FileLockRegion($region, $directory, (string) $this->regionsConfig->getLockLifetime($cache['region']));
|
||||
}
|
||||
|
||||
return $this->regions[$cache['region']] = $region;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return CacheAdapter
|
||||
*/
|
||||
private function createRegionCache($name)
|
||||
private function createRegionCache(string $name): CacheAdapter
|
||||
{
|
||||
$cacheAdapter = clone $this->cache;
|
||||
|
||||
|
||||
@@ -345,10 +345,9 @@ class DefaultQueryCache implements QueryCache
|
||||
* @param mixed $assocValue
|
||||
*
|
||||
* @return mixed[]|null
|
||||
*
|
||||
* @psalm-return array{targetEntity: string, type: mixed, list?: array[], identifier?: array}|null
|
||||
* @psalm-return array{targetEntity: class-string, type: mixed, list?: array[], identifier?: array}|null
|
||||
*/
|
||||
private function storeAssociationCache(QueryCacheKey $key, array $assoc, $assocValue)
|
||||
private function storeAssociationCache(QueryCacheKey $key, array $assoc, $assocValue): ?array
|
||||
{
|
||||
$assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']);
|
||||
$assocMetadata = $assocPersister->getClassMetadata();
|
||||
@@ -398,13 +397,15 @@ class DefaultQueryCache implements QueryCache
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $assocAlias
|
||||
* @param object $entity
|
||||
*
|
||||
* @return array<object>|object
|
||||
*/
|
||||
private function getAssociationValue(ResultSetMapping $rsm, $assocAlias, $entity)
|
||||
{
|
||||
private function getAssociationValue(
|
||||
ResultSetMapping $rsm,
|
||||
string $assocAlias,
|
||||
$entity
|
||||
) {
|
||||
$path = [];
|
||||
$alias = $assocAlias;
|
||||
|
||||
@@ -441,7 +442,7 @@ class DefaultQueryCache implements QueryCache
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($path)) {
|
||||
if ($path === []) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,12 +40,14 @@ class EntityCacheEntry implements CacheEntry
|
||||
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
|
||||
*
|
||||
* @var string The entity class name
|
||||
* @psalm-var class-string
|
||||
*/
|
||||
public $class;
|
||||
|
||||
/**
|
||||
* @param string $class The entity class.
|
||||
* @param array<string,mixed> $data The entity data.
|
||||
* @psalm-param class-string $class
|
||||
*/
|
||||
public function __construct($class, array $data)
|
||||
{
|
||||
|
||||
@@ -34,6 +34,8 @@ class CacheLoggerChain implements CacheLogger
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setLogger($name, CacheLogger $logger)
|
||||
{
|
||||
|
||||
@@ -194,6 +194,8 @@ class StatisticsCacheLogger implements CacheLogger
|
||||
* Clear region statistics
|
||||
*
|
||||
* @param string $regionName The name of the cache region.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearRegionStats($regionName)
|
||||
{
|
||||
@@ -204,6 +206,8 @@ class StatisticsCacheLogger implements CacheLogger
|
||||
|
||||
/**
|
||||
* Clear all statistics
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearStats()
|
||||
{
|
||||
|
||||
@@ -72,7 +72,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
/** @var CollectionHydrator */
|
||||
protected $hydrator;
|
||||
|
||||
/** @var CacheLogger */
|
||||
/** @var CacheLogger|null */
|
||||
protected $cacheLogger;
|
||||
|
||||
/**
|
||||
@@ -239,6 +239,8 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
|
||||
/**
|
||||
* Clears cache entries related to the current collection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function evictCollectionCache(PersistentCollection $collection)
|
||||
{
|
||||
@@ -258,6 +260,8 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
|
||||
/**
|
||||
* @param string $targetEntity
|
||||
* @param object $element
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function evictElementCache($targetEntity, $element)
|
||||
{
|
||||
|
||||
@@ -52,7 +52,7 @@ interface CachedCollectionPersister extends CachedPersister, CollectionPersister
|
||||
/**
|
||||
* Stores a collection into cache
|
||||
*
|
||||
* @param mixed[]|Collection $elements
|
||||
* @param array|mixed[]|Collection $elements
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
@@ -75,7 +75,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/** @var Cache */
|
||||
protected $cache;
|
||||
|
||||
/** @var CacheLogger */
|
||||
/** @var CacheLogger|null */
|
||||
protected $cacheLogger;
|
||||
|
||||
/** @var string */
|
||||
@@ -226,7 +226,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
/**
|
||||
* @param object $entity
|
||||
*/
|
||||
private function storeJoinedAssociations($entity)
|
||||
private function storeJoinedAssociations($entity): void
|
||||
{
|
||||
if ($this->joinedAssociations === null) {
|
||||
$associations = [];
|
||||
@@ -344,7 +344,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
|
||||
*/
|
||||
public function load(array $criteria, $entity = null, $assoc = null, array $hints = [], $lockMode = null, $limit = null, ?array $orderBy = null)
|
||||
{
|
||||
if ($entity !== null || $assoc !== null || ! empty($hints) || $lockMode !== null) {
|
||||
if ($entity !== null || $assoc !== null || $hints !== [] || $lockMode !== null) {
|
||||
return $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy);
|
||||
}
|
||||
|
||||
|
||||
@@ -100,17 +100,14 @@ class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
|
||||
|
||||
/**
|
||||
* @param object $entity
|
||||
* @param bool $isChanged
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function updateCache($entity, $isChanged)
|
||||
private function updateCache($entity, bool $isChanged): bool
|
||||
{
|
||||
$class = $this->metadataFactory->getMetadataFor(get_class($entity));
|
||||
$key = new EntityCacheKey($class->rootEntityName, $this->uow->getEntityIdentifier($entity));
|
||||
$entry = $this->hydrator->buildCacheEntry($class, $key, $entity);
|
||||
$cached = $this->region->put($key, $entry);
|
||||
$isChanged = $isChanged ?: $cached;
|
||||
$isChanged = $isChanged || $cached;
|
||||
|
||||
if ($this->cacheLogger && $cached) {
|
||||
$this->cacheLogger->entityCachePut($this->regionName, $key);
|
||||
|
||||
@@ -129,6 +129,8 @@ class DefaultRegion implements Region
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function put(CacheKey $key, CacheEntry $entry, ?Lock $lock = null)
|
||||
{
|
||||
@@ -137,6 +139,8 @@ class DefaultRegion implements Region
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function evict(CacheKey $key)
|
||||
{
|
||||
@@ -145,6 +149,8 @@ class DefaultRegion implements Region
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function evictAll()
|
||||
{
|
||||
|
||||
@@ -63,8 +63,8 @@ class FileLockRegion implements ConcurrentRegion
|
||||
private $lockLifetime;
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
* @param string $lockLifetime
|
||||
* @param string $directory
|
||||
* @param numeric-string $lockLifetime
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
@@ -83,10 +83,7 @@ class FileLockRegion implements ConcurrentRegion
|
||||
$this->lockLifetime = $lockLifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function isLocked(CacheKey $key, ?Lock $lock = null)
|
||||
private function isLocked(CacheKey $key, ?Lock $lock = null): bool
|
||||
{
|
||||
$filename = $this->getLockFileName($key);
|
||||
|
||||
@@ -117,30 +114,23 @@ class FileLockRegion implements ConcurrentRegion
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function getLockFileName(CacheKey $key)
|
||||
private function getLockFileName(CacheKey $key): string
|
||||
{
|
||||
return $this->directory . DIRECTORY_SEPARATOR . $key->hash . '.' . self::LOCK_EXTENSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
*
|
||||
* @return string
|
||||
* @return string|false
|
||||
*/
|
||||
private function getLockContent($filename)
|
||||
private function getLockContent(string $filename)
|
||||
{
|
||||
return @file_get_contents($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
*
|
||||
* @return int
|
||||
* @return int|false
|
||||
*/
|
||||
private function getLockTime($filename)
|
||||
private function getLockTime(string $filename)
|
||||
{
|
||||
return @fileatime($filename);
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ class RegionsConfiguration
|
||||
|
||||
/**
|
||||
* @param int $defaultLifetime
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDefaultLifetime($defaultLifetime)
|
||||
{
|
||||
@@ -73,6 +75,8 @@ class RegionsConfiguration
|
||||
|
||||
/**
|
||||
* @param int $defaultLockLifetime
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDefaultLockLifetime($defaultLockLifetime)
|
||||
{
|
||||
@@ -92,6 +96,8 @@ class RegionsConfiguration
|
||||
/**
|
||||
* @param string $name
|
||||
* @param int $lifetime
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setLifetime($name, $lifetime)
|
||||
{
|
||||
@@ -111,6 +117,8 @@ class RegionsConfiguration
|
||||
/**
|
||||
* @param string $name
|
||||
* @param int $lifetime
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setLockLifetime($name, $lifetime)
|
||||
{
|
||||
|
||||
@@ -48,10 +48,7 @@ class TimestampQueryCacheValidator implements QueryCacheValidator
|
||||
return $entry->time + $key->lifetime > microtime(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function regionUpdated(QueryCacheKey $key, QueryCacheEntry $entry)
|
||||
private function regionUpdated(QueryCacheKey $key, QueryCacheEntry $entry): bool
|
||||
{
|
||||
if ($key->timestampKey === null) {
|
||||
return false;
|
||||
|
||||
@@ -26,7 +26,9 @@ use Doctrine\Common\Annotations\CachedReader;
|
||||
use Doctrine\Common\Annotations\SimpleAnnotationReader;
|
||||
use Doctrine\Common\Cache\ArrayCache;
|
||||
use Doctrine\Common\Cache\Cache as CacheDriver;
|
||||
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
|
||||
use Doctrine\Common\Proxy\AbstractProxyFactory;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Cache\CacheConfiguration;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
|
||||
@@ -41,8 +43,10 @@ use Doctrine\ORM\Repository\DefaultRepositoryFactory;
|
||||
use Doctrine\ORM\Repository\RepositoryFactory;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use ReflectionClass;
|
||||
|
||||
use function class_exists;
|
||||
use function strtolower;
|
||||
use function trim;
|
||||
|
||||
@@ -151,10 +155,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* is true, the notation `@Entity` will work, otherwise, the notation `@ORM\Entity` will be supported.
|
||||
*
|
||||
* @param bool $useSimpleAnnotationReader
|
||||
* @psalm-param string|list<string> $paths
|
||||
*
|
||||
* @return AnnotationDriver
|
||||
*
|
||||
* @psalm-param string|list<string> $paths
|
||||
*/
|
||||
public function newDefaultAnnotationDriver($paths = [], $useSimpleAnnotationReader = true)
|
||||
{
|
||||
@@ -164,13 +167,16 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
// Register the ORM Annotations in the AnnotationRegistry
|
||||
$reader = new SimpleAnnotationReader();
|
||||
$reader->addNamespace('Doctrine\ORM\Mapping');
|
||||
$cachedReader = new CachedReader($reader, new ArrayCache());
|
||||
} else {
|
||||
$reader = new AnnotationReader();
|
||||
}
|
||||
|
||||
return new AnnotationDriver($cachedReader, (array) $paths);
|
||||
if (class_exists(ArrayCache::class)) {
|
||||
$reader = new CachedReader($reader, new ArrayCache());
|
||||
}
|
||||
|
||||
return new AnnotationDriver(
|
||||
new CachedReader(new AnnotationReader(), new ArrayCache()),
|
||||
$reader,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
@@ -209,9 +215,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
/**
|
||||
* Sets the entity alias map.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array<string, string> $entityNamespaces
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setEntityNamespaces(array $entityNamespaces)
|
||||
{
|
||||
@@ -283,23 +289,55 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for metadata caching.
|
||||
*
|
||||
* @deprecated Deprecated in favor of getMetadataCache
|
||||
*
|
||||
* @return CacheDriver|null
|
||||
*/
|
||||
public function getMetadataCacheImpl()
|
||||
{
|
||||
return $this->_attributes['metadataCacheImpl'] ?? null;
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8650',
|
||||
'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use getMetadataCache() instead.',
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
if (isset($this->_attributes['metadataCacheImpl'])) {
|
||||
return $this->_attributes['metadataCacheImpl'];
|
||||
}
|
||||
|
||||
return isset($this->_attributes['metadataCache']) ? DoctrineProvider::wrap($this->_attributes['metadataCache']) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver implementation that is used for metadata caching.
|
||||
*
|
||||
* @deprecated Deprecated in favor of setMetadataCache
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setMetadataCacheImpl(CacheDriver $cacheImpl)
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8650',
|
||||
'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use setMetadataCache() instead.',
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
$this->_attributes['metadataCacheImpl'] = $cacheImpl;
|
||||
}
|
||||
|
||||
public function getMetadataCache(): ?CacheItemPoolInterface
|
||||
{
|
||||
return $this->_attributes['metadataCache'] ?? null;
|
||||
}
|
||||
|
||||
public function setMetadataCache(CacheItemPoolInterface $cache): void
|
||||
{
|
||||
$this->_attributes['metadataCache'] = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a named DQL query to the configuration.
|
||||
*
|
||||
@@ -350,13 +388,13 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*
|
||||
* @param string $name The name of the query.
|
||||
*
|
||||
* @throws ORMException
|
||||
*
|
||||
* @psalm-return array{string, ResultSetMapping} A tuple with the first
|
||||
* element being the SQL
|
||||
* string and the second
|
||||
* element being the
|
||||
* ResultSetMapping.
|
||||
*
|
||||
* @throws ORMException
|
||||
*/
|
||||
public function getNamedNativeQuery($name)
|
||||
{
|
||||
@@ -388,6 +426,14 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
throw ORMException::queryCacheUsesNonPersistentCache($queryCacheImpl);
|
||||
}
|
||||
|
||||
if ($this->getAutoGenerateProxyClasses()) {
|
||||
throw ORMException::proxyClassesAlwaysRegenerating();
|
||||
}
|
||||
|
||||
if ($this->getMetadataCache()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$metadataCacheImpl = $this->getMetadataCacheImpl();
|
||||
|
||||
if (! $metadataCacheImpl) {
|
||||
@@ -397,10 +443,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
if ($metadataCacheImpl instanceof ArrayCache) {
|
||||
throw ORMException::metadataCacheUsesNonPersistentCache($metadataCacheImpl);
|
||||
}
|
||||
|
||||
if ($this->getAutoGenerateProxyClasses()) {
|
||||
throw ORMException::proxyClassesAlwaysRegenerating();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,7 +468,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* @param string $name
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @psalm-return ?class-string
|
||||
*/
|
||||
public function getCustomStringFunction($name)
|
||||
@@ -444,10 +485,10 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*
|
||||
* Any previously added string functions are discarded.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array<string, class-string> $functions The map of custom
|
||||
* DQL string functions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCustomStringFunctions(array $functions)
|
||||
{
|
||||
@@ -479,7 +520,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* @param string $name
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @psalm-return ?class-string
|
||||
*/
|
||||
public function getCustomNumericFunction($name)
|
||||
@@ -497,10 +537,10 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*
|
||||
* Any previously added numeric functions are discarded.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array<string, class-string> $functions The map of custom
|
||||
* DQL numeric functions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCustomNumericFunctions(array $functions)
|
||||
{
|
||||
@@ -518,10 +558,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*
|
||||
* @param string $name Function name.
|
||||
* @param string|callable $className Class name or a callable that returns the function.
|
||||
* @psalm-param class-string|callable $className
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param class-string|callable $className
|
||||
*/
|
||||
public function addCustomDatetimeFunction($name, $className)
|
||||
{
|
||||
@@ -534,7 +573,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* @param string $name
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @psalm-return ?class-string $name
|
||||
*/
|
||||
public function getCustomDatetimeFunction($name)
|
||||
@@ -553,10 +591,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* Any previously added date/time functions are discarded.
|
||||
*
|
||||
* @param array $functions The map of custom DQL date/time functions.
|
||||
* @psalm-param array<string, string> $functions
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array<string, string> $functions
|
||||
*/
|
||||
public function setCustomDatetimeFunctions(array $functions)
|
||||
{
|
||||
@@ -587,7 +624,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* @param string $modeName The hydration mode name.
|
||||
*
|
||||
* @return string|null The hydrator class name.
|
||||
*
|
||||
* @psalm-return ?class-string
|
||||
*/
|
||||
public function getCustomHydrationMode($modeName)
|
||||
@@ -599,10 +635,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* Adds a custom hydration mode.
|
||||
*
|
||||
* @param string $modeName The hydration mode name.
|
||||
* @psalm-param class-string $hydrator The hydrator class name.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param class-string $hydrator The hydrator class name.
|
||||
*/
|
||||
public function addCustomHydrationMode($modeName, $hydrator)
|
||||
{
|
||||
@@ -613,10 +648,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* Sets a class metadata factory.
|
||||
*
|
||||
* @param string $cmfName
|
||||
* @psalm-param class-string $cmfName
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param class-string $cmfName
|
||||
*/
|
||||
public function setClassMetadataFactoryName($cmfName)
|
||||
{
|
||||
@@ -625,7 +659,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @psalm-return class-string
|
||||
*/
|
||||
public function getClassMetadataFactoryName()
|
||||
@@ -642,6 +675,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*
|
||||
* @param string $name The name of the filter.
|
||||
* @param string $className The class name of the filter.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addFilter($name, $className)
|
||||
{
|
||||
@@ -655,7 +690,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*
|
||||
* @return string|null The class name of the filter, or null if it is not
|
||||
* defined.
|
||||
*
|
||||
* @psalm-return ?class-string
|
||||
*/
|
||||
public function getFilterClassName($name)
|
||||
@@ -687,7 +721,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* Get default repository class.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @psalm-return class-string
|
||||
*/
|
||||
public function getDefaultRepositoryClassName()
|
||||
@@ -745,6 +778,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
|
||||
/**
|
||||
* Set the entity listener resolver.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setEntityListenerResolver(EntityListenerResolver $resolver)
|
||||
{
|
||||
@@ -767,6 +802,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
|
||||
/**
|
||||
* Set the entity repository factory.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setRepositoryFactory(RepositoryFactory $repositoryFactory)
|
||||
{
|
||||
@@ -835,6 +872,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* Sets array of query hints, which will be applied to every query in application
|
||||
*
|
||||
* @psalm-param array<string, mixed> $defaultQueryHints
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDefaultQueryHints(array $defaultQueryHints)
|
||||
{
|
||||
@@ -858,6 +897,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
*
|
||||
* @param string $name The name of the hint.
|
||||
* @param mixed $value The value of the hint.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDefaultQueryHint($name, $value)
|
||||
{
|
||||
|
||||
@@ -21,11 +21,14 @@
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Doctrine\Common\Cache\Psr6\CacheAdapter;
|
||||
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\Common\Util\ClassUtils;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||
@@ -47,10 +50,8 @@ use function is_callable;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function ltrim;
|
||||
use function method_exists;
|
||||
use function sprintf;
|
||||
use function trigger_error;
|
||||
|
||||
use const E_USER_DEPRECATED;
|
||||
|
||||
/**
|
||||
* The EntityManager is the central access point to ORM functionality.
|
||||
@@ -166,7 +167,8 @@ use const E_USER_DEPRECATED;
|
||||
|
||||
$this->metadataFactory = new $metadataFactoryClassName();
|
||||
$this->metadataFactory->setEntityManager($this);
|
||||
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
|
||||
|
||||
$this->configureMetadataCache();
|
||||
|
||||
$this->repositoryFactory = $config->getRepositoryFactory();
|
||||
$this->unitOfWork = new UnitOfWork($this);
|
||||
@@ -284,9 +286,7 @@ use const E_USER_DEPRECATED;
|
||||
*
|
||||
* Internal note: Performance-sensitive method.
|
||||
*
|
||||
* @param string $className
|
||||
*
|
||||
* @return ClassMetadata
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getClassMetadata($className)
|
||||
{
|
||||
@@ -365,9 +365,11 @@ use const E_USER_DEPRECATED;
|
||||
public function flush($entity = null)
|
||||
{
|
||||
if ($entity !== null) {
|
||||
@trigger_error(
|
||||
'Calling ' . __METHOD__ . '() with any arguments to flush specific entities is deprecated and will not be supported in Doctrine ORM 3.0.',
|
||||
E_USER_DEPRECATED
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8459',
|
||||
'Calling %s() with any arguments to flush specific entities is deprecated and will not be supported in Doctrine ORM 3.0.',
|
||||
__METHOD__
|
||||
);
|
||||
}
|
||||
|
||||
@@ -386,8 +388,10 @@ use const E_USER_DEPRECATED;
|
||||
* during the search.
|
||||
* @param int|null $lockVersion The version of the entity to find when using
|
||||
* optimistic locking.
|
||||
* @psalm-param class-string<T> $className
|
||||
*
|
||||
* @return object|null The entity instance or NULL if the entity can not be found.
|
||||
* @psalm-return ?T
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
* @throws ORMInvalidArgumentException
|
||||
@@ -395,8 +399,6 @@ use const E_USER_DEPRECATED;
|
||||
* @throws ORMException
|
||||
*
|
||||
* @template T
|
||||
* @psalm-param class-string<T> $className
|
||||
* @psalm-return ?T
|
||||
*/
|
||||
public function find($className, $id, $lockMode = null, $lockVersion = null)
|
||||
{
|
||||
@@ -575,9 +577,11 @@ use const E_USER_DEPRECATED;
|
||||
}
|
||||
|
||||
if ($entityName !== null) {
|
||||
@trigger_error(
|
||||
'Calling ' . __METHOD__ . '() with any arguments to clear specific entities is deprecated and will not be supported in Doctrine ORM 3.0.',
|
||||
E_USER_DEPRECATED
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8460',
|
||||
'Calling %s() with any arguments to clear specific entities is deprecated and will not be supported in Doctrine ORM 3.0.',
|
||||
__METHOD__
|
||||
);
|
||||
}
|
||||
|
||||
@@ -678,8 +682,6 @@ use const E_USER_DEPRECATED;
|
||||
* Entities which previously referenced the detached entity will continue to
|
||||
* reference it.
|
||||
*
|
||||
* @deprecated 2.7 This method is being removed from the ORM and won't have any replacement
|
||||
*
|
||||
* @param object $entity The entity to detach.
|
||||
*
|
||||
* @return void
|
||||
@@ -688,8 +690,6 @@ use const E_USER_DEPRECATED;
|
||||
*/
|
||||
public function detach($entity)
|
||||
{
|
||||
@trigger_error('Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0.', E_USER_DEPRECATED);
|
||||
|
||||
if (! is_object($entity)) {
|
||||
throw ORMInvalidArgumentException::invalidObject('EntityManager#detach()', $entity);
|
||||
}
|
||||
@@ -713,7 +713,12 @@ use const E_USER_DEPRECATED;
|
||||
*/
|
||||
public function merge($entity)
|
||||
{
|
||||
@trigger_error('Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0.', E_USER_DEPRECATED);
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8461',
|
||||
'Method %s() is deprecated and will be removed in Doctrine ORM 3.0.',
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
if (! is_object($entity)) {
|
||||
throw ORMInvalidArgumentException::invalidObject('EntityManager#merge()', $entity);
|
||||
@@ -729,7 +734,12 @@ use const E_USER_DEPRECATED;
|
||||
*/
|
||||
public function copy($entity, $deep = false)
|
||||
{
|
||||
@trigger_error('Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0.', E_USER_DEPRECATED);
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8462',
|
||||
'Method %s() is deprecated and will be removed in Doctrine ORM 3.0.',
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
throw new BadMethodCallException('Not implemented.');
|
||||
}
|
||||
@@ -746,12 +756,12 @@ use const E_USER_DEPRECATED;
|
||||
* Gets the repository for an entity class.
|
||||
*
|
||||
* @param string $entityName The name of the entity.
|
||||
* @psalm-param class-string<T> $entityName
|
||||
*
|
||||
* @return ObjectRepository|EntityRepository The repository class.
|
||||
* @psalm-return EntityRepository<T>
|
||||
*
|
||||
* @template T
|
||||
* @psalm-param class-string<T> $entityName
|
||||
* @psalm-return EntityRepository<T>
|
||||
*/
|
||||
public function getRepository($entityName)
|
||||
{
|
||||
@@ -791,11 +801,9 @@ use const E_USER_DEPRECATED;
|
||||
/**
|
||||
* Throws an exception if the EntityManager is closed or currently not active.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws ORMException If the EntityManager is closed.
|
||||
*/
|
||||
private function errorIfClosed()
|
||||
private function errorIfClosed(): void
|
||||
{
|
||||
if ($this->closed) {
|
||||
throw ORMException::entityManagerClosed();
|
||||
@@ -980,4 +988,42 @@ use const E_USER_DEPRECATED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function configureMetadataCache(): void
|
||||
{
|
||||
$metadataCache = $this->config->getMetadataCache();
|
||||
if (! $metadataCache) {
|
||||
$this->configureLegacyMetadataCache();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We have a PSR-6 compatible metadata factory. Use cache directly
|
||||
if (method_exists($this->metadataFactory, 'setCache')) {
|
||||
$this->metadataFactory->setCache($metadataCache);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Wrap PSR-6 cache to provide doctrine/cache interface
|
||||
$this->metadataFactory->setCacheDriver(DoctrineProvider::wrap($metadataCache));
|
||||
}
|
||||
|
||||
private function configureLegacyMetadataCache(): void
|
||||
{
|
||||
$metadataCache = $this->config->getMetadataCacheImpl();
|
||||
if (! $metadataCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Metadata factory is not PSR-6 compatible. Use cache directly
|
||||
if (! method_exists($this->metadataFactory, 'setCache')) {
|
||||
$this->metadataFactory->setCacheDriver($metadataCache);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Wrap doctrine/cache to provide PSR-6 interface
|
||||
$this->metadataFactory->setCache(CacheAdapter::wrap($metadataCache));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,10 +34,21 @@ use Doctrine\Persistence\ObjectManager;
|
||||
/**
|
||||
* EntityManager interface
|
||||
*
|
||||
* @method Mapping\ClassMetadata getClassMetadata($className)
|
||||
* @method Mapping\ClassMetadataFactory getMetadataFactory()
|
||||
*/
|
||||
interface EntityManagerInterface extends ObjectManager
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @psalm-param class-string<T> $className
|
||||
*
|
||||
* @psalm-return EntityRepository<T>
|
||||
*
|
||||
* @template T
|
||||
*/
|
||||
public function getRepository($className);
|
||||
|
||||
/**
|
||||
* Returns the cache API for managing the second level cache regions or NULL if the cache is not enabled.
|
||||
*
|
||||
@@ -155,14 +166,14 @@ interface EntityManagerInterface extends ObjectManager
|
||||
*
|
||||
* @param string $entityName The name of the entity type.
|
||||
* @param mixed $id The entity identifier.
|
||||
* @psalm-param class-string<T> $entityName
|
||||
*
|
||||
* @return object|null The entity reference.
|
||||
* @psalm-return ?T
|
||||
*
|
||||
* @throws ORMException
|
||||
*
|
||||
* @template T
|
||||
* @psalm-param class-string<T> $entityName
|
||||
* @psalm-return ?T
|
||||
*/
|
||||
public function getReference($entityName, $id);
|
||||
|
||||
@@ -305,4 +316,16 @@ interface EntityManagerInterface extends ObjectManager
|
||||
* @return bool True, if the EM has a filter collection.
|
||||
*/
|
||||
public function hasFilters();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-param string|class-string<T> $className
|
||||
*
|
||||
* @return Mapping\ClassMetadata
|
||||
* @psalm-return Mapping\ClassMetadata<T>
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
public function getClassMetadata($className);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use function implode;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Exception thrown when a Proxy fails to retrieve an Entity result.
|
||||
@@ -47,4 +48,17 @@ class EntityNotFoundException extends ORMException
|
||||
'Entity of type \'' . $className . '\'' . ($ids ? ' for IDs ' . implode(', ', $ids) : '') . ' was not found'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance for which no identifier can be found
|
||||
*
|
||||
* @psalm-param class-string $className
|
||||
*/
|
||||
public static function noIdentifierFound(string $className): self
|
||||
{
|
||||
return new self(sprintf(
|
||||
'Unable to find "%s" entity identifier associated with the UnitOfWork',
|
||||
$className
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ use BadMethodCallException;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\Inflector\Inflector;
|
||||
use Doctrine\Inflector\InflectorFactory;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
@@ -35,9 +36,6 @@ use function lcfirst;
|
||||
use function sprintf;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use function trigger_error;
|
||||
|
||||
use const E_USER_DEPRECATED;
|
||||
|
||||
/**
|
||||
* An EntityRepository serves as a repository for entities with generic as well as
|
||||
@@ -111,24 +109,44 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
/**
|
||||
* Creates a new Query instance based on a predefined metadata named query.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @param string $queryName
|
||||
*
|
||||
* @return Query
|
||||
*/
|
||||
public function createNamedQuery($queryName)
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8592',
|
||||
'Named Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository',
|
||||
$queryName,
|
||||
$this->_class->name
|
||||
);
|
||||
|
||||
return $this->_em->createQuery($this->_class->getNamedQuery($queryName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a native SQL query.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @param string $queryName
|
||||
*
|
||||
* @return NativeQuery
|
||||
*/
|
||||
public function createNativeNamedQuery($queryName)
|
||||
{
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8592',
|
||||
'Named Native Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository',
|
||||
$queryName,
|
||||
$this->_class->name
|
||||
);
|
||||
|
||||
$queryMapping = $this->_class->getNamedNativeQuery($queryName);
|
||||
$rsm = new Query\ResultSetMappingBuilder($this->_em);
|
||||
$rsm->addNamedNativeQueryMapping($this->_class, $queryMapping);
|
||||
@@ -145,7 +163,12 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
@trigger_error('Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0.', E_USER_DEPRECATED);
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8460',
|
||||
'Calling %s() is deprecated and will not be supported in Doctrine ORM 3.0.',
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
$this->_em->clear($this->_class->rootEntityName);
|
||||
}
|
||||
@@ -160,7 +183,6 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
* @param int|null $lockVersion The lock version.
|
||||
*
|
||||
* @return object|null The entity instance or NULL if the entity can not be found.
|
||||
*
|
||||
* @psalm-return ?T
|
||||
*/
|
||||
public function find($id, $lockMode = null, $lockVersion = null)
|
||||
@@ -183,9 +205,9 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
*
|
||||
* @param int|null $limit
|
||||
* @param int|null $offset
|
||||
*
|
||||
* @psalm-param array<string, mixed> $criteria
|
||||
* @psalm-param list<string>|null $orderBy
|
||||
* @psalm-param array<string, string>|null $orderBy
|
||||
*
|
||||
* @psalm-return list<T> The objects.
|
||||
*/
|
||||
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null)
|
||||
@@ -198,10 +220,10 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
/**
|
||||
* Finds a single entity by a set of criteria.
|
||||
*
|
||||
* @return object|null The entity instance or NULL if the entity can not be found.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $criteria
|
||||
* @psalm-param array<string, string>|null $orderBy
|
||||
*
|
||||
* @return object|null The entity instance or NULL if the entity can not be found.
|
||||
* @psalm-return ?T
|
||||
*/
|
||||
public function findOneBy(array $criteria, ?array $orderBy = null)
|
||||
@@ -214,9 +236,10 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
/**
|
||||
* Counts entities by a set of criteria.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $criteria
|
||||
*
|
||||
* @return int The cardinality of the objects that match the given criteria.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $criteria
|
||||
* @todo Add this method to `ObjectRepository` interface in the next major release
|
||||
*/
|
||||
public function count(array $criteria)
|
||||
@@ -228,13 +251,12 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
* Adds support for magic method calls.
|
||||
*
|
||||
* @param string $method
|
||||
* @psalm-param list<mixed> $arguments
|
||||
*
|
||||
* @return mixed The returned value from the resolved method.
|
||||
*
|
||||
* @throws ORMException
|
||||
* @throws BadMethodCallException If the method called is invalid.
|
||||
*
|
||||
* @psalm-param list<mixed> $arguments
|
||||
*/
|
||||
public function __call($method, $arguments)
|
||||
{
|
||||
@@ -293,6 +315,7 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
* Select all elements from a selectable that match the expression and
|
||||
* return a new collection containing these elements.
|
||||
*
|
||||
* @return LazyCriteriaCollection
|
||||
* @psalm-return Collection<int, T>
|
||||
*/
|
||||
public function matching(Criteria $criteria)
|
||||
@@ -307,12 +330,11 @@ class EntityRepository implements ObjectRepository, Selectable
|
||||
*
|
||||
* @param string $method The method to call
|
||||
* @param string $by The property name used as condition
|
||||
* @psalm-param list<mixed> $arguments The arguments to pass at method call
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws ORMException If the method called is invalid or the requested field/association does not exist.
|
||||
*
|
||||
* @psalm-param list<mixed> $arguments The arguments to pass at method call
|
||||
*/
|
||||
private function resolveMagicCall(string $method, string $by, array $arguments)
|
||||
{
|
||||
|
||||
@@ -90,6 +90,8 @@ class ListenersInvoker
|
||||
* @param object $entity The Entity on which the event occurred.
|
||||
* @param EventArgs $event The Event args.
|
||||
* @param int $invoke Bitmask to invoke listeners.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function invoke(ClassMetadata $metadata, $eventName, $entity, EventArgs $event, $invoke)
|
||||
{
|
||||
|
||||
@@ -48,6 +48,9 @@ class OnClassMetadataNotFoundEventArgs extends ManagerEventArgs
|
||||
parent::__construct($objectManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setFoundMetadata(?ClassMetadata $classMetadata = null)
|
||||
{
|
||||
$this->foundMetadata = $classMetadata;
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
@@ -49,7 +48,7 @@ class OnClearEventArgs extends EventArgs
|
||||
/**
|
||||
* Retrieves associated EntityManager.
|
||||
*
|
||||
* @return EntityManager
|
||||
* @return EntityManagerInterface
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
@@ -42,7 +41,7 @@ class OnFlushEventArgs extends EventArgs
|
||||
/**
|
||||
* Retrieve associated EntityManager.
|
||||
*
|
||||
* @return EntityManager
|
||||
* @return EntityManagerInterface
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
|
||||
@@ -113,13 +113,9 @@ class PreUpdateEventArgs extends LifecycleEventArgs
|
||||
/**
|
||||
* Asserts the field exists in changeset.
|
||||
*
|
||||
* @param string $field
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function assertValidField($field)
|
||||
private function assertValidField(string $field): void
|
||||
{
|
||||
if (! isset($this->entityChangeSet[$field])) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
|
||||
@@ -146,10 +146,8 @@ class CommitOrderCalculator
|
||||
* Visit a given node definition for reordering.
|
||||
*
|
||||
* {@internal Highly performance-sensitive method.}
|
||||
*
|
||||
* @param stdClass $vertex
|
||||
*/
|
||||
private function visit($vertex)
|
||||
private function visit(stdClass $vertex): void
|
||||
{
|
||||
$vertex->state = self::IN_PROGRESS;
|
||||
|
||||
|
||||
@@ -24,12 +24,14 @@ use Doctrine\DBAL\Driver\ResultStatement;
|
||||
use Doctrine\DBAL\FetchMode;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Events;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Generator;
|
||||
use PDO;
|
||||
use ReflectionClass;
|
||||
|
||||
@@ -38,9 +40,6 @@ use function array_merge;
|
||||
use function count;
|
||||
use function end;
|
||||
use function in_array;
|
||||
use function trigger_error;
|
||||
|
||||
use const E_USER_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Base class for all hydrators. A hydrator is a class that provides some form
|
||||
@@ -123,16 +122,17 @@ abstract class AbstractHydrator
|
||||
*
|
||||
* @param object $stmt
|
||||
* @param object $resultSetMapping
|
||||
* @psalm-param array<string, mixed> $hints
|
||||
*
|
||||
* @return IterableResult
|
||||
*
|
||||
* @psalm-param array<string, mixed> $hints
|
||||
*/
|
||||
public function iterate($stmt, $resultSetMapping, array $hints = [])
|
||||
{
|
||||
@trigger_error(
|
||||
'Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0. Use toIterable() instead.',
|
||||
E_USER_DEPRECATED
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8463',
|
||||
'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use toIterable() instead.',
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
$this->_stmt = $stmt;
|
||||
@@ -151,9 +151,9 @@ abstract class AbstractHydrator
|
||||
/**
|
||||
* Initiates a row-by-row hydration.
|
||||
*
|
||||
* @return iterable<mixed>
|
||||
*
|
||||
* @psalm-param array<string, mixed> $hints
|
||||
*
|
||||
* @return Generator<int, mixed>
|
||||
*/
|
||||
public function toIterable(ResultStatement $stmt, ResultSetMapping $resultSetMapping, array $hints = []): iterable
|
||||
{
|
||||
@@ -195,10 +195,9 @@ abstract class AbstractHydrator
|
||||
*
|
||||
* @param object $stmt
|
||||
* @param object $resultSetMapping
|
||||
* @psalm-param array<string, string> $hints
|
||||
*
|
||||
* @return mixed[]
|
||||
*
|
||||
* @psalm-param array<string, string> $hints
|
||||
*/
|
||||
public function hydrateAll($stmt, $resultSetMapping, array $hints = [])
|
||||
{
|
||||
@@ -222,7 +221,7 @@ abstract class AbstractHydrator
|
||||
* Hydrates a single row returned by the current statement instance during
|
||||
* row-by-row hydration with {@link iterate()} or {@link toIterable()}.
|
||||
*
|
||||
* @return mixed
|
||||
* @return mixed[]|false
|
||||
*/
|
||||
public function hydrateRow()
|
||||
{
|
||||
@@ -322,14 +321,13 @@ abstract class AbstractHydrator
|
||||
* the values applied. Scalar values are kept in a specific key 'scalars'.
|
||||
*
|
||||
* @param mixed[] $data SQL Result Row.
|
||||
* @psalm-param array<string, string> $id Dql-Alias => ID-Hash.
|
||||
* @psalm-param array<string, bool> $nonemptyComponents Does this DQL-Alias has at least one non NULL value?
|
||||
*
|
||||
* @return array<string, array<string, mixed>> An array with all the fields
|
||||
* (name => value) of the data
|
||||
* row, grouped by their
|
||||
* component alias.
|
||||
*
|
||||
* @psalm-param array<string, string> $id Dql-Alias => ID-Hash.
|
||||
* @psalm-param array<string, bool> $nonemptyComponents Does this DQL-Alias has at least one non NULL value?
|
||||
* @psalm-return array{
|
||||
* data: array<array-key, array>,
|
||||
* newObjects?: array<array-key, array{
|
||||
@@ -415,6 +413,7 @@ abstract class AbstractHydrator
|
||||
* of elements as before.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $data
|
||||
*
|
||||
* @psalm-return array<string, mixed> The processed row.
|
||||
*/
|
||||
protected function gatherScalarRowData(&$data)
|
||||
@@ -541,6 +540,7 @@ abstract class AbstractHydrator
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @psalm-return non-empty-list<string>
|
||||
*/
|
||||
private function getDiscriminatorValues(ClassMetadata $classMetadata): array
|
||||
{
|
||||
|
||||
@@ -259,15 +259,16 @@ class ArrayHydrator extends AbstractHydrator
|
||||
* Updates the result pointer for an Entity. The result pointers point to the
|
||||
* last seen instance of each Entity type. This is used for graph construction.
|
||||
*
|
||||
* @param mixed[] $coll The element.
|
||||
* @param bool|int $index Index of the element in the collection.
|
||||
* @param string $dqlAlias
|
||||
* @param bool $oneToOne Whether it is a single-valued association or not.
|
||||
*
|
||||
* @return void
|
||||
* @param mixed[]|null $coll The element.
|
||||
* @param bool|int $index Index of the element in the collection.
|
||||
* @param bool $oneToOne Whether it is a single-valued association or not.
|
||||
*/
|
||||
private function updateResultPointer(array &$coll, $index, $dqlAlias, $oneToOne)
|
||||
{
|
||||
private function updateResultPointer(
|
||||
?array &$coll,
|
||||
$index,
|
||||
string $dqlAlias,
|
||||
bool $oneToOne
|
||||
): void {
|
||||
if ($coll === null) {
|
||||
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
|
||||
|
||||
|
||||
@@ -100,10 +100,9 @@ class HydrationException extends ORMException
|
||||
|
||||
/**
|
||||
* @param string $discrValue
|
||||
* @psalm-param array<string, string> $discrMap
|
||||
*
|
||||
* @return HydrationException
|
||||
*
|
||||
* @psalm-param array<string, string> $discrMap
|
||||
*/
|
||||
public static function invalidDiscriminatorValue($discrValue, $discrMap)
|
||||
{
|
||||
|
||||
@@ -39,7 +39,7 @@ class IterableResult implements Iterator
|
||||
/** @var int */
|
||||
private $_key = -1;
|
||||
|
||||
/** @var object|null */
|
||||
/** @var mixed[]|null */
|
||||
private $_current = null;
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Proxy\Proxy;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Proxy\Proxy;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use PDO;
|
||||
@@ -171,15 +171,16 @@ class ObjectHydrator extends AbstractHydrator
|
||||
/**
|
||||
* Initializes a related collection.
|
||||
*
|
||||
* @param object $entity The entity to which the collection belongs.
|
||||
* @param ClassMetadata $class
|
||||
* @param string $fieldName The name of the field on the entity that holds the collection.
|
||||
* @param string $parentDqlAlias Alias of the parent fetch joining this collection.
|
||||
*
|
||||
* @return PersistentCollection
|
||||
* @param object $entity The entity to which the collection belongs.
|
||||
* @param string $fieldName The name of the field on the entity that holds the collection.
|
||||
* @param string $parentDqlAlias Alias of the parent fetch joining this collection.
|
||||
*/
|
||||
private function initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias)
|
||||
{
|
||||
private function initRelatedCollection(
|
||||
$entity,
|
||||
ClassMetadata $class,
|
||||
string $fieldName,
|
||||
string $parentDqlAlias
|
||||
): PersistentCollection {
|
||||
$oid = spl_object_hash($entity);
|
||||
$relation = $class->associationMappings[$fieldName];
|
||||
$value = $class->reflFields[$fieldName]->getValue($entity);
|
||||
@@ -223,14 +224,13 @@ class ObjectHydrator extends AbstractHydrator
|
||||
* Gets an entity instance.
|
||||
*
|
||||
* @param string $dqlAlias The DQL alias of the entity's class.
|
||||
* @psalm-param array<string, mixed> $data The instance data.
|
||||
*
|
||||
* @return object The entity.
|
||||
* @return object
|
||||
*
|
||||
* @throws HydrationException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $data The instance data.
|
||||
*/
|
||||
private function getEntity(array $data, $dqlAlias)
|
||||
private function getEntity(array $data, string $dqlAlias)
|
||||
{
|
||||
$className = $this->_rsm->aliasMap[$dqlAlias];
|
||||
|
||||
@@ -273,13 +273,12 @@ class ObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $className
|
||||
* @psalm-param class-string $className
|
||||
* @psalm-param array<string, mixed> $data
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @psalm-param array<string, mixed> $data
|
||||
*/
|
||||
private function getEntityFromIdentityMap($className, array $data)
|
||||
private function getEntityFromIdentityMap(string $className, array $data)
|
||||
{
|
||||
// TODO: Abstract this code and UnitOfWork::createEntity() equivalent?
|
||||
$class = $this->_metadataCache[$className];
|
||||
@@ -433,7 +432,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// PATH B: Single-valued association
|
||||
$reflFieldValue = $reflField->getValue($parentObject);
|
||||
|
||||
if (! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && ! $reflFieldValue->__isInitialized__)) {
|
||||
if (! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && ! $reflFieldValue->__isInitialized())) {
|
||||
// we only need to take action if this value is null,
|
||||
// we refresh the entity or its an uninitialized proxy.
|
||||
if (isset($nonemptyComponents[$dqlAlias])) {
|
||||
|
||||
@@ -133,6 +133,11 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have inheritance in resultset, make sure the field belongs to the correct class
|
||||
if (isset($cacheKeyInfo['discriminatorValues']) && ! in_array((string) $discrColumnValue, $cacheKeyInfo['discriminatorValues'], true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if value is null before conversion (because some types convert null to something else)
|
||||
$valueIsNull = $value === null;
|
||||
|
||||
@@ -146,11 +151,6 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
|
||||
// Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
|
||||
if (! isset($data[$fieldName]) || ! $valueIsNull) {
|
||||
// If we have inheritance in resultset, make sure the field belongs to the correct class
|
||||
if (isset($cacheKeyInfo['discriminatorValues']) && ! in_array((string) $discrColumnValue, $cacheKeyInfo['discriminatorValues'], true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[$fieldName] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ final class HydrationCompleteHandler
|
||||
*
|
||||
* @param object $entity
|
||||
*/
|
||||
public function deferPostLoadInvoking(ClassMetadata $class, $entity)
|
||||
public function deferPostLoadInvoking(ClassMetadata $class, $entity): void
|
||||
{
|
||||
$invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postLoad);
|
||||
|
||||
@@ -71,7 +71,7 @@ final class HydrationCompleteHandler
|
||||
*
|
||||
* Method fires all deferred invocations of postLoad events
|
||||
*/
|
||||
public function hydrationComplete()
|
||||
public function hydrationComplete(): void
|
||||
{
|
||||
$toInvoke = $this->deferredPostLoadInvocations;
|
||||
$this->deferredPostLoadInvocations = [];
|
||||
|
||||
@@ -20,12 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* This annotation is used to override the mapping of a entity property.
|
||||
*
|
||||
* @Annotation
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
|
||||
final class AttributeOverride implements Annotation
|
||||
{
|
||||
/**
|
||||
|
||||
@@ -39,7 +39,7 @@ class ClassMetadataBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassMetadata
|
||||
* @return ClassMetadataInfo
|
||||
*/
|
||||
public function getClassMetadata()
|
||||
{
|
||||
@@ -138,10 +138,9 @@ class ClassMetadataBuilder
|
||||
* Adds Index.
|
||||
*
|
||||
* @param string $name
|
||||
* @psalm-param list<string> $columns
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @psalm-param list<string> $columns
|
||||
*/
|
||||
public function addIndex(array $columns, $name)
|
||||
{
|
||||
@@ -158,10 +157,9 @@ class ClassMetadataBuilder
|
||||
* Adds Unique Constraint.
|
||||
*
|
||||
* @param string $name
|
||||
* @psalm-param list<string> $columns
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @psalm-param list<string> $columns
|
||||
*/
|
||||
public function addUniqueConstraint(array $columns, $name)
|
||||
{
|
||||
@@ -299,10 +297,9 @@ class ClassMetadataBuilder
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
* @psalm-param array<string, mixed> $mapping
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping
|
||||
*/
|
||||
public function addField($name, $type, array $mapping = [])
|
||||
{
|
||||
|
||||
@@ -50,6 +50,8 @@ class EntityListenerBuilder
|
||||
* @param ClassMetadata $metadata The entity metadata.
|
||||
* @param string $className The listener class name.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException When the listener class not found.
|
||||
*/
|
||||
public static function bindEntityListener(ClassMetadata $metadata, $className)
|
||||
|
||||
@@ -28,9 +28,9 @@ namespace Doctrine\ORM\Mapping\Builder;
|
||||
class OneToManyAssociationBuilder extends AssociationBuilder
|
||||
{
|
||||
/**
|
||||
* @return static
|
||||
* @psalm-param array<string, string> $fieldNames
|
||||
*
|
||||
* @psalm-param list<string> $fieldNames
|
||||
* @return static
|
||||
*/
|
||||
public function setOrderBy(array $fieldNames)
|
||||
{
|
||||
|
||||
@@ -20,12 +20,17 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* Caching to an entity or a collection.
|
||||
*
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target({"CLASS","PROPERTY"})
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
|
||||
final class Cache implements Annotation
|
||||
{
|
||||
/**
|
||||
@@ -36,4 +41,10 @@ final class Cache implements Annotation
|
||||
|
||||
/** @var string Cache region name. */
|
||||
public $region;
|
||||
|
||||
public function __construct(string $usage = 'READ_ONLY', ?string $region = null)
|
||||
{
|
||||
$this->usage = $usage;
|
||||
$this->region = $region;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class ChangeTrackingPolicy implements Annotation
|
||||
{
|
||||
/**
|
||||
@@ -33,4 +38,9 @@ final class ChangeTrackingPolicy implements Annotation
|
||||
* @Enum({"DEFERRED_IMPLICIT", "DEFERRED_EXPLICIT", "NOTIFY"})
|
||||
*/
|
||||
public $value;
|
||||
|
||||
public function __construct(string $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace Doctrine\ORM\Mapping;
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @todo remove or rename ClassMetadataInfo to ClassMetadata
|
||||
* @template T of object
|
||||
* @template-extends ClassMetadataInfo<T>
|
||||
*/
|
||||
class ClassMetadata extends ClassMetadataInfo
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace Doctrine\ORM\Mapping;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\DBAL\Platforms;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
|
||||
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
|
||||
@@ -54,13 +55,17 @@ use function strtolower;
|
||||
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
|
||||
* metadata mapping information of a class which describes how a class should be mapped
|
||||
* to a relational database.
|
||||
*
|
||||
* @method ClassMetadata[] getAllMetadata()
|
||||
* @method ClassMetadata[] getLoadedMetadata()
|
||||
* @method ClassMetadata getMetadataFor($className)
|
||||
*/
|
||||
class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
{
|
||||
/** @var EntityManagerInterface|null */
|
||||
private $em;
|
||||
|
||||
/** @var AbstractPlatform */
|
||||
/** @var AbstractPlatform|null */
|
||||
private $targetPlatform;
|
||||
|
||||
/** @var MappingDriver */
|
||||
@@ -84,6 +89,9 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
return $loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setEntityManager(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
@@ -238,6 +246,15 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
|
||||
}
|
||||
|
||||
if ($class->changeTrackingPolicy === ClassMetadataInfo::CHANGETRACKING_NOTIFY) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8383',
|
||||
'NOTIFY Change Tracking policy used in "%s" is deprecated, use deferred explicit instead.',
|
||||
$class->name
|
||||
);
|
||||
}
|
||||
|
||||
$this->validateRuntimeMetadata($class, $parent);
|
||||
}
|
||||
|
||||
@@ -297,11 +314,9 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
* Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
|
||||
* map classes and looking for a fitting one.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
private function resolveDiscriminatorValue(ClassMetadata $metadata)
|
||||
private function resolveDiscriminatorValue(ClassMetadata $metadata): void
|
||||
{
|
||||
if (
|
||||
$metadata->discriminatorValue
|
||||
@@ -346,7 +361,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
private function addDefaultDiscriminatorMap(ClassMetadata $class)
|
||||
private function addDefaultDiscriminatorMap(ClassMetadata $class): void
|
||||
{
|
||||
$allClasses = $this->driver->getAllClassNames();
|
||||
$fqcn = $class->getName();
|
||||
@@ -375,11 +390,9 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
/**
|
||||
* Gets the lower-case short name of a class.
|
||||
*
|
||||
* @param string $className
|
||||
*
|
||||
* @return string
|
||||
* @psalm-param class-string $className
|
||||
*/
|
||||
private function getShortName($className)
|
||||
private function getShortName(string $className): string
|
||||
{
|
||||
if (strpos($className, '\\') === false) {
|
||||
return strtolower($className);
|
||||
@@ -392,10 +405,8 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
|
||||
/**
|
||||
* Adds inherited fields to the subclass mapping.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
|
||||
private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass): void
|
||||
{
|
||||
foreach ($parentClass->fieldMappings as $mapping) {
|
||||
if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
|
||||
@@ -417,11 +428,9 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
/**
|
||||
* Adds inherited association mappings to the subclass mapping.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
|
||||
private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass): void
|
||||
{
|
||||
foreach ($parentClass->associationMappings as $field => $mapping) {
|
||||
if ($parentClass->isMappedSuperclass) {
|
||||
@@ -445,7 +454,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
}
|
||||
}
|
||||
|
||||
private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass)
|
||||
private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass): void
|
||||
{
|
||||
foreach ($parentClass->embeddedClasses as $field => $embeddedClass) {
|
||||
if (! isset($embeddedClass['inherited']) && ! $parentClass->isMappedSuperclass) {
|
||||
@@ -467,8 +476,11 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
* @param ClassMetadata $parentClass Parent class to add nested embedded classes metadata to.
|
||||
* @param string $prefix Embedded classes' prefix to use for nested embedded classes field names.
|
||||
*/
|
||||
private function addNestedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass, $prefix)
|
||||
{
|
||||
private function addNestedEmbeddedClasses(
|
||||
ClassMetadata $subClass,
|
||||
ClassMetadata $parentClass,
|
||||
string $prefix
|
||||
): void {
|
||||
foreach ($subClass->embeddedClasses as $property => $embeddableClass) {
|
||||
if (isset($embeddableClass['inherited'])) {
|
||||
continue;
|
||||
@@ -492,10 +504,8 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
|
||||
/**
|
||||
* Copy the table indices from the parent class superclass to the child class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass)
|
||||
private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass): void
|
||||
{
|
||||
if (! $parentClass->isMappedSuperclass) {
|
||||
return;
|
||||
@@ -516,10 +526,8 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
|
||||
/**
|
||||
* Adds inherited named queries to the subclass mapping.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
|
||||
private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass): void
|
||||
{
|
||||
foreach ($parentClass->namedQueries as $name => $query) {
|
||||
if (! isset($subClass->namedQueries[$name])) {
|
||||
@@ -535,10 +543,8 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
|
||||
/**
|
||||
* Adds inherited named native queries to the subclass mapping.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
|
||||
private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass): void
|
||||
{
|
||||
foreach ($parentClass->namedNativeQueries as $name => $query) {
|
||||
if (! isset($subClass->namedNativeQueries[$name])) {
|
||||
@@ -557,10 +563,8 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
|
||||
/**
|
||||
* Adds inherited sql result set mappings to the subclass mapping.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass)
|
||||
private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass): void
|
||||
{
|
||||
foreach ($parentClass->sqlResultSetMappings as $name => $mapping) {
|
||||
if (! isset($subClass->sqlResultSetMappings[$name])) {
|
||||
@@ -589,11 +593,9 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
* Completes the ID generator mapping. If "auto" is specified we choose the generator
|
||||
* most appropriate for the targeted database platform.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws ORMException
|
||||
*/
|
||||
private function completeIdGeneratorMapping(ClassMetadataInfo $class)
|
||||
private function completeIdGeneratorMapping(ClassMetadataInfo $class): void
|
||||
{
|
||||
$idGenType = $class->generatorType;
|
||||
if ($idGenType === ClassMetadata::GENERATOR_TYPE_AUTO) {
|
||||
@@ -705,7 +707,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
/**
|
||||
* Inherits the ID generator mapping from a parent class.
|
||||
*/
|
||||
private function inheritIdGeneratorMapping(ClassMetadataInfo $class, ClassMetadataInfo $parent)
|
||||
private function inheritIdGeneratorMapping(ClassMetadataInfo $class, ClassMetadataInfo $parent): void
|
||||
{
|
||||
if ($parent->isIdGeneratorSequence()) {
|
||||
$class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition);
|
||||
@@ -745,6 +747,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
*/
|
||||
protected function getFqcnFromAlias($namespaceAlias, $simpleClassName)
|
||||
{
|
||||
/** @psalm-var class-string */
|
||||
return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
||||
}
|
||||
|
||||
@@ -764,10 +767,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Platforms\AbstractPlatform
|
||||
*/
|
||||
private function getTargetPlatform()
|
||||
private function getTargetPlatform(): Platforms\AbstractPlatform
|
||||
{
|
||||
if (! $this->targetPlatform) {
|
||||
$this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
|
||||
|
||||
@@ -21,8 +21,13 @@
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use BadMethodCallException;
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\Instantiator\Instantiator;
|
||||
use Doctrine\Instantiator\InstantiatorInterface;
|
||||
use Doctrine\ORM\Cache\CacheException;
|
||||
@@ -31,6 +36,7 @@ use Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\Persistence\Mapping\ReflectionService;
|
||||
use InvalidArgumentException;
|
||||
use ReflectionClass;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionProperty;
|
||||
use RuntimeException;
|
||||
|
||||
@@ -59,6 +65,8 @@ use function strtolower;
|
||||
use function trait_exists;
|
||||
use function trim;
|
||||
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
/**
|
||||
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
|
||||
* of an entity and its associations.
|
||||
@@ -72,6 +80,9 @@ use function trim;
|
||||
* 2) To drastically reduce the size of a serialized instance (private/protected members
|
||||
* get the whole class name, namespace inclusive, prepended to every property in
|
||||
* the serialized representation).
|
||||
*
|
||||
* @template T of object
|
||||
* @template-implements ClassMetadata<T>
|
||||
*/
|
||||
class ClassMetadataInfo implements ClassMetadata
|
||||
{
|
||||
@@ -235,6 +246,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* READ-ONLY: The name of the entity class.
|
||||
*
|
||||
* @var string
|
||||
* @psalm-var class-string<T>
|
||||
*/
|
||||
public $name;
|
||||
|
||||
@@ -252,6 +264,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* as {@link $name}.
|
||||
*
|
||||
* @var string
|
||||
* @psalm-var class-string
|
||||
*/
|
||||
public $rootEntityName;
|
||||
|
||||
@@ -371,6 +384,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* READ-ONLY: The inheritance mapping type used by the class.
|
||||
*
|
||||
* @var int
|
||||
* @psalm-var self::$INHERITANCE_TYPE_*
|
||||
*/
|
||||
public $inheritanceType = self::INHERITANCE_TYPE_NONE;
|
||||
|
||||
@@ -420,6 +434,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* - <b>'unique'</b> (string, optional, schema-only)
|
||||
* Whether a unique constraint should be generated for the column.
|
||||
*
|
||||
* @var mixed[]
|
||||
* @psalm-var array<string, array{
|
||||
* type: string,
|
||||
* fieldName: string,
|
||||
@@ -436,6 +451,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* originalField?: string,
|
||||
* quoted?: bool,
|
||||
* requireSQLConversion?: bool,
|
||||
* declaredField?: string,
|
||||
* options: array<mixed>
|
||||
* }>
|
||||
*/
|
||||
public $fieldMappings = [];
|
||||
@@ -500,7 +517,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* indexes => array
|
||||
* uniqueConstraints => array
|
||||
*
|
||||
* @psalm-var array<string, mixed>
|
||||
* @var mixed[]
|
||||
* @psalm-var array{name: string, schema: string, indexes: array, uniqueConstraints: array}
|
||||
*/
|
||||
public $table;
|
||||
|
||||
@@ -612,7 +630,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* @psalm-var array<string, mixed>
|
||||
* @var mixed[]
|
||||
* @psalm-var array{sequenceName: string, allocationSize: int, initialValue: int}
|
||||
* @todo Merge with tableGeneratorDefinition into generic generatorDefinition
|
||||
*/
|
||||
public $sequenceGeneratorDefinition;
|
||||
@@ -704,7 +723,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Gets the ReflectionProperties of the mapped class.
|
||||
*
|
||||
* @return ReflectionProperty[]|null[] An array of ReflectionProperty instances.
|
||||
*
|
||||
* @psalm-return array<ReflectionProperty|null>
|
||||
*/
|
||||
public function getReflectionProperties()
|
||||
@@ -748,7 +766,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*
|
||||
* @param object $entity
|
||||
*
|
||||
* @return array<string|int, mixed>
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getIdentifierValues($entity)
|
||||
{
|
||||
@@ -780,10 +798,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Populates the entity identifier of an entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @psalm-param array<string, mixed> $id
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array<string, mixed> $id
|
||||
* @todo Rename to assignIdentifier()
|
||||
*/
|
||||
public function setIdentifierValues($entity, array $id)
|
||||
@@ -1102,9 +1120,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{usage?: mixed, region?: mixed} $cache
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enableCache(array $cache)
|
||||
{
|
||||
@@ -1121,10 +1139,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
|
||||
/**
|
||||
* @param string $fieldName
|
||||
* @psalm-param array{usage?: int, region?: string} $cache
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{usage?: mixed, region?: mixed} $cache
|
||||
*/
|
||||
public function enableAssociationCache($fieldName, array $cache)
|
||||
{
|
||||
@@ -1134,11 +1151,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* @param string $fieldName
|
||||
* @param array $cache
|
||||
* @psalm-param array{usage?: int, region?: string} $cache
|
||||
*
|
||||
* @return mixed[]
|
||||
*
|
||||
* @psalm-param array{usage?: mixed, region?: mixed} $cache
|
||||
* @psalm-return array{usage: mixed, region: mixed}
|
||||
* @return int[]|string[]
|
||||
* @psalm-return array{usage: int, region: string|null}
|
||||
*/
|
||||
public function getAssociationCacheDefaults($fieldName, array $cache)
|
||||
{
|
||||
@@ -1264,9 +1280,21 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*
|
||||
* @param string $fieldName The field name.
|
||||
*
|
||||
* @throws MappingException
|
||||
* @return mixed[] The field mapping.
|
||||
* @psalm-return array{
|
||||
* type: string,
|
||||
* fieldName: string,
|
||||
* columnName?: string,
|
||||
* inherited?: class-string,
|
||||
* nullable?: bool,
|
||||
* originalClass?: class-string,
|
||||
* originalField?: string,
|
||||
* scale?: int,
|
||||
* precision?: int,
|
||||
* length?: int
|
||||
* }
|
||||
*
|
||||
* @psalm-return array<string, mixed> The field mapping.
|
||||
* @throws MappingException
|
||||
*/
|
||||
public function getFieldMapping($fieldName)
|
||||
{
|
||||
@@ -1285,9 +1313,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* @param string $fieldName The field name that represents the association in
|
||||
* the object model.
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-return array<string, mixed> The mapping.
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
public function getAssociationMapping($fieldName)
|
||||
{
|
||||
@@ -1344,7 +1372,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Gets all named queries of the class.
|
||||
*
|
||||
* @psalm-return array<string, mixed>
|
||||
* @return mixed[][]
|
||||
* @psalm-return array<string, array<string, mixed>>
|
||||
*/
|
||||
public function getNamedQueries()
|
||||
{
|
||||
@@ -1358,9 +1387,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*
|
||||
* @param string $queryName The query name.
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-return array<string, mixed>
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
public function getNamedNativeQuery($queryName)
|
||||
{
|
||||
@@ -1388,9 +1417,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*
|
||||
* @param string $name The result set mapping name.
|
||||
*
|
||||
* @throws MappingException
|
||||
* @return mixed[]
|
||||
* @psalm-return array{name: string, entities: array, columns: array}
|
||||
*
|
||||
* @psalm-return array<string, mixed>
|
||||
* @throws MappingException
|
||||
*/
|
||||
public function getSqlResultSetMapping($name)
|
||||
{
|
||||
@@ -1404,29 +1434,129 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Gets all sql result set mappings of the class.
|
||||
*
|
||||
* @psalm-return array<string, array<string, mixed>>
|
||||
* @return mixed[]
|
||||
* @psalm-return array<string, array{name: string, entities: array, columns: array}>
|
||||
*/
|
||||
public function getSqlResultSetMappings()
|
||||
{
|
||||
return $this->sqlResultSetMappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether given property has type
|
||||
*
|
||||
* @param string $name Property name
|
||||
*/
|
||||
private function isTypedProperty(string $name): bool
|
||||
{
|
||||
return PHP_VERSION_ID >= 70400
|
||||
&& isset($this->reflClass)
|
||||
&& $this->reflClass->hasProperty($name)
|
||||
&& $this->reflClass->getProperty($name)->hasType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates & completes the given field mapping based on typed property.
|
||||
*
|
||||
* @param mixed[] $mapping The field mapping to validate & complete.
|
||||
*
|
||||
* @return mixed[] The updated mapping.
|
||||
*/
|
||||
private function validateAndCompleteTypedFieldMapping(array $mapping): array
|
||||
{
|
||||
$type = $this->reflClass->getProperty($mapping['fieldName'])->getType();
|
||||
|
||||
if ($type) {
|
||||
if (! isset($mapping['nullable'])) {
|
||||
$mapping['nullable'] = $type->allowsNull();
|
||||
}
|
||||
|
||||
if (
|
||||
! isset($mapping['type'])
|
||||
&& ($type instanceof ReflectionNamedType)
|
||||
) {
|
||||
switch ($type->getName()) {
|
||||
case DateInterval::class:
|
||||
$mapping['type'] = Types::DATEINTERVAL;
|
||||
break;
|
||||
case DateTime::class:
|
||||
$mapping['type'] = Types::DATETIME_MUTABLE;
|
||||
break;
|
||||
case DateTimeImmutable::class:
|
||||
$mapping['type'] = Types::DATETIME_IMMUTABLE;
|
||||
break;
|
||||
case 'array':
|
||||
$mapping['type'] = Types::JSON;
|
||||
break;
|
||||
case 'bool':
|
||||
$mapping['type'] = Types::BOOLEAN;
|
||||
break;
|
||||
case 'float':
|
||||
$mapping['type'] = Types::FLOAT;
|
||||
break;
|
||||
case 'int':
|
||||
$mapping['type'] = Types::INTEGER;
|
||||
break;
|
||||
case 'string':
|
||||
$mapping['type'] = Types::STRING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates & completes the basic mapping information based on typed property.
|
||||
*
|
||||
* @param mixed[] $mapping The mapping.
|
||||
*
|
||||
* @return mixed[] The updated mapping.
|
||||
*/
|
||||
private function validateAndCompleteTypedAssociationMapping(array $mapping): array
|
||||
{
|
||||
$type = $this->reflClass->getProperty($mapping['fieldName'])->getType();
|
||||
|
||||
if ($type === null || ($mapping['type'] & self::TO_ONE) === 0) {
|
||||
return $mapping;
|
||||
}
|
||||
|
||||
if (! isset($mapping['targetEntity']) && $type instanceof ReflectionNamedType) {
|
||||
$mapping['targetEntity'] = $type->getName();
|
||||
}
|
||||
|
||||
if (isset($mapping['joinColumns'])) {
|
||||
foreach ($mapping['joinColumns'] as &$joinColumn) {
|
||||
if ($type->allowsNull() === false) {
|
||||
$joinColumn['nullable'] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates & completes the given field mapping.
|
||||
*
|
||||
* @return void
|
||||
* @psalm-param array<string, mixed> $mapping The field mapping to validate & complete.
|
||||
*
|
||||
* @return mixed[] The updated mapping.
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The field mapping to validate & complete.
|
||||
*/
|
||||
protected function _validateAndCompleteFieldMapping(array &$mapping)
|
||||
protected function validateAndCompleteFieldMapping(array $mapping): array
|
||||
{
|
||||
// Check mandatory fields
|
||||
if (! isset($mapping['fieldName']) || ! $mapping['fieldName']) {
|
||||
throw MappingException::missingFieldName($this->name);
|
||||
}
|
||||
|
||||
if ($this->isTypedProperty($mapping['fieldName'])) {
|
||||
$mapping = $this->validateAndCompleteTypedFieldMapping($mapping);
|
||||
}
|
||||
|
||||
if (! isset($mapping['type'])) {
|
||||
// Default to string
|
||||
$mapping['type'] = 'string';
|
||||
@@ -1473,22 +1603,22 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
|
||||
$mapping['requireSQLConversion'] = true;
|
||||
}
|
||||
|
||||
return $mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates & completes the basic mapping information that is common to all
|
||||
* association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
|
||||
*
|
||||
* @return mixed[] The updated mapping.
|
||||
*
|
||||
* @throws MappingException If something is wrong with the mapping.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The mapping.
|
||||
*
|
||||
* @return mixed[] The updated mapping.
|
||||
* @psalm-return array{
|
||||
* mappedBy: mixed,
|
||||
* inversedBy: mixed,
|
||||
* mappedBy: mixed|null,
|
||||
* inversedBy: mixed|null,
|
||||
* isOwningSide: bool,
|
||||
* sourceEntity: string,
|
||||
* sourceEntity: class-string,
|
||||
* targetEntity: string,
|
||||
* fieldName: mixed,
|
||||
* fetch: mixed,
|
||||
@@ -1498,8 +1628,13 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* isCascadeRefresh: bool,
|
||||
* isCascadeMerge: bool,
|
||||
* isCascadeDetach: bool,
|
||||
* type: int,
|
||||
* originalField: string,
|
||||
* originalClass: class-string,
|
||||
* ?orphanRemoval: bool
|
||||
* }
|
||||
*
|
||||
* @throws MappingException If something is wrong with the mapping.
|
||||
*/
|
||||
protected function _validateAndCompleteAssociationMapping(array $mapping)
|
||||
{
|
||||
@@ -1521,6 +1656,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
// the sourceEntity.
|
||||
$mapping['sourceEntity'] = $this->name;
|
||||
|
||||
if ($this->isTypedProperty($mapping['fieldName'])) {
|
||||
$mapping = $this->validateAndCompleteTypedAssociationMapping($mapping);
|
||||
}
|
||||
|
||||
if (isset($mapping['targetEntity'])) {
|
||||
$mapping['targetEntity'] = $this->fullyQualifiedClassName($mapping['targetEntity']);
|
||||
$mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
|
||||
@@ -1617,13 +1756,38 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Validates & completes a one-to-one association mapping.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The mapping to validate & complete.
|
||||
* @psalm-param array<string, mixed> $mapping The mapping to validate & complete.
|
||||
*
|
||||
* @return mixed[] The validated & completed mapping.
|
||||
* @psalm-return array{isOwningSide: mixed, orphanRemoval: bool, isCascadeRemove: bool}
|
||||
* @psalm-return array{
|
||||
* mappedBy: mixed|null,
|
||||
* inversedBy: mixed|null,
|
||||
* isOwningSide: bool,
|
||||
* sourceEntity: class-string,
|
||||
* targetEntity: string,
|
||||
* fieldName: mixed,
|
||||
* fetch: mixed,
|
||||
* cascade: array<string>,
|
||||
* isCascadeRemove: bool,
|
||||
* isCascadePersist: bool,
|
||||
* isCascadeRefresh: bool,
|
||||
* isCascadeMerge: bool,
|
||||
* isCascadeDetach: bool,
|
||||
* type: int,
|
||||
* originalField: string,
|
||||
* originalClass: class-string,
|
||||
* joinColumns?: array{0: array{name: string, referencedColumnName: string}}|mixed,
|
||||
* id?: mixed,
|
||||
* sourceToTargetKeyColumns?: array,
|
||||
* joinColumnFieldNames?: array,
|
||||
* targetToSourceKeyColumns?: array<array-key>,
|
||||
* orphanRemoval: bool
|
||||
* }
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The mapping to validate & complete.
|
||||
* @psalm-return array{isOwningSide: mixed, orphanRemoval: bool, isCascadeRemove: bool}
|
||||
*/
|
||||
protected function _validateAndCompleteOneToOneMapping(array $mapping)
|
||||
{
|
||||
@@ -1707,12 +1871,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Validates & completes a one-to-many association mapping.
|
||||
*
|
||||
* @return mixed[] The validated and completed mapping.
|
||||
*
|
||||
* @throws MappingException
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The mapping to validate and complete.
|
||||
*
|
||||
* @return mixed[] The validated and completed mapping.
|
||||
* @psalm-return array{
|
||||
* mappedBy: mixed,
|
||||
* inversedBy: mixed,
|
||||
@@ -1729,6 +1890,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* isCascadeDetach: bool,
|
||||
* orphanRemoval: bool
|
||||
* }
|
||||
*
|
||||
* @throws MappingException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function _validateAndCompleteOneToManyMapping(array $mapping)
|
||||
{
|
||||
@@ -1736,7 +1900,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
|
||||
// OneToMany-side MUST be inverse (must have mappedBy)
|
||||
if (! isset($mapping['mappedBy'])) {
|
||||
throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
|
||||
throw MappingException::oneToManyRequiresMappedBy($this->name, $mapping['fieldName']);
|
||||
}
|
||||
|
||||
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'];
|
||||
@@ -1750,12 +1914,36 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Validates & completes a many-to-many association mapping.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The mapping to validate & complete.
|
||||
* @psalm-param array<string, mixed> $mapping The mapping to validate & complete.
|
||||
*
|
||||
* @return mixed[] The validated & completed mapping.
|
||||
* @psalm-return array{
|
||||
* mappedBy: mixed,
|
||||
* inversedBy: mixed,
|
||||
* isOwningSide: bool,
|
||||
* sourceEntity: class-string,
|
||||
* targetEntity: string,
|
||||
* fieldName: mixed,
|
||||
* fetch: mixed,
|
||||
* cascade: array<string>,
|
||||
* isCascadeRemove: bool,
|
||||
* isCascadePersist: bool,
|
||||
* isCascadeRefresh: bool,
|
||||
* isCascadeMerge: bool,
|
||||
* isCascadeDetach: bool,
|
||||
* type: int,
|
||||
* originalField: string,
|
||||
* originalClass: class-string,
|
||||
* joinTable?: array{inverseJoinColumns: mixed}|mixed,
|
||||
* joinTableColumns?: list<mixed>,
|
||||
* isOnDeleteCascade?: true,
|
||||
* relationToSourceKeyColumns?: array,
|
||||
* relationToTargetKeyColumns?: array,
|
||||
* orphanRemoval: bool
|
||||
* }
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The mapping to validate & complete.
|
||||
* @psalm-return array{isOwningSide: mixed, orphanRemoval: bool}
|
||||
*/
|
||||
protected function _validateAndCompleteManyToManyMapping(array $mapping)
|
||||
{
|
||||
@@ -1901,9 +2089,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Sets the mapped identifier/primary key fields of this class.
|
||||
* Mainly used by the ClassMetadataFactory to assign inherited identifiers.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param list<mixed> $identifier
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setIdentifier(array $identifier)
|
||||
{
|
||||
@@ -1930,9 +2118,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Gets an array containing all the column names.
|
||||
*
|
||||
* @return mixed[]
|
||||
*
|
||||
* @psalm-param list<string>|null $fieldNames
|
||||
*
|
||||
* @return mixed[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public function getColumnNames(?array $fieldNames = null)
|
||||
@@ -2151,9 +2339,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Sets the mapped subclasses of this class.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param list<string> $subclasses The names of all mapped subclasses.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setSubclasses(array $subclasses)
|
||||
{
|
||||
@@ -2167,9 +2355,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Assumes that the class names in the passed array are in the order:
|
||||
* directParent -> directParentParent -> directParentParentParent ... -> root.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param list<class-string> $classNames
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setParentClasses(array $classNames)
|
||||
{
|
||||
@@ -2202,12 +2390,11 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Sets the association to override association mapping of property for an entity relationship.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @psalm-param array<string, mixed> $overrideMapping
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $overrideMapping
|
||||
*/
|
||||
public function setAssociationOverride($fieldName, array $overrideMapping)
|
||||
{
|
||||
@@ -2268,12 +2455,11 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Sets the override for a mapped field.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @psalm-param array<string, mixed> $overrideMapping
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $overrideMapping
|
||||
*/
|
||||
public function setAttributeOverride($fieldName, array $overrideMapping)
|
||||
{
|
||||
@@ -2308,7 +2494,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
unset($this->fieldNames[$mapping['columnName']]);
|
||||
unset($this->columnNames[$mapping['fieldName']]);
|
||||
|
||||
$this->_validateAndCompleteFieldMapping($overrideMapping);
|
||||
$overrideMapping = $this->validateAndCompleteFieldMapping($overrideMapping);
|
||||
|
||||
$this->fieldMappings[$fieldName] = $overrideMapping;
|
||||
}
|
||||
@@ -2381,9 +2567,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*
|
||||
* If a key is omitted, the current value is kept.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array<string, mixed> $table The table description.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPrimaryTable(array $table)
|
||||
{
|
||||
@@ -2425,11 +2611,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Checks whether the given type identifies an inheritance type.
|
||||
*
|
||||
* @param int $type
|
||||
*
|
||||
* @return bool TRUE if the given type identifies an inheritance type, FALSe otherwise.
|
||||
* @return bool TRUE if the given type identifies an inheritance type, FALSE otherwise.
|
||||
*/
|
||||
private function isInheritanceType($type)
|
||||
private function isInheritanceType(int $type): bool
|
||||
{
|
||||
return $type === self::INHERITANCE_TYPE_NONE ||
|
||||
$type === self::INHERITANCE_TYPE_SINGLE_TABLE ||
|
||||
@@ -2440,15 +2624,15 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Adds a mapped field to the class.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The field mapping.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The field mapping.
|
||||
*/
|
||||
public function mapField(array $mapping)
|
||||
{
|
||||
$this->_validateAndCompleteFieldMapping($mapping);
|
||||
$mapping = $this->validateAndCompleteFieldMapping($mapping);
|
||||
$this->assertFieldNotMapped($mapping['fieldName']);
|
||||
|
||||
$this->fieldMappings[$mapping['fieldName']] = $mapping;
|
||||
@@ -2459,11 +2643,11 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Adds an association mapping without completing/validating it.
|
||||
* This is mainly used to add inherited association mappings to derived classes.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping
|
||||
*/
|
||||
public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
|
||||
{
|
||||
@@ -2479,9 +2663,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Adds a field mapping without completing/validating it.
|
||||
* This is mainly used to add inherited field mappings to derived classes.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array<string, mixed> $fieldMapping
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addInheritedFieldMapping(array $fieldMapping)
|
||||
{
|
||||
@@ -2494,11 +2678,13 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* INTERNAL:
|
||||
* Adds a named query to this class.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @psalm-param array<string, mixed> $queryMapping
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $queryMapping
|
||||
*/
|
||||
public function addNamedQuery(array $queryMapping)
|
||||
{
|
||||
@@ -2506,6 +2692,14 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
throw MappingException::nameIsMandatoryForQueryMapping($this->name);
|
||||
}
|
||||
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8592',
|
||||
'Named Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository',
|
||||
$queryMapping['name'],
|
||||
$this->name
|
||||
);
|
||||
|
||||
if (isset($this->namedQueries[$queryMapping['name']])) {
|
||||
throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
|
||||
}
|
||||
@@ -2529,11 +2723,13 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* INTERNAL:
|
||||
* Adds a named native query to this class.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @psalm-param array<string, mixed> $queryMapping
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $queryMapping
|
||||
*/
|
||||
public function addNamedNativeQuery(array $queryMapping)
|
||||
{
|
||||
@@ -2541,6 +2737,14 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
throw MappingException::nameIsMandatoryForQueryMapping($this->name);
|
||||
}
|
||||
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8592',
|
||||
'Named Native Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository',
|
||||
$queryMapping['name'],
|
||||
$this->name
|
||||
);
|
||||
|
||||
if (isset($this->namedNativeQueries[$queryMapping['name']])) {
|
||||
throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
|
||||
}
|
||||
@@ -2572,11 +2776,11 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* INTERNAL:
|
||||
* Adds a sql result set mapping to this class.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $resultMapping
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $resultMapping
|
||||
*/
|
||||
public function addSqlResultSetMapping(array $resultMapping)
|
||||
{
|
||||
@@ -2646,9 +2850,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Adds a one-to-many mapping.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The mapping.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function mapOneToMany(array $mapping)
|
||||
{
|
||||
@@ -2662,9 +2866,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Adds a many-to-one mapping.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The mapping.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function mapManyToOne(array $mapping)
|
||||
{
|
||||
@@ -2679,9 +2883,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Adds a many-to-many mapping.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The mapping.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function mapManyToMany(array $mapping)
|
||||
{
|
||||
@@ -2695,11 +2899,11 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Stores the association mapping.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $assocMapping
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $assocMapping
|
||||
*/
|
||||
protected function _storeAssociationMapping(array $assocMapping)
|
||||
{
|
||||
@@ -2714,10 +2918,9 @@ 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
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param class-string $repositoryClassName
|
||||
*/
|
||||
public function setCustomRepositoryClass($repositoryClassName)
|
||||
{
|
||||
@@ -2776,6 +2979,16 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public function addLifecycleCallback($callback, $event)
|
||||
{
|
||||
if ($this->isEmbeddedClass) {
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/pull/8381',
|
||||
'Registering lifecycle callback %s on Embedded class %s is not doing anything and will throw exception in 3.0',
|
||||
$event,
|
||||
$this->name
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
|
||||
return;
|
||||
}
|
||||
@@ -2787,9 +3000,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Sets the lifecycle callbacks for entities of this class.
|
||||
* Any previously registered callbacks are overwritten.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array<string, list<string>> $callbacks
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setLifecycleCallbacks(array $callbacks)
|
||||
{
|
||||
@@ -2803,6 +3016,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* @param string $class The listener class.
|
||||
* @param string $method The listener callback method.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
public function addEntityListener($eventName, $class, $method)
|
||||
@@ -2834,11 +3049,11 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*
|
||||
* @see getDiscriminatorColumn()
|
||||
*
|
||||
* @psalm-param array<string, mixed> $columnDef
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $columnDef
|
||||
*/
|
||||
public function setDiscriminatorColumn($columnDef)
|
||||
{
|
||||
@@ -2871,9 +3086,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Sets the discriminator values used by this class.
|
||||
* Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array<string, class-string> $map
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDiscriminatorMap(array $map)
|
||||
{
|
||||
@@ -2886,12 +3101,11 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Adds one entry of the discriminator map with a new class and corresponding name.
|
||||
*
|
||||
* @param string $name
|
||||
* @psalm-param class-string $className
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param class-string $className
|
||||
*/
|
||||
public function addDiscriminatorMapClass($name, $className)
|
||||
{
|
||||
@@ -3071,9 +3285,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Sets definition.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array<string, string> $definition
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCustomGeneratorDefinition(array $definition)
|
||||
{
|
||||
@@ -3093,11 +3307,11 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* @psalm-param array<string, string> $definition
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, string> $definition
|
||||
*/
|
||||
public function setSequenceGeneratorDefinition(array $definition)
|
||||
{
|
||||
@@ -3125,11 +3339,11 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Sets the version field mapping used for versioning. Sets the default
|
||||
* value to use depending on the column type.
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The version field mapping array.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping The version field mapping array.
|
||||
*/
|
||||
public function setVersionMapping(array &$mapping)
|
||||
{
|
||||
@@ -3345,11 +3559,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $className
|
||||
* @param string|null $className
|
||||
* @psalm-param ?class-string $className
|
||||
*
|
||||
* @return string|null null if the input value is null
|
||||
*
|
||||
* @psalm-param ?class-string $className
|
||||
*/
|
||||
public function fullyQualifiedClassName($className)
|
||||
{
|
||||
@@ -3381,11 +3594,11 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Map Embedded Class
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping
|
||||
*/
|
||||
public function mapEmbedded(array $mapping)
|
||||
{
|
||||
@@ -3403,6 +3616,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Inline the embeddable class
|
||||
*
|
||||
* @param string $property
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function inlineEmbeddable($property, ClassMetadataInfo $embeddable)
|
||||
{
|
||||
|
||||
@@ -20,17 +20,22 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target({"PROPERTY","ANNOTATION"})
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_PROPERTY)]
|
||||
final class Column implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $name;
|
||||
|
||||
/** @var mixed */
|
||||
public $type = 'string';
|
||||
public $type;
|
||||
|
||||
/** @var int */
|
||||
public $length;
|
||||
@@ -52,12 +57,37 @@ final class Column implements Annotation
|
||||
/** @var bool */
|
||||
public $unique = false;
|
||||
|
||||
/** @var bool */
|
||||
public $nullable = false;
|
||||
/** @var bool|null */
|
||||
public $nullable;
|
||||
|
||||
/** @var array */
|
||||
/** @var array<string,mixed> */
|
||||
public $options = [];
|
||||
|
||||
/** @var string */
|
||||
public $columnDefinition;
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $options
|
||||
*/
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
?string $type = null,
|
||||
?int $length = null,
|
||||
?int $precision = null,
|
||||
?int $scale = null,
|
||||
bool $unique = false,
|
||||
?bool $nullable = null,
|
||||
array $options = [],
|
||||
?string $columnDefinition = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
$this->length = $length;
|
||||
$this->precision = $precision;
|
||||
$this->scale = $scale;
|
||||
$this->unique = $unique;
|
||||
$this->nullable = $nullable;
|
||||
$this->options = $options;
|
||||
$this->columnDefinition = $columnDefinition;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,22 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_PROPERTY)]
|
||||
final class CustomIdGenerator implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $class;
|
||||
|
||||
public function __construct(?string $class = null)
|
||||
{
|
||||
$this->class = $class;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class DiscriminatorColumn implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
@@ -44,4 +49,16 @@ final class DiscriminatorColumn implements Annotation
|
||||
|
||||
/** @var string */
|
||||
public $columnDefinition;
|
||||
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
?string $type = null,
|
||||
?int $length = null,
|
||||
?string $columnDefinition = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
$this->length = $length;
|
||||
$this->columnDefinition = $columnDefinition;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,23 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class DiscriminatorMap implements Annotation
|
||||
{
|
||||
/** @var array<string> */
|
||||
public $value;
|
||||
|
||||
/** @param array<string> $value */
|
||||
public function __construct(array $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ use UnexpectedValueException;
|
||||
|
||||
use function class_exists;
|
||||
use function constant;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function get_class;
|
||||
use function is_array;
|
||||
@@ -110,7 +111,28 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
|
||||
if ($tableAnnot->indexes !== null) {
|
||||
foreach ($tableAnnot->indexes as $indexAnnot) {
|
||||
$index = ['columns' => $indexAnnot->columns];
|
||||
$index = [];
|
||||
|
||||
if (! empty($indexAnnot->columns)) {
|
||||
$index['columns'] = $indexAnnot->columns;
|
||||
}
|
||||
|
||||
if (! empty($indexAnnot->fields)) {
|
||||
$index['fields'] = $indexAnnot->fields;
|
||||
}
|
||||
|
||||
if (
|
||||
isset($index['columns'], $index['fields'])
|
||||
|| (
|
||||
! isset($index['columns'])
|
||||
&& ! isset($index['fields'])
|
||||
)
|
||||
) {
|
||||
throw MappingException::invalidIndexConfiguration(
|
||||
$className,
|
||||
(string) ($indexAnnot->name ?? count($primaryTable['indexes']))
|
||||
);
|
||||
}
|
||||
|
||||
if (! empty($indexAnnot->flags)) {
|
||||
$index['flags'] = $indexAnnot->flags;
|
||||
@@ -130,7 +152,28 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
|
||||
if ($tableAnnot->uniqueConstraints !== null) {
|
||||
foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
|
||||
$uniqueConstraint = ['columns' => $uniqueConstraintAnnot->columns];
|
||||
$uniqueConstraint = [];
|
||||
|
||||
if (! empty($uniqueConstraintAnnot->columns)) {
|
||||
$uniqueConstraint['columns'] = $uniqueConstraintAnnot->columns;
|
||||
}
|
||||
|
||||
if (! empty($uniqueConstraintAnnot->fields)) {
|
||||
$uniqueConstraint['fields'] = $uniqueConstraintAnnot->fields;
|
||||
}
|
||||
|
||||
if (
|
||||
isset($uniqueConstraint['columns'], $uniqueConstraint['fields'])
|
||||
|| (
|
||||
! isset($uniqueConstraint['columns'])
|
||||
&& ! isset($uniqueConstraint['fields'])
|
||||
)
|
||||
) {
|
||||
throw MappingException::invalidUniqueConstraintConfiguration(
|
||||
$className,
|
||||
(string) ($uniqueConstraintAnnot->name ?? count($primaryTable['uniqueConstraints']))
|
||||
);
|
||||
}
|
||||
|
||||
if (! empty($uniqueConstraintAnnot->options)) {
|
||||
$uniqueConstraint['options'] = $uniqueConstraintAnnot->options;
|
||||
@@ -156,7 +199,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
$cacheAnnot = $classAnnotations[Mapping\Cache::class];
|
||||
$cacheMap = [
|
||||
'region' => $cacheAnnot->region,
|
||||
'usage' => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage),
|
||||
'usage' => (int) constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage),
|
||||
];
|
||||
|
||||
$metadata->enableCache($cacheMap);
|
||||
@@ -302,7 +345,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
$mapping['cache'] = $metadata->getAssociationCacheDefaults(
|
||||
$mapping['fieldName'],
|
||||
[
|
||||
'usage' => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage),
|
||||
'usage' => (int) constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage),
|
||||
'region' => $cacheAnnot->region,
|
||||
]
|
||||
);
|
||||
@@ -327,10 +370,6 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
// @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany
|
||||
$columnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Column::class);
|
||||
if ($columnAnnot) {
|
||||
if ($columnAnnot->type === null) {
|
||||
throw MappingException::propertyTypeIsRequired($className, $property->getName());
|
||||
}
|
||||
|
||||
$mapping = $this->columnToArray($property->getName(), $columnAnnot);
|
||||
|
||||
$idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class);
|
||||
@@ -488,7 +527,6 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
|
||||
/**
|
||||
* @param mixed[] $joinColumns
|
||||
*
|
||||
* @psalm-param array<string, mixed> $mapping
|
||||
*/
|
||||
private function loadRelationShipMapping(
|
||||
@@ -598,14 +636,11 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
/**
|
||||
* Attempts to resolve the fetch mode.
|
||||
*
|
||||
* @param string $className The class name.
|
||||
* @param string $fetchMode The fetch mode.
|
||||
*
|
||||
* @return int The fetch mode as defined in ClassMetadata.
|
||||
* @psalm-return \Doctrine\ORM\Mapping\ClassMetadata::FETCH_* The fetch mode as defined in ClassMetadata.
|
||||
*
|
||||
* @throws MappingException If the fetch mode is not valid.
|
||||
*/
|
||||
private function getFetchMode($className, $fetchMode)
|
||||
private function getFetchMode(string $className, string $fetchMode): int
|
||||
{
|
||||
if (! defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) {
|
||||
throw MappingException::invalidFetchMode($className, $fetchMode);
|
||||
@@ -618,8 +653,9 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
* Parses the given method.
|
||||
*
|
||||
* @return callable[]
|
||||
* @psalm-return list<callable-array>
|
||||
*/
|
||||
private function getMethodCallbacks(ReflectionMethod $method)
|
||||
private function getMethodCallbacks(ReflectionMethod $method): array
|
||||
{
|
||||
$callbacks = [];
|
||||
$annotations = $this->reader->getMethodAnnotations($method);
|
||||
@@ -665,7 +701,6 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
* Parse the given JoinColumn as array
|
||||
*
|
||||
* @return mixed[]
|
||||
*
|
||||
* @psalm-return array{
|
||||
* name: string,
|
||||
* unique: bool,
|
||||
@@ -675,7 +710,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
* referencedColumnName: string
|
||||
* }
|
||||
*/
|
||||
private function joinColumnToArray(Mapping\JoinColumn $joinColumn)
|
||||
private function joinColumnToArray(Mapping\JoinColumn $joinColumn): array
|
||||
{
|
||||
return [
|
||||
'name' => $joinColumn->name,
|
||||
@@ -690,10 +725,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
/**
|
||||
* Parse the given Column as array
|
||||
*
|
||||
* @param string $fieldName
|
||||
*
|
||||
* @return mixed[]
|
||||
*
|
||||
* @psalm-return array{
|
||||
* fieldName: string,
|
||||
* type: mixed,
|
||||
@@ -707,7 +739,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
* columnDefinition?: string
|
||||
* }
|
||||
*/
|
||||
private function columnToArray($fieldName, Mapping\Column $column)
|
||||
private function columnToArray(string $fieldName, Mapping\Column $column): array
|
||||
{
|
||||
$mapping = [
|
||||
'fieldName' => $fieldName,
|
||||
|
||||
571
lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php
Normal file
571
lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php
Normal file
@@ -0,0 +1,571 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Driver;
|
||||
|
||||
use Doctrine\ORM\Events;
|
||||
use Doctrine\ORM\Mapping;
|
||||
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\Persistence\Mapping\Driver\AnnotationDriver;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
use function constant;
|
||||
use function count;
|
||||
use function defined;
|
||||
|
||||
class AttributeDriver extends AnnotationDriver
|
||||
{
|
||||
/** @var array<string,int> */
|
||||
// @phpcs:ignore
|
||||
protected $entityAnnotationClasses = [
|
||||
Mapping\Entity::class => 1,
|
||||
Mapping\MappedSuperclass::class => 2,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param array<string> $paths
|
||||
*/
|
||||
public function __construct(array $paths)
|
||||
{
|
||||
parent::__construct(new AttributeReader(), $paths);
|
||||
}
|
||||
|
||||
public function loadMetadataForClass($className, ClassMetadata $metadata): void
|
||||
{
|
||||
assert($metadata instanceof ClassMetadataInfo);
|
||||
|
||||
$reflectionClass = $metadata->getReflectionClass();
|
||||
|
||||
$classAttributes = $this->reader->getClassAnnotations($reflectionClass);
|
||||
|
||||
// Evaluate Entity annotation
|
||||
if (isset($classAttributes[Mapping\Entity::class])) {
|
||||
$entityAttribute = $classAttributes[Mapping\Entity::class];
|
||||
if ($entityAttribute->repositoryClass !== null) {
|
||||
$metadata->setCustomRepositoryClass($entityAttribute->repositoryClass);
|
||||
}
|
||||
|
||||
if ($entityAttribute->readOnly) {
|
||||
$metadata->markReadOnly();
|
||||
}
|
||||
} elseif (isset($classAttributes[Mapping\MappedSuperclass::class])) {
|
||||
$mappedSuperclassAttribute = $classAttributes[Mapping\MappedSuperclass::class];
|
||||
|
||||
$metadata->setCustomRepositoryClass($mappedSuperclassAttribute->repositoryClass);
|
||||
$metadata->isMappedSuperclass = true;
|
||||
} elseif (isset($classAttributes[Mapping\Embeddable::class])) {
|
||||
$metadata->isEmbeddedClass = true;
|
||||
} else {
|
||||
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
|
||||
}
|
||||
|
||||
$primaryTable = [];
|
||||
|
||||
if (isset($classAttributes[Mapping\Table::class])) {
|
||||
$tableAnnot = $classAttributes[Mapping\Table::class];
|
||||
$primaryTable['name'] = $tableAnnot->name;
|
||||
$primaryTable['schema'] = $tableAnnot->schema;
|
||||
|
||||
if ($tableAnnot->options) {
|
||||
$primaryTable['options'] = $tableAnnot->options;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($classAttributes[Mapping\Index::class])) {
|
||||
foreach ($classAttributes[Mapping\Index::class] as $idx => $indexAnnot) {
|
||||
$index = [];
|
||||
|
||||
if (! empty($indexAnnot->columns)) {
|
||||
$index['columns'] = $indexAnnot->columns;
|
||||
}
|
||||
|
||||
if (! empty($indexAnnot->fields)) {
|
||||
$index['fields'] = $indexAnnot->fields;
|
||||
}
|
||||
|
||||
if (
|
||||
isset($index['columns'], $index['fields'])
|
||||
|| (
|
||||
! isset($index['columns'])
|
||||
&& ! isset($index['fields'])
|
||||
)
|
||||
) {
|
||||
throw MappingException::invalidIndexConfiguration(
|
||||
$className,
|
||||
(string) ($indexAnnot->name ?? $idx)
|
||||
);
|
||||
}
|
||||
|
||||
if (! empty($indexAnnot->flags)) {
|
||||
$index['flags'] = $indexAnnot->flags;
|
||||
}
|
||||
|
||||
if (! empty($indexAnnot->options)) {
|
||||
$index['options'] = $indexAnnot->options;
|
||||
}
|
||||
|
||||
if (! empty($indexAnnot->name)) {
|
||||
$primaryTable['indexes'][$indexAnnot->name] = $index;
|
||||
} else {
|
||||
$primaryTable['indexes'][] = $index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($classAttributes[Mapping\UniqueConstraint::class])) {
|
||||
foreach ($classAttributes[Mapping\UniqueConstraint::class] as $idx => $uniqueConstraintAnnot) {
|
||||
$uniqueConstraint = [];
|
||||
|
||||
if (! empty($uniqueConstraintAnnot->columns)) {
|
||||
$uniqueConstraint['columns'] = $uniqueConstraintAnnot->columns;
|
||||
}
|
||||
|
||||
if (! empty($uniqueConstraintAnnot->fields)) {
|
||||
$uniqueConstraint['fields'] = $uniqueConstraintAnnot->fields;
|
||||
}
|
||||
|
||||
if (
|
||||
isset($uniqueConstraint['columns'], $uniqueConstraint['fields'])
|
||||
|| (
|
||||
! isset($uniqueConstraint['columns'])
|
||||
&& ! isset($uniqueConstraint['fields'])
|
||||
)
|
||||
) {
|
||||
throw MappingException::invalidUniqueConstraintConfiguration(
|
||||
$className,
|
||||
(string) ($uniqueConstraintAnnot->name ?? $idx)
|
||||
);
|
||||
}
|
||||
|
||||
if (! empty($uniqueConstraintAnnot->options)) {
|
||||
$uniqueConstraint['options'] = $uniqueConstraintAnnot->options;
|
||||
}
|
||||
|
||||
if (! empty($uniqueConstraintAnnot->name)) {
|
||||
$primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint;
|
||||
} else {
|
||||
$primaryTable['uniqueConstraints'][] = $uniqueConstraint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$metadata->setPrimaryTable($primaryTable);
|
||||
|
||||
// Evaluate @Cache annotation
|
||||
if (isset($classAttributes[Mapping\Cache::class])) {
|
||||
$cacheAttribute = $classAttributes[Mapping\Cache::class];
|
||||
$cacheMap = [
|
||||
'region' => $cacheAttribute->region,
|
||||
'usage' => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAttribute->usage),
|
||||
];
|
||||
|
||||
$metadata->enableCache($cacheMap);
|
||||
}
|
||||
|
||||
// Evaluate InheritanceType annotation
|
||||
if (isset($classAttributes[Mapping\InheritanceType::class])) {
|
||||
$inheritanceTypeAttribute = $classAttributes[Mapping\InheritanceType::class];
|
||||
|
||||
$metadata->setInheritanceType(
|
||||
constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAttribute->value)
|
||||
);
|
||||
|
||||
if ($metadata->inheritanceType !== Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
// Evaluate DiscriminatorColumn annotation
|
||||
if (isset($classAttributes[Mapping\DiscriminatorColumn::class])) {
|
||||
$discrColumnAttribute = $classAttributes[Mapping\DiscriminatorColumn::class];
|
||||
|
||||
$metadata->setDiscriminatorColumn(
|
||||
[
|
||||
'name' => $discrColumnAttribute->name,
|
||||
'type' => $discrColumnAttribute->type ?: 'string',
|
||||
'length' => $discrColumnAttribute->length ?: 255,
|
||||
'columnDefinition' => $discrColumnAttribute->columnDefinition,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$metadata->setDiscriminatorColumn(['name' => 'dtype', 'type' => 'string', 'length' => 255]);
|
||||
}
|
||||
|
||||
// Evaluate DiscriminatorMap annotation
|
||||
if (isset($classAttributes[Mapping\DiscriminatorMap::class])) {
|
||||
$discrMapAttribute = $classAttributes[Mapping\DiscriminatorMap::class];
|
||||
$metadata->setDiscriminatorMap($discrMapAttribute->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate DoctrineChangeTrackingPolicy annotation
|
||||
if (isset($classAttributes[Mapping\ChangeTrackingPolicy::class])) {
|
||||
$changeTrackingAttribute = $classAttributes[Mapping\ChangeTrackingPolicy::class];
|
||||
$metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAttribute->value));
|
||||
}
|
||||
|
||||
foreach ($reflectionClass->getProperties() as $property) {
|
||||
assert($property instanceof ReflectionProperty);
|
||||
if (
|
||||
$metadata->isMappedSuperclass && ! $property->isPrivate()
|
||||
||
|
||||
$metadata->isInheritedField($property->name)
|
||||
||
|
||||
$metadata->isInheritedAssociation($property->name)
|
||||
||
|
||||
$metadata->isInheritedEmbeddedClass($property->name)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$mapping = [];
|
||||
$mapping['fieldName'] = $property->getName();
|
||||
|
||||
// Evaluate @Cache annotation
|
||||
$cacheAttribute = $this->reader->getPropertyAnnotation($property, Mapping\Cache::class);
|
||||
if ($cacheAttribute !== null) {
|
||||
assert($cacheAttribute instanceof Mapping\Cache);
|
||||
|
||||
$mapping['cache'] = $metadata->getAssociationCacheDefaults(
|
||||
$mapping['fieldName'],
|
||||
[
|
||||
'usage' => (int) constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAttribute->usage),
|
||||
'region' => $cacheAttribute->region,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Check for JoinColumn/JoinColumns annotations
|
||||
$joinColumns = [];
|
||||
|
||||
$joinColumnAttributes = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class);
|
||||
|
||||
foreach ($joinColumnAttributes as $joinColumnAttribute) {
|
||||
$joinColumns[] = $this->joinColumnToArray($joinColumnAttribute);
|
||||
}
|
||||
|
||||
// Field can only be attributed with one of:
|
||||
// Column, OneToOne, OneToMany, ManyToOne, ManyToMany, Embedded
|
||||
$columnAttribute = $this->reader->getPropertyAnnotation($property, Mapping\Column::class);
|
||||
$oneToOneAttribute = $this->reader->getPropertyAnnotation($property, Mapping\OneToOne::class);
|
||||
$oneToManyAttribute = $this->reader->getPropertyAnnotation($property, Mapping\OneToMany::class);
|
||||
$manyToOneAttribute = $this->reader->getPropertyAnnotation($property, Mapping\ManyToOne::class);
|
||||
$manyToManyAttribute = $this->reader->getPropertyAnnotation($property, Mapping\ManyToMany::class);
|
||||
$embeddedAttribute = $this->reader->getPropertyAnnotation($property, Mapping\Embedded::class);
|
||||
|
||||
if ($columnAttribute !== null) {
|
||||
$mapping = $this->columnToArray($property->getName(), $columnAttribute);
|
||||
|
||||
if ($this->reader->getPropertyAnnotation($property, Mapping\Id::class)) {
|
||||
$mapping['id'] = true;
|
||||
}
|
||||
|
||||
$generatedValueAttribute = $this->reader->getPropertyAnnotation($property, Mapping\GeneratedValue::class);
|
||||
|
||||
if ($generatedValueAttribute !== null) {
|
||||
$metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAttribute->strategy));
|
||||
}
|
||||
|
||||
if ($this->reader->getPropertyAnnotation($property, Mapping\Version::class)) {
|
||||
$metadata->setVersionMapping($mapping);
|
||||
}
|
||||
|
||||
$metadata->mapField($mapping);
|
||||
|
||||
// Check for SequenceGenerator/TableGenerator definition
|
||||
$seqGeneratorAttribute = $this->reader->getPropertyAnnotation($property, Mapping\SequenceGenerator::class);
|
||||
$customGeneratorAttribute = $this->reader->getPropertyAnnotation($property, Mapping\CustomIdGenerator::class);
|
||||
|
||||
if ($seqGeneratorAttribute !== null) {
|
||||
$metadata->setSequenceGeneratorDefinition(
|
||||
[
|
||||
'sequenceName' => $seqGeneratorAttribute->sequenceName,
|
||||
'allocationSize' => $seqGeneratorAttribute->allocationSize,
|
||||
'initialValue' => $seqGeneratorAttribute->initialValue,
|
||||
]
|
||||
);
|
||||
} elseif ($customGeneratorAttribute !== null) {
|
||||
$metadata->setCustomGeneratorDefinition(
|
||||
[
|
||||
'class' => $customGeneratorAttribute->class,
|
||||
]
|
||||
);
|
||||
}
|
||||
} elseif ($oneToOneAttribute !== null) {
|
||||
if ($this->reader->getPropertyAnnotation($property, Mapping\Id::class)) {
|
||||
$mapping['id'] = true;
|
||||
}
|
||||
|
||||
$mapping['targetEntity'] = $oneToOneAttribute->targetEntity;
|
||||
$mapping['joinColumns'] = $joinColumns;
|
||||
$mapping['mappedBy'] = $oneToOneAttribute->mappedBy;
|
||||
$mapping['inversedBy'] = $oneToOneAttribute->inversedBy;
|
||||
$mapping['cascade'] = $oneToOneAttribute->cascade;
|
||||
$mapping['orphanRemoval'] = $oneToOneAttribute->orphanRemoval;
|
||||
$mapping['fetch'] = $this->getFetchMode($className, $oneToOneAttribute->fetch);
|
||||
$metadata->mapOneToOne($mapping);
|
||||
} elseif ($oneToManyAttribute !== null) {
|
||||
$mapping['mappedBy'] = $oneToManyAttribute->mappedBy;
|
||||
$mapping['targetEntity'] = $oneToManyAttribute->targetEntity;
|
||||
$mapping['cascade'] = $oneToManyAttribute->cascade;
|
||||
$mapping['indexBy'] = $oneToManyAttribute->indexBy;
|
||||
$mapping['orphanRemoval'] = $oneToManyAttribute->orphanRemoval;
|
||||
$mapping['fetch'] = $this->getFetchMode($className, $oneToManyAttribute->fetch);
|
||||
|
||||
$orderByAttribute = $this->reader->getPropertyAnnotation($property, Mapping\OrderBy::class);
|
||||
|
||||
if ($orderByAttribute !== null) {
|
||||
$mapping['orderBy'] = $orderByAttribute->value;
|
||||
}
|
||||
|
||||
$metadata->mapOneToMany($mapping);
|
||||
} elseif ($manyToOneAttribute !== null) {
|
||||
$idAttribute = $this->reader->getPropertyAnnotation($property, Mapping\Id::class);
|
||||
|
||||
if ($idAttribute !== null) {
|
||||
$mapping['id'] = true;
|
||||
}
|
||||
|
||||
$mapping['joinColumns'] = $joinColumns;
|
||||
$mapping['cascade'] = $manyToOneAttribute->cascade;
|
||||
$mapping['inversedBy'] = $manyToOneAttribute->inversedBy;
|
||||
$mapping['targetEntity'] = $manyToOneAttribute->targetEntity;
|
||||
$mapping['fetch'] = $this->getFetchMode($className, $manyToOneAttribute->fetch);
|
||||
$metadata->mapManyToOne($mapping);
|
||||
} elseif ($manyToManyAttribute !== null) {
|
||||
$joinTable = [];
|
||||
$joinTableAttribute = $this->reader->getPropertyAnnotation($property, Mapping\JoinTable::class);
|
||||
|
||||
if ($joinTableAttribute !== null) {
|
||||
$joinTable = [
|
||||
'name' => $joinTableAttribute->name,
|
||||
'schema' => $joinTableAttribute->schema,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class) as $joinColumn) {
|
||||
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
|
||||
}
|
||||
|
||||
foreach ($this->reader->getPropertyAnnotation($property, Mapping\InverseJoinColumn::class) as $joinColumn) {
|
||||
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
|
||||
}
|
||||
|
||||
$mapping['joinTable'] = $joinTable;
|
||||
$mapping['targetEntity'] = $manyToManyAttribute->targetEntity;
|
||||
$mapping['mappedBy'] = $manyToManyAttribute->mappedBy;
|
||||
$mapping['inversedBy'] = $manyToManyAttribute->inversedBy;
|
||||
$mapping['cascade'] = $manyToManyAttribute->cascade;
|
||||
$mapping['indexBy'] = $manyToManyAttribute->indexBy;
|
||||
$mapping['orphanRemoval'] = $manyToManyAttribute->orphanRemoval;
|
||||
$mapping['fetch'] = $this->getFetchMode($className, $manyToManyAttribute->fetch);
|
||||
|
||||
$orderByAttribute = $this->reader->getPropertyAnnotation($property, Mapping\OrderBy::class);
|
||||
|
||||
if ($orderByAttribute !== null) {
|
||||
$mapping['orderBy'] = $orderByAttribute->value;
|
||||
}
|
||||
|
||||
$metadata->mapManyToMany($mapping);
|
||||
} elseif ($embeddedAttribute !== null) {
|
||||
$mapping['class'] = $embeddedAttribute->class;
|
||||
$mapping['columnPrefix'] = $embeddedAttribute->columnPrefix;
|
||||
|
||||
$metadata->mapEmbedded($mapping);
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate AttributeOverrides annotation
|
||||
if (isset($classAttributes[Mapping\AttributeOverride::class])) {
|
||||
foreach ($classAttributes[Mapping\AttributeOverride::class] as $attributeOverrideAttribute) {
|
||||
$attributeOverride = $this->columnToArray($attributeOverrideAttribute->name, $attributeOverrideAttribute->column);
|
||||
|
||||
$metadata->setAttributeOverride($attributeOverrideAttribute->name, $attributeOverride);
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate EntityListeners annotation
|
||||
if (isset($classAttributes[Mapping\EntityListeners::class])) {
|
||||
$entityListenersAttribute = $classAttributes[Mapping\EntityListeners::class];
|
||||
|
||||
foreach ($entityListenersAttribute->value as $item) {
|
||||
$listenerClassName = $metadata->fullyQualifiedClassName($item);
|
||||
|
||||
if (! class_exists($listenerClassName)) {
|
||||
throw MappingException::entityListenerClassNotFound($listenerClassName, $className);
|
||||
}
|
||||
|
||||
$hasMapping = false;
|
||||
$listenerClass = new ReflectionClass($listenerClassName);
|
||||
|
||||
foreach ($listenerClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
assert($method instanceof ReflectionMethod);
|
||||
// find method callbacks.
|
||||
$callbacks = $this->getMethodCallbacks($method);
|
||||
$hasMapping = $hasMapping ?: ! empty($callbacks);
|
||||
|
||||
foreach ($callbacks as $value) {
|
||||
$metadata->addEntityListener($value[1], $listenerClassName, $value[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the listener using naming convention.
|
||||
if (! $hasMapping) {
|
||||
EntityListenerBuilder::bindEntityListener($metadata, $listenerClassName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate @HasLifecycleCallbacks annotation
|
||||
if (isset($classAttributes[Mapping\HasLifecycleCallbacks::class])) {
|
||||
foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
assert($method instanceof ReflectionMethod);
|
||||
foreach ($this->getMethodCallbacks($method) as $value) {
|
||||
$metadata->addLifecycleCallback($value[0], $value[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to resolve the fetch mode.
|
||||
*
|
||||
* @param string $className The class name.
|
||||
* @param string $fetchMode The fetch mode.
|
||||
*
|
||||
* @return int The fetch mode as defined in ClassMetadata.
|
||||
*
|
||||
* @throws MappingException If the fetch mode is not valid.
|
||||
*/
|
||||
private function getFetchMode(string $className, string $fetchMode): int
|
||||
{
|
||||
if (! defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) {
|
||||
throw MappingException::invalidFetchMode($className, $fetchMode);
|
||||
}
|
||||
|
||||
return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given method.
|
||||
*
|
||||
* @return callable[]
|
||||
*/
|
||||
private function getMethodCallbacks(ReflectionMethod $method): array
|
||||
{
|
||||
$callbacks = [];
|
||||
$attributes = $this->reader->getMethodAnnotations($method);
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
if ($attribute instanceof Mapping\PrePersist) {
|
||||
$callbacks[] = [$method->name, Events::prePersist];
|
||||
}
|
||||
|
||||
if ($attribute instanceof Mapping\PostPersist) {
|
||||
$callbacks[] = [$method->name, Events::postPersist];
|
||||
}
|
||||
|
||||
if ($attribute instanceof Mapping\PreUpdate) {
|
||||
$callbacks[] = [$method->name, Events::preUpdate];
|
||||
}
|
||||
|
||||
if ($attribute instanceof Mapping\PostUpdate) {
|
||||
$callbacks[] = [$method->name, Events::postUpdate];
|
||||
}
|
||||
|
||||
if ($attribute instanceof Mapping\PreRemove) {
|
||||
$callbacks[] = [$method->name, Events::preRemove];
|
||||
}
|
||||
|
||||
if ($attribute instanceof Mapping\PostRemove) {
|
||||
$callbacks[] = [$method->name, Events::postRemove];
|
||||
}
|
||||
|
||||
if ($attribute instanceof Mapping\PostLoad) {
|
||||
$callbacks[] = [$method->name, Events::postLoad];
|
||||
}
|
||||
|
||||
if ($attribute instanceof Mapping\PreFlush) {
|
||||
$callbacks[] = [$method->name, Events::preFlush];
|
||||
}
|
||||
}
|
||||
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given JoinColumn as array
|
||||
*
|
||||
* @param Mapping\JoinColumn|Mapping\InverseJoinColumn $joinColumn
|
||||
*
|
||||
* @return mixed[]
|
||||
* @psalm-return array{
|
||||
* name: string,
|
||||
* unique: bool,
|
||||
* nullable: bool,
|
||||
* onDelete: mixed,
|
||||
* columnDefinition: string,
|
||||
* referencedColumnName: string
|
||||
* }
|
||||
*/
|
||||
private function joinColumnToArray($joinColumn): array
|
||||
{
|
||||
return [
|
||||
'name' => $joinColumn->name,
|
||||
'unique' => $joinColumn->unique,
|
||||
'nullable' => $joinColumn->nullable,
|
||||
'onDelete' => $joinColumn->onDelete,
|
||||
'columnDefinition' => $joinColumn->columnDefinition,
|
||||
'referencedColumnName' => $joinColumn->referencedColumnName,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given Column as array
|
||||
*
|
||||
* @return mixed[]
|
||||
* @psalm-return array{
|
||||
* fieldName: string,
|
||||
* type: mixed,
|
||||
* scale: int,
|
||||
* length: int,
|
||||
* unique: bool,
|
||||
* nullable: bool,
|
||||
* precision: int,
|
||||
* options?: mixed[],
|
||||
* columnName?: string,
|
||||
* columnDefinition?: string
|
||||
* }
|
||||
*/
|
||||
private function columnToArray(string $fieldName, Mapping\Column $column): array
|
||||
{
|
||||
$mapping = [
|
||||
'fieldName' => $fieldName,
|
||||
'type' => $column->type,
|
||||
'scale' => $column->scale,
|
||||
'length' => $column->length,
|
||||
'unique' => $column->unique,
|
||||
'nullable' => $column->nullable,
|
||||
'precision' => $column->precision,
|
||||
];
|
||||
|
||||
if ($column->options) {
|
||||
$mapping['options'] = $column->options;
|
||||
}
|
||||
|
||||
if (isset($column->name)) {
|
||||
$mapping['columnName'] = $column->name;
|
||||
}
|
||||
|
||||
if (isset($column->columnDefinition)) {
|
||||
$mapping['columnDefinition'] = $column->columnDefinition;
|
||||
}
|
||||
|
||||
return $mapping;
|
||||
}
|
||||
}
|
||||
99
lib/Doctrine/ORM/Mapping/Driver/AttributeReader.php
Normal file
99
lib/Doctrine/ORM/Mapping/Driver/AttributeReader.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Driver;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\ORM\Mapping\Annotation;
|
||||
use ReflectionAttribute;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function count;
|
||||
use function is_subclass_of;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class AttributeReader
|
||||
{
|
||||
/** @var array<string,bool> */
|
||||
private array $isRepeatableAttribute = [];
|
||||
|
||||
/** @return array<object> */
|
||||
public function getClassAnnotations(ReflectionClass $class): array
|
||||
{
|
||||
return $this->convertToAttributeInstances($class->getAttributes());
|
||||
}
|
||||
|
||||
/** @return array<object>|object|null */
|
||||
public function getClassAnnotation(ReflectionClass $class, $annotationName)
|
||||
{
|
||||
return $this->getClassAnnotations($class)[$annotationName] ?? ($this->isRepeatable($annotationName) ? [] : null);
|
||||
}
|
||||
|
||||
/** @return array<object> */
|
||||
public function getMethodAnnotations(ReflectionMethod $method): array
|
||||
{
|
||||
return $this->convertToAttributeInstances($method->getAttributes());
|
||||
}
|
||||
|
||||
/** @return array<object>|object|null */
|
||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
|
||||
{
|
||||
return $this->getMethodAnnotations($method)[$annotationName] ?? ($this->isRepeatable($annotationName) ? [] : null);
|
||||
}
|
||||
|
||||
/** @return array<object> */
|
||||
public function getPropertyAnnotations(ReflectionProperty $property): array
|
||||
{
|
||||
return $this->convertToAttributeInstances($property->getAttributes());
|
||||
}
|
||||
|
||||
/** @return array<object>|object|null */
|
||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
|
||||
{
|
||||
return $this->getPropertyAnnotations($property)[$annotationName] ?? ($this->isRepeatable($annotationName) ? [] : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<object> $attributes
|
||||
*
|
||||
* @return array<Annotation>
|
||||
*/
|
||||
private function convertToAttributeInstances(array $attributes): array
|
||||
{
|
||||
$instances = [];
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
// Make sure we only get Doctrine Annotations
|
||||
if (! is_subclass_of($attribute->getName(), Annotation::class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$instance = $attribute->newInstance();
|
||||
|
||||
if ($this->isRepeatable($attribute->getName())) {
|
||||
$instances[$attribute->getName()][] = $instance;
|
||||
} else {
|
||||
$instances[$attribute->getName()] = $instance;
|
||||
}
|
||||
}
|
||||
|
||||
return $instances;
|
||||
}
|
||||
|
||||
private function isRepeatable(string $attributeClassName): bool
|
||||
{
|
||||
if (isset($this->isRepeatableAttribute[$attributeClassName])) {
|
||||
return $this->isRepeatableAttribute[$attributeClassName];
|
||||
}
|
||||
|
||||
$reflectionClass = new ReflectionClass($attributeClassName);
|
||||
$attribute = $reflectionClass->getAttributes()[0]->newInstance();
|
||||
|
||||
return $this->isRepeatableAttribute[$attributeClassName] = ($attribute->flags & Attribute::IS_REPEATABLE) > 0;
|
||||
}
|
||||
}
|
||||
@@ -145,10 +145,10 @@ class DatabaseDriver implements MappingDriver
|
||||
/**
|
||||
* Sets tables manually instead of relying on the reverse engineering capabilities of SchemaManager.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param list<Table> $entityTables
|
||||
* @psalm-param list<Table> $manyToManyTables
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTables($entityTables, $manyToManyTables)
|
||||
{
|
||||
@@ -259,11 +259,9 @@ class DatabaseDriver implements MappingDriver
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
private function reverseEngineerMappingFromDatabase()
|
||||
private function reverseEngineerMappingFromDatabase(): void
|
||||
{
|
||||
if ($this->tables !== null) {
|
||||
return;
|
||||
@@ -316,7 +314,7 @@ class DatabaseDriver implements MappingDriver
|
||||
/**
|
||||
* Build indexes from a class metadata.
|
||||
*/
|
||||
private function buildIndexes(ClassMetadataInfo $metadata)
|
||||
private function buildIndexes(ClassMetadataInfo $metadata): void
|
||||
{
|
||||
$tableName = $metadata->table['name'];
|
||||
$indexes = $this->tables[$tableName]->getIndexes();
|
||||
@@ -339,7 +337,7 @@ class DatabaseDriver implements MappingDriver
|
||||
/**
|
||||
* Build field mapping from class metadata.
|
||||
*/
|
||||
private function buildFieldMappings(ClassMetadataInfo $metadata)
|
||||
private function buildFieldMappings(ClassMetadataInfo $metadata): void
|
||||
{
|
||||
$tableName = $metadata->table['name'];
|
||||
$columns = $this->tables[$tableName]->getColumns();
|
||||
@@ -382,8 +380,6 @@ class DatabaseDriver implements MappingDriver
|
||||
/**
|
||||
* Build field mapping from a schema column definition
|
||||
*
|
||||
* @param string $tableName
|
||||
*
|
||||
* @psalm-return array{
|
||||
* fieldName: string,
|
||||
* columnName: string,
|
||||
@@ -400,7 +396,7 @@ class DatabaseDriver implements MappingDriver
|
||||
* length?: int|null
|
||||
* }
|
||||
*/
|
||||
private function buildFieldMapping($tableName, Column $column)
|
||||
private function buildFieldMapping(string $tableName, Column $column): array
|
||||
{
|
||||
$fieldMapping = [
|
||||
'fieldName' => $this->getFieldNameForColumn($tableName, $column->getName(), false),
|
||||
@@ -453,6 +449,8 @@ class DatabaseDriver implements MappingDriver
|
||||
|
||||
/**
|
||||
* Build to one (one to one, many to one) association mapping from class metadata.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function buildToOneAssociationMappings(ClassMetadataInfo $metadata)
|
||||
{
|
||||
@@ -498,10 +496,9 @@ class DatabaseDriver implements MappingDriver
|
||||
* Retrieve schema table definition foreign keys.
|
||||
*
|
||||
* @return ForeignKeyConstraint[]
|
||||
*
|
||||
* @psalm-return array<string, ForeignKeyConstraint>
|
||||
*/
|
||||
private function getTableForeignKeys(Table $table)
|
||||
private function getTableForeignKeys(Table $table): array
|
||||
{
|
||||
return $this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()
|
||||
? $table->getForeignKeys()
|
||||
@@ -513,7 +510,7 @@ class DatabaseDriver implements MappingDriver
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function getTablePrimaryKeys(Table $table)
|
||||
private function getTablePrimaryKeys(Table $table): array
|
||||
{
|
||||
try {
|
||||
return $table->getPrimaryKey()->getColumns();
|
||||
@@ -527,11 +524,9 @@ class DatabaseDriver implements MappingDriver
|
||||
/**
|
||||
* Returns the mapped class name for a table if it exists. Otherwise return "classified" version.
|
||||
*
|
||||
* @param string $tableName
|
||||
*
|
||||
* @return string
|
||||
* @psalm-return class-string
|
||||
*/
|
||||
private function getClassNameForTable($tableName)
|
||||
private function getClassNameForTable(string $tableName): string
|
||||
{
|
||||
if (isset($this->classNamesForTables[$tableName])) {
|
||||
return $this->namespace . $this->classNamesForTables[$tableName];
|
||||
@@ -543,14 +538,13 @@ class DatabaseDriver implements MappingDriver
|
||||
/**
|
||||
* Return the mapped field name for a column, if it exists. Otherwise return camelized version.
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param string $columnName
|
||||
* @param bool $fk Whether the column is a foreignkey or not.
|
||||
*
|
||||
* @return string
|
||||
* @param bool $fk Whether the column is a foreignkey or not.
|
||||
*/
|
||||
private function getFieldNameForColumn($tableName, $columnName, $fk = false)
|
||||
{
|
||||
private function getFieldNameForColumn(
|
||||
string $tableName,
|
||||
string $columnName,
|
||||
bool $fk = false
|
||||
): string {
|
||||
if (isset($this->fieldNamesForColumns[$tableName]) && isset($this->fieldNamesForColumns[$tableName][$columnName])) {
|
||||
return $this->fieldNamesForColumns[$tableName][$columnName];
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ use SimpleXMLElement;
|
||||
|
||||
use function assert;
|
||||
use function constant;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function explode;
|
||||
use function file_get_contents;
|
||||
@@ -212,7 +213,28 @@ class XmlDriver extends FileDriver
|
||||
if (isset($xmlRoot->indexes)) {
|
||||
$metadata->table['indexes'] = [];
|
||||
foreach ($xmlRoot->indexes->index as $indexXml) {
|
||||
$index = ['columns' => explode(',', (string) $indexXml['columns'])];
|
||||
$index = [];
|
||||
|
||||
if (isset($indexXml['columns']) && ! empty($indexXml['columns'])) {
|
||||
$index['columns'] = explode(',', (string) $indexXml['columns']);
|
||||
}
|
||||
|
||||
if (isset($indexXml['fields'])) {
|
||||
$index['fields'] = explode(',', (string) $indexXml['fields']);
|
||||
}
|
||||
|
||||
if (
|
||||
isset($index['columns'], $index['fields'])
|
||||
|| (
|
||||
! isset($index['columns'])
|
||||
&& ! isset($index['fields'])
|
||||
)
|
||||
) {
|
||||
throw MappingException::invalidIndexConfiguration(
|
||||
$className,
|
||||
(string) ($indexXml['name'] ?? count($metadata->table['indexes']))
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($indexXml['flags'])) {
|
||||
$index['flags'] = explode(',', (string) $indexXml['flags']);
|
||||
@@ -234,7 +256,28 @@ class XmlDriver extends FileDriver
|
||||
if (isset($xmlRoot->{'unique-constraints'})) {
|
||||
$metadata->table['uniqueConstraints'] = [];
|
||||
foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $uniqueXml) {
|
||||
$unique = ['columns' => explode(',', (string) $uniqueXml['columns'])];
|
||||
$unique = [];
|
||||
|
||||
if (isset($uniqueXml['columns']) && ! empty($uniqueXml['columns'])) {
|
||||
$unique['columns'] = explode(',', (string) $uniqueXml['columns']);
|
||||
}
|
||||
|
||||
if (isset($uniqueXml['fields'])) {
|
||||
$unique['fields'] = explode(',', (string) $uniqueXml['fields']);
|
||||
}
|
||||
|
||||
if (
|
||||
isset($unique['columns'], $unique['fields'])
|
||||
|| (
|
||||
! isset($unique['columns'])
|
||||
&& ! isset($unique['fields'])
|
||||
)
|
||||
) {
|
||||
throw MappingException::invalidUniqueConstraintConfiguration(
|
||||
$className,
|
||||
(string) ($uniqueXml['name'] ?? count($metadata->table['uniqueConstraints']))
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($uniqueXml->options)) {
|
||||
$unique['options'] = $this->parseOptions($uniqueXml->options->children());
|
||||
@@ -366,9 +409,12 @@ class XmlDriver extends FileDriver
|
||||
foreach ($xmlRoot->{'one-to-one'} as $oneToOneElement) {
|
||||
$mapping = [
|
||||
'fieldName' => (string) $oneToOneElement['field'],
|
||||
'targetEntity' => (string) $oneToOneElement['target-entity'],
|
||||
];
|
||||
|
||||
if (isset($oneToOneElement['target-entity'])) {
|
||||
$mapping['targetEntity'] = (string) $oneToOneElement['target-entity'];
|
||||
}
|
||||
|
||||
if (isset($associationIds[$mapping['fieldName']])) {
|
||||
$mapping['id'] = true;
|
||||
}
|
||||
@@ -419,10 +465,13 @@ class XmlDriver extends FileDriver
|
||||
foreach ($xmlRoot->{'one-to-many'} as $oneToManyElement) {
|
||||
$mapping = [
|
||||
'fieldName' => (string) $oneToManyElement['field'],
|
||||
'targetEntity' => (string) $oneToManyElement['target-entity'],
|
||||
'mappedBy' => (string) $oneToManyElement['mapped-by'],
|
||||
];
|
||||
|
||||
if (isset($oneToManyElement['target-entity'])) {
|
||||
$mapping['targetEntity'] = (string) $oneToManyElement['target-entity'];
|
||||
}
|
||||
|
||||
if (isset($oneToManyElement['fetch'])) {
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $oneToManyElement['fetch']);
|
||||
}
|
||||
@@ -466,9 +515,12 @@ class XmlDriver extends FileDriver
|
||||
foreach ($xmlRoot->{'many-to-one'} as $manyToOneElement) {
|
||||
$mapping = [
|
||||
'fieldName' => (string) $manyToOneElement['field'],
|
||||
'targetEntity' => (string) $manyToOneElement['target-entity'],
|
||||
];
|
||||
|
||||
if (isset($manyToOneElement['target-entity'])) {
|
||||
$mapping['targetEntity'] = (string) $manyToOneElement['target-entity'];
|
||||
}
|
||||
|
||||
if (isset($associationIds[$mapping['fieldName']])) {
|
||||
$mapping['id'] = true;
|
||||
}
|
||||
@@ -511,9 +563,12 @@ class XmlDriver extends FileDriver
|
||||
foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) {
|
||||
$mapping = [
|
||||
'fieldName' => (string) $manyToManyElement['field'],
|
||||
'targetEntity' => (string) $manyToManyElement['target-entity'],
|
||||
];
|
||||
|
||||
if (isset($manyToManyElement['target-entity'])) {
|
||||
$mapping['targetEntity'] = (string) $manyToManyElement['target-entity'];
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement['fetch'])) {
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $manyToManyElement['fetch']);
|
||||
}
|
||||
@@ -680,8 +735,9 @@ class XmlDriver extends FileDriver
|
||||
* @param SimpleXMLElement $options The XML element.
|
||||
*
|
||||
* @return mixed[] The options array.
|
||||
* @psalm-return array<int|string, array<int|string, mixed|string>|bool|string>
|
||||
*/
|
||||
private function parseOptions(SimpleXMLElement $options)
|
||||
private function parseOptions(SimpleXMLElement $options): array
|
||||
{
|
||||
$array = [];
|
||||
|
||||
@@ -714,7 +770,6 @@ class XmlDriver extends FileDriver
|
||||
* @param SimpleXMLElement $joinColumnElement The XML element.
|
||||
*
|
||||
* @return mixed[] The mapping array.
|
||||
*
|
||||
* @psalm-return array{
|
||||
* name: string,
|
||||
* referencedColumnName: string,
|
||||
@@ -724,7 +779,7 @@ class XmlDriver extends FileDriver
|
||||
* columnDefinition?: string
|
||||
* }
|
||||
*/
|
||||
private function joinColumnToArray(SimpleXMLElement $joinColumnElement)
|
||||
private function joinColumnToArray(SimpleXMLElement $joinColumnElement): array
|
||||
{
|
||||
$joinColumn = [
|
||||
'name' => (string) $joinColumnElement['name'],
|
||||
@@ -754,7 +809,6 @@ class XmlDriver extends FileDriver
|
||||
* Parses the given field as array.
|
||||
*
|
||||
* @return mixed[]
|
||||
*
|
||||
* @psalm-return array{
|
||||
* fieldName: string,
|
||||
* type?: string,
|
||||
@@ -769,7 +823,7 @@ class XmlDriver extends FileDriver
|
||||
* options?: array
|
||||
* }
|
||||
*/
|
||||
private function columnToArray(SimpleXMLElement $fieldMapping)
|
||||
private function columnToArray(SimpleXMLElement $fieldMapping): array
|
||||
{
|
||||
$mapping = [
|
||||
'fieldName' => (string) $fieldMapping['name'],
|
||||
@@ -822,10 +876,9 @@ class XmlDriver extends FileDriver
|
||||
* Parse / Normalize the cache configuration
|
||||
*
|
||||
* @return mixed[]
|
||||
*
|
||||
* @psalm-return array{usage: mixed, region: string|null}
|
||||
* @psalm-return array{usage: int|null, region?: string}
|
||||
*/
|
||||
private function cacheToArray(SimpleXMLElement $cacheMapping)
|
||||
private function cacheToArray(SimpleXMLElement $cacheMapping): array
|
||||
{
|
||||
$region = isset($cacheMapping['region']) ? (string) $cacheMapping['region'] : null;
|
||||
$usage = isset($cacheMapping['usage']) ? strtoupper($cacheMapping['usage']) : null;
|
||||
@@ -835,7 +888,7 @@ class XmlDriver extends FileDriver
|
||||
}
|
||||
|
||||
if ($usage) {
|
||||
$usage = constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage);
|
||||
$usage = (int) constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage);
|
||||
}
|
||||
|
||||
return [
|
||||
@@ -850,10 +903,9 @@ class XmlDriver extends FileDriver
|
||||
* @param SimpleXMLElement $cascadeElement The cascade element.
|
||||
*
|
||||
* @return string[] The list of cascade options.
|
||||
*
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
private function getCascadeMappings(SimpleXMLElement $cascadeElement)
|
||||
private function getCascadeMappings(SimpleXMLElement $cascadeElement): array
|
||||
{
|
||||
$cascades = [];
|
||||
foreach ($cascadeElement->children() as $action) {
|
||||
@@ -879,16 +931,19 @@ class XmlDriver extends FileDriver
|
||||
|
||||
if (isset($xmlElement->entity)) {
|
||||
foreach ($xmlElement->entity as $entityElement) {
|
||||
/** @psalm-var class-string */
|
||||
$entityName = (string) $entityElement['name'];
|
||||
$result[$entityName] = $entityElement;
|
||||
}
|
||||
} elseif (isset($xmlElement->{'mapped-superclass'})) {
|
||||
foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) {
|
||||
/** @psalm-var class-string */
|
||||
$className = (string) $mappedSuperClass['name'];
|
||||
$result[$className] = $mappedSuperClass;
|
||||
}
|
||||
} elseif (isset($xmlElement->embeddable)) {
|
||||
foreach ($xmlElement->embeddable as $embeddableElement) {
|
||||
/** @psalm-var class-string */
|
||||
$embeddableName = (string) $embeddableElement['name'];
|
||||
$result[$embeddableName] = $embeddableElement;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Driver;
|
||||
|
||||
use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata as Metadata;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
@@ -39,9 +40,6 @@ use function sprintf;
|
||||
use function strlen;
|
||||
use function strtoupper;
|
||||
use function substr;
|
||||
use function trigger_error;
|
||||
|
||||
use const E_USER_DEPRECATED;
|
||||
|
||||
/**
|
||||
* The YamlDriver reads the mapping metadata from yaml schema files.
|
||||
@@ -57,9 +55,10 @@ class YamlDriver extends FileDriver
|
||||
*/
|
||||
public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
|
||||
{
|
||||
@trigger_error(
|
||||
'YAML mapping driver is deprecated and will be removed in Doctrine ORM 3.0, please migrate to annotation or XML driver.',
|
||||
E_USER_DEPRECATED
|
||||
Deprecation::trigger(
|
||||
'doctrine/orm',
|
||||
'https://github.com/doctrine/orm/issues/8465',
|
||||
'YAML mapping driver is deprecated and will be removed in Doctrine ORM 3.0, please migrate to annotation or XML driver.'
|
||||
);
|
||||
|
||||
parent::__construct($locator, $fileExtension);
|
||||
@@ -229,10 +228,35 @@ class YamlDriver extends FileDriver
|
||||
$indexYml['name'] = $name;
|
||||
}
|
||||
|
||||
if (is_string($indexYml['columns'])) {
|
||||
$index = ['columns' => array_map('trim', explode(',', $indexYml['columns']))];
|
||||
} else {
|
||||
$index = ['columns' => $indexYml['columns']];
|
||||
$index = [];
|
||||
|
||||
if (isset($indexYml['columns'])) {
|
||||
if (is_string($indexYml['columns'])) {
|
||||
$index['columns'] = array_map('trim', explode(',', $indexYml['columns']));
|
||||
} else {
|
||||
$index['columns'] = $indexYml['columns'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($indexYml['fields'])) {
|
||||
if (is_string($indexYml['fields'])) {
|
||||
$index['fields'] = array_map('trim', explode(',', $indexYml['fields']));
|
||||
} else {
|
||||
$index['fields'] = $indexYml['fields'];
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
isset($index['columns'], $index['fields'])
|
||||
|| (
|
||||
! isset($index['columns'])
|
||||
&& ! isset($index['fields'])
|
||||
)
|
||||
) {
|
||||
throw MappingException::invalidIndexConfiguration(
|
||||
$className,
|
||||
$indexYml['name']
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($indexYml['flags'])) {
|
||||
@@ -258,10 +282,35 @@ class YamlDriver extends FileDriver
|
||||
$uniqueYml['name'] = $name;
|
||||
}
|
||||
|
||||
if (is_string($uniqueYml['columns'])) {
|
||||
$unique = ['columns' => array_map('trim', explode(',', $uniqueYml['columns']))];
|
||||
} else {
|
||||
$unique = ['columns' => $uniqueYml['columns']];
|
||||
$unique = [];
|
||||
|
||||
if (isset($uniqueYml['columns'])) {
|
||||
if (is_string($uniqueYml['columns'])) {
|
||||
$unique['columns'] = array_map('trim', explode(',', $uniqueYml['columns']));
|
||||
} else {
|
||||
$unique['columns'] = $uniqueYml['columns'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($uniqueYml['fields'])) {
|
||||
if (is_string($uniqueYml['fields'])) {
|
||||
$unique['fields'] = array_map('trim', explode(',', $uniqueYml['fields']));
|
||||
} else {
|
||||
$unique['fields'] = $uniqueYml['fields'];
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
isset($unique['columns'], $unique['fields'])
|
||||
|| (
|
||||
! isset($unique['columns'])
|
||||
&& ! isset($unique['fields'])
|
||||
)
|
||||
) {
|
||||
throw MappingException::invalidUniqueConstraintConfiguration(
|
||||
$className,
|
||||
$uniqueYml['name']
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($uniqueYml['options'])) {
|
||||
@@ -371,7 +420,7 @@ class YamlDriver extends FileDriver
|
||||
foreach ($element['oneToOne'] as $name => $oneToOneElement) {
|
||||
$mapping = [
|
||||
'fieldName' => $name,
|
||||
'targetEntity' => $oneToOneElement['targetEntity'],
|
||||
'targetEntity' => $oneToOneElement['targetEntity'] ?? null,
|
||||
];
|
||||
|
||||
if (isset($associationIds[$mapping['fieldName']])) {
|
||||
@@ -466,7 +515,7 @@ class YamlDriver extends FileDriver
|
||||
foreach ($element['manyToOne'] as $name => $manyToOneElement) {
|
||||
$mapping = [
|
||||
'fieldName' => $name,
|
||||
'targetEntity' => $manyToOneElement['targetEntity'],
|
||||
'targetEntity' => $manyToOneElement['targetEntity'] ?? null,
|
||||
];
|
||||
|
||||
if (isset($associationIds[$mapping['fieldName']])) {
|
||||
@@ -689,8 +738,6 @@ class YamlDriver extends FileDriver
|
||||
* Constructs a joinColumn mapping array based on the information
|
||||
* found in the given join column element.
|
||||
*
|
||||
* @return mixed[] The mapping array.
|
||||
*
|
||||
* @psalm-param array{
|
||||
* referencedColumnName?: mixed,
|
||||
* name?: mixed,
|
||||
@@ -700,6 +747,8 @@ class YamlDriver extends FileDriver
|
||||
* onDelete?: mixed,
|
||||
* columnDefinition?: mixed
|
||||
* } $joinColumnElement The array join column element.
|
||||
*
|
||||
* @return mixed[] The mapping array.
|
||||
* @psalm-return array{
|
||||
* referencedColumnName?: string,
|
||||
* name?: string,
|
||||
@@ -747,8 +796,6 @@ class YamlDriver extends FileDriver
|
||||
/**
|
||||
* Parses the given column as array.
|
||||
*
|
||||
* @return mixed[]
|
||||
*
|
||||
* @psalm-param array{
|
||||
* type?: string,
|
||||
* column?: string,
|
||||
@@ -760,11 +807,13 @@ class YamlDriver extends FileDriver
|
||||
* version?: mixed,
|
||||
* columnDefinition?: mixed
|
||||
* }|null $column
|
||||
*
|
||||
* @return mixed[]
|
||||
* @psalm-return array{
|
||||
* fieldName: string,
|
||||
* type?: string,
|
||||
* columnName?: mixed,
|
||||
* length?: mixed,
|
||||
* columnName?: string,
|
||||
* length?: int,
|
||||
* precision?: mixed,
|
||||
* scale?: mixed,
|
||||
* unique?: bool,
|
||||
@@ -832,13 +881,13 @@ class YamlDriver extends FileDriver
|
||||
* Parse / Normalize the cache configuration
|
||||
*
|
||||
* @param mixed[] $cacheMapping
|
||||
* @psalm-param array{usage: mixed, region: (string|null)} $cacheMapping
|
||||
* @psalm-param array{usage: string, region?: string} $cacheMapping
|
||||
*
|
||||
* @return mixed[]
|
||||
*
|
||||
* @psalm-param array{usage: mixed, region: (string|null)} $cacheMapping
|
||||
* @psalm-return array{usage: mixed, region: (string|null)}
|
||||
* @psalm-return array{usage: int, region: string|null}
|
||||
*/
|
||||
private function cacheToArray($cacheMapping)
|
||||
private function cacheToArray(array $cacheMapping): array
|
||||
{
|
||||
$region = isset($cacheMapping['region']) ? (string) $cacheMapping['region'] : null;
|
||||
$usage = isset($cacheMapping['usage']) ? strtoupper($cacheMapping['usage']) : null;
|
||||
@@ -848,7 +897,7 @@ class YamlDriver extends FileDriver
|
||||
}
|
||||
|
||||
if ($usage) {
|
||||
$usage = constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage);
|
||||
$usage = (int) constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage);
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class Embeddable implements Annotation
|
||||
{
|
||||
}
|
||||
|
||||
@@ -20,10 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_PROPERTY)]
|
||||
final class Embedded implements Annotation
|
||||
{
|
||||
/**
|
||||
@@ -32,6 +37,12 @@ final class Embedded implements Annotation
|
||||
*/
|
||||
public $class;
|
||||
|
||||
/** @var mixed */
|
||||
/** @var string|bool|null */
|
||||
public $columnPrefix;
|
||||
|
||||
public function __construct(string $class, $columnPrefix = null)
|
||||
{
|
||||
$this->class = $class;
|
||||
$this->columnPrefix = $columnPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class Entity implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
@@ -31,4 +36,10 @@ final class Entity implements Annotation
|
||||
|
||||
/** @var bool */
|
||||
public $readOnly = false;
|
||||
|
||||
public function __construct(?string $repositoryClass = null, bool $readOnly = false)
|
||||
{
|
||||
$this->repositoryClass = $repositoryClass;
|
||||
$this->readOnly = $readOnly;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,13 +20,18 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* The EntityListeners annotation specifies the callback listener classes to be used for an entity or mapped superclass.
|
||||
* The EntityListeners annotation may be applied to an entity class or mapped superclass.
|
||||
*
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class EntityListeners implements Annotation
|
||||
{
|
||||
/**
|
||||
@@ -35,4 +40,12 @@ final class EntityListeners implements Annotation
|
||||
* @var array<string>
|
||||
*/
|
||||
public $value = [];
|
||||
|
||||
/**
|
||||
* @param array<string> $value
|
||||
*/
|
||||
public function __construct(array $value = [])
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_PROPERTY)]
|
||||
final class GeneratedValue implements Annotation
|
||||
{
|
||||
/**
|
||||
@@ -33,4 +38,9 @@ final class GeneratedValue implements Annotation
|
||||
* @Enum({"AUTO", "SEQUENCE", "TABLE", "IDENTITY", "NONE", "UUID", "CUSTOM"})
|
||||
*/
|
||||
public $strategy = 'AUTO';
|
||||
|
||||
public function __construct(string $strategy = 'AUTO')
|
||||
{
|
||||
$this->strategy = $strategy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class HasLifecycleCallbacks implements Annotation
|
||||
{
|
||||
}
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_PROPERTY)]
|
||||
final class Id implements Annotation
|
||||
{
|
||||
}
|
||||
|
||||
@@ -20,10 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
|
||||
final class Index implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
@@ -32,9 +37,32 @@ final class Index implements Annotation
|
||||
/** @var array<string> */
|
||||
public $columns;
|
||||
|
||||
/** @var array<string> */
|
||||
public $fields;
|
||||
|
||||
/** @var array<string> */
|
||||
public $flags;
|
||||
|
||||
/** @var array */
|
||||
/** @var array<string,mixed> */
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* @param array<string> $columns
|
||||
* @param array<string> $fields
|
||||
* @param array<string> $flags
|
||||
* @param array<string> $options
|
||||
*/
|
||||
public function __construct(
|
||||
?array $columns = null,
|
||||
?array $fields = null,
|
||||
?string $name = null,
|
||||
?array $flags = null,
|
||||
?array $options = null
|
||||
) {
|
||||
$this->columns = $columns;
|
||||
$this->fields = $fields;
|
||||
$this->name = $name;
|
||||
$this->flags = $flags;
|
||||
$this->options = $options;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class InheritanceType implements Annotation
|
||||
{
|
||||
/**
|
||||
@@ -33,4 +38,9 @@ final class InheritanceType implements Annotation
|
||||
* @Enum({"NONE", "JOINED", "SINGLE_TABLE", "TABLE_PER_CLASS"})
|
||||
*/
|
||||
public $value;
|
||||
|
||||
public function __construct(string $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
}
|
||||
|
||||
72
lib/Doctrine/ORM/Mapping/InverseJoinColumn.php
Normal file
72
lib/Doctrine/ORM/Mapping/InverseJoinColumn.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
|
||||
final class InverseJoinColumn implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $name;
|
||||
|
||||
/** @var string */
|
||||
public $referencedColumnName = 'id';
|
||||
|
||||
/** @var bool */
|
||||
public $unique = false;
|
||||
|
||||
/** @var bool */
|
||||
public $nullable = true;
|
||||
|
||||
/** @var mixed */
|
||||
public $onDelete;
|
||||
|
||||
/** @var string */
|
||||
public $columnDefinition;
|
||||
|
||||
/**
|
||||
* Field name used in non-object hydration (array/scalar).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $fieldName;
|
||||
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
string $referencedColumnName = 'id',
|
||||
bool $unique = false,
|
||||
bool $nullable = true,
|
||||
$onDelete = null,
|
||||
?string $columnDefinition = null,
|
||||
?string $fieldName = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->referencedColumnName = $referencedColumnName;
|
||||
$this->unique = $unique;
|
||||
$this->nullable = $nullable;
|
||||
$this->onDelete = $onDelete;
|
||||
$this->columnDefinition = $columnDefinition;
|
||||
$this->fieldName = $fieldName;
|
||||
}
|
||||
}
|
||||
@@ -20,10 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target({"PROPERTY","ANNOTATION"})
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
|
||||
final class JoinColumn implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
@@ -50,4 +55,22 @@ final class JoinColumn implements Annotation
|
||||
* @var string
|
||||
*/
|
||||
public $fieldName;
|
||||
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
string $referencedColumnName = 'id',
|
||||
bool $unique = false,
|
||||
bool $nullable = true,
|
||||
$onDelete = null,
|
||||
?string $columnDefinition = null,
|
||||
?string $fieldName = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->referencedColumnName = $referencedColumnName;
|
||||
$this->unique = $unique;
|
||||
$this->nullable = $nullable;
|
||||
$this->onDelete = $onDelete;
|
||||
$this->columnDefinition = $columnDefinition;
|
||||
$this->fieldName = $fieldName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target({"PROPERTY","ANNOTATION"})
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_PROPERTY)]
|
||||
final class JoinTable implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
@@ -37,4 +42,18 @@ final class JoinTable implements Annotation
|
||||
|
||||
/** @var array<\Doctrine\ORM\Mapping\JoinColumn> */
|
||||
public $inverseJoinColumns = [];
|
||||
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
?string $schema = null,
|
||||
$joinColumns = [],
|
||||
$inverseJoinColumns = []
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->schema = $schema;
|
||||
$this->joinColumns = $joinColumns instanceof JoinColumn ? [$joinColumns] : $joinColumns;
|
||||
$this->inverseJoinColumns = $inverseJoinColumns instanceof JoinColumn
|
||||
? [$inverseJoinColumns]
|
||||
: $inverseJoinColumns;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_PROPERTY)]
|
||||
final class ManyToMany implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
@@ -51,4 +56,25 @@ final class ManyToMany implements Annotation
|
||||
|
||||
/** @var string */
|
||||
public $indexBy;
|
||||
|
||||
/**
|
||||
* @param array<string> $cascade
|
||||
*/
|
||||
public function __construct(
|
||||
string $targetEntity,
|
||||
?string $mappedBy = null,
|
||||
?string $inversedBy = null,
|
||||
?array $cascade = null,
|
||||
string $fetch = 'LAZY',
|
||||
bool $orphanRemoval = false,
|
||||
?string $indexBy = null
|
||||
) {
|
||||
$this->targetEntity = $targetEntity;
|
||||
$this->mappedBy = $mappedBy;
|
||||
$this->inversedBy = $inversedBy;
|
||||
$this->cascade = $cascade;
|
||||
$this->fetch = $fetch;
|
||||
$this->orphanRemoval = $orphanRemoval;
|
||||
$this->indexBy = $indexBy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_PROPERTY)]
|
||||
final class ManyToOne implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
@@ -42,4 +47,19 @@ final class ManyToOne implements Annotation
|
||||
|
||||
/** @var string */
|
||||
public $inversedBy;
|
||||
|
||||
/**
|
||||
* @param array<string> $cascade
|
||||
*/
|
||||
public function __construct(
|
||||
?string $targetEntity = null,
|
||||
?array $cascade = null,
|
||||
string $fetch = 'LAZY',
|
||||
?string $inversedBy = null
|
||||
) {
|
||||
$this->targetEntity = $targetEntity;
|
||||
$this->cascade = $cascade;
|
||||
$this->fetch = $fetch;
|
||||
$this->inversedBy = $inversedBy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,22 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
final class MappedSuperclass implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $repositoryClass;
|
||||
|
||||
public function __construct(?string $repositoryClass = null)
|
||||
{
|
||||
$this->repositoryClass = $repositoryClass;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,14 +278,13 @@ class MappingException extends ORMException
|
||||
return new self(sprintf("Result set mapping name on entity class '%s' is not defined.", $className));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fieldName
|
||||
*
|
||||
* @return MappingException
|
||||
*/
|
||||
public static function oneToManyRequiresMappedBy($fieldName)
|
||||
public static function oneToManyRequiresMappedBy(string $entityName, string $fieldName): MappingException
|
||||
{
|
||||
return new self(sprintf("OneToMany mapping on field '%s' requires the 'mappedBy' attribute.", $fieldName));
|
||||
return new self(sprintf(
|
||||
"OneToMany mapping on entity '%s' field '%s' requires the 'mappedBy' attribute.",
|
||||
$entityName,
|
||||
$fieldName
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -383,6 +382,8 @@ class MappingException extends ORMException
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 2.9 no longer in use
|
||||
*
|
||||
* @param string $className
|
||||
* @param string $propertyName
|
||||
*
|
||||
@@ -921,7 +922,7 @@ class MappingException extends ORMException
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MappingException
|
||||
* @return self
|
||||
*/
|
||||
public static function illegalOverrideOfInheritedProperty($className, $propertyName)
|
||||
{
|
||||
@@ -934,4 +935,32 @@ class MappingException extends ORMException
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function invalidIndexConfiguration($className, $indexName)
|
||||
{
|
||||
return new self(
|
||||
sprintf(
|
||||
'Index %s for entity %s should contain columns or fields values, but not both.',
|
||||
$indexName,
|
||||
$className
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function invalidUniqueConstraintConfiguration($className, $indexName)
|
||||
{
|
||||
return new self(
|
||||
sprintf(
|
||||
'Unique constraint %s for entity %s should contain columns or fields values, but not both.',
|
||||
$indexName,
|
||||
$className
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_PROPERTY)]
|
||||
final class OneToMany implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
@@ -48,4 +53,23 @@ final class OneToMany implements Annotation
|
||||
|
||||
/** @var string */
|
||||
public $indexBy;
|
||||
|
||||
/**
|
||||
* @param array<string> $cascade
|
||||
*/
|
||||
public function __construct(
|
||||
?string $mappedBy = null,
|
||||
?string $targetEntity = null,
|
||||
?array $cascade = null,
|
||||
string $fetch = 'LAZY',
|
||||
bool $orphanRemoval = false,
|
||||
?string $indexBy = null
|
||||
) {
|
||||
$this->mappedBy = $mappedBy;
|
||||
$this->targetEntity = $targetEntity;
|
||||
$this->cascade = $cascade;
|
||||
$this->fetch = $fetch;
|
||||
$this->orphanRemoval = $orphanRemoval;
|
||||
$this->indexBy = $indexBy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_PROPERTY)]
|
||||
final class OneToOne implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
@@ -48,4 +53,23 @@ final class OneToOne implements Annotation
|
||||
|
||||
/** @var bool */
|
||||
public $orphanRemoval = false;
|
||||
|
||||
/**
|
||||
* @param array<string> $cascade
|
||||
*/
|
||||
public function __construct(
|
||||
?string $mappedBy = null,
|
||||
?string $inversedBy = null,
|
||||
?string $targetEntity = null,
|
||||
?array $cascade = null,
|
||||
string $fetch = 'LAZY',
|
||||
bool $orphanRemoval = false
|
||||
) {
|
||||
$this->mappedBy = $mappedBy;
|
||||
$this->inversedBy = $inversedBy;
|
||||
$this->targetEntity = $targetEntity;
|
||||
$this->cascade = $cascade;
|
||||
$this->fetch = $fetch;
|
||||
$this->orphanRemoval = $orphanRemoval;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,25 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_PROPERTY)]
|
||||
final class OrderBy implements Annotation
|
||||
{
|
||||
/** @var array<string> */
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* @param array<string> $value
|
||||
*/
|
||||
public function __construct(array $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_METHOD)]
|
||||
final class PostLoad implements Annotation
|
||||
{
|
||||
}
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_METHOD)]
|
||||
final class PostPersist implements Annotation
|
||||
{
|
||||
}
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_METHOD)]
|
||||
final class PostRemove implements Annotation
|
||||
{
|
||||
}
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_METHOD)]
|
||||
final class PostUpdate implements Annotation
|
||||
{
|
||||
}
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_METHOD)]
|
||||
final class PreFlush implements Annotation
|
||||
{
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user