From ae842259f54eabfdc99330d9fb6c7224dfe2659a Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sun, 17 Mar 2024 12:24:10 +0100 Subject: [PATCH 1/7] [Documentation] Query Result Formats Page: https://www.doctrine-project.org/projects/doctrine-orm/en/2.19/reference/dql-doctrine-query-language.html#query-result-formats Follow-up of https://github.com/doctrine/orm/pull/11359 The table I suggested is probably not working, since the text for each method is too long. And what I really wanted is to make it more *scanable*. So I tried boldfacing - if this doesn't work, I'll try something else. Questions: 1. This section here is basically the same as https://www.doctrine-project.org/projects/doctrine-orm/en/2.10/reference/dql-doctrine-query-language.html#hydration-modes ! So I'll try to merge them (in another PR), OK? I think the list is a better format (more scanable) - since those methods all work the same, there's no need for a full-blown code sample for each, IMO. 2. `getSingleColumnResult()` is missing. --- .../reference/dql-doctrine-query-language.rst | 46 ++++++------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/docs/en/reference/dql-doctrine-query-language.rst b/docs/en/reference/dql-doctrine-query-language.rst index 571190072e3..0e21c0aa0bc 100644 --- a/docs/en/reference/dql-doctrine-query-language.rst +++ b/docs/en/reference/dql-doctrine-query-language.rst @@ -1003,7 +1003,7 @@ The Query class --------------- An instance of the ``Doctrine\ORM\Query`` class represents a DQL -query. You create a Query instance be calling +query. You create a Query instance by calling ``EntityManager#createQuery($dql)``, passing the DQL query string. Alternatively you can create an empty ``Query`` instance and invoke ``Query#setDQL($dql)`` afterwards. Here are some examples: @@ -1023,26 +1023,21 @@ Alternatively you can create an empty ``Query`` instance and invoke Query Result Formats ~~~~~~~~~~~~~~~~~~~~ -The format in which the result of a DQL SELECT query is returned -can be influenced by a so-called ``hydration mode``. A hydration -mode specifies a particular way in which a SQL result set is -transformed. Each hydration mode has its own dedicated method on -the Query class. Here they are: +The way in which the SQL result set of a DQL SELECT query is transformed +to PHP is determined by the so-called "hydration mode": - -- ``Query#getResult()``: Retrieves a collection of objects. The +- **``getResult()``** (``HYDRATE_OBJECT``): Retrieves a collection of objects. The result is either a plain collection of objects (pure) or an array where the objects are nested in the result rows (mixed). -- ``Query#getSingleResult()``: Retrieves a single object. If the - result contains more than one object, an ``NonUniqueResultException`` - is thrown. If the result contains no objects, an ``NoResultException`` +- **``getSingleResult()``**: Retrieves a single object. If the + result contains more than one object, a ``NonUniqueResultException`` + is thrown. If the result contains no objects, a ``NoResultException`` is thrown. The pure/mixed distinction does not apply. -- ``Query#getOneOrNullResult()``: Retrieve a single object. If the +- **``getOneOrNullResult()``**: Retrieve a single object. If the result contains more than one object, a ``NonUniqueResultException`` is thrown. If no object is found null will be returned. -- ``Query#getArrayResult()``: Retrieves an array graph (a nested - array) that is largely interchangeable with the object graph - generated by ``Query#getResult()`` for read-only purposes. +- **``getArrayResult()``** (``HYDRATE_ARRAY``): Retrieves an array graph (a nested + array) for read-only purposes. .. note:: @@ -1050,28 +1045,17 @@ the Query class. Here they are: graph in certain scenarios due to the difference of the identity semantics between arrays and objects. - - -- ``Query#getScalarResult()``: Retrieves a flat/rectangular result +- **``getScalarResult()``** (``HYDRATE_SCALAR``): Retrieves a flat/rectangular result set of scalar values that can contain duplicate data. The pure/mixed distinction does not apply. -- ``Query#getSingleScalarResult()``: Retrieves a single scalar +- **``getSingleScalarResult()``** (``HYDRATE_SINGLE_SCALAR``: Retrieves a single scalar value from the result returned by the dbms. If the result contains more than a single scalar value, an exception is thrown. The pure/mixed distinction does not apply. -Instead of using these methods, you can alternatively use the -general-purpose method -``Query#execute(array $params = [], $hydrationMode = Query::HYDRATE_OBJECT)``. -Using this method you can directly supply the hydration mode as the -second parameter via one of the Query constants. In fact, the -methods mentioned earlier are just convenient shortcuts for the -execute method. For example, the method ``Query#getResult()`` -internally invokes execute, passing in ``Query::HYDRATE_OBJECT`` as -the hydration mode. - -The use of the methods mentioned earlier is generally preferred as -it leads to more concise code. +In parentheses are the constants of the ``Query`` class which you can use with the +general-purpose method ``Query::execute(array $params = [], $hydrationMode = Query::HYDRATE_OBJECT)``. +In fact, the recommended methods in the list are just convenient shortcuts for the hydration mode. Pure and Mixed Results ~~~~~~~~~~~~~~~~~~~~~~ From 4b4b9b7b6fdd01959e187b4ccfca4c7708a63f3a Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sun, 17 Mar 2024 12:25:05 +0100 Subject: [PATCH 2/7] Adding `NonUniqueResultException` --- docs/en/reference/dql-doctrine-query-language.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/dql-doctrine-query-language.rst b/docs/en/reference/dql-doctrine-query-language.rst index 0e21c0aa0bc..65ed4d67b02 100644 --- a/docs/en/reference/dql-doctrine-query-language.rst +++ b/docs/en/reference/dql-doctrine-query-language.rst @@ -1050,7 +1050,7 @@ to PHP is determined by the so-called "hydration mode": pure/mixed distinction does not apply. - **``getSingleScalarResult()``** (``HYDRATE_SINGLE_SCALAR``: Retrieves a single scalar value from the result returned by the dbms. If the result contains - more than a single scalar value, an exception is thrown. The + more than a single scalar value, a ``NonUniqueResultException`` is thrown. The pure/mixed distinction does not apply. In parentheses are the constants of the ``Query`` class which you can use with the From 46d0865339d00bfa02a35f1ce04e2de7f38600f5 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Thu, 21 Mar 2024 17:55:39 +0100 Subject: [PATCH 3/7] Update dql-doctrine-query-language.rst --- docs/en/reference/dql-doctrine-query-language.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/dql-doctrine-query-language.rst b/docs/en/reference/dql-doctrine-query-language.rst index 65ed4d67b02..1cc4c22cec3 100644 --- a/docs/en/reference/dql-doctrine-query-language.rst +++ b/docs/en/reference/dql-doctrine-query-language.rst @@ -1055,7 +1055,7 @@ to PHP is determined by the so-called "hydration mode": In parentheses are the constants of the ``Query`` class which you can use with the general-purpose method ``Query::execute(array $params = [], $hydrationMode = Query::HYDRATE_OBJECT)``. -In fact, the recommended methods in the list are just convenient shortcuts for the hydration mode. +In fact, the methods in the list are just convenient shortcuts for the hydration mode. Pure and Mixed Results ~~~~~~~~~~~~~~~~~~~~~~ From c54c557e02bda20bc84883ab1d0f94319f124dfe Mon Sep 17 00:00:00 2001 From: Ludwig Rafelsberger Date: Fri, 22 Mar 2024 11:05:00 +0100 Subject: [PATCH 4/7] Fix psalm errors: remove override of template type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/doctrine/collections/issues/368 for the same issue in doctrine/collections which has been fixed there. The issue happens when using ->contains(). Running psalm emits > InvalidArgument - Argument 1 of Doctrine\ORM\PersistentCollection::contains > expects > TMaybeContained:fn-doctrine\common\collections\readablecollection::contains > as mixed, but … provided. Solution: we should either not define @template TMaybeContained or re-define the complete psalm docblock from ReadableCollection. Repairing the docblock necessitates an update to the psalm baseline: one "known issue" is no longer an issue and thus removed. --- psalm-baseline.xml | 3 --- src/LazyCriteriaCollection.php | 6 +++--- src/PersistentCollection.php | 5 ----- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 7a3b729e849..d04835c1e85 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -462,9 +462,6 @@ - - - diff --git a/src/LazyCriteriaCollection.php b/src/LazyCriteriaCollection.php index 5580f971eb0..48040922616 100644 --- a/src/LazyCriteriaCollection.php +++ b/src/LazyCriteriaCollection.php @@ -76,11 +76,11 @@ public function isEmpty() } /** - * {@inheritDoc} - * * Do an optimized search of an element * - * @template TMaybeContained + * @param mixed $element The element to search for. + * + * @return bool TRUE if the collection contains $element, FALSE otherwise. */ public function contains($element) { diff --git a/src/PersistentCollection.php b/src/PersistentCollection.php index 4470a64a5cd..c94bb77496b 100644 --- a/src/PersistentCollection.php +++ b/src/PersistentCollection.php @@ -412,11 +412,6 @@ public function containsKey($key): bool return parent::containsKey($key); } - /** - * {@inheritDoc} - * - * @template TMaybeContained - */ public function contains($element): bool { if (! $this->initialized && $this->getMapping()['fetch'] === ClassMetadata::FETCH_EXTRA_LAZY) { From 5f3c1dbab8513d3529017b4df6558dcfd185b934 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Tue, 26 Mar 2024 21:40:18 +0100 Subject: [PATCH 5/7] [Documentation] Merging "Query Result Formats" with "Hydration Modes" Page: https://www.doctrine-project.org/projects/doctrine-orm/en/2.19/reference/dql-doctrine-query-language.html#query-result-formats As announced in https://github.com/doctrine/orm/pull/11372#issue-2190613801, I merged the (mostly) identical sections. * I changed the `const`s from `Query` to `AbstractQuery` * I deleted this - mainly cause I didn't find a nice place for it: > In parentheses are the constants of the ``Query`` class which you can use with the general-purpose method ``Query::execute(array $params = [], $hydrationMode = Query::HYDRATE_OBJECT)``. --- .../reference/dql-doctrine-query-language.rst | 331 ++++++++---------- 1 file changed, 138 insertions(+), 193 deletions(-) diff --git a/docs/en/reference/dql-doctrine-query-language.rst b/docs/en/reference/dql-doctrine-query-language.rst index 1cc4c22cec3..ce06d02b81a 100644 --- a/docs/en/reference/dql-doctrine-query-language.rst +++ b/docs/en/reference/dql-doctrine-query-language.rst @@ -1020,42 +1020,146 @@ Alternatively you can create an empty ``Query`` instance and invoke $q = $em->createQuery(); $q->setDQL('select u from MyProject\Model\User u'); -Query Result Formats -~~~~~~~~~~~~~~~~~~~~ +Query Result Formats (Hydration Modes) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The way in which the SQL result set of a DQL SELECT query is transformed -to PHP is determined by the so-called "hydration mode": - -- **``getResult()``** (``HYDRATE_OBJECT``): Retrieves a collection of objects. The - result is either a plain collection of objects (pure) or an array - where the objects are nested in the result rows (mixed). -- **``getSingleResult()``**: Retrieves a single object. If the - result contains more than one object, a ``NonUniqueResultException`` - is thrown. If the result contains no objects, a ``NoResultException`` - is thrown. The pure/mixed distinction does not apply. -- **``getOneOrNullResult()``**: Retrieve a single object. If the - result contains more than one object, a ``NonUniqueResultException`` - is thrown. If no object is found null will be returned. -- **``getArrayResult()``** (``HYDRATE_ARRAY``): Retrieves an array graph (a nested - array) for read-only purposes. - - .. note:: - - An array graph can differ from the corresponding object - graph in certain scenarios due to the difference of the identity - semantics between arrays and objects. - -- **``getScalarResult()``** (``HYDRATE_SCALAR``): Retrieves a flat/rectangular result - set of scalar values that can contain duplicate data. The - pure/mixed distinction does not apply. -- **``getSingleScalarResult()``** (``HYDRATE_SINGLE_SCALAR``: Retrieves a single scalar - value from the result returned by the dbms. If the result contains - more than a single scalar value, a ``NonUniqueResultException`` is thrown. The - pure/mixed distinction does not apply. - -In parentheses are the constants of the ``Query`` class which you can use with the -general-purpose method ``Query::execute(array $params = [], $hydrationMode = Query::HYDRATE_OBJECT)``. -In fact, the methods in the list are just convenient shortcuts for the hydration mode. +to PHP is determined by the so-called "hydration mode". + +``getResult()`` +^^^^^^^^^^^^^^^ + +Retrieves a collection of objects. The result is either a plain collection of objects (pure) or an array +where the objects are nested in the result rows (mixed): + +.. code-block:: php + + createQuery('SELECT u FROM User u'); + $users = $query->getResult(); + // same as: + $users = $query->getResult(AbstractQuery::HYDRATE_OBJECT); + +- Objects fetched in a FROM clause are returned as a Set, that means every + object is only ever included in the resulting array once. This is the case + even when using JOIN or GROUP BY in ways that return the same row for an + object multiple times. If the hydrator sees the same object multiple times, + then it makes sure it is only returned once. + +- If an object is already in memory from a previous query of any kind, then + then the previous object is used, even if the database may contain more + recent data. This even happens if the previous object is still an unloaded proxy. + +``getArrayResult()`` +^^^^^^^^^^^^^^^^^^^^ + +Retrieves an array graph (a nested array) for read-only purposes. + +.. note:: + + An array graph can differ from the corresponding object + graph in certain scenarios due to the difference of the identity + semantics between arrays and objects. + +.. code-block:: php + + getArrayResult(); + // same as: + $users = $query->getResult(AbstractQuery::HYDRATE_ARRAY); + +``getScalarResult()`` +^^^^^^^^^^^^^^^^^^^^^ + +Retrieves a flat/rectangular result set of scalar values that can contain duplicate data. The +pure/mixed distinction does not apply. + +.. code-block:: php + + getScalarResult(); + // same as: + $users = $query->getResult(AbstractQuery::HYDRATE_SCALAR); + +Fields from classes are prefixed by the DQL alias in the result. +A query of the kind `SELECT u.name ...` returns a key `u_name` in the result rows. + +``getSingleScalarResult()`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Retrieves a single scalar value from the result returned by the database. If the result contains +more than a single scalar value, a ``NonUniqueResultException`` is thrown. The pure/mixed distinction does not apply. + +.. code-block:: php + + createQuery('SELECT COUNT(u.id) FROM User u'); + $numUsers = $query->getSingleScalarResult(); + // same as: + $numUsers = $query->getResult(AbstractQuery::HYDRATE_SINGLE_SCALAR); + +``getSingleColumnResult()`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Retrieves an array from a one-dimensional array of scalar values: + +.. code-block:: php + + createQuery('SELECT a.id FROM User u'); + $ids = $query->getSingleColumnResult(); + // same as: + $ids = $query->getResult(AbstractQuery::HYDRATE_SCALAR_COLUMN); + +``getSingleResult()`` +^^^^^^^^^^^^^^^^^^^^^ + +Retrieves a single object. If the result contains more than one object, a ``NonUniqueResultException`` +is thrown. If the result contains no objects, a ``NoResultException`` is thrown. The pure/mixed distinction does not apply. + +``getOneOrNullResult()`` +^^^^^^^^^^^^^^^^^^^^^^^^ + +Retrieves a single object. If the result contains more than one object, a ``NonUniqueResultException`` +is thrown. If no object is found, ``null`` will be returned. + +Custom Hydration Modes +^^^^^^^^^^^^^^^^^^^^^^ + +You can easily add your own custom hydration modes by first +creating a class which extends ``AbstractHydrator``: + +.. code-block:: php + + _stmt->fetchAllAssociative(); + } + } + +Next you just need to add the class to the ORM configuration: + +.. code-block:: php + + getConfiguration()->addCustomHydrationMode('CustomHydrator', 'MyProject\Hydrators\CustomHydrator'); + +Now the hydrator is ready to be used in your queries: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u'); + $results = $query->getResult('CustomHydrator'); Pure and Mixed Results ~~~~~~~~~~~~~~~~~~~~~~ @@ -1159,165 +1263,6 @@ will return the rows iterating the different top-level entities. [2] => Object (User) [3] => Object (Group) - -Hydration Modes -~~~~~~~~~~~~~~~ - -Each of the Hydration Modes makes assumptions about how the result -is returned to user land. You should know about all the details to -make best use of the different result formats: - -The constants for the different hydration modes are: - - -- ``Query::HYDRATE_OBJECT`` -- ``Query::HYDRATE_ARRAY`` -- ``Query::HYDRATE_SCALAR`` -- ``Query::HYDRATE_SINGLE_SCALAR`` -- ``Query::HYDRATE_SCALAR_COLUMN`` - -Object Hydration -^^^^^^^^^^^^^^^^ - -Object hydration hydrates the result set into the object graph: - -.. code-block:: php - - createQuery('SELECT u FROM CmsUser u'); - $users = $query->getResult(Query::HYDRATE_OBJECT); - -Sometimes the behavior in the object hydrator can be confusing, which is -why we are listing as many of the assumptions here for reference: - -- Objects fetched in a FROM clause are returned as a Set, that means every - object is only ever included in the resulting array once. This is the case - even when using JOIN or GROUP BY in ways that return the same row for an - object multiple times. If the hydrator sees the same object multiple times, - then it makes sure it is only returned once. - -- If an object is already in memory from a previous query of any kind, then - then the previous object is used, even if the database may contain more - recent data. Data from the database is discarded. This even happens if the - previous object is still an unloaded proxy. - -This list might be incomplete. - -Array Hydration -^^^^^^^^^^^^^^^ - -You can run the same query with array hydration and the result set -is hydrated into an array that represents the object graph: - -.. code-block:: php - - createQuery('SELECT u FROM CmsUser u'); - $users = $query->getResult(Query::HYDRATE_ARRAY); - -You can use the ``getArrayResult()`` shortcut as well: - -.. code-block:: php - - getArrayResult(); - -Scalar Hydration -^^^^^^^^^^^^^^^^ - -If you want to return a flat rectangular result set instead of an -object graph you can use scalar hydration: - -.. code-block:: php - - createQuery('SELECT u FROM CmsUser u'); - $users = $query->getResult(Query::HYDRATE_SCALAR); - echo $users[0]['u_id']; - -The following assumptions are made about selected fields using -Scalar Hydration: - - -1. Fields from classes are prefixed by the DQL alias in the result. - A query of the kind 'SELECT u.name ..' returns a key 'u_name' in - the result rows. - -Single Scalar Hydration -^^^^^^^^^^^^^^^^^^^^^^^ - -If you have a query which returns just a single scalar value you can use -single scalar hydration: - -.. code-block:: php - - createQuery('SELECT COUNT(a.id) FROM CmsUser u LEFT JOIN u.articles a WHERE u.username = ?1 GROUP BY u.id'); - $query->setParameter(1, 'jwage'); - $numArticles = $query->getResult(Query::HYDRATE_SINGLE_SCALAR); - -You can use the ``getSingleScalarResult()`` shortcut as well: - -.. code-block:: php - - getSingleScalarResult(); - -Scalar Column Hydration -^^^^^^^^^^^^^^^^^^^^^^^ - -If you have a query which returns a one-dimensional array of scalar values -you can use scalar column hydration: - -.. code-block:: php - - createQuery('SELECT a.id FROM CmsUser u'); - $ids = $query->getResult(Query::HYDRATE_SCALAR_COLUMN); - -You can use the ``getSingleColumnResult()`` shortcut as well: - -.. code-block:: php - - getSingleColumnResult(); - -Custom Hydration Modes -^^^^^^^^^^^^^^^^^^^^^^ - -You can easily add your own custom hydration modes by first -creating a class which extends ``AbstractHydrator``: - -.. code-block:: php - - _stmt->fetchAllAssociative(); - } - } - -Next you just need to add the class to the ORM configuration: - -.. code-block:: php - - getConfiguration()->addCustomHydrationMode('CustomHydrator', 'MyProject\Hydrators\CustomHydrator'); - -Now the hydrator is ready to be used in your queries: - -.. code-block:: php - - createQuery('SELECT u FROM CmsUser u'); - $results = $query->getResult('CustomHydrator'); - Iterating Large Result Sets ~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 8709fb38b0b0c650b5e8113c56537c87e482ef81 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 1 Apr 2024 12:44:58 +0200 Subject: [PATCH 6/7] Fix templated phpdoc return type (#11407) * Improve getClassMetadata phpdoc * Update baseline --- phpstan-baseline.neon | 5 ----- psalm-baseline.xml | 8 -------- src/EntityManagerInterface.php | 2 +- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index c33099dee7a..6f0a73dd5a3 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -135,11 +135,6 @@ parameters: count: 2 path: src/EntityManager.php - - - message: "#^Template type T of method Doctrine\\\\ORM\\\\EntityManagerInterface\\:\\:getClassMetadata\\(\\) is not referenced in a parameter\\.$#" - count: 1 - path: src/EntityManagerInterface.php - - message: "#^Method Doctrine\\\\ORM\\\\EntityRepository\\:\\:matching\\(\\) should return Doctrine\\\\Common\\\\Collections\\\\AbstractLazyCollection\\&Doctrine\\\\Common\\\\Collections\\\\Selectable\\ but returns Doctrine\\\\ORM\\\\LazyCriteriaCollection\\<\\(int\\|string\\), object\\>\\.$#" count: 1 diff --git a/psalm-baseline.xml b/psalm-baseline.xml index d04835c1e85..6c70101de13 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -257,12 +257,6 @@ - - wrapped->getClassMetadata($className)]]> - - - - @@ -309,11 +303,9 @@ name ? $entity : null]]> load($sortedId, null, null, [], $lockMode)]]> loadById($sortedId)]]> - metadataFactory->getMetadataFor($className)]]> - diff --git a/src/EntityManagerInterface.php b/src/EntityManagerInterface.php index 326cb933a59..b3b5ddf9e13 100644 --- a/src/EntityManagerInterface.php +++ b/src/EntityManagerInterface.php @@ -340,7 +340,7 @@ public function hasFilters(); * @psalm-param string|class-string $className * * @return Mapping\ClassMetadata - * @psalm-return Mapping\ClassMetadata + * @psalm-return ($className is class-string ? Mapping\ClassMetadata : Mapping\ClassMetadata) * * @psalm-template T of object */ From b27489348658cd718d18005de37b94f7f8561467 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 15 Apr 2024 15:11:10 +0200 Subject: [PATCH 7/7] Fix BIGINT validation (#11414) --- src/Tools/SchemaValidator.php | 40 ++++++++++--------- .../Tests/Models/BigIntegers/BigIntegers.php | 27 +++++++++++++ tests/Tests/ORM/Tools/SchemaValidatorTest.php | 14 +++++++ 3 files changed, 62 insertions(+), 19 deletions(-) create mode 100644 tests/Tests/Models/BigIntegers/BigIntegers.php diff --git a/src/Tools/SchemaValidator.php b/src/Tools/SchemaValidator.php index 1ea5e688fab..212ad019846 100644 --- a/src/Tools/SchemaValidator.php +++ b/src/Tools/SchemaValidator.php @@ -64,18 +64,18 @@ class SchemaValidator * It maps built-in Doctrine types to PHP types */ private const BUILTIN_TYPES_MAP = [ - AsciiStringType::class => 'string', - BigIntType::class => 'string', - BooleanType::class => 'bool', - DecimalType::class => 'string', - FloatType::class => 'float', - GuidType::class => 'string', - IntegerType::class => 'int', - JsonType::class => 'array', - SimpleArrayType::class => 'array', - SmallIntType::class => 'int', - StringType::class => 'string', - TextType::class => 'string', + AsciiStringType::class => ['string'], + BigIntType::class => ['int', 'string'], + BooleanType::class => ['bool'], + DecimalType::class => ['string'], + FloatType::class => ['float'], + GuidType::class => ['string'], + IntegerType::class => ['int'], + JsonType::class => ['array'], + SimpleArrayType::class => ['array'], + SmallIntType::class => ['int'], + StringType::class => ['string'], + TextType::class => ['string'], ]; public function __construct(EntityManagerInterface $em, bool $validatePropertyTypes = true) @@ -390,21 +390,21 @@ function (array $fieldMapping) use ($class): ?string { $propertyType = $propertyType->getName(); // If the property type is the same as the metadata field type, we are ok - if ($propertyType === $metadataFieldType) { + if (in_array($propertyType, $metadataFieldType, true)) { return null; } if (is_a($propertyType, BackedEnum::class, true)) { $backingType = (string) (new ReflectionEnum($propertyType))->getBackingType(); - if ($metadataFieldType !== $backingType) { + if (! in_array($backingType, $metadataFieldType, true)) { return sprintf( "The field '%s#%s' has the property type '%s' with a backing type of '%s' that differs from the metadata field type '%s'.", $class->name, $fieldName, $propertyType, $backingType, - $metadataFieldType + implode('|', $metadataFieldType) ); } @@ -429,7 +429,7 @@ function (array $fieldMapping) use ($class): ?string { ) { $backingType = (string) (new ReflectionEnum($fieldMapping['enumType']))->getBackingType(); - if ($metadataFieldType === $backingType) { + if (in_array($backingType, $metadataFieldType, true)) { return null; } @@ -439,7 +439,7 @@ function (array $fieldMapping) use ($class): ?string { $fieldName, $fieldMapping['enumType'], $backingType, - $metadataFieldType + implode('|', $metadataFieldType) ); } @@ -455,7 +455,7 @@ function (array $fieldMapping) use ($class): ?string { $class->name, $fieldName, $propertyType, - $metadataFieldType, + implode('|', $metadataFieldType), $fieldMapping['type'] ); }, @@ -468,8 +468,10 @@ function (array $fieldMapping) use ($class): ?string { /** * The exact DBAL type must be used (no subclasses), since consumers of doctrine/orm may have their own * customization around field types. + * + * @return list|null */ - private function findBuiltInType(Type $type): ?string + private function findBuiltInType(Type $type): ?array { $typeName = get_class($type); diff --git a/tests/Tests/Models/BigIntegers/BigIntegers.php b/tests/Tests/Models/BigIntegers/BigIntegers.php new file mode 100644 index 00000000000..16b6f58dd0e --- /dev/null +++ b/tests/Tests/Models/BigIntegers/BigIntegers.php @@ -0,0 +1,27 @@ +em->getClassMetadata(BigIntegers::class); + + self::assertSame( + ['The field \'Doctrine\Tests\Models\BigIntegers\BigIntegers#three\' has the property type \'float\' that differs from the metadata field type \'int|string\' returned by the \'bigint\' DBAL type.'], + $this->validator->validateClass($class) + ); + } } /** @MappedSuperclass */