feat(infra): add HasUuidV7CreatedAt trait to derive created_at from UUIDv7
This commit is contained in:
parent
05af05e171
commit
6d63e68908
2 changed files with 144 additions and 0 deletions
34
src/Infra/Database/Concerns/HasUuidV7CreatedAt.php
Normal file
34
src/Infra/Database/Concerns/HasUuidV7CreatedAt.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Infra\Database\Concerns;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Shared\Helpers\UuidHelper;
|
||||
|
||||
/** @mixin \Illuminate\Database\Eloquent\Model&\Illuminate\Database\Eloquent\Concerns\HasUuids */
|
||||
trait HasUuidV7CreatedAt
|
||||
{
|
||||
public static function bootHasUuidV7CreatedAt(): void
|
||||
{
|
||||
static::creating(static function (Model $model): void {
|
||||
if ($model->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));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
110
tests/Unit/Infra/Database/Concerns/HasUuidV7CreatedAtTest.php
Normal file
110
tests/Unit/Infra/Database/Concerns/HasUuidV7CreatedAtTest.php
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Infra\Database\Concerns;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Infra\Casts\UtcDateTimeCast;
|
||||
use Infra\Database\Concerns\HasUuidV7CreatedAt;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Tests\TestCase;
|
||||
|
||||
covers(HasUuidV7CreatedAt::class);
|
||||
|
||||
uses(TestCase::class);
|
||||
|
||||
describe('HasUuidV7CreatedAt Trait Unit Tests', function (): void {
|
||||
beforeEach(function (): void {
|
||||
Schema::create('test_uuid_models', function (Blueprint $table): void {
|
||||
$table->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');
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue