-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,100 +10,124 @@ | |
use Doctrine\DBAL\Platforms\SQLServerPlatform; | ||
use Doctrine\DBAL\Types\Exception\InvalidType; | ||
use Doctrine\DBAL\Types\Exception\ValueNotConvertible; | ||
use ReflectionClass; | ||
use Throwable; | ||
|
||
use function array_map; | ||
use function class_exists; | ||
use function enum_exists; | ||
use function implode; | ||
use function is_string; | ||
use function sprintf; | ||
|
||
final class EnumType extends Type | ||
{ | ||
public string $name = 'enum'; | ||
|
||
public ?string $enumClassname = null; | ||
|
||
/** @var array<int, string> */ | ||
public array $members = []; | ||
|
||
/** | ||
* Gets an array of database types that map to this Doctrine type. | ||
* | ||
* @return string[] | ||
* {@inheritDoc} | ||
*/ | ||
public function getMappedDatabaseTypes(AbstractPlatform $platform): array | ||
{ | ||
return [$this->name]; | ||
} | ||
|
||
/** | ||
* Gets the SQL declaration snippet for a field of this type. | ||
* | ||
* @param mixed[] $column The field declaration | ||
* @param AbstractPlatform $platform The currently used database platform | ||
* {@inheritDoc} | ||
*/ | ||
public function getSqlDeclaration(array $column, AbstractPlatform $platform): string | ||
{ | ||
assert($column['type'] instanceof self::class); | ||
|
||
$values = implode( | ||
', ', | ||
array_map( | ||
static fn (string $value) => "'{$value}'", | ||
$column['members'] ?: $column['type']->members | ||
) | ||
static fn (string $value) => sprintf('\'%s\'', $value), | ||
$column['members'] ?: $column['type']->members, | ||
), | ||
); | ||
|
||
$sqlDeclaration = match (true) { | ||
return match (true) { | ||
$platform instanceof SqlitePlatform => sprintf('TEXT CHECK(%s IN (%s))', $column['name'], $values), | ||
Check failure on line 54 in src/Types/EnumType.php GitHub Actions / Static Analysis with Psalm (8.3)InvalidClass
|
||
$platform instanceof PostgreSqlPlatform, $platform instanceof SQLServerPlatform => sprintf('VARCHAR(255) CHECK(%s IN (%s))', $column['name'], $values), | ||
Check warning on line 55 in src/Types/EnumType.php GitHub Actions / Coding Standards / Coding Standards (8.3)
Check failure on line 55 in src/Types/EnumType.php GitHub Actions / Static Analysis with Psalm (8.3)InvalidClass
|
||
default => sprintf('ENUM(%s)', $values), | ||
}; | ||
|
||
return $sqlDeclaration; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
* | ||
* @return mixed the database representation of the value | ||
* | ||
* @throws \InvalidArgumentException | ||
* {@inheritDoc} | ||
*/ | ||
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string | ||
{ | ||
if (null === $value) { | ||
if ($value === null) { | ||
return null; | ||
} | ||
|
||
if ($value instanceof \UnitEnum) { | ||
return $value->value; | ||
} | ||
|
||
return (string) $value; | ||
} | ||
|
||
/** | ||
Check failure on line 76 in src/Types/EnumType.php GitHub Actions / Coding Standards / Coding Standards (8.3)
|
||
* {@inheritdoc} | ||
* | ||
* @return mixed the PHP representation of the value | ||
* {@inheritDoc} | ||
*/ | ||
public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed | ||
{ | ||
if (null === $value) { | ||
if ($value === null) { | ||
return null; | ||
} | ||
|
||
if (!\is_string($value)) { | ||
if (! is_string($value)) { | ||
throw InvalidType::new($value, $this->name, ['null', 'string']); | ||
} | ||
|
||
$refl = new \ReflectionClass($this->enumClassname); | ||
|
||
try { | ||
return $refl->newInstance($value); | ||
} catch (\Throwable $e) { | ||
throw ValueNotConvertible::new($value, $this->name, $e->getMessage(), $e); | ||
if ($this->enumClassname) { | ||
Check failure on line 89 in src/Types/EnumType.php GitHub Actions / Static Analysis with Psalm (8.3)RiskyTruthyFalsyComparison
|
||
if (enum_exists($this->enumClassname)) { | ||
try { | ||
//var_dump($this->enumClassname::cases());die(); | ||
/*$refl = new \ReflectionEnum($this->enumClassname); | ||
foreach ($refl->getCases() as $case) { | ||
if ($value === $case->getValue()->value) { | ||
return $case->getValue(); | ||
} | ||
}*/ | ||
|
||
foreach ($this->enumClassname::cases() as $case) { | ||
if ($value === $case->value) { | ||
return $case; | ||
} | ||
} | ||
|
||
throw new \ValueError(sprintf("'%s' is not a valid backing value for enum %s", $value, $this->enumClassname)); | ||
Check failure on line 106 in src/Types/EnumType.php GitHub Actions / Coding Standards / Coding Standards (8.3)
|
||
} catch (Throwable $e) { | ||
throw ValueNotConvertible::new($value, $this->name, $e->getMessage(), $e); | ||
} | ||
} | ||
|
||
if (class_exists($this->enumClassname)) { | ||
$refl = new ReflectionClass($this->enumClassname); | ||
|
||
try { | ||
return $refl->newInstance($value); | ||
} catch (Throwable $e) { | ||
throw ValueNotConvertible::new($value, $this->name, $e->getMessage(), $e); | ||
} | ||
} | ||
} | ||
|
||
return $value; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public static function addType(string $name, string $enumClassname): void | ||
Check failure on line 126 in src/Types/EnumType.php GitHub Actions / Static Analysis with Psalm (8.3)ParamNameMismatch
|
||
{ | ||
self::getTypeRegistry()->register($name, $me = new self()); | ||
$me->name = $name; | ||
$me->name = $name; | ||
$me->enumClassname = $enumClassname; | ||
$me->members = $enumClassname::getAllowedValues(); | ||
$me->members = $enumClassname::getAllowedValues(); | ||
Check failure on line 131 in src/Types/EnumType.php GitHub Actions / Static Analysis with Psalm (8.3)UndefinedMethod
Check failure on line 131 in src/Types/EnumType.php GitHub Actions / Static Analysis with PHPStan (8.3)
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\DBAL\Tests\Functional\Types; | ||
|
||
use Doctrine\DBAL\Schema\Table; | ||
use Doctrine\DBAL\Tests\FunctionalTestCase; | ||
use Doctrine\DBAL\Types\Types; | ||
|
||
class EnumTest extends FunctionalTestCase | ||
{ | ||
protected function setUp(): void | ||
{ | ||
$table = new Table('enum_table'); | ||
$table->addColumn('val', Types::ENUM, ['members' => ['a', 'b']]); | ||
|
||
$this->dropAndCreateTable($table); | ||
} | ||
|
||
public function testInsertAndSelect(): void | ||
{ | ||
$val = 'b'; | ||
|
||
$result = $this->connection->insert('enum_table', ['val' => $val]); | ||
self::assertSame(1, $result); | ||
|
||
$value = $this->connection->fetchOne('SELECT val FROM enum_table'); | ||
|
||
self::assertEquals($val, $value); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<?php | ||
Check warning on line 1 in tests/Types/EnumTest.php GitHub Actions / Coding Standards / Coding Standards (8.3)
|
||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\DBAL\Tests\Types; | ||
|
||
use Doctrine\DBAL\Platforms\AbstractPlatform; | ||
use Doctrine\DBAL\Types\EnumType; | ||
use PHPUnit\Framework\MockObject\MockObject; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
use function anonymous_class; | ||
|
||
class EnumTest extends TestCase | ||
{ | ||
private AbstractPlatform&MockObject $platform; | ||
private EnumType $type; | ||
|
||
protected function setUp(): void | ||
{ | ||
$this->platform = $this->createMock(AbstractPlatform::class); | ||
$this->type = new EnumType(); | ||
} | ||
|
||
public function testReturnsSQLDeclaration(): void | ||
{ | ||
self::assertSame('ENUM(\'a\', \'b\')', $this->type->getSQLDeclaration(['members' => ['a', 'b']], $this->platform)); | ||
} | ||
|
||
public function testConvertToPHPValue(): void | ||
{ | ||
self::assertIsString($this->type->convertToPHPValue('foo', $this->platform)); | ||
self::assertIsString($this->type->convertToPHPValue('', $this->platform)); | ||
self::assertNull($this->type->convertToPHPValue(null, $this->platform)); | ||
} | ||
|
||
public function testConvertToPHPEnum(): void | ||
{ | ||
$this->type->enumClassname = EnumPhp::class; | ||
|
||
self::assertInstanceOf($this->type->enumClassname, $this->type->convertToPHPValue('a', $this->platform)); | ||
self::assertSame(EnumPhp::VALUE_A, $this->type->convertToPHPValue('a', $this->platform)); | ||
self::assertNull($this->type->convertToPHPValue(null, $this->platform)); | ||
} | ||
|
||
public function testConvertToPHPObject(): void | ||
{ | ||
$this->type->enumClassname = EnumClass::class; | ||
|
||
self::assertInstanceOf($this->type->enumClassname, $this->type->convertToPHPValue('a', $this->platform)); | ||
self::assertEquals(new EnumClass('a'), $this->type->convertToPHPValue('a', $this->platform)); | ||
self::assertNull($this->type->convertToPHPValue(null, $this->platform)); | ||
} | ||
|
||
public function testConvertStringToDatabaseValue(): void | ||
{ | ||
self::assertSame('a', $this->type->convertToDatabaseValue('a', $this->platform)); | ||
self::assertNull($this->type->convertToDatabaseValue(null, $this->platform)); | ||
} | ||
|
||
public function testConvertEnumToDatabaseValue(): void | ||
{ | ||
$this->type->enumClassname = EnumPhp::class; | ||
|
||
self::assertSame('a', $this->type->convertToDatabaseValue(EnumPhp::VALUE_A, $this->platform)); | ||
self::assertNull($this->type->convertToDatabaseValue(null, $this->platform)); | ||
} | ||
|
||
public function testConvertObjectToDatabaseValue(): void | ||
{ | ||
$enum = new EnumClass('a'); | ||
$this->type->enumClassname = EnumClass::class; | ||
|
||
self::assertSame('a', $this->type->convertToDatabaseValue($enum, $this->platform)); | ||
self::assertNull($this->type->convertToDatabaseValue(null, $this->platform)); | ||
} | ||
} | ||
|
||
enum EnumPhp: string | ||
{ | ||
case VALUE_A = 'a'; | ||
case VALUE_B = 'b'; | ||
} | ||
|
||
final class EnumClass implements \Stringable { | ||
Check failure on line 85 in tests/Types/EnumTest.php GitHub Actions / Coding Standards / Coding Standards (8.3)
|
||
function __construct( | ||
private ?string $value | ||
) { | ||
} | ||
|
||
function __tostring(): string | ||
{ | ||
return $this->value; | ||
Check failure on line 93 in tests/Types/EnumTest.php GitHub Actions / Static Analysis with Psalm (8.3)NullableReturnStatement
|
||
} | ||
}; |