Compare commits

...

21 Commits

Author SHA1 Message Date
Grégoire Paris 81d472f6f9 Merge pull request #9139 from greg0ire/upgrade-workflows
Upgrade workflows to 1.1.1
2021-10-21 19:57:02 +02:00
Grégoire Paris d458968cee Upgrade workflows to 1.1.1
That version fixes a bug with the release workflow. Releasing is not
possible unless we do that upgrade.
2021-10-21 19:55:57 +02:00
Christophe Coevoet 5eb01da0a0 Fix the upgrade guide for 2.8 changes (#9138) 2021-10-21 16:44:42 +02:00
Grégoire Paris 5aaf361139 Merge pull request #9136 from greg0ire/revert-bc-break
Revert "Removing all the occurence of any"
2021-10-21 08:49:18 +02:00
Alexander M. Turek 6a8dcbc392 Regenerate Psalm baseline (#9135) 2021-10-20 23:30:57 +02:00
Grégoire Paris 12babcc1c2 Revert "Removing all the occurence of any"
This reverts commit 84afd6c937, because it
is a BC-break that seems to affect more people than we originally
thought it would.
2021-10-20 23:12:01 +02:00
Thomas Landauer 416aa1d2d7 Explaining the two major ways to register an event v2 (#9128)
Co-authored-by: Javier Spagnoletti <phansys@gmail.com>
2021-10-16 11:45:10 +02:00
Grégoire Paris 8e16bb4ddc Merge pull request #9126 from greg0ire/explicitly-pass-secrets
Explicitly pass secrets
2021-10-14 23:45:30 +02:00
Grégoire Paris e1dee439bb Explicitly pass secrets
Secrets are sensitive and not passed implicitly.
2021-10-14 23:35:29 +02:00
Thomas Landauer e313d012ae Overview table for events (#9039)
* Overview table for events

Better late than never - finally delivering what I announced at https://github.com/doctrine/orm/pull/8435#issuecomment-769940427 :-)

* Update events.rst

* Update events.rst

* Adding "Lifecycle Callback" column

* Update events.rst
2021-10-14 10:58:07 +02:00
Javier Spagnoletti dede619b9e Add "@method" annotation for wrapInTransaction() method at EntityManagerInterface (#9091) 2021-10-13 20:18:50 +02:00
Grégoire Paris 142cfb39fc Merge pull request #9114 from greg0ire/try-out-reusable-workflows
Directly reference upstream CS workflow
2021-10-11 21:00:41 +02:00
Alexander M. Turek 53d41a456a PHP CodeSniffer 3.6.1 (#9115) 2021-10-11 12:05:46 +02:00
Grégoire Paris 95b34ca940 Directly reference some upstream workflows 2021-10-10 21:08:57 +02:00
Paul Capron 3eaf76eebd Fix typo & minor issues in dql-custom-walkers.rst (#9113) 2021-10-09 23:29:38 +02:00
Grégoire Paris 5c12d36be3 Merge pull request #9107 from BackEndTea/patch-1
Remove the twitter #doctrine2 hashtag refference
2021-10-07 22:35:44 +02:00
Grégoire Paris 1ee68eb318 Merge pull request #9098 from ajgarlag/bugfix-indexed-iterable
Honor INDEX BY construct in Query::toIterable
2021-10-07 22:34:54 +02:00
orklah 705c7f0a4b [Psalm] always true/false conditions (#9108) 2021-10-07 20:21:58 +02:00
Gert de Pagter 8c5e49efc0 Remove the twitter #doctrine2 hashtag refference
Looking at twitter, the hashtag its hardly used. There was 1 question posted in the last year, and it went unanswered.

The `2` part has mostly been dropped everywhere, and orm is now just refered to doctrine orm instead of doctrine2
2021-10-07 16:12:06 +02:00
Antonio J. García Lagar 483e09cf1c Fix Query::toIterable to honor INDEX BY construct 2021-10-07 13:02:22 +02:00
Benjamin Morel bbb68d0072 Fix SchemaValidator with abstract child class in discriminator map (#9096) 2021-10-06 22:35:51 +02:00
18 changed files with 249 additions and 120 deletions
+3 -27
View File
@@ -10,30 +10,6 @@ on:
jobs:
coding-standards:
name: "Coding Standards"
runs-on: "ubuntu-20.04"
strategy:
matrix:
php-version:
- "7.4"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
tools: "cs2pr"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
with:
dependency-versions: "highest"
# https://github.com/doctrine/.github/issues/3
- name: "Run PHP_CodeSniffer"
run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"
uses: "doctrine/.github/.github/workflows/coding-standards.yml@1.1.1"
with:
php-version: "7.4"
@@ -7,40 +7,10 @@ on:
jobs:
release:
name: "Git tag, release & create merge-up PR"
runs-on: "ubuntu-20.04"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
- name: "Release"
uses: "laminas/automatic-releases@v1"
with:
command-name: "laminas:automatic-releases:release"
env:
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
"SHELL_VERBOSITY": "3"
- name: "Create Merge-Up Pull Request"
uses: "laminas/automatic-releases@v1"
with:
command-name: "laminas:automatic-releases:create-merge-up-pull-request"
env:
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
- name: "Create new milestones"
uses: "laminas/automatic-releases@v1"
with:
command-name: "laminas:automatic-releases:create-milestones"
env:
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@1.1.1"
secrets:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}
ORGANIZATION_ADMIN_TOKEN: ${{ secrets.ORGANIZATION_ADMIN_TOKEN }}
SIGNING_SECRET_KEY: ${{ secrets.SIGNING_SECRET_KEY }}
+3 -3
View File
@@ -108,10 +108,10 @@ now always cleared regardless of the cache adapter being used.
Method `Doctrine\ORM\UnitOfWork#commit()` can throw an OptimisticLockException when a commit silently fails and returns false
since `Doctrine\DBAL\Connection#commit()` signature changed from returning void to boolean
## Deprecated: `Doctrine\ORM\AbstractQuery#iterator()`
## Deprecated: `Doctrine\ORM\AbstractQuery#iterate()`
The method `Doctrine\ORM\AbstractQuery#iterator()` is deprecated in favor of `Doctrine\ORM\AbstractQuery#toIterable()`.
Note that `toIterable()` yields results of the query, unlike `iterator()` which yielded each result wrapped into an array.
The method `Doctrine\ORM\AbstractQuery#iterate()` is deprecated in favor of `Doctrine\ORM\AbstractQuery#toIterable()`.
Note that `toIterable()` yields results of the query, unlike `iterate()` which yielded each result wrapped into an array.
# Upgrade to 2.7
+1 -1
View File
@@ -41,7 +41,7 @@
"phpbench/phpbench": "^0.16.10 || ^1.0",
"phpstan/phpstan": "0.12.99",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
"squizlabs/php_codesniffer": "3.6.0",
"squizlabs/php_codesniffer": "3.6.1",
"symfony/cache": "^4.4 || ^5.2",
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
"vimeo/psalm": "4.10.0"
+5 -5
View File
@@ -33,8 +33,8 @@ the DQL parser:
is only ever one of them. We implemented the default SqlWalker
implementation for it.
- A tree walker. There can be many tree walkers, they cannot
generate the sql, however they can modify the AST before its
rendered to sql.
generate the SQL, however they can modify the AST before its
rendered to SQL.
Now this is all awfully technical, so let me come to some use-cases
fast to keep you motivated. Using walker implementation you can for
@@ -50,7 +50,7 @@ example:
- Modify the Output walker to pretty print the SQL for debugging
purposes.
In this cookbook-entry I will show examples on the first two
In this cookbook-entry I will show examples of the first two
points. There are probably much more use-cases.
Generic count query for pagination
@@ -64,7 +64,7 @@ like:
SELECT p, c, a FROM BlogPost p JOIN p.category c JOIN p.author a WHERE ...
Now in this query the blog post is the root entity, meaning its the
Now in this query the blog post is the root entity, meaning it's the
one that is hydrated directly from the query and returned as an
array of blog posts. In contrast the comment and author are loaded
for deeper use in the object tree.
@@ -79,7 +79,7 @@ query for pagination would look like:
SELECT count(DISTINCT p.id) FROM BlogPost p JOIN p.category c JOIN p.author a WHERE ...
Now you could go and write each of these queries by hand, or you
can use a tree walker to modify the AST for you. Lets see how the
can use a tree walker to modify the AST for you. Let's see how the
API would look for this use-case:
.. code-block:: php
-1
View File
@@ -16,7 +16,6 @@ Doctrine ORM don't panic. You can get help from different sources:
- The `Doctrine Mailing List <https://groups.google.com/group/doctrine-user>`_
- Slack chat room `#orm <https://www.doctrine-project.org/slack>`_
- Report a bug on `GitHub <https://github.com/doctrine/orm/issues>`_.
- On `Twitter <https://twitter.com/search/%23doctrine2>`_ with ``#doctrine2``
- On `StackOverflow <https://stackoverflow.com/questions/tagged/doctrine-orm>`_
If you need more structure over the different topics you can browse the :doc:`table
+49
View File
@@ -121,6 +121,52 @@ Now you can test the ``$eventSubscriber`` instance to see if the
echo 'pre foo invoked!';
}
Registering Events
~~~~~~~~~~~~~~~~~~
There are two ways to register an event:
* *All events* can be registered by calling ``$eventManager->addEventListener()``
or ``eventManager->addEventSubscriber()``, see :ref:`listening-and-subscribing-to-lifecycle-events`
* *Lifecycle Callbacks* can also be registered in the entity mapping (annotation, attribute, etc.),
see :ref:`lifecycle-callbacks`
Events Overview
---------------
+-----------------------------+-----------------------+-----------+
| Event | Dispatched by | Lifecycle |
| | | Callback |
+=============================+=======================+===========+
| ``preRemove`` | ``$em->remove()`` | Yes |
+-----------------------------+-----------------------+-----------+
| ``postRemove`` | ``$em->flush()`` | Yes |
+-----------------------------+-----------------------+-----------+
| ``prePersist`` | ``$em->persist()`` | Yes |
| | on *initial* persist | |
+-----------------------------+-----------------------+-----------+
| ``postPersist`` | ``$em->flush()`` | Yes |
+-----------------------------+-----------------------+-----------+
| ``preUpdate`` | ``$em->flush()`` | Yes |
+-----------------------------+-----------------------+-----------+
| ``postUpdate`` | ``$em->flush()`` | Yes |
+-----------------------------+-----------------------+-----------+
| ``postLoad`` | Loading from database | Yes |
+-----------------------------+-----------------------+-----------+
| ``loadClassMetadata`` | Loading of mapping | No |
| | metadata | |
+-----------------------------+-----------------------+-----------+
| ``onClassMetadataNotFound`` | ``MappingException`` | No |
+-----------------------------+-----------------------+-----------+
| ``preFlush`` | ``$em->flush()`` | Yes |
+-----------------------------+-----------------------+-----------+
| ``onFlush`` | ``$em->flush()`` | No |
+-----------------------------+-----------------------+-----------+
| ``postFlush`` | ``$em->flush()`` | No |
+-----------------------------+-----------------------+-----------+
| ``onClear`` | ``$em->clear()`` | No |
+-----------------------------+-----------------------+-----------+
Naming convention
~~~~~~~~~~~~~~~~~
@@ -237,6 +283,7 @@ The ``EventArgs`` instance received by the listener gives access to the entity,
:ref:`reference-events-implementing-listeners` section very carefully
to understand which operations are allowed in which lifecycle event.
.. _lifecycle-callbacks:
Lifecycle Callbacks
-------------------
@@ -403,6 +450,8 @@ With the additional argument you have access to the
}
}
.. _listening-and-subscribing-to-lifecycle-events:
Listening and subscribing to Lifecycle Events
---------------------------------------------
+75
View File
@@ -18,13 +18,17 @@
<xs:element name="mapped-superclass" type="orm:mapped-superclass" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="entity" type="orm:entity" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="embeddable" type="orm:embeddable" minOccurs="0" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
</xs:element>
<xs:complexType name="emptyType">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="cascade-type">
@@ -35,7 +39,9 @@
<xs:element name="cascade-remove" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-refresh" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-detach" type="orm:emptyType" minOccurs="0"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:simpleType name="lifecycle-callback-type">
@@ -61,15 +67,19 @@
<xs:complexType name="lifecycle-callback">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="type" type="orm:lifecycle-callback-type" use="required" />
<xs:attribute name="method" type="xs:NMTOKEN" use="required" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="lifecycle-callbacks">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="lifecycle-callback" type="orm:lifecycle-callback" minOccurs="1" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="named-query">
@@ -88,6 +98,7 @@
<xs:complexType name="named-native-query">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="query" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="result-class" type="orm:fqcn" />
@@ -97,12 +108,14 @@
<xs:complexType name="named-native-queries">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="named-native-query" type="orm:named-native-query" minOccurs="1" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="entity-listener">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="lifecycle-callback" type="orm:lifecycle-callback" minOccurs="0" maxOccurs="unbounded"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="class" type="orm:fqcn"/>
</xs:complexType>
@@ -135,7 +148,9 @@
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="entity-result" type="orm:entity-result"/>
<xs:element name="column-result" type="orm:column-result"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:string" use="required" />
</xs:complexType>
@@ -173,6 +188,7 @@
<xs:element name="many-to-many" type="orm:many-to-many" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="association-overrides" type="orm:association-overrides" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="attribute-overrides" type="orm:attribute-overrides" minOccurs="0" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="table" type="orm:tablename" />
@@ -181,6 +197,7 @@
<xs:attribute name="inheritance-type" type="orm:inheritance-type"/>
<xs:attribute name="change-tracking-policy" type="orm:change-tracking-policy" />
<xs:attribute name="read-only" type="xs:boolean" default="false" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:simpleType name="tablename" id="tablename">
@@ -193,21 +210,27 @@
<xs:complexType name="option" mixed="true">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="option" type="orm:option"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:NMTOKEN" use="required"/>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="options">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="option" type="orm:option" minOccurs="0" maxOccurs="unbounded"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="mapped-superclass" >
<xs:complexContent>
<xs:extension base="orm:entity">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:anyAttribute namespace="##other"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
@@ -216,6 +239,7 @@
<xs:complexContent>
<xs:extension base="orm:entity">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
</xs:extension>
</xs:complexContent>
@@ -267,6 +291,7 @@
<xs:complexType name="field">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="options" type="orm:options" minOccurs="0" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="type" type="xs:NMTOKEN" default="string" />
@@ -278,6 +303,7 @@
<xs:attribute name="column-definition" type="xs:string" />
<xs:attribute name="precision" type="xs:integer" use="optional" />
<xs:attribute name="scale" type="xs:integer" use="optional" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="embedded">
@@ -292,62 +318,78 @@
<xs:complexType name="discriminator-column">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="type" type="xs:NMTOKEN"/>
<xs:attribute name="field-name" type="xs:NMTOKEN" />
<xs:attribute name="length" type="xs:NMTOKEN" />
<xs:attribute name="column-definition" type="xs:string" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="unique-constraint">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="options" type="orm:options" minOccurs="0" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:NMTOKEN" use="optional"/>
<xs:attribute name="columns" type="xs:string" use="optional"/>
<xs:attribute name="fields" type="xs:string" use="optional"/>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="unique-constraints">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="unique-constraint" type="orm:unique-constraint" minOccurs="1" maxOccurs="unbounded"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="index">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="options" type="orm:options" minOccurs="0" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:NMTOKEN" use="optional"/>
<xs:attribute name="columns" type="xs:string" use="required"/>
<xs:attribute name="fields" type="xs:string" use="optional"/>
<xs:attribute name="flags" type="xs:string" use="optional"/>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="indexes">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="index" type="orm:index" minOccurs="1" maxOccurs="unbounded"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="discriminator-mapping">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="value" type="xs:NMTOKEN" use="required"/>
<xs:attribute name="class" type="orm:fqcn" use="required"/>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="discriminator-map">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="discriminator-mapping" type="orm:discriminator-mapping" minOccurs="1" maxOccurs="unbounded"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="generator">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="strategy" type="orm:generator-strategy" use="optional" default="AUTO" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="id">
@@ -356,6 +398,7 @@
<xs:element name="sequence-generator" type="orm:sequence-generator" minOccurs="0" maxOccurs="1" />
<xs:element name="custom-id-generator" type="orm:custom-id-generator" minOccurs="0" maxOccurs="1" />
<xs:element name="options" type="orm:options" minOccurs="0" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="type" type="xs:NMTOKEN" />
@@ -363,18 +406,22 @@
<xs:attribute name="length" type="xs:NMTOKEN" />
<xs:attribute name="association-key" type="xs:boolean" default="false" />
<xs:attribute name="column-definition" type="xs:string" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="sequence-generator">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="sequence-name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="allocation-size" type="xs:integer" use="optional" default="1" />
<xs:attribute name="initial-value" type="xs:integer" use="optional" default="1" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="custom-id-generator">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="class" type="orm:fqcn" use="required" />
</xs:complexType>
@@ -389,11 +436,14 @@
<xs:complexType name="inverse-join-columns">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="join-column" type="orm:join-column" minOccurs="1" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="join-column">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:NMTOKEN" use="optional" />
<xs:attribute name="referenced-column-name" type="xs:NMTOKEN" use="optional" default="id" />
@@ -401,34 +451,43 @@
<xs:attribute name="nullable" type="xs:boolean" default="true" />
<xs:attribute name="on-delete" type="orm:fk-action" />
<xs:attribute name="column-definition" type="xs:string" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="join-columns">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="join-column" type="orm:join-column" minOccurs="1" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="join-table">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="join-columns" type="orm:join-columns" />
<xs:element name="inverse-join-columns" type="orm:join-columns" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="schema" type="xs:NMTOKEN" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="order-by">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="order-by-field" type="orm:order-by-field" minOccurs="1" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="order-by-field">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="direction" type="orm:order-by-direction" default="ASC" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:simpleType name="order-by-direction">
@@ -444,6 +503,7 @@
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
<xs:element name="join-table" type="orm:join-table" minOccurs="0" />
<xs:element name="order-by" type="orm:order-by" minOccurs="0" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="target-entity" type="xs:string" use="required" />
@@ -452,6 +512,7 @@
<xs:attribute name="index-by" type="xs:NMTOKEN" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="one-to-many">
@@ -459,6 +520,7 @@
<xs:element name="cache" type="orm:cache" minOccurs="0" maxOccurs="1"/>
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
<xs:element name="order-by" type="orm:order-by" minOccurs="0" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="target-entity" type="xs:string" use="required" />
@@ -466,6 +528,7 @@
<xs:attribute name="index-by" type="xs:NMTOKEN" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="many-to-one">
@@ -475,12 +538,15 @@
<xs:choice minOccurs="0" maxOccurs="1">
<xs:element name="join-column" type="orm:join-column"/>
<xs:element name="join-columns" type="orm:join-columns"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="field" type="xs:NMTOKEN" 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"/>
</xs:complexType>
<xs:complexType name="one-to-one">
@@ -490,7 +556,9 @@
<xs:choice minOccurs="0" maxOccurs="1">
<xs:element name="join-column" type="orm:join-column"/>
<xs:element name="join-columns" type="orm:join-columns"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="target-entity" type="xs:string" />
@@ -498,11 +566,13 @@
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="association-overrides">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="association-override" type="orm:association-override" minOccurs="1" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
</xs:complexType>
@@ -511,6 +581,7 @@
<xs:element name="join-table" type="orm:join-table" minOccurs="0" />
<xs:element name="join-columns" type="orm:join-columns" minOccurs="0" />
<xs:element name="inversed-by" type="orm:inversed-by-override" minOccurs="0" maxOccurs="1" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="fetch" type="orm:fetch-type" use="optional" />
@@ -523,12 +594,14 @@
<xs:complexType name="attribute-overrides">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="attribute-override" type="orm:attribute-override" minOccurs="1" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="attribute-override">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="field" type="orm:attribute-override-field" minOccurs="1" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
</xs:complexType>
@@ -536,6 +609,7 @@
<xs:complexType name="attribute-override-field">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="options" type="orm:options" minOccurs="0" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:attribute name="type" type="xs:NMTOKEN" default="string" />
<xs:attribute name="column" type="xs:NMTOKEN" />
@@ -546,6 +620,7 @@
<xs:attribute name="column-definition" type="xs:string" />
<xs:attribute name="precision" type="xs:integer" use="optional" />
<xs:attribute name="scale" type="xs:integer" use="optional" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
</xs:schema>
@@ -357,9 +357,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
$cached = $queryCache->put($queryKey, $rsm, [$result]);
if ($this->cacheLogger) {
if ($result) {
$this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
}
$this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
if ($cached) {
$this->cacheLogger->queryCachePut($this->regionName, $queryKey);
@@ -19,6 +19,7 @@ use Doctrine\Persistence\ObjectManager;
* EntityManager interface
*
* @method Mapping\ClassMetadataFactory getMetadataFactory()
* @method mixed wrapInTransaction(callable $func)
*/
interface EntityManagerInterface extends ObjectManager
{
@@ -142,7 +142,7 @@ abstract class AbstractHydrator
* @param Result|ResultStatement $stmt
* @psalm-param array<string, mixed> $hints
*
* @return Generator<int, mixed>
* @return Generator<array-key, mixed>
*
* @final
*/
@@ -194,9 +194,12 @@ abstract class AbstractHydrator
$this->hydrateRowData($row, $result);
$this->cleanupAfterRowIteration();
if (count($result) === 1) {
yield end($result);
if (count($resultSetMapping->indexByMap) === 0) {
yield end($result);
} else {
yield from $result;
}
} else {
yield $result;
}
@@ -184,9 +184,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
$class->setPrimaryTable($parent->table);
}
if ($parent) {
$this->addInheritedIndexes($class, $parent);
}
$this->addInheritedIndexes($class, $parent);
if ($parent->cache) {
$class->cache = $parent->cache;
@@ -276,7 +274,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
} else {
assert($parent instanceof ClassMetadataInfo); // https://github.com/doctrine/orm/issues/8746
if (
(! $class->reflClass || ! $class->reflClass->isAbstract())
! $class->reflClass->isAbstract()
&& ! in_array($class->name, $class->discriminatorMap, true)
) {
throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName);
@@ -31,7 +31,7 @@ use const DIRECTORY_SEPARATOR;
*/
class EntityRepositoryGenerator
{
/** @psalm-var class-string */
/** @psalm-var class-string|null */
private $repositoryName;
/** @var string */
+1 -1
View File
@@ -239,7 +239,7 @@ class SchemaValidator
}
}
if (! $class->isInheritanceTypeNone() && ! $class->isRootEntity() && ! $class->isMappedSuperclass && array_search($class->name, $class->discriminatorMap, true) === false) {
if (! $class->isInheritanceTypeNone() && ! $class->isRootEntity() && ! $class->reflClass->isAbstract() && ! $class->isMappedSuperclass && array_search($class->name, $class->discriminatorMap, true) === false) {
$ce[] = "Entity class '" . $class->name . "' is part of inheritance hierarchy, but is " .
"not mapped in the root entity '" . $class->rootEntityName . "' discriminator map. " .
'All subclasses must be listed in the discriminator map.';
+2 -7
View File
@@ -85,11 +85,6 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php
-
message: "#^If condition is always true\\.$#"
count: 1
path: lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php
-
message: "#^Parameter \\#3 \\$entry of method Doctrine\\\\ORM\\\\Cache\\\\EntityHydrator\\:\\:loadCacheEntry\\(\\) expects Doctrine\\\\ORM\\\\Cache\\\\EntityCacheEntry, Doctrine\\\\ORM\\\\Cache\\\\CacheEntry given\\.$#"
count: 1
@@ -412,7 +407,7 @@ parameters:
-
message: "#^If condition is always true\\.$#"
count: 2
count: 1
path: lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
-
@@ -422,7 +417,7 @@ parameters:
-
message: "#^Negated boolean expression is always false\\.$#"
count: 3
count: 2
path: lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
-
+7 -27
View File
@@ -273,8 +273,7 @@
<code>getCacheFactory</code>
<code>getTimestampRegion</code>
</PossiblyNullReference>
<RedundantConditionGivenDocblockType occurrences="2">
<code>$result</code>
<RedundantConditionGivenDocblockType occurrences="1">
<code>assert($metadata instanceof ClassMetadata)</code>
</RedundantConditionGivenDocblockType>
<UndefinedInterfaceMethod occurrences="9">
@@ -503,10 +502,9 @@
<code>$entityName</code>
<code>$entityName</code>
</ParamNameMismatch>
<PossiblyNullArgument occurrences="3">
<PossiblyNullArgument occurrences="2">
<code>$config-&gt;getProxyDir()</code>
<code>$config-&gt;getProxyNamespace()</code>
<code>$entity</code>
</PossiblyNullArgument>
<PossiblyNullReference occurrences="2">
<code>createCache</code>
@@ -827,8 +825,7 @@
<code>addNamedNativeQuery</code>
<code>addNamedQuery</code>
</DeprecatedMethod>
<DocblockTypeContradiction occurrences="3">
<code>! $class-&gt;reflClass</code>
<DocblockTypeContradiction occurrences="2">
<code>$class-&gt;reflClass</code>
<code>$definition</code>
</DocblockTypeContradiction>
@@ -893,8 +890,7 @@
<PropertyTypeCoercion occurrences="1">
<code>$subClass-&gt;table</code>
</PropertyTypeCoercion>
<RedundantConditionGivenDocblockType occurrences="2">
<code>$parent</code>
<RedundantConditionGivenDocblockType occurrences="1">
<code>$parent-&gt;idGenerator</code>
</RedundantConditionGivenDocblockType>
<UndefinedInterfaceMethod occurrences="17">
@@ -1973,9 +1969,6 @@
<code>$queryCacheTTL</code>
<code>Query</code>
</PropertyNotSetInConstructor>
<RedundantCastGivenDocblockType occurrences="1">
<code>(int) $timeToLive</code>
</RedundantCastGivenDocblockType>
<RedundantConditionGivenDocblockType occurrences="4">
<code>$dqlQuery !== null</code>
<code>$rsm !== null</code>
@@ -3239,10 +3232,9 @@
<NullableReturnStatement occurrences="1">
<code>$this-&gt;cacheMode</code>
</NullableReturnStatement>
<PossiblyFalseArgument occurrences="3">
<PossiblyFalseArgument occurrences="2">
<code>$spacePos</code>
<code>$spacePos</code>
<code>strpos($join, '.')</code>
</PossiblyFalseArgument>
<PossiblyFalseOperand occurrences="2">
<code>$spacePos</code>
@@ -3258,13 +3250,6 @@
<PropertyNotSetInConstructor occurrences="1">
<code>$_dql</code>
</PropertyNotSetInConstructor>
<RedundantCastGivenDocblockType occurrences="5">
<code>(bool) $cacheable</code>
<code>(bool) $flag</code>
<code>(int) $cacheMode</code>
<code>(int) $lifetime</code>
<code>(string) $cacheRegion</code>
</RedundantCastGivenDocblockType>
<RedundantConditionGivenDocblockType occurrences="1">
<code>$this-&gt;_dql !== null</code>
</RedundantConditionGivenDocblockType>
@@ -3538,15 +3523,9 @@
<code>$fullClassName</code>
<code>$fullClassName</code>
</ArgumentTypeCoercion>
<DocblockTypeContradiction occurrences="1">
<code>EntityRepository::class</code>
</DocblockTypeContradiction>
<PossiblyFalseOperand occurrences="1">
<code>strrpos($fullClassName, '\\')</code>
</PossiblyFalseOperand>
<PropertyNotSetInConstructor occurrences="1">
<code>$repositoryName</code>
</PropertyNotSetInConstructor>
<PropertyTypeCoercion occurrences="1">
<code>$repositoryName</code>
</PropertyTypeCoercion>
@@ -3877,7 +3856,8 @@
<code>setValue</code>
<code>setValue</code>
</PossiblyNullReference>
<PossiblyUndefinedMethod occurrences="3">
<PossiblyUndefinedMethod occurrences="4">
<code>addPropertyChangedListener</code>
<code>unwrap</code>
<code>unwrap</code>
<code>unwrap</code>
@@ -6,6 +6,7 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\IterableTester;
use Doctrine\Tests\Models\CMS\CmsAddress;
use Doctrine\Tests\Models\CMS\CmsArticle;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\OrmFunctionalTestCase;
@@ -68,4 +69,48 @@ final class QueryIterableTest extends OrmFunctionalTestCase
IterableTester::assertResultsAreTheSame($query);
}
public function testIndexByQueryWithOneResult(): void
{
$user = new CmsUser();
$user->name = 'Antonio J.';
$user->username = 'ajgarlag';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$query = $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u INDEX BY u.username');
IterableTester::assertResultsAreTheSame($query);
}
public function testIndexByQueryWithMultipleResults(): void
{
$article1 = new CmsArticle();
$article1->topic = 'Doctrine 2';
$article1->text = 'This is an introduction to Doctrine 2.';
$article2 = new CmsArticle();
$article2->topic = 'Symfony 2';
$article2->text = 'This is an introduction to Symfony 2.';
$article3 = new CmsArticle();
$article3->topic = 'Laminas';
$article3->text = 'This is an introduction to Laminas.';
$article4 = new CmsArticle();
$article4->topic = 'CodeIgniter';
$article4->text = 'This is an introduction to CodeIgniter.';
$this->_em->persist($article1);
$this->_em->persist($article2);
$this->_em->persist($article3);
$this->_em->persist($article4);
$this->_em->flush();
$this->_em->clear();
$query = $this->_em->createQuery('select a from ' . CmsArticle::class . ' a INDEX BY a.topic');
IterableTester::assertResultsAreTheSame($query);
}
}
@@ -225,6 +225,17 @@ class SchemaValidatorTest extends OrmTestCase
$this->assertEquals([], $ce);
}
/**
* @group 9095
*/
public function testAbstractChildClassNotPresentInDiscriminator(): void
{
$class1 = $this->em->getClassMetadata(Issue9095AbstractChild::class);
$ce = $this->validator->validateClass($class1);
$this->assertEquals([], $ce);
}
}
/**
@@ -256,6 +267,35 @@ class ChildEntity extends MappedSuperclassEntity
{
}
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorMap({"child" = Issue9095Child::class})
*/
abstract class Issue9095Parent
{
/**
* @var mixed
* @Id
* @Column
*/
protected $key;
}
/**
* @Entity
*/
abstract class Issue9095AbstractChild extends Issue9095Parent
{
}
/**
* @Entity
*/
class Issue9095Child extends Issue9095AbstractChild
{
}
/**
* @Entity
*/