diff --git a/src/Type/Doctrine/Query/QueryResultTypeWalker.php b/src/Type/Doctrine/Query/QueryResultTypeWalker.php index d82dc2b6..4cbf4a2e 100644 --- a/src/Type/Doctrine/Query/QueryResultTypeWalker.php +++ b/src/Type/Doctrine/Query/QueryResultTypeWalker.php @@ -506,6 +506,55 @@ public function walkFunction($function) return $this->marshalType($type); + case $function instanceof AST\Functions\IdentityFunction: + $dqlAlias = $function->pathExpression->identificationVariable; + $assocField = $function->pathExpression->field; + $queryComp = $this->queryComponents[$dqlAlias]; + $class = $queryComp['metadata']; + $assoc = $class->associationMappings[$assocField]; + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + if ($function->fieldMapping === null) { + $identifierFieldNames = $targetClass->getIdentifierFieldNames(); + if (count($identifierFieldNames) === 0) { + throw new ShouldNotHappenException(); + } + + $targetFieldName = $identifierFieldNames[0]; + } else { + $targetFieldName = $function->fieldMapping; + } + + $typeName = $targetClass->getTypeOfField($targetFieldName); + if ($typeName === null) { + return $this->marshalType(new MixedType()); + } + + $fieldMapping = $targetClass->fieldMappings[$targetFieldName] ?? null; + if ($fieldMapping === null) { + return $this->marshalType(new MixedType()); + } + + $joinColumn = null; + + foreach ($assoc['joinColumns'] as $item) { + if ($item['referencedColumnName'] === $fieldMapping['columnName']) { + $joinColumn = $item; + break; + } + } + + if ($joinColumn === null) { + return $this->marshalType(new MixedType()); + } + + $nullable = (bool) ($joinColumn['nullable'] ?? true) + || $this->hasAggregateWithoutGroupBy(); + + $fieldType = $this->resolveDatabaseInternalType($typeName, $nullable); + + return $this->marshalType($fieldType); + default: return $this->marshalType(new MixedType()); } diff --git a/tests/Type/Doctrine/Query/QueryResultTypeWalkerTest.php b/tests/Type/Doctrine/Query/QueryResultTypeWalkerTest.php index ed801ed9..cc03fa5b 100644 --- a/tests/Type/Doctrine/Query/QueryResultTypeWalkerTest.php +++ b/tests/Type/Doctrine/Query/QueryResultTypeWalkerTest.php @@ -1152,6 +1152,29 @@ public function getTestData(): array FROM QueryResult\Entities\Many m ', ], + 'identity function' => [ + $this->constantArray([ + [new ConstantIntegerType(1), TypeCombinator::addNull($this->numericStringOrInt())], + [new ConstantIntegerType(2), $this->numericStringOrInt()], + [new ConstantIntegerType(3), TypeCombinator::addNull($this->numericStringOrInt())], + [new ConstantIntegerType(4), TypeCombinator::addNull(new StringType())], + [new ConstantIntegerType(5), TypeCombinator::addNull(new StringType())], + [new ConstantIntegerType(6), TypeCombinator::addNull($this->numericStringOrInt())], + [new ConstantIntegerType(7), TypeCombinator::addNull(new MixedType())], + [new ConstantIntegerType(8), TypeCombinator::addNull($this->numericStringOrInt())], + ]), + ' + SELECT IDENTITY(m.oneNull), + IDENTITY(m.one), + IDENTITY(m.oneDefaultNullability), + IDENTITY(m.compoundPk), + IDENTITY(m.compoundPk, \'id\'), + IDENTITY(m.compoundPk, \'version\'), + IDENTITY(m.compoundPkAssoc), + IDENTITY(m.compoundPkAssoc, \'version\') + FROM QueryResult\Entities\Many m + ', + ], 'select nullable association' => [ $this->constantArray([ [new ConstantIntegerType(1), TypeCombinator::addNull($this->numericStringOrInt())], diff --git a/tests/Type/Doctrine/data/QueryResult/Entities/CompoundPk.php b/tests/Type/Doctrine/data/QueryResult/Entities/CompoundPk.php new file mode 100644 index 00000000..4b826a5c --- /dev/null +++ b/tests/Type/Doctrine/data/QueryResult/Entities/CompoundPk.php @@ -0,0 +1,35 @@ +