Skip to content

Commit

Permalink
Ensure consistent original data with enums
Browse files Browse the repository at this point in the history
  • Loading branch information
HypeMC committed Oct 1, 2022
1 parent 757d950 commit 8022b52
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 10 deletions.
2 changes: 1 addition & 1 deletion lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php
Expand Up @@ -697,7 +697,7 @@ protected function registerManaged(ClassMetadata $class, $entity, array $data)
*
* @return BackedEnum|array<BackedEnum>
*/
private function buildEnum($value, string $enumType)
protected function buildEnum($value, string $enumType)
{
if (is_array($value)) {
return array_map(static function ($value) use ($enumType): BackedEnum {
Expand Down
4 changes: 4 additions & 0 deletions lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php
Expand Up @@ -140,6 +140,10 @@ protected function hydrateRowData(array $row, array &$result)
$value = $type->convertToPHPValue($value, $this->_platform);
}

if ($value !== null && isset($cacheKeyInfo['enumType'])) {
$value = $this->buildEnum($value, $cacheKeyInfo['enumType']);
}

$fieldName = $cacheKeyInfo['fieldName'];

// Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
Expand Down
3 changes: 3 additions & 0 deletions lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
Expand Up @@ -1506,6 +1506,9 @@ protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r'
$columnAlias = $this->getSQLColumnAlias($fieldMapping['columnName']);

$this->currentPersisterContext->rsm->addFieldResult($alias, $columnAlias, $field);
if (! empty($fieldMapping['enumType'])) {
$this->currentPersisterContext->rsm->addEnumResult($columnAlias, $fieldMapping['enumType']);
}

if (isset($fieldMapping['requireSQLConversion'])) {
$type = Type::getType($fieldMapping['type']);
Expand Down
15 changes: 6 additions & 9 deletions tests/Doctrine/Tests/ORM/Functional/EnumTest.php
Expand Up @@ -7,7 +7,6 @@
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\ORM\Query\Expr\Func;
use Doctrine\ORM\Tools\SchemaTool;
use Doctrine\Tests\Models\DataTransferObjects\DtoWithArrayOfEnums;
Expand All @@ -22,11 +21,14 @@
use Doctrine\Tests\Models\Enums\TypedCard;
use Doctrine\Tests\Models\Enums\Unit;
use Doctrine\Tests\OrmFunctionalTestCase;
use ValueError;

use function dirname;
use function sprintf;
use function uniqid;

use const PHP_VERSION_ID;

/**
* @requires PHP 8.1
*/
Expand Down Expand Up @@ -307,15 +309,10 @@ public function testEnumWithNonMatchingDatabaseValueThrowsException(string $card
[$metadata->fieldMappings['id']['columnName'] => $card->id]
);

$this->expectException(MappingException::class);
$this->expectException(ValueError::class);
$this->expectExceptionMessage(sprintf(
<<<'EXCEPTION'
Context: Trying to hydrate enum property "%s::$suit"
Problem: Case "invalid" is not listed in enum "Doctrine\Tests\Models\Enums\Suit"
Solution: Either add the case to the enum type or migrate the database column to use another case of the enum
EXCEPTION
,
$cardClass
'"invalid" is not a valid backing value for enum ' . (PHP_VERSION_ID < 80200 ? '"%s"' : '%s'),
Suit::class
));

$this->_em->find($cardClass, $card->id);
Expand Down
27 changes: 27 additions & 0 deletions tests/Doctrine/Tests/ORM/Hydration/SimpleObjectHydratorTest.php
Expand Up @@ -13,6 +13,8 @@
use Doctrine\Tests\Mocks\ArrayResultFactory;
use Doctrine\Tests\Models\CMS\CmsAddress;
use Doctrine\Tests\Models\Company\CompanyPerson;
use Doctrine\Tests\Models\Enums\Card;
use Doctrine\Tests\Models\Enums\Suit;
use Doctrine\Tests\Models\GH8565\GH8565Employee;
use Doctrine\Tests\Models\GH8565\GH8565Manager;
use Doctrine\Tests\Models\GH8565\GH8565Person;
Expand Down Expand Up @@ -155,4 +157,29 @@ public function testWrongValuesShouldNotBeConvertedToPhpValue(): void
$result = $hydrator->hydrateAll($stmt, $rsm);
self::assertEquals($result[0], $expectedEntity);
}

/**
* @requires PHP 8.1
*/
public function testEnumsAreBuilt(): void
{
$rsm = new ResultSetMapping();
$rsm->addEntityResult(Card::class, 'r');
$rsm->addFieldResult('r', 'id_1', 'id');
$rsm->addFieldResult('r', 'suit_2', 'suit');
$rsm->addEnumResult('suit_2', Suit::class);
$resultSet = [
[
'id_1' => 1,
'suit_2' => 'C',
],
];

$stmt = ArrayResultFactory::createFromArray($resultSet);
$hydrator = new SimpleObjectHydrator($this->entityManager);
$result = $hydrator->hydrateAll($stmt, $rsm)[0];

self::assertEquals(Suit::Clubs, $result->suit);
self::assertEquals(Suit::Clubs, $this->entityManager->getUnitOfWork()->getOriginalEntityData($result)['suit']);
}
}
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Persisters;

use Doctrine\ORM\Persisters\Entity\BasicEntityPersister;
use Doctrine\Tests\Mocks\EntityManagerMock;
use Doctrine\Tests\Models\Enums\Card;
use Doctrine\Tests\Models\Enums\Suit;
use Doctrine\Tests\OrmTestCase;

class BasicEntityPersisterResultMappingTest extends OrmTestCase
{
/** @var BasicEntityPersister */
protected $persister;

/** @var EntityManagerMock */
protected $entityManager;

protected function setUp(): void
{
parent::setUp();

$this->entityManager = $this->getTestEntityManager();
$this->persister = new BasicEntityPersister($this->entityManager, $this->entityManager->getClassMetadata(Card::class));
}

/**
* @requires PHP 8.1
*/
public function testEnumTypeIsAddedToResultMapping(): void
{
$statement = $this->persister->getSelectSQL([]);
self::assertEquals('SELECT t0.id AS id_1, t0.suit AS suit_2 FROM Card t0', $statement);
self::assertEquals(['suit_2' => Suit::class], $this->persister->getResultSetMapping()->enumMappings);
}
}

0 comments on commit 8022b52

Please sign in to comment.