From 803230cdb0d8e9ac749f4655325d85de2376ab24 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Tue, 25 Oct 2022 14:12:50 +0200 Subject: [PATCH 1/7] Result infering: support indexBy and lists --- .../CreateQueryDynamicReturnTypeExtension.php | 6 ++-- .../QueryResultDynamicReturnTypeExtension.php | 35 +++++++++++++++---- .../Doctrine/Query/QueryResultTypeBuilder.php | 15 ++++++++ .../Doctrine/Query/QueryResultTypeWalker.php | 6 ++++ src/Type/Doctrine/Query/QueryType.php | 13 ++++--- ...lderGetQueryDynamicReturnTypeExtension.php | 6 ++-- stubs/ORM/AbstractQuery.stub | 1 + stubs/ORM/Query.stub | 3 +- .../Doctrine/data/QueryResult/config.neon | 2 ++ .../Doctrine/data/QueryResult/createQuery.php | 8 ++--- .../data/QueryResult/queryBuilderGetQuery.php | 34 +++++++++++++----- .../Doctrine/data/QueryResult/queryResult.php | 31 ++++++++-------- 12 files changed, 115 insertions(+), 45 deletions(-) diff --git a/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php b/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php index 6384f718..a9167da9 100644 --- a/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php @@ -78,7 +78,7 @@ public function getTypeFromMethodCall( $em = $this->objectMetadataResolver->getObjectManager(); if (!$em instanceof EntityManagerInterface) { - return new QueryType($queryString, null); + return new QueryType($queryString, null, null); } $typeBuilder = new QueryResultTypeBuilder(); @@ -87,10 +87,10 @@ public function getTypeFromMethodCall( $query = $em->createQuery($queryString); QueryResultTypeWalker::walk($query, $typeBuilder, $this->descriptorRegistry); } catch (ORMException | DBALException | NewDBALException | CommonException $e) { - return new QueryType($queryString, null); + return new QueryType($queryString, null, null); } - return new QueryType($queryString, $typeBuilder->getResultType()); + return new QueryType($queryString, $typeBuilder->getResultType(), $typeBuilder->getIndexType()); } return new GenericObjectType( Query::class, diff --git a/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php b/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php index 66991f11..1a8e1b40 100644 --- a/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php @@ -8,6 +8,7 @@ use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\ShouldNotHappenException; +use PHPStan\Type\Accessory\AccessoryArrayListType; use PHPStan\Type\ArrayType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\DynamicMethodReturnTypeExtension; @@ -18,6 +19,7 @@ use PHPStan\Type\NullType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; +use PHPStan\Type\TypeUtils; use PHPStan\Type\VoidType; final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension @@ -70,14 +72,27 @@ public function getTypeFromMethodCall( $queryType = $scope->getType($methodCall->var); $queryResultType = $this->getQueryResultType($queryType); + $queryIndexType = $this->getQueryIndexType($queryType); return $this->getMethodReturnTypeForHydrationMode( $methodReflection, $hydrationMode, - $queryResultType + $queryResultType, + $queryIndexType ); } + private function getQueryIndexType(Type $queryType): Type + { + if (!$queryType instanceof GenericObjectType) { + return new MixedType(); + } + + $types = $queryType->getTypes(); + + return $types[1] ?? new MixedType(); + } + private function getQueryResultType(Type $queryType): Type { if (!$queryType instanceof GenericObjectType) { @@ -92,7 +107,8 @@ private function getQueryResultType(Type $queryType): Type private function getMethodReturnTypeForHydrationMode( MethodReflection $methodReflection, Type $hydrationMode, - Type $queryResultType + Type $queryResultType, + ?Type $queryIndexType ): Type { $isVoidType = (new VoidType())->isSuperTypeOf($queryResultType); @@ -126,10 +142,17 @@ private function getMethodReturnTypeForHydrationMode( $queryResultType ); default: - return new ArrayType( - new MixedType(), - $queryResultType - ); + return $queryIndexType instanceof MixedType + ? AccessoryArrayListType::intersectWith( + TypeCombinator::intersect( + new ArrayType(new IntegerType(), $queryResultType), + ...TypeUtils::getAccessoryTypes($queryResultType) + ) + ) + : new ArrayType( + $queryIndexType, + $queryResultType + ); } } diff --git a/src/Type/Doctrine/Query/QueryResultTypeBuilder.php b/src/Type/Doctrine/Query/QueryResultTypeBuilder.php index d8b93bc0..929c518a 100644 --- a/src/Type/Doctrine/Query/QueryResultTypeBuilder.php +++ b/src/Type/Doctrine/Query/QueryResultTypeBuilder.php @@ -62,6 +62,11 @@ final class QueryResultTypeBuilder */ private $newObjects = []; + /** + * @var Type|null + */ + private $indexedBy = null; + public function setSelectQuery(): void { $this->selectQuery = true; @@ -230,4 +235,14 @@ private function resolveOffsetType($alias): Type return new ConstantStringType($alias); } + public function setIndexedBy(Type $type): void + { + $this->indexedBy = $type; + } + + public function getIndexType(): ?Type + { + return $this->indexedBy; + } + } diff --git a/src/Type/Doctrine/Query/QueryResultTypeWalker.php b/src/Type/Doctrine/Query/QueryResultTypeWalker.php index 1f592e42..06cc093d 100644 --- a/src/Type/Doctrine/Query/QueryResultTypeWalker.php +++ b/src/Type/Doctrine/Query/QueryResultTypeWalker.php @@ -312,6 +312,10 @@ public function walkFromClause($fromClause) */ public function walkIdentificationVariableDeclaration($identificationVariableDecl) { + if ($identificationVariableDecl->indexBy !== null) { + $identificationVariableDecl->indexBy->dispatch($this); + } + foreach ($identificationVariableDecl->joins as $join) { assert($join instanceof AST\Node); @@ -326,6 +330,8 @@ public function walkIdentificationVariableDeclaration($identificationVariableDec */ public function walkIndexBy($indexBy): void { + $type = $this->unmarshalType($indexBy->singleValuedPathExpression->dispatch($this)); + $this->typeBuilder->setIndexedBy($type); } /** diff --git a/src/Type/Doctrine/Query/QueryType.php b/src/Type/Doctrine/Query/QueryType.php index 63766acc..2c40b263 100644 --- a/src/Type/Doctrine/Query/QueryType.php +++ b/src/Type/Doctrine/Query/QueryType.php @@ -14,10 +14,15 @@ class QueryType extends GenericObjectType /** @var string */ private $dql; - public function __construct(string $dql, ?Type $resultType = null) - { - $resultType = $resultType ?? new MixedType(); - parent::__construct('Doctrine\ORM\Query', [$resultType]); + public function __construct( + string $dql, + ?Type $resultType, + ?Type $indexType + ) { + parent::__construct('Doctrine\ORM\Query', [ + $resultType ?? new MixedType(), + $indexType ?? new MixedType(), + ]); $this->dql = $dql; } diff --git a/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php b/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php index c828411f..5b2fde24 100644 --- a/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php @@ -153,7 +153,7 @@ private function getQueryType(string $dql): Type { $em = $this->objectMetadataResolver->getObjectManager(); if (!$em instanceof EntityManagerInterface) { - return new QueryType($dql, null); + return new QueryType($dql, null, null); } $typeBuilder = new QueryResultTypeBuilder(); @@ -162,10 +162,10 @@ private function getQueryType(string $dql): Type $query = $em->createQuery($dql); QueryResultTypeWalker::walk($query, $typeBuilder, $this->descriptorRegistry); } catch (ORMException | DBALException | CommonException $e) { - return new QueryType($dql, null); + return new QueryType($dql, null, null); } - return new QueryType($dql, $typeBuilder->getResultType()); + return new QueryType($dql, $typeBuilder->getResultType(), $typeBuilder->getIndexType()); } } diff --git a/stubs/ORM/AbstractQuery.stub b/stubs/ORM/AbstractQuery.stub index 8c6dc07b..bc964c22 100644 --- a/stubs/ORM/AbstractQuery.stub +++ b/stubs/ORM/AbstractQuery.stub @@ -6,6 +6,7 @@ use Doctrine\Common\Collections\ArrayCollection; /** * @template TResult The type of results returned by this query in HYDRATE_OBJECT mode + * @template TIndex The index type of results */ abstract class AbstractQuery { diff --git a/stubs/ORM/Query.stub b/stubs/ORM/Query.stub index d3610bb7..4066ae0d 100644 --- a/stubs/ORM/Query.stub +++ b/stubs/ORM/Query.stub @@ -4,8 +4,9 @@ namespace Doctrine\ORM; /** * @template TResult The type of results returned by this query in HYDRATE_OBJECT mode + * @template TIndex The index type of the results * - * @extends AbstractQuery + * @extends AbstractQuery */ final class Query extends AbstractQuery { diff --git a/tests/Type/Doctrine/data/QueryResult/config.neon b/tests/Type/Doctrine/data/QueryResult/config.neon index 147e4f94..5ce3210a 100644 --- a/tests/Type/Doctrine/data/QueryResult/config.neon +++ b/tests/Type/Doctrine/data/QueryResult/config.neon @@ -3,3 +3,5 @@ includes: parameters: doctrine: objectManagerLoader: entity-manager.php + featureToggles: + listType: true diff --git a/tests/Type/Doctrine/data/QueryResult/createQuery.php b/tests/Type/Doctrine/data/QueryResult/createQuery.php index 2511b435..36c8ac7c 100644 --- a/tests/Type/Doctrine/data/QueryResult/createQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/createQuery.php @@ -15,28 +15,28 @@ public function testQueryTypeParametersAreInfered(EntityManagerInterface $em): v FROM QueryResult\Entities\Many m '); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->createQuery(' SELECT m.intColumn, m.stringNullColumn FROM QueryResult\Entities\Many m '); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } public function testQueryResultTypeIsMixedWhenDQLIsNotKnown(EntityManagerInterface $em, string $dql): void { $query = $em->createQuery($dql); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); // TODO fix } public function testQueryResultTypeIsMixedWhenDQLIsInvalid(EntityManagerInterface $em, string $dql): void { $query = $em->createQuery('invalid'); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } } diff --git a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php index 000f9d7e..c22f430e 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php @@ -20,12 +20,12 @@ private function getQueryBuilderMany(EntityManagerInterface $em): QueryBuilder public function addAndWhereAndGetQuery(EntityManagerInterface $em): void { $qb = $this->getQueryBuilderMany($em)->andWhere('m.intColumn = 1'); - assertType('array', $qb->getQuery()->getResult()); + assertType('list', $qb->getQuery()->getResult()); } public function getQueryDirectly(EntityManagerInterface $em): void { - assertType('array', $this->getQueryBuilderMany($em)->getQuery()->getResult()); + assertType('list', $this->getQueryBuilderMany($em)->getQuery()->getResult()); } public function testQueryTypeParametersAreInfered(EntityManagerInterface $em): void @@ -35,14 +35,32 @@ public function testQueryTypeParametersAreInfered(EntityManagerInterface $em): v ->from(Many::class, 'm') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->createQueryBuilder() ->select(['m.intColumn', 'm.stringNullColumn']) ->from(Many::class, 'm') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); + } + + public function testIndexByInfering(EntityManagerInterface $em): void + { + $query = $em->createQueryBuilder() + ->select('m') + ->from(Many::class, 'm', 'm.intColumn') + ->getQuery(); + + assertType('Doctrine\ORM\Query', $query); + + $query = $em->createQueryBuilder() + ->select(['m.intColumn', 'm.stringNullColumn']) + ->from(Many::class, 'm') + ->indexBy('m', 'm.stringColumn') + ->getQuery(); + + assertType('Doctrine\ORM\Query', $query); } public function testQueryResultTypeIsMixedWhenDQLIsNotKnown(QueryBuilder $builder): void @@ -59,7 +77,7 @@ public function testQueryResultTypeIsMixedWhenDQLIsInvalid(EntityManagerInterfac ->from(Many::class, 'm') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } public function testQueryResultTypeIsVoidWithDeleteOrUpdate(EntityManagerInterface $em): void @@ -71,7 +89,7 @@ public function testQueryResultTypeIsVoidWithDeleteOrUpdate(EntityManagerInterfa ->delete() ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->getRepository(Many::class) ->createQueryBuilder('m') @@ -81,7 +99,7 @@ public function testQueryResultTypeIsVoidWithDeleteOrUpdate(EntityManagerInterfa ->set('m.intColumn', '42') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } @@ -90,7 +108,7 @@ public function testQueryTypeIsInferredOnAcrossMethods(EntityManagerInterface $e $query = $this->getQueryBuilder($em) ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } private function getQueryBuilder(EntityManagerInterface $em): QueryBuilder diff --git a/tests/Type/Doctrine/data/QueryResult/queryResult.php b/tests/Type/Doctrine/data/QueryResult/queryResult.php index 3a1e144e..5f08d539 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryResult.php +++ b/tests/Type/Doctrine/data/QueryResult/queryResult.php @@ -16,15 +16,14 @@ public function testQueryTypeParametersAreInfered(EntityManagerInterface $em): v FROM QueryResult\Entities\Many m '); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->createQuery(' SELECT m.intColumn, m.stringNullColumn FROM QueryResult\Entities\Many m '); - assertType('Doctrine\ORM\Query', $query); - + assertType('Doctrine\ORM\Query', $query); } /** @@ -41,7 +40,7 @@ public function testReturnTypeOfQueryMethodsWithImplicitHydrationMode(EntityMana '); assertType( - 'array', + 'list', $query->getResult() ); assertType( @@ -83,7 +82,7 @@ public function testReturnTypeOfQueryMethodsWithExplicitObjectHydrationMode(Enti '); assertType( - 'array', + 'list', $query->getResult(AbstractQuery::HYDRATE_OBJECT) ); assertType( @@ -91,15 +90,15 @@ public function testReturnTypeOfQueryMethodsWithExplicitObjectHydrationMode(Enti $query->toIterable([], AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'array', + 'list', $query->execute(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'array', + 'list', $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'array', + 'list', $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( @@ -117,19 +116,19 @@ public function testReturnTypeOfQueryMethodsWithExplicitObjectHydrationMode(Enti '); assertType( - 'array', + 'list', $query->getResult(AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'array', + 'list', $query->execute(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'array', + 'list', $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'array', + 'list', $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( @@ -317,7 +316,7 @@ public function testReturnTypeOfQueryMethodsWithReturnTypeIsTemplateMixedType(En public function testReturnTypeOfQueryMethodsWithReturnTypeIsNonVoidTemplate(EntityManagerInterface $em, Query $query): void { assertType( - 'array', + 'list', $query->getResult(AbstractQuery::HYDRATE_OBJECT) ); assertType( @@ -325,15 +324,15 @@ public function testReturnTypeOfQueryMethodsWithReturnTypeIsNonVoidTemplate(Enti $query->toIterable([], AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'array', + 'list', $query->execute(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'array', + 'list', $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'array', + 'list', $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( From 76050f43fcc65e308d0d42fc6a062e1ee92e3483 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Fri, 4 Nov 2022 14:58:17 +0100 Subject: [PATCH 2/7] Safer ListIndexMarkerType, fix Query TIndex generics everywhere --- composer.json | 2 +- .../CreateQueryDynamicReturnTypeExtension.php | 4 ++-- .../QueryResultDynamicReturnTypeExtension.php | 5 +++-- .../Doctrine/Query/QueryResultTypeBuilder.php | 20 +++++++++++-------- .../Doctrine/Query/QueryResultTypeWalker.php | 6 +++--- src/Type/Doctrine/Query/QueryType.php | 3 ++- .../Doctrine/Type/ListIndexMarkerType.php | 10 ++++++++++ stubs/ORM/AbstractQuery.stub | 2 +- stubs/ORM/Query.stub | 2 +- stubs/ORM/QueryBuilder.stub | 2 +- stubs/bleedingEdge/ORM/QueryBuilder.stub | 4 ++-- .../Doctrine/data/QueryResult/createQuery.php | 2 +- .../data/QueryResult/queryBuilderGetQuery.php | 9 ++++++++- .../Doctrine/data/QueryResult/queryResult.php | 14 ++++++------- 14 files changed, 54 insertions(+), 31 deletions(-) create mode 100644 src/Type/Doctrine/Type/ListIndexMarkerType.php diff --git a/composer.json b/composer.json index 12c8f210..cb54847d 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ ], "require": { "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.8.11" + "phpstan/phpstan": "^1.9.0" }, "conflict": { "doctrine/collections": "<1.0", diff --git a/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php b/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php index a9167da9..680531ec 100644 --- a/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php @@ -24,7 +24,7 @@ use PHPStan\Type\UnionType; /** - * Infers TResult in Query on EntityManagerInterface::createQuery() + * Infers TResult and TIndex in Query on EntityManagerInterface::createQuery() */ final class CreateQueryDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -94,7 +94,7 @@ public function getTypeFromMethodCall( } return new GenericObjectType( Query::class, - [new MixedType()] + [new MixedType(), new MixedType()] ); }); } diff --git a/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php b/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php index 1a8e1b40..df16a1b6 100644 --- a/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php @@ -11,6 +11,7 @@ use PHPStan\Type\Accessory\AccessoryArrayListType; use PHPStan\Type\ArrayType; use PHPStan\Type\Constant\ConstantIntegerType; +use PHPStan\Type\Doctrine\Type\ListIndexMarkerType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Generic\GenericObjectType; use PHPStan\Type\IntegerType; @@ -142,7 +143,7 @@ private function getMethodReturnTypeForHydrationMode( $queryResultType ); default: - return $queryIndexType instanceof MixedType + return $queryIndexType instanceof ListIndexMarkerType ? AccessoryArrayListType::intersectWith( TypeCombinator::intersect( new ArrayType(new IntegerType(), $queryResultType), @@ -150,7 +151,7 @@ private function getMethodReturnTypeForHydrationMode( ) ) : new ArrayType( - $queryIndexType, + new MixedType(), $queryResultType ); } diff --git a/src/Type/Doctrine/Query/QueryResultTypeBuilder.php b/src/Type/Doctrine/Query/QueryResultTypeBuilder.php index 929c518a..cdea8505 100644 --- a/src/Type/Doctrine/Query/QueryResultTypeBuilder.php +++ b/src/Type/Doctrine/Query/QueryResultTypeBuilder.php @@ -5,6 +5,7 @@ use PHPStan\Type\Constant\ConstantArrayTypeBuilder; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\Constant\ConstantStringType; +use PHPStan\Type\Doctrine\Type\ListIndexMarkerType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\VoidType; @@ -62,10 +63,13 @@ final class QueryResultTypeBuilder */ private $newObjects = []; - /** - * @var Type|null - */ - private $indexedBy = null; + /** @var Type */ + private $indexedBy; + + public function __construct() + { + $this->indexedBy = new ListIndexMarkerType(); + } public function setSelectQuery(): void { @@ -235,12 +239,12 @@ private function resolveOffsetType($alias): Type return new ConstantStringType($alias); } - public function setIndexedBy(Type $type): void - { + public function setIndexedBy(Type $type): void + { $this->indexedBy = $type; - } + } - public function getIndexType(): ?Type + public function getIndexType(): Type { return $this->indexedBy; } diff --git a/src/Type/Doctrine/Query/QueryResultTypeWalker.php b/src/Type/Doctrine/Query/QueryResultTypeWalker.php index 06cc093d..6fef57ed 100644 --- a/src/Type/Doctrine/Query/QueryResultTypeWalker.php +++ b/src/Type/Doctrine/Query/QueryResultTypeWalker.php @@ -78,7 +78,7 @@ class QueryResultTypeWalker extends SqlWalker */ private $newObjectCounter = 0; - /** @var Query */ + /** @var Query */ private $query; /** @var EntityManagerInterface */ @@ -107,7 +107,7 @@ class QueryResultTypeWalker extends SqlWalker private $hasGroupByClause; /** - * @param Query $query + * @param Query $query */ public static function walk(Query $query, QueryResultTypeBuilder $typeBuilder, DescriptorRegistry $descriptorRegistry): void { @@ -122,7 +122,7 @@ public static function walk(Query $query, QueryResultTypeBuilder $typeBuilder, D /** * {@inheritDoc} * - * @param Query $query + * @param Query $query * @param ParserResult $parserResult * @param array $queryComponents */ diff --git a/src/Type/Doctrine/Query/QueryType.php b/src/Type/Doctrine/Query/QueryType.php index 2c40b263..5124fa4a 100644 --- a/src/Type/Doctrine/Query/QueryType.php +++ b/src/Type/Doctrine/Query/QueryType.php @@ -18,7 +18,8 @@ public function __construct( string $dql, ?Type $resultType, ?Type $indexType - ) { + ) + { parent::__construct('Doctrine\ORM\Query', [ $resultType ?? new MixedType(), $indexType ?? new MixedType(), diff --git a/src/Type/Doctrine/Type/ListIndexMarkerType.php b/src/Type/Doctrine/Type/ListIndexMarkerType.php new file mode 100644 index 00000000..d620b369 --- /dev/null +++ b/src/Type/Doctrine/Type/ListIndexMarkerType.php @@ -0,0 +1,10 @@ + */ diff --git a/stubs/ORM/QueryBuilder.stub b/stubs/ORM/QueryBuilder.stub index db14c5b2..0630a79a 100644 --- a/stubs/ORM/QueryBuilder.stub +++ b/stubs/ORM/QueryBuilder.stub @@ -15,7 +15,7 @@ class QueryBuilder } /** - * @return Query + * @return Query */ public function getQuery() { diff --git a/stubs/bleedingEdge/ORM/QueryBuilder.stub b/stubs/bleedingEdge/ORM/QueryBuilder.stub index 9fb03cff..aae683aa 100644 --- a/stubs/bleedingEdge/ORM/QueryBuilder.stub +++ b/stubs/bleedingEdge/ORM/QueryBuilder.stub @@ -8,7 +8,7 @@ class QueryBuilder { /** - * @return Query + * @return Query */ public function getQuery() { @@ -143,7 +143,7 @@ class QueryBuilder { } - + /** * @param literal-string|object|array $predicates * @return $this diff --git a/tests/Type/Doctrine/data/QueryResult/createQuery.php b/tests/Type/Doctrine/data/QueryResult/createQuery.php index 36c8ac7c..d4b33b0d 100644 --- a/tests/Type/Doctrine/data/QueryResult/createQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/createQuery.php @@ -29,7 +29,7 @@ public function testQueryResultTypeIsMixedWhenDQLIsNotKnown(EntityManagerInterfa { $query = $em->createQuery($dql); - assertType('Doctrine\ORM\Query', $query); // TODO fix + assertType('Doctrine\ORM\Query', $query); } public function testQueryResultTypeIsMixedWhenDQLIsInvalid(EntityManagerInterface $em, string $dql): void diff --git a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php index c22f430e..3679a44c 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php @@ -54,6 +54,13 @@ public function testIndexByInfering(EntityManagerInterface $em): void assertType('Doctrine\ORM\Query', $query); + $query = $em->createQueryBuilder() + ->select('m') + ->from(Many::class, 'm', 'm.stringColumn') + ->getQuery(); + + assertType('Doctrine\ORM\Query', $query); + $query = $em->createQueryBuilder() ->select(['m.intColumn', 'm.stringNullColumn']) ->from(Many::class, 'm') @@ -67,7 +74,7 @@ public function testQueryResultTypeIsMixedWhenDQLIsNotKnown(QueryBuilder $builde { $query = $builder->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } public function testQueryResultTypeIsMixedWhenDQLIsInvalid(EntityManagerInterface $em): void diff --git a/tests/Type/Doctrine/data/QueryResult/queryResult.php b/tests/Type/Doctrine/data/QueryResult/queryResult.php index 5f08d539..fbfc77e2 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryResult.php +++ b/tests/Type/Doctrine/data/QueryResult/queryResult.php @@ -231,7 +231,7 @@ public function testReturnTypeOfQueryMethodsWithExplicitNonConstantHydrationMode * Test that we return the original return type when ResultType may be * VoidType * - * @param Query $query + * @param Query $query */ public function testReturnTypeOfQueryMethodsWithReturnTypeIsMixed(EntityManagerInterface $em, Query $query): void { @@ -271,7 +271,7 @@ public function testReturnTypeOfQueryMethodsWithReturnTypeIsMixed(EntityManagerI * * @template T * - * @param Query $query + * @param Query $query */ public function testReturnTypeOfQueryMethodsWithReturnTypeIsTemplateMixedType(EntityManagerInterface $em, Query $query): void { @@ -311,12 +311,12 @@ public function testReturnTypeOfQueryMethodsWithReturnTypeIsTemplateMixedType(En * * @template T of array|object * - * @param Query $query + * @param Query $query */ public function testReturnTypeOfQueryMethodsWithReturnTypeIsNonVoidTemplate(EntityManagerInterface $em, Query $query): void { assertType( - 'list', + 'array', $query->getResult(AbstractQuery::HYDRATE_OBJECT) ); assertType( @@ -324,15 +324,15 @@ public function testReturnTypeOfQueryMethodsWithReturnTypeIsNonVoidTemplate(Enti $query->toIterable([], AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'list', + 'array', $query->execute(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'list', + 'array', $query->executeIgnoreQueryCache(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( - 'list', + 'array', $query->executeUsingQueryCache(null, AbstractQuery::HYDRATE_OBJECT) ); assertType( From dd0ee24600e1d69b6b49d82edc45991bb0185971 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Fri, 4 Nov 2022 15:09:07 +0100 Subject: [PATCH 3/7] testIndexByResultInfering --- .../data/QueryResult/queryBuilderGetQuery.php | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php index 3679a44c..44f6579f 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php @@ -70,6 +70,34 @@ public function testIndexByInfering(EntityManagerInterface $em): void assertType('Doctrine\ORM\Query', $query); } + public function testIndexByResultInfering(EntityManagerInterface $em): void + { + $result = $em->createQueryBuilder() + ->select('m') + ->from(Many::class, 'm', 'm.intColumn') + ->getQuery() + ->getResult(); + + assertType('array', $result); + + $result = $em->createQueryBuilder() + ->select('m') + ->from(Many::class, 'm', 'm.stringColumn') + ->getQuery() + ->getResult(); + + assertType('array', $result); + + $result = $em->createQueryBuilder() + ->select(['m.intColumn', 'm.stringNullColumn']) + ->from(Many::class, 'm') + ->indexBy('m', 'm.stringColumn') + ->getQuery() + ->getResult(); + + assertType('array', $result); + } + public function testQueryResultTypeIsMixedWhenDQLIsNotKnown(QueryBuilder $builder): void { $query = $builder->getQuery(); From 5032a40a2cb63cb88d0a7c79fef8a37e66820b8f Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 14 Nov 2022 13:20:15 +0100 Subject: [PATCH 4/7] ListIndexMarkerType extends IntegerType --- src/Type/Doctrine/Type/ListIndexMarkerType.php | 4 ++-- .../Doctrine/data/QueryResult/queryBuilderGetQuery.php | 10 +++++----- tests/Type/Doctrine/data/QueryResult/queryResult.php | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Type/Doctrine/Type/ListIndexMarkerType.php b/src/Type/Doctrine/Type/ListIndexMarkerType.php index d620b369..f353a9ec 100644 --- a/src/Type/Doctrine/Type/ListIndexMarkerType.php +++ b/src/Type/Doctrine/Type/ListIndexMarkerType.php @@ -2,9 +2,9 @@ namespace PHPStan\Type\Doctrine\Type; -use PHPStan\Type\MixedType; +use PHPStan\Type\IntegerType; -class ListIndexMarkerType extends MixedType +class ListIndexMarkerType extends IntegerType { } diff --git a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php index 44f6579f..f96807f3 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php @@ -35,14 +35,14 @@ public function testQueryTypeParametersAreInfered(EntityManagerInterface $em): v ->from(Many::class, 'm') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->createQueryBuilder() ->select(['m.intColumn', 'm.stringNullColumn']) ->from(Many::class, 'm') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } public function testIndexByInfering(EntityManagerInterface $em): void @@ -124,7 +124,7 @@ public function testQueryResultTypeIsVoidWithDeleteOrUpdate(EntityManagerInterfa ->delete() ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->getRepository(Many::class) ->createQueryBuilder('m') @@ -134,7 +134,7 @@ public function testQueryResultTypeIsVoidWithDeleteOrUpdate(EntityManagerInterfa ->set('m.intColumn', '42') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } @@ -143,7 +143,7 @@ public function testQueryTypeIsInferredOnAcrossMethods(EntityManagerInterface $e $query = $this->getQueryBuilder($em) ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } private function getQueryBuilder(EntityManagerInterface $em): QueryBuilder diff --git a/tests/Type/Doctrine/data/QueryResult/queryResult.php b/tests/Type/Doctrine/data/QueryResult/queryResult.php index fbfc77e2..784ae1e4 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryResult.php +++ b/tests/Type/Doctrine/data/QueryResult/queryResult.php @@ -16,14 +16,14 @@ public function testQueryTypeParametersAreInfered(EntityManagerInterface $em): v FROM QueryResult\Entities\Many m '); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->createQuery(' SELECT m.intColumn, m.stringNullColumn FROM QueryResult\Entities\Many m '); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } /** From 2c59428958f57b0cd80adffb1a59fa45c5523f53 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 14 Nov 2022 14:10:59 +0100 Subject: [PATCH 5/7] Fix CreateQueryDynamicReturnTypeExtensionTest --- tests/Type/Doctrine/data/QueryResult/createQuery.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Type/Doctrine/data/QueryResult/createQuery.php b/tests/Type/Doctrine/data/QueryResult/createQuery.php index d4b33b0d..2195ebec 100644 --- a/tests/Type/Doctrine/data/QueryResult/createQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/createQuery.php @@ -15,14 +15,14 @@ public function testQueryTypeParametersAreInfered(EntityManagerInterface $em): v FROM QueryResult\Entities\Many m '); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->createQuery(' SELECT m.intColumn, m.stringNullColumn FROM QueryResult\Entities\Many m '); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } public function testQueryResultTypeIsMixedWhenDQLIsNotKnown(EntityManagerInterface $em, string $dql): void From b6c3bc7d5e6f05a03907a6b801c9b86b3fcf6b11 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 14 Nov 2022 14:43:20 +0100 Subject: [PATCH 6/7] Swap TKey and TResult --- .../CreateQueryDynamicReturnTypeExtension.php | 6 +++--- .../QueryResultDynamicReturnTypeExtension.php | 6 +++--- src/Type/Doctrine/Query/QueryType.php | 6 +++--- ...BuilderGetQueryDynamicReturnTypeExtension.php | 2 +- stubs/ORM/AbstractQuery.stub | 2 +- stubs/ORM/Query.stub | 4 ++-- .../Doctrine/data/QueryResult/createQuery.php | 4 ++-- .../data/QueryResult/queryBuilderGetQuery.php | 16 ++++++++-------- .../Doctrine/data/QueryResult/queryResult.php | 8 ++++---- 9 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php b/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php index 680531ec..dfaecda4 100644 --- a/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/CreateQueryDynamicReturnTypeExtension.php @@ -24,7 +24,7 @@ use PHPStan\Type\UnionType; /** - * Infers TResult and TIndex in Query on EntityManagerInterface::createQuery() + * Infers TResult and TKey in Query on EntityManagerInterface::createQuery() */ final class CreateQueryDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -63,7 +63,7 @@ public function getTypeFromMethodCall( if (!isset($args[$queryStringArgIndex])) { return new GenericObjectType( Query::class, - [new MixedType()] + [new MixedType(), new MixedType()] ); } @@ -90,7 +90,7 @@ public function getTypeFromMethodCall( return new QueryType($queryString, null, null); } - return new QueryType($queryString, $typeBuilder->getResultType(), $typeBuilder->getIndexType()); + return new QueryType($queryString, $typeBuilder->getIndexType(), $typeBuilder->getResultType()); } return new GenericObjectType( Query::class, diff --git a/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php b/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php index df16a1b6..657e9bb8 100644 --- a/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php @@ -91,7 +91,7 @@ private function getQueryIndexType(Type $queryType): Type $types = $queryType->getTypes(); - return $types[1] ?? new MixedType(); + return $types[0] ?? new MixedType(); } private function getQueryResultType(Type $queryType): Type @@ -102,14 +102,14 @@ private function getQueryResultType(Type $queryType): Type $types = $queryType->getTypes(); - return $types[0] ?? new MixedType(); + return $types[1] ?? new MixedType(); } private function getMethodReturnTypeForHydrationMode( MethodReflection $methodReflection, Type $hydrationMode, Type $queryResultType, - ?Type $queryIndexType + Type $queryIndexType ): Type { $isVoidType = (new VoidType())->isSuperTypeOf($queryResultType); diff --git a/src/Type/Doctrine/Query/QueryType.php b/src/Type/Doctrine/Query/QueryType.php index 5124fa4a..54a58e08 100644 --- a/src/Type/Doctrine/Query/QueryType.php +++ b/src/Type/Doctrine/Query/QueryType.php @@ -16,13 +16,13 @@ class QueryType extends GenericObjectType public function __construct( string $dql, - ?Type $resultType, - ?Type $indexType + ?Type $indexType, + ?Type $resultType ) { parent::__construct('Doctrine\ORM\Query', [ - $resultType ?? new MixedType(), $indexType ?? new MixedType(), + $resultType ?? new MixedType(), ]); $this->dql = $dql; } diff --git a/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php b/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php index 5b2fde24..788f72e0 100644 --- a/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/QueryBuilder/QueryBuilderGetQueryDynamicReturnTypeExtension.php @@ -165,7 +165,7 @@ private function getQueryType(string $dql): Type return new QueryType($dql, null, null); } - return new QueryType($dql, $typeBuilder->getResultType(), $typeBuilder->getIndexType()); + return new QueryType($dql, $typeBuilder->getIndexType(), $typeBuilder->getResultType()); } } diff --git a/stubs/ORM/AbstractQuery.stub b/stubs/ORM/AbstractQuery.stub index 638ce77d..60102d4d 100644 --- a/stubs/ORM/AbstractQuery.stub +++ b/stubs/ORM/AbstractQuery.stub @@ -5,8 +5,8 @@ namespace Doctrine\ORM; use Doctrine\Common\Collections\ArrayCollection; /** + * @template TKey The type of column used in indexBy * @template TResult The type of results returned by this query in HYDRATE_OBJECT mode - * @template TIndex The type of column used in indexBy, mixed if no indexBy is used */ abstract class AbstractQuery { diff --git a/stubs/ORM/Query.stub b/stubs/ORM/Query.stub index 040134f5..1ca1635f 100644 --- a/stubs/ORM/Query.stub +++ b/stubs/ORM/Query.stub @@ -3,10 +3,10 @@ namespace Doctrine\ORM; /** + * @template TKey The type of column used in indexBy * @template TResult The type of results returned by this query in HYDRATE_OBJECT mode - * @template TIndex The type of column used in indexBy, mixed if no indexBy is used * - * @extends AbstractQuery + * @extends AbstractQuery */ final class Query extends AbstractQuery { diff --git a/tests/Type/Doctrine/data/QueryResult/createQuery.php b/tests/Type/Doctrine/data/QueryResult/createQuery.php index 2195ebec..204bf329 100644 --- a/tests/Type/Doctrine/data/QueryResult/createQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/createQuery.php @@ -15,14 +15,14 @@ public function testQueryTypeParametersAreInfered(EntityManagerInterface $em): v FROM QueryResult\Entities\Many m '); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->createQuery(' SELECT m.intColumn, m.stringNullColumn FROM QueryResult\Entities\Many m '); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } public function testQueryResultTypeIsMixedWhenDQLIsNotKnown(EntityManagerInterface $em, string $dql): void diff --git a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php index f96807f3..b476160f 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php @@ -35,14 +35,14 @@ public function testQueryTypeParametersAreInfered(EntityManagerInterface $em): v ->from(Many::class, 'm') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->createQueryBuilder() ->select(['m.intColumn', 'm.stringNullColumn']) ->from(Many::class, 'm') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } public function testIndexByInfering(EntityManagerInterface $em): void @@ -52,14 +52,14 @@ public function testIndexByInfering(EntityManagerInterface $em): void ->from(Many::class, 'm', 'm.intColumn') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->createQueryBuilder() ->select('m') ->from(Many::class, 'm', 'm.stringColumn') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->createQueryBuilder() ->select(['m.intColumn', 'm.stringNullColumn']) @@ -67,7 +67,7 @@ public function testIndexByInfering(EntityManagerInterface $em): void ->indexBy('m', 'm.stringColumn') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } public function testIndexByResultInfering(EntityManagerInterface $em): void @@ -124,7 +124,7 @@ public function testQueryResultTypeIsVoidWithDeleteOrUpdate(EntityManagerInterfa ->delete() ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->getRepository(Many::class) ->createQueryBuilder('m') @@ -134,7 +134,7 @@ public function testQueryResultTypeIsVoidWithDeleteOrUpdate(EntityManagerInterfa ->set('m.intColumn', '42') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } @@ -143,7 +143,7 @@ public function testQueryTypeIsInferredOnAcrossMethods(EntityManagerInterface $e $query = $this->getQueryBuilder($em) ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } private function getQueryBuilder(EntityManagerInterface $em): QueryBuilder diff --git a/tests/Type/Doctrine/data/QueryResult/queryResult.php b/tests/Type/Doctrine/data/QueryResult/queryResult.php index 784ae1e4..64384b2c 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryResult.php +++ b/tests/Type/Doctrine/data/QueryResult/queryResult.php @@ -16,14 +16,14 @@ public function testQueryTypeParametersAreInfered(EntityManagerInterface $em): v FROM QueryResult\Entities\Many m '); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->createQuery(' SELECT m.intColumn, m.stringNullColumn FROM QueryResult\Entities\Many m '); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); } /** @@ -271,7 +271,7 @@ public function testReturnTypeOfQueryMethodsWithReturnTypeIsMixed(EntityManagerI * * @template T * - * @param Query $query + * @param Query $query */ public function testReturnTypeOfQueryMethodsWithReturnTypeIsTemplateMixedType(EntityManagerInterface $em, Query $query): void { @@ -311,7 +311,7 @@ public function testReturnTypeOfQueryMethodsWithReturnTypeIsTemplateMixedType(En * * @template T of array|object * - * @param Query $query + * @param Query $query */ public function testReturnTypeOfQueryMethodsWithReturnTypeIsNonVoidTemplate(EntityManagerInterface $em, Query $query): void { From 5d787bf1ae0de010580ac705198498783202b13b Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 14 Nov 2022 14:48:15 +0100 Subject: [PATCH 7/7] Also TKey is VoidType for non-select queries --- src/Type/Doctrine/Query/QueryResultTypeBuilder.php | 4 ++++ tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Type/Doctrine/Query/QueryResultTypeBuilder.php b/src/Type/Doctrine/Query/QueryResultTypeBuilder.php index cdea8505..7a5d657b 100644 --- a/src/Type/Doctrine/Query/QueryResultTypeBuilder.php +++ b/src/Type/Doctrine/Query/QueryResultTypeBuilder.php @@ -246,6 +246,10 @@ public function setIndexedBy(Type $type): void public function getIndexType(): Type { + if (!$this->selectQuery) { + return new VoidType(); + } + return $this->indexedBy; } diff --git a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php index b476160f..5c384fd7 100644 --- a/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php +++ b/tests/Type/Doctrine/data/QueryResult/queryBuilderGetQuery.php @@ -124,7 +124,7 @@ public function testQueryResultTypeIsVoidWithDeleteOrUpdate(EntityManagerInterfa ->delete() ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); $query = $em->getRepository(Many::class) ->createQueryBuilder('m') @@ -134,7 +134,7 @@ public function testQueryResultTypeIsVoidWithDeleteOrUpdate(EntityManagerInterfa ->set('m.intColumn', '42') ->getQuery(); - assertType('Doctrine\ORM\Query', $query); + assertType('Doctrine\ORM\Query', $query); }