From 0d95983ced8e4f628398ef056558380640f7cd2c Mon Sep 17 00:00:00 2001 From: Julian Torices Date: Wed, 29 Jan 2025 11:54:14 +0100 Subject: [PATCH 1/5] breaking changes: added validation to UniqueId VO. Delete deprecated date time range VO. --- .../ValueObject/CollectionValueObject.php | 10 +- .../ValueObject/DateTimeRangeValeObject.php | 9 -- src/Domain/Model/ValueObject/UniqueId.php | 22 +++- .../DateTimeRangeValeObjectTest.php | 116 ------------------ .../Domain/Model/ValueObject/UniqueIdTest.php | 27 +++- 5 files changed, 52 insertions(+), 132 deletions(-) delete mode 100644 src/Domain/Model/ValueObject/DateTimeRangeValeObject.php delete mode 100644 tests/Domain/Model/ValueObject/DateTimeRangeValeObjectTest.php diff --git a/src/Domain/Model/ValueObject/CollectionValueObject.php b/src/Domain/Model/ValueObject/CollectionValueObject.php index 226664f..e9caf16 100644 --- a/src/Domain/Model/ValueObject/CollectionValueObject.php +++ b/src/Domain/Model/ValueObject/CollectionValueObject.php @@ -95,6 +95,11 @@ public function first() return $this->items[array_key_first($this->items)] ?? null; } + public function value(): array + { + return $this->items; + } + protected function addItem($item): static { $items = $this->items; @@ -109,9 +114,4 @@ protected function removeItem($item): static static fn ($current) => $current !== $item, ); } - - public function value(): array - { - return $this->items; - } } diff --git a/src/Domain/Model/ValueObject/DateTimeRangeValeObject.php b/src/Domain/Model/ValueObject/DateTimeRangeValeObject.php deleted file mode 100644 index a63d825..0000000 --- a/src/Domain/Model/ValueObject/DateTimeRangeValeObject.php +++ /dev/null @@ -1,9 +0,0 @@ -assertEquals(DateTimeValueObject::from('2000-01-01 00:00:00'), $range->start()); - $this->assertEquals(DateTimeValueObject::from('2019-12-31 23:59:59'), $range->end()); - } - - /** @test */ - public function given_invalid_range_when_creating_range_then_throw_exception(): void - { - $this->expectException(\InvalidArgumentException::class); - - DateTimeRangeValeObject::from( - DateTimeValueObject::from('2019-12-31 23:59:59'), - DateTimeValueObject::from('2000-01-01 00:00:00'), - ); - } - - /** @test */ - public function given_equal_dates_when_creating_range_then_throw_exception(): void - { - $this->expectException(\InvalidArgumentException::class); - - DateTimeRangeValeObject::from( - DateTimeValueObject::from('2000-01-01 00:00:00'), - DateTimeValueObject::from('2000-01-01 00:00:00'), - ); - } - - /** @test */ - public function given_expired_range_when_ask_if_expired_then_return_expected_info(): void - { - $range = DateTimeRangeValeObject::from( - DateTimeValueObject::from('2000-01-01 00:00:00'), - DateTimeValueObject::from('2001-12-31 23:59:59'), - ); - - $this->assertFalse($range->isInDate()); - $this->assertTrue($range->isExpired()); - $this->assertFalse($range->isNotStarted()); - } - - /** @test */ - public function given_in_date_range_when_ask_if_in_date_then_return_expected_info(): void - { - $range = DateTimeRangeValeObject::from( - DateTimeValueObject::from('2020-01-01 00:00:00'), - DateTimeValueObject::from('2100-12-31 23:59:59'), - ); - - $this->assertTrue($range->isInDate()); - $this->assertFalse($range->isExpired()); - $this->assertFalse($range->isNotStarted()); - } - - /** @test */ - public function given_not_started_range_when_ask_if_not_started_then_return_expected_info(): void - { - $range = DateTimeRangeValeObject::from( - DateTimeValueObject::from('2050-01-01 00:00:00'), - DateTimeValueObject::from('2100-12-31 23:59:59'), - ); - - $this->assertFalse($range->isInDate()); - $this->assertFalse($range->isExpired()); - $this->assertTrue($range->isNotStarted()); - } - - /** @test */ - public function given_two_equal_ranges_when_ask_to_check_equality_then_return_true(): void - { - $range = DateTimeRangeValeObject::from( - DateTimeValueObject::from('2000-01-01 00:00:00'), - DateTimeValueObject::from('2019-12-31 23:59:59'), - ); - - $other = DateTimeRangeValeObject::from( - DateTimeValueObject::from('2000-01-01 00:00:00'), - DateTimeValueObject::from('2019-12-31 23:59:59'), - ); - - $this->assertTrue($range->equalTo($other)); - } - - /** @test */ - public function given_two_different_ranges_when_ask_to_check_equality_then_return_false(): void - { - $range = DateTimeRangeValeObject::from( - DateTimeValueObject::from('2000-01-01 00:00:00'), - DateTimeValueObject::from('2019-12-31 23:59:59'), - ); - - $other = DateTimeRangeValeObject::from( - DateTimeValueObject::from('2000-01-01 00:00:01'), - DateTimeValueObject::from('2019-12-31 23:59:59'), - ); - - $this->assertFalse($range->equalTo($other)); - } -} diff --git a/tests/Domain/Model/ValueObject/UniqueIdTest.php b/tests/Domain/Model/ValueObject/UniqueIdTest.php index ac186a5..5d16cc3 100644 --- a/tests/Domain/Model/ValueObject/UniqueIdTest.php +++ b/tests/Domain/Model/ValueObject/UniqueIdTest.php @@ -28,7 +28,7 @@ public function given_uid_class_when_ask_to_generate_an_uid_then_return_uid_inst { $uid = UniqueId::create(); $this->assertInstanceOf(UniqueId::class, $uid); - $this->assertMatchesRegularExpression('/^[0-9A-Z]{10}$/i', $uid->value()); + $this->assertTrue(UniqueId::isValid($uid->value())); } /** @@ -53,4 +53,29 @@ public function given_two_different_uids_when_ask_to_check_equality_then_return_ $this->assertFalse($str->equalTo($other)); } + + /** + * @test + */ + public function given_invalid_uid_value_when_construct_then_fail() + { + $this->expectException(\InvalidArgumentException::class); + UniqueId::from('12345'); + } + + /** + * @test + */ + public function given_invalid_uid_value_when_ask_is_valid_then_return_false() + { + self::assertFalse(UniqueId::isValid('12345')); + } + + /** + * @test + */ + public function given_valid_uid_value_when_ask_is_valid_then_return_true() + { + self::assertTrue(UniqueId::isValid('H7HVQ2U72X')); + } } From f9f67cdcda77fe68a124f314be00c7ec3586ee40 Mon Sep 17 00:00:00 2001 From: "cristian.almohalla" Date: Tue, 11 Feb 2025 11:54:13 +0100 Subject: [PATCH 2/5] feat: throw exception on invalid date time arguments --- .../Model/ValueObject/DateTimeValueObject.php | 30 ++++++++----------- .../Model/ValueObject/DateValueObject.php | 30 ++++++++----------- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/src/Domain/Model/ValueObject/DateTimeValueObject.php b/src/Domain/Model/ValueObject/DateTimeValueObject.php index 04eac10..345c2b4 100644 --- a/src/Domain/Model/ValueObject/DateTimeValueObject.php +++ b/src/Domain/Model/ValueObject/DateTimeValueObject.php @@ -40,11 +40,11 @@ final public static function createFromFormat( string $format, string $datetime, ?\DateTimeZone $timezone = null - ): static | false { + ): static { $datetime = parent::createFromFormat($format, $datetime, $timezone); if (false === $datetime) { - return false; + throw new \InvalidArgumentException('Invalid date format'); } $timeZone = new \DateTimeZone(self::TIME_ZONE); @@ -52,20 +52,14 @@ final public static function createFromFormat( return static::createFromInterface($datetime->setTimezone($timeZone)); } - final public static function fromFormat(string $format, string $str): static | false + final public static function fromFormat(string $format, string $str): static { return static::createFromFormat($format, $str, new \DateTimeZone(self::TIME_ZONE)); } final public static function createFromTimestamp(float|int $timestamp): static { - $dateTime = \is_int($timestamp) - ? self::fromFormat('U', (string) $timestamp) - : self::fromFormat('U.u', \number_format($timestamp, 6, '.', '')); - - \assert(false !== $dateTime, 'Unexpected error on create date time from timestamp'); - - return $dateTime; + return self::fromFormat('U.u', \number_format($timestamp, 6, '.', '')); } final public static function fromTimestamp(int|float $timestamp): static @@ -83,12 +77,12 @@ final public function value(): string return $this->format(self::FORMAT); } - final public function modify(string $modifier): static | false + final public function modify(string $modifier): static { $dateTime = parent::modify($modifier); if (false === $dateTime) { - return false; + throw new \InvalidArgumentException('Invalid date modifier'); } return static::createFromInterface($dateTime); @@ -99,34 +93,34 @@ final public function add(\DateInterval $interval): static return parent::add($interval); } - final public function setDate(int $year, int $month, int $day): static | false + final public function setDate(int $year, int $month, int $day): static { $dateTime = parent::setDate($year, $month, $day); if (false === $dateTime) { - return false; + throw new \InvalidArgumentException('Invalid date provided'); } return $dateTime; } - final public function setISODate(int $year, int $week, int $dayOfWeek = 1): static | false + final public function setISODate(int $year, int $week, int $dayOfWeek = 1): static { $dateTime = parent::setISODate($year, $week, $dayOfWeek); if (false === $dateTime) { - return false; + throw new \InvalidArgumentException('Invalid ISO date provided'); } return $dateTime; } - final public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): static | false + final public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): static { $dateTime = parent::setTime($hour, $minute, $second, $microsecond); if (false === $dateTime) { - return false; + throw new \InvalidArgumentException('Invalid time provided'); } return $dateTime; diff --git a/src/Domain/Model/ValueObject/DateValueObject.php b/src/Domain/Model/ValueObject/DateValueObject.php index f7a1b79..86b420c 100644 --- a/src/Domain/Model/ValueObject/DateValueObject.php +++ b/src/Domain/Model/ValueObject/DateValueObject.php @@ -40,11 +40,11 @@ final public static function createFromFormat( string $format, string $datetime, ?\DateTimeZone $timezone = null - ): static | false { + ): static { $datetime = parent::createFromFormat($format, $datetime, $timezone); if (false === $datetime) { - return false; + throw new \InvalidArgumentException('Invalid date format'); } $timeZone = new \DateTimeZone(self::TIME_ZONE); @@ -52,20 +52,14 @@ final public static function createFromFormat( return static::createFromInterface($datetime->setTimezone($timeZone)); } - final public static function fromFormat(string $format, string $str): static | false + final public static function fromFormat(string $format, string $str): static { return static::createFromFormat($format, $str, new \DateTimeZone(self::TIME_ZONE)); } final public static function createFromTimestamp(float|int $timestamp): static { - $dateTime = \is_int($timestamp) - ? self::fromFormat('U', (string) $timestamp) - : self::fromFormat('U.u', \number_format($timestamp, 6, '.', '')); - - \assert(false !== $dateTime, 'Unexpected error on create date time from timestamp'); - - return $dateTime; + return self::fromFormat('U.u', \number_format($timestamp, 6, '.', '')); } final public static function fromTimestamp(int|float $timestamp): static @@ -83,12 +77,12 @@ final public function value(): string return $this->format(self::FORMAT); } - final public function modify(string $modifier): static | false + final public function modify(string $modifier): static { $dateTime = parent::modify($modifier); if (false === $dateTime) { - return false; + throw new \InvalidArgumentException('Invalid date modifier'); } return static::createFromInterface($dateTime); @@ -99,34 +93,34 @@ final public function add(\DateInterval $interval): static return parent::add($interval); } - final public function setDate(int $year, int $month, int $day): static | false + final public function setDate(int $year, int $month, int $day): static { $dateTime = parent::setDate($year, $month, $day); if (false === $dateTime) { - return false; + throw new \InvalidArgumentException('Invalid date provided'); } return $dateTime; } - final public function setISODate(int $year, int $week, int $dayOfWeek = 1): static | false + final public function setISODate(int $year, int $week, int $dayOfWeek = 1): static { $dateTime = parent::setISODate($year, $week, $dayOfWeek); if (false === $dateTime) { - return false; + throw new \InvalidArgumentException('Invalid ISO date provided'); } return $dateTime; } - final public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): static | false + final public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): static { $dateTime = parent::setTime($hour, $minute, $second, $microsecond); if (false === $dateTime) { - return false; + throw new \InvalidArgumentException('Invalid time provided'); } return $dateTime; From 1220b70d0e4b13b2314fecda96a1a22ec28f3258 Mon Sep 17 00:00:00 2001 From: "cristian.almohalla" Date: Tue, 11 Feb 2025 11:55:02 +0100 Subject: [PATCH 3/5] feat: ensure date value object has zero time --- src/Domain/Model/ValueObject/DateValueObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Domain/Model/ValueObject/DateValueObject.php b/src/Domain/Model/ValueObject/DateValueObject.php index 86b420c..4e146a8 100644 --- a/src/Domain/Model/ValueObject/DateValueObject.php +++ b/src/Domain/Model/ValueObject/DateValueObject.php @@ -18,7 +18,7 @@ public static function from(string $str): static { $timeZone = new \DateTimeZone(self::TIME_ZONE); - return (new static($str, $timeZone))->setTimezone($timeZone); + return (new static($str, $timeZone))->setTimezone($timeZone)->setTime(0, 0, 0, 0); } final public static function now(): static From daaa07cf9c4983ea3b1845d4efd3b5334973733e Mon Sep 17 00:00:00 2001 From: "cristian.almohalla" Date: Tue, 29 Jul 2025 12:10:21 +0200 Subject: [PATCH 4/5] feat: add find function to CollectionValueObject --- Dockerfile | 2 +- composer.json | 2 +- src/Domain/Model/ValueObject/CollectionValueObject.php | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 339fc04..fb4aa70 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM php:8.0-cli-alpine3.13 +FROM php:8.4-cli-alpine3.22 RUN apk update && \ apk add --no-cache \ diff --git a/composer.json b/composer.json index 7d6f5ef..5c51902 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ } }, "require": { - "php": "^8.0", + "php": "^8.4", "ext-json": "*", "ramsey/uuid": "^4.2" }, diff --git a/src/Domain/Model/ValueObject/CollectionValueObject.php b/src/Domain/Model/ValueObject/CollectionValueObject.php index 58c92a7..60d8cd6 100644 --- a/src/Domain/Model/ValueObject/CollectionValueObject.php +++ b/src/Domain/Model/ValueObject/CollectionValueObject.php @@ -52,6 +52,11 @@ public function walk(callable $func): void \array_walk($this->items, $func); } + public function find(callable $func) + { + return \array_find($this->items, $func); + } + public function filter(callable $func): static { return static::from(\array_values(\array_filter($this->items, $func))); From d67060b0e14aff2b4b0ab5745f5b1dfdf4c3a73f Mon Sep 17 00:00:00 2001 From: "cristian.almohalla" Date: Mon, 11 Aug 2025 16:01:52 +0200 Subject: [PATCH 5/5] refactor: array find function name --- src/Domain/Model/ValueObject/CollectionValueObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Domain/Model/ValueObject/CollectionValueObject.php b/src/Domain/Model/ValueObject/CollectionValueObject.php index 60d8cd6..0e01c35 100644 --- a/src/Domain/Model/ValueObject/CollectionValueObject.php +++ b/src/Domain/Model/ValueObject/CollectionValueObject.php @@ -52,7 +52,7 @@ public function walk(callable $func): void \array_walk($this->items, $func); } - public function find(callable $func) + public function findOne(callable $func) { return \array_find($this->items, $func); }