From 861d05568fb3afceb80bcefa9c7ecfc72774f8bd Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Wed, 15 Nov 2023 11:12:18 +0900 Subject: [PATCH] ChronosDate does not implement DateTimeInterface #9967 For coherence, we also update Chronos and cover everything with tests since we have more code than before. --- src/DBAL/Types/ChronosType.php | 27 ++++++++++- src/DBAL/Types/DateType.php | 28 ++++++++++- tests/DBAL/Types/ChronosTypeTest.php | 71 ++++++++++++++++++++++++++++ tests/DBAL/Types/DateTypeTest.php | 63 ++++++++++++++++++++++++ 4 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 tests/DBAL/Types/ChronosTypeTest.php create mode 100644 tests/DBAL/Types/DateTypeTest.php diff --git a/src/DBAL/Types/ChronosType.php b/src/DBAL/Types/ChronosType.php index bea28f2..0f504fa 100644 --- a/src/DBAL/Types/ChronosType.php +++ b/src/DBAL/Types/ChronosType.php @@ -7,12 +7,29 @@ use Cake\Chronos\Chronos; use DateTimeInterface; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Types\ConversionException; use Doctrine\DBAL\Types\DateTimeType; final class ChronosType extends DateTimeType { /** - * @param null|DateTimeInterface|int|string $value + * @return ($value is null ? null : string) + */ + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string + { + if ($value === null) { + return $value; + } + + if ($value instanceof DateTimeInterface) { + return $value->format($platform->getDateTimeFormatString()); + } + + throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'Chronos']); + } + + /** + * @return ($value is null ? null : Chronos) */ public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Chronos { @@ -20,6 +37,14 @@ public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Ch return $value; } + if (!is_string($value) && !$value instanceof DateTimeInterface) { + throw ConversionException::conversionFailedFormat( + $value, + $this->getName(), + $platform->getDateTimeFormatString(), + ); + } + $val = new Chronos($value); return $val; diff --git a/src/DBAL/Types/DateType.php b/src/DBAL/Types/DateType.php index 0062b97..c3ea794 100644 --- a/src/DBAL/Types/DateType.php +++ b/src/DBAL/Types/DateType.php @@ -5,13 +5,29 @@ namespace Ecodev\Felix\DBAL\Types; use Cake\Chronos\ChronosDate; -use DateTimeInterface; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Types\ConversionException; final class DateType extends \Doctrine\DBAL\Types\DateType { /** - * @param null|ChronosDate|DateTimeInterface|string $value + * @return ($value is null ? null : string) + */ + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string + { + if ($value === null) { + return $value; + } + + if ($value instanceof ChronosDate) { + return $value->format($platform->getDateFormatString()); + } + + throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'ChronosDate']); + } + + /** + * @return ($value is null ? null : ChronosDate) */ public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?ChronosDate { @@ -19,6 +35,14 @@ public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Ch return $value; } + if (!is_string($value)) { + throw ConversionException::conversionFailedFormat( + $value, + $this->getName(), + $platform->getDateFormatString(), + ); + } + $val = new ChronosDate($value); return $val; diff --git a/tests/DBAL/Types/ChronosTypeTest.php b/tests/DBAL/Types/ChronosTypeTest.php new file mode 100644 index 0000000..b2db9d0 --- /dev/null +++ b/tests/DBAL/Types/ChronosTypeTest.php @@ -0,0 +1,71 @@ +type = new ChronosType(); + $this->platform = new MySQLPlatform(); + } + + public function testConvertToDatabaseValue(): void + { + self::assertSame('DATETIME', $this->type->getSqlDeclaration(['foo'], $this->platform)); + self::assertFalse($this->type->requiresSQLCommentHint($this->platform)); + + $actual = $this->type->convertToDatabaseValue(new Chronos('2016-01-01 15:58:59'), $this->platform); + self::assertSame('2016-01-01 15:58:59', $actual, 'support Chronos'); + + $actual = $this->type->convertToDatabaseValue(new DateTimeImmutable('2016-01-01 15:58:59'), $this->platform); + self::assertSame('2016-01-01 15:58:59', $actual, 'support DateTimeImmutable'); + + self::assertNull($this->type->convertToDatabaseValue(null, $this->platform), 'support null values'); + } + + public function testConvertToPHPValue(): void + { + $actualPhp = $this->type->convertToPHPValue('2016-01-01 15:58:59', $this->platform); + self::assertInstanceOf(Chronos::class, $actualPhp); + self::assertSame('2016-01-01 15:58:59', $actualPhp->__toString(), 'support string'); + + $actualPhp = $this->type->convertToPHPValue(new Chronos('2016-01-01 15:58:59'), $this->platform); + self::assertInstanceOf(Chronos::class, $actualPhp); + self::assertSame('2016-01-01 15:58:59', $actualPhp->__toString(), 'support Chronos'); + + $actualPhp = $this->type->convertToPHPValue(new DateTimeImmutable('2016-01-01 15:58:59'), $this->platform); + self::assertInstanceOf(Chronos::class, $actualPhp); + self::assertSame('2016-01-01 15:58:59', $actualPhp->__toString(), 'support DateTimeImmutable'); + + self::assertNull($this->type->convertToPHPValue(null, $this->platform), 'support null values'); + } + + public function testConvertToPHPValueThrowsWithInvalidValue(): void + { + $this->expectException(ConversionException::class); + + $this->type->convertToPHPValue(123, $this->platform); + } + + public function testConvertToDatabaseValueThrowsWithInvalidValue(): void + { + $this->expectException(ConversionException::class); + + $this->type->convertToDatabaseValue(123, $this->platform); + } +} diff --git a/tests/DBAL/Types/DateTypeTest.php b/tests/DBAL/Types/DateTypeTest.php new file mode 100644 index 0000000..288e0f7 --- /dev/null +++ b/tests/DBAL/Types/DateTypeTest.php @@ -0,0 +1,63 @@ +type = new DateType(); + $this->platform = new MySQLPlatform(); + } + + public function testConvertToDatabaseValue(): void + { + self::assertSame('DATE', $this->type->getSqlDeclaration(['foo'], $this->platform)); + self::assertFalse($this->type->requiresSQLCommentHint($this->platform)); + + $actual = $this->type->convertToDatabaseValue(new ChronosDate('2016-01-01'), $this->platform); + self::assertSame('2016-01-01', $actual, 'support Chronos'); + + self::assertNull($this->type->convertToDatabaseValue(null, $this->platform), 'support null values'); + } + + public function testConvertToPHPValue(): void + { + $actualPhp = $this->type->convertToPHPValue('2022-12-31', $this->platform); + self::assertInstanceOf(ChronosDate::class, $actualPhp); + self::assertSame('2022-12-31', $actualPhp->__toString(), 'support string'); + + $actualPhp = $this->type->convertToPHPValue(new ChronosDate('2022-12-31'), $this->platform); + self::assertInstanceOf(ChronosDate::class, $actualPhp); + self::assertSame('2022-12-31', $actualPhp->__toString(), 'support ChronosDate'); + + self::assertNull($this->type->convertToPHPValue(null, $this->platform), 'support null values'); + } + + public function testConvertToPHPValueThrowsWithInvalidValue(): void + { + $this->expectException(ConversionException::class); + + $this->type->convertToPHPValue(123, $this->platform); + } + + public function testConvertToDatabaseValueThrowsWithInvalidValue(): void + { + $this->expectException(ConversionException::class); + + $this->type->convertToDatabaseValue(123, $this->platform); + } +}