Compare commits

...

68 Commits

Author SHA1 Message Date
Andrii Dembitskyi cccb2e2fdf Docs: use canonical order for phpdoc tags, add missed semicolon (#9190) 2021-12-20 22:23:47 +01:00
Benjamin Eberlei 18138d895e Make PrimaryReadReplicaConnection enforcement explicit (#9239)
* Move primary replica connection logic into ORM explicitly.

* Housekeeping: Use full named variables

* Housekeeping: phpcs
2021-12-20 13:50:25 +01:00
Benjamin Eberlei fa2b52c974 [docs] Fix wording for attributes=>parameters. (#9265) 2021-12-18 11:16:35 +01:00
olsavmic 5bf814032f Revert "Fix SchemaValidator with abstract child class in discriminator map (#9096)" (#9262)
This reverts commit bbb68d0072.
2021-12-18 11:01:30 +01:00
Andrii Dembitskyi 02a4e4099d Docs: consistency for FQCN, spacing, etc (#9232)
* Docs: consistent spacing, consistent array-style, consistent FQCN, avoid double escaped slashes, avoid double quotes if not necessary

* Docs: use special note block instead of markdown-based style

* Docs: Quote FQCN in table with backticks to be compatible with all render engines

* Drop all mentions API doc - it is not available anymore

* Add missed FQCN for code snippets
2021-12-13 23:10:01 +01:00
Alexander M. Turek 12a70bbefb PHPCS 3.6.2, Psalm 4.15.0 (#9247) 2021-12-13 21:28:56 +01:00
Grégoire Paris 5a4ddb2870 Merge pull request #9184 from ThomasLandauer/patch-1
[Documentation] Events Overview Table: Adding "Passed Argument" column
2021-12-12 16:21:13 +01:00
Alexander M. Turek 68fa55f310 Remove fallbacks for old doctrine/annotations version (#9235) 2021-12-11 17:11:34 +01:00
Thomas Landauer 0b0c3e7e58 Update docs/en/reference/events.rst
Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-12-09 12:00:30 +01:00
Kevin van Sonsbeek 92434f91c7 Added psalm param to abstract addFilterConstraint (#9229) 2021-12-08 22:31:52 +00:00
Alexander Schranz 6414ad4cbb Merge pull request #9210 from alexander-schranz/patch-2
Fix making columns  optional in indexes xml schema as they can be defined via fields now
2021-12-06 00:55:01 +01:00
Grégoire Paris 7b24275346 Merge pull request #9218 from Florian-Varrin/patch-1
Fix typo assumptio--> assumption
2021-12-03 13:27:05 +01:00
Florian Varrin ed1a576305 Fix typo assumptio--> assumption 2021-12-03 11:39:59 +01:00
Thomas Landauer 8b6fe52f74 Update events.rst 2021-12-01 01:01:04 +01:00
Alexander M. Turek 15ec77fa79 Suppress Psalm's ReservedWord errors (#9212) 2021-11-30 20:20:27 +01:00
Thomas Landauer 32cd2106d0 Completing links to EventArgs classes in overview table
Questions:
1. Is https://github.com/doctrine/persistence/blob/master/lib/Doctrine/Persistence/Event/LifecycleEventArgs.php correct at all? Shouldn't this be https://github.com/doctrine/orm/blob/2.10.x/lib/Doctrine/ORM/Event/LifecycleEventArgs.php, like all the others?

2. Which one is correct for `preUpdate`? https://www.doctrine-project.org/projects/doctrine-orm/en/2.10/reference/events.html#entity-listeners-class says `PreUpdateEventArgs`, but https://www.doctrine-project.org/projects/doctrine-orm/en/2.10/reference/events.html#listening-and-subscribing-to-lifecycle-events says `LifecycleEventArgs`

For the two links to `doctrine/persistence`, I'm linking to `/master/` now, which is being forwarded to `/2.2.x/`.
2021-11-30 15:51:20 +01:00
Alexander M. Turek cac2acae07 Psalm 4.13.1, PHPStan 1.2.0 (#9204) 2021-11-28 00:50:56 +01:00
Grégoire Paris 146b465ec1 Merge pull request #9198 from laryjulien/fix-fieldmapping-definition
Add a psalm type for field mapping
2021-11-23 21:10:18 +01:00
Julien LARY 5aba762a33 Add a psalm type for field mapping
Field mapping have different definitions
in property definition and method return.
As suggested in issue and to avoid further desynchronization,
a psalm type has been created.
Fixes #9193
2021-11-23 18:05:47 +01:00
Thomas Landauer 77b7107d05 Using const for type 2021-11-23 01:41:01 +01:00
Simon Podlipsky a663dda869 Use equal to instead of equal of in assertSqlGeneration() (#9195) 2021-11-20 21:27:46 +01:00
Thomas Landauer db14f0fa89 Adding Attributes code block (#9161)
Just that there is some real-world example somewhere ;-) see https://github.com/doctrine/orm/issues/9020#issuecomment-955582801
2021-11-20 18:14:49 +01:00
Vincent Langlet 9a74ae6280 Fix discriminatorColumn phpdoc (#9168) 2021-11-11 23:01:34 +01:00
Grégoire Paris 32eb38ebd9 Merge pull request #9181 from greg0ire/fix-broken-build
Remove similar assertions for other platforms
2021-11-11 16:20:53 +01:00
Thomas Landauer 2dde65c4ba [Documentation] Events Overview Table: Adding "Passed Argument" column
As announced in https://github.com/doctrine/orm/pull/9160#issuecomment-954304588 I'm adding the passed "EventArgs" class to the overview table. Once this is complete, my further plan is to remove the entire paragraph https://www.doctrine-project.org/projects/doctrine-orm/en/2.10/reference/events.html#lifecycle-callbacks-event-argument, and probably also the second code block at https://www.doctrine-project.org/projects/doctrine-orm/en/2.10/reference/events.html#entity-listeners-class

Is there a better way to link to the source code of `LifecycleEventArgs` than https://github.com/doctrine/persistence/blob/2.2.x/lib/Doctrine/Persistence/Event/LifecycleEventArgs.php ?

Also, I changed `postLoad` to `preUpdate` in the code block, to have an example that does not receive `LifecycleEventArgs` ;-)
2021-11-11 00:22:25 +01:00
Thomas Landauer 176fbedc69 Fine-tuning codeblock (#9176)
* Deleting "Not needed for XML and YAML mapping" - this was stupid of me, since *all* annotations are obviously not needed in XML&YAML ;-)
* Shortening the @Column annotation, for consistency with the following event handlers
* Removing some blank lines from XML, for consistency with YAML
* Adding PHP Attributes
2021-11-10 22:43:09 +01:00
Grégoire Paris 1b15af44b6 Remove similar assertions for other platforms
Testing with several platforms should not increase code coverage here,
since the DBAL is responsible for providing the concat expression for
each platform.

Moreover, whenever that concat expression changes for one of the tested
platforms, this test will break.

In doctrine/dbal 3.2, that is the case for SQLServer2012Platform, which
means this test no longer passes.
2021-11-08 21:21:41 +01:00
Grégoire Paris 8336420a26 Merge pull request #9153 from armenio/2.10.x
Infer type from field instead of column
2021-11-08 07:45:07 +01:00
Thomas Landauer a6b7569d7a Fixing more links (#9154)
* Fixing more links

The first two I missed in https://github.com/doctrine/orm/pull/9151
The third is probably older.
Shouldn't the chapter name be displayed as link text by default?? Are you sure that everything is set up correctly with the parser?

* Update architecture.rst

* Update getting-started.rst

* Update events.rst
2021-11-06 20:49:54 +01:00
Rafael Armenio 9e37c788ef Infer type from field instead of column
getTypeOfColumn() relies on getTypeOfField(), and does not suffer from
mismatching issues caused by quoting, because you cannot quote a field.
Since a field can be composite, that method returns an array, hence why we
need to select the first element.
2021-11-05 13:58:53 -03:00
Grégoire Paris ca0a6bbf71 Merge pull request #9167 from derrabus/bump/phpstan
PHPStan 1.0.1
2021-11-03 21:15:19 +01:00
Grégoire Paris a3da3d78d4 Merge pull request #9159 from ThomasLandauer/patch-10
Merging Lifecycle Callbacks code samples for PHP + XML + YAML
2021-11-03 21:13:53 +01:00
Alexander M. Turek e1c2d2e65d PHPStan 1.0.1
Signed-off-by: Alexander M. Turek <me@derrabus.de>
2021-11-02 20:41:48 +01:00
Alexander Schranz 6f194eeabf Remove reverted bc break (#9166) 2021-11-01 13:56:12 +01:00
Grégoire Paris 16cbc16998 Document BC break (#9143)
Closes #9141
2021-10-30 19:10:25 +02:00
Thomas Landauer 5e6608b48e Update events.rst 2021-10-30 13:48:03 +02:00
Grégoire Paris 94bc137526 Merge pull request #9123 from phansys/quotes_in_column_names
Add XSD "orm:columntoken" type in order to support reserved words in column names
2021-10-29 18:19:35 +02:00
Thomas Landauer 276a0f55ee Removing paragraph on consts (#9158)
IMO, this is better shown by example, so I added it there.
2021-10-29 14:21:37 +02:00
Thomas Landauer dbaf99f3d9 Update events.rst 2021-10-29 01:17:32 +02:00
Thomas Landauer 97411f5567 Merging Lifecycle Callbacks code samples for PHP + XML + YAML
IMO, the text I deleted just repeated things that are obvious in the example anyway.
2021-10-29 01:12:02 +02:00
Chase Noel 641330baa6 Add doctrine/dbal to project composer.json (#9152)
As discussed in https://github.com/doctrine/orm/issues/9078 when entities utilize data mappings which are provided by the dbal lib it is expected behavior that users will explicitly define their dependency on the package.

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-10-28 23:42:05 +02:00
Thomas Landauer 35e680cd3f Fixing links in overview table (#9151)
I got them wrong in https://github.com/doctrine/orm/pull/9131 ;-)
2021-10-28 21:29:47 +02:00
Javier Spagnoletti 705d88eaba Add XSD "orm:columntoken" type in order to support reserved words in column names 2021-10-28 14:00:39 -03:00
Paul Waring 3271d8f6e2 Fix markup for variable names (#9150)
Three references to `$isDevMode` were marked up with a single backtick, however two backticks are required in order for the variable name to be highlighted correctly (c.f. `ArrayCache`).
2021-10-26 10:59:31 +00:00
Thomas Landauer 3622381f8c Overview table for events: Jump links (#9131)
* Overview table for events: Jump links

* Update events.rst
2021-10-25 22:34:36 +02:00
wickedOne f2729b0610 Return 0 when there's no metadata to process (#9147) 2021-10-23 09:43:40 +00:00
chapterjason cd44547573 Remove old use statements (#9146) 2021-10-23 11:32:44 +02:00
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
56 changed files with 1247 additions and 648 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 }}
+10 -7
View File
@@ -1,5 +1,12 @@
# Upgrade to 2.10
## BC Break: `UnitOfWork` now relies on SPL object IDs, not hashes
When calling the following methods, you are now supposed to use the result of
`spl_object_id()`, and not `spl_object_hash()`:
- `UnitOfWork::clearEntityChangeSet()`
- `UnitOfWork::setOriginalEntityProperty()`
## BC Break: Removed `TABLE` id generator strategy
The implementation was unfinished for 14 years.
@@ -9,10 +16,6 @@ It is now deprecated to rely on:
- `Doctrine\ORM\Mapping\ClassMetadata::$tableGeneratorDefinition`;
- or `Doctrine\ORM\Mapping\ClassMetadata::isIdGeneratorTable()`.
## BC Break: Removed possibility to extend the doctrine mapping xml schema with anything
If you want to extend it now you have to provide your own validation schema.
## New method `Doctrine\ORM\EntityManagerInterface#wrapInTransaction($func)`
Works the same as `Doctrine\ORM\EntityManagerInterface#transactional()` but returns any value returned from `$func` closure rather than just _non-empty value returned from the closure or true_.
@@ -108,10 +111,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
+3 -3
View File
@@ -39,12 +39,12 @@
"doctrine/annotations": "^1.13",
"doctrine/coding-standard": "^9.0",
"phpbench/phpbench": "^0.16.10 || ^1.0",
"phpstan/phpstan": "0.12.99",
"phpstan/phpstan": "1.2.0",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
"squizlabs/php_codesniffer": "3.6.0",
"squizlabs/php_codesniffer": "3.6.2",
"symfony/cache": "^4.4 || ^5.2",
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
"vimeo/psalm": "4.10.0"
"vimeo/psalm": "4.15.0"
},
"conflict": {
"doctrine/annotations": "<1.13 || >= 2.0"
@@ -4,7 +4,7 @@ Advanced field value conversion using custom mapping types
.. sectionauthor:: Jan Sorgalla <jsorgalla@googlemail.com>
When creating entities, you sometimes have the need to transform field values
before they are saved to the database. In Doctrine you can use Custom Mapping
before they are saved to the database. In Doctrine you can use Custom Mapping
Types to solve this (see: :ref:`reference-basic-mapping-custom-mapping-types`).
There are several ways to achieve this: converting the value inside the Type
@@ -15,7 +15,7 @@ type `Point <https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html>`_.
The ``Point`` type is part of the `Spatial extension <https://dev.mysql.com/doc/refman/8.0/en/spatial-extensions.html>`_
of MySQL and enables you to store a single location in a coordinate space by
using x and y coordinates. You can use the Point type to store a
using x and y coordinates. You can use the Point type to store a
longitude/latitude pair to represent a geographic location.
The entity
@@ -29,9 +29,9 @@ The entity class:
.. code-block:: php
<?php
namespace Geo\Entity;
/**
* @Entity
*/
@@ -84,7 +84,7 @@ The entity class:
}
}
We use the custom type ``point`` in the ``@Column`` docblock annotation of the
We use the custom type ``point`` in the ``@Column`` docblock annotation of the
``$point`` field. We will create this custom mapping type in the next chapter.
The point class:
@@ -92,7 +92,7 @@ The point class:
.. code-block:: php
<?php
namespace Geo\ValueObject;
class Point
@@ -196,7 +196,7 @@ The format of the string representation format is called
`Well-known text (WKT) <https://en.wikipedia.org/wiki/Well-known_text>`_.
The advantage of this format is, that it is both human readable and parsable by MySQL.
Internally, MySQL stores geometry values in a binary format that is not
Internally, MySQL stores geometry values in a binary format that is not
identical to the WKT format. So, we need to let MySQL transform the WKT
representation into its internal format.
@@ -210,13 +210,13 @@ which convert WKT strings to and from the internal format of MySQL.
.. note::
When using DQL queries, the ``convertToPHPValueSQL`` and
When using DQL queries, the ``convertToPHPValueSQL`` and
``convertToDatabaseValueSQL`` methods only apply to identification variables
and path expressions in SELECT clauses. Expressions in WHERE clauses are
and path expressions in SELECT clauses. Expressions in WHERE clauses are
**not** wrapped!
If you want to use Point values in WHERE clauses, you have to implement a
:doc:`user defined function <dql-user-defined-functions>` for
:doc:`user defined function <dql-user-defined-functions>` for
``PointFromText``.
Example usage
@@ -252,5 +252,5 @@ Example usage
$query = $em->createQuery("SELECT l FROM Geo\Entity\Location l WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'");
$location = $query->getSingleResult();
/* @var Geo\ValueObject\Point */
/** @var Geo\ValueObject\Point */
$point = $location->getPoint();
+13 -13
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
@@ -88,7 +88,7 @@ API would look for this use-case:
$pageNum = 1;
$query = $em->createQuery($dql);
$query->setFirstResult( ($pageNum-1) * 20)->setMaxResults(20);
$totalResults = Paginate::count($query);
$results = $query->getResult();
@@ -101,12 +101,12 @@ The ``Paginate::count(Query $query)`` looks like:
{
static public function count(Query $query)
{
/* @var $countQuery Query */
/** @var Query $countQuery */
$countQuery = clone $query;
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('DoctrineExtensions\Paginate\CountSqlWalker'));
$countQuery->setFirstResult(null)->setMaxResults(null);
return $countQuery->getSingleScalarResult();
}
}
@@ -137,13 +137,13 @@ implementation is:
break;
}
}
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,
$parent['metadata']->getSingleIdentifierFieldName()
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
$AST->selectClause->selectExpressions = array(
new SelectExpression(
new AggregateExpression('count', $pathExpression, true), null
@@ -196,7 +196,7 @@ modify the generation of the SELECT clause, adding the
public function walkSelectClause($selectClause)
{
$sql = parent::walkSelectClause($selectClause);
if ($this->getQuery()->getHint('mysqlWalker.sqlNoCache') === true) {
if ($selectClause->isDistinct) {
$sql = str_replace('SELECT DISTINCT', 'SELECT DISTINCT SQL_NO_CACHE', $sql);
@@ -204,7 +204,7 @@ modify the generation of the SELECT clause, adding the
$sql = str_replace('SELECT', 'SELECT SQL_NO_CACHE', $sql);
}
}
return $sql;
}
}
+12 -12
View File
@@ -45,7 +45,7 @@ configuration:
$config->addCustomStringFunction($name, $class);
$config->addCustomNumericFunction($name, $class);
$config->addCustomDatetimeFunction($name, $class);
$em = EntityManager::create($dbParams, $config);
The ``$name`` is the name the function will be referred to in the
@@ -96,7 +96,7 @@ discuss it step by step:
// (1)
public $firstDateExpression = null;
public $secondDateExpression = null;
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER); // (2)
@@ -106,7 +106,7 @@ discuss it step by step:
$this->secondDateExpression = $parser->ArithmeticPrimary(); // (6)
$parser->match(Lexer::T_CLOSE_PARENTHESIS); // (3)
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return 'DATEDIFF(' .
@@ -180,28 +180,28 @@ I'll skip the blah and show the code for this function:
public $firstDateExpression = null;
public $intervalExpression = null;
public $unit = null;
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->firstDateExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$parser->match(Lexer::T_IDENTIFIER);
$this->intervalExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_IDENTIFIER);
/* @var $lexer Lexer */
/** @var Lexer $lexer */
$lexer = $parser->getLexer();
$this->unit = $lexer->token['value'];
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return 'DATE_ADD(' .
-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
+2
View File
@@ -184,6 +184,8 @@ in well defined units of work. Work with your objects and modify
them as usual and when you're done call ``EntityManager#flush()``
to make your changes persistent.
.. _unit-of-work:
The Unit of Work
~~~~~~~~~~~~~~~~
+27 -27
View File
@@ -59,12 +59,12 @@ inside the instance variables PHP DocBlock comment. Any value hold
inside this variable will be saved to and loaded from the database
as part of the lifecycle of the instance variables entity-class.
Required attributes:
Required parameters:
- **type**: Name of the DBAL Type which does the conversion between PHP
and Database representation.
Optional attributes:
Optional parameters:
- **name**: By default the property name is used for the database
column name also, however the ``name`` attribute allows you to
@@ -165,7 +165,7 @@ Examples:
~~~~~~~~
Add caching strategy to a root entity or a collection.
Optional attributes:
Optional parameters:
- **usage**: One of ``READ_ONLY``, ``READ_WRITE`` or ``NONSTRICT_READ_WRITE``, By default this is ``READ_ONLY``.
- **region**: An specific region name
@@ -210,7 +210,7 @@ Example:
This attribute allows you to specify a user-provided class to generate identifiers. This attribute only works when both :ref:`#[Id] <attrref_id>` and :ref:`#[GeneratedValue(strategy: "CUSTOM")] <attrref_generatedvalue>` are specified.
Required attributes:
Required parameters:
- **class**: name of the class which should extend Doctrine\ORM\Id\AbstractIdGenerator
@@ -244,13 +244,13 @@ actually instantiated as.
If this attribute is not specified, the discriminator column defaults
to a string column of length 255 called ``dtype``.
Required attributes:
Required parameters:
- **name**: The column name of the discriminator. This name is also
used during Array hydration as key to specify the class-name.
Optional attributes:
Optional parameters:
- **type**: By default this is string.
@@ -319,7 +319,7 @@ attribute to establish the relationship between the two classes.
The embedded attribute is required on an entity's member variable,
in order to specify that it is an embedded class.
Required attributes:
Required parameters:
- **class**: The embeddable class
@@ -331,7 +331,7 @@ Required attributes:
Required attribute to mark a PHP class as an entity. Doctrine manages
the persistence of all classes marked as entities.
Optional attributes:
Optional parameters:
- **repositoryClass**: Specifies the FQCN of a subclass of the
``EntityRepository``. Use of repositories for entities is encouraged to keep
@@ -368,7 +368,7 @@ conjunction with #[Id].
If this attribute is not specified with ``#[Id]`` the ``NONE`` strategy is
used as default.
Optional attributes:
Optional parameters:
- **strategy**: Set the name of the identifier generation strategy.
Valid values are ``AUTO``, ``SEQUENCE``, ``IDENTITY``, ``UUID``
@@ -424,14 +424,14 @@ Attribute is used on the entity-class level. It provides a hint to the SchemaToo
generate a database index on the specified table columns. It only
has meaning in the ``SchemaTool`` schema generation context.
Required attributes:
Required parameters:
- **name**: Name of the Index
- **fields**: Array of fields. Exactly one of **fields, columns** is required.
- **columns**: Array of columns. Exactly one of **fields, columns** is required.
Optional attributes:
Optional parameters:
- **options**: Array of platform specific options:
@@ -548,7 +548,7 @@ The ``#[InverseJoinColumn]`` is the same as ``#[JoinColumn]`` and is used in the
of a ``#[ManyToMany]`` attribute declaration to specifiy the details of the join table's
column information used for the join to the inverse entity.
Optional attributes:
Optional parameters:
- **name**: Column name that holds the foreign key identifier for
this relation. In the context of ``#[JoinTable]`` it specifies the column
@@ -623,14 +623,14 @@ Example:
Defines that the annotated instance variable holds a reference that
describes a many-to-one relationship between two entities.
Required attributes:
Required parameters:
- **targetEntity**: FQCN of the referenced target entity. Can be the
unqualified class name if both classes are in the same namespace.
*IMPORTANT:* No leading backslash!
Optional attributes:
Optional parameters:
- **cascade**: Cascade Option
@@ -659,14 +659,14 @@ additional, optional attribute that has reasonable default
configuration values using the table and names of the two related
entities.
Required attributes:
Required parameters:
- **targetEntity**: FQCN of the referenced target entity. Can be the
unqualified class name if both classes are in the same namespace.
*IMPORTANT:* No leading backslash!
Optional attributes:
Optional parameters:
- **mappedBy**: This option specifies the property name on the
@@ -720,7 +720,7 @@ The ``#[MappedSuperclass]`` attribute cannot be used in conjunction with
``#[Entity]``. See the Inheritance Mapping section for
:doc:`more details on the restrictions of mapped superclasses <inheritance-mapping>`.
Optional attributes:
Optional parameters:
- **repositoryClass**: Specifies the FQCN of a subclass of the EntityRepository.
That will be inherited for all subclasses of that Mapped Superclass.
@@ -756,13 +756,13 @@ be specified. When no
:ref:`#[JoinColumn] <attrref_joincolumn>` is specified it defaults to using the target entity table and
primary key column names and the current naming strategy to determine a name for the join column.
Required attributes:
Required parameters:
- **targetEntity**: FQCN of the referenced target entity. Can be the
unqualified class name if both classes are in the same namespace.
*IMPORTANT:* No leading backslash!
Optional attributes:
Optional parameters:
- **cascade**: Cascade Option
- **fetch**: One of LAZY or EAGER
@@ -786,13 +786,13 @@ Example:
#[OneToMany]
~~~~~~~~~~~~
Required attributes:
Required parameters:
- **targetEntity**: FQCN of the referenced target entity. Can be the
unqualified class name if both classes are in the same namespace.
*IMPORTANT:* No leading backslash!
Optional attributes:
Optional parameters:
- **cascade**: Cascade Option
- **orphanRemoval**: Boolean that specifies if orphans, inverse
@@ -916,11 +916,11 @@ For use with ``#[GeneratedValue(strategy: "SEQUENCE")]`` this
attribute allows to specify details about the sequence, such as
the increment size and initial values of the sequence.
Required attributes:
Required parameters:
- **sequenceName**: Name of the sequence
Optional attributes:
Optional parameters:
- **allocationSize**: Increment the sequence by the allocation size
when its fetched. A value larger than 1 allows optimization for
@@ -954,11 +954,11 @@ placed on the entity-class level and is optional. If it is
not specified the table name will default to the entity's
unqualified classname.
Required attributes:
Required parameters:
- **name**: Name of the table
Optional attributes:
Optional parameters:
- **schema**: Name of the schema the table lies in.
@@ -985,12 +985,12 @@ generate a database unique constraint on the specified table
columns. It only has meaning in the SchemaTool schema generation
context.
Required attributes:
Required parameters:
- **name**: Name of the Index
- **columns**: Array of columns.
Optional attributes:
Optional parameters:
- **options**: Array of platform specific options:
+3 -3
View File
@@ -85,9 +85,9 @@ Or if you prefer YAML:
Inside the ``Setup`` methods several assumptions are made:
- If `$isDevMode` is true caching is done in memory with the ``ArrayCache``. Proxy objects are recreated on every request.
- If `$isDevMode` is false, check for Caches in the order APC, Xcache, Memcache (127.0.0.1:11211), Redis (127.0.0.1:6379) unless `$cache` is passed as fourth argument.
- If `$isDevMode` is false, set then proxy classes have to be explicitly created through the command line.
- If ``$isDevMode`` is true caching is done in memory with the ``ArrayCache``. Proxy objects are recreated on every request.
- If ``$isDevMode`` is false, check for Caches in the order APC, Xcache, Memcache (127.0.0.1:11211), Redis (127.0.0.1:6379) unless `$cache` is passed as fourth argument.
- If ``$isDevMode`` is false, set then proxy classes have to be explicitly created through the command line.
- If third argument `$proxyDir` is not set, use the systems temporary directory.
If you want to configure Doctrine in more detail, take a look at the :doc:`Advanced Configuration <reference/advanced-configuration>` section.
+187 -129
View File
@@ -121,6 +121,53 @@ 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<listening-and-subscribing-to-lifecycle-events>`
* *Lifecycle Callbacks* can also be registered in the entity mapping (annotation, attribute, etc.),
see :ref:`Lifecycle Callbacks<lifecycle-callbacks>`
Events Overview
---------------
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| Event | Dispatched by | Lifecycle | Passed |
| | | Callback | Argument |
+=================================================================+=======================+===========+=====================================+
| :ref:`preRemove<reference-events-pre-remove>` | ``$em->remove()`` | Yes | `_LifecycleEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`postRemove<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `_LifecycleEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`prePersist<reference-events-pre-persist>` | ``$em->persist()`` | Yes | `_LifecycleEventArgs`_ |
| | on *initial* persist | | |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`postPersist<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `_LifecycleEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`preUpdate<reference-events-pre-update>` | ``$em->flush()`` | Yes | `_PreUpdateEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`postUpdate<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `_LifecycleEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`postLoad<reference-events-post-load>` | Loading from database | Yes | `_LifecycleEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`loadClassMetadata<reference-events-load-class-metadata>` | Loading of mapping | No | `_LoadClassMetadataEventArgs` |
| | metadata | | |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| ``onClassMetadataNotFound`` | ``MappingException`` | No | `_OnClassMetadataNotFoundEventArgs` |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`preFlush<reference-events-pre-flush>` | ``$em->flush()`` | Yes | `_PreFlushEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`onFlush<reference-events-on-flush>` | ``$em->flush()`` | No | `_OnFlushEventArgs` |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`postFlush<reference-events-post-flush>` | ``$em->flush()`` | No | `_PostFlushEventArgs` |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| ``onClear`` | ``$em->clear()`` | No | `_OnClearEventArgs` |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
Naming convention
~~~~~~~~~~~~~~~~~
@@ -208,15 +255,6 @@ events during the life-time of their registered entities.
cascade remove relations. In this case, you should load yourself the proxy in
the associated pre event.
You can access the Event constants from the ``Events`` class in the
ORM package.
.. code-block:: php
<?php
use Doctrine\ORM\Events;
echo Events::preUpdate;
These can be hooked into by two different types of event
listeners:
@@ -237,6 +275,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
-------------------
@@ -250,135 +289,109 @@ specific to a particular entity class's lifecycle.
.. note::
Note that Licecycle Callbacks are not supported for Embeddables.
Lifecycle Callbacks are not supported for :doc:`Embeddables </tutorials/embeddables>`.
.. code-block:: php
.. configuration-block::
<?php
.. code-block:: attribute
/** @Entity @HasLifecycleCallbacks */
class User
{
// ...
<?php
use Doctrine\DBAL\Types\Types;
use Doctrine\Persistence\Event\LifecycleEventArgs;
/**
* @Column(type="string", length=255)
* #[Entity]
* #[HasLifecycleCallbacks]
*/
public $value;
/** @Column(name="created_at", type="string", length=255) */
private $createdAt;
/** @PrePersist */
public function doStuffOnPrePersist()
{
$this->createdAt = date('Y-m-d H:i:s');
}
/** @PrePersist */
public function doOtherStuffOnPrePersist()
{
$this->value = 'changed from prePersist callback!';
}
/** @PostPersist */
public function doStuffOnPostPersist()
{
$this->value = 'changed from postPersist callback!';
}
/** @PostLoad */
public function doStuffOnPostLoad()
{
$this->value = 'changed from postLoad callback!';
}
/** @PreUpdate */
public function doStuffOnPreUpdate()
{
$this->value = 'changed from preUpdate callback!';
}
}
Note that the methods set as lifecycle callbacks need to be public and,
when using these annotations, you have to apply the
``@HasLifecycleCallbacks`` marker annotation on the entity class.
If you want to register lifecycle callbacks from YAML or XML you
can do it with the following.
.. code-block:: yaml
User:
type: entity
fields:
# ...
name:
type: string(50)
lifecycleCallbacks:
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ]
postPersist: [ doStuffOnPostPersist ]
In YAML the ``key`` of the lifecycleCallbacks entry is the event that you
are triggering on and the value is the method (or methods) to call. The allowed
event types are the ones listed in the previous Lifecycle Events section.
XML would look something like this:
.. 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">
<entity name="User">
<lifecycle-callbacks>
<lifecycle-callback type="prePersist" method="doStuffOnPrePersist"/>
<lifecycle-callback type="postPersist" method="doStuffOnPostPersist"/>
</lifecycle-callbacks>
</entity>
</doctrine-mapping>
In XML the ``type`` of the lifecycle-callback entry is the event that you
are triggering on and the ``method`` is the method to call. The allowed event
types are the ones listed in the previous Lifecycle Events section.
When using YAML or XML you need to remember to create public methods to match the
callback names you defined. E.g. in these examples ``doStuffOnPrePersist()``,
``doOtherStuffOnPrePersist()`` and ``doStuffOnPostPersist()`` methods need to be
defined on your ``User`` model.
.. code-block:: php
<?php
// ...
class User
{
// ...
public function doStuffOnPrePersist()
class User
{
// ...
}
public function doOtherStuffOnPrePersist()
#[Column(type: Types::STRING, length: 255)]
public $value;
#[PrePersist]
public function doStuffOnPrePersist(LifecycleEventArgs $eventArgs)
{
$this->createdAt = date('Y-m-d H:i:s');
}
#[PrePersist]
public function doOtherStuffOnPrePersist()
{
$this->value = 'changed from prePersist callback!';
}
#[PreUpdate]
public function doStuffOnPreUpdate(PreUpdateEventArgs $eventArgs)
{
$this->value = 'changed from preUpdate callback!';
}
}
.. code-block:: annotation
<?php
use Doctrine\Persistence\Event\LifecycleEventArgs;
/**
* @Entity
* @HasLifecycleCallbacks
*/
class User
{
// ...
}
public function doStuffOnPostPersist()
{
// ...
}
}
/** @Column(type="string", length=255) */
public $value;
/** @PrePersist */
public function doStuffOnPrePersist(LifecycleEventArgs $eventArgs)
{
$this->createdAt = date('Y-m-d H:i:s');
}
/** @PrePersist */
public function doOtherStuffOnPrePersist()
{
$this->value = 'changed from prePersist callback!';
}
/** @PreUpdate */
public function doStuffOnPreUpdate(PreUpdateEventArgs $eventArgs)
{
$this->value = 'changed from preUpdate callback!';
}
}
.. 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">
<entity name="User">
<!-- ... -->
<lifecycle-callbacks>
<lifecycle-callback type="prePersist" method="doStuffOnPrePersist"/>
<lifecycle-callback type="prePersist" method="doOtherStuffOnPrePersist"/>
<lifecycle-callback type="preUpdate" method="doStuffOnPreUpdate"/>
</lifecycle-callbacks>
</entity>
</doctrine-mapping>
.. code-block:: yaml
User:
type: entity
fields:
# ...
value:
type: string(255)
lifecycleCallbacks:
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ]
preUpdate: [ doStuffOnPreUpdate ]
Lifecycle Callbacks Event Argument
----------------------------------
@@ -403,6 +416,8 @@ With the additional argument you have access to the
}
}
.. _listening-and-subscribing-to-lifecycle-events:
Listening and subscribing to Lifecycle Events
---------------------------------------------
@@ -413,7 +428,7 @@ behaviors across different entity classes.
Note that they require much more detailed knowledge about the inner
workings of the ``EntityManager`` and ``UnitOfWork`` classes. Please
read the :ref:`reference-events-implementing-listeners` section
read the :ref:`Implementing Event Listeners<reference-events-implementing-listeners>` section
carefully if you are trying to write your own listener.
For event subscribers, there are no surprises. They declare the
@@ -482,8 +497,10 @@ EventManager that is passed to the EntityManager factory:
.. code-block:: php
<?php
use Doctrine\ORM\Events;
$eventManager = new EventManager();
$eventManager->addEventListener(array(Events::preUpdate), new MyEventListener());
$eventManager->addEventListener([Events::preUpdate], new MyEventListener());
$eventManager->addEventSubscriber(new MyEventSubscriber());
$entityManager = EntityManager::create($dbOpts, $config, $eventManager);
@@ -494,7 +511,9 @@ EntityManager was created:
.. code-block:: php
<?php
$entityManager->getEventManager()->addEventListener(array(Events::preUpdate), new MyEventListener());
use Doctrine\ORM\Events;
$entityManager->getEventManager()->addEventListener([Events::preUpdate], new MyEventListener());
$entityManager->getEventManager()->addEventSubscriber(new MyEventSubscriber());
.. _reference-events-implementing-listeners:
@@ -514,6 +533,8 @@ the restrictions apply as well, with the additional restriction
that (prior to version 2.4) you do not have access to the
``EntityManager`` or ``UnitOfWork`` APIs inside these events.
.. _reference-events-pre-persist:
prePersist
~~~~~~~~~~
@@ -539,6 +560,8 @@ The following restrictions apply to ``prePersist``:
- Doctrine will not recognize changes made to relations in a prePersist
event. This includes modifications to
collections such as additions, removals or replacement.
.. _reference-events-pre-remove:
preRemove
~~~~~~~~~
@@ -551,6 +574,8 @@ There are no restrictions to what methods can be called inside the
``preRemove`` event, except when the remove method itself was
called during a flush operation.
.. _reference-events-pre-flush:
preFlush
~~~~~~~~
@@ -573,6 +598,8 @@ result in infinite loop.
}
}
.. _reference-events-on-flush:
onFlush
~~~~~~~
@@ -636,6 +663,8 @@ The following restrictions apply to the onFlush event:
affected entity. This can be done by calling
``$unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity)``.
.. _reference-events-post-flush:
postFlush
~~~~~~~~~
@@ -656,6 +685,8 @@ postFlush
}
}
.. _reference-events-pre-update:
preUpdate
~~~~~~~~~
@@ -740,6 +771,8 @@ Restrictions for this event:
API are strongly discouraged and don't work as expected outside the
flush operation.
.. _reference-events-post-update-remove-persist:
postUpdate, postRemove, postPersist
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -749,6 +782,8 @@ database, but you can use these events to alter non-persistable items,
like non-mapped fields, logging or even associated classes that are
not directly mapped by Doctrine.
.. _reference-events-post-load:
postLoad
~~~~~~~~
@@ -765,7 +800,19 @@ An entity listener is a lifecycle listener class used for an entity.
.. configuration-block::
.. code-block:: php
.. code-block:: attribute
<?php
namespace MyProject\Entity;
use App\EventListener\UserListener;
#[Entity]
#[EntityListeners([UserListener::class])]
class User
{
// ....
}
.. code-block:: annotation
<?php
namespace MyProject\Entity;
@@ -961,6 +1008,8 @@ Implementing your own resolver :
$configurations->setEntityListenerResolver(new MyEntityListenerResolver);
EntityManager::create(.., $configurations, ..);
.. _reference-events-load-class-metadata:
Load ClassMetadata Event
------------------------
@@ -1041,3 +1090,12 @@ and the EntityManager.
$em = $eventArgs->getEntityManager();
}
}
.. _LifecycleEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/LifecycleEventArgs.php
.. _PreUpdateEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php
.. _PreFlushEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/PreFlushEventArgs.php
.. _PostFlushEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/PostFlushEventArgs.php
.. _OnFlushEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/OnFlushEventArgs.php
.. _OnClearEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/OnClearEventArgs.php
.. _LoadClassMetadataEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php
.. _OnClassMetadataNotFoundEventArgs: https://github.com/doctrine/orm/blob/HEAD/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php
+51 -61
View File
@@ -31,31 +31,31 @@ Each cache region resides in a specific cache namespace and has its own lifetime
Notice that when caching collection and queries only identifiers are stored.
The entity values will be stored in its own region
Something like below for an entity region :
Something like below for an entity region:
.. code-block:: php
<?php
[
'region_name:entity_1_hash' => ['id'=> 1, 'name' => 'FooBar', 'associationName'=>null],
'region_name:entity_2_hash' => ['id'=> 2, 'name' => 'Foo', 'associationName'=>['id'=>11]],
'region_name:entity_3_hash' => ['id'=> 3, 'name' => 'Bar', 'associationName'=>['id'=>22]]
'region_name:entity_1_hash' => ['id' => 1, 'name' => 'FooBar', 'associationName' => null],
'region_name:entity_2_hash' => ['id' => 2, 'name' => 'Foo', 'associationName' => ['id' => 11]],
'region_name:entity_3_hash' => ['id' => 3, 'name' => 'Bar', 'associationName' => ['id' => 22]]
];
If the entity holds a collection that also needs to be cached.
An collection region could look something like :
An collection region could look something like:
.. code-block:: php
<?php
[
'region_name:entity_1_coll_assoc_name_hash' => ['ownerId'=> 1, 'list' => [1, 2, 3]],
'region_name:entity_2_coll_assoc_name_hash' => ['ownerId'=> 2, 'list' => [2, 3]],
'region_name:entity_3_coll_assoc_name_hash' => ['ownerId'=> 3, 'list' => [2, 4]]
'region_name:entity_1_coll_assoc_name_hash' => ['ownerId' => 1, 'list' => [1, 2, 3]],
'region_name:entity_2_coll_assoc_name_hash' => ['ownerId' => 2, 'list' => [2, 3]],
'region_name:entity_3_coll_assoc_name_hash' => ['ownerId' => 3, 'list' => [2, 4]]
];
A query region might be something like :
A query region might be something like:
.. code-block:: php
@@ -93,8 +93,6 @@ Cache region
``Doctrine\ORM\Cache\Region`` defines a contract for accessing a particular
cache region.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Region.html>`_.
Concurrent cache region
~~~~~~~~~~~~~~~~~~~~~~~
@@ -105,8 +103,6 @@ If you want to use an ``READ_WRITE`` cache, you should consider providing your o
``Doctrine\ORM\Cache\ConcurrentRegion`` defines a contract for concurrently managed data region.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/ConcurrentRegion.html>`_.
Timestamp region
~~~~~~~~~~~~~~~~
@@ -114,8 +110,6 @@ Timestamp region
Tracks the timestamps of the most recent updates to particular entity.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/TimestampRegion.html>`_.
.. _reference-second-level-cache-mode:
Caching mode
@@ -132,7 +126,7 @@ Caching mode
* Read Write Cache doesnt employ any locks but can do reads, inserts, updates and deletes.
* Good if the application needs to update data rarely.
* ``READ_WRITE``
@@ -147,21 +141,21 @@ Built-in cached persisters
Cached persisters are responsible to access cache regions.
+-----------------------+-------------------------------------------------------------------------------------------+
| Cache Usage | Persister |
+=======================+===========================================================================================+
| READ_ONLY | Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadOnlyCachedEntityPersister |
+-----------------------+-------------------------------------------------------------------------------------------+
| READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\Entity\\ReadWriteCachedEntityPersister |
+-----------------------+-------------------------------------------------------------------------------------------+
| NONSTRICT_READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\Entity\\NonStrictReadWriteCachedEntityPersister |
+-----------------------+-------------------------------------------------------------------------------------------+
| READ_ONLY | Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadOnlyCachedCollectionPersister |
+-----------------------+-------------------------------------------------------------------------------------------+
| READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\Collection\\ReadWriteCachedCollectionPersister |
+-----------------------+-------------------------------------------------------------------------------------------+
| NONSTRICT_READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\Collection\\NonStrictReadWriteCachedCollectionPersister |
+-----------------------+-------------------------------------------------------------------------------------------+
+-----------------------+------------------------------------------------------------------------------------------+
| Cache Usage | Persister |
+=======================+==========================================================================================+
| READ_ONLY | ``Doctrine\ORM\Cache\Persister\Entity\ReadOnlyCachedEntityPersister`` |
+-----------------------+------------------------------------------------------------------------------------------+
| READ_WRITE | ``Doctrine\ORM\Cache\Persister\Entity\ReadWriteCachedEntityPersister`` |
+-----------------------+------------------------------------------------------------------------------------------+
| NONSTRICT_READ_WRITE | ``Doctrine\ORM\Cache\Persister\Entity\NonStrictReadWriteCachedEntityPersister`` |
+-----------------------+------------------------------------------------------------------------------------------+
| READ_ONLY | ``Doctrine\ORM\Cache\Persister\Collection\ReadOnlyCachedCollectionPersister`` |
+-----------------------+------------------------------------------------------------------------------------------+
| READ_WRITE | ``Doctrine\ORM\Cache\Persister\Collection\ReadWriteCachedCollectionPersister`` |
+-----------------------+------------------------------------------------------------------------------------------+
| NONSTRICT_READ_WRITE | ``Doctrine\ORM\Cache\Persister\Collection\NonStrictReadWriteCachedCollectionPersister`` |
+-----------------------+------------------------------------------------------------------------------------------+
Configuration
-------------
@@ -172,7 +166,7 @@ Enable Second Level Cache
~~~~~~~~~~~~~~~~~~~~~~~~~
To enable the second-level-cache, you should provide a cache factory.
``\Doctrine\ORM\Cache\DefaultCacheFactory`` is the default implementation.
``Doctrine\ORM\Cache\DefaultCacheFactory`` is the default implementation.
.. code-block:: php
@@ -196,7 +190,7 @@ Cache Factory
Cache Factory is the main point of extension.
It allows you to provide a specific implementation of the following components :
It allows you to provide a specific implementation of the following components:
``QueryCache``
stores and retrieves query cache results.
@@ -209,8 +203,6 @@ It allows you to provide a specific implementation of the following components :
``CollectionHydrator``
transforms collections into cache entries and cache entries into collections
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/DefaultCacheFactory.html>`_.
Region Lifetime
~~~~~~~~~~~~~~~
@@ -234,12 +226,12 @@ Cache Log
~~~~~~~~~
By providing a cache logger you should be able to get information about all cache operations such as hits, misses and puts.
``\Doctrine\ORM\Cache\Logging\StatisticsCacheLogger`` is a built-in implementation that provides basic statistics.
``Doctrine\ORM\Cache\Logging\StatisticsCacheLogger`` is a built-in implementation that provides basic statistics.
.. code-block:: php
<?php
/* @var $config \Doctrine\ORM\Configuration */
/** @var \Doctrine\ORM\Configuration $config */
$logger = new \Doctrine\ORM\Cache\Logging\StatisticsCacheLogger();
// Cache logger
@@ -269,12 +261,9 @@ By providing a cache logger you should be able to get information about all cach
$logger->getMissCount();
If you want to get more information you should implement
``\Doctrine\ORM\Cache\Logging\CacheLogger`` and collect
``Doctrine\ORM\Cache\Logging\CacheLogger`` and collect
all the information you want.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Logging/CacheLogger.html>`_.
Entity cache definition
-----------------------
* Entity cache configuration allows you to define the caching strategy and region for an entity.
@@ -330,8 +319,8 @@ level cache region.
Country:
type: entity
cache:
usage : READ_ONLY
region : my_entity_region
usage: READ_ONLY
region: my_entity_region
id:
id:
type: integer
@@ -401,7 +390,7 @@ It caches the primary keys of association and cache each element will be cached
</id>
<field name="name" type="string" column="name"/>
<many-to-one field="country" target-entity="Country">
<cache usage="NONSTRICT_READ_WRITE" />
@@ -421,7 +410,7 @@ It caches the primary keys of association and cache each element will be cached
State:
type: entity
cache:
usage : NONSTRICT_READ_WRITE
usage: NONSTRICT_READ_WRITE
id:
id:
type: integer
@@ -439,17 +428,18 @@ It caches the primary keys of association and cache each element will be cached
country_id:
referencedColumnName: id
cache:
usage : NONSTRICT_READ_WRITE
usage: NONSTRICT_READ_WRITE
oneToMany:
cities:
targetEntity:City
mappedBy: state
cache:
usage : NONSTRICT_READ_WRITE
usage: NONSTRICT_READ_WRITE
.. note::
> Note: for this to work, the target entity must also be marked as cacheable.
for this to work, the target entity must also be marked as cacheable.
Cache usage
~~~~~~~~~~~
@@ -466,8 +456,8 @@ Basic entity cache
$country1 = $em->find('Country', 1); // Retrieve item from cache
$country1->setName("New Name");
$country1->setName('New Name');
$em->flush(); // Hit database to update the row and update cache
$em->clear(); // Clear entity manager
@@ -492,7 +482,7 @@ Association cache
$state = $em->find('State', 1);
// Hit database to update the row and update cache entry
$state->setName("New Name");
$state->setName('New Name');
$em->persist($state);
$em->flush();
@@ -543,14 +533,14 @@ The query cache stores the results of the query but as identifiers, entity value
.. code-block:: php
<?php
/* @var $em \Doctrine\ORM\EntityManager */
/** @var \Doctrine\ORM\EntityManager $em */
// Execute database query, store query cache and entity cache
$result1 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
->setCacheable(true)
->getResult();
$em->clear()
$em->clear();
// Check if query result is valid and load entities from cache
$result2 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
@@ -570,10 +560,10 @@ The Cache Mode controls how a particular query interacts with the second-level c
.. code-block:: php
<?php
/* @var $em \Doctrine\ORM\EntityManager */
/** @var \Doctrine\ORM\EntityManager $em */
// Will refresh the query cache and all entities the cache as it reads from the database.
$result1 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name')
->setCacheMode(Cache::MODE_GET)
->setCacheMode(\Doctrine\ORM\Cache::MODE_GET)
->setCacheable(true)
->getResult();
@@ -597,7 +587,7 @@ Execute the ``UPDATE`` and invalidate ``all cache entries`` using ``Query::HINT_
<?php
// Execute and invalidate
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
->setHint(Query::HINT_CACHE_EVICT, true)
->setHint(\Doctrine\ORM\Query::HINT_CACHE_EVICT, true)
->execute();
@@ -659,7 +649,7 @@ However, you can use the cache API to check / invalidate cache entries.
.. code-block:: php
<?php
/* @var $cache \Doctrine\ORM\Cache */
/** @var \Doctrine\ORM\Cache $cache */
$cache = $em->getCache();
$cache->containsEntity('Entity\State', 1) // Check if the cache exists
@@ -704,19 +694,19 @@ For performance reasons the cache API does not extract from composite primary ke
}
// Supported
/* @var $article Article */
/** @var Article $article */
$article = $em->find('Article', 1);
// Supported
/* @var $article Article */
/** @var Article $article */
$article = $em->find('Article', $article);
// Supported
$id = array('source' => 1, 'target' => 2);
$id = ['source' => 1, 'target' => 2];
$reference = $em->find('Reference', $id);
// NOT Supported
$id = array('source' => new Article(1), 'target' => new Article(2));
$id = ['source' => new Article(1), 'target' => new Article(2)];
$reference = $em->find('Reference', $id);
Distributed environments
@@ -40,7 +40,7 @@ easily using a combination of ``count`` and ``slice``.
``removeElement`` directly issued DELETE queries to the database from
version 2.4.0 to 2.7.0. This circumvents the flush operation and might run
outside a transactional boundary if you don't create one yourself. We
consider this a critical bug in the assumptio of how the ORM works and
consider this a critical bug in the assumption of how the ORM works and
reverted ``removeElement`` EXTRA_LAZY behavior in 2.7.1.
+12 -3
View File
@@ -81,7 +81,8 @@ that directory with the following contents:
{
"require": {
"doctrine/orm": "^2.6.2",
"doctrine/orm": "^2.10.2",
"doctrine/dbal": "^3.1.1",
"symfony/yaml": "2.*",
"symfony/cache": "^5.3"
},
@@ -112,6 +113,14 @@ Add the following directories:
.. note::
The YAML driver is deprecated and will be removed in version 3.0.
It is strongly recommended to switch to one of the other mappings.
.. note::
It is strongly recommended that you require ``doctrine/dbal`` in your
``composer.json`` as well, because using the ORM means mapping objects
and their fields to database tables and their columns, and that
requires mentioning so-called types that are defined in ``doctrine/dbal``
in your application. Having an explicit requirement means you control
when the upgrade to the next major version happens, so that you can
do the necessary changes in your application beforehand.
Obtaining the EntityManager
---------------------------
@@ -642,7 +651,7 @@ Let's continue by creating a script to display the name of a product based on it
echo sprintf("-%s\n", $product->getName());
Next we'll update a product's name, given its id. This simple example will
help demonstrate Doctrine's implementation of the UnitOfWork pattern. Doctrine
help demonstrate Doctrine's implementation of the :ref:`UnitOfWork pattern <unit-of-work>`. Doctrine
keeps track of all the entities that were retrieved from the Entity Manager,
and can detect when any of those entities' properties have been modified.
As a result, rather than needing to call ``persist($entity)`` for each individual
@@ -1334,7 +1343,7 @@ call this script as follows:
php create_bug.php 1 1 1
See how simple it is to relate a Bug, Reporter, Engineer and Products?
Also recall that thanks to the UnitOfWork pattern, Doctrine will detect
Also recall that thanks to the :ref:`UnitOfWork pattern <unit-of-work>`, Doctrine will detect
these relations and update all of the modified entities in the database
automatically when ``flush()`` is called.
+85 -4
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,10 +291,11 @@
<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" />
<xs:attribute name="column" type="xs:NMTOKEN" />
<xs:attribute name="column" type="orm:columntoken" />
<xs:attribute name="length" type="xs:NMTOKEN" />
<xs:attribute name="unique" type="xs:boolean" default="false" />
<xs:attribute name="nullable" type="xs:boolean" default="false" />
@@ -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="columns" type="xs:string" use="optional"/>
<xs:attribute name="fields" type="xs:string" use="optional"/>
<xs:attribute name="flags" type="xs:string" use="optional"/>
<xs:anyAttribute namespace="##other"/>
</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,25 +398,30 @@
<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" />
<xs:attribute name="column" type="xs:NMTOKEN" />
<xs:attribute name="column" type="orm:columntoken" />
<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">
@@ -438,12 +497,19 @@
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="columntoken" id="columntoken">
<xs:restriction base="xs:token">
<xs:pattern value="[-._:A-Za-z0-9`]+" id="columntoken.pattern"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="many-to-many">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="cache" type="orm:cache" minOccurs="0" maxOccurs="1"/>
<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 +518,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 +526,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 +534,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 +544,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 +562,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 +572,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 +587,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 +600,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,9 +615,10 @@
<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" />
<xs:attribute name="column" type="orm:columntoken" />
<xs:attribute name="length" type="xs:NMTOKEN" />
<xs:attribute name="unique" type="xs:boolean" default="false" />
<xs:attribute name="nullable" type="xs:boolean" default="false" />
@@ -546,6 +626,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>
+2 -1
View File
@@ -1253,7 +1253,8 @@ abstract class AbstractQuery
* Will return the configured id if it exists otherwise a hash will be
* automatically generated for you.
*
* @return array<string, string> ($key, $hash)
* @return string[] ($key, $hash)
* @psalm-return array{string, string} ($key, $hash)
*/
protected function getHydrationCacheId()
{
@@ -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);
@@ -5,9 +5,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Cache\Persister\Entity;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\Cache\CacheException;
use Doctrine\ORM\Cache\Exception\CannotUpdateReadOnlyEntity;
use Doctrine\ORM\Utility\StaticClassNameConverter;
/**
* Specific read-only region entity persister
+1 -2
View File
@@ -19,6 +19,7 @@ use Doctrine\Persistence\ObjectManager;
* EntityManager interface
*
* @method Mapping\ClassMetadataFactory getMetadataFactory()
* @method mixed wrapInTransaction(callable $func)
*/
interface EntityManagerInterface extends ObjectManager
{
@@ -329,11 +330,9 @@ interface EntityManagerInterface extends ObjectManager
* {@inheritDoc}
*
* @psalm-param string|class-string<T> $className
* @phpstan-param string $className
*
* @return Mapping\ClassMetadata
* @psalm-return Mapping\ClassMetadata<T>
* @phpstan-return Mapping\ClassMetadata<object>
*
* @psalm-template T of object
*/
+8 -4
View File
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Id;
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
use Doctrine\ORM\EntityManager;
use Serializable;
@@ -54,11 +55,14 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
{
if ($this->_maxValue === null || $this->_nextValue === $this->_maxValue) {
// Allocate new values
$conn = $em->getConnection();
$sql = $conn->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName);
$connection = $em->getConnection();
$sql = $connection->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName);
// Using `query` to force usage of the master server in MasterSlaveConnection
$this->_nextValue = (int) $conn->executeQuery($sql)->fetchOne();
if ($connection instanceof PrimaryReadReplicaConnection) {
$connection->ensureConnectedToPrimary();
}
$this->_nextValue = (int) $connection->executeQuery($sql)->fetchOne();
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
}
+8 -3
View File
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Id;
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\EntityManager;
@@ -39,9 +40,13 @@ class UuidGenerator extends AbstractIdGenerator
*/
public function generate(EntityManager $em, $entity)
{
$conn = $em->getConnection();
$sql = 'SELECT ' . $conn->getDatabasePlatform()->getGuidExpression();
$connection = $em->getConnection();
$sql = 'SELECT ' . $connection->getDatabasePlatform()->getGuidExpression();
return $conn->executeQuery($sql)->fetchOne();
if ($connection instanceof PrimaryReadReplicaConnection) {
$connection->ensureConnectedToPrimary();
}
return $connection->executeQuery($sql)->fetchOne();
}
}
@@ -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;
}
@@ -79,7 +79,8 @@ class SimpleObjectHydrator extends AbstractHydrator
// We need to find the correct entity class name if we have inheritance in resultset
if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
$discrColumnName = $this->getSQLResultCasing($this->_platform, $this->class->discriminatorColumn['name']);
$discrColumn = $this->class->getDiscriminatorColumn();
$discrColumnName = $this->getSQLResultCasing($this->_platform, $discrColumn['name']);
// Find mapped discriminator column from the result set.
$metaMappingDiscrColumnName = array_search($discrColumnName, $this->resultSetMapping()->metaMappings, true);
@@ -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);
+45 -34
View File
@@ -20,6 +20,7 @@ use Doctrine\ORM\Id\AbstractIdGenerator;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\ReflectionService;
use InvalidArgumentException;
use LogicException;
use ReflectionClass;
use ReflectionNamedType;
use ReflectionProperty;
@@ -69,6 +70,26 @@ use const PHP_VERSION_ID;
*
* @template-covariant T of object
* @template-implements ClassMetadata<T>
* @psalm-type FieldMapping = array{
* type: string,
* fieldName: string,
* columnName?: string,
* length?: int,
* id?: bool,
* nullable?: bool,
* columnDefinition?: string,
* precision?: int,
* scale?: int,
* unique?: string,
* inherited?: class-string,
* originalClass?: class-string,
* originalField?: string,
* quoted?: bool,
* requireSQLConversion?: bool,
* declared?: class-string,
* declaredField?: string,
* options?: array<string, mixed>
* }
*/
class ClassMetadataInfo implements ClassMetadata
{
@@ -426,25 +447,7 @@ class ClassMetadataInfo implements ClassMetadata
* Whether a unique constraint should be generated for the column.
*
* @var mixed[]
* @psalm-var array<string, array{
* type: string,
* fieldName: string,
* columnName?: string,
* length?: int,
* id?: bool,
* nullable?: bool,
* columnDefinition?: string,
* precision?: int,
* scale?: int,
* unique?: string,
* inherited?: class-string,
* originalClass?: class-string,
* originalField?: string,
* quoted?: bool,
* requireSQLConversion?: bool,
* declaredField?: string,
* options: array<mixed>
* }>
* @psalm-var array<string, FieldMapping>
*/
public $fieldMappings = [];
@@ -495,7 +498,7 @@ class ClassMetadataInfo implements ClassMetadata
* READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
* inheritance mappings.
*
* @psalm-var array<string, mixed>
* @psalm-var array<string, mixed>|null
*/
public $discriminatorColumn;
@@ -509,7 +512,14 @@ class ClassMetadataInfo implements ClassMetadata
* uniqueConstraints => array
*
* @var mixed[]
* @psalm-var array{name: string, schema: string, indexes: array, uniqueConstraints: array}
* @psalm-var array{
* name: string,
* schema: string,
* indexes: array,
* uniqueConstraints: array,
* options: array<string, mixed>,
* quoted?: bool
* }
*/
public $table;
@@ -665,7 +675,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* The ReflectionClass instance of the mapped class.
*
* @var ReflectionClass
* @var ReflectionClass|null
*/
public $reflClass;
@@ -1276,18 +1286,7 @@ class ClassMetadataInfo implements ClassMetadata
* @param string $fieldName The field name.
*
* @return mixed[] The field mapping.
* @psalm-return array{
* type: string,
* fieldName: string,
* columnName?: string,
* inherited?: class-string,
* nullable?: bool,
* originalClass?: class-string,
* originalField?: string,
* scale?: int,
* precision?: int,
* length?: int
* }
* @psalm-return FieldMapping
*
* @throws MappingException
*/
@@ -3090,6 +3089,18 @@ class ClassMetadataInfo implements ClassMetadata
}
}
/**
* @return array<string, mixed>
*/
final public function getDiscriminatorColumn(): array
{
if ($this->discriminatorColumn === null) {
throw new LogicException('The discriminator column was not set.');
}
return $this->discriminatorColumn;
}
/**
* Sets the discriminator values used by this class.
* Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
+5 -5
View File
@@ -15,26 +15,26 @@ use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
#[Attribute(Attribute::TARGET_PROPERTY)]
final class Column implements Annotation
{
/** @var string */
/** @var string|null */
public $name;
/** @var mixed */
public $type;
/** @var int */
/** @var int|null */
public $length;
/**
* The precision for a decimal (exact numeric) column (Applies only for decimal column).
*
* @var int
* @var int|null
*/
public $precision = 0;
/**
* The scale for a decimal (exact numeric) column (Applies only for decimal column).
*
* @var int
* @var int|null
*/
public $scale = 0;
@@ -47,7 +47,7 @@ final class Column implements Annotation
/** @var array<string,mixed> */
public $options = [];
/** @var string */
/** @var string|null */
public $columnDefinition;
/**
@@ -249,7 +249,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
$field = $this->quoteStrategy->getColumnName($name, $targetClass, $this->platform);
$whereClauses[] = sprintf('te.%s %s ?', $field, $operator);
$params[] = $value;
$paramTypes[] = PersisterHelper::getTypeOfColumn($field, $targetClass, $this->em);
$paramTypes[] = PersisterHelper::getTypeOfField($name, $targetClass, $this->em)[0];
}
$tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);
@@ -24,7 +24,7 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
$data = parent::prepareInsertData($entity);
// Populate the discriminator column
$discColumn = $this->class->discriminatorColumn;
$discColumn = $this->class->getDiscriminatorColumn();
$this->columnTypes[$discColumn['name']] = $discColumn['type'];
$data[$this->getDiscriminatorColumnTableName()][$discColumn['name']] = $this->class->discriminatorValue;
@@ -13,7 +13,6 @@ use Doctrine\ORM\Utility\PersisterHelper;
use function array_combine;
use function implode;
use function is_array;
/**
* The joined subclass persister maps a single entity instance to several tables in the
@@ -412,14 +411,15 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
}
$columnList = [];
$discrColumn = $this->class->discriminatorColumn['name'];
$discrColumnType = $this->class->discriminatorColumn['type'];
$discrColumn = $this->class->getDiscriminatorColumn();
$discrColumnName = $discrColumn['name'];
$discrColumnType = $discrColumn['type'];
$baseTableAlias = $this->getSQLTableAlias($this->class->name);
$resultColumnName = $this->getSQLResultCasing($this->platform, $discrColumn);
$resultColumnName = $this->getSQLResultCasing($this->platform, $discrColumnName);
$this->currentPersisterContext->rsm->addEntityResult($this->class->name, 'r');
$this->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName);
$this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumn, false, $discrColumnType);
$this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumnName, false, $discrColumnType);
// Add regular columns
foreach ($this->class->fieldMappings as $fieldName => $mapping) {
@@ -457,7 +457,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
? $baseTableAlias
: $this->getSQLTableAlias($this->class->rootEntityName);
$columnList[] = $tableAlias . '.' . $discrColumn;
$columnList[] = $tableAlias . '.' . $discrColumnName;
// sub tables
foreach ($this->class->subClasses as $subClassName) {
@@ -540,7 +540,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Add discriminator column if it is the topmost class.
if ($this->class->name === $this->class->rootEntityName) {
$columns[] = $this->class->discriminatorColumn['name'];
$columns[] = $this->class->getDiscriminatorColumn()['name'];
}
return $columns;
@@ -44,16 +44,17 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
$rootClass = $this->em->getClassMetadata($this->class->rootEntityName);
$tableAlias = $this->getSQLTableAlias($rootClass->name);
// Append discriminator column
$discrColumn = $this->class->discriminatorColumn['name'];
$discrColumnType = $this->class->discriminatorColumn['type'];
// Append discriminator column
$discrColumn = $this->class->getDiscriminatorColumn();
$discrColumnName = $discrColumn['name'];
$discrColumnType = $discrColumn['type'];
$columnList[] = $tableAlias . '.' . $discrColumn;
$columnList[] = $tableAlias . '.' . $discrColumnName;
$resultColumnName = $this->getSQLResultCasing($this->platform, $discrColumn);
$resultColumnName = $this->getSQLResultCasing($this->platform, $discrColumnName);
$this->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName);
$this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumn, false, $discrColumnType);
$this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumnName, false, $discrColumnType);
// Append subclass columns
foreach ($this->class->subClasses as $subClassName) {
@@ -100,7 +101,7 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
$columns = parent::getInsertColumnList();
// Add discriminator column to the INSERT SQL
$columns[] = $this->class->discriminatorColumn['name'];
$columns[] = $this->class->getDiscriminatorColumn()['name'];
return $columns;
}
@@ -158,11 +159,12 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
$values[] = $this->conn->quote($discrValues[$subclassName]);
}
$discColumnName = $this->class->getDiscriminatorColumn()['name'];
$values = implode(', ', $values);
$discColumn = $this->class->discriminatorColumn['name'];
$tableAlias = $this->getSQLTableAlias($this->class->name);
return $tableAlias . '.' . $discColumn . ' IN (' . $values . ')';
return $tableAlias . '.' . $discColumnName . ' IN (' . $values . ')';
}
/**
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Query\AST;
use Doctrine\ORM\Query\AST\DeleteStatement;
@@ -49,6 +50,10 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
$platform = $conn->getDatabasePlatform();
$quoteStrategy = $em->getConfiguration()->getQuoteStrategy();
if ($conn instanceof PrimaryReadReplicaConnection) {
$conn->ensureConnectedToPrimary();
}
$primaryClass = $em->getClassMetadata($AST->deleteClause->abstractSchemaName);
$primaryDqlAlias = $AST->deleteClause->aliasIdentificationVariable;
$rootClass = $em->getClassMetadata($primaryClass->rootEntityName);
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Query\AST;
use Doctrine\ORM\Query\AST\UpdateStatement;
@@ -55,6 +56,10 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
$platform = $conn->getDatabasePlatform();
$quoteStrategy = $em->getConfiguration()->getQuoteStrategy();
if ($conn instanceof PrimaryReadReplicaConnection) {
$conn->ensureConnectedToPrimary();
}
$updateClause = $AST->updateClause;
$primaryClass = $sqlWalker->getEntityManager()->getClassMetadata($updateClause->abstractSchemaName);
$rootClass = $em->getClassMetadata($primaryClass->rootEntityName);
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
use Doctrine\ORM\Query\AST;
use Doctrine\ORM\Query\SqlWalker;
@@ -37,6 +38,10 @@ class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor
*/
public function execute(Connection $conn, array $params, array $types)
{
if ($conn instanceof PrimaryReadReplicaConnection) {
$conn->ensureConnectedToPrimary();
}
return $conn->executeStatement($this->_sqlStatements, $params, $types);
}
}
@@ -195,6 +195,7 @@ abstract class SQLFilter
* Gets the SQL query part to add to a query.
*
* @param string $targetTableAlias
* @psalm-param ClassMetadata<object> $targetEntity
*
* @return string The constraint SQL if there is available, empty string otherwise.
*/
+1 -1
View File
@@ -214,7 +214,7 @@ class Parser
*/
private $customOutputWalker;
/** @psalm-var list<AST\SelectExpression> */
/** @psalm-var array<string, AST\SelectExpression> */
private $identVariableExpressions = [];
/**
@@ -369,7 +369,7 @@ class ResultSetMappingBuilder extends ResultSetMapping
{
if (isset($entityMapping['discriminatorColumn']) && $entityMapping['discriminatorColumn']) {
$discriminatorColumn = $entityMapping['discriminatorColumn'];
$discriminatorType = $classMetadata->discriminatorColumn['type'];
$discriminatorType = $classMetadata->getDiscriminatorColumn()['type'];
$this->setDiscriminatorColumn($alias, $discriminatorColumn);
$this->addMetaResult($alias, $discriminatorColumn, $discriminatorColumn, false, $discriminatorType);
+3 -3
View File
@@ -465,7 +465,7 @@ class SqlWalker implements TreeWalker
? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'
: '';
$sqlParts[] = $sqlTableAlias . $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')';
$sqlParts[] = $sqlTableAlias . $class->getDiscriminatorColumn()['name'] . ' IN (' . implode(', ', $values) . ')';
}
$sql = implode(' AND ', $sqlParts);
@@ -739,7 +739,7 @@ class SqlWalker implements TreeWalker
// Add discriminator columns to SQL
$rootClass = $this->em->getClassMetadata($class->rootEntityName);
$tblAlias = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias);
$discrColumn = $rootClass->discriminatorColumn;
$discrColumn = $rootClass->getDiscriminatorColumn();
$columnAlias = $this->getSQLColumnAlias($discrColumn['name']);
$sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias;
@@ -2091,7 +2091,7 @@ class SqlWalker implements TreeWalker
$sql .= $this->getSQLTableAlias($discrClass->getTableName(), $dqlAlias) . '.';
}
$sql .= $class->discriminatorColumn['name'] . ($instanceOfExpr->not ? ' NOT IN ' : ' IN ');
$sql .= $class->getDiscriminatorColumn()['name'] . ($instanceOfExpr->not ? ' NOT IN ' : ' IN ');
$sql .= $this->getChildDiscriminatorsFromClassMetadata($discrClass, $instanceOfExpr);
return $sql;
@@ -151,7 +151,7 @@ EOT
if (empty($metadata)) {
$ui->success('No Metadata Classes to process.');
return;
return 0;
}
foreach ($metadata as $class) {
+5 -1
View File
@@ -1140,7 +1140,11 @@ public function __construct(<params>)
return '';
}
$discrColumn = $metadata->discriminatorColumn;
$discrColumn = $metadata->discriminatorColumn;
if ($discrColumn === null) {
return '';
}
$columnDefinition = 'name="' . $discrColumn['name']
. '", type="' . $discrColumn['type']
. '", length=' . $discrColumn['length'];
@@ -31,7 +31,7 @@ use const DIRECTORY_SEPARATOR;
*/
class EntityRepositoryGenerator
{
/** @psalm-var class-string */
/** @psalm-var class-string|null */
private $repositoryName;
/** @var string */
+7
View File
@@ -9,6 +9,7 @@ use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\EventManager;
use Doctrine\Common\Proxy\Proxy;
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
use Doctrine\DBAL\LockMode;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\Cache\Persister\CachedPersister;
@@ -338,6 +339,12 @@ class UnitOfWork implements PropertyChangedListener
*/
public function commit($entity = null)
{
$connection = $this->em->getConnection();
if ($connection instanceof PrimaryReadReplicaConnection) {
$connection->ensureConnectedToPrimary();
}
// Raise preFlush
if ($this->evm->hasListeners(Events::preFlush)) {
$this->evm->dispatchEvent(Events::preFlush, new PreFlushEventArgs($this->em));
+189 -39
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
@@ -105,6 +100,11 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php
-
message: "#^PHPDoc type Doctrine\\\\Common\\\\Cache\\\\Cache\\|Doctrine\\\\Common\\\\Cache\\\\MultiGetCache of property Doctrine\\\\ORM\\\\Cache\\\\Region\\\\DefaultMultiGetRegion\\:\\:\\$cache is not covariant with PHPDoc type Doctrine\\\\Common\\\\Cache\\\\Cache of overridden property Doctrine\\\\ORM\\\\Cache\\\\Region\\\\DefaultRegion\\:\\:\\$cache\\.$#"
count: 1
path: lib/Doctrine/ORM/Cache/Region/DefaultMultiGetRegion.php
-
message: "#^Method Doctrine\\\\ORM\\\\Cache\\\\Region\\\\DefaultRegion\\:\\:getCache\\(\\) should return Doctrine\\\\Common\\\\Cache\\\\CacheProvider but returns Doctrine\\\\Common\\\\Cache\\\\Cache\\.$#"
count: 1
@@ -185,6 +185,11 @@ parameters:
count: 2
path: lib/Doctrine/ORM/EntityManager.php
-
message: "#^Template type T of method Doctrine\\\\ORM\\\\EntityManagerInterface\\:\\:getClassMetadata\\(\\) is not referenced in a parameter\\.$#"
count: 1
path: lib/Doctrine/ORM/EntityManagerInterface.php
-
message: "#^Method Doctrine\\\\ORM\\\\EntityRepository\\:\\:findOneBy\\(\\) should return T\\|null but returns object\\|null\\.$#"
count: 1
@@ -240,6 +245,16 @@ parameters:
count: 1
path: lib/Doctrine/ORM/LazyCriteriaCollection.php
-
message: "#^Offset 'indexes' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php
-
message: "#^Offset 'uniqueConstraints' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php
-
message: "#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$cache\\.$#"
count: 2
@@ -412,7 +427,7 @@ parameters:
-
message: "#^If condition is always true\\.$#"
count: 2
count: 1
path: lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
-
@@ -422,7 +437,12 @@ parameters:
-
message: "#^Negated boolean expression is always false\\.$#"
count: 3
count: 1
path: lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
-
message: "#^Offset 'indexes'\\|'uniqueConstraints' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
-
@@ -530,21 +550,6 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
-
message: "#^Array \\(array\\('name' \\=\\> string, 'schema' \\=\\> string, 'indexes' \\=\\> array, 'uniqueConstraints' \\=\\> array\\)\\) does not accept key 'options'\\.$#"
count: 1
path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
-
message: "#^Array \\(array\\('name' \\=\\> string, 'schema' \\=\\> string, 'indexes' \\=\\> array, 'uniqueConstraints' \\=\\> array\\)\\) does not accept key 'quoted'\\.$#"
count: 2
path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
-
message: "#^Array \\(array\\<string, array\\|string\\>\\) does not accept true\\.$#"
count: 1
path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
-
message: "#^Call to an undefined method ReflectionProperty\\:\\:getType\\(\\)\\.$#"
count: 3
@@ -566,13 +571,13 @@ parameters:
path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
-
message: "#^Parameter \\#2 \\$type of static method Doctrine\\\\ORM\\\\Mapping\\\\MappingException\\:\\:invalidInheritanceType\\(\\) expects string, int given\\.$#"
message: "#^Offset 'schema' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} on left side of \\?\\? always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
-
message: "#^Result of && is always false\\.$#"
count: 2
message: "#^Parameter \\#2 \\$type of static method Doctrine\\\\ORM\\\\Mapping\\\\MappingException\\:\\:invalidInheritanceType\\(\\) expects string, int given\\.$#"
count: 1
path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
-
@@ -770,6 +775,11 @@ parameters:
count: 2
path: lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php
-
message: "#^PHPDoc type array\\<string, int\\> of property Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\AttributeDriver\\:\\:\\$entityAnnotationClasses is not covariant with PHPDoc type array\\<class\\-string, bool\\|int\\> of overridden property Doctrine\\\\Persistence\\\\Mapping\\\\Driver\\\\AnnotationDriver\\:\\:\\$entityAnnotationClasses\\.$#"
count: 1
path: lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php
-
message: "#^Parameter \\#1 \\$metadata of static method Doctrine\\\\ORM\\\\Mapping\\\\Builder\\\\EntityListenerBuilder\\:\\:bindEntityListener\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata, Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo&Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\<T of object\\> given\\.$#"
count: 1
@@ -965,6 +975,11 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
-
message: "#^Offset 'version' on \\*NEVER\\* in isset\\(\\) always exists and is always null\\.$#"
count: 1
path: lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
-
message: "#^Parameter \\#1 \\$metadata of static method Doctrine\\\\ORM\\\\Mapping\\\\Builder\\\\EntityListenerBuilder\\:\\:bindEntityListener\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata, Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\<T of object\\> given\\.$#"
count: 1
@@ -1120,6 +1135,11 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php
-
message: "#^Offset 'usage' on array\\{usage\\: string, region\\?\\: string\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php
-
message: "#^Parameter \\#1 \\$metadata of static method Doctrine\\\\ORM\\\\Mapping\\\\Builder\\\\EntityListenerBuilder\\:\\:bindEntityListener\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata, Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\<T of object\\> given\\.$#"
count: 1
@@ -1385,15 +1405,30 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php
-
message: "#^PHPDoc type array\\<string\\> of property Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Andx\\:\\:\\$allowedClasses is not covariant with PHPDoc type array\\<int, class\\-string\\> of overridden property Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Base\\:\\:\\$allowedClasses\\.$#"
count: 1
path: lib/Doctrine/ORM/Query/Expr/Andx.php
-
message: "#^PHPDoc type array\\<string\\> of property Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Orx\\:\\:\\$allowedClasses is not covariant with PHPDoc type array\\<int, class\\-string\\> of overridden property Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Base\\:\\:\\$allowedClasses\\.$#"
count: 1
path: lib/Doctrine/ORM/Query/Expr/Orx.php
-
message: "#^PHPDoc type array\\<string\\> of property Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Select\\:\\:\\$allowedClasses is not covariant with PHPDoc type array\\<int, class\\-string\\> of overridden property Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Base\\:\\:\\$allowedClasses\\.$#"
count: 1
path: lib/Doctrine/ORM/Query/Expr/Select.php
-
message: "#^Property Doctrine\\\\ORM\\\\Query\\\\FilterCollection\\:\\:\\$em \\(Doctrine\\\\ORM\\\\EntityManager\\) does not accept Doctrine\\\\ORM\\\\EntityManagerInterface\\.$#"
count: 1
path: lib/Doctrine/ORM/Query/FilterCollection.php
-
message: "#^Array \\(array\\<int, Doctrine\\\\ORM\\\\Query\\\\AST\\\\SelectExpression\\>\\) does not accept key non\\-empty\\-string\\.$#"
message: "#^Property Doctrine\\\\ORM\\\\Query\\\\FilterCollection\\:\\:\\$filterHash is never written, only read\\.$#"
count: 1
path: lib/Doctrine/ORM/Query/Parser.php
path: lib/Doctrine/ORM/Query/FilterCollection.php
-
message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#"
@@ -1427,14 +1462,19 @@ parameters:
path: lib/Doctrine/ORM/Query/Parser.php
-
message: "#^Result of && is always false\\.$#"
count: 1
message: "#^Unreachable statement \\- code above always terminates\\.$#"
count: 3
path: lib/Doctrine/ORM/Query/Parser.php
-
message: "#^Unreachable statement \\- code above always terminates\\.$#"
count: 4
path: lib/Doctrine/ORM/Query/Parser.php
message: "#^Offset 'columns' on array\\{name\\: string, entities\\: array, columns\\: array\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php
-
message: "#^Offset 'entities' on array\\{name\\: string, entities\\: array, columns\\: array\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php
-
message: "#^Parameter \\#2 \\$class of static method Doctrine\\\\ORM\\\\Utility\\\\PersisterHelper\\:\\:getTypeOfColumn\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata, Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo given\\.$#"
@@ -1481,6 +1521,16 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Query/SqlWalker.php
-
message: "#^Offset 'resultVariable' on array\\{metadata\\: Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata, parent\\: string, relation\\: array, map\\: mixed, nestingLevel\\: int, token\\: array\\} in isset\\(\\) does not exist\\.$#"
count: 3
path: lib/Doctrine/ORM/Query/SqlWalker.php
-
message: "#^Offset string on array\\<int, array\\{class\\: Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata, dqlAlias\\: string, resultAlias\\: string\\}\\> in isset\\(\\) does not exist\\.$#"
count: 1
path: lib/Doctrine/ORM/Query/SqlWalker.php
-
message: "#^Parameter \\#1 \\$entity of static method Doctrine\\\\ORM\\\\OptimisticLockException\\:\\:lockFailed\\(\\) expects object, class\\-string\\<object\\> given\\.$#"
count: 1
@@ -1987,7 +2037,7 @@ parameters:
path: lib/Doctrine/ORM/QueryBuilder.php
-
message: "#^Parameter \\#2 \\$dqlPart of method Doctrine\\\\ORM\\\\QueryBuilder\\:\\:add\\(\\) expects array\\<'join'\\|int, array\\<int\\|string, object\\>\\|string\\>\\|object\\|string, array\\<string, Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Join\\>&nonEmpty given\\.$#"
message: "#^Parameter \\#2 \\$dqlPart of method Doctrine\\\\ORM\\\\QueryBuilder\\:\\:add\\(\\) expects array\\<'join'\\|int, array\\<int\\|string, object\\>\\|string\\>\\|object\\|string, non\\-empty\\-array\\<string, Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Join\\> given\\.$#"
count: 2
path: lib/Doctrine/ORM/QueryBuilder.php
@@ -2026,11 +2076,6 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php
-
message: "#^Method Doctrine\\\\ORM\\\\Tools\\\\Console\\\\Command\\\\ConvertMappingCommand\\:\\:execute\\(\\) should return int but empty return statement found\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php
-
message: "#^Parameter \\#1 \\$metadata of method Doctrine\\\\ORM\\\\Tools\\\\Export\\\\Driver\\\\AbstractExporter\\:\\:setMetadata\\(\\) expects array\\<int, Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\>, array\\<Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\> given\\.$#"
count: 1
@@ -2067,7 +2112,47 @@ parameters:
path: lib/Doctrine/ORM/Tools/EntityGenerator.php
-
message: "#^Result of && is always false\\.$#"
message: "#^Offset 'allocationSize' on array\\{sequenceName\\: string, allocationSize\\: string, initialValue\\: string, quoted\\?\\: mixed\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/EntityGenerator.php
-
message: "#^Offset 'indexes' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/EntityGenerator.php
-
message: "#^Offset 'initialValue' on array\\{sequenceName\\: string, allocationSize\\: string, initialValue\\: string, quoted\\?\\: mixed\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/EntityGenerator.php
-
message: "#^Offset 'name' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/EntityGenerator.php
-
message: "#^Offset 'options' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/EntityGenerator.php
-
message: "#^Offset 'schema' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/EntityGenerator.php
-
message: "#^Offset 'sequenceName' on array\\{sequenceName\\: string, allocationSize\\: string, initialValue\\: string, quoted\\?\\: mixed\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/EntityGenerator.php
-
message: "#^Offset 'uniqueConstraints' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/EntityGenerator.php
-
message: "#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\<object\\>\\:\\:\\$lifecycleCallbacks \\(array\\<string, array\\<int, string\\>\\>\\) in isset\\(\\) is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/EntityGenerator.php
@@ -2101,6 +2186,36 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php
-
message: "#^Offset 'indexes' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php
-
message: "#^Offset 'name' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php
-
message: "#^Offset 'options' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php
-
message: "#^Offset 'schema' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php
-
message: "#^Offset 'uniqueConstraints' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php
-
message: "#^Offset 'version' on array\\{type\\: string, fieldName\\: string, columnName\\?\\: string, length\\?\\: int, id\\?\\: bool, nullable\\?\\: bool, columnDefinition\\?\\: string, precision\\?\\: int, \\.\\.\\.\\} in isset\\(\\) does not exist\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php
-
message: "#^Parameter \\#1 \\$policy of method Doctrine\\\\ORM\\\\Tools\\\\Export\\\\Driver\\\\AbstractExporter\\:\\:_getChangeTrackingPolicyString\\(\\) expects 1\\|2\\|3, int given\\.$#"
count: 1
@@ -2111,6 +2226,11 @@ parameters:
count: 2
path: lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php
-
message: "#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\<object\\>\\:\\:\\$lifecycleCallbacks \\(array\\<string, array\\<int, string\\>\\>\\) in isset\\(\\) is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php
-
message: "#^Right side of && is always true\\.$#"
count: 2
@@ -2121,6 +2241,16 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php
-
message: "#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\<object\\>\\:\\:\\$lifecycleCallbacks \\(array\\<string, array\\<int, string\\>\\>\\) in isset\\(\\) is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php
-
message: "#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\<object\\>\\:\\:\\$table \\(array\\<string, array\\|bool\\|string\\>\\) on left side of \\?\\? is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php
-
message: "#^Return type \\(void\\) of method Doctrine\\\\ORM\\\\Tools\\\\Pagination\\\\CountWalker\\:\\:walkSelectStatement\\(\\) should be compatible with return type \\(string\\) of method Doctrine\\\\ORM\\\\Query\\\\TreeWalker\\:\\:walkSelectStatement\\(\\)$#"
count: 1
@@ -2131,6 +2261,11 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php
-
message: "#^Offset 'parent' on array\\{metadata\\: Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata, parent\\: string, relation\\: array, map\\: mixed, nestingLevel\\: int, token\\: array\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php
-
message: "#^Return type \\(void\\) of method Doctrine\\\\ORM\\\\Tools\\\\Pagination\\\\LimitSubqueryWalker\\:\\:walkSelectStatement\\(\\) should be compatible with return type \\(string\\) of method Doctrine\\\\ORM\\\\Query\\\\TreeWalker\\:\\:walkSelectStatement\\(\\)$#"
count: 1
@@ -2166,6 +2301,21 @@ parameters:
count: 1
path: lib/Doctrine/ORM/Tools/SchemaTool.php
-
message: "#^Offset 'indexes' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/SchemaTool.php
-
message: "#^Offset 'options' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/SchemaTool.php
-
message: "#^Offset 'uniqueConstraints' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\<string, mixed\\>, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: lib/Doctrine/ORM/Tools/SchemaTool.php
-
message: "#^Binary operation \"&\" between string and 3 results in an error\\.$#"
count: 1
+1 -10
View File
@@ -21,7 +21,7 @@ parameters:
message: '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getSQLResultCasing\(\)\.$/'
path: lib/Doctrine/ORM/Internal/SQLResultCasing.php
-
message: '/^Parameter \$stmt of method .* has invalid typehint type Doctrine\\DBAL\\Driver\\ResultStatement\.$/'
message: '/^Parameter \$stmt of method .* has invalid type Doctrine\\DBAL\\Driver\\ResultStatement\.$/'
path: lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php
-
message: '/^Class Doctrine\\DBAL\\Driver\\ResultStatement not found\.$/'
@@ -30,15 +30,6 @@ parameters:
message: '/^Call to static method ensure\(\) on an unknown class Doctrine\\DBAL\\ForwardCompatibility\\Result\.$/'
path: lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php
# Forward compatibility for DBAL 3.2
- '/^Call to an undefined method Doctrine\\DBAL\\Cache\\QueryCacheProfile::[gs]etResultCache\(\)\.$/'
-
message: '/^Call to an undefined static method Doctrine\\DBAL\\Configuration::[gs]etResultCache\(\)\.$/'
path: lib/Doctrine/ORM/Configuration.php
-
message: '/^Parameter #3 \$resultCache of class Doctrine\\DBAL\\Cache\\QueryCacheProfile constructor/'
path: lib/Doctrine/ORM/AbstractQuery.php
# False positive
-
message: '/^Variable \$offset in isset\(\) always exists and is not nullable\.$/'
+122 -129
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.10.0@916b098b008f6de4543892b1e0651c1c3b92cbfa">
<files psalm-version="v4.15.0@a1b5e489e6fcebe40cb804793d964e99fc347820">
<file src="lib/Doctrine/ORM/AbstractQuery.php">
<DeprecatedClass occurrences="1">
<code>IterableResult</code>
@@ -7,9 +7,6 @@
<DeprecatedMethod occurrences="1">
<code>iterate</code>
</DeprecatedMethod>
<DocblockTypeContradiction occurrences="1">
<code>$resultCacheDriver !== null &amp;&amp; ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)</code>
</DocblockTypeContradiction>
<FalsableReturnStatement occurrences="1">
<code>! $filteredParameters-&gt;isEmpty() ? $filteredParameters-&gt;first() : null</code>
</FalsableReturnStatement>
@@ -273,8 +270,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 +499,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>
@@ -520,15 +515,16 @@
<PropertyTypeCoercion occurrences="1">
<code>new $metadataFactoryClassName()</code>
</PropertyTypeCoercion>
<RedundantCondition occurrences="1">
<RedundantCondition occurrences="2">
<code>is_object($connection)</code>
<code>is_object($connection)</code>
</RedundantCondition>
<RedundantConditionGivenDocblockType occurrences="1">
<code>$this-&gt;filterCollection !== null</code>
</RedundantConditionGivenDocblockType>
<TypeDoesNotContainType occurrences="2">
<code>$connection instanceof Connection</code>
<code>': "' . $connection . '"'</code>
<code>gettype($connection)</code>
</TypeDoesNotContainType>
</file>
<file src="lib/Doctrine/ORM/EntityManagerInterface.php">
@@ -606,9 +602,6 @@
<PossiblyNullPropertyAssignmentValue occurrences="1">
<code>$sequenceName</code>
</PossiblyNullPropertyAssignmentValue>
<RedundantCastGivenDocblockType occurrences="1">
<code>(string) $em-&gt;getConnection()-&gt;lastInsertId($this-&gt;sequenceName)</code>
</RedundantCastGivenDocblockType>
</file>
<file src="lib/Doctrine/ORM/Id/TableGenerator.php">
<PossiblyFalseOperand occurrences="3">
@@ -779,9 +772,7 @@
</RedundantCastGivenDocblockType>
</file>
<file src="lib/Doctrine/ORM/Mapping/ClassMetadata.php">
<PropertyNotSetInConstructor occurrences="8">
<code>ClassMetadata</code>
<code>ClassMetadata</code>
<PropertyNotSetInConstructor occurrences="6">
<code>ClassMetadata</code>
<code>ClassMetadata</code>
<code>ClassMetadata</code>
@@ -823,13 +814,13 @@
<DeprecatedConstant occurrences="1">
<code>ClassMetadata::GENERATOR_TYPE_UUID</code>
</DeprecatedConstant>
<DeprecatedMethod occurrences="2">
<DeprecatedMethod occurrences="3">
<code>addNamedNativeQuery</code>
<code>addNamedQuery</code>
<code>getName</code>
</DeprecatedMethod>
<DocblockTypeContradiction occurrences="3">
<code>! $class-&gt;reflClass</code>
<code>$class-&gt;reflClass</code>
<DocblockTypeContradiction occurrences="2">
<code>! $definition</code>
<code>$definition</code>
</DocblockTypeContradiction>
<InvalidArrayOffset occurrences="1">
@@ -872,6 +863,9 @@
<code>$parent-&gt;table</code>
<code>$parent-&gt;versionField</code>
</NoInterfaceProperties>
<PossiblyInvalidArrayAssignment occurrences="1">
<code>$subClass-&gt;table[$indexType][$indexName]</code>
</PossiblyInvalidArrayAssignment>
<PossiblyInvalidIterator occurrences="1">
<code>$parentClass-&gt;table[$indexType]</code>
</PossiblyInvalidIterator>
@@ -894,7 +888,7 @@
<code>$subClass-&gt;table</code>
</PropertyTypeCoercion>
<RedundantConditionGivenDocblockType occurrences="2">
<code>$parent</code>
<code>$parent-&gt;idGenerator</code>
<code>$parent-&gt;idGenerator</code>
</RedundantConditionGivenDocblockType>
<UndefinedInterfaceMethod occurrences="17">
@@ -925,13 +919,17 @@
<DeprecatedConstant occurrences="1">
<code>self::GENERATOR_TYPE_UUID</code>
</DeprecatedConstant>
<DeprecatedMethod occurrences="1">
<code>canEmulateSchemas</code>
</DeprecatedMethod>
<DeprecatedProperty occurrences="4">
<code>$this-&gt;columnNames</code>
<code>$this-&gt;columnNames</code>
<code>$this-&gt;columnNames</code>
<code>$this-&gt;columnNames</code>
</DeprecatedProperty>
<DocblockTypeContradiction occurrences="1">
<DocblockTypeContradiction occurrences="2">
<code>! $this-&gt;table</code>
<code>$this-&gt;table</code>
</DocblockTypeContradiction>
<InvalidDocblock occurrences="4">
@@ -940,9 +938,10 @@
<code>protected function _validateAndCompleteOneToOneMapping(array $mapping)</code>
<code>public $inheritanceType = self::INHERITANCE_TYPE_NONE;</code>
</InvalidDocblock>
<InvalidNullableReturnType occurrences="2">
<InvalidNullableReturnType occurrences="3">
<code>ReflectionProperty</code>
<code>ReflectionProperty</code>
<code>getReflectionClass</code>
</InvalidNullableReturnType>
<InvalidPropertyAssignmentValue occurrences="1">
<code>$definition</code>
@@ -971,7 +970,8 @@
<MoreSpecificReturnType occurrences="1">
<code>array{usage: int, region: string|null}</code>
</MoreSpecificReturnType>
<NullableReturnStatement occurrences="2">
<NullableReturnStatement occurrences="3">
<code>$this-&gt;reflClass</code>
<code>$this-&gt;reflFields[$name]</code>
<code>$this-&gt;reflFields[$this-&gt;identifier[0]]</code>
</NullableReturnStatement>
@@ -991,12 +991,17 @@
<code>$queryMapping['resultClass']</code>
<code>$reflService-&gt;getAccessibleProperty($mapping['originalClass'], $mapping['originalField'])</code>
</PossiblyNullArgument>
<PossiblyNullPropertyAssignmentValue occurrences="3">
<code>$reflService-&gt;getClass($this-&gt;name)</code>
<code>$reflService-&gt;getClass($this-&gt;name)</code>
<PossiblyNullPropertyAssignmentValue occurrences="1">
<code>null</code>
</PossiblyNullPropertyAssignmentValue>
<PossiblyNullReference occurrences="6">
<PossiblyNullPropertyFetch occurrences="2">
<code>$embeddable-&gt;reflClass-&gt;name</code>
<code>$this-&gt;reflClass-&gt;name</code>
</PossiblyNullPropertyFetch>
<PossiblyNullReference occurrences="9">
<code>getProperty</code>
<code>getProperty</code>
<code>getProperty</code>
<code>getValue</code>
<code>getValue</code>
<code>getValue</code>
@@ -1017,12 +1022,10 @@
<code>$this-&gt;fieldMappings[$idProperty]['columnName']</code>
<code>$this-&gt;fieldMappings[$idProperty]['columnName']</code>
</PossiblyUndefinedArrayOffset>
<PropertyNotSetInConstructor occurrences="8">
<code>$discriminatorColumn</code>
<PropertyNotSetInConstructor occurrences="6">
<code>$idGenerator</code>
<code>$isVersioned</code>
<code>$namespace</code>
<code>$reflClass</code>
<code>$sequenceGeneratorDefinition</code>
<code>$table</code>
<code>$tableGeneratorDefinition</code>
@@ -1040,26 +1043,19 @@
<code>$this-&gt;table</code>
<code>$this-&gt;table</code>
</PropertyTypeCoercion>
<RedundantCast occurrences="1">
<code>array_values</code>
</RedundantCast>
<RedundantConditionGivenDocblockType occurrences="3">
<code>$className !== null</code>
<code>$mapping !== false</code>
<code>$mapping !== false</code>
</RedundantConditionGivenDocblockType>
<RedundantPropertyInitializationCheck occurrences="1"/>
<TooManyArguments occurrences="2">
<code>joinColumnName</code>
<code>joinColumnName</code>
</TooManyArguments>
</file>
<file src="lib/Doctrine/ORM/Mapping/Column.php">
<PossiblyNullPropertyAssignmentValue occurrences="5">
<code>$columnDefinition</code>
<code>$length</code>
<code>$name</code>
<code>$precision</code>
<code>$scale</code>
</PossiblyNullPropertyAssignmentValue>
</file>
<file src="lib/Doctrine/ORM/Mapping/ColumnResult.php">
<MissingConstructor occurrences="1">
<code>$name</code>
@@ -1088,6 +1084,10 @@
</PossiblyFalseOperand>
</file>
<file src="lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php">
<DeprecatedMethod occurrences="2">
<code>canEmulateSchemas</code>
<code>canEmulateSchemas</code>
</DeprecatedMethod>
<MissingClosureParamType occurrences="1">
<code>$joinColumn</code>
</MissingClosureParamType>
@@ -1108,9 +1108,14 @@
<ArgumentTypeCoercion occurrences="1">
<code>$metadata</code>
</ArgumentTypeCoercion>
<DocblockTypeContradiction occurrences="1">
<DocblockTypeContradiction occurrences="2">
<code>! $class</code>
<code>$class</code>
</DocblockTypeContradiction>
<LessSpecificReturnStatement occurrences="1">
<code>$mapping</code>
</LessSpecificReturnStatement>
<MoreSpecificReturnType occurrences="1"/>
<NoInterfaceProperties occurrences="5">
<code>$metadata-&gt;inheritanceType</code>
<code>$metadata-&gt;isEmbeddedClass</code>
@@ -1125,10 +1130,6 @@
<code>$primaryTable['indexes']</code>
<code>$primaryTable['uniqueConstraints']</code>
</PossiblyUndefinedArrayOffset>
<RedundantConditionGivenDocblockType occurrences="2">
<code>isset($column-&gt;columnDefinition)</code>
<code>isset($column-&gt;name)</code>
</RedundantConditionGivenDocblockType>
<UndefinedInterfaceMethod occurrences="32">
<code>addEntityListener</code>
<code>addLifecycleCallback</code>
@@ -1177,6 +1178,10 @@
<code>$value[1]</code>
<code>$value[1]</code>
</InvalidArrayAccess>
<LessSpecificReturnStatement occurrences="1">
<code>$mapping</code>
</LessSpecificReturnStatement>
<MoreSpecificReturnType occurrences="1"/>
<NonInvariantDocblockPropertyType occurrences="1">
<code>$entityAnnotationClasses</code>
</NonInvariantDocblockPropertyType>
@@ -1193,10 +1198,8 @@
<code>assert($method instanceof ReflectionMethod)</code>
<code>assert($property instanceof ReflectionProperty)</code>
</RedundantCondition>
<RedundantConditionGivenDocblockType occurrences="3">
<RedundantConditionGivenDocblockType occurrences="1">
<code>assert($cacheAttribute instanceof Mapping\Cache)</code>
<code>isset($column-&gt;columnDefinition)</code>
<code>isset($column-&gt;name)</code>
</RedundantConditionGivenDocblockType>
</file>
<file src="lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php">
@@ -1291,11 +1294,7 @@
<code>$indexXml-&gt;options</code>
<code>$uniqueXml-&gt;options</code>
</PossiblyInvalidPropertyFetch>
<RedundantCast occurrences="1">
<code>(string) $attributes-&gt;name</code>
</RedundantCast>
<RedundantCondition occurrences="20">
<code>isset($attributes-&gt;name)</code>
<RedundantCondition occurrences="19">
<code>isset($xmlRoot-&gt;cache)</code>
<code>isset($xmlRoot-&gt;embedded)</code>
<code>isset($xmlRoot-&gt;field)</code>
@@ -1587,16 +1586,7 @@
<InvalidDocblock occurrences="1">
<code>final class PersistentCollection extends AbstractLazyCollection implements Selectable</code>
</InvalidDocblock>
<InvalidReturnStatement occurrences="5">
<code>$persister-&gt;slice($this, $offset, $length)</code>
<code>$this-&gt;collection</code>
<code>new ArrayCollection($persister-&gt;loadCriteria($this, $criteria))</code>
<code>parent::slice($offset, $length)</code>
</InvalidReturnStatement>
<InvalidReturnType occurrences="2">
<code>Collection&lt;TKey, T&gt;</code>
<code>array&lt;TKey,T&gt;</code>
</InvalidReturnType>
<InvalidReturnStatement occurrences="1"/>
<MissingParamType occurrences="2">
<code>$key</code>
<code>$offset</code>
@@ -1631,7 +1621,9 @@
<PropertyNotSetInConstructor occurrences="1">
<code>$backRefFieldName</code>
</PropertyNotSetInConstructor>
<RedundantConditionGivenDocblockType occurrences="3">
<RedundantConditionGivenDocblockType occurrences="5">
<code>$this-&gt;em</code>
<code>$this-&gt;em</code>
<code>is_object($this-&gt;collection)</code>
<code>is_object($value) &amp;&amp; $this-&gt;em</code>
<code>is_object($value) &amp;&amp; $this-&gt;em</code>
@@ -1857,9 +1849,6 @@
<PropertyTypeCoercion occurrences="1">
<code>$this-&gt;currentPersisterContext-&gt;sqlTableAliases</code>
</PropertyTypeCoercion>
<RedundantCondition occurrences="1">
<code>($comparison === Comparison::EQ || $comparison === Comparison::IS) &amp;&amp; $value === null</code>
</RedundantCondition>
<RedundantConditionGivenDocblockType occurrences="1">
<code>$this-&gt;insertSql !== null</code>
</RedundantConditionGivenDocblockType>
@@ -1900,11 +1889,6 @@
<code>$columnList</code>
</PossiblyUndefinedVariable>
</file>
<file src="lib/Doctrine/ORM/Persisters/SqlValueVisitor.php">
<RedundantCondition occurrences="1">
<code>($operator === Comparison::EQ || $operator === Comparison::IS) &amp;&amp; $value === null</code>
</RedundantCondition>
</file>
<file src="lib/Doctrine/ORM/Proxy/ProxyFactory.php">
<ArgumentTypeCoercion occurrences="2">
<code>$classMetadata</code>
@@ -1973,9 +1957,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>
@@ -2203,6 +2184,9 @@
<ArgumentTypeCoercion occurrences="1">
<code>$this-&gt;stringPrimary</code>
</ArgumentTypeCoercion>
<DeprecatedMethod occurrences="1">
<code>getLowerExpression</code>
</DeprecatedMethod>
<PropertyNotSetInConstructor occurrences="1">
<code>$stringPrimary</code>
</PropertyNotSetInConstructor>
@@ -2232,6 +2216,9 @@
</PropertyNotSetInConstructor>
</file>
<file src="lib/Doctrine/ORM/Query/AST/Functions/SqrtFunction.php">
<DeprecatedMethod occurrences="1">
<code>getSqrtExpression</code>
</DeprecatedMethod>
<PropertyNotSetInConstructor occurrences="1">
<code>$simpleArithmeticExpression</code>
</PropertyNotSetInConstructor>
@@ -2270,6 +2257,9 @@
<ArgumentTypeCoercion occurrences="1">
<code>$this-&gt;stringPrimary</code>
</ArgumentTypeCoercion>
<DeprecatedMethod occurrences="1">
<code>getUpperExpression</code>
</DeprecatedMethod>
<PropertyNotSetInConstructor occurrences="1">
<code>$stringPrimary</code>
</PropertyNotSetInConstructor>
@@ -2636,16 +2626,9 @@
<code>call_user_func($functionClass, $functionName)</code>
<code>call_user_func($functionClass, $functionName)</code>
</DocblockTypeContradiction>
<InvalidArrayOffset occurrences="2">
<code>$this-&gt;identVariableExpressions[$dqlAlias]</code>
<code>$this-&gt;queryComponents[$dqlAlias]</code>
</InvalidArrayOffset>
<InvalidNullableReturnType occurrences="1">
<code>SelectStatement|UpdateStatement|DeleteStatement</code>
</InvalidNullableReturnType>
<InvalidPropertyAssignmentValue occurrences="1">
<code>$this-&gt;identVariableExpressions</code>
</InvalidPropertyAssignmentValue>
<InvalidReturnStatement occurrences="9">
<code>$primary</code>
<code>$this-&gt;CollectionMemberExpression()</code>
@@ -2850,27 +2833,36 @@
<code>$renameMode</code>
<code>$renameMode</code>
</ArgumentTypeCoercion>
<PossiblyNullPropertyFetch occurrences="1">
<code>$classMetadata-&gt;reflClass-&gt;name</code>
</PossiblyNullPropertyFetch>
<PossiblyNullReference occurrences="3">
<code>getShortName</code>
<code>getShortName</code>
<code>getShortName</code>
</PossiblyNullReference>
<PossiblyUndefinedArrayOffset occurrences="1">
<code>$class-&gt;fieldMappings[$this-&gt;fieldMappings[$columnName]]['columnName']</code>
</PossiblyUndefinedArrayOffset>
</file>
<file src="lib/Doctrine/ORM/Query/SqlWalker.php">
<DocblockTypeContradiction occurrences="10">
<code>! ($factor instanceof AST\ConditionalFactor)</code>
<code>$condExpr instanceof AST\ConditionalExpression</code>
<code>$condTerm instanceof AST\ConditionalTerm</code>
<code>$simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression</code>
<DocblockTypeContradiction occurrences="8">
<code>$likeExpr-&gt;stringPattern instanceof AST\Functions\FunctionNode</code>
<code>$likeExpr-&gt;stringPattern instanceof AST\PathExpression</code>
<code>$this-&gt;queryComponents[$dqlAlias]['relation'] === null</code>
<code>''</code>
<code>is_numeric($leftExpr) ? $leftExpr : $this-&gt;conn-&gt;quote($leftExpr)</code>
<code>is_numeric($rightExpr) ? $rightExpr : $this-&gt;conn-&gt;quote($rightExpr)</code>
<code>is_numeric($leftExpr)</code>
<code>is_numeric($rightExpr)</code>
<code>is_string($expression)</code>
<code>is_string($stringExpr)</code>
</DocblockTypeContradiction>
<ImplicitToStringCast occurrences="1">
<code>$expr</code>
</ImplicitToStringCast>
<InvalidArgument occurrences="1">
<InvalidArgument occurrences="4">
<code>$condExpr</code>
<code>$condTerm</code>
<code>$factor</code>
<code>$selectedClass['class']-&gt;name</code>
</InvalidArgument>
<InvalidArrayOffset occurrences="1">
@@ -2941,10 +2933,12 @@
<code>$this-&gt;queryComponents</code>
<code>$this-&gt;selectedClasses</code>
</PropertyTypeCoercion>
<RedundantConditionGivenDocblockType occurrences="5">
<RedundantConditionGivenDocblockType occurrences="7">
<code>$leftExpr instanceof AST\Node</code>
<code>$likeExpr-&gt;stringPattern instanceof AST\InputParameter</code>
<code>$rightExpr instanceof AST\Node</code>
<code>$this-&gt;conn-&gt;quote($leftExpr)</code>
<code>$this-&gt;conn-&gt;quote($rightExpr)</code>
<code>$whereClause !== null</code>
<code>($factor-&gt;not ? 'NOT ' : '') . $this-&gt;walkConditionalPrimary($factor-&gt;conditionalPrimary)</code>
</RedundantConditionGivenDocblockType>
@@ -3239,10 +3233,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 +3251,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>
@@ -3315,13 +3301,9 @@
<MissingReturnType occurrences="1">
<code>configure</code>
</MissingReturnType>
<PossiblyNullArgument occurrences="2">
<code>$cacheDriver</code>
<PossiblyNullArgument occurrences="1">
<code>$cacheDriver</code>
</PossiblyNullArgument>
<PossiblyNullReference occurrences="1">
<code>deleteAll</code>
</PossiblyNullReference>
</file>
<file src="lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php">
<ArgumentTypeCoercion occurrences="1">
@@ -3352,9 +3334,6 @@
<code>new ClassMetadataExporter()</code>
<code>new EntityGenerator()</code>
</DeprecatedClass>
<InvalidNullableReturnType occurrences="1">
<code>int</code>
</InvalidNullableReturnType>
<MissingReturnType occurrences="1">
<code>configure</code>
</MissingReturnType>
@@ -3494,10 +3473,8 @@
<DeprecatedConstant occurrences="1">
<code>ClassMetadataInfo::GENERATOR_TYPE_UUID</code>
</DeprecatedConstant>
<DocblockTypeContradiction occurrences="3">
<code>$metadata-&gt;reflClass !== null</code>
<DocblockTypeContradiction occurrences="1">
<code>class_exists($metadata-&gt;name)</code>
<code>new ReflectionClass($metadata-&gt;name)</code>
</DocblockTypeContradiction>
<InvalidDocblock occurrences="1">
<code>public function setFieldVisibility($visibility)</code>
@@ -3523,12 +3500,13 @@
<PropertyNotSetInConstructor occurrences="1">
<code>$classToExtend</code>
</PropertyNotSetInConstructor>
<RedundantCastGivenDocblockType occurrences="1">
<RedundantCastGivenDocblockType occurrences="2">
<code>(array) $metadata-&gt;table['options']</code>
<code>(bool) $embeddablesImmutable</code>
</RedundantCastGivenDocblockType>
<RedundantConditionGivenDocblockType occurrences="3">
<code>$metadata-&gt;reflClass</code>
<code>$metadata-&gt;reflClass !== null</code>
<code>$metadata-&gt;sequenceGeneratorDefinition</code>
<code>$metadata-&gt;sequenceGeneratorDefinition</code>
<code>isset($metadata-&gt;lifecycleCallbacks)</code>
</RedundantConditionGivenDocblockType>
</file>
@@ -3538,15 +3516,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>
@@ -3609,6 +3581,10 @@
<NonInvariantDocblockPropertyType occurrences="1">
<code>$_extension</code>
</NonInvariantDocblockPropertyType>
<RedundantConditionGivenDocblockType occurrences="2">
<code>$metadata-&gt;table</code>
<code>$metadata-&gt;table</code>
</RedundantConditionGivenDocblockType>
</file>
<file src="lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php">
<ArgumentTypeCoercion occurrences="3">
@@ -3635,10 +3611,12 @@
<PossiblyNullReference occurrences="1">
<code>addAttribute</code>
</PossiblyNullReference>
<RedundantCondition occurrences="1">
<RedundantCondition occurrences="2">
<code>$field['associationKey']</code>
<code>isset($field['associationKey']) &amp;&amp; $field['associationKey']</code>
</RedundantCondition>
<RedundantConditionGivenDocblockType occurrences="1">
<RedundantConditionGivenDocblockType occurrences="2">
<code>$sequenceDefinition</code>
<code>isset($metadata-&gt;lifecycleCallbacks)</code>
</RedundantConditionGivenDocblockType>
</file>
@@ -3675,6 +3653,10 @@
</RedundantConditionGivenDocblockType>
</file>
<file src="lib/Doctrine/ORM/Tools/Pagination/CountOutputWalker.php">
<DeprecatedMethod occurrences="2">
<code>getCountExpression</code>
<code>getCountExpression</code>
</DeprecatedMethod>
<MoreSpecificImplementedParamType occurrences="1">
<code>$query</code>
</MoreSpecificImplementedParamType>
@@ -3728,7 +3710,8 @@
</PropertyNotSetInConstructor>
</file>
<file src="lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php">
<DocblockTypeContradiction occurrences="2">
<DocblockTypeContradiction occurrences="3">
<code>$AST-&gt;whereClause-&gt;conditionalExpression instanceof ConditionalFactor</code>
<code>$AST-&gt;whereClause-&gt;conditionalExpression instanceof ConditionalPrimary</code>
</DocblockTypeContradiction>
<ImplementedReturnTypeMismatch occurrences="1">
@@ -3740,12 +3723,16 @@
<PossiblyInvalidPropertyAssignmentValue occurrences="1">
<code>$AST-&gt;whereClause-&gt;conditionalExpression</code>
</PossiblyInvalidPropertyAssignmentValue>
<RedundantConditionGivenDocblockType occurrences="1">
<code>$AST-&gt;whereClause-&gt;conditionalExpression instanceof ConditionalExpression</code>
</RedundantConditionGivenDocblockType>
<RedundantConditionGivenDocblockType occurrences="1"/>
</file>
<file src="lib/Doctrine/ORM/Tools/SchemaTool.php">
<DocblockTypeContradiction occurrences="1">
<DeprecatedMethod occurrences="3">
<code>canEmulateSchemas</code>
<code>compare</code>
<code>setExplicitForeignKeyIndexes</code>
</DeprecatedMethod>
<DocblockTypeContradiction occurrences="2">
<code>! $definingClass</code>
<code>$definingClass</code>
</DocblockTypeContradiction>
<MissingClosureParamType occurrences="1">
@@ -3773,6 +3760,11 @@
<code>$indexName</code>
</TypeDoesNotContainType>
</file>
<file src="lib/Doctrine/ORM/Tools/SchemaValidator.php">
<PossiblyNullReference occurrences="1">
<code>isAbstract</code>
</PossiblyNullReference>
</file>
<file src="lib/Doctrine/ORM/Tools/Setup.php">
<DeprecatedClass occurrences="3">
<code>new ClassLoader('Doctrine', $directory)</code>
@@ -3877,7 +3869,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>
+11
View File
@@ -30,6 +30,10 @@
<!-- We're calling the deprecated method for BC here. -->
<file name="lib/Doctrine/ORM/Internal/SQLResultCasing.php"/>
<!-- We need to keep the calls for DBAL 2.13 compatibility. -->
<referencedMethod name="Doctrine\DBAL\Cache\QueryCacheProfile::getResultCacheDriver"/>
<referencedMethod name="Doctrine\DBAL\Cache\QueryCacheProfile::setResultCacheDriver"/>
<referencedMethod name="Doctrine\DBAL\Configuration::getResultCacheImpl"/>
<referencedMethod name="Doctrine\DBAL\Configuration::setResultCacheImpl"/>
<referencedMethod name="Doctrine\DBAL\Connection::getSchemaManager"/>
<referencedMethod name="Doctrine\DBAL\Platforms\AbstractPlatform::getGuidExpression"/>
</errorLevel>
@@ -83,6 +87,13 @@
<file name="lib/Doctrine/ORM/QueryBuilder.php"/>
</errorLevel>
</RedundantCastGivenDocblockType>
<!-- Workaround for https://github.com/vimeo/psalm/issues/7026 -->
<ReservedWord>
<errorLevel type="suppress">
<directory name="lib"/>
<directory name="tests"/>
</errorLevel>
</ReservedWord>
<TypeDoesNotContainType>
<errorLevel type="suppress">
<file name="lib/Doctrine/ORM/Internal/SQLResultCasing.php"/>
@@ -17,16 +17,14 @@ abstract class GetMetadata
{
/**
* @param string|object $class
* @phpstan-param class-string|object $class
* @psalm-param class-string|object $class
*/
abstract public function getEntityManager($class): EntityManagerInterface;
/**
* @psalm-param class-string<TObject> $class
* @phpstan-param class-string $class
*
* @psalm-return ClassMetadata<TObject>
* @phpstan-return ClassMetadata<object>
*
* @psalm-template TObject of object
*/
@@ -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);
}
}
@@ -239,9 +239,7 @@ class SecondLevelCacheManyToOneTest extends SecondLevelCacheAbstractTest
self::assertFalse($this->cache->containsEntity(Action::class, $action3->name));
$queryCount = $this->getCurrentQueryCount();
/**
* @var $entity Token
*/
$entity = $this->_em->find(Token::class, $token->token);
self::assertInstanceOf(Token::class, $entity);
@@ -0,0 +1,217 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\ManyToMany;
use Doctrine\Tests\OrmFunctionalTestCase;
/**
* @group GH-9109
*/
class GH9109Test extends OrmFunctionalTestCase
{
protected function setUp(): void
{
parent::setUp();
$this->_schemaTool->createSchema(
[
$this->_em->getClassMetadata(GH9109User::class),
$this->_em->getClassMetadata(GH9109Product::class),
]
);
}
protected function tearDown(): void
{
$this->_schemaTool->dropSchema(
[
$this->_em->getClassMetadata(GH9109User::class),
$this->_em->getClassMetadata(GH9109Product::class),
]
);
parent::tearDown();
}
public function testIssue(): void
{
$userFirstName = 'GH9109Test';
$userLastName = 'UserGH9109';
$productTitle = 'Test product';
$userRepository = $this->_em->getRepository(GH9109User::class);
$user = new GH9109User();
$user->setFirstName($userFirstName);
$user->setLastName($userLastName);
$product = new GH9109Product();
$product->setTitle($productTitle);
$this->_em->persist($user);
$this->_em->persist($product);
$this->_em->flush();
$product->addBuyer($user);
$this->_em->persist($product);
$this->_em->flush();
$this->_em->clear();
$persistedProduct = $this->_em->find(GH9109Product::class, $product->getId());
// assert Product was persisted
self::assertInstanceOf(GH9109Product::class, $persistedProduct);
self::assertEquals($productTitle, $persistedProduct->getTitle());
// assert Product has a Buyer
$count = $persistedProduct->getBuyers()->count();
self::assertEquals(1, $count);
// assert NOT QUOTED will WORK with findOneBy
$user = $userRepository->findOneBy(['lastName' => $userLastName]);
self::assertInstanceOf(GH9109User::class, $user);
self::assertEquals($userLastName, $user->getLastName());
// assert NOT QUOTED will WORK with Criteria
$criteria = Criteria::create();
$criteria->where($criteria->expr()->eq('lastName', $userLastName));
$user = $persistedProduct->getBuyers()->matching($criteria)->first();
self::assertInstanceOf(GH9109User::class, $user);
self::assertEquals($userLastName, $user->getLastName());
// assert QUOTED will WORK with findOneBy
$user = $userRepository->findOneBy(['firstName' => $userFirstName]);
self::assertInstanceOf(GH9109User::class, $user);
self::assertEquals($userFirstName, $user->getFirstName());
// assert QUOTED will WORK with Criteria
$criteria = Criteria::create();
$criteria->where($criteria->expr()->eq('firstName', $userFirstName));
$user = $persistedProduct->getBuyers()->matching($criteria)->first();
self::assertInstanceOf(GH9109User::class, $user);
self::assertEquals($userFirstName, $user->getFirstName());
}
}
/**
* @Entity
*/
class GH9109Product
{
/**
* @var int $id
* @Column(name="`id`", type="integer")
* @Id
* @GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string $title
* @Column(name="`title`", type="string", length=255)
*/
private $title;
/**
* @var Collection|GH9109User[]
* @psalm-var Collection<int, GH9109User>
* @ManyToMany(targetEntity="GH9109User")
*/
private $buyers;
public function __construct()
{
$this->buyers = new ArrayCollection();
}
public function getId(): int
{
return $this->id;
}
public function setTitle(string $title): void
{
$this->title = $title;
}
public function getTitle(): string
{
return $this->title;
}
/**
* @psalm-return Collection<int, GH9109User>
*/
public function getBuyers(): Collection
{
return $this->buyers;
}
public function addBuyer(GH9109User $buyer): void
{
$this->buyers[] = $buyer;
}
}
/**
* @Entity
*/
class GH9109User
{
/**
* @var int
* @Column(name="`id`", type="integer")
* @Id
* @GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
* @Column(name="`first_name`", type="string")
*/
private $firstName;
/**
* @var string
* @Column(name="last_name", type="string")
*/
private $lastName;
public function getId(): int
{
return $this->id;
}
public function getFirstName(): string
{
return $this->firstName;
}
public function setFirstName(string $firstName): void
{
$this->firstName = $firstName;
}
public function getLastName(): string
{
return $this->lastName;
}
public function setLastName(string $lastName): void
{
$this->lastName = $lastName;
}
}
@@ -1119,6 +1119,13 @@ abstract class AbstractMappingDriverTest extends OrmTestCase
$class = $this->createClassMetadata(SingleTableEntityIncompleteDiscriminatorColumnMapping::class);
self::assertEquals('dtype', $class->discriminatorColumn['name']);
}
public function testReservedWordInTableColumn(): void
{
$metadata = $this->createClassMetadata(ReservedWordInTableColumn::class);
self::assertSame('count', $metadata->getFieldMapping('count')['columnName']);
}
}
/**
@@ -1774,3 +1781,42 @@ class SingleTableEntityIncompleteDiscriminatorColumnMappingSub1 extends SingleTa
class SingleTableEntityIncompleteDiscriminatorColumnMappingSub2 extends SingleTableEntityIncompleteDiscriminatorColumnMapping
{
}
/** @Entity */
#[ORM\Entity]
class ReservedWordInTableColumn
{
/**
* @var int
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="NONE")
*/
#[ORM\Id, ORM\Column(type: 'integer'), ORM\GeneratedValue(strategy: 'NONE')]
public $id;
/**
* @var string|null
* @Column(name="`count`", type="integer")
*/
#[ORM\Column(name: '`count`', type: 'integer')]
public $count;
public static function loadMetadata(ClassMetadataInfo $metadata): void
{
$metadata->mapField(
[
'id' => true,
'fieldName' => 'id',
'type' => 'integer',
]
);
$metadata->mapField(
[
'fieldName' => 'count',
'type' => 'integer',
'columnName' => '`count`',
]
);
}
}
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
$metadata->mapField(
[
'id' => true,
'fieldName' => 'id',
'type' => 'integer',
]
);
$metadata->mapField(
[
'fieldName' => 'count',
'type' => 'integer',
'columnName' => '`count`',
]
);
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://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="Doctrine\Tests\ORM\Mapping\ReservedWordInTableColumn">
<id name="id" type="integer" column="id">
<generator strategy="NONE"/>
</id>
<field name="count" column="`count`" type="integer"/>
</entity>
</doctrine-mapping>
@@ -0,0 +1,10 @@
Doctrine\Tests\ORM\Mapping\ReservedWordInTableColumn:
type: entity
id:
id:
generator:
strategy: NONE
fields:
count:
type: integer
column: '`count`'
@@ -76,7 +76,7 @@ class SelectSqlGenerationTest extends OrmTestCase
parent::assertEquals(
$sqlToBeConfirmed,
$sqlGenerated,
sprintf('"%s" is not equal of "%s"', $sqlGenerated, $sqlToBeConfirmed)
sprintf('"%s" is not equal to "%s"', $sqlGenerated, $sqlToBeConfirmed)
);
$query->free();
@@ -2116,26 +2116,6 @@ class SelectSqlGenerationTest extends OrmTestCase
'SELECT CONCAT(c0_.id, c0_.name, c0_.status) AS sclr_0 FROM cms_users c0_ WHERE c0_.id = ?'
);
$connMock->setDatabasePlatform(new PostgreSQL94Platform());
$this->assertSqlGeneration(
"SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(u.name, u.status, 's') = ?1",
"SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE c0_.name || c0_.status || 's' = ?"
);
$this->assertSqlGeneration(
'SELECT CONCAT(u.id, u.name, u.status) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1',
'SELECT c0_.id || c0_.name || c0_.status AS sclr_0 FROM cms_users c0_ WHERE c0_.id = ?'
);
$connMock->setDatabasePlatform(new SQLServer2012Platform());
$this->assertSqlGeneration(
"SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(u.name, u.status, 's') = ?1",
"SELECT c0_.id AS id_0 FROM cms_users c0_ WHERE (c0_.name + c0_.status + 's') = ?"
);
$this->assertSqlGeneration(
'SELECT CONCAT(u.id, u.name, u.status) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1',
'SELECT (c0_.id + c0_.name + c0_.status) AS sclr_0 FROM cms_users c0_ WHERE c0_.id = ?'
);
$connMock->setDatabasePlatform($orgPlatform);
}
+5 -16
View File
@@ -58,23 +58,12 @@ abstract class OrmTestCase extends DoctrineTestCase
/** @var Cache|null */
protected $secondLevelCacheDriverImpl = null;
/**
* @param mixed $alias
*/
protected function createAnnotationDriver(array $paths = [], $alias = null): AnnotationDriver
protected function createAnnotationDriver(array $paths = []): AnnotationDriver
{
// Register the ORM Annotations in the AnnotationRegistry
$reader = new Annotations\AnnotationReader();
if (class_exists(Annotations\PsrCachedReader::class)) {
$reader = new Annotations\PsrCachedReader($reader, new ArrayAdapter());
} else {
$reader = new Annotations\CachedReader($reader, DoctrineProvider::wrap(new ArrayAdapter()));
}
Annotations\AnnotationRegistry::registerFile(__DIR__ . '/../../../lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
return new AnnotationDriver($reader, (array) $paths);
return new AnnotationDriver(
new Annotations\PsrCachedReader(new Annotations\AnnotationReader(), new ArrayAdapter()),
$paths
);
}
/**