diff --git a/UPGRADE.md b/UPGRADE.md index dc056e06f73..ca5235e857a 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -17,6 +17,13 @@ Because of that, functionality that aims to do so has been deprecated: # 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. @@ -26,10 +33,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_. diff --git a/composer.json b/composer.json index 2f633e15cd1..a1c8427619d 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "doctrine/annotations": "^1.13", "doctrine/coding-standard": "^9.0", "phpbench/phpbench": "^0.16.10 || ^1.0", - "phpstan/phpstan": "0.12.99", + "phpstan/phpstan": "1.0.1", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", "squizlabs/php_codesniffer": "3.6.1", "symfony/cache": "^4.4 || ^5.2", diff --git a/docs/en/reference/architecture.rst b/docs/en/reference/architecture.rst index 6558c3ee8e7..6c005e43298 100644 --- a/docs/en/reference/architecture.rst +++ b/docs/en/reference/architecture.rst @@ -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 ~~~~~~~~~~~~~~~~ diff --git a/docs/en/reference/configuration.rst b/docs/en/reference/configuration.rst index a7478a78627..36cd5978818 100644 --- a/docs/en/reference/configuration.rst +++ b/docs/en/reference/configuration.rst @@ -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 ` section. diff --git a/docs/en/reference/events.rst b/docs/en/reference/events.rst index a4dc98185a1..2d93fe675c1 100644 --- a/docs/en/reference/events.rst +++ b/docs/en/reference/events.rst @@ -128,45 +128,46 @@ There are two ways to set up an event handler: * For *all events* you can create a Lifecycle Event Listener or Subscriber class and register it by calling ``$eventManager->addEventListener()`` or ``eventManager->addEventSubscriber()``, -see :ref:`listening-and-subscribing-to-lifecycle-events`. +see +:ref:`Listening and subscribing to Lifecycle Events` * For *some events* (see table below), you can create a *Lifecycle Callback* method in the -entity, see :ref:`lifecycle-callbacks`. +entity, see :ref:`Lifecycle Callbacks`. Events Overview --------------- -+-----------------------------+-----------------------+-----------+ -| Event | Dispatched by | Lifecycle | -| | | Callback | -+=============================+=======================+===========+ -| ``preRemove`` | ``$em->remove()`` | Yes | -+-----------------------------+-----------------------+-----------+ -| ``postRemove`` | ``$em->flush()`` | Yes | -+-----------------------------+-----------------------+-----------+ -| ``prePersist`` | ``$em->persist()`` | Yes | -| | on *initial* persist | | -+-----------------------------+-----------------------+-----------+ -| ``postPersist`` | ``$em->flush()`` | Yes | -+-----------------------------+-----------------------+-----------+ -| ``preUpdate`` | ``$em->flush()`` | Yes | -+-----------------------------+-----------------------+-----------+ -| ``postUpdate`` | ``$em->flush()`` | Yes | -+-----------------------------+-----------------------+-----------+ -| ``postLoad`` | Loading from database | Yes | -+-----------------------------+-----------------------+-----------+ -| ``loadClassMetadata`` | Loading of mapping | No | -| | metadata | | -+-----------------------------+-----------------------+-----------+ -| ``onClassMetadataNotFound`` | ``MappingException`` | No | -+-----------------------------+-----------------------+-----------+ -| ``preFlush`` | ``$em->flush()`` | Yes | -+-----------------------------+-----------------------+-----------+ -| ``onFlush`` | ``$em->flush()`` | No | -+-----------------------------+-----------------------+-----------+ -| ``postFlush`` | ``$em->flush()`` | No | -+-----------------------------+-----------------------+-----------+ -| ``onClear`` | ``$em->clear()`` | No | -+-----------------------------+-----------------------+-----------+ ++-----------------------------------------------------------------+-----------------------+-----------+ +| Event | Dispatched by | Lifecycle | +| | | Callback | ++=================================================================+=======================+===========+ +| :ref:`preRemove` | ``$em->remove()`` | Yes | ++-----------------------------------------------------------------+-----------------------+-----------+ +| :ref:`postRemove` | ``$em->flush()`` | Yes | ++-----------------------------------------------------------------+-----------------------+-----------+ +| :ref:`prePersist` | ``$em->persist()`` | Yes | +| | on *initial* persist | | ++-----------------------------------------------------------------+-----------------------+-----------+ +| :ref:`postPersist` | ``$em->flush()`` | Yes | ++-----------------------------------------------------------------+-----------------------+-----------+ +| :ref:`preUpdate` | ``$em->flush()`` | Yes | ++-----------------------------------------------------------------+-----------------------+-----------+ +| :ref:`postUpdate` | ``$em->flush()`` | Yes | ++-----------------------------------------------------------------+-----------------------+-----------+ +| :ref:`postLoad` | Loading from database | Yes | ++-----------------------------------------------------------------+-----------------------+-----------+ +| :ref:`loadClassMetadata` | Loading of mapping | No | +| | metadata | | ++-----------------------------------------------------------------+-----------------------+-----------+ +| ``onClassMetadataNotFound`` | ``MappingException`` | No | ++-----------------------------------------------------------------+-----------------------+-----------+ +| :ref:`preFlush` | ``$em->flush()`` | Yes | ++-----------------------------------------------------------------+-----------------------+-----------+ +| :ref:`onFlush` | ``$em->flush()`` | No | ++-----------------------------------------------------------------+-----------------------+-----------+ +| :ref:`postFlush` | ``$em->flush()`` | No | ++-----------------------------------------------------------------+-----------------------+-----------+ +| ``onClear`` | ``$em->clear()`` | No | ++-----------------------------------------------------------------+-----------------------+-----------+ Naming convention ~~~~~~~~~~~~~~~~~ @@ -286,135 +287,104 @@ 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 `. -.. code-block:: php +.. configuration-block:: - createdAt = date('Y-m-d H:i:s'); - } + // ... - /** @PrePersist */ - public function doOtherStuffOnPrePersist() - { - $this->value = 'changed from prePersist callback!'; - } + #[Column(type: 'string', length: 255)] + public $value; - /** @PostPersist */ - public function doStuffOnPostPersist() - { - $this->value = 'changed from postPersist callback!'; - } + #[PrePersist] + public function doStuffOnPrePersist() + { + $this->createdAt = date('Y-m-d H:i:s'); + } - /** @PostLoad */ - public function doStuffOnPostLoad() - { - $this->value = 'changed from postLoad callback!'; - } + #[PrePersist] + public function doOtherStuffOnPrePersist() + { + $this->value = 'changed from prePersist callback!'; + } - /** @PreUpdate */ - public function doStuffOnPreUpdate() - { - $this->value = 'changed from preUpdate callback!'; + #[PostLoad] + public function doStuffOnPostLoad() + { + $this->value = 'changed from postLoad 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 - - + .. code-block:: annotation - - - - - - - - - - - - - -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. + createdAt = date('Y-m-d H:i:s'); + } - public function doStuffOnPrePersist() - { - // ... - } + /** @PrePersist */ + public function doOtherStuffOnPrePersist() + { + $this->value = 'changed from prePersist callback!'; + } - public function doOtherStuffOnPrePersist() - { - // ... + /** @PostLoad */ + public function doStuffOnPostLoad() + { + $this->value = 'changed from postLoad callback!'; + } } + .. code-block:: xml - public function doStuffOnPostPersist() - { - // ... - } - } + + + + + + + + + + + + + .. code-block:: yaml + User: + type: entity + fields: + # ... + value: + type: string(255) + lifecycleCallbacks: + prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ] + postLoad: [ doStuffOnPostLoad ] Lifecycle Callbacks Event Argument ---------------------------------- @@ -451,7 +421,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` section carefully if you are trying to write your own listener. For event subscribers, there are no surprises. They declare the @@ -520,8 +490,10 @@ EventManager that is passed to the EntityManager factory: .. code-block:: php addEventListener(array(Events::preUpdate), new MyEventListener()); + $eventManager->addEventListener([Events::preUpdate], new MyEventListener()); $eventManager->addEventSubscriber(new MyEventSubscriber()); $entityManager = EntityManager::create($dbOpts, $config, $eventManager); @@ -532,7 +504,9 @@ EntityManager was created: .. code-block:: php 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: @@ -552,6 +526,8 @@ the restrictions apply as well, with the additional restriction that (prior to version 2.4) you do not have access to the ``EntityManager`` or ``UnitOfWork`` APIs inside these events. +.. _reference-events-pre-persist: + prePersist ~~~~~~~~~~ @@ -577,6 +553,8 @@ The following restrictions apply to ``prePersist``: - Doctrine will not recognize changes made to relations in a prePersist event. This includes modifications to collections such as additions, removals or replacement. + +.. _reference-events-pre-remove: preRemove ~~~~~~~~~ @@ -589,6 +567,8 @@ There are no restrictions to what methods can be called inside the ``preRemove`` event, except when the remove method itself was called during a flush operation. +.. _reference-events-pre-flush: + preFlush ~~~~~~~~ @@ -611,6 +591,8 @@ result in infinite loop. } } +.. _reference-events-on-flush: + onFlush ~~~~~~~ @@ -674,6 +656,8 @@ The following restrictions apply to the onFlush event: affected entity. This can be done by calling ``$unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity)``. +.. _reference-events-post-flush: + postFlush ~~~~~~~~~ @@ -694,6 +678,8 @@ postFlush } } +.. _reference-events-pre-update: + preUpdate ~~~~~~~~~ @@ -778,6 +764,8 @@ Restrictions for this event: API are strongly discouraged and don't work as expected outside the flush operation. +.. _reference-events-post-update-remove-persist: + postUpdate, postRemove, postPersist ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -787,6 +775,8 @@ database, but you can use these events to alter non-persistable items, like non-mapped fields, logging or even associated classes that are not directly mapped by Doctrine. +.. _reference-events-post-load: + postLoad ~~~~~~~~ @@ -999,6 +989,8 @@ Implementing your own resolver : $configurations->setEntityListenerResolver(new MyEntityListenerResolver); EntityManager::create(.., $configurations, ..); +.. _reference-events-load-class-metadata: + Load ClassMetadata Event ------------------------ diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 1cb108668ca..846264331e7 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -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 `. 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 `, Doctrine will detect these relations and update all of the modified entities in the database automatically when ``flush()`` is called. diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index bc28309a26e..30f30ce217d 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -295,7 +295,7 @@ - + @@ -402,7 +402,7 @@ - + @@ -497,6 +497,12 @@ + + + + + + @@ -612,7 +618,7 @@ - + diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index f23144810e0..bab96a448c5 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -1253,7 +1253,8 @@ private function getTimestampKey(): ?TimestampCacheKey * Will return the configured id if it exists otherwise a hash will be * automatically generated for you. * - * @return array ($key, $hash) + * @return string[] ($key, $hash) + * @psalm-return array{string, string} ($key, $hash) */ protected function getHydrationCacheId() { diff --git a/lib/Doctrine/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php b/lib/Doctrine/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php index c8576294062..657d53221d6 100644 --- a/lib/Doctrine/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php +++ b/lib/Doctrine/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php @@ -5,9 +5,7 @@ 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 diff --git a/lib/Doctrine/ORM/EntityManagerInterface.php b/lib/Doctrine/ORM/EntityManagerInterface.php index ebb841d057b..3218d9dd119 100644 --- a/lib/Doctrine/ORM/EntityManagerInterface.php +++ b/lib/Doctrine/ORM/EntityManagerInterface.php @@ -330,11 +330,9 @@ public function hasFilters(); * {@inheritDoc} * * @psalm-param string|class-string $className - * @phpstan-param string $className * * @return Mapping\ClassMetadata * @psalm-return Mapping\ClassMetadata - * @phpstan-return Mapping\ClassMetadata * * @psalm-template T of object */ diff --git a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php index 13319dc5b9c..454330290eb 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php @@ -79,7 +79,8 @@ protected function hydrateRowData(array $row, array &$result) // 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); diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index d736886563c..c898198f675 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -20,6 +20,7 @@ use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\Mapping\ReflectionService; use InvalidArgumentException; +use LogicException; use ReflectionClass; use ReflectionNamedType; use ReflectionProperty; @@ -442,6 +443,7 @@ class ClassMetadataInfo implements ClassMetadata * originalField?: string, * quoted?: bool, * requireSQLConversion?: bool, + * declared?: class-string, * declaredField?: string, * options: array * }> @@ -495,7 +497,7 @@ class ClassMetadataInfo implements ClassMetadata * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE * inheritance mappings. * - * @psalm-var array + * @psalm-var array|null */ public $discriminatorColumn; @@ -509,7 +511,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, + * quoted?: bool + * } */ public $table; @@ -665,7 +674,7 @@ class ClassMetadataInfo implements ClassMetadata /** * The ReflectionClass instance of the mapped class. * - * @var ReflectionClass + * @var ReflectionClass|null */ public $reflClass; @@ -3090,6 +3099,18 @@ public function setDiscriminatorColumn($columnDef) } } + /** + * @return array + */ + 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. diff --git a/lib/Doctrine/ORM/Mapping/Column.php b/lib/Doctrine/ORM/Mapping/Column.php index 622b507a0e2..9602c8e6651 100644 --- a/lib/Doctrine/ORM/Mapping/Column.php +++ b/lib/Doctrine/ORM/Mapping/Column.php @@ -15,26 +15,26 @@ #[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 */ public $options = []; - /** @var string */ + /** @var string|null */ public $columnDefinition; /** diff --git a/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php index 0e06e889df7..b008e63ebaa 100644 --- a/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php @@ -249,7 +249,7 @@ public function loadCriteria(PersistentCollection $collection, Criteria $criteri $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); diff --git a/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php b/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php index 61751a8706d..c5f5e04f10e 100644 --- a/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php +++ b/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php @@ -24,7 +24,7 @@ protected function prepareInsertData($entity) $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; diff --git a/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php index 151aad7bc92..5c59168eb75 100644 --- a/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php @@ -13,7 +13,6 @@ 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 @@ protected function getSelectColumnsSQL() } $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 @@ protected function getSelectColumnsSQL() ? $baseTableAlias : $this->getSQLTableAlias($this->class->rootEntityName); - $columnList[] = $tableAlias . '.' . $discrColumn; + $columnList[] = $tableAlias . '.' . $discrColumnName; // sub tables foreach ($this->class->subClasses as $subClassName) { @@ -540,7 +540,7 @@ protected function getInsertColumnList() // 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; diff --git a/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php b/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php index 2eca457c4f4..b9f95c58123 100644 --- a/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php +++ b/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php @@ -44,16 +44,17 @@ protected function getSelectColumnsSQL() $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 @@ protected function getInsertColumnList() $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 @@ protected function getSelectConditionDiscriminatorValueSQL() $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 . ')'; } /** diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 67ccb9d830e..294cda48c1d 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -214,7 +214,7 @@ class Parser */ private $customOutputWalker; - /** @psalm-var list */ + /** @psalm-var array */ private $identVariableExpressions = []; /** diff --git a/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php b/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php index 4815e706c4a..65618a9ef7d 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php +++ b/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php @@ -369,7 +369,7 @@ public function addNamedNativeQueryEntityResultMapping(ClassMetadataInfo $classM { 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); diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 26099f79e8c..2a1139181cb 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -465,7 +465,7 @@ private function generateDiscriminatorColumnConditionSQL(array $dqlAliases): str ? $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 @@ public function walkSelectClause($selectClause) // 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 @@ public function walkInstanceOfExpression($instanceOfExpr) $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; diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php index fc22d2c7664..1067647e97f 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -151,7 +151,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (empty($metadata)) { $ui->success('No Metadata Classes to process.'); - return; + return 0; } foreach ($metadata as $class) { diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 06d6b0243c7..92c17708ade 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -1140,7 +1140,11 @@ protected function generateDiscriminatorColumnAnnotation(ClassMetadataInfo $meta return ''; } - $discrColumn = $metadata->discriminatorColumn; + $discrColumn = $metadata->discriminatorColumn; + if ($discrColumn === null) { + return ''; + } + $columnDefinition = 'name="' . $discrColumn['name'] . '", type="' . $discrColumn['type'] . '", length=' . $discrColumn['length']; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 81ce4dac0fd..ecde6563d6c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -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\\, 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\\, 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\\, quoted\\?\\: bool\\} in isset\\(\\) always exists and is not nullable\\.$#" + count: 1 path: lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php - @@ -526,37 +551,32 @@ parameters: 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 + message: "#^Call to an undefined method ReflectionProperty\\:\\:getType\\(\\)\\.$#" + count: 3 path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php - - message: "#^Array \\(array\\\\) does not accept true\\.$#" + message: "#^Call to an undefined method ReflectionProperty\\:\\:hasType\\(\\)\\.$#" count: 1 path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php - - message: "#^Call to an undefined method ReflectionProperty\\:\\:getType\\(\\)\\.$#" - count: 3 + message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\NamingStrategy\\:\\:joinColumnName\\(\\) invoked with 2 parameters, 1 required\\.$#" + count: 2 path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php - - message: "#^Call to an undefined method ReflectionProperty\\:\\:hasType\\(\\)\\.$#" + message: "#^Negated boolean expression is always false\\.$#" count: 1 path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php - - message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\NamingStrategy\\:\\:joinColumnName\\(\\) invoked with 2 parameters, 1 required\\.$#" - count: 2 + message: "#^Offset 'schema' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\, quoted\\?\\: bool\\} on left side of \\?\\? always exists and is not nullable\\.$#" + count: 1 path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php - - message: "#^Negated boolean expression is always false\\.$#" + message: "#^Offset 'unique' on array\\{type\\: string, fieldName\\: string, columnName\\?\\: string, inherited\\?\\: class\\-string, nullable\\?\\: bool, originalClass\\?\\: class\\-string, originalField\\?\\: string, scale\\?\\: int, \\.\\.\\.\\} in isset\\(\\) does not exist\\.$#" count: 1 path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -765,6 +785,11 @@ parameters: count: 2 path: lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php + - + message: "#^PHPDoc type array\\ of property Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\AttributeDriver\\:\\:\\$entityAnnotationClasses is not covariant with PHPDoc type array\\ 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\\ given\\.$#" count: 1 @@ -960,6 +985,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\\ given\\.$#" count: 1 @@ -1115,6 +1145,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\\ given\\.$#" count: 1 @@ -1380,15 +1415,30 @@ parameters: count: 1 path: lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php + - + message: "#^PHPDoc type array\\ of property Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Andx\\:\\:\\$allowedClasses is not covariant with PHPDoc type array\\ of overridden property Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Base\\:\\:\\$allowedClasses\\.$#" + count: 1 + path: lib/Doctrine/ORM/Query/Expr/Andx.php + + - + message: "#^PHPDoc type array\\ of property Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Orx\\:\\:\\$allowedClasses is not covariant with PHPDoc type array\\ of overridden property Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Base\\:\\:\\$allowedClasses\\.$#" + count: 1 + path: lib/Doctrine/ORM/Query/Expr/Orx.php + + - + message: "#^PHPDoc type array\\ of property Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Select\\:\\:\\$allowedClasses is not covariant with PHPDoc type array\\ 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\\\\) 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\\.$#" @@ -1422,14 +1472,19 @@ parameters: path: lib/Doctrine/ORM/Query/Parser.php - - message: "#^Result of && is always false\\.$#" - count: 1 + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 3 path: lib/Doctrine/ORM/Query/Parser.php - - message: "#^Unreachable statement \\- code above always terminates\\.$#" - count: 4 - path: lib/Doctrine/ORM/Query/Parser.php + message: "#^Offset 'columns' on array\\{name\\: string, entities\\: array, columns\\: array\\} in isset\\(\\) always exists and is not nullable\\.$#" + count: 1 + path: lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php + + - + message: "#^Offset 'entities' on array\\{name\\: string, entities\\: array, columns\\: array\\} in isset\\(\\) always exists and is not nullable\\.$#" + count: 1 + path: lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php - message: "#^Parameter \\#2 \\$class of static method Doctrine\\\\ORM\\\\Utility\\\\PersisterHelper\\:\\:getTypeOfColumn\\(\\) expects Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata, Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo given\\.$#" @@ -1476,6 +1531,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\\ 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\\ given\\.$#" count: 1 @@ -1982,7 +2047,7 @@ parameters: path: lib/Doctrine/ORM/QueryBuilder.php - - message: "#^Parameter \\#2 \\$dqlPart of method Doctrine\\\\ORM\\\\QueryBuilder\\:\\:add\\(\\) expects array\\<'join'\\|int, array\\\\|string\\>\\|object\\|string, array\\&nonEmpty given\\.$#" + message: "#^Parameter \\#2 \\$dqlPart of method Doctrine\\\\ORM\\\\QueryBuilder\\:\\:add\\(\\) expects array\\<'join'\\|int, array\\\\|string\\>\\|object\\|string, non\\-empty\\-array\\ given\\.$#" count: 2 path: lib/Doctrine/ORM/QueryBuilder.php @@ -2021,11 +2086,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\\, array\\ given\\.$#" count: 1 @@ -2062,7 +2122,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\\, 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\\, 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\\, 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\\, 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\\, 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\\\\:\\:\\$lifecycleCallbacks \\(array\\\\>\\) in isset\\(\\) is not nullable\\.$#" count: 1 path: lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -2096,6 +2196,41 @@ 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\\, 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\\, 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\\, 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\\{type\\: string, fieldName\\: string, columnName\\?\\: string, length\\?\\: int, id\\?\\: bool, nullable\\?\\: bool, columnDefinition\\?\\: string, precision\\?\\: int, \\.\\.\\.\\} 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\\, 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\\, 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, options\\: array, columnName\\?\\: string, length\\?\\: int, id\\?\\: bool, nullable\\?\\: bool, columnDefinition\\?\\: string, \\.\\.\\.\\} 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 +2241,11 @@ parameters: count: 2 path: lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php + - + message: "#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\\\:\\:\\$lifecycleCallbacks \\(array\\\\>\\) 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 +2256,16 @@ parameters: count: 1 path: lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php + - + message: "#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\\\:\\:\\$lifecycleCallbacks \\(array\\\\>\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php + + - + message: "#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\\\:\\:\\$table \\(array\\\\) 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 +2276,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 +2316,26 @@ parameters: count: 1 path: lib/Doctrine/ORM/Tools/SchemaTool.php + - + message: "#^Offset 'columnDefinition' on array\\{type\\: string, fieldName\\: string, columnName\\?\\: string, inherited\\?\\: class\\-string, nullable\\?\\: bool, originalClass\\?\\: class\\-string, originalField\\?\\: string, scale\\?\\: int, \\.\\.\\.\\} in isset\\(\\) does not exist\\.$#" + count: 1 + path: lib/Doctrine/ORM/Tools/SchemaTool.php + + - + message: "#^Offset 'indexes' on array\\{name\\: string, schema\\: string, indexes\\: array, uniqueConstraints\\: array, options\\: array\\, 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\\, 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\\, 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 diff --git a/phpstan.neon b/phpstan.neon index 2ccd6581380..7a0b60a06b1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -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\.$/' diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 378203ee6f9..3f29dd590cf 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -777,8 +777,7 @@ - - ClassMetadata + ClassMetadata ClassMetadata ClassMetadata @@ -825,8 +824,7 @@ addNamedNativeQuery addNamedQuery - - $class->reflClass + $definition @@ -869,6 +867,9 @@ $parent->table $parent->versionField + + $subClass->table[$indexType][$indexName] + $parentClass->table[$indexType] @@ -936,9 +937,10 @@ protected function _validateAndCompleteOneToOneMapping(array $mapping) public $inheritanceType = self::INHERITANCE_TYPE_NONE; - + ReflectionProperty ReflectionProperty + getReflectionClass $definition @@ -967,7 +969,8 @@ array{usage: int, region: string|null} - + + $this->reflClass $this->reflFields[$name] $this->reflFields[$this->identifier[0]] @@ -987,12 +990,17 @@ $queryMapping['resultClass'] $reflService->getAccessibleProperty($mapping['originalClass'], $mapping['originalField']) - - $reflService->getClass($this->name) - $reflService->getClass($this->name) + null - + + $embeddable->reflClass->name + $this->reflClass->name + + + getProperty + getProperty + getProperty getValue getValue getValue @@ -1013,12 +1021,11 @@ $this->fieldMappings[$idProperty]['columnName'] $this->fieldMappings[$idProperty]['columnName'] - + $discriminatorColumn $idGenerator $isVersioned $namespace - $reflClass $sequenceGeneratorDefinition $table $tableGeneratorDefinition @@ -1041,21 +1048,11 @@ $mapping !== false $mapping !== false - joinColumnName joinColumnName - - - $columnDefinition - $length - $name - $precision - $scale - - $name @@ -1107,6 +1104,10 @@ $class + + $mapping + + $metadata->inheritanceType $metadata->isEmbeddedClass @@ -1121,10 +1122,6 @@ $primaryTable['indexes'] $primaryTable['uniqueConstraints'] - - isset($column->columnDefinition) - isset($column->name) - addEntityListener addLifecycleCallback @@ -1173,6 +1170,10 @@ $value[1] $value[1] + + $mapping + + $entityAnnotationClasses @@ -1189,10 +1190,8 @@ assert($method instanceof ReflectionMethod) assert($property instanceof ReflectionProperty) - + assert($cacheAttribute instanceof Mapping\Cache) - isset($column->columnDefinition) - isset($column->name) @@ -2629,16 +2628,9 @@ call_user_func($functionClass, $functionName) call_user_func($functionClass, $functionName) - - $this->identVariableExpressions[$dqlAlias] - $this->queryComponents[$dqlAlias] - SelectStatement|UpdateStatement|DeleteStatement - - $this->identVariableExpressions - $primary $this->CollectionMemberExpression() @@ -2843,6 +2835,14 @@ $renameMode $renameMode + + $classMetadata->reflClass->name + + + getShortName + getShortName + getShortName + $class->fieldMappings[$this->fieldMappings[$columnName]]['columnName'] @@ -3337,9 +3337,6 @@ new ClassMetadataExporter() new EntityGenerator() - - int - configure @@ -3479,10 +3476,8 @@ ClassMetadataInfo::GENERATOR_TYPE_UUID - - $metadata->reflClass !== null + class_exists($metadata->name) - new ReflectionClass($metadata->name) public function setFieldVisibility($visibility) @@ -3508,12 +3503,11 @@ $classToExtend - + + (array) $metadata->table['options'] (bool) $embeddablesImmutable - - $metadata->reflClass - $metadata->reflClass !== null + isset($metadata->lifecycleCallbacks) @@ -3752,6 +3746,11 @@ $indexName + + + isAbstract + + new ClassLoader('Doctrine', $directory) diff --git a/tests/Doctrine/StaticAnalysis/get-metadata.php b/tests/Doctrine/StaticAnalysis/get-metadata.php index 0b9f29bc60a..c9b11036155 100644 --- a/tests/Doctrine/StaticAnalysis/get-metadata.php +++ b/tests/Doctrine/StaticAnalysis/get-metadata.php @@ -17,16 +17,14 @@ abstract class GetMetadata { /** * @param string|object $class - * @phpstan-param class-string|object $class + * @psalm-param class-string|object $class */ abstract public function getEntityManager($class): EntityManagerInterface; /** * @psalm-param class-string $class - * @phpstan-param class-string $class * * @psalm-return ClassMetadata - * @phpstan-return ClassMetadata * * @psalm-template TObject of object */ diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH9109Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH9109Test.php new file mode 100644 index 00000000000..97d5da842b2 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH9109Test.php @@ -0,0 +1,217 @@ +_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 + * @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 + */ + 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; + } +} diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php index e47ea8b325a..0e79aad7c30 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php @@ -1119,6 +1119,13 @@ public function testDiscriminatorColumnDefaultName(): void $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`', + ] + ); + } +} diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.php new file mode 100644 index 00000000000..03f5a86eb61 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.php @@ -0,0 +1,18 @@ +mapField( + [ + 'id' => true, + 'fieldName' => 'id', + 'type' => 'integer', + ] +); +$metadata->mapField( + [ + 'fieldName' => 'count', + 'type' => 'integer', + 'columnName' => '`count`', + ] +); diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.dcm.xml new file mode 100644 index 00000000000..16eb27692b3 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.dcm.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.dcm.yml new file mode 100644 index 00000000000..a315310e4d1 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.ReservedWordInTableColumn.dcm.yml @@ -0,0 +1,10 @@ +Doctrine\Tests\ORM\Mapping\ReservedWordInTableColumn: + type: entity + id: + id: + generator: + strategy: NONE + fields: + count: + type: integer + column: '`count`' diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index baabb66c7a8..5efd836d74a 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -2116,26 +2116,6 @@ public function testSupportsMoreThanTwoParametersInConcatFunction(): void '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); }