Skip to content

Commit

Permalink
feat: add new enum type (PHP 8.1)
Browse files Browse the repository at this point in the history
Related to issue doctrine/orm#9021
Related to PR ORM doctrine/orm#9149
  • Loading branch information
Nek- committed Oct 25, 2021
1 parent 8d66218 commit a3d32b7
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 0 deletions.
65 changes: 65 additions & 0 deletions src/Types/EnumType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Doctrine\DBAL\Types;

use Doctrine\DBAL\Platforms\AbstractPlatform;

final class EnumType extends Type
{
public function getSQLDeclaration(array $column, AbstractPlatform $platform)
{
return $platform->getVarcharTypeDeclarationSQL($column);
}

/**
* {@inheritdoc}
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if ($value === null) {
return null;
}

if ($value instanceof \UnitEnum) {
return serialize($value);
}

throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'UnitEnum']);
}

/**
* {@inheritdoc}
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
if ($value === null) {
return null;
}

set_error_handler(function (int $code, string $message): bool {
throw ConversionException::conversionFailedUnserialization($this->getName(), $message);
});

try {
return unserialize($value);
} finally {
restore_error_handler();
}
}

/**
* {@inheritdoc}
*/
public function getName()
{
return Types::ENUM;
}

/**
* {@inheritdoc}
*/
public function requiresSQLCommentHint(AbstractPlatform $platform)
{
return true;
}
}
1 change: 1 addition & 0 deletions src/Types/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ abstract class Type
Types::DATETIMETZ_MUTABLE => DateTimeTzType::class,
Types::DATETIMETZ_IMMUTABLE => DateTimeTzImmutableType::class,
Types::DECIMAL => DecimalType::class,
Types::ENUM => EnumType::class,
Types::FLOAT => FloatType::class,
Types::GUID => GuidType::class,
Types::INTEGER => IntegerType::class,
Expand Down
1 change: 1 addition & 0 deletions src/Types/Types.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ final class Types
public const DATETIMETZ_MUTABLE = 'datetimetz';
public const DATETIMETZ_IMMUTABLE = 'datetimetz_immutable';
public const DECIMAL = 'decimal';
public const ENUM = 'enum';
public const FLOAT = 'float';
public const GUID = 'guid';
public const INTEGER = 'integer';
Expand Down
34 changes: 34 additions & 0 deletions tests/Functional/Types/EnumTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Doctrine\DBAL\Tests\Functional\Types;

use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Tests\FunctionalTestCase;
use Doctrine\DBAL\Tests\Tools\TestAsset\SimpleEnum;
use Doctrine\DBAL\Types\Types;

/**
* @requires PHP >= 8.1
*/
class EnumTest extends FunctionalTestCase
{
protected function setUp(): void
{
$table = new Table('enum_table');
$table->addColumn('enum', 'enum');

$this->connection->createSchemaManager()->dropAndCreateTable($table);
}

public function testInsertAndSelect(): void
{
$draft = SimpleEnum::DRAFT;

$result = $this->connection->insert('enum_table', ['enum' => $draft], [Types::ENUM]);
self::assertSame(1, $result);

$value = $this->connection->fetchOne('SELECT enum FROM enum_table');

self::assertSame($draft, $this->connection->convertToPHPValue($value, Types::ENUM));
}
}
10 changes: 10 additions & 0 deletions tests/Tools/TestAsset/SimpleEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Doctrine\DBAL\Tests\Tools\TestAsset;

enum SimpleEnum
{
case DRAFT;
case PUBLISHED;
case UNPUBLISHED;
}
62 changes: 62 additions & 0 deletions tests/Types/EnumTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace Doctrine\DBAL\Tests\Types;

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\EnumType;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Doctrine\DBAL\Tests\Tools\TestAsset\SimpleEnum;

/**
* @requires PHP >= 8.1
*/
class EnumTest extends TestCase
{
/** @var AbstractPlatform&MockObject */
private $platform;

/** @var EnumType */
private $type;

protected function setUp(): void
{
$this->platform = $this->createMock(AbstractPlatform::class);
$this->type = new EnumType();
}

public function testObjectConvertsToDatabaseValue(): void
{
self::assertIsString($this->type->convertToDatabaseValue(SimpleEnum::DRAFT, $this->platform));
}

public function testObjectConvertsToPHPValue(): void
{
self::assertTrue(is_a($this->type->convertToPHPValue(serialize(SimpleEnum::DRAFT), $this->platform), SimpleEnum::class));
}

public function testConversionToDatabaseValueFailure(): void
{
$this->expectException(ConversionException::class);
$this->expectExceptionMessage(
"Could not convert PHP value 'abcdefg' to type enum. Expected one of the following types: null, UnitEnum"
);
$this->type->convertToDatabaseValue('abcdefg', $this->platform);
}

public function testConversionToPHPValueFailure(): void
{
$this->expectException(ConversionException::class);
$this->expectExceptionMessage(
"Could not convert database value to 'enum' as an error was triggered by the unserialization:"
. " 'unserialize(): Error at offset 0 of 7 bytes'"
);
$this->type->convertToPHPValue('abcdefg', $this->platform);
}

public function testNullConversion(): void
{
self::assertNull($this->type->convertToPHPValue(null, $this->platform));
}
}

0 comments on commit a3d32b7

Please sign in to comment.