diff --git a/src/Infra/Database/Concerns/HasUuidV7CreatedAt.php b/src/Infra/Database/Concerns/HasUuidV7CreatedAt.php new file mode 100644 index 0000000..45b827c --- /dev/null +++ b/src/Infra/Database/Concerns/HasUuidV7CreatedAt.php @@ -0,0 +1,34 @@ +getAttribute('created_at') === null) { + $key = $model->getKey(); + + if (empty($key)) { + $key = $model->newUniqueId(); + $model->setAttribute($model->getKeyName(), $key); + } + + if (! empty($key) && (is_string($key) || $key instanceof \Stringable) && Uuid::isValid((string) $key)) { + $uuid = Uuid::fromString((string) $key); + if (UuidHelper::isV7($uuid)) { + $model->setAttribute('created_at', UuidHelper::toDateTime($uuid)); + } + } + } + }); + } +} diff --git a/tests/Unit/Infra/Database/Concerns/HasUuidV7CreatedAtTest.php b/tests/Unit/Infra/Database/Concerns/HasUuidV7CreatedAtTest.php new file mode 100644 index 0000000..bb6db29 --- /dev/null +++ b/tests/Unit/Infra/Database/Concerns/HasUuidV7CreatedAtTest.php @@ -0,0 +1,110 @@ +uuid('id')->primary(); + $table->string('name'); + $table->dateTime('created_at'); + }); + }); + + afterEach(function (): void { + Schema::dropIfExists('test_uuid_models'); + }); + + it('automatically populates created_at from UUIDv7 upon model creation', function (): void { + // GIVEN + $expectedDate = new \DateTimeImmutable('2026-08-21 11:30:00 UTC'); + $uuid7 = Uuid::uuid7($expectedDate)->toString(); + + $testModelClass = new class () extends Model + { + use HasUuidV7CreatedAt; + + protected $table = 'test_uuid_models'; + + public $timestamps = false; + + public $incrementing = false; + + protected $keyType = 'string'; + + protected $fillable = ['id', 'name', 'created_at']; + + #[\Override] + protected function casts(): array + { + return [ + 'created_at' => UtcDateTimeCast::class, + ]; + } + }; + + // WHEN + $instance = $testModelClass::query()->create([ + 'id' => $uuid7, + 'name' => 'Test Item', + ]); + + // THEN + expect($instance->created_at)->toBeInstanceOf(\DateTimeImmutable::class) + ->and($instance->created_at->format('Y-m-d H:i:s'))->toBe('2026-08-21 11:30:00'); + }); + + it('does not overwrite explicitly provided created_at', function (): void { + // GIVEN + $uuidDate = new \DateTimeImmutable('2026-08-21 11:30:00 UTC'); + $explicitDate = new \DateTimeImmutable('2026-01-01 00:00:00 UTC'); + $uuid7 = Uuid::uuid7($uuidDate)->toString(); + + $testModelClass = new class () extends Model + { + use HasUuidV7CreatedAt; + + protected $table = 'test_uuid_models'; + + public $timestamps = false; + + public $incrementing = false; + + protected $keyType = 'string'; + + protected $fillable = ['id', 'name', 'created_at']; + + #[\Override] + protected function casts(): array + { + return [ + 'created_at' => UtcDateTimeCast::class, + ]; + } + }; + + // WHEN + $instance = $testModelClass::query()->create([ + 'id' => $uuid7, + 'name' => 'Test Item', + 'created_at' => $explicitDate, + ]); + + // THEN + expect($instance->created_at->format('Y-m-d H:i:s'))->toBe('2026-01-01 00:00:00'); + }); +});