mirror of
https://github.com/doctrine/orm.git
synced 2026-04-23 22:48:08 +02:00
Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1e972b6e0e | |||
| b7d822972e | |||
| 9a2f1f380d | |||
| 2fecb3cb1a | |||
| f3630ea16b | |||
| fd19444761 | |||
| 4b1afb41b3 | |||
| 2640f88f8a | |||
| fa731b10ec | |||
| 760397c429 | |||
| efc982a48d | |||
| 96bc214acd | |||
| 44aa8c2c5b | |||
| c4561571aa | |||
| 8b5ee54c6a | |||
| 03fa495fbc | |||
| d40f9e57ff | |||
| d30e748e64 | |||
| 40d1e7bbfc | |||
| e8275f6e4d | |||
| 6a9393e8ed | |||
| ab98d0ffc6 | |||
| 2ddeb79431 | |||
| 7c58dc89c3 | |||
| b513f7c935 | |||
| 72edfbc270 | |||
| 5ccf2eac40 | |||
| 6696b0dfbf | |||
| aead77d597 | |||
| f6e1dd44f0 | |||
| cccb2e2fdf | |||
| 18138d895e | |||
| fa2b52c974 | |||
| 5bf814032f | |||
| 02a4e4099d | |||
| 12a70bbefb | |||
| 5a4ddb2870 | |||
| 68fa55f310 | |||
| 0b0c3e7e58 | |||
| 92434f91c7 | |||
| 6414ad4cbb | |||
| 7b24275346 | |||
| ed1a576305 | |||
| 66c95a65c5 | |||
| 62a0d7359b | |||
| 2c7d7ebb48 | |||
| 8b6fe52f74 | |||
| 15ec77fa79 | |||
| 32cd2106d0 | |||
| cac2acae07 | |||
| 146b465ec1 | |||
| 5aba762a33 | |||
| 77b7107d05 | |||
| a663dda869 | |||
| db14f0fa89 | |||
| 9a74ae6280 | |||
| 32eb38ebd9 | |||
| 2dde65c4ba | |||
| 176fbedc69 | |||
| 1b15af44b6 | |||
| 8336420a26 | |||
| a6b7569d7a | |||
| 9e37c788ef | |||
| ca0a6bbf71 | |||
| a3da3d78d4 | |||
| e1c2d2e65d | |||
| 6f194eeabf | |||
| 16cbc16998 | |||
| 5e6608b48e | |||
| 94bc137526 | |||
| 276a0f55ee | |||
| dbaf99f3d9 | |||
| 97411f5567 | |||
| 641330baa6 | |||
| 35e680cd3f | |||
| 705d88eaba | |||
| 3271d8f6e2 | |||
| 3622381f8c | |||
| f2729b0610 | |||
| cd44547573 |
@@ -10,6 +10,6 @@ on:
|
||||
|
||||
jobs:
|
||||
coding-standards:
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@1.1.1"
|
||||
uses: "doctrine/.github/.github/workflows/coding-standards.yml@1.4.1"
|
||||
with:
|
||||
php-version: "7.4"
|
||||
php-version: "8.1"
|
||||
|
||||
@@ -7,9 +7,8 @@ on:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@1.1.1"
|
||||
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@1.4.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 }}
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.0"
|
||||
- "8.1"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "2.13"
|
||||
@@ -58,10 +58,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.0"
|
||||
dbal-version:
|
||||
- "default"
|
||||
- "2.13"
|
||||
- "8.1"
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
@@ -73,10 +70,6 @@ jobs:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
|
||||
- name: "Require specific DBAL version"
|
||||
run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update"
|
||||
if: "${{ matrix.dbal-version != 'default' }}"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
|
||||
+7
-4
@@ -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_.
|
||||
|
||||
+8
-4
@@ -13,6 +13,10 @@
|
||||
{"name": "Marco Pivetta", "email": "ocramius@gmail.com"}
|
||||
],
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"require": {
|
||||
@@ -39,12 +43,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.3.3",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
|
||||
"squizlabs/php_codesniffer": "3.6.1",
|
||||
"symfony/cache": "^4.4 || ^5.2",
|
||||
"squizlabs/php_codesniffer": "3.6.2",
|
||||
"symfony/cache": "^4.4 || ^5.4 || ^6.0",
|
||||
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
|
||||
"vimeo/psalm": "4.10.0"
|
||||
"vimeo/psalm": "4.18.1"
|
||||
},
|
||||
"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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(' .
|
||||
|
||||
@@ -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
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -670,11 +670,23 @@ The same restrictions apply for the reference of related entities.
|
||||
|
||||
.. warning::
|
||||
|
||||
DQL DELETE statements are ported directly into a
|
||||
Database DELETE statement and therefore bypass any events and checks for the
|
||||
version column if they are not explicitly added to the WHERE clause
|
||||
of the query. Additionally Deletes of specified entities are *NOT*
|
||||
cascaded to related entities even if specified in the metadata.
|
||||
DQL DELETE statements are ported directly into an SQL DELETE statement.
|
||||
Therefore, some limitations apply:
|
||||
|
||||
- Lifecycle events for the affected entities are not executed.
|
||||
- A cascading ``remove`` operation (as indicated e. g. by ``cascade={"remove"}``
|
||||
or ``cascade={"all"}`` in the mapping configuration) is not being performed
|
||||
for associated entities. You can rely on database level cascade operations by
|
||||
configuring each join column with the ``onDelete`` option.
|
||||
- Checks for the version column are bypassed if they are not explicitly added
|
||||
to the WHERE clause of the query.
|
||||
|
||||
When you rely on one of these features, one option is to use the
|
||||
``EntityManager#remove($entity)`` method. This, however, is costly performance-wise:
|
||||
It means collections and related entities are fetched into memory
|
||||
(even if they are marked as lazy). Pulling object graphs into memory on cascade
|
||||
can cause considerable performance overhead, especially when the cascaded collections
|
||||
are large. Make sure to weigh the benefits and downsides.
|
||||
|
||||
Comments in queries
|
||||
-------------------
|
||||
@@ -718,7 +730,7 @@ clauses:
|
||||
- ``SQRT(q)`` - Return the square-root of q.
|
||||
- ``SUBSTRING(str, start [, length])`` - Return substring of given
|
||||
string.
|
||||
- ``TRIM([LEADING \| TRAILING \| BOTH] ['trchar' FROM] str)`` - Trim
|
||||
- ``TRIM([LEADING | TRAILING | BOTH] ['trchar' FROM] str)`` - Trim
|
||||
the string by the given trim char, defaults to whitespaces.
|
||||
- ``UPPER(str)`` - Return the upper-case of the given string.
|
||||
- ``DATE_ADD(date, value, unit)`` - Add the given time to a given date.
|
||||
|
||||
+254
-289
@@ -127,45 +127,48 @@ 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`
|
||||
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`
|
||||
see :ref:`Lifecycle Callbacks<lifecycle-callbacks>`
|
||||
|
||||
.. _reference-events-lifecycle-events:
|
||||
|
||||
Events Overview
|
||||
---------------
|
||||
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
| Event | Dispatched by | Lifecycle |
|
||||
| | | Callback |
|
||||
+=============================+=======================+===========+
|
||||
| ``preRemove`` | ``$em->remove()`` | Yes |
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
| ``postRemove`` | ``$em->flush()`` | Yes |
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
| ``prePersist`` | ``$em->persist()`` | Yes |
|
||||
| | on *initial* persist | |
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
| ``postPersist`` | ``$em->flush()`` | Yes |
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
| ``preUpdate`` | ``$em->flush()`` | Yes |
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
| ``postUpdate`` | ``$em->flush()`` | Yes |
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
| ``postLoad`` | Loading from database | Yes |
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
| ``loadClassMetadata`` | Loading of mapping | No |
|
||||
| | metadata | |
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
| ``onClassMetadataNotFound`` | ``MappingException`` | No |
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
| ``preFlush`` | ``$em->flush()`` | Yes |
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
| ``onFlush`` | ``$em->flush()`` | No |
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
| ``postFlush`` | ``$em->flush()`` | No |
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
| ``onClear`` | ``$em->clear()`` | No |
|
||||
+-----------------------------+-----------------------+-----------+
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| 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` |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
| :ref:`onClear<reference-events-on-clear>` | ``$em->clear()`` | No | `_OnClearEventArgs` |
|
||||
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
|
||||
|
||||
Naming convention
|
||||
~~~~~~~~~~~~~~~~~
|
||||
@@ -186,103 +189,6 @@ several reasons:
|
||||
An example for a correct notation can be found in the example
|
||||
``TestEvent`` above.
|
||||
|
||||
.. _reference-events-lifecycle-events:
|
||||
|
||||
Lifecycle Events
|
||||
----------------
|
||||
|
||||
The ``EntityManager`` and ``UnitOfWork`` classes trigger a bunch of
|
||||
events during the life-time of their registered entities.
|
||||
|
||||
|
||||
|
||||
- ``preRemove`` - The ``preRemove`` event occurs for a given entity
|
||||
before the respective ``EntityManager`` remove operation for that
|
||||
entity is executed. It is not called for a DQL ``DELETE`` statement.
|
||||
- ``postRemove`` - The ``postRemove`` event occurs for an entity after the
|
||||
entity has been deleted. It will be invoked after the database
|
||||
delete operations. It is not called for a DQL ``DELETE`` statement.
|
||||
- ``prePersist`` - The ``prePersist`` event occurs for a given entity
|
||||
before the respective ``EntityManager`` persist operation for that
|
||||
entity is executed. It should be noted that this event is only triggered on
|
||||
*initial* persist of an entity (i.e. it does not trigger on future updates).
|
||||
- ``postPersist`` - The ``postPersist`` event occurs for an entity after
|
||||
the entity has been made persistent. It will be invoked after the
|
||||
database insert operations. Generated primary key values are
|
||||
available in the postPersist event.
|
||||
- ``preUpdate`` - The ``preUpdate`` event occurs before the database
|
||||
update operations to entity data. It is not called for a DQL
|
||||
``UPDATE`` statement nor when the computed changeset is empty.
|
||||
- ``postUpdate`` - The ``postUpdate`` event occurs after the database
|
||||
update operations to entity data. It is not called for a DQL
|
||||
``UPDATE`` statement.
|
||||
- ``postLoad`` - The postLoad event occurs for an entity after the
|
||||
entity has been loaded into the current ``EntityManager`` from the
|
||||
database or after the refresh operation has been applied to it.
|
||||
- ``loadClassMetadata`` - The ``loadClassMetadata`` event occurs after the
|
||||
mapping metadata for a class has been loaded from a mapping source
|
||||
(annotations/xml/yaml). This event is not a lifecycle callback.
|
||||
- ``onClassMetadataNotFound`` - Loading class metadata for a particular
|
||||
requested class name failed. Manipulating the given event args instance
|
||||
allows providing fallback metadata even when no actual metadata exists
|
||||
or could be found. This event is not a lifecycle callback.
|
||||
- ``preFlush`` - The ``preFlush`` event occurs at the very beginning of
|
||||
a flush operation.
|
||||
- ``onFlush`` - The ``onFlush`` event occurs after the change-sets of all
|
||||
managed entities are computed. This event is not a lifecycle
|
||||
callback.
|
||||
- ``postFlush`` - The ``postFlush`` event occurs at the end of a flush operation. This
|
||||
event is not a lifecycle callback.
|
||||
- ``onClear`` - The ``onClear`` event occurs when the
|
||||
``EntityManager#clear()`` operation is invoked, after all references
|
||||
to entities have been removed from the unit of work. This event is not
|
||||
a lifecycle callback.
|
||||
|
||||
|
||||
.. warning::
|
||||
|
||||
Note that, when using ``Doctrine\ORM\AbstractQuery#toIterable()``, ``postLoad``
|
||||
events will be executed immediately after objects are being hydrated, and therefore
|
||||
associations are not guaranteed to be initialized. It is not safe to combine
|
||||
usage of ``Doctrine\ORM\AbstractQuery#toIterable()`` and ``postLoad`` event
|
||||
handlers.
|
||||
|
||||
.. warning::
|
||||
|
||||
Note that the ``postRemove`` event or any events triggered after an entity removal
|
||||
can receive an uninitializable proxy in case you have configured an entity to
|
||||
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:
|
||||
|
||||
- Lifecycle Callbacks are methods on the entity classes that are
|
||||
called when the event is triggered. They receive some kind
|
||||
of ``EventArgs`` instance.
|
||||
- Lifecycle Event Listeners and Subscribers are classes with specific callback
|
||||
methods that receives some kind of ``EventArgs`` instance.
|
||||
|
||||
The ``EventArgs`` instance received by the listener gives access to the entity,
|
||||
``EntityManager`` instance and other relevant data.
|
||||
|
||||
.. note::
|
||||
|
||||
All Lifecycle events that happen during the ``flush()`` of
|
||||
an ``EntityManager`` have very specific constraints on the allowed
|
||||
operations that can be executed. Please read the
|
||||
:ref:`reference-events-implementing-listeners` section very carefully
|
||||
to understand which operations are allowed in which lifecycle event.
|
||||
|
||||
.. _lifecycle-callbacks:
|
||||
|
||||
Lifecycle Callbacks
|
||||
@@ -297,135 +203,107 @@ 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;
|
||||
|
||||
#[Entity]
|
||||
#[HasLifecycleCallbacks]
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
|
||||
#[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;
|
||||
|
||||
/**
|
||||
* @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="string", length=255) */
|
||||
public $value;
|
||||
|
||||
public function doStuffOnPostPersist()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
/** @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
|
||||
----------------------------------
|
||||
@@ -462,7 +340,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
|
||||
@@ -531,8 +409,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);
|
||||
@@ -543,7 +423,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:
|
||||
@@ -563,24 +445,28 @@ 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
|
||||
~~~~~~~~~~
|
||||
|
||||
There are two ways for the ``prePersist`` event to be triggered.
|
||||
One is obviously when you call ``EntityManager#persist()``. The
|
||||
event is also called for all cascaded associations.
|
||||
There are two ways for the ``prePersist`` event to be triggered:
|
||||
|
||||
There is another way for ``prePersist`` to be called, inside the
|
||||
- One is obviously when you call ``EntityManager::persist()``. The
|
||||
event is also called for all :ref:`cascaded associations<transitive-persistence>`.
|
||||
- The other is inside the
|
||||
``flush()`` method when changes to associations are computed and
|
||||
this association is marked as cascade persist. Any new entity found
|
||||
this association is marked as :ref:`cascade: persist<transitive-persistence>`. Any new entity found
|
||||
during this operation is also persisted and ``prePersist`` called
|
||||
on it. This is called "persistence by reachability".
|
||||
on it. This is called :ref:`persistence by reachability<persistence-by-reachability>`.
|
||||
|
||||
In both cases you get passed a ``LifecycleEventArgs`` instance
|
||||
which has access to the entity and the entity manager.
|
||||
|
||||
The following restrictions apply to ``prePersist``:
|
||||
This event is only triggered on *initial* persist of an entity
|
||||
(i.e. it does not trigger on future updates).
|
||||
|
||||
The following restrictions apply to ``prePersist``:
|
||||
|
||||
- If you are using a PrePersist Identity Generator such as
|
||||
sequences the ID value will *NOT* be available within any
|
||||
@@ -589,24 +475,30 @@ The following restrictions apply to ``prePersist``:
|
||||
event. This includes modifications to
|
||||
collections such as additions, removals or replacement.
|
||||
|
||||
.. _reference-events-pre-remove:
|
||||
|
||||
preRemove
|
||||
~~~~~~~~~
|
||||
|
||||
The ``preRemove`` event is called on every entity when its passed
|
||||
to the ``EntityManager#remove()`` method. It is cascaded for all
|
||||
associations that are marked as cascade delete.
|
||||
The ``preRemove`` event is called on every entity immediately when it is passed
|
||||
to the ``EntityManager::remove()`` method. It is cascaded for all
|
||||
associations that are marked as :ref:`cascade: remove<transitive-persistence>`
|
||||
|
||||
It is not called for a DQL ``DELETE`` statement.
|
||||
|
||||
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
|
||||
~~~~~~~~
|
||||
|
||||
``preFlush`` is called at ``EntityManager#flush()`` before
|
||||
anything else. ``EntityManager#flush()`` should not be called inside
|
||||
its listeners, since `preFlush` event is dispatched in it, which would
|
||||
result in infinite loop.
|
||||
``preFlush`` is called inside ``EntityManager::flush()`` before
|
||||
anything else. ``EntityManager::flush()`` must not be called inside
|
||||
its listeners, since it would fire the ``preFlush`` event again, which would
|
||||
result in an infinite loop.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -622,15 +514,16 @@ result in infinite loop.
|
||||
}
|
||||
}
|
||||
|
||||
.. _reference-events-on-flush:
|
||||
|
||||
onFlush
|
||||
~~~~~~~
|
||||
|
||||
OnFlush is a very powerful event. It is called inside
|
||||
``EntityManager#flush()`` after the changes to all the managed
|
||||
``onFlush`` is a very powerful event. It is called inside
|
||||
``EntityManager::flush()`` after the changes to all the managed
|
||||
entities and their associations have been computed. This means, the
|
||||
``onFlush`` event has access to the sets of:
|
||||
|
||||
|
||||
- Entities scheduled for insert
|
||||
- Entities scheduled for update
|
||||
- Entities scheduled for removal
|
||||
@@ -638,7 +531,7 @@ entities and their associations have been computed. This means, the
|
||||
- Collections scheduled for removal
|
||||
|
||||
To make use of the ``onFlush`` event you have to be familiar with the
|
||||
internal ``UnitOfWork`` API, which grants you access to the previously
|
||||
internal :ref:`UnitOfWork<unit-of-work>` API, which grants you access to the previously
|
||||
mentioned sets. See this example:
|
||||
|
||||
.. code-block:: php
|
||||
@@ -673,11 +566,10 @@ mentioned sets. See this example:
|
||||
}
|
||||
}
|
||||
|
||||
The following restrictions apply to the onFlush event:
|
||||
|
||||
The following restrictions apply to the ``onFlush`` event:
|
||||
|
||||
- If you create and persist a new entity in ``onFlush``, then
|
||||
calling ``EntityManager#persist()`` is not enough.
|
||||
calling ``EntityManager::persist()`` is not enough.
|
||||
You have to execute an additional call to
|
||||
``$unitOfWork->computeChangeSet($classMetadata, $entity)``.
|
||||
- Changing primitive fields or associations requires you to
|
||||
@@ -685,11 +577,14 @@ 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
|
||||
~~~~~~~~~
|
||||
|
||||
``postFlush`` is called at the end of ``EntityManager#flush()``.
|
||||
``EntityManager#flush()`` can **NOT** be called safely inside its listeners.
|
||||
``postFlush`` is called at the end of ``EntityManager::flush()``.
|
||||
``EntityManager::flush()`` can **NOT** be called safely inside its listeners.
|
||||
This event is not a lifecycle callback.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -705,25 +600,27 @@ postFlush
|
||||
}
|
||||
}
|
||||
|
||||
.. _reference-events-pre-update:
|
||||
|
||||
preUpdate
|
||||
~~~~~~~~~
|
||||
|
||||
PreUpdate is called inside the ``EntityManager#flush()`` method,
|
||||
PreUpdate is called inside the ``EntityManager::flush()`` method,
|
||||
right before an SQL ``UPDATE`` statement. This event is not
|
||||
triggered when the computed changeset is empty.
|
||||
triggered when the computed changeset is empty, nor for a DQL
|
||||
``UPDATE`` statement.
|
||||
|
||||
Changes to associations of the updated entity are never allowed in
|
||||
this event, since Doctrine cannot guarantee to correctly handle
|
||||
referential integrity at this point of the flush operation. This
|
||||
event has a powerful feature however, it is executed with a
|
||||
``PreUpdateEventArgs`` instance, which contains a reference to the
|
||||
`_PreUpdateEventArgs`_ instance, which contains a reference to the
|
||||
computed change-set of this entity.
|
||||
|
||||
This means you have access to all the fields that have changed for
|
||||
this entity with their old and new value. The following methods are
|
||||
available on the ``PreUpdateEventArgs``:
|
||||
|
||||
|
||||
- ``getEntity()`` to get access to the actual entity.
|
||||
- ``getEntityChangeSet()`` to get a copy of the changeset array.
|
||||
Changes to this returned array do not affect updating.
|
||||
@@ -777,32 +674,70 @@ lifecycle callback when there are expensive validations to call:
|
||||
|
||||
Restrictions for this event:
|
||||
|
||||
|
||||
- Changes to associations of the passed entities are not
|
||||
recognized by the flush operation anymore.
|
||||
- Changes to fields of the passed entities are not recognized by
|
||||
the flush operation anymore, use the computed change-set passed to
|
||||
the event to modify primitive field values, e.g. use
|
||||
``$eventArgs->setNewValue($field, $value);`` as in the Alice to Bob example above.
|
||||
- Any calls to ``EntityManager#persist()`` or
|
||||
``EntityManager#remove()``, even in combination with the ``UnitOfWork``
|
||||
- Any calls to ``EntityManager::persist()`` or
|
||||
``EntityManager::remove()``, even in combination with the ``UnitOfWork``
|
||||
API are strongly discouraged and don't work as expected outside the
|
||||
flush operation.
|
||||
|
||||
.. _reference-events-post-update-remove-persist:
|
||||
|
||||
postUpdate, postRemove, postPersist
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The three post events are called inside ``EntityManager#flush()``.
|
||||
These three post* events are called inside ``EntityManager::flush()``.
|
||||
Changes in here are not relevant to the persistence in the
|
||||
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.
|
||||
|
||||
- The ``postUpdate`` event occurs after the database
|
||||
update operations to entity data. It is not called for a DQL
|
||||
``UPDATE`` statement.
|
||||
- The ``postPersist`` event occurs for an entity after
|
||||
the entity has been made persistent. It will be invoked after the
|
||||
database insert operations. Generated primary key values are
|
||||
available in the postPersist event.
|
||||
- The ``postRemove`` event occurs for an entity after the
|
||||
entity has been deleted. It will be invoked after the database
|
||||
delete operations. It is not called for a DQL ``DELETE`` statement.
|
||||
|
||||
.. warning::
|
||||
|
||||
The ``postRemove`` event or any events triggered after an entity removal
|
||||
can receive an uninitializable proxy in case you have configured an entity to
|
||||
cascade remove relations. In this case, you should load yourself the proxy in
|
||||
the associated ``pre*`` event.
|
||||
|
||||
.. _reference-events-post-load:
|
||||
|
||||
postLoad
|
||||
~~~~~~~~
|
||||
|
||||
This event is called after an entity is constructed by the
|
||||
EntityManager.
|
||||
The postLoad event occurs after the entity has been loaded into the current
|
||||
``EntityManager`` from the database or after ``refresh()`` has been applied to it.
|
||||
|
||||
.. warning::
|
||||
|
||||
When using ``Doctrine\ORM\AbstractQuery::toIterable()``, ``postLoad``
|
||||
events will be executed immediately after objects are being hydrated, and therefore
|
||||
associations are not guaranteed to be initialized. It is not safe to combine
|
||||
usage of ``Doctrine\ORM\AbstractQuery::toIterable()`` and ``postLoad`` event
|
||||
handlers.
|
||||
|
||||
.. _reference-events-on-clear:
|
||||
|
||||
onClear
|
||||
~~~~~~~~
|
||||
|
||||
The ``onClear`` event occurs when the ``EntityManager::clear()`` operation is invoked,
|
||||
after all references to entities have been removed from the unit of work.
|
||||
This event is not a lifecycle callback.
|
||||
|
||||
Entity listeners
|
||||
----------------
|
||||
@@ -814,7 +749,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;
|
||||
@@ -1010,12 +957,16 @@ Implementing your own resolver :
|
||||
$configurations->setEntityListenerResolver(new MyEntityListenerResolver);
|
||||
EntityManager::create(.., $configurations, ..);
|
||||
|
||||
.. _reference-events-load-class-metadata:
|
||||
|
||||
Load ClassMetadata Event
|
||||
------------------------
|
||||
|
||||
When the mapping information for an entity is read, it is populated
|
||||
in to a ``Doctrine\ORM\Mapping\ClassMetadata`` instance. You can hook in to this
|
||||
process and manipulate the instance.
|
||||
``loadClassMetadata`` - The ``loadClassMetadata`` event occurs after the
|
||||
mapping metadata for a class has been loaded from a mapping source
|
||||
(annotations/xml/yaml) in to a ``Doctrine\ORM\Mapping\ClassMetadata`` instance.
|
||||
You can hook in to this process and manipulate the instance.
|
||||
This event is not a lifecycle callback.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -1038,6 +989,11 @@ process and manipulate the instance.
|
||||
}
|
||||
}
|
||||
|
||||
If not class metadata can be found, an ``onClassMetadataNotFound`` event is dispatched.
|
||||
Manipulating the given event args instance
|
||||
allows providing fallback metadata even when no actual metadata exists
|
||||
or could be found. This event is not a lifecycle callback.
|
||||
|
||||
SchemaTool Events
|
||||
-----------------
|
||||
|
||||
@@ -1090,3 +1046,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
|
||||
|
||||
@@ -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 doesn’t 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
|
||||
|
||||
@@ -521,6 +521,8 @@ For each cascade operation that gets activated, Doctrine also
|
||||
applies that operation to the association, be it single or
|
||||
collection valued.
|
||||
|
||||
.. _persistence-by-reachability:
|
||||
|
||||
Persistence by Reachability: Cascade Persist
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
+10
-4
@@ -295,7 +295,7 @@
|
||||
</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" />
|
||||
@@ -353,7 +353,7 @@
|
||||
<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"/>
|
||||
@@ -402,7 +402,7 @@
|
||||
</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" />
|
||||
@@ -497,6 +497,12 @@
|
||||
</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"/>
|
||||
@@ -612,7 +618,7 @@
|
||||
<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" />
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -330,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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -32,12 +32,12 @@ class SingleScalarHydrator extends AbstractHydrator
|
||||
throw new NonUniqueResultException('The query returned multiple rows. Change the query or use a different result function like getScalarResult().');
|
||||
}
|
||||
|
||||
if (count($data[key($data)]) > 1) {
|
||||
$result = $this->gatherScalarRowData($data[key($data)]);
|
||||
|
||||
if (count($result) > 1) {
|
||||
throw new NonUniqueResultException('The query returned a row containing multiple columns. Change the query or use a different result function like getScalarResult().');
|
||||
}
|
||||
|
||||
$result = $this->gatherScalarRowData($data[key($data)]);
|
||||
|
||||
return array_shift($result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
/**
|
||||
* A lazy collection that allows a fast count when using criteria object
|
||||
@@ -38,6 +39,7 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
if ($this->isInitialized()) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,12 +10,12 @@ use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use ReturnTypeWillChange;
|
||||
use RuntimeException;
|
||||
|
||||
use function array_combine;
|
||||
use function array_diff_key;
|
||||
use function array_map;
|
||||
use function array_udiff_assoc;
|
||||
use function array_values;
|
||||
use function array_walk;
|
||||
use function get_class;
|
||||
@@ -505,6 +505,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
|
||||
*
|
||||
* @return object|null
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
return $this->remove($offset);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1933,20 +1933,7 @@ class BasicEntityPersister implements EntityPersister
|
||||
return [$newValue];
|
||||
}
|
||||
|
||||
if (is_object($value) && $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) {
|
||||
$class = $this->em->getClassMetadata(get_class($value));
|
||||
if ($class->isIdentifierComposite) {
|
||||
$newValue = [];
|
||||
|
||||
foreach ($class->getIdentifierValues($value) as $innerValue) {
|
||||
$newValue = array_merge($newValue, $this->getValues($innerValue));
|
||||
}
|
||||
|
||||
return $newValue;
|
||||
}
|
||||
}
|
||||
|
||||
return [$this->getIndividualValue($value)];
|
||||
return $this->getIndividualValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1954,15 +1941,34 @@ class BasicEntityPersister implements EntityPersister
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
* @return array<mixed>
|
||||
* @psalm-return list<mixed>
|
||||
*/
|
||||
private function getIndividualValue($value)
|
||||
{
|
||||
if (! is_object($value) || ! $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) {
|
||||
return $value;
|
||||
if (! is_object($value)) {
|
||||
return [$value];
|
||||
}
|
||||
|
||||
return $this->em->getUnitOfWork()->getSingleIdentifierValue($value);
|
||||
$valueClass = ClassUtils::getClass($value);
|
||||
|
||||
if ($this->em->getMetadataFactory()->isTransient($valueClass)) {
|
||||
return [$value];
|
||||
}
|
||||
|
||||
$class = $this->em->getClassMetadata($valueClass);
|
||||
|
||||
if ($class->isIdentifierComposite) {
|
||||
$newValue = [];
|
||||
|
||||
foreach ($class->getIdentifierValues($value) as $innerValue) {
|
||||
$newValue = array_merge($newValue, $this->getValues($innerValue));
|
||||
}
|
||||
|
||||
return $newValue;
|
||||
}
|
||||
|
||||
return [$this->em->getUnitOfWork()->getSingleIdentifierValue($value)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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'];
|
||||
|
||||
@@ -90,7 +90,7 @@ class XmlExporter extends AbstractExporter
|
||||
$trackingPolicy = $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy);
|
||||
|
||||
if ($trackingPolicy !== 'DEFERRED_IMPLICIT') {
|
||||
$root->addChild('change-tracking-policy', $trackingPolicy);
|
||||
$root->addAttribute('change-tracking-policy', $trackingPolicy);
|
||||
}
|
||||
|
||||
if (isset($metadata->table['indexes'])) {
|
||||
@@ -178,10 +178,7 @@ class XmlExporter extends AbstractExporter
|
||||
$fieldXml = $root->addChild('field');
|
||||
$fieldXml->addAttribute('name', $field['fieldName']);
|
||||
$fieldXml->addAttribute('type', $field['type']);
|
||||
|
||||
if (isset($field['columnName'])) {
|
||||
$fieldXml->addAttribute('column', $field['columnName']);
|
||||
}
|
||||
$fieldXml->addAttribute('column', $field['columnName']);
|
||||
|
||||
if (isset($field['length'])) {
|
||||
$fieldXml->addAttribute('length', (string) $field['length']);
|
||||
|
||||
@@ -28,7 +28,7 @@ use function is_array;
|
||||
use function reset;
|
||||
|
||||
/**
|
||||
* Replaces the whereClause of the AST with a WHERE id IN (:foo_1, :foo_2) equivalent.
|
||||
* Appends a condition "id IN (:foo_1, :foo_2)" to the whereClause of the AST.
|
||||
*/
|
||||
class WhereInWalker extends TreeWalkerAdapter
|
||||
{
|
||||
@@ -43,9 +43,7 @@ class WhereInWalker extends TreeWalkerAdapter
|
||||
public const PAGINATOR_ID_ALIAS = 'dpid';
|
||||
|
||||
/**
|
||||
* Replaces the whereClause in the AST.
|
||||
*
|
||||
* Generates a clause equivalent to WHERE IN (:dpid_1, :dpid_2, ...)
|
||||
* Appends a condition equivalent to "WHERE IN (:dpid_1, :dpid_2, ...)" to the whereClause of the AST.
|
||||
*
|
||||
* The parameter namespace (dpid) is defined by
|
||||
* the PAGINATOR_ID_ALIAS
|
||||
|
||||
@@ -239,7 +239,7 @@ class SchemaValidator
|
||||
}
|
||||
}
|
||||
|
||||
if (! $class->isInheritanceTypeNone() && ! $class->isRootEntity() && ! $class->reflClass->isAbstract() && ! $class->isMappedSuperclass && array_search($class->name, $class->discriminatorMap, true) === false) {
|
||||
if (! $class->isInheritanceTypeNone() && ! $class->isRootEntity() && ! $class->isMappedSuperclass && array_search($class->name, $class->discriminatorMap, true) === false) {
|
||||
$ce[] = "Entity class '" . $class->name . "' is part of inheritance hierarchy, but is " .
|
||||
"not mapped in the root entity '" . $class->rootEntityName . "' discriminator map. " .
|
||||
'All subclasses must be listed in the discriminator map.';
|
||||
|
||||
@@ -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));
|
||||
@@ -1159,14 +1166,16 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$identifier = [];
|
||||
|
||||
foreach ($class->getIdentifierFieldNames() as $idField) {
|
||||
$value = $class->getFieldValue($entity, $idField);
|
||||
$origValue = $class->getFieldValue($entity, $idField);
|
||||
|
||||
$value = null;
|
||||
if (isset($class->associationMappings[$idField])) {
|
||||
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
|
||||
$value = $this->getSingleIdentifierValue($value);
|
||||
$value = $this->getSingleIdentifierValue($origValue);
|
||||
}
|
||||
|
||||
$identifier[$idField] = $this->originalEntityData[$oid][$idField] = $value;
|
||||
$identifier[$idField] = $value ?? $origValue;
|
||||
$this->originalEntityData[$oid][$idField] = $origValue;
|
||||
}
|
||||
|
||||
$this->entityStates[$oid] = self::STATE_MANAGED;
|
||||
|
||||
@@ -134,11 +134,6 @@
|
||||
<exclude-pattern>lib/Doctrine/ORM/Cache/DefaultQueryCache.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.Commenting.UselessInheritDocComment.UselessInheritDocComment">
|
||||
<!-- Workaround for https://github.com/slevomat/coding-standard/issues/1233 -->
|
||||
<exclude-pattern>lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming">
|
||||
<exclude-pattern>lib/Doctrine/ORM/EntityManagerInterface.php</exclude-pattern>
|
||||
</rule>
|
||||
@@ -265,14 +260,6 @@
|
||||
<exclude-pattern>tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1843Test.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="PSR2.ControlStructures.SwitchDeclaration.TerminatingComment">
|
||||
<!--
|
||||
Remove when upgrading to squizlabs/php_codesniffer 3.6.0
|
||||
https://github.com/squizlabs/PHP_CodeSniffer/pull/3186
|
||||
-->
|
||||
<exclude-pattern>lib/Doctrine/ORM/Query/Parser.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="Generic.CodeAnalysis.EmptyStatement.DetectedElse">
|
||||
<!-- The missing code needs to be implemented someday -->
|
||||
<exclude-pattern>lib/Doctrine/ORM/Id/TableGenerator.php</exclude-pattern>
|
||||
|
||||
+216
-62
@@ -100,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
|
||||
@@ -180,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
|
||||
@@ -235,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
|
||||
@@ -417,7 +437,12 @@ parameters:
|
||||
|
||||
-
|
||||
message: "#^Negated boolean expression is always false\\.$#"
|
||||
count: 2
|
||||
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
|
||||
|
||||
-
|
||||
@@ -525,31 +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
|
||||
path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
|
||||
|
||||
-
|
||||
message: "#^Call to an undefined method ReflectionProperty\\:\\:hasType\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\NamingStrategy\\:\\:joinColumnName\\(\\) invoked with 2 parameters, 1 required\\.$#"
|
||||
count: 2
|
||||
@@ -561,13 +561,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
|
||||
|
||||
-
|
||||
@@ -765,6 +765,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
|
||||
@@ -960,6 +965,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
|
||||
@@ -1115,6 +1125,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
|
||||
@@ -1321,14 +1336,9 @@ parameters:
|
||||
path: lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$start of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getSubstringExpression\\(\\) expects int, string given\\.$#"
|
||||
message: "#^Property Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\TrimFunction\\:\\:\\$trimChar \\(bool\\) does not accept string\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$length of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getSubstringExpression\\(\\) expects int\\|null, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php
|
||||
path: lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$simpleArithmeticExpr of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkSimpleArithmeticExpression\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\SimpleArithmeticExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#"
|
||||
@@ -1380,15 +1390,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\\.$#"
|
||||
@@ -1401,35 +1426,54 @@ parameters:
|
||||
path: lib/Doctrine/ORM/Query/Parser.php
|
||||
|
||||
-
|
||||
message:
|
||||
"""
|
||||
#^PHPDoc tag @return has invalid value \\(AST\\\\BetweenExpression\\|
|
||||
AST\\\\CollectionMemberExpression\\|
|
||||
AST\\\\ComparisonExpression\\|
|
||||
AST\\\\EmptyCollectionComparisonExpression\\|
|
||||
AST\\\\ExistsExpression\\|
|
||||
AST\\\\InExpression\\|
|
||||
AST\\\\InstanceOfExpression\\|
|
||||
AST\\\\LikeExpression\\|
|
||||
AST\\\\NullComparisonExpression\\)\\: Unexpected token "\\\\n \\* ", expected type at offset 344$#
|
||||
"""
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\Parser\\:\\:ArithmeticTerm\\(\\) should return Doctrine\\\\ORM\\\\Query\\\\AST\\\\ArithmeticTerm but returns Doctrine\\\\ORM\\\\Query\\\\AST\\\\ArithmeticFactor\\|string\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Query/Parser.php
|
||||
|
||||
-
|
||||
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\Parser\\:\\:SimpleArithmeticExpression\\(\\) should return Doctrine\\\\ORM\\\\Query\\\\AST\\\\SimpleArithmeticExpression but returns Doctrine\\\\ORM\\\\Query\\\\AST\\\\ArithmeticTerm\\|string\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Query/Parser.php
|
||||
|
||||
-
|
||||
message: """
|
||||
#^PHPDoc tag @return has invalid value \\(AST\\\\BetweenExpression\\|
|
||||
AST\\\\CollectionMemberExpression\\|
|
||||
AST\\\\ComparisonExpression\\|
|
||||
AST\\\\EmptyCollectionComparisonExpression\\|
|
||||
AST\\\\ExistsExpression\\|
|
||||
AST\\\\InExpression\\|
|
||||
AST\\\\InstanceOfExpression\\|
|
||||
AST\\\\LikeExpression\\|
|
||||
AST\\\\NullComparisonExpression\\)\\: Unexpected token "\\\\n \\* ", expected type at offset 344$#
|
||||
"""
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Query/Parser.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$tokenType of method Doctrine\\\\ORM\\\\Query\\\\Parser\\:\\:isAggregateFunction\\(\\) expects 1\\|2\\|3\\|4\\|5\\|6\\|7\\|8\\|9\\|10\\|11\\|12\\|13\\|14\\|15\\|16\\|17\\|18\\|19\\|100\\|101\\|102\\|200\\|201\\|202\\|203\\|204\\|205\\|206\\|207\\|208\\|209\\|210\\|211\\|212\\|213\\|214\\|215\\|216\\|217\\|218\\|219\\|220\\|221\\|222\\|223\\|224\\|225\\|226\\|227\\|228\\|229\\|230\\|231\\|232\\|233\\|234\\|235\\|236\\|237\\|238\\|239\\|240\\|241\\|242\\|243\\|244\\|245\\|246\\|247\\|248\\|249\\|250\\|251\\|252\\|253\\|254\\|255\\|256, int\\|string\\|null given\\.$#"
|
||||
count: 2
|
||||
path: lib/Doctrine/ORM/Query/Parser.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$stringPattern of class Doctrine\\\\ORM\\\\Query\\\\AST\\\\LikeExpression constructor expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\InputParameter, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#"
|
||||
count: 1
|
||||
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\\.$#"
|
||||
@@ -1476,6 +1520,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
|
||||
@@ -1982,7 +2036,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
|
||||
|
||||
@@ -2021,11 +2075,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
|
||||
@@ -2062,7 +2111,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
|
||||
|
||||
@@ -2096,6 +2185,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
|
||||
@@ -2106,6 +2225,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
|
||||
@@ -2116,6 +2240,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
|
||||
@@ -2126,6 +2260,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
|
||||
@@ -2161,6 +2300,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
|
||||
|
||||
@@ -25,3 +25,15 @@ parameters:
|
||||
message: '/^Call to an undefined method Doctrine\\Common\\Cache\\Cache::deleteAll\(\)\.$/'
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php
|
||||
|
||||
|
||||
# Won't get fixed in DBAL 2
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$start of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getSubstringExpression\\(\\) expects int, string given\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$length of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getSubstringExpression\\(\\) expects int\\|null, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ parameters:
|
||||
earlyTerminatingMethodCalls:
|
||||
Doctrine\ORM\Query\Parser:
|
||||
- syntaxError
|
||||
phpVersion: 70100
|
||||
phpVersion: 80100
|
||||
|
||||
+1
-10
@@ -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\.$/'
|
||||
|
||||
+142
-172
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<files psalm-version="4.10.0@916b098b008f6de4543892b1e0651c1c3b92cbfa">
|
||||
<files psalm-version="4.18.1@dda05fa913f4dc6eb3386f2f7ce5a45d37a71bcb">
|
||||
<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 && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)</code>
|
||||
</DocblockTypeContradiction>
|
||||
<FalsableReturnStatement occurrences="1">
|
||||
<code>! $filteredParameters->isEmpty() ? $filteredParameters->first() : null</code>
|
||||
</FalsableReturnStatement>
|
||||
@@ -105,9 +102,6 @@
|
||||
<MissingReturnType occurrences="1">
|
||||
<code>loadCacheEntry</code>
|
||||
</MissingReturnType>
|
||||
<PossiblyUndefinedArrayOffset occurrences="1">
|
||||
<code>$fieldMapping['columnName']</code>
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
<UndefinedInterfaceMethod occurrences="1">
|
||||
<code>getCacheRegion</code>
|
||||
</UndefinedInterfaceMethod>
|
||||
@@ -428,9 +422,6 @@
|
||||
<MissingReturnType occurrences="1">
|
||||
<code>wrapInTransaction</code>
|
||||
</MissingReturnType>
|
||||
<NonInvariantDocblockPropertyType occurrences="1">
|
||||
<code>$wrapped</code>
|
||||
</NonInvariantDocblockPropertyType>
|
||||
<TooManyArguments occurrences="2">
|
||||
<code>find</code>
|
||||
<code>flush</code>
|
||||
@@ -518,15 +509,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->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">
|
||||
@@ -555,6 +547,9 @@
|
||||
<code>?T</code>
|
||||
<code>Collection<int, T></code>
|
||||
</InvalidReturnType>
|
||||
<LessSpecificImplementedReturnType occurrences="1">
|
||||
<code>string</code>
|
||||
</LessSpecificImplementedReturnType>
|
||||
<PropertyTypeCoercion occurrences="1">
|
||||
<code>$em</code>
|
||||
</PropertyTypeCoercion>
|
||||
@@ -604,9 +599,6 @@
|
||||
<PossiblyNullPropertyAssignmentValue occurrences="1">
|
||||
<code>$sequenceName</code>
|
||||
</PossiblyNullPropertyAssignmentValue>
|
||||
<RedundantCastGivenDocblockType occurrences="1">
|
||||
<code>(string) $em->getConnection()->lastInsertId($this->sequenceName)</code>
|
||||
</RedundantCastGivenDocblockType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Id/TableGenerator.php">
|
||||
<PossiblyFalseOperand occurrences="3">
|
||||
@@ -717,11 +709,6 @@
|
||||
<code>matching</code>
|
||||
</UndefinedInterfaceMethod>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Mapping/AnsiQuoteStrategy.php">
|
||||
<PossiblyUndefinedArrayOffset occurrences="1">
|
||||
<code>$class->fieldMappings[$fieldName]['columnName']</code>
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Mapping/AssociationOverride.php">
|
||||
<MissingConstructor occurrences="5">
|
||||
<code>$fetch</code>
|
||||
@@ -777,9 +764,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>
|
||||
@@ -821,12 +806,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="2">
|
||||
<code>$class->reflClass</code>
|
||||
<code>! $definition</code>
|
||||
<code>$definition</code>
|
||||
</DocblockTypeContradiction>
|
||||
<InvalidArrayOffset occurrences="1">
|
||||
@@ -869,6 +855,9 @@
|
||||
<code>$parent->table</code>
|
||||
<code>$parent->versionField</code>
|
||||
</NoInterfaceProperties>
|
||||
<PossiblyInvalidArrayAssignment occurrences="1">
|
||||
<code>$subClass->table[$indexType][$indexName]</code>
|
||||
</PossiblyInvalidArrayAssignment>
|
||||
<PossiblyInvalidIterator occurrences="1">
|
||||
<code>$parentClass->table[$indexType]</code>
|
||||
</PossiblyInvalidIterator>
|
||||
@@ -890,7 +879,8 @@
|
||||
<PropertyTypeCoercion occurrences="1">
|
||||
<code>$subClass->table</code>
|
||||
</PropertyTypeCoercion>
|
||||
<RedundantConditionGivenDocblockType occurrences="1">
|
||||
<RedundantConditionGivenDocblockType occurrences="2">
|
||||
<code>$parent->idGenerator</code>
|
||||
<code>$parent->idGenerator</code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
<UndefinedInterfaceMethod occurrences="17">
|
||||
@@ -921,13 +911,17 @@
|
||||
<DeprecatedConstant occurrences="1">
|
||||
<code>self::GENERATOR_TYPE_UUID</code>
|
||||
</DeprecatedConstant>
|
||||
<DeprecatedMethod occurrences="1">
|
||||
<code>canEmulateSchemas</code>
|
||||
</DeprecatedMethod>
|
||||
<DeprecatedProperty occurrences="4">
|
||||
<code>$this->columnNames</code>
|
||||
<code>$this->columnNames</code>
|
||||
<code>$this->columnNames</code>
|
||||
<code>$this->columnNames</code>
|
||||
</DeprecatedProperty>
|
||||
<DocblockTypeContradiction occurrences="1">
|
||||
<DocblockTypeContradiction occurrences="2">
|
||||
<code>! $this->table</code>
|
||||
<code>$this->table</code>
|
||||
</DocblockTypeContradiction>
|
||||
<InvalidDocblock occurrences="4">
|
||||
@@ -936,9 +930,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>
|
||||
@@ -967,7 +962,8 @@
|
||||
<MoreSpecificReturnType occurrences="1">
|
||||
<code>array{usage: int, region: string|null}</code>
|
||||
</MoreSpecificReturnType>
|
||||
<NullableReturnStatement occurrences="2">
|
||||
<NullableReturnStatement occurrences="3">
|
||||
<code>$this->reflClass</code>
|
||||
<code>$this->reflFields[$name]</code>
|
||||
<code>$this->reflFields[$this->identifier[0]]</code>
|
||||
</NullableReturnStatement>
|
||||
@@ -987,12 +983,17 @@
|
||||
<code>$queryMapping['resultClass']</code>
|
||||
<code>$reflService->getAccessibleProperty($mapping['originalClass'], $mapping['originalField'])</code>
|
||||
</PossiblyNullArgument>
|
||||
<PossiblyNullPropertyAssignmentValue occurrences="3">
|
||||
<code>$reflService->getClass($this->name)</code>
|
||||
<code>$reflService->getClass($this->name)</code>
|
||||
<PossiblyNullPropertyAssignmentValue occurrences="1">
|
||||
<code>null</code>
|
||||
</PossiblyNullPropertyAssignmentValue>
|
||||
<PossiblyNullReference occurrences="6">
|
||||
<PossiblyNullPropertyFetch occurrences="2">
|
||||
<code>$embeddable->reflClass->name</code>
|
||||
<code>$this->reflClass->name</code>
|
||||
</PossiblyNullPropertyFetch>
|
||||
<PossiblyNullReference occurrences="9">
|
||||
<code>getProperty</code>
|
||||
<code>getProperty</code>
|
||||
<code>getProperty</code>
|
||||
<code>getValue</code>
|
||||
<code>getValue</code>
|
||||
<code>getValue</code>
|
||||
@@ -1000,25 +1001,15 @@
|
||||
<code>setValue</code>
|
||||
<code>setValue</code>
|
||||
</PossiblyNullReference>
|
||||
<PossiblyUndefinedArrayOffset occurrences="11">
|
||||
<code>$fieldMapping['columnName']</code>
|
||||
<code>$fieldMapping['columnName']</code>
|
||||
<code>$mapping['columnName']</code>
|
||||
<PossiblyUndefinedArrayOffset occurrences="3">
|
||||
<code>$mapping['originalClass']</code>
|
||||
<code>$mapping['originalField']</code>
|
||||
<code>$mapping['targetEntity']</code>
|
||||
<code>$this->fieldMappings[$field]['columnName']</code>
|
||||
<code>$this->fieldMappings[$field]['columnName']</code>
|
||||
<code>$this->fieldMappings[$idProperty]['columnName']</code>
|
||||
<code>$this->fieldMappings[$idProperty]['columnName']</code>
|
||||
<code>$this->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>
|
||||
@@ -1041,21 +1032,14 @@
|
||||
<code>$mapping !== false</code>
|
||||
<code>$mapping !== false</code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
<RedundantPropertyInitializationCheck occurrences="1"/>
|
||||
<RedundantFunctionCall occurrences="1">
|
||||
<code>array_values</code>
|
||||
</RedundantFunctionCall>
|
||||
<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>
|
||||
@@ -1084,13 +1068,13 @@
|
||||
</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>
|
||||
<PossiblyUndefinedArrayOffset occurrences="2">
|
||||
<code>$class->fieldMappings[$fieldName]['columnName']</code>
|
||||
<code>$class->fieldMappings[$fieldName]['columnName']</code>
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php">
|
||||
<PossiblyNullPropertyAssignmentValue occurrences="4">
|
||||
@@ -1104,9 +1088,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->inheritanceType</code>
|
||||
<code>$metadata->isEmbeddedClass</code>
|
||||
@@ -1121,10 +1110,6 @@
|
||||
<code>$primaryTable['indexes']</code>
|
||||
<code>$primaryTable['uniqueConstraints']</code>
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
<RedundantConditionGivenDocblockType occurrences="2">
|
||||
<code>isset($column->columnDefinition)</code>
|
||||
<code>isset($column->name)</code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
<UndefinedInterfaceMethod occurrences="32">
|
||||
<code>addEntityListener</code>
|
||||
<code>addLifecycleCallback</code>
|
||||
@@ -1173,6 +1158,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>
|
||||
@@ -1189,10 +1178,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->columnDefinition)</code>
|
||||
<code>isset($column->name)</code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php">
|
||||
@@ -1287,11 +1274,7 @@
|
||||
<code>$indexXml->options</code>
|
||||
<code>$uniqueXml->options</code>
|
||||
</PossiblyInvalidPropertyFetch>
|
||||
<RedundantCast occurrences="1">
|
||||
<code>(string) $attributes->name</code>
|
||||
</RedundantCast>
|
||||
<RedundantCondition occurrences="20">
|
||||
<code>isset($attributes->name)</code>
|
||||
<RedundantCondition occurrences="19">
|
||||
<code>isset($xmlRoot->cache)</code>
|
||||
<code>isset($xmlRoot->embedded)</code>
|
||||
<code>isset($xmlRoot->field)</code>
|
||||
@@ -1583,16 +1566,7 @@
|
||||
<InvalidDocblock occurrences="1">
|
||||
<code>final class PersistentCollection extends AbstractLazyCollection implements Selectable</code>
|
||||
</InvalidDocblock>
|
||||
<InvalidReturnStatement occurrences="5">
|
||||
<code>$persister->slice($this, $offset, $length)</code>
|
||||
<code>$this->collection</code>
|
||||
<code>new ArrayCollection($persister->loadCriteria($this, $criteria))</code>
|
||||
<code>parent::slice($offset, $length)</code>
|
||||
</InvalidReturnStatement>
|
||||
<InvalidReturnType occurrences="2">
|
||||
<code>Collection<TKey, T></code>
|
||||
<code>array<TKey,T></code>
|
||||
</InvalidReturnType>
|
||||
<InvalidReturnStatement occurrences="1"/>
|
||||
<MissingParamType occurrences="2">
|
||||
<code>$key</code>
|
||||
<code>$offset</code>
|
||||
@@ -1627,7 +1601,9 @@
|
||||
<PropertyNotSetInConstructor occurrences="1">
|
||||
<code>$backRefFieldName</code>
|
||||
</PropertyNotSetInConstructor>
|
||||
<RedundantConditionGivenDocblockType occurrences="3">
|
||||
<RedundantConditionGivenDocblockType occurrences="5">
|
||||
<code>$this->em</code>
|
||||
<code>$this->em</code>
|
||||
<code>is_object($this->collection)</code>
|
||||
<code>is_object($value) && $this->em</code>
|
||||
<code>is_object($value) && $this->em</code>
|
||||
@@ -1783,11 +1759,6 @@
|
||||
<code>$targetClass->associationMappings</code>
|
||||
</PossiblyNullArrayOffset>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php">
|
||||
<PossiblyUndefinedArrayOffset occurrences="1">
|
||||
<code>$fieldMapping['columnName']</code>
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php">
|
||||
<ArgumentTypeCoercion occurrences="1">
|
||||
<code>$this->em</code>
|
||||
@@ -1843,19 +1814,12 @@
|
||||
<code>getValue</code>
|
||||
<code>setValue</code>
|
||||
</PossiblyNullReference>
|
||||
<PossiblyUndefinedArrayOffset occurrences="2">
|
||||
<code>$fieldMapping['columnName']</code>
|
||||
<code>$fieldMapping['columnName']</code>
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
<PropertyNotSetInConstructor occurrences="1">
|
||||
<code>$insertSql</code>
|
||||
</PropertyNotSetInConstructor>
|
||||
<PropertyTypeCoercion occurrences="1">
|
||||
<code>$this->currentPersisterContext->sqlTableAliases</code>
|
||||
</PropertyTypeCoercion>
|
||||
<RedundantCondition occurrences="1">
|
||||
<code>($comparison === Comparison::EQ || $comparison === Comparison::IS) && $value === null</code>
|
||||
</RedundantCondition>
|
||||
<RedundantConditionGivenDocblockType occurrences="1">
|
||||
<code>$this->insertSql !== null</code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
@@ -1896,11 +1860,6 @@
|
||||
<code>$columnList</code>
|
||||
</PossiblyUndefinedVariable>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Persisters/SqlValueVisitor.php">
|
||||
<RedundantCondition occurrences="1">
|
||||
<code>($operator === Comparison::EQ || $operator === Comparison::IS) && $value === null</code>
|
||||
</RedundantCondition>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Proxy/ProxyFactory.php">
|
||||
<ArgumentTypeCoercion occurrences="2">
|
||||
<code>$classMetadata</code>
|
||||
@@ -2163,9 +2122,9 @@
|
||||
<PossiblyNullArrayOffset occurrences="1">
|
||||
<code>$class->associationMappings</code>
|
||||
</PossiblyNullArrayOffset>
|
||||
<PossiblyUndefinedArrayOffset occurrences="1">
|
||||
<code>$field['columnName']</code>
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
<PossiblyNullPropertyAssignmentValue occurrences="1">
|
||||
<code>$parser->getLexer()->token['value']</code>
|
||||
</PossiblyNullPropertyAssignmentValue>
|
||||
<PropertyNotSetInConstructor occurrences="2">
|
||||
<code>$fieldMapping</code>
|
||||
<code>$pathExpression</code>
|
||||
@@ -2183,7 +2142,6 @@
|
||||
</PropertyNotSetInConstructor>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/AST/Functions/LocateFunction.php">
|
||||
<InvalidScalarArgument occurrences="1"/>
|
||||
<PossiblyInvalidArgument occurrences="1">
|
||||
<code>$this->simpleArithmeticExpression</code>
|
||||
</PossiblyInvalidArgument>
|
||||
@@ -2196,6 +2154,9 @@
|
||||
<ArgumentTypeCoercion occurrences="1">
|
||||
<code>$this->stringPrimary</code>
|
||||
</ArgumentTypeCoercion>
|
||||
<DeprecatedMethod occurrences="1">
|
||||
<code>getLowerExpression</code>
|
||||
</DeprecatedMethod>
|
||||
<PropertyNotSetInConstructor occurrences="1">
|
||||
<code>$stringPrimary</code>
|
||||
</PropertyNotSetInConstructor>
|
||||
@@ -2225,15 +2186,14 @@
|
||||
</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>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php">
|
||||
<InvalidScalarArgument occurrences="2">
|
||||
<code>$optionalSecondSimpleArithmeticExpression</code>
|
||||
<code>$sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression)</code>
|
||||
</InvalidScalarArgument>
|
||||
<PropertyNotSetInConstructor occurrences="2">
|
||||
<code>$firstSimpleArithmeticExpression</code>
|
||||
<code>$stringPrimary</code>
|
||||
@@ -2245,6 +2205,9 @@
|
||||
</PropertyNotSetInConstructor>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php">
|
||||
<InvalidPropertyAssignmentValue occurrences="1">
|
||||
<code>$lexer->token['value']</code>
|
||||
</InvalidPropertyAssignmentValue>
|
||||
<PossiblyNullArgument occurrences="1">
|
||||
<code>$value</code>
|
||||
</PossiblyNullArgument>
|
||||
@@ -2263,6 +2226,9 @@
|
||||
<ArgumentTypeCoercion occurrences="1">
|
||||
<code>$this->stringPrimary</code>
|
||||
</ArgumentTypeCoercion>
|
||||
<DeprecatedMethod occurrences="1">
|
||||
<code>getUpperExpression</code>
|
||||
</DeprecatedMethod>
|
||||
<PropertyNotSetInConstructor occurrences="1">
|
||||
<code>$stringPrimary</code>
|
||||
</PropertyNotSetInConstructor>
|
||||
@@ -2629,18 +2595,21 @@
|
||||
<code>call_user_func($functionClass, $functionName)</code>
|
||||
<code>call_user_func($functionClass, $functionName)</code>
|
||||
</DocblockTypeContradiction>
|
||||
<InvalidArrayOffset occurrences="2">
|
||||
<code>$this->identVariableExpressions[$dqlAlias]</code>
|
||||
<code>$this->queryComponents[$dqlAlias]</code>
|
||||
</InvalidArrayOffset>
|
||||
<InvalidNullableReturnType occurrences="1">
|
||||
<InvalidArgument occurrences="1">
|
||||
<code>$lookaheadType</code>
|
||||
</InvalidArgument>
|
||||
<InvalidNullableReturnType occurrences="6">
|
||||
<code>SelectStatement|UpdateStatement|DeleteStatement</code>
|
||||
<code>string</code>
|
||||
<code>string</code>
|
||||
<code>string</code>
|
||||
<code>string</code>
|
||||
<code>string</code>
|
||||
</InvalidNullableReturnType>
|
||||
<InvalidPropertyAssignmentValue occurrences="1">
|
||||
<code>$this->identVariableExpressions</code>
|
||||
</InvalidPropertyAssignmentValue>
|
||||
<InvalidReturnStatement occurrences="9">
|
||||
<InvalidReturnStatement occurrences="11">
|
||||
<code>$factors[0]</code>
|
||||
<code>$primary</code>
|
||||
<code>$terms[0]</code>
|
||||
<code>$this->CollectionMemberExpression()</code>
|
||||
<code>$this->ComparisonExpression()</code>
|
||||
<code>$this->EmptyCollectionComparisonExpression()</code>
|
||||
@@ -2650,9 +2619,11 @@
|
||||
<code>$this->LikeExpression()</code>
|
||||
<code>$this->NullComparisonExpression()</code>
|
||||
</InvalidReturnStatement>
|
||||
<InvalidReturnType occurrences="2">
|
||||
<InvalidReturnType occurrences="4">
|
||||
<code>AST\BetweenExpression|</code>
|
||||
<code>ArithmeticFactor</code>
|
||||
<code>ArithmeticTerm</code>
|
||||
<code>SimpleArithmeticExpression</code>
|
||||
</InvalidReturnType>
|
||||
<InvalidScalarArgument occurrences="3">
|
||||
<code>$this->lexer->getLiteral($token)</code>
|
||||
@@ -2690,7 +2661,7 @@
|
||||
<code>$this->ConditionalExpression()</code>
|
||||
<code>$this->ConditionalExpression()</code>
|
||||
</PossiblyInvalidPropertyAssignmentValue>
|
||||
<PossiblyNullArgument occurrences="23">
|
||||
<PossiblyNullArgument occurrences="22">
|
||||
<code>$aliasIdentVariable</code>
|
||||
<code>$dql</code>
|
||||
<code>$field</code>
|
||||
@@ -2698,7 +2669,6 @@
|
||||
<code>$functionName</code>
|
||||
<code>$functionName</code>
|
||||
<code>$functionName</code>
|
||||
<code>$lookaheadType</code>
|
||||
<code>$resultVariable</code>
|
||||
<code>$this->lexer->lookahead['value']</code>
|
||||
<code>$this->lexer->lookahead['value']</code>
|
||||
@@ -2715,7 +2685,7 @@
|
||||
<code>$token['value']</code>
|
||||
<code>$token['value']</code>
|
||||
</PossiblyNullArgument>
|
||||
<PossiblyNullArrayAccess occurrences="75">
|
||||
<PossiblyNullArrayAccess occurrences="74">
|
||||
<code>$glimpse['type']</code>
|
||||
<code>$glimpse['value']</code>
|
||||
<code>$lookahead['type']</code>
|
||||
@@ -2786,12 +2756,14 @@
|
||||
<code>$token['type']</code>
|
||||
<code>$token['type']</code>
|
||||
<code>$token['type']</code>
|
||||
<code>$token['type']</code>
|
||||
<code>$token['value']</code>
|
||||
<code>$token['value']</code>
|
||||
<code>$token['value']</code>
|
||||
<code>$token['value']</code>
|
||||
</PossiblyNullArrayAccess>
|
||||
<PossiblyNullPropertyAssignmentValue occurrences="1">
|
||||
<code>$value</code>
|
||||
</PossiblyNullPropertyAssignmentValue>
|
||||
<PossiblyNullReference occurrences="1">
|
||||
<code>getNumberOfRequiredParameters</code>
|
||||
</PossiblyNullReference>
|
||||
@@ -2843,27 +2815,33 @@
|
||||
<code>$renameMode</code>
|
||||
<code>$renameMode</code>
|
||||
</ArgumentTypeCoercion>
|
||||
<PossiblyUndefinedArrayOffset occurrences="1">
|
||||
<code>$class->fieldMappings[$this->fieldMappings[$columnName]]['columnName']</code>
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
<PossiblyNullPropertyFetch occurrences="1">
|
||||
<code>$classMetadata->reflClass->name</code>
|
||||
</PossiblyNullPropertyFetch>
|
||||
<PossiblyNullReference occurrences="3">
|
||||
<code>getShortName</code>
|
||||
<code>getShortName</code>
|
||||
<code>getShortName</code>
|
||||
</PossiblyNullReference>
|
||||
</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->stringPattern instanceof AST\Functions\FunctionNode</code>
|
||||
<code>$likeExpr->stringPattern instanceof AST\PathExpression</code>
|
||||
<code>$this->queryComponents[$dqlAlias]['relation'] === null</code>
|
||||
<code>''</code>
|
||||
<code>is_numeric($leftExpr) ? $leftExpr : $this->conn->quote($leftExpr)</code>
|
||||
<code>is_numeric($rightExpr) ? $rightExpr : $this->conn->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']->name</code>
|
||||
</InvalidArgument>
|
||||
<InvalidArrayOffset occurrences="1">
|
||||
@@ -2924,20 +2902,17 @@
|
||||
<PossiblyNullReference occurrences="1">
|
||||
<code>dispatch</code>
|
||||
</PossiblyNullReference>
|
||||
<PossiblyUndefinedArrayOffset occurrences="3">
|
||||
<code>$fieldMapping['columnName']</code>
|
||||
<code>$mapping['columnName']</code>
|
||||
<code>$mapping['columnName']</code>
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
<PropertyTypeCoercion occurrences="3">
|
||||
<code>$query</code>
|
||||
<code>$this->queryComponents</code>
|
||||
<code>$this->selectedClasses</code>
|
||||
</PropertyTypeCoercion>
|
||||
<RedundantConditionGivenDocblockType occurrences="5">
|
||||
<RedundantConditionGivenDocblockType occurrences="7">
|
||||
<code>$leftExpr instanceof AST\Node</code>
|
||||
<code>$likeExpr->stringPattern instanceof AST\InputParameter</code>
|
||||
<code>$rightExpr instanceof AST\Node</code>
|
||||
<code>$this->conn->quote($leftExpr)</code>
|
||||
<code>$this->conn->quote($rightExpr)</code>
|
||||
<code>$whereClause !== null</code>
|
||||
<code>($factor->not ? 'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary)</code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
@@ -3300,13 +3275,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">
|
||||
@@ -3337,9 +3308,6 @@
|
||||
<code>new ClassMetadataExporter()</code>
|
||||
<code>new EntityGenerator()</code>
|
||||
</DeprecatedClass>
|
||||
<InvalidNullableReturnType occurrences="1">
|
||||
<code>int</code>
|
||||
</InvalidNullableReturnType>
|
||||
<MissingReturnType occurrences="1">
|
||||
<code>configure</code>
|
||||
</MissingReturnType>
|
||||
@@ -3479,10 +3447,8 @@
|
||||
<DeprecatedConstant occurrences="1">
|
||||
<code>ClassMetadataInfo::GENERATOR_TYPE_UUID</code>
|
||||
</DeprecatedConstant>
|
||||
<DocblockTypeContradiction occurrences="3">
|
||||
<code>$metadata->reflClass !== null</code>
|
||||
<DocblockTypeContradiction occurrences="1">
|
||||
<code>class_exists($metadata->name)</code>
|
||||
<code>new ReflectionClass($metadata->name)</code>
|
||||
</DocblockTypeContradiction>
|
||||
<InvalidDocblock occurrences="1">
|
||||
<code>public function setFieldVisibility($visibility)</code>
|
||||
@@ -3508,12 +3474,13 @@
|
||||
<PropertyNotSetInConstructor occurrences="1">
|
||||
<code>$classToExtend</code>
|
||||
</PropertyNotSetInConstructor>
|
||||
<RedundantCastGivenDocblockType occurrences="1">
|
||||
<RedundantCastGivenDocblockType occurrences="2">
|
||||
<code>(array) $metadata->table['options']</code>
|
||||
<code>(bool) $embeddablesImmutable</code>
|
||||
</RedundantCastGivenDocblockType>
|
||||
<RedundantConditionGivenDocblockType occurrences="3">
|
||||
<code>$metadata->reflClass</code>
|
||||
<code>$metadata->reflClass !== null</code>
|
||||
<code>$metadata->sequenceGeneratorDefinition</code>
|
||||
<code>$metadata->sequenceGeneratorDefinition</code>
|
||||
<code>isset($metadata->lifecycleCallbacks)</code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
</file>
|
||||
@@ -3588,6 +3555,10 @@
|
||||
<NonInvariantDocblockPropertyType occurrences="1">
|
||||
<code>$_extension</code>
|
||||
</NonInvariantDocblockPropertyType>
|
||||
<RedundantConditionGivenDocblockType occurrences="2">
|
||||
<code>$metadata->table</code>
|
||||
<code>$metadata->table</code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php">
|
||||
<ArgumentTypeCoercion occurrences="3">
|
||||
@@ -3598,10 +3569,6 @@
|
||||
<DeprecatedClass occurrences="1">
|
||||
<code>AbstractExporter</code>
|
||||
</DeprecatedClass>
|
||||
<MissingClosureParamType occurrences="2">
|
||||
<code>$m1</code>
|
||||
<code>$m2</code>
|
||||
</MissingClosureParamType>
|
||||
<MissingPropertyType occurrences="1">
|
||||
<code>$metadata->inheritanceType</code>
|
||||
</MissingPropertyType>
|
||||
@@ -3614,10 +3581,12 @@
|
||||
<PossiblyNullReference occurrences="1">
|
||||
<code>addAttribute</code>
|
||||
</PossiblyNullReference>
|
||||
<RedundantCondition occurrences="1">
|
||||
<RedundantCondition occurrences="2">
|
||||
<code>$field['associationKey']</code>
|
||||
<code>isset($field['associationKey']) && $field['associationKey']</code>
|
||||
</RedundantCondition>
|
||||
<RedundantConditionGivenDocblockType occurrences="1">
|
||||
<RedundantConditionGivenDocblockType occurrences="2">
|
||||
<code>$sequenceDefinition</code>
|
||||
<code>isset($metadata->lifecycleCallbacks)</code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
</file>
|
||||
@@ -3645,15 +3614,16 @@
|
||||
<NonInvariantDocblockPropertyType occurrences="1">
|
||||
<code>$_extension</code>
|
||||
</NonInvariantDocblockPropertyType>
|
||||
<PossiblyUndefinedArrayOffset occurrences="1">
|
||||
<code>$fieldMapping['columnName']</code>
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
<RedundantConditionGivenDocblockType occurrences="2">
|
||||
<code>$metadata->table</code>
|
||||
<code>isset($metadata->lifecycleCallbacks)</code>
|
||||
</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>
|
||||
@@ -3707,7 +3677,8 @@
|
||||
</PropertyNotSetInConstructor>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php">
|
||||
<DocblockTypeContradiction occurrences="2">
|
||||
<DocblockTypeContradiction occurrences="3">
|
||||
<code>$AST->whereClause->conditionalExpression instanceof ConditionalFactor</code>
|
||||
<code>$AST->whereClause->conditionalExpression instanceof ConditionalPrimary</code>
|
||||
</DocblockTypeContradiction>
|
||||
<ImplementedReturnTypeMismatch occurrences="1">
|
||||
@@ -3719,21 +3690,20 @@
|
||||
<PossiblyInvalidPropertyAssignmentValue occurrences="1">
|
||||
<code>$AST->whereClause->conditionalExpression</code>
|
||||
</PossiblyInvalidPropertyAssignmentValue>
|
||||
<RedundantConditionGivenDocblockType occurrences="1">
|
||||
<code>$AST->whereClause->conditionalExpression instanceof ConditionalExpression</code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
<RedundantConditionGivenDocblockType occurrences="1"/>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Tools/SchemaTool.php">
|
||||
<DocblockTypeContradiction occurrences="1">
|
||||
<code>$definingClass</code>
|
||||
</DocblockTypeContradiction>
|
||||
<DeprecatedMethod occurrences="3">
|
||||
<code>canEmulateSchemas</code>
|
||||
<code>compare</code>
|
||||
<code>setExplicitForeignKeyIndexes</code>
|
||||
</DeprecatedMethod>
|
||||
<MissingClosureParamType occurrences="1">
|
||||
<code>$asset</code>
|
||||
</MissingClosureParamType>
|
||||
<PossiblyNullArrayAccess occurrences="2">
|
||||
<code>$definingClass</code>
|
||||
<PossiblyNullArgument occurrences="1">
|
||||
<code>$referencedFieldName</code>
|
||||
</PossiblyNullArrayAccess>
|
||||
</PossiblyNullArgument>
|
||||
<PossiblyNullReference occurrences="1">
|
||||
<code>getColumns</code>
|
||||
</PossiblyNullReference>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
errorLevel="2"
|
||||
phpVersion="8.1"
|
||||
resolveFromConfigFile="true"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
@@ -30,6 +31,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>
|
||||
@@ -89,12 +94,6 @@
|
||||
<file name="lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php"/>
|
||||
</errorLevel>
|
||||
</TypeDoesNotContainType>
|
||||
<UndefinedAttributeClass>
|
||||
<errorLevel type="suppress">
|
||||
<!-- The class was added in PHP 8.1 -->
|
||||
<referencedClass name="ReturnTypeWillChange"/>
|
||||
</errorLevel>
|
||||
</UndefinedAttributeClass>
|
||||
<UndefinedClass>
|
||||
<errorLevel type="suppress">
|
||||
<referencedClass name="Doctrine\Common\Cache\ApcCache"/>
|
||||
|
||||
@@ -5,14 +5,13 @@ declare(strict_types=1);
|
||||
namespace Doctrine\Performance\Mock;
|
||||
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Doctrine\Tests\ORM\Performance\PersisterMock;
|
||||
|
||||
/**
|
||||
* An unit of work mock that prevents lazy-loading of proxies
|
||||
*/
|
||||
class NonProxyLoadingUnitOfWork extends UnitOfWork
|
||||
{
|
||||
/** @var PersisterMock */
|
||||
/** @var NonLoadingPersister */
|
||||
private $entityPersister;
|
||||
|
||||
public function __construct()
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -21,7 +21,7 @@ class CacheLoggerChainTest extends DoctrineTestCase
|
||||
/** @var CacheLoggerChain */
|
||||
private $logger;
|
||||
|
||||
/** @var MockObject|CacheLogger */
|
||||
/** @var CacheLogger&MockObject */
|
||||
private $mock;
|
||||
|
||||
protected function setUp(): void
|
||||
|
||||
@@ -31,6 +31,7 @@ use Doctrine\Tests\Models\Cache\State;
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
use function assert;
|
||||
|
||||
@@ -39,7 +40,7 @@ use function assert;
|
||||
*/
|
||||
class DefaultCacheFactoryTest extends OrmTestCase
|
||||
{
|
||||
/** @var CacheFactory */
|
||||
/** @var CacheFactory&MockObject */
|
||||
private $factory;
|
||||
|
||||
/** @var EntityManager */
|
||||
|
||||
+5
-1
@@ -14,13 +14,14 @@ use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
|
||||
use Doctrine\Tests\Models\Cache\State;
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
/**
|
||||
* @group DDC-2183
|
||||
*/
|
||||
abstract class AbstractCollectionPersisterTest extends OrmTestCase
|
||||
{
|
||||
/** @var Region */
|
||||
/** @var Region&MockObject */
|
||||
protected $region;
|
||||
|
||||
/** @var CollectionPersister */
|
||||
@@ -70,6 +71,9 @@ abstract class AbstractCollectionPersisterTest extends OrmTestCase
|
||||
->getMock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Region&MockObject
|
||||
*/
|
||||
protected function createRegion(): Region
|
||||
{
|
||||
return $this->getMockBuilder(Region::class)
|
||||
|
||||
@@ -17,16 +17,17 @@ use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
use Doctrine\Tests\Models\Cache\Country;
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
/**
|
||||
* @group DDC-2183
|
||||
*/
|
||||
abstract class AbstractEntityPersisterTest extends OrmTestCase
|
||||
{
|
||||
/** @var Region */
|
||||
/** @var Region&MockObject */
|
||||
protected $region;
|
||||
|
||||
/** @var EntityPersister */
|
||||
/** @var EntityPersister&MockObject */
|
||||
protected $entityPersister;
|
||||
|
||||
/** @var EntityManager */
|
||||
@@ -45,6 +46,9 @@ abstract class AbstractEntityPersisterTest extends OrmTestCase
|
||||
$this->entityPersister = $this->createMock(EntityPersister::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Region&MockObject
|
||||
*/
|
||||
protected function createRegion(): Region
|
||||
{
|
||||
return $this->createMock(Region::class);
|
||||
|
||||
@@ -33,7 +33,7 @@ class EntityManagerDecoratorTest extends TestCase
|
||||
'lock',
|
||||
];
|
||||
|
||||
/** @var EntityManagerInterface|MockObject */
|
||||
/** @var EntityManagerInterface&MockObject */
|
||||
private $wrapped;
|
||||
|
||||
protected function setUp(): void
|
||||
|
||||
@@ -7,7 +7,6 @@ namespace Doctrine\Tests\ORM\Functional;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Configuration;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
@@ -28,6 +27,7 @@ use Doctrine\Tests\Models\Company\CompanyOrganization;
|
||||
use Doctrine\Tests\Models\Company\CompanyPerson;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
use InvalidArgumentException;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use ReflectionMethod;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
|
||||
@@ -221,7 +221,7 @@ class SQLFilterTest extends OrmFunctionalTestCase
|
||||
self::assertFalse($em->getFilters()->isEnabled('foo_filter'));
|
||||
}
|
||||
|
||||
protected function configureFilters($em): void
|
||||
private function configureFilters(EntityManagerInterface $em): void
|
||||
{
|
||||
// Add filters to the configuration of the EM
|
||||
$config = $em->getConfiguration();
|
||||
@@ -229,23 +229,30 @@ class SQLFilterTest extends OrmFunctionalTestCase
|
||||
$config->addFilter('soft_delete', '\Doctrine\Tests\ORM\Functional\MySoftDeleteFilter');
|
||||
}
|
||||
|
||||
protected function getMockConnection(): Connection
|
||||
/**
|
||||
* @return Connection&MockObject
|
||||
*/
|
||||
private function getMockConnection(): Connection
|
||||
{
|
||||
// Setup connection mock
|
||||
return $this->createMock(Connection::class);
|
||||
}
|
||||
|
||||
protected function getMockEntityManager(): EntityManagerInterface
|
||||
/**
|
||||
* @return EntityManagerInterface&MockObject
|
||||
*/
|
||||
private function getMockEntityManager(): EntityManagerInterface
|
||||
{
|
||||
// Setup entity manager mock
|
||||
return $this->createMock(EntityManager::class);
|
||||
return $this->createMock(EntityManagerInterface::class);
|
||||
}
|
||||
|
||||
protected function addMockFilterCollection(EntityManagerInterface $em): FilterCollection
|
||||
/**
|
||||
* @psalm-param EntityManagerInterface&MockObject $em
|
||||
*
|
||||
* @return FilterCollection&MockObject
|
||||
*/
|
||||
private function addMockFilterCollection(EntityManagerInterface $em): FilterCollection
|
||||
{
|
||||
$filterCollection = $this->getMockBuilder(FilterCollection::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$filterCollection = $this->createMock(FilterCollection::class);
|
||||
|
||||
$em->expects(self::any())
|
||||
->method('getFilters')
|
||||
|
||||
@@ -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,97 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\DiscriminatorMap;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\GeneratedValue;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
use Doctrine\ORM\Mapping\InheritanceType;
|
||||
use Doctrine\ORM\Mapping\ManyToOne;
|
||||
use Doctrine\ORM\Mapping\OneToMany;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
class GH7512Test extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->setUpEntitySchema([
|
||||
GH7512EntityA::class,
|
||||
GH7512EntityB::class,
|
||||
GH7512EntityC::class,
|
||||
]);
|
||||
|
||||
$this->_em->persist(new GH7512EntityA());
|
||||
$this->_em->persist(new GH7512EntityC());
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
}
|
||||
|
||||
public function testFindEntityByAssociationPropertyJoinedChildWithClearMetadata(): void
|
||||
{
|
||||
// unset metadata for entity B as though it hasn't been touched yet in application lifecycle.
|
||||
$this->_em->getMetadataFactory()->setMetadataFor(GH7512EntityB::class, null);
|
||||
$result = $this->_em->getRepository(GH7512EntityC::class)->findBy([
|
||||
'entityA' => new GH7512EntityB(),
|
||||
]);
|
||||
$this->assertEmpty($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity()
|
||||
* @InheritanceType("JOINED")
|
||||
* @DiscriminatorMap({
|
||||
* "entitya"=Doctrine\Tests\ORM\Functional\Ticket\GH7512EntityA::class,
|
||||
* "entityB"=Doctrine\Tests\ORM\Functional\Ticket\GH7512EntityB::class
|
||||
* })
|
||||
*/
|
||||
class GH7512EntityA
|
||||
{
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
* @Id()
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\GH7512EntityC", mappedBy="entityA")
|
||||
* @var Collection<int, GH7512EntityC>
|
||||
*/
|
||||
public $entityCs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity()
|
||||
*/
|
||||
class GH7512EntityB extends GH7512EntityA
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity()
|
||||
*/
|
||||
class GH7512EntityC
|
||||
{
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
* @Id()
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\GH7512EntityA", inversedBy="entityCs")
|
||||
* @var GH7512EntityA
|
||||
*/
|
||||
public $entityA;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\GeneratedValue;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
use Doctrine\ORM\Mapping\JoinColumn;
|
||||
use Doctrine\ORM\Mapping\ManyToOne;
|
||||
use Doctrine\ORM\Mapping\OneToMany;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
use function count;
|
||||
|
||||
final class GH8217Test extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->setUpEntitySchema(
|
||||
[
|
||||
GH8217Collection::class,
|
||||
GH8217CollectionItem::class,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group GH-8217
|
||||
*/
|
||||
public function testNoQueriesAfterSecondFlush(): void
|
||||
{
|
||||
$collection = new GH8217Collection();
|
||||
$collection->addItem(new GH8217CollectionItem($collection, 0));
|
||||
$collection->addItem(new GH8217CollectionItem($collection, 1));
|
||||
$this->_em->persist($collection);
|
||||
$this->_em->flush();
|
||||
|
||||
$logger = $this->_sqlLoggerStack;
|
||||
$queriesNumberBeforeSecondFlush = count($logger->queries);
|
||||
$this->_em->flush();
|
||||
$queriesNumberAfterSecondFlush = count($logger->queries);
|
||||
self::assertEquals($queriesNumberBeforeSecondFlush, $queriesNumberAfterSecondFlush);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class GH8217Collection
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @psalm-var Collection<int, GH8217CollectionItem>
|
||||
* @OneToMany(targetEntity="GH8217CollectionItem", mappedBy="collection",
|
||||
* cascade={"persist", "remove"}, orphanRemoval=true)
|
||||
*/
|
||||
public $items;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->items = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function addItem(GH8217CollectionItem $item): void
|
||||
{
|
||||
$this->items->add($item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class GH8217CollectionItem
|
||||
{
|
||||
/**
|
||||
* @var GH8217Collection
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="GH8217Collection", inversedBy="items")
|
||||
* @JoinColumn(name="id", referencedColumnName="id")
|
||||
*/
|
||||
public $collection;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @Id
|
||||
* @Column(type="integer", options={"unsigned": true})
|
||||
*/
|
||||
public $collectionIndex;
|
||||
|
||||
public function __construct(GH8217Collection $collection, int $collectionIndex)
|
||||
{
|
||||
$this->collection = $collection;
|
||||
$this->collectionIndex = $collectionIndex;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -23,16 +23,16 @@ use function iterator_to_array;
|
||||
*/
|
||||
class AbstractHydratorTest extends OrmFunctionalTestCase
|
||||
{
|
||||
/** @var EventManager|MockObject */
|
||||
/** @var EventManager&MockObject */
|
||||
private $mockEventManager;
|
||||
|
||||
/** @var Result|MockObject */
|
||||
/** @var Result&MockObject */
|
||||
private $mockResult;
|
||||
|
||||
/** @var ResultSetMapping|MockObject */
|
||||
/** @var ResultSetMapping&MockObject */
|
||||
private $mockResultMapping;
|
||||
|
||||
/** @var AbstractHydrator */
|
||||
/** @var AbstractHydrator&MockObject */
|
||||
private $hydrator;
|
||||
|
||||
protected function setUp(): void
|
||||
@@ -43,7 +43,7 @@ class AbstractHydratorTest extends OrmFunctionalTestCase
|
||||
$mockEntityManagerInterface = $this->createMock(EntityManagerInterface::class);
|
||||
$this->mockEventManager = $this->createMock(EventManager::class);
|
||||
$this->mockResult = $this->createMock(Result::class);
|
||||
$this->mockResultMapping = $this->getMockBuilder(ResultSetMapping::class);
|
||||
$this->mockResultMapping = $this->createMock(ResultSetMapping::class);
|
||||
|
||||
$mockEntityManagerInterface
|
||||
->expects(self::any())
|
||||
|
||||
@@ -9,56 +9,56 @@ use Doctrine\ORM\NonUniqueResultException;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\Tests\Mocks\ArrayResultFactory;
|
||||
use Doctrine\Tests\Models\CMS\CmsUser;
|
||||
use Generator;
|
||||
|
||||
use function in_array;
|
||||
|
||||
class SingleScalarHydratorTest extends HydrationTestCase
|
||||
{
|
||||
/** Result set provider for the HYDRATE_SINGLE_SCALAR tests */
|
||||
public static function singleScalarResultSetProvider(): array
|
||||
/**
|
||||
* @return Generator<int, array{list<array<string,mixed>>,mixed}>
|
||||
*/
|
||||
public static function validResultSetProvider(): Generator
|
||||
{
|
||||
return [
|
||||
// valid
|
||||
'valid' => [
|
||||
'name' => 'result1',
|
||||
'resultSet' => [
|
||||
['u__name' => 'romanb'],
|
||||
],
|
||||
],
|
||||
// valid
|
||||
// SELECT u.name FROM CmsUser u WHERE u.id = 1
|
||||
yield [
|
||||
[
|
||||
'name' => 'result2',
|
||||
'resultSet' => [
|
||||
['u__id' => '1'],
|
||||
],
|
||||
['u__name' => 'romanb'],
|
||||
],
|
||||
// invalid
|
||||
'romanb',
|
||||
];
|
||||
|
||||
// SELECT u.id FROM CmsUser u WHERE u.id = 1
|
||||
yield [
|
||||
[
|
||||
'name' => 'result3',
|
||||
'resultSet' => [
|
||||
[
|
||||
'u__id' => '1',
|
||||
'u__name' => 'romanb',
|
||||
],
|
||||
],
|
||||
['u__id' => '1'],
|
||||
],
|
||||
// invalid
|
||||
1,
|
||||
];
|
||||
|
||||
// SELECT
|
||||
// u.id,
|
||||
// COUNT(u.postsCount + u.likesCount) AS HIDDEN score
|
||||
// FROM CmsUser u
|
||||
// WHERE u.id = 1
|
||||
yield [
|
||||
[
|
||||
'name' => 'result4',
|
||||
'resultSet' => [
|
||||
['u__id' => '1'],
|
||||
['u__id' => '2'],
|
||||
[
|
||||
'u__id' => '1',
|
||||
'score' => 10, // Ignored since not part of ResultSetMapping (cf. HIDDEN keyword)
|
||||
],
|
||||
],
|
||||
1,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* select u.name from CmsUser u where u.id = 1
|
||||
* @param list<array<string, mixed>> $resultSet
|
||||
* @param mixed $expectedResult
|
||||
*
|
||||
* @dataProvider singleScalarResultSetProvider
|
||||
* @dataProvider validResultSetProvider
|
||||
*/
|
||||
public function testHydrateSingleScalar($name, $resultSet): void
|
||||
public function testHydrateSingleScalarFromFieldMappingWithValidResultSet(array $resultSet, $expectedResult): void
|
||||
{
|
||||
$rsm = new ResultSetMapping();
|
||||
$rsm->addEntityResult(CmsUser::class, 'u');
|
||||
@@ -68,23 +68,116 @@ class SingleScalarHydratorTest extends HydrationTestCase
|
||||
$stmt = ArrayResultFactory::createFromArray($resultSet);
|
||||
$hydrator = new SingleScalarHydrator($this->entityManager);
|
||||
|
||||
if ($name === 'result1') {
|
||||
$result = $hydrator->hydrateAll($stmt, $rsm);
|
||||
self::assertEquals('romanb', $result);
|
||||
$result = $hydrator->hydrateAll($stmt, $rsm);
|
||||
$this->assertEquals($expectedResult, $result);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* @param list<array<string, mixed>> $resultSet
|
||||
* @param mixed $expectedResult
|
||||
*
|
||||
* @dataProvider validResultSetProvider
|
||||
*/
|
||||
public function testHydrateSingleScalarFromScalarMappingWithValidResultSet(array $resultSet, $expectedResult): void
|
||||
{
|
||||
$rsm = new ResultSetMapping();
|
||||
$rsm->addScalarResult('u__id', 'id', 'string');
|
||||
$rsm->addScalarResult('u__name', 'name', 'string');
|
||||
|
||||
if ($name === 'result2') {
|
||||
$result = $hydrator->hydrateAll($stmt, $rsm);
|
||||
self::assertEquals(1, $result);
|
||||
$stmt = ArrayResultFactory::createFromArray($resultSet);
|
||||
$hydrator = new SingleScalarHydrator($this->entityManager);
|
||||
|
||||
return;
|
||||
}
|
||||
$result = $hydrator->hydrateAll($stmt, $rsm);
|
||||
$this->assertEquals($expectedResult, $result);
|
||||
}
|
||||
|
||||
if (in_array($name, ['result3', 'result4'], true)) {
|
||||
$this->expectException(NonUniqueResultException::class);
|
||||
$hydrator->hydrateAll($stmt, $rsm);
|
||||
}
|
||||
/**
|
||||
* @return Generator<int, array{list<array<string,mixed>>}>
|
||||
*/
|
||||
public static function invalidResultSetProvider(): Generator
|
||||
{
|
||||
// Single row (OK), multiple columns (NOT OK)
|
||||
yield [
|
||||
[
|
||||
[
|
||||
'u__id' => '1',
|
||||
'u__name' => 'romanb',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Multiple rows (NOT OK), single column (OK)
|
||||
yield [
|
||||
[
|
||||
['u__id' => '1'],
|
||||
['u__id' => '2'],
|
||||
],
|
||||
];
|
||||
|
||||
// Multiple rows (NOT OK), single column with HIDDEN result (OK)
|
||||
yield [
|
||||
[
|
||||
[
|
||||
'u__id' => '1',
|
||||
'score' => 10, // Ignored since not part of ResultSetMapping
|
||||
],
|
||||
[
|
||||
'u__id' => '2',
|
||||
'score' => 10, // Ignored since not part of ResultSetMapping
|
||||
],
|
||||
],
|
||||
1,
|
||||
];
|
||||
|
||||
// Multiple row (NOT OK), multiple columns (NOT OK)
|
||||
yield [
|
||||
[
|
||||
[
|
||||
'u__id' => '1',
|
||||
'u__name' => 'romanb',
|
||||
],
|
||||
[
|
||||
'u__id' => '2',
|
||||
'u__name' => 'romanb',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $resultSet
|
||||
*
|
||||
* @dataProvider invalidResultSetProvider
|
||||
*/
|
||||
public function testHydrateSingleScalarFromFieldMappingWithInvalidResultSet(array $resultSet): void
|
||||
{
|
||||
$rsm = new ResultSetMapping();
|
||||
$rsm->addEntityResult(CmsUser::class, 'u');
|
||||
$rsm->addFieldResult('u', 'u__id', 'id');
|
||||
$rsm->addFieldResult('u', 'u__name', 'name');
|
||||
|
||||
$stmt = ArrayResultFactory::createFromArray($resultSet);
|
||||
$hydrator = new SingleScalarHydrator($this->entityManager);
|
||||
|
||||
$this->expectException(NonUniqueResultException::class);
|
||||
$hydrator->hydrateAll($stmt, $rsm);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $resultSet
|
||||
*
|
||||
* @dataProvider invalidResultSetProvider
|
||||
*/
|
||||
public function testHydrateSingleScalarFromScalarMappingWithInvalidResultSet(array $resultSet): void
|
||||
{
|
||||
$rsm = new ResultSetMapping();
|
||||
$rsm->addScalarResult('u__id', 'id', 'string');
|
||||
$rsm->addScalarResult('u__name', 'name', 'string');
|
||||
|
||||
$stmt = ArrayResultFactory::createFromArray($resultSet);
|
||||
$hydrator = new SingleScalarHydrator($this->entityManager);
|
||||
|
||||
$this->expectException(NonUniqueResultException::class);
|
||||
$hydrator->hydrateAll($stmt, $rsm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@ use function in_array;
|
||||
*/
|
||||
class HydrationCompleteHandlerTest extends TestCase
|
||||
{
|
||||
/** @var ListenersInvoker|MockObject */
|
||||
/** @var ListenersInvoker&MockObject */
|
||||
private $listenersInvoker;
|
||||
|
||||
/** @var EntityManagerInterface|MockObject */
|
||||
/** @var EntityManagerInterface&MockObject */
|
||||
private $entityManager;
|
||||
|
||||
/** @var HydrationCompleteHandler */
|
||||
|
||||
@@ -17,7 +17,7 @@ use stdClass;
|
||||
*/
|
||||
class LazyCriteriaCollectionTest extends TestCase
|
||||
{
|
||||
/** @var EntityPersister|MockObject */
|
||||
/** @var EntityPersister&MockObject */
|
||||
private $persister;
|
||||
|
||||
/** @var Criteria */
|
||||
|
||||
@@ -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`',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -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`',
|
||||
]
|
||||
);
|
||||
+18
@@ -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>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
Doctrine\Tests\ORM\Mapping\ReservedWordInTableColumn:
|
||||
type: entity
|
||||
id:
|
||||
id:
|
||||
generator:
|
||||
strategy: NONE
|
||||
fields:
|
||||
count:
|
||||
type: integer
|
||||
column: '`count`'
|
||||
@@ -30,7 +30,7 @@ class ORMInvalidArgumentExceptionTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
* @psalm-return list<array{mixed, string}>
|
||||
*/
|
||||
public function invalidEntityNames(): array
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ class QueryExpressionVisitorTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder\Comparison|string $queryExpr
|
||||
* @param QueryBuilder\Comparison|QueryBuilder\Func|string $queryExpr
|
||||
*
|
||||
* @dataProvider comparisonData
|
||||
*/
|
||||
@@ -39,7 +39,13 @@ class QueryExpressionVisitorTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
/** @psalm-return list<array{CriteriaComparison, QueryBuilder\Comparison|string, ?Parameter} */
|
||||
/**
|
||||
* @psalm-return list<array{
|
||||
* 0: CriteriaComparison,
|
||||
* 1: QueryBuilder\Comparison|QueryBuilder\Func|string,
|
||||
* 2?: Parameter,
|
||||
* }>
|
||||
*/
|
||||
public function comparisonData(): array
|
||||
{
|
||||
$cb = new CriteriaBuilder();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Repository;
|
||||
|
||||
use Closure;
|
||||
use Doctrine\ORM\Configuration;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
@@ -22,10 +23,10 @@ use function assert;
|
||||
*/
|
||||
class DefaultRepositoryFactoryTest extends TestCase
|
||||
{
|
||||
/** @var EntityManagerInterface|MockObject */
|
||||
/** @var EntityManagerInterface&MockObject */
|
||||
private $entityManager;
|
||||
|
||||
/** @var Configuration|MockObject */
|
||||
/** @var Configuration&MockObject */
|
||||
private $configuration;
|
||||
|
||||
/** @var DefaultRepositoryFactory */
|
||||
@@ -48,7 +49,7 @@ class DefaultRepositoryFactoryTest extends TestCase
|
||||
$this->entityManager
|
||||
->expects(self::any())
|
||||
->method('getClassMetadata')
|
||||
->will(self::returnCallback([$this, 'buildClassMetadata']));
|
||||
->will(self::returnCallback(Closure::fromCallable([$this, 'buildClassMetadata'])));
|
||||
|
||||
self::assertInstanceOf(
|
||||
DDC869PaymentRepository::class,
|
||||
@@ -61,7 +62,7 @@ class DefaultRepositoryFactoryTest extends TestCase
|
||||
$this->entityManager
|
||||
->expects(self::any())
|
||||
->method('getClassMetadata')
|
||||
->will(self::returnCallback([$this, 'buildClassMetadata']));
|
||||
->will(self::returnCallback(Closure::fromCallable([$this, 'buildClassMetadata'])));
|
||||
|
||||
self::assertSame(
|
||||
$this->repositoryFactory->getRepository($this->entityManager, self::class),
|
||||
@@ -92,11 +93,11 @@ class DefaultRepositoryFactoryTest extends TestCase
|
||||
|
||||
$em1->expects(self::any())
|
||||
->method('getClassMetadata')
|
||||
->will(self::returnCallback([$this, 'buildClassMetadata']));
|
||||
->will(self::returnCallback(Closure::fromCallable([$this, 'buildClassMetadata'])));
|
||||
|
||||
$em2->expects(self::any())
|
||||
->method('getClassMetadata')
|
||||
->will(self::returnCallback([$this, 'buildClassMetadata']));
|
||||
->will(self::returnCallback(Closure::fromCallable([$this, 'buildClassMetadata'])));
|
||||
|
||||
$repo1 = $this->repositoryFactory->getRepository($em1, self::class);
|
||||
$repo2 = $this->repositoryFactory->getRepository($em2, self::class);
|
||||
@@ -108,15 +109,11 @@ class DefaultRepositoryFactoryTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MockObject|ClassMetadata
|
||||
*
|
||||
* @private
|
||||
* @return ClassMetadata&MockObject
|
||||
*/
|
||||
public function buildClassMetadata(string $className)
|
||||
private function buildClassMetadata(string $className): ClassMetadata
|
||||
{
|
||||
$metadata = $this->createMock(ClassMetadata::class);
|
||||
assert($metadata instanceof ClassMetadata || $metadata instanceof MockObject);
|
||||
|
||||
$metadata->expects(self::any())->method('getName')->will(self::returnValue($className));
|
||||
|
||||
$metadata->customRepositoryClassName = null;
|
||||
@@ -125,7 +122,7 @@ class DefaultRepositoryFactoryTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManagerInterface|MockObject
|
||||
* @return EntityManagerInterface&MockObject
|
||||
*/
|
||||
private function createEntityManager(): EntityManagerInterface
|
||||
{
|
||||
|
||||
@@ -99,6 +99,48 @@ XML;
|
||||
</field>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
XML;
|
||||
|
||||
self::assertXmlStringEqualsXmlString($expectedFileContent, $exporter->exportClassMetadata($metadata));
|
||||
}
|
||||
|
||||
public function testPolicyExport(): void
|
||||
{
|
||||
$exporter = new XmlExporter();
|
||||
$metadata = new ClassMetadata('entityTest');
|
||||
|
||||
// DEFERRED_IMPLICIT
|
||||
$metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_IMPLICIT);
|
||||
|
||||
$expectedFileContent = <<<'XML'
|
||||
<?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="entityTest"/>
|
||||
</doctrine-mapping>
|
||||
XML;
|
||||
|
||||
self::assertXmlStringEqualsXmlString($expectedFileContent, $exporter->exportClassMetadata($metadata));
|
||||
|
||||
// DEFERRED_EXPLICIT
|
||||
$metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT);
|
||||
|
||||
$expectedFileContent = <<<'XML'
|
||||
<?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="entityTest" change-tracking-policy="DEFERRED_EXPLICIT"/>
|
||||
</doctrine-mapping>
|
||||
XML;
|
||||
|
||||
self::assertXmlStringEqualsXmlString($expectedFileContent, $exporter->exportClassMetadata($metadata));
|
||||
|
||||
// NOTIFY
|
||||
$metadata->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_NOTIFY);
|
||||
|
||||
$expectedFileContent = <<<'XML'
|
||||
<?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="entityTest" change-tracking-policy="NOTIFY"/>
|
||||
</doctrine-mapping>
|
||||
XML;
|
||||
|
||||
self::assertXmlStringEqualsXmlString($expectedFileContent, $exporter->exportClassMetadata($metadata));
|
||||
|
||||
@@ -13,14 +13,15 @@ use Doctrine\ORM\Tools\Pagination\Paginator;
|
||||
use Doctrine\Tests\Mocks\ConnectionMock;
|
||||
use Doctrine\Tests\Mocks\DriverMock;
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class PaginatorTest extends OrmTestCase
|
||||
{
|
||||
/** @var Connection */
|
||||
/** @var Connection&MockObject */
|
||||
private $connection;
|
||||
/** @var EntityManagerInterface */
|
||||
/** @var EntityManagerInterface&MockObject */
|
||||
private $em;
|
||||
/** @var AbstractHydrator */
|
||||
/** @var AbstractHydrator&MockObject */
|
||||
private $hydrator;
|
||||
|
||||
protected function setUp(): void
|
||||
|
||||
@@ -225,17 +225,6 @@ class SchemaValidatorTest extends OrmTestCase
|
||||
|
||||
$this->assertEquals([], $ce);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group 9095
|
||||
*/
|
||||
public function testAbstractChildClassNotPresentInDiscriminator(): void
|
||||
{
|
||||
$class1 = $this->em->getClassMetadata(Issue9095AbstractChild::class);
|
||||
$ce = $this->validator->validateClass($class1);
|
||||
|
||||
$this->assertEquals([], $ce);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,35 +256,6 @@ class ChildEntity extends MappedSuperclassEntity
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("SINGLE_TABLE")
|
||||
* @DiscriminatorMap({"child" = Issue9095Child::class})
|
||||
*/
|
||||
abstract class Issue9095Parent
|
||||
{
|
||||
/**
|
||||
* @var mixed
|
||||
* @Id
|
||||
* @Column
|
||||
*/
|
||||
protected $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
abstract class Issue9095AbstractChild extends Issue9095Parent
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Issue9095Child extends Issue9095AbstractChild
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
|
||||
@@ -61,7 +61,7 @@ class UnitOfWorkTest extends OrmTestCase
|
||||
/**
|
||||
* Provides a sequence mock to the UnitOfWork
|
||||
*
|
||||
* @var ConnectionMock
|
||||
* @var ConnectionMock&MockObject
|
||||
*/
|
||||
private $_connectionMock;
|
||||
|
||||
@@ -72,7 +72,7 @@ class UnitOfWorkTest extends OrmTestCase
|
||||
*/
|
||||
private $_emMock;
|
||||
|
||||
/** @var EventManager|MockObject */
|
||||
/** @var EventManager&MockObject */
|
||||
private $eventManager;
|
||||
|
||||
protected function setUp(): void
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user