Compare commits

...

55 Commits

Author SHA1 Message Date
Alexander M. Turek d2de4ec03c Revert "Introduce FilterCollection#restore method (#10537)"
This reverts commit 8e20e1598e.
2023-07-28 16:08:17 +02:00
Vincent Langlet 8e20e1598e Introduce FilterCollection#restore method (#10537)
* Introduce FilterCollection#restore method

* Add suspend method instead

* Add more tests
2023-07-28 16:05:51 +02:00
Grégoire Paris 24df74d61d Merge pull request #10856 from VincentLanglet/fixDeprecation
Fix/Self deprecation with getQueryCacheImpl
2023-07-26 23:41:39 +02:00
Vincent Langlet 442f073d25 Fix static analysis 2023-07-26 17:42:20 +02:00
Vincent Langlet a5161e9485 Other solution 2023-07-26 16:00:37 +02:00
Vincent Langlet ddc7d953b9 Avoid self deprecation 2023-07-26 12:37:42 +02:00
Kévin Dunglas db51ed4f4c fix: use platform options instead of deprecated custom options (#10855) 2023-07-25 16:23:46 +02:00
Grégoire Paris f7e4b61459 Merge pull request #10847 from greg0ire/remove-toc 2023-07-18 09:50:04 +02:00
Grégoire Paris 6b0afdbd58 Avoid triple colons
It confuses the guides, and is ugly.
2023-07-18 08:57:09 +02:00
Grégoire Paris d3cf17b26d Remove toc
We already have the sidebar for this.
2023-07-18 08:56:24 +02:00
Alexander M. Turek 5213228a64 PHPStan 1.10.25, Psalm 5.13.1 (#10842) 2023-07-16 23:38:29 +02:00
Grégoire Paris 7848417488 Merge pull request #10838 from greg0ire/remove-dummy-title 2023-07-12 11:07:46 +02:00
Grégoire Paris 56e5856ad7 Remove dummy title
This was never meant to be under version control. Did not spot it in the
diff.

Closes #10836
2023-07-12 11:06:06 +02:00
Grégoire Paris 385bdd33f1 Merge pull request #10798 from greg0ire/less-partial-load
Resort on Query::HINT_FORCE_PARTIAL_LOAD less
2023-07-11 23:46:17 +02:00
Grégoire Paris 1413b496d7 Merge pull request #10824 from greg0ire/fix-build 2023-07-11 12:10:23 +02:00
Grégoire Paris ec7a8a7a0f Merge pull request #10828 from greg0ire/matching-xml
Match namespace in XML file with namespace in XSD file
2023-07-09 23:02:43 +02:00
Grégoire Paris e94fa8588d Match namespace in XML file with namespace in XSD file
In 7fa3e6ec7c, a global search and replace
was used for http and https.
This broke the documentation examples in that as soon as you turn on XSD
validation, it will fail because the namespace in the XML file does not
match the ones defined in the XSD file, which do not exhibit the https.

Note that this is not a security concern, because these URIs are
not meant to be actually resolved, but to serve as a unique identifier
for the namespace in which we define our elements.
2023-07-09 21:18:57 +02:00
Grégoire Paris 450cae2caa Add dummy title to the sidebar before checking docs
The way we have our docs, the sidebar is a separate document and as
such, needs a title. Let us prepend a dummy title until the guides-cli
provides a way to ignore that particular error.
2023-07-07 13:59:27 +02:00
Grégoire Paris 4978e0e336 Merge pull request #10819 from nicolas-grekas/fix-proxy-clone 2023-07-06 16:23:53 +02:00
Nicolas Grekas eee87c376d Fix cloning entities when using lazy-ghost proxies 2023-07-06 15:08:59 +02:00
Grégoire Paris 0b9060c728 Merge pull request #10806 from rmikalkenas/fix-enum-recomputation 2023-07-06 10:23:59 +02:00
Grégoire Paris 514f6b8c28 Merge pull request #10813 from Greg0/fix-primary-id-mapping-xml-driver 2023-07-06 09:12:53 +02:00
Rokas Mikalkėnas c1018fe299 Fixes recomputation of single entity change set when entity contains enum attributes.
Due to the fact that originalEntityData contains enum objects and
ReflectionEnumProperty::getValue() returns value of enum,
comparison of values are always falsy, resulting to update
columns value even though it has not changes.
2023-07-05 23:57:34 +03:00
Grzegorz Kuźnik 075824f5b5 Fix code style issues 2023-07-05 21:10:48 +02:00
Grégoire Paris d6f4834476 Merge pull request #10815 from greg0ire/test-docs 2023-07-05 10:01:37 +02:00
Grégoire Paris 9dadffe270 Use absolute references
According to the Sphinx docs, when in reference/architecture.rst, a
reference to reference/inheritance-mapping would resolve to
reference/reference/inheritance-mapping.rst, because it is relative to
the current document
2023-07-04 17:44:44 +02:00
Grégoire Paris b6e7e6d723 Avoid colon followed by double colon
It seems to confuse the guides-cli, if it is even valid.
2023-07-04 17:31:05 +02:00
Grégoire Paris 710937d6f8 Use correct syntax for references
doc was used when it is clearly a ref, plus there was a leading
underscore preventing the resolution.
2023-07-04 17:26:04 +02:00
Grégoire Paris 5d2d6642c8 Fix invalid reference syntax
Without this change, a hash is displayed for some reason.
2023-07-04 17:14:03 +02:00
Grégoire Paris 4d56711d8c Use rst syntax
Using underscore for emphasis is not an RST thing, in rst the difference
between emphasis and strong emphasis is in the number of asterisks.

Right now these underscores are just ignored and displayed on the
website.

See https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#emphasis
2023-07-04 17:11:46 +02:00
Grégoire Paris a157bc3fb3 Use internal link 2023-07-04 17:05:47 +02:00
Grégoire Paris 1aeab391c7 Escape pipes
Pipes can be used to define substitutions, it is part of the rst
standard. This explains why some of the links in this document are not
displayed on the website.

See https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#substitution-definitions
2023-07-04 16:48:14 +02:00
Grégoire Paris a4ecd02349 Introduce new workflow to test docs
This allows to check compatibility with phpDocumentor/guides, but also
should allow to spot embarassing mistakes in our existing docs.
2023-07-04 16:45:18 +02:00
Grégoire Paris 8c59828f6c Remove lone dash (#10812) 2023-07-03 21:46:12 +02:00
Grzegorz K 0877ecbe56 Treat id field proprites same as regular field 2023-07-03 15:15:08 +02:00
Grégoire Paris 4887359827 Merge pull request #10807 from mpdude/move-tests
Move three "Ticket/"-style tests to the right namespace
2023-06-28 20:54:43 +02:00
Matthias Pigulla 5afe9b80a8 Move three "Ticket/"-style tests to the right namespace 2023-06-28 16:39:47 +02:00
Grégoire Paris 584c4aeed1 Merge pull request #10804 from greg0ire/parenthesized 2023-06-28 14:11:41 +02:00
Grégoire Paris c9c5157fda Follow recommendation about multiline type
Apparently, there is consensus about multiline types between:

- PHPStan
- Psalm
- Slevomat Coding Standard

See https://github.com/slevomat/coding-standard/issues/1586#issuecomment-1610195706

Using parenthesis is less ambiguous, it makes it clear to the parser
where the type begins and where it ends.

The change has a positive impact on the Psalm baseline, showing
that psalm-return annotation was not really understood previously.
2023-06-28 07:46:35 +02:00
Grégoire Paris dba90c1a91 Merge pull request #10786 from vuongxuongminh/fix-attach-entity-listener-when-reset-class-metadata-factory
Fix attach entity listener when reset class metadata factory
2023-06-27 23:53:01 +02:00
Nicolas Grekas 55d477dc50 Fix unserialize() errors when running tests on PHP 8.3 (#10803) 2023-06-27 17:47:09 +02:00
Grégoire Paris 4da8d3be96 Resort on Query::HINT_FORCE_PARTIAL_LOAD less
A lot of our tests mention it, but I do not think it is important to the
test. Maybe it was a way to have more efficient tests? Most times,
removing the hint does not affect the test outcome.
2023-06-27 08:37:05 +02:00
Matthias Pigulla 4aadba65ce Explain EntityManager::getReference() peculiarities (#10800)
* Explain `EntityManager::getReference()` peculiarities

As one takeaway from https://github.com/doctrine/orm/issues/3037#issuecomment-1605657003 and #843, we should look into better explaining the `EntityManager::getReference()` method, it’s semantics, caveats and potential responsibilities placed on the user.

This PR tries to do that, so it fixes #10797.

* Update docs/en/reference/advanced-configuration.rst

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>

---------

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2023-06-27 00:16:55 +02:00
Grégoire Paris 814d8d4d39 Merge pull request #10802 from greg0ire/psalm-5-13
Upgrade to Psalm 5.13
2023-06-27 00:05:00 +02:00
Grégoire Paris dc411954ad Upgrade to Psalm 5.13
This is a nice release as far as the ORM is concerned:
- a small baseline reduction;
- lots of useless calls to sprintf spotted.
2023-06-26 19:12:02 +02:00
Minh Vuong da29eb675c test: assert postLoad has data first 2023-06-26 09:29:00 +07:00
Grégoire Paris b17e52ba6b Merge pull request #10791 from mpdude/understand-3037
Avoid creating unmanaged proxy instances for referred-to entities during `merge()`
2023-06-25 23:52:53 +02:00
Grégoire Paris 4e138903d0 Merge pull request #10789 from macroparts/distinct-updates-state-correctly
distinct() updates QueryBuilder state correctly
2023-06-25 18:11:37 +02:00
Daniel Jurkovic efb50b9bdd distinct() updates QueryBuilder state correctly
Previously calling distinct() when the QueryBuilder was in clean state would cause subsequent getDQL() calls to ignore the distinct queryPart

Fixes #10784
2023-06-25 17:53:24 +02:00
Matthias Pigulla 1989531d4f Update tests/Doctrine/Tests/ORM/Functional/Ticket/GH7407Test.php
Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2023-06-23 22:47:34 +02:00
Matthias Pigulla d738ecfcfe Avoid creating unmanaged proxy instances for referred-to entities during merge()
This PR tries to improve the situation/problem explained in #3037:

Under certain conditions – there may be multiple and not all are known/well-understood – we may get inconsistencies between the `\Doctrine\ORM\UnitOfWork::$entityIdentifiers` and `\Doctrine\ORM\UnitOfWork::$identityMap` arrays.

Since the `::$identityMap` is a plain array holding object references, objects contained in it cannot be garbage-collected.
`::$entityIdentifiers`, however, is indexed by `spl_object_id` values. When those objects are destructed and/or garbage-collected, the OID may be reused and reassigned to other objects later on.

When the OID re-assignment happens to be for another entity, the UoW may assume incorrect entity states and, for example, miss INSERT or UPDATE operations.

One cause for such inconsistencies is _replacing_ identity map entries with other object instances: This makes it possible that the old object becomes GC'd, while its OID is not cleaned up. Since that is not a use case we need to support (IMHO), #10785 is about adding a safeguard against it.

In this test shown here, the `merge()` operation is currently too eager in creating a proxy object for another referred-to entity. This proxy represents an entity already present in the identity map at that time, potentially leading to this problem later on.
2023-06-23 22:19:06 +02:00
Grégoire Paris f76bab2b73 Merge pull request #10790 from greg0ire/slevomat-cs-upgrade
Work around slevomat/coding-standard issues
2023-06-23 22:13:30 +02:00
Grégoire Paris 0e06d6b67d Apply latest coding standard rules 2023-06-23 18:11:22 +02:00
Grégoire Paris 5114dcee0b Work around slevomat/coding-standard issues
I tweaked the code so that it would not fall victim to
https://github.com/slevomat/coding-standard/issues/1585 or
https://github.com/slevomat/coding-standard/issues/1586, thus fixing the
phpcs job without losing information or breaking other jobs.
2023-06-23 18:11:07 +02:00
Minh Vuong 338deacb58 fix: attach entity listener when reset metadata factory 2023-06-22 17:02:24 +07:00
54 changed files with 552 additions and 408 deletions
+51
View File
@@ -0,0 +1,51 @@
name: "Documentation"
on:
pull_request:
branches:
- "*.x"
paths:
- .github/workflows/documentation.yml
- docs/**
push:
branches:
- "*.x"
paths:
- .github/workflows/documentation.yml
- docs/**
jobs:
validate-with-guides:
name: "Validate documentation with phpDocumentor/guides"
runs-on: "ubuntu-22.04"
steps:
- name: "Checkout code"
uses: "actions/checkout@v3"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "8.2"
- name: "Remove existing composer file"
run: "rm composer.json"
- name: "Require phpdocumentor/guides-cli"
run: "composer require --dev phpdocumentor/guides-cli dev-main@dev --no-update"
- name: "Configure minimum stability"
run: "composer config minimum-stability dev"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v2"
with:
dependency-versions: "highest"
- name: "Add dummy title to the sidebar"
run: |
printf '%s\n%s\n\n%s\n' "Dummy title" "===========" "$(cat docs/en/sidebar.rst)" > docs/en/sidebar.rst
- name: "Run guides-cli"
run: "vendor/bin/guides -vvv --no-progress docs/en /tmp/test 2>&1 | ( ! grep WARNING )"
+2 -2
View File
@@ -42,14 +42,14 @@
"doctrine/annotations": "^1.13 || ^2",
"doctrine/coding-standard": "^9.0.2 || ^12.0",
"phpbench/phpbench": "^0.16.10 || ^1.0",
"phpstan/phpstan": "~1.4.10 || 1.10.18",
"phpstan/phpstan": "~1.4.10 || 1.10.25",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6",
"psr/log": "^1 || ^2 || ^3",
"squizlabs/php_codesniffer": "3.7.2",
"symfony/cache": "^4.4 || ^5.4 || ^6.0",
"symfony/var-exporter": "^4.4 || ^5.4 || ^6.2",
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
"vimeo/psalm": "4.30.0 || 5.12.0"
"vimeo/psalm": "4.30.0 || 5.13.1"
},
"conflict": {
"doctrine/annotations": "<1.13 || >= 3.0"
+21 -23
View File
@@ -18,8 +18,8 @@ Doctrine ORM don't panic. You can get help from different sources:
- Report a bug on `GitHub <https://github.com/doctrine/orm/issues>`_.
- On `StackOverflow <https://stackoverflow.com/questions/tagged/doctrine-orm>`_
If you need more structure over the different topics you can browse the :doc:`table
of contents <toc>`.
If you need more structure over the different topics you can browse the table
of contents.
Getting Started
---------------
@@ -34,32 +34,32 @@ Mapping Objects onto a Database
-------------------------------
* **Mapping**:
:doc:`Objects <reference/basic-mapping>` |
:doc:`Associations <reference/association-mapping>` |
:doc:`Objects <reference/basic-mapping>` \|
:doc:`Associations <reference/association-mapping>` \|
:doc:`Inheritance <reference/inheritance-mapping>`
* **Drivers**:
:doc:`Docblock Annotations <reference/annotations-reference>` |
:doc:`Attributes <reference/attributes-reference>` |
:doc:`XML <reference/xml-mapping>` |
:doc:`YAML <reference/yaml-mapping>` |
:doc:`Docblock Annotations <reference/annotations-reference>` \|
:doc:`Attributes <reference/attributes-reference>` \|
:doc:`XML <reference/xml-mapping>` \|
:doc:`YAML <reference/yaml-mapping>` \|
:doc:`PHP <reference/php-mapping>`
Working with Objects
--------------------
* **Basic Reference**:
:doc:`Entities <reference/working-with-objects>` |
:doc:`Associations <reference/working-with-associations>` |
:doc:`Entities <reference/working-with-objects>` \|
:doc:`Associations <reference/working-with-associations>` \|
:doc:`Events <reference/events>`
* **Query Reference**:
:doc:`DQL <reference/dql-doctrine-query-language>` |
:doc:`QueryBuilder <reference/query-builder>` |
:doc:`DQL <reference/dql-doctrine-query-language>` \|
:doc:`QueryBuilder <reference/query-builder>` \|
:doc:`Native SQL <reference/native-sql>`
* **Internals**:
:doc:`Internals explained <reference/unitofwork>` |
:doc:`Internals explained <reference/unitofwork>` \|
:doc:`Associations <reference/unitofwork-associations>`
Advanced Topics
@@ -102,20 +102,20 @@ Cookbook
--------
* **Patterns**:
:doc:`Aggregate Fields <cookbook/aggregate-fields>` |
:doc:`Decorator Pattern <cookbook/decorator-pattern>` |
:doc:`Aggregate Fields <cookbook/aggregate-fields>` \|
:doc:`Decorator Pattern <cookbook/decorator-pattern>` \|
:doc:`Strategy Pattern <cookbook/strategy-cookbook-introduction>`
* **DQL Extension Points**:
:doc:`DQL Custom Walkers <cookbook/dql-custom-walkers>` |
:doc:`DQL Custom Walkers <cookbook/dql-custom-walkers>` \|
:doc:`DQL User-Defined-Functions <cookbook/dql-user-defined-functions>`
* **Implementation**:
:doc:`Array Access <cookbook/implementing-arrayaccess-for-domain-objects>` |
:doc:`Notify ChangeTracking Example <cookbook/implementing-the-notify-changetracking-policy>` |
:doc:`Working with DateTime <cookbook/working-with-datetime>` |
:doc:`Validation <cookbook/validation-of-entities>` |
:doc:`Entities in the Session <cookbook/entities-in-session>` |
:doc:`Array Access <cookbook/implementing-arrayaccess-for-domain-objects>` \|
:doc:`Notify ChangeTracking Example <cookbook/implementing-the-notify-changetracking-policy>` \|
:doc:`Working with DateTime <cookbook/working-with-datetime>` \|
:doc:`Validation <cookbook/validation-of-entities>` \|
:doc:`Entities in the Session <cookbook/entities-in-session>` \|
:doc:`Keeping your Modules independent <cookbook/resolve-target-entity-listener>`
* **Hidden Gems**
@@ -124,5 +124,3 @@ Cookbook
* **Custom Datatypes**
:doc:`MySQL Enums <cookbook/mysql-enums>`
:doc:`Advanced Field Value Conversion <cookbook/advanced-field-value-conversion-using-custom-mapping-types>`
.. include:: toc.rst
+31 -11
View File
@@ -311,10 +311,12 @@ Reference Proxies
The method ``EntityManager#getReference($entityName, $identifier)``
lets you obtain a reference to an entity for which the identifier
is known, without loading that entity from the database. This is
useful, for example, as a performance enhancement, when you want to
establish an association to an entity for which you have the
identifier. You could simply do this:
is known, without necessarily loading that entity from the database.
This is useful, for example, as a performance enhancement, when you
want to establish an association to an entity for which you have the
identifier.
Consider the following example:
.. code-block:: php
@@ -324,15 +326,33 @@ identifier. You could simply do this:
$item = $em->getReference('MyProject\Model\Item', $itemId);
$cart->addItem($item);
Here, we added an Item to a Cart without loading the Item from the
database. If you access any state that isn't yet available in the
Item instance, the proxying mechanism would fully initialize the
object's state transparently from the database. Here
$item is actually an instance of the proxy class that was generated
for the Item class but your code does not need to care. In fact it
**should not care**. Proxy objects should be transparent to your
Whether the object being returned from ``EntityManager#getReference()``
is a proxy or a direct instance of the entity class may depend on different
factors, including whether the entity has already been loaded into memory
or entity inheritance being used. But your code does not need to care
and in fact it **should not care**. Proxy objects should be transparent to your
code.
When using the ``EntityManager#getReference()`` method, you need to be aware
of a few peculiarities.
At the best case, the ORM can avoid querying the database at all. But, that
also means that this method will not throw an exception when an invalid value
for the ``$identifier`` parameter is passed. ``$identifier`` values are
not checked and there is no guarantee that the requested entity instance even
exists the method will still return a proxy object.
Its only when the proxy has to be fully initialized or associations cannot
be written to the database that invalid ``$identifier`` values may lead to
exceptions.
The ``EntityManager#getReference()`` is mostly useful when you only
need a reference to some entity to make an association, like in the example
above. In that case, it can save you from loading data from the database
that you don't need. But remember as soon as you read any property values
besides those making up the ID, a database request will be made to initialize
all fields.
Association proxies
~~~~~~~~~~~~~~~~~~~
+1 -1
View File
@@ -102,7 +102,7 @@ persistent entity state and mapping information for its subclasses,
but which is not itself an entity.
Mapped superclasses are explained in greater detail in the chapter
on :doc:`inheritance mapping <reference/inheritance-mapping>`.
on :doc:`inheritance mapping </reference/inheritance-mapping>`.
Transient Classes
~~~~~~~~~~~~~~~~~
+1 -1
View File
@@ -460,7 +460,7 @@ Here is the list of possible generation strategies:
a new entity is passed to ``EntityManager#persist``. NONE is the
same as leaving off the ``#[GeneratedValue]`` entirely.
- ``CUSTOM``: With this option, you can use the ``#[CustomIdGenerator]`` attribute.
It will allow you to pass a :doc:`class of your own to generate the identifiers.<_annref_customidgenerator>`
It will allow you to pass a :ref:`class of your own to generate the identifiers.<annref_customidgenerator>`
Sequence Generator
^^^^^^^^^^^^^^^^^^
+1 -1
View File
@@ -104,7 +104,7 @@ Inside the ``ORMSetup`` methods several assumptions are made:
In order to have ``ORMSetup`` configure the cache automatically, the library ``symfony/cache``
has to be installed as a dependency.
If you want to configure Doctrine in more detail, take a look at the :doc:`Advanced Configuration <reference/advanced-configuration>` section.
If you want to configure Doctrine in more detail, take a look at the :doc:`Advanced Configuration </reference/advanced-configuration>` section.
.. note::
@@ -1336,8 +1336,8 @@ There are situations when a query you want to execute returns a
very large result-set that needs to be processed. All the
previously described hydration modes completely load a result-set
into memory which might not be feasible with large result sets. See
the `Batch Processing <batch-processing.html>`_ section on details how
to iterate large result sets.
the :doc:`Batch Processing </reference/batch-processing>` section on
details how to iterate large result sets.
Functions
~~~~~~~~~
+4 -4
View File
@@ -281,10 +281,10 @@ specific to a particular entity class's lifecycle.
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="User">
<!-- ... -->
<lifecycle-callbacks>
+1 -1
View File
@@ -91,7 +91,7 @@ Apply Best Practices
A lot of the points mentioned in the Best Practices chapter will
also positively affect the performance of Doctrine.
See :doc:`Best Practices <reference/best-practices>`
See :doc:`Best Practices </reference/best-practices>`
Change Tracking policies
------------------------
+4 -4
View File
@@ -35,7 +35,7 @@ have to be used.
superclass, since they require the "many" side to hold the foreign
key.
It is, however, possible to use the :doc:`ResolveTargetEntityListener <cookbook/resolve-target-entity-listener>`
It is, however, possible to use the :doc:`ResolveTargetEntityListener </cookbook/resolve-target-entity-listener>`
to replace references to a mapped superclass with an entity class at runtime.
As long as there is only one entity subclass inheriting from the mapped
superclass and all references to the mapped superclass are resolved to that
@@ -45,7 +45,7 @@ have to be used.
.. warning::
At least when using attributes or annotations to specify your mapping,
it _seems_ as if you could inherit from a base class that is neither
it *seems* as if you could inherit from a base class that is neither
an entity nor a mapped superclass, but has properties with mapping configuration
on them that would also be used in the inheriting class.
@@ -60,7 +60,7 @@ have to be used.
You may be tempted to use traits to mix mapped fields or relationships
into your entity classes to circumvent some of the limitations of
mapped superclasses. Before doing that, please read the section on traits
in the :doc:`Limitations and Known Issues <reference/limitations-and-known-issues>` chapter.
in the :doc:`Limitations and Known Issues </reference/limitations-and-known-issues>` chapter.
Example:
@@ -380,7 +380,7 @@ It is not supported to use overrides in entity inheritance scenarios.
.. note::
When using traits, make sure not to miss the warnings given in the
:doc:`Limitations and Known Issues<reference/limitations-and-known-issues>` chapter.
:doc:`Limitations and Known Issues</reference/limitations-and-known-issues>` chapter.
Association Override
+1 -1
View File
@@ -1,4 +1,4 @@
Installation
============
The installation chapter has moved to :doc:`Installation and Configuration <reference/configuration>`_.
The installation chapter has moved to :doc:`Installation and Configuration </reference/configuration>`.
@@ -145,7 +145,7 @@ more than two years after the initial Doctrine 2 release and the time where
core components were designed.
In fact, this documentation mentions traits only in the context of
:doc:`overriding field association mappings in subclasses <tutorials/override-field-association-mappings-in-subclasses>`.
:doc:`overriding field association mappings in subclasses </tutorials/override-field-association-mappings-in-subclasses>`.
Coverage of traits in test cases is practically nonexistent.
Thus, you should at least be aware that when using traits in your entity and
@@ -162,7 +162,7 @@ that, some precedence and conflict resolution rules apply.
When it comes to loading mapping configuration, the annotation and attribute drivers
rely on PHP reflection to inspect class properties including their docblocks.
As long as the results are consistent with what a solution _without_ traits would
As long as the results are consistent with what a solution *without* traits would
have produced, this is probably fine.
However, to mention known limitations, it is currently not possible to use "class"
-2
View File
@@ -578,8 +578,6 @@ of DQL. It takes 3 parameters: ``$dqlPartName``, ``$dqlPart`` and
not (no effect on the ``where`` and ``having`` DQL query parts,
which always override all previously defined items)
-
.. code-block:: php
<?php
+8 -2
View File
@@ -322,7 +322,10 @@ level cache region.
.. code-block:: xml
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Country">
<cache usage="READ_ONLY" region="my_entity_region" />
<id name="id" type="integer" column="id">
@@ -427,7 +430,10 @@ It caches the primary keys of association and cache each element will be cached
.. code-block:: xml
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="State">
<cache usage="NONSTRICT_READ_WRITE" />
+6 -6
View File
@@ -16,9 +16,9 @@ setup for the latest code in trunk.
.. code-block:: xml
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
...
@@ -102,9 +102,9 @@ of several common elements:
// Doctrine.Tests.ORM.Mapping.User.dcm.xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\ORM\Mapping\User" table="cms_users">
@@ -769,9 +769,9 @@ entity relationship. You can define this in XML with the "association-key" attri
.. code-block:: xml
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Application\Model\ArticleAttribute">
-86
View File
@@ -1,86 +0,0 @@
Welcome to Doctrine 2 ORM's documentation!
==========================================
Tutorials
---------
.. toctree::
:maxdepth: 1
tutorials/getting-started
tutorials/getting-started-database
tutorials/getting-started-models
tutorials/working-with-indexed-associations
tutorials/extra-lazy-associations
tutorials/composite-primary-keys
tutorials/ordered-associations
tutorials/override-field-association-mappings-in-subclasses
tutorials/pagination.rst
tutorials/embeddables.rst
Reference Guide
---------------
.. toctree::
:maxdepth: 1
:numbered:
reference/architecture
reference/configuration.rst
reference/faq
reference/basic-mapping
reference/association-mapping
reference/inheritance-mapping
reference/working-with-objects
reference/working-with-associations
reference/events
reference/unitofwork
reference/unitofwork-associations
reference/transactions-and-concurrency
reference/batch-processing
reference/dql-doctrine-query-language
reference/query-builder
reference/native-sql
reference/change-tracking-policies
reference/partial-objects
reference/annotations-reference
reference/attributes-reference
reference/xml-mapping
reference/yaml-mapping
reference/php-mapping
reference/caching
reference/improving-performance
reference/tools
reference/metadata-drivers
reference/best-practices
reference/limitations-and-known-issues
tutorials/pagination
reference/filters
reference/namingstrategy
reference/advanced-configuration
reference/second-level-cache
reference/security
Cookbook
--------
.. toctree::
:maxdepth: 1
cookbook/aggregate-fields
cookbook/custom-mapping-types
cookbook/decorator-pattern
cookbook/dql-custom-walkers
cookbook/dql-user-defined-functions
cookbook/implementing-arrayaccess-for-domain-objects
cookbook/implementing-the-notify-changetracking-policy
cookbook/resolve-target-entity-listener
cookbook/sql-table-prefixes
cookbook/strategy-cookbook-introduction
cookbook/validation-of-entities
cookbook/working-with-datetime
cookbook/mysql-enums
cookbook/advanced-field-value-conversion-using-custom-mapping-types
cookbook/entities-in-session
+4 -4
View File
@@ -85,9 +85,9 @@ and year of production as primary keys:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="VehicleCatalogue\Model\Car">
@@ -267,9 +267,9 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
.. code-block:: xml
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Application\Model\ArticleAttribute">
@@ -85,9 +85,9 @@ switch to extra lazy as shown in these examples:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\CMS\CmsGroup">
+16 -18
View File
@@ -102,8 +102,7 @@ Install Doctrine using the Composer Dependency Management tool, by calling:
This will install the packages Doctrine Common, Doctrine DBAL, Doctrine ORM,
into the ``vendor`` directory.
Add the following directories:
::
Add the following directories::
doctrine2-tutorial
|-- config
@@ -558,10 +557,10 @@ methods, but you only need to choose one.
.. code-block:: xml
<!-- config/xml/Product.dcm.xml -->
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Product" table="products">
<id name="id" type="integer">
@@ -1139,10 +1138,10 @@ the ``Product`` before:
.. code-block:: xml
<!-- config/xml/Bug.dcm.xml -->
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Bug" table="bugs">
<id name="id" type="integer">
@@ -1294,10 +1293,10 @@ Finally, we'll add metadata mappings for the ``User`` entity.
.. code-block:: xml
<!-- config/xml/User.dcm.xml -->
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="User" table="users">
<id name="id" type="integer">
@@ -1344,8 +1343,7 @@ means the join details have already been defined on the owning
side. Therefore we only have to specify the property on the Bug
class that holds the owning sides.
Update your database schema by running:
::
Update your database schema by running::
$ php bin/doctrine orm:schema-tool:update --force
@@ -1819,9 +1817,9 @@ we have to adjust the metadata slightly.
.. code-block:: xml
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Bug" table="bugs" repository-class="BugRepository">
@@ -161,9 +161,9 @@ The code and mappings for the Market entity looks like this:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\StockExchange\Market">
@@ -278,9 +278,9 @@ here are the code and mappings for it:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\StockExchange\Stock">
+2 -2
View File
@@ -208,10 +208,10 @@ class Configuration extends \Doctrine\DBAL\Configuration
}
/**
* @deprecated No replacement planned.
*
* Adds a namespace under a certain alias.
*
* @deprecated No replacement planned.
*
* @param string $alias
* @param string $namespace
*
@@ -313,7 +313,6 @@ class ObjectHydrator extends AbstractHydrator
* that belongs to a particular component/class. Afterwards, all these chunks
* are processed, one after the other. For each chunk of class data only one of the
* following code paths is executed:
*
* Path A: The data chunk belongs to a joined/associated object and the association
* is collection-valued.
* Path B: The data chunk belongs to a joined/associated object and the association
@@ -23,7 +23,6 @@ use function class_exists;
use function constant;
use function defined;
use function get_class;
use function sprintf;
use const PHP_VERSION_ID;
@@ -56,10 +55,10 @@ class AttributeDriver extends CompatibilityAnnotationDriver
public function __construct(array $paths)
{
if (PHP_VERSION_ID < 80000) {
throw new LogicException(sprintf(
throw new LogicException(
'The attribute metadata driver cannot be enabled on PHP 7. Please upgrade to PHP 8 or choose a different'
. ' metadata driver.'
));
);
}
$this->reader = new AttributeReader();
+8 -32
View File
@@ -50,27 +50,25 @@ class XmlDriver extends FileDriver
public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION, bool $isXsdValidationEnabled = false)
{
if (! extension_loaded('simplexml')) {
throw new LogicException(sprintf(
throw new LogicException(
'The XML metadata driver cannot be enabled because the SimpleXML PHP extension is missing.'
. ' Please configure PHP with SimpleXML or choose a different metadata driver.'
));
);
}
if (! $isXsdValidationEnabled) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/6728',
sprintf(
'Using XML mapping driver with XSD validation disabled is deprecated'
. ' and will not be supported in Doctrine ORM 3.0.'
)
'Using XML mapping driver with XSD validation disabled is deprecated'
. ' and will not be supported in Doctrine ORM 3.0.'
);
}
if ($isXsdValidationEnabled && ! extension_loaded('dom')) {
throw new LogicException(sprintf(
throw new LogicException(
'XSD validation cannot be enabled because the DOM extension is missing.'
));
);
}
$this->isXsdValidationEnabled = $isXsdValidationEnabled;
@@ -378,30 +376,8 @@ class XmlDriver extends FileDriver
continue;
}
$mapping = [
'id' => true,
'fieldName' => (string) $idElement['name'],
];
if (isset($idElement['type'])) {
$mapping['type'] = (string) $idElement['type'];
}
if (isset($idElement['length'])) {
$mapping['length'] = (int) $idElement['length'];
}
if (isset($idElement['column'])) {
$mapping['columnName'] = (string) $idElement['column'];
}
if (isset($idElement['column-definition'])) {
$mapping['columnDefinition'] = (string) $idElement['column-definition'];
}
if (isset($idElement->options)) {
$mapping['options'] = $this->parseOptions($idElement->options->children());
}
$mapping = $this->columnToArray($idElement);
$mapping['id'] = true;
$metadata->mapField($mapping);
@@ -48,11 +48,11 @@ class YamlDriver extends FileDriver
);
if (! class_exists(Yaml::class)) {
throw new LogicException(sprintf(
throw new LogicException(
'The YAML metadata driver cannot be enabled because the "symfony/yaml" library'
. ' is not installed. Please run "composer require symfony/yaml" or choose a different'
. ' metadata driver.'
));
);
}
parent::__construct($locator, $fileExtension);
+2 -3
View File
@@ -24,7 +24,6 @@ use function apcu_enabled;
use function class_exists;
use function extension_loaded;
use function md5;
use function sprintf;
use function sys_get_temp_dir;
final class ORMSetup
@@ -72,11 +71,11 @@ final class ORMSetup
__METHOD__
);
if (! class_exists(AnnotationReader::class)) {
throw new LogicException(sprintf(
throw new LogicException(
'The annotation metadata driver cannot be enabled because the "doctrine/annotations" library'
. ' is not installed. Please run "composer require doctrine/annotations" or choose a different'
. ' metadata driver.'
));
);
}
$reader = new AnnotationReader();
+35 -25
View File
@@ -48,13 +48,12 @@ class <proxyShortClassName> extends \<className> implements \<baseProxyInterface
{
<useLazyGhostTrait>
/**
* @internal
*/
public bool $__isCloning = false;
public function __construct(?\Closure $initializer = null)
public function __construct(?\Closure $initializer = null, ?\Closure $cloner = null)
{
if ($cloner !== null) {
return;
}
self::createLazyGhost($initializer, <skippedProperties>, $this);
}
@@ -63,17 +62,6 @@ class <proxyShortClassName> extends \<className> implements \<baseProxyInterface
return isset($this->lazyObjectState) && $this->isLazyObjectInitialized();
}
public function __clone()
{
$this->__isCloning = true;
try {
$this->__doClone();
} finally {
$this->__isCloning = false;
}
}
public function __serialize(): array
{
<serializeImpl>
@@ -98,6 +86,9 @@ EOPHP;
*/
private $identifierFlattener;
/** @var ProxyDefinition[] */
private $definitions = [];
/**
* Initializes a new instance of the <tt>ProxyFactory</tt> class that is
* connected to the given <tt>EntityManager</tt>.
@@ -131,6 +122,26 @@ EOPHP;
$this->identifierFlattener = new IdentifierFlattener($this->uow, $em->getMetadataFactory());
}
/**
* {@inheritDoc}
*/
public function getProxy($className, array $identifier)
{
$proxy = parent::getProxy($className, $identifier);
if (! $this->em->getConfiguration()->isLazyGhostObjectEnabled()) {
return $proxy;
}
$initializer = $this->definitions[$className]->initializer;
$proxy->__construct(static function (Proxy $object) use ($initializer, $proxy): void {
$initializer($object, $proxy);
});
return $proxy;
}
/**
* {@inheritDoc}
*/
@@ -158,7 +169,7 @@ EOPHP;
$cloner = $this->createCloner($classMetadata, $entityPersister);
}
return new ProxyDefinition(
return $this->definitions[$className] = new ProxyDefinition(
ClassUtils::generateProxyClassName($className, $this->proxyNs),
$classMetadata->getIdentifierFieldNames(),
$classMetadata->getReflectionProperties(),
@@ -231,15 +242,15 @@ EOPHP;
/**
* Creates a closure capable of initializing a proxy
*
* @return Closure(Proxy):void
* @return Closure(Proxy, Proxy):void
*
* @throws EntityNotFoundException
*/
private function createLazyInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister): Closure
{
return function (Proxy $proxy) use ($entityPersister, $classMetadata): void {
$identifier = $classMetadata->getIdentifierValues($proxy);
$entity = $entityPersister->loadById($identifier, $proxy->__isCloning ? null : $proxy);
return function (Proxy $proxy, Proxy $original) use ($entityPersister, $classMetadata): void {
$identifier = $classMetadata->getIdentifierValues($original);
$entity = $entityPersister->loadById($identifier, $original);
if ($entity === null) {
throw EntityNotFoundException::fromClassNameAndIdentifier(
@@ -248,7 +259,7 @@ EOPHP;
);
}
if (! $proxy->__isCloning) {
if ($proxy === $original) {
return;
}
@@ -315,7 +326,6 @@ EOPHP;
isLazyObjectInitialized as private;
createLazyGhost as private;
resetLazyObject as private;
__clone as private __doClone;
}'), $code);
return $code;
@@ -323,7 +333,7 @@ EOPHP;
private function generateSkippedProperties(ClassMetadata $class): string
{
$skippedProperties = ['__isCloning' => true];
$skippedProperties = [];
$identifiers = array_flip($class->getIdentifierFieldNames());
$filter = ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE;
$reflector = $class->getReflectionClass();
@@ -53,10 +53,9 @@ abstract class AbstractSqlExecutor
/**
* Executes all sql statements.
*
* @param Connection $conn The database connection that is used to execute the queries.
* @psalm-param list<mixed>|array<string, mixed> $params The parameters.
* @psalm-param array<int, int|string|Type|null>|
* array<string, int|string|Type|null> $types The parameter types.
* @param Connection $conn The database connection that is used to execute the queries.
* @param list<mixed>|array<string, mixed> $params The parameters.
* @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types The parameter types.
*
* @return Result|int
*/
+2 -2
View File
@@ -2566,7 +2566,7 @@ class Parser
* EmptyCollectionComparisonExpression | CollectionMemberExpression |
* InstanceOfExpression
*
* @return AST\BetweenExpression|
* @return (AST\BetweenExpression|
* AST\CollectionMemberExpression|
* AST\ComparisonExpression|
* AST\EmptyCollectionComparisonExpression|
@@ -2574,7 +2574,7 @@ class Parser
* AST\InExpression|
* AST\InstanceOfExpression|
* AST\LikeExpression|
* AST\NullComparisonExpression
* AST\NullComparisonExpression)
*/
public function SimpleConditionalExpression()
{
+6 -1
View File
@@ -809,7 +809,12 @@ class QueryBuilder
*/
public function distinct($flag = true)
{
$this->dqlParts['distinct'] = (bool) $flag;
$flag = (bool) $flag;
if ($this->dqlParts['distinct'] !== $flag) {
$this->dqlParts['distinct'] = $flag;
$this->state = self::STATE_DIRTY;
}
return $this;
}
@@ -56,7 +56,5 @@ class AttachEntityListenersListener
$metadata->addEntityListener($listener['event'], $listener['class'], $listener['method']);
}
}
unset($this->entityListeners[$metadata->name]);
}
}
@@ -17,6 +17,7 @@ use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use function assert;
use function get_debug_type;
use function sprintf;
@@ -63,32 +64,46 @@ EOT
{
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();
$em = $this->getEntityManager($input);
$cache = $em->getConfiguration()->getQueryCache();
$cacheDriver = $em->getConfiguration()->getQueryCacheImpl();
$em = $this->getEntityManager($input);
$cache = $em->getConfiguration()->getQueryCache();
if (! $cacheDriver) {
throw new InvalidArgumentException('No Query cache driver is configured on given EntityManager.');
}
if ($cacheDriver instanceof ApcCache || $cache instanceof ApcuAdapter) {
if ($cache instanceof ApcuAdapter) {
throw new LogicException('Cannot clear APCu Cache from Console, it\'s shared in the Webserver memory and not accessible from the CLI.');
}
if ($cacheDriver instanceof XcacheCache) {
throw new LogicException('Cannot clear XCache Cache from Console, it\'s shared in the Webserver memory and not accessible from the CLI.');
}
$cacheDriver = null;
if (! $cache) {
$cacheDriver = $em->getConfiguration()->getQueryCacheImpl();
if (! ($cacheDriver instanceof ClearableCache)) {
throw new LogicException(sprintf(
'Can only clear cache when ClearableCache interface is implemented, %s does not implement.',
get_debug_type($cacheDriver)
));
if (! $cacheDriver) {
throw new InvalidArgumentException('No Query cache driver is configured on given EntityManager.');
}
if ($cacheDriver instanceof ApcCache) {
throw new LogicException('Cannot clear APCu Cache from Console, it\'s shared in the Webserver memory and not accessible from the CLI.');
}
if ($cacheDriver instanceof XcacheCache) {
throw new LogicException('Cannot clear XCache Cache from Console, it\'s shared in the Webserver memory and not accessible from the CLI.');
}
if (! ($cacheDriver instanceof ClearableCache)) {
throw new LogicException(sprintf(
'Can only clear cache when ClearableCache interface is implemented, %s does not implement.',
get_debug_type($cacheDriver)
));
}
}
$ui->comment('Clearing <info>all</info> Query cache entries');
$result = $cache ? $cache->clear() : $cacheDriver->deleteAll();
if ($cache) {
$result = $cache->clear();
} else {
assert($cacheDriver !== null);
$result = $cacheDriver->deleteAll();
}
$message = $result ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.';
if ($input->getOption('flush') === true && ! $cache) {
+2 -2
View File
@@ -821,8 +821,8 @@ class SchemaTool
return [];
}
$options = array_intersect_key($mappingOptions, array_flip(self::KNOWN_COLUMN_OPTIONS));
$options['customSchemaOptions'] = array_diff_key($mappingOptions, $options);
$options = array_intersect_key($mappingOptions, array_flip(self::KNOWN_COLUMN_OPTIONS));
$options['platformOptions'] = array_diff_key($mappingOptions, $options);
return $options;
}
+26 -8
View File
@@ -1124,6 +1124,20 @@ class UnitOfWork implements PropertyChangedListener
foreach ($actualData as $propName => $actualValue) {
$orgValue = $originalData[$propName] ?? null;
if (isset($class->fieldMappings[$propName]['enumType'])) {
if (is_array($orgValue)) {
foreach ($orgValue as $id => $val) {
if ($val instanceof BackedEnum) {
$orgValue[$id] = $val->value;
}
}
} else {
if ($orgValue instanceof BackedEnum) {
$orgValue = $orgValue->value;
}
}
}
if ($orgValue !== $actualValue) {
$changeSet[$propName] = [$orgValue, $actualValue];
}
@@ -3669,14 +3683,18 @@ class UnitOfWork implements PropertyChangedListener
$targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
$relatedId = $targetClass->getIdentifierValues($other);
if ($targetClass->subClasses) {
$other = $this->em->find($targetClass->name, $relatedId);
} else {
$other = $this->em->getProxyFactory()->getProxy(
$assoc2['targetEntity'],
$relatedId
);
$this->registerManaged($other, $relatedId, []);
$other = $this->tryGetById($relatedId, $targetClass->name);
if (! $other) {
if ($targetClass->subClasses) {
$other = $this->em->find($targetClass->name, $relatedId);
} else {
$other = $this->em->getProxyFactory()->getProxy(
$assoc2['targetEntity'],
$relatedId
);
$this->registerManaged($other, $relatedId, []);
}
}
}
+1 -16
View File
@@ -286,7 +286,7 @@ parameters:
path: lib/Doctrine/ORM/Proxy/ProxyFactory.php
-
message: "#^Access to an undefined property Doctrine\\\\Persistence\\\\Proxy\\:\\:\\$__isCloning\\.$#"
message: "#^Call to an undefined method Doctrine\\\\Common\\\\Proxy\\\\Proxy\\:\\:__construct\\(\\)\\.$#"
count: 1
path: lib/Doctrine/ORM/Proxy/ProxyFactory.php
@@ -395,21 +395,6 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Query/Parser.php
-
message: """
#^PHPDoc tag @return has invalid value \\(AST\\\\BetweenExpression\\|
AST\\\\CollectionMemberExpression\\|
AST\\\\ComparisonExpression\\|
AST\\\\EmptyCollectionComparisonExpression\\|
AST\\\\ExistsExpression\\|
AST\\\\InExpression\\|
AST\\\\InstanceOfExpression\\|
AST\\\\LikeExpression\\|
AST\\\\NullComparisonExpression\\)\\: Unexpected token "\\\\n \\* ", expected type at offset 344$#
"""
count: 1
path: lib/Doctrine/ORM/Query/Parser.php
-
message: "#^Parameter \\#2 \\$stringPattern of class Doctrine\\\\ORM\\\\Query\\\\AST\\\\LikeExpression constructor expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\FunctionNode\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\InputParameter\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\Literal\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\PathExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#"
count: 1
+7 -19
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="5.12.0@f90118cdeacd0088e7215e64c0c99ceca819e176">
<files psalm-version="5.13.1@086b94371304750d1c673315321a55d15fc59015">
<file src="lib/Doctrine/ORM/AbstractQuery.php">
<DeprecatedClass>
<code>IterableResult</code>
@@ -213,11 +213,6 @@
<code>CacheProvider</code>
</MoreSpecificReturnType>
</file>
<file src="lib/Doctrine/ORM/Cache/Region/FileLockRegion.php">
<ArgumentTypeCoercion>
<code><![CDATA[sprintf('%s/*.%s', $this->directory, self::LOCK_EXTENSION)]]></code>
</ArgumentTypeCoercion>
</file>
<file src="lib/Doctrine/ORM/Cache/RegionsConfiguration.php">
<RedundantCastGivenDocblockType>
<code>(int) $defaultLifetime</code>
@@ -1397,6 +1392,11 @@
<code>$classMetadata</code>
<code>$classMetadata</code>
</ArgumentTypeCoercion>
<DirectConstructorCall>
<code><![CDATA[$proxy->__construct(static function (Proxy $object) use ($initializer, $proxy): void {
$initializer($object, $proxy);
})]]></code>
</DirectConstructorCall>
<InvalidArgument>
<code><![CDATA[$classMetadata->getReflectionProperties()]]></code>
<code><![CDATA[$em->getMetadataFactory()]]></code>
@@ -1405,7 +1405,6 @@
<NoInterfaceProperties>
<code><![CDATA[$metadata->isEmbeddedClass]]></code>
<code><![CDATA[$metadata->isMappedSuperclass]]></code>
<code><![CDATA[$proxy->__isCloning]]></code>
</NoInterfaceProperties>
<PossiblyNullPropertyFetch>
<code><![CDATA[$property->name]]></code>
@@ -1416,6 +1415,7 @@
<code>setAccessible</code>
</PossiblyNullReference>
<UndefinedInterfaceMethod>
<code>__construct</code>
<code>__wakeup</code>
</UndefinedInterfaceMethod>
</file>
@@ -2013,19 +2013,10 @@
<code>$factors[0]</code>
<code>$primary</code>
<code>$terms[0]</code>
<code><![CDATA[$this->CollectionMemberExpression()]]></code>
<code><![CDATA[$this->ComparisonExpression()]]></code>
<code><![CDATA[$this->EmptyCollectionComparisonExpression()]]></code>
<code><![CDATA[$this->ExistsExpression()]]></code>
<code><![CDATA[$this->InExpression()]]></code>
<code><![CDATA[$this->InstanceOfExpression()]]></code>
<code><![CDATA[$this->LikeExpression()]]></code>
<code><![CDATA[$this->NullComparisonExpression()]]></code>
</InvalidReturnStatement>
<InvalidReturnType>
<code>AST\ArithmeticFactor</code>
<code>AST\ArithmeticTerm</code>
<code>AST\BetweenExpression|</code>
<code>AST\SimpleArithmeticExpression|AST\ArithmeticTerm</code>
</InvalidReturnType>
<InvalidStringClass>
@@ -2456,9 +2447,6 @@
</MissingTemplateParam>
</file>
<file src="lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php">
<ArgumentTypeCoercion>
<code><![CDATA[$path . '/*.yml']]></code>
</ArgumentTypeCoercion>
<PossiblyUndefinedArrayOffset>
<code><![CDATA[$column['type']]]></code>
</PossiblyUndefinedArrayOffset>
@@ -260,6 +260,78 @@ class EnumTest extends OrmFunctionalTestCase
self::assertEqualsCanonicalizing([Unit::Gram, Unit::Meter], $result[0]->supportedUnits);
}
public function testEnumSingleEntityChangeSetsSimpleObjectHydrator(): void
{
$this->setUpEntitySchema([Card::class]);
$card = new Card();
$card->suit = Suit::Clubs;
$this->_em->persist($card);
$this->_em->flush();
$this->_em->clear();
$result = $this->_em->find(Card::class, $card->id);
$this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet(
$this->_em->getClassMetadata(Card::class),
$result
);
self::assertFalse($this->_em->getUnitOfWork()->isScheduledForUpdate($result));
$result->suit = Suit::Hearts;
$this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet(
$this->_em->getClassMetadata(Card::class),
$result
);
self::assertTrue($this->_em->getUnitOfWork()->isScheduledForUpdate($result));
}
public function testEnumSingleEntityChangeSetsObjectHydrator(): void
{
$this->setUpEntitySchema([Card::class]);
$card = new Card();
$card->suit = Suit::Clubs;
$this->_em->persist($card);
$this->_em->flush();
$this->_em->clear();
$result = $this->_em->find(Card::class, $card->id);
$this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet(
$this->_em->getClassMetadata(Card::class),
$result
);
self::assertFalse($this->_em->getUnitOfWork()->isScheduledForUpdate($result));
}
public function testEnumArraySingleEntityChangeSets(): void
{
$this->setUpEntitySchema([Scale::class]);
$scale = new Scale();
$scale->supportedUnits = [Unit::Gram];
$this->_em->persist($scale);
$this->_em->flush();
$this->_em->clear();
$result = $this->_em->find(Scale::class, $scale->id);
$this->_em->getUnitOfWork()->recomputeSingleEntityChangeSet(
$this->_em->getClassMetadata(Scale::class),
$result
);
self::assertFalse($this->_em->getUnitOfWork()->isScheduledForUpdate($result));
}
public function testEnumChangeSetsSimpleObjectHydrator(): void
{
$this->setUpEntitySchema([Card::class]);
@@ -109,8 +109,7 @@ class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociati
/** @psalm-return list<ECommerceProduct> */
protected function findProducts(): array
{
$query = $this->_em->createQuery('SELECT p, c FROM Doctrine\Tests\Models\ECommerce\ECommerceProduct p LEFT JOIN p.categories c ORDER BY p.id, c.id');
//$query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
$query = $this->_em->createQuery('SELECT p, c FROM Doctrine\Tests\Models\ECommerce\ECommerceProduct p LEFT JOIN p.categories c ORDER BY p.id, c.id');
$result = $query->getResult();
self::assertCount(2, $result);
$cats1 = $result[0]->getCategories();
@@ -126,8 +125,7 @@ class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociati
/** @psalm-return list<ECommerceCategory> */
protected function findCategories(): array
{
$query = $this->_em->createQuery('SELECT c, p FROM Doctrine\Tests\Models\ECommerce\ECommerceCategory c LEFT JOIN c.products p ORDER BY c.id, p.id');
//$query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
$query = $this->_em->createQuery('SELECT c, p FROM Doctrine\Tests\Models\ECommerce\ECommerceCategory c LEFT JOIN c.products p ORDER BY c.id, p.id');
$result = $query->getResult();
self::assertCount(2, $result);
self::assertInstanceOf(ECommerceCategory::class, $result[0]);
@@ -13,6 +13,7 @@ use Generator;
use ReflectionMethod;
use function file_get_contents;
use function rtrim;
use function serialize;
use function unserialize;
@@ -56,8 +57,8 @@ class ParserResultSerializationTest extends OrmFunctionalTestCase
/** @return Generator<string, array{string}> */
public static function provideSerializedSingleSelectResults(): Generator
{
yield '2.14.3' => [file_get_contents(__DIR__ . '/ParserResults/single_select_2_14_3.txt')];
yield '2.15.0' => [file_get_contents(__DIR__ . '/ParserResults/single_select_2_15_0.txt')];
yield '2.14.3' => [rtrim(file_get_contents(__DIR__ . '/ParserResults/single_select_2_14_3.txt'), "\n")];
yield '2.15.0' => [rtrim(file_get_contents(__DIR__ . '/ParserResults/single_select_2_15_0.txt'), "\n")];
}
private static function parseQuery(Query $query): ParserResult
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional;
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
@@ -93,7 +93,7 @@ class GH10747Article
{
/**
* @Id
* @Column(type="Doctrine\Tests\ORM\Functional\GH10747CustomIdObjectHashType")
* @Column(type="Doctrine\Tests\ORM\Functional\Ticket\GH10747CustomIdObjectHashType")
* @var CustomIdObject
*/
public $id;
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional;
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
@@ -103,7 +103,7 @@ class GH5804Article
* @Id
* @Column(type="GH5804Type", length=255)
* @GeneratedValue(strategy="CUSTOM")
* @CustomIdGenerator(class=\Doctrine\Tests\ORM\Functional\Ticket\GH5804Generator::class)
* @CustomIdGenerator(class=GH5804Generator::class)
*/
public $id;
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional;
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type as DBALType;
@@ -106,7 +106,7 @@ abstract class GH5988CustomIdObjectTypeParent
{
/**
* @Id
* @Column(type="Doctrine\Tests\ORM\Functional\GH5988CustomIdObjectHashType", length=255)
* @Column(type="Doctrine\Tests\ORM\Functional\Ticket\GH5988CustomIdObjectHashType", length=255)
* @var CustomIdObject
*/
public $id;
@@ -15,7 +15,6 @@ use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\InheritanceType;
use Doctrine\ORM\Mapping\ManyToOne;
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\Tests\Mocks\ArrayResultFactory;
use Doctrine\Tests\OrmFunctionalTestCase;
@@ -79,7 +78,7 @@ final class GH6362Test extends OrmFunctionalTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertInstanceOf(GH6362Start::class, $result[0]['base']);
self::assertInstanceOf(GH6362Child::class, $result[1][0]);
@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\UnitOfWork;
use Doctrine\Tests\Models\CMS\CmsArticle;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\OrmFunctionalTestCase;
use ReflectionClass;
use function spl_object_id;
class GH7407Test extends OrmFunctionalTestCase
{
protected function setUp(): void
{
$this->useModelSet('cms');
parent::setUp();
}
public function testMergingEntitiesDoesNotCreateUnmanagedProxyReferences(): void
{
// 1. Create an article with a user; persist, flush and clear the entity manager
$user = new CmsUser();
$user->username = 'Test';
$user->name = 'Test';
$this->_em->persist($user);
$article = new CmsArticle();
$article->topic = 'Test';
$article->text = 'Test';
$article->setAuthor($user);
$this->_em->persist($article);
$this->_em->flush();
$this->_em->clear();
// 2. Merge the user object back in:
// We get a new (different) entity object that represents the user instance
// which is now (through this object instance) managed by the EM/UoW
$mergedUser = $this->_em->merge($user);
$mergedUserOid = spl_object_id($mergedUser);
// 3. Merge the article object back in,
// the returned entity object is the article instance as it is managed by the EM/UoW
$mergedArticle = $this->_em->merge($article);
$mergedArticleOid = spl_object_id($mergedArticle);
self::assertSame($mergedUser, $mergedArticle->user, 'The $mergedArticle\'s #user property should hold the $mergedUser we obtained previously, since that\'s the only legitimate object instance representing the user from the UoW\'s point of view.');
// Inspect internal UoW state
$uow = $this->_em->getUnitOfWork();
$entityIdentifiers = $this->grabProperty('entityIdentifiers', $uow);
$identityMap = $this->grabProperty('identityMap', $uow);
$entityStates = $this->grabProperty('entityStates', $uow);
self::assertCount(2, $entityIdentifiers, 'UoW#entityIdentifiers contains exactly two OID -> ID value mapping entries one for the article, one for the user object');
self::assertArrayHasKey($mergedArticleOid, $entityIdentifiers);
self::assertArrayHasKey($mergedUserOid, $entityIdentifiers);
self::assertSame([
$mergedUserOid => UnitOfWork::STATE_MANAGED,
$mergedArticleOid => UnitOfWork::STATE_MANAGED,
], $entityStates, 'UoW#entityStates contains two OID -> state entries, one for the article, one for the user object');
self::assertCount(2, $entityIdentifiers);
self::assertArrayHasKey($mergedArticleOid, $entityIdentifiers);
self::assertArrayHasKey($mergedUserOid, $entityIdentifiers);
self::assertSame([
CmsUser::class => [$user->id => $mergedUser],
CmsArticle::class => [$article->id => $mergedArticle],
], $identityMap, 'The identity map contains exactly two objects, the article and the user.');
}
/** @return mixed */
private function grabProperty(string $name, UnitOfWork $uow)
{
$reflection = new ReflectionClass($uow);
$property = $reflection->getProperty($name);
$property->setAccessible(true);
return $property->getValue($uow);
}
}
@@ -8,7 +8,6 @@ use Doctrine\ORM\Internal\Hydration\ObjectHydrator;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\Tests\Mocks\ArrayResultFactory;
use Doctrine\Tests\Models\CMS\CmsAddress;
@@ -65,7 +64,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id,name}
* SELECT u
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*/
public function testSimpleEntityQuery(): void
@@ -89,7 +88,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -104,7 +103,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id,name} AS user
* SELECT u AS user
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*/
public function testSimpleEntityQueryWithAliasedUserEntity(): void
@@ -128,7 +127,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -146,7 +145,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, name}, PARTIAL a.{id, topic}
* SELECT u, a
* FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a
*/
public function testSimpleMultipleRootEntityQuery(): void
@@ -177,7 +176,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(4, count($result));
@@ -200,7 +199,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, name} AS user, PARTIAL a.{id, topic}
* SELECT u AS user, a
* FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a
*/
public function testSimpleMultipleRootEntityQueryWithAliasedUserEntity(): void
@@ -231,7 +230,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(4, count($result));
@@ -261,7 +260,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, name}, PARTIAL a.{id, topic} AS article
* SELECT u, a AS article
* FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a
*/
public function testSimpleMultipleRootEntityQueryWithAliasedArticleEntity(): void
@@ -292,7 +291,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(4, count($result));
@@ -322,7 +321,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, name} AS user, PARTIAL a.{id, topic} AS article
* SELECT u AS user, a AS article
* FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a
*/
public function testSimpleMultipleRootEntityQueryWithAliasedEntities(): void
@@ -353,7 +352,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(4, count($result));
@@ -383,7 +382,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, status}, COUNT(p.phonenumber) numPhones
* SELECT u, COUNT(p.phonenumber) numPhones
* FROM User u
* JOIN u.phonenumbers p
* GROUP BY u.id
@@ -415,7 +414,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -433,7 +432,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, status}, PARTIAL p.{phonenumber}, UPPER(u.name) nameUpper
* SELECT u, p, UPPER(u.name) nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* JOIN u.phonenumbers p
*
@@ -479,7 +478,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -559,7 +558,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -673,7 +672,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -803,7 +802,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -916,7 +915,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -936,7 +935,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id,name}
* SELECT u
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @group DDC-644
@@ -959,7 +958,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(1, count($result));
self::assertInstanceOf(CmsUser::class, $result[0]);
@@ -992,7 +991,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -1104,7 +1103,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, status}, PARTIAL a.{id, topic}, PARTIAL c.{id, topic}
* SELECT u, a, c
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* LEFT JOIN u.articles a
* LEFT JOIN a.comments c
@@ -1155,7 +1154,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -1167,7 +1166,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, status} AS user, PARTIAL a.{id, topic}, PARTIAL c.{id, topic}
* SELECT u AS user, a, c
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* LEFT JOIN u.articles a
* LEFT JOIN a.comments c
@@ -1218,7 +1217,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -1233,7 +1232,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, name}
* SELECT u
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*/
public function testResultIteration(): void
@@ -1259,8 +1258,7 @@ class ObjectHydratorTest extends HydrationTestCase
$iterableResult = $hydrator->iterate(
ArrayResultFactory::createFromArray($resultSet),
$rsm,
[Query::HINT_FORCE_PARTIAL_LOAD => true]
$rsm
);
$rowNum = 0;
@@ -1283,8 +1281,7 @@ class ObjectHydratorTest extends HydrationTestCase
$iterableResult = $hydrator->toIterable(
ArrayResultFactory::createFromArray($resultSet),
$rsm,
[Query::HINT_FORCE_PARTIAL_LOAD => true]
$rsm
);
$rowNum = 0;
@@ -1308,7 +1305,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, name}
* SELECT u
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*/
public function testResultIterationWithAliasedUserEntity(): void
@@ -1334,8 +1331,7 @@ class ObjectHydratorTest extends HydrationTestCase
$rowNum = 0;
$iterableResult = $hydrator->iterate(
ArrayResultFactory::createFromArray($resultSet),
$rsm,
[Query::HINT_FORCE_PARTIAL_LOAD => true]
$rsm
);
while (($row = $iterableResult->next()) !== false) {
@@ -1360,8 +1356,7 @@ class ObjectHydratorTest extends HydrationTestCase
$rowNum = 0;
$iterableResult = $hydrator->toIterable(
ArrayResultFactory::createFromArray($resultSet),
$rsm,
[Query::HINT_FORCE_PARTIAL_LOAD => true]
$rsm
);
foreach ($iterableResult as $row) {
@@ -1388,7 +1383,7 @@ class ObjectHydratorTest extends HydrationTestCase
/**
* Checks if multiple joined multiple-valued collections is hydrated correctly.
*
* SELECT PARTIAL u.{id, status}, PARTIAL g.{id, name}, PARTIAL p.{phonenumber}
* SELECT u, g, p
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @group DDC-809
@@ -1495,7 +1490,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -1511,7 +1506,7 @@ class ObjectHydratorTest extends HydrationTestCase
/**
* Checks if multiple joined multiple-valued collections is hydrated correctly.
*
* SELECT PARTIAL u.{id, status} As user, PARTIAL g.{id, name}, PARTIAL p.{phonenumber}
* SELECT u As user, g, p
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @group DDC-809
@@ -1618,7 +1613,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -1635,7 +1630,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, status}, UPPER(u.name) as nameUpper
* SELECT u, UPPER(u.name) as nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @group DDC-1358
@@ -1676,7 +1671,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(4, count($result), 'Should hydrate four results.');
@@ -1693,7 +1688,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, status}, PARTIAL p.{phonenumber}, UPPER(u.name) AS nameUpper
* SELECT u, p, UPPER(u.name) AS nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* LEFT JOIN u.phonenumbers u
*
@@ -1746,7 +1741,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -1755,7 +1750,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, status}, PARTIAL a.{id, city}, UPPER(u.name) AS nameUpper
* SELECT u, a, UPPER(u.name) AS nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* JOIN u.address a
*
@@ -1800,7 +1795,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -1809,7 +1804,7 @@ class ObjectHydratorTest extends HydrationTestCase
}
/**
* SELECT PARTIAL u.{id, status}, UPPER(u.name) AS nameUpper
* SELECT u, UPPER(u.name) AS nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* INDEX BY u.id
*
@@ -1842,7 +1837,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(2, count($result));
@@ -1876,7 +1871,7 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new ObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals(
[
@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Doctrine\Tests\ORM\Proxy;
use Doctrine\Common\EventManager;
use Doctrine\Common\Proxy\Proxy as CommonProxy;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\ORM\EntityNotFoundException;
@@ -227,21 +226,12 @@ class ProxyFactoryTest extends OrmTestCase
->expects(self::atLeastOnce())
->method('loadById');
if ($proxy instanceof CommonProxy) {
$loadByIdMock->willReturn($companyEmployee);
$loadByIdMock->willReturn($companyEmployee);
$persister
->expects(self::atLeastOnce())
->method('getClassMetadata')
->willReturn($classMetaData);
} else {
$loadByIdMock->willReturnCallback(static function (array $id, CompanyEmployee $companyEmployee) {
$companyEmployee->setSalary(1000); // A property on the CompanyEmployee
$companyEmployee->setName('Bob'); // A property on the parent class, CompanyPerson
return $companyEmployee;
});
}
$persister
->expects(self::atLeastOnce())
->method('getClassMetadata')
->willReturn($classMetaData);
$cloned = clone $proxy;
assert($cloned instanceof CompanyEmployee);
@@ -42,7 +42,6 @@ class LanguageRecognitionTest extends OrmTestCase
public function parseDql(string $dql, array $hints = []): ParserResult
{
$query = $this->entityManager->createQuery($dql);
$query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
$query->setDQL($dql);
foreach ($hints as $key => $value) {
@@ -1087,6 +1087,18 @@ class QueryBuilderTest extends OrmTestCase
self::assertEquals('SELECT DISTINCT u FROM Doctrine\Tests\Models\CMS\CmsUser u', $qb->getDQL());
}
public function testDistinctUpdatesState(): void
{
$qb = $this->entityManager->createQueryBuilder()
->select('u')
->from(CmsUser::class, 'u');
$qb->getDQL();
$qb->distinct();
self::assertEquals('SELECT DISTINCT u FROM Doctrine\Tests\Models\CMS\CmsUser u', $qb->getDQL());
}
/** @group DDC-2192 */
public function testWhereAppend(): void
{
@@ -56,6 +56,17 @@ class AttachEntityListenersListenerTest extends OrmTestCase
self::assertCount(1, $metadata->entityListeners['postLoad']);
self::assertEquals('postLoadHandler', $metadata->entityListeners['postLoad'][0]['method']);
self::assertEquals(AttachEntityListenersListenerTestListener::class, $metadata->entityListeners['postLoad'][0]['class']);
// Can reattach entity listeners even class metadata factory recreated.
$factory2 = new ClassMetadataFactory();
$factory2->setEntityManager($this->em);
$metadata2 = $factory2->getMetadataFor(AttachEntityListenersListenerTestFooEntity::class);
self::assertArrayHasKey('postLoad', $metadata2->entityListeners);
self::assertCount(1, $metadata2->entityListeners['postLoad']);
self::assertEquals('postLoadHandler', $metadata2->entityListeners['postLoad'][0]['method']);
self::assertEquals(AttachEntityListenersListenerTestListener::class, $metadata2->entityListeners['postLoad'][0]['class']);
}
public function testAttachToExistingEntityListeners(): void
@@ -15,6 +15,8 @@ use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Mapping\Driver\XmlDriver;
use Doctrine\ORM\Mapping\Driver\YamlDriver;
use Doctrine\ORM\Mapping\PostPersist;
use Doctrine\ORM\Mapping\PrePersist;
use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
use Doctrine\ORM\Tools\EntityGenerator;
use Doctrine\ORM\Tools\Export\ClassMetadataExporter;
@@ -399,26 +401,26 @@ class Group
}
class UserListener
{
/** @\Doctrine\ORM\Mapping\PrePersist */
/** @PrePersist */
public function customPrePersist(): void
{
}
/** @\Doctrine\ORM\Mapping\PostPersist */
/** @PostPersist */
public function customPostPersist(): void
{
}
}
class GroupListener
{
/** @\Doctrine\ORM\Mapping\PrePersist */
/** @PrePersist */
public function prePersist(): void
{
}
}
class AddressListener
{
/** @\Doctrine\ORM\Mapping\PostPersist */
/** @PostPersist */
public function customPostPersist(): void
{
}
@@ -77,7 +77,7 @@ class SchemaToolTest extends OrmTestCase
);
$table = $schema->getTable('TestEntityWithAnnotationOptionsAttribute');
foreach ([$table->getOptions(), $table->getColumn('test')->getCustomSchemaOptions()] as $options) {
foreach ([$table->getOptions(), $table->getColumn('test')->getPlatformOptions()] as $options) {
self::assertArrayHasKey('foo', $options);
self::assertSame('bar', $options['foo']);
self::assertArrayHasKey('baz', $options);
@@ -139,7 +139,7 @@ class SchemaToolTest extends OrmTestCase
self::assertEquals(
['collation' => 'latin1_bin', 'foo' => 'bar'],
$tableBoard->getColumn('category_id')->getCustomSchemaOptions()
$tableBoard->getColumn('category_id')->getPlatformOptions()
);
}
@@ -192,13 +192,13 @@ class SchemaToolTest extends OrmTestCase
$em = $this->getTestEntityManager();
$schemaTool = new SchemaTool($em);
$customSchemaOptions = $schemaTool->getSchemaFromMetadata([$em->getClassMetadata(Card::class)])
$platformOptions = $schemaTool->getSchemaFromMetadata([$em->getClassMetadata(Card::class)])
->getTable('Card')
->getColumn('suit')
->getCustomSchemaOptions();
->getPlatformOptions();
self::assertArrayHasKey('enumType', $customSchemaOptions);
self::assertSame(Suit::class, $customSchemaOptions['enumType']);
self::assertArrayHasKey('enumType', $platformOptions);
self::assertSame(Suit::class, $platformOptions['enumType']);
}
/** @group DDC-3671 */
@@ -384,7 +384,8 @@ class DDC1649Two
{
/**
* @var DDC1649One
* @Id @ManyToOne(targetEntity="DDC1649One")
* @Id
* @ManyToOne(targetEntity="DDC1649One")
*/
public $one;
}