fix(casts): update OrderReferenceCast to use base58 encoding

Refactor OrderReferenceCast to utilize base58 encoding for order references instead of UUID.
This change improves compatibility with the new OrderReference implementation.
This commit is contained in:
Jérémy Munsch 2026-10-27 16:09:39 +01:00
parent 91a82af969
commit 713c937890
No known key found for this signature in database
GPG key ID: 2AAECDA6B7330F91
10 changed files with 159 additions and 27 deletions

View file

@ -6,7 +6,6 @@ namespace App\Casts;
use Domain\Models\ValueObjects\OrderReference;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Ramsey\Uuid\Uuid;
/**
* @implements CastsAttributes<OrderReference,OrderReference>
@ -20,7 +19,7 @@ class OrderReferenceCast implements CastsAttributes
throw new \UnexpectedValueException('OrderReference cannot be null.');
}
return new OrderReference(Uuid::fromString($value));
return OrderReference::fromString($value);
}
/** @param array<string, mixed> $attributes */
@ -30,6 +29,6 @@ class OrderReferenceCast implements CastsAttributes
throw new \UnexpectedValueException('The given value is not an instance of OrderReference.');
}
return $value->value->toString();
return (string) $value;
}
}

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Domain\Models\ValueObjects;
use Ramsey\Uuid\UuidInterface;
use Shared\Exceptions\InvalidUuidException;
use Shared\Helpers\UuidHelper;
final readonly class OrderReference implements \Stringable
@ -17,9 +18,29 @@ final readonly class OrderReference implements \Stringable
}
}
public static function fromString(string $value): self
{
if (UuidHelper::isValid($value)) {
return new self(\Ramsey\Uuid\Uuid::fromString($value));
}
return self::fromBase58($value);
}
public static function fromBase58(string $base58): self
{
try {
$uuid = UuidHelper::fromBase58($base58);
} catch (InvalidUuidException) {
throw new \DomainException('Order reference must be a valid base-58 string');
}
return new self($uuid);
}
#[\Override]
public function __toString(): string
{
return $this->value->toString();
return UuidHelper::toBase58($this->value);
}
}

View file

@ -4,7 +4,7 @@
{{ __('Hello') }},
{{ __('The order') }} **{{ $order->reference->value }}** {{ __('from') }} {{ $order->employee->firstname }} {{ Str::upper($order->employee->lastname) }} ({{ $order->employee->matricule }}) {{ __('is confirmed.') }}
{{ __('The order') }} **{{ $order->reference }}** {{ __('from') }} {{ $order->employee->firstname }} {{ Str::upper($order->employee->lastname) }} ({{ $order->employee->matricule }}) {{ __('is confirmed.') }}
<x-mail::table>
| {{ __('Product') }} | {{ __('Quantity') }} | {{ __('Total TTC') }} |

View file

@ -4,7 +4,7 @@
{{ __('Hello') }} {{ $order->employee->firstname }} {{ Str::upper($order->employee->lastname) }} ({{ $order->employee->matricule }}),
{{ __('Your order') }} **{{ $order->reference->value }}** {{ __('is confirmed.') }}
{{ __('Your order') }} **{{ $order->reference }}** {{ __('is confirmed.') }}
<x-mail::table>
| {{ __('Product') }} | {{ __('Quantity') }} | {{ __('Price per kilo TTC') }} | {{ __('Total TTC') }} |

View file

@ -8,6 +8,7 @@ use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Shared\Exceptions\InvalidUuidException;
use Symfony\Component\Uid\Uuid as SymfonyUuid;
final readonly class UuidHelper
{
@ -16,6 +17,31 @@ final readonly class UuidHelper
// Static class
}
public static function isValid(string $uuid): bool
{
return Uuid::isValid($uuid);
}
public static function toBase58(UuidInterface $uuid): string
{
return SymfonyUuid::fromString($uuid->toString())->toBase58();
}
public static function fromBase58(string $base58): UuidInterface
{
$base58 = trim($base58);
if ($base58 === '' || ! preg_match('/^[1-9A-HJ-NP-Za-km-z]{21,22}$/', $base58)) {
throw InvalidUuidException::forValue($base58);
}
try {
return Uuid::fromString(SymfonyUuid::fromBase58($base58)->toString());
} catch (\Throwable) {
throw InvalidUuidException::forValue($base58);
}
}
public static function isV4(UuidInterface $uuid): bool
{
return self::version($uuid) === 4;

View file

@ -93,13 +93,14 @@ describe('Bo\OrdersController', function (): void {
it('can filter index by search term', function (): void {
/** @var \Tests\TestCase $this */
// GIVEN
$refUuid = Uuid::uuid7();
$ref = $refUuid->toString();
$shop = Shop::factory()->create(['name' => 'UniqueShopSearch']);
$refUuid = Uuid::uuid7();
$reference = new OrderReference($refUuid);
$ref = (string) $reference;
$shop = Shop::factory()->create(['name' => 'UniqueShopSearch']);
Order::forceCreate([
'id' => Uuid::uuid7()->toString(),
'shop_id' => $shop->id,
'reference' => new OrderReference($refUuid),
'reference' => $reference,
'status' => OrderStatus::VALIDATED->value,
'total' => new Money(1500, new Currency('EUR')),
'employee' => new OrderEmployee(new Matricule('1000102'), 'Bob', 'Jones', new ShopperEstablishmentCode('FAC01'), new ShopperSiteCode('SITE'), new ShopperServiceCode('101')),

View file

@ -115,13 +115,14 @@ describe('OrderController', function () {
'updated_at' => CarbonImmutable::now(),
]);
$shop = Shop::factory()->create();
$refUuid = Uuid::uuid7();
$ref = $refUuid->toString();
$shop = Shop::factory()->create();
$refUuid = Uuid::uuid7();
$reference = new OrderReference($refUuid);
$ref = (string) $reference;
Order::forceCreate([
'id' => Uuid::uuid7()->toString(),
'shop_id' => $shop->id,
'reference' => new OrderReference($refUuid),
'reference' => $reference,
'status' => OrderStatus::VALIDATED->value,
'total' => new Money(5000, new Currency('EUR')),
'employee' => new OrderEmployee(new Matricule('8888001'), 'Jean', 'Dupont', new ShopperEstablishmentCode('FAC01'), new ShopperSiteCode('SITE'), new ShopperServiceCode('101')),
@ -197,13 +198,14 @@ describe('OrderController', function () {
'updated_at' => CarbonImmutable::now(),
]);
$shop = Shop::factory()->create();
$refUuid = Uuid::uuid7();
$ref = $refUuid->toString();
$order = Order::forceCreate([
$shop = Shop::factory()->create();
$refUuid = Uuid::uuid7();
$reference = new OrderReference($refUuid);
$ref = (string) $reference;
$order = Order::forceCreate([
'id' => Uuid::uuid7()->toString(),
'shop_id' => $shop->id,
'reference' => new OrderReference($refUuid),
'reference' => $reference,
'status' => OrderStatus::VALIDATED->value,
'total' => new Money(5000, new Currency('EUR')),
'employee' => new OrderEmployee(new Matricule('8888002'), 'Paul', 'Martin', new ShopperEstablishmentCode('FAC01'), new ShopperSiteCode('SITE'), new ShopperServiceCode('101')),

View file

@ -501,7 +501,7 @@ describe('TunnelController', function (): void {
]);
// THEN
$response->assertRedirectToRoute('fo.sales.confirmation.show-confirmed', $orderReference->value);
$response->assertRedirectToRoute('fo.sales.confirmation.show-confirmed', (string) $orderReference);
});
it('redirects back with message when limit exception occurs on confirmation submit', function (): void {
@ -596,7 +596,7 @@ describe('TunnelController', function (): void {
$order->save();
// WHEN
$response = $this->get('/commande/confirmation/' . $orderReference->value);
$response = $this->get('/commande/confirmation/' . (string) $orderReference);
// THEN
$response->assertOk()

View file

@ -6,8 +6,25 @@ use App\Casts\OrderReferenceCast;
use Domain\Models\ValueObjects\OrderReference;
use Illuminate\Database\Eloquent\Model;
use Ramsey\Uuid\Uuid;
use Shared\Helpers\UuidHelper;
it('gets a reference', function () {
// GIVEN
$cast = new OrderReferenceCast();
$model = \Mockery::mock(Model::class);
$uuid = Uuid::uuid7();
$value = UuidHelper::toBase58($uuid);
// WHEN
$reference = $cast->get($model, 'reference', $value, []);
// THEN
expect($reference)->toBeInstanceOf(OrderReference::class)
->and((string) $reference)->toBe($value)
->and($reference->value->equals($uuid))->toBeTrue();
});
it('gets a reference from raw uuid string', function () {
// GIVEN
$cast = new OrderReferenceCast();
$model = \Mockery::mock(Model::class);
@ -19,7 +36,7 @@ it('gets a reference', function () {
// THEN
expect($reference)->toBeInstanceOf(OrderReference::class)
->and((string) $reference)->toBe($value)
->and((string) $reference)->toBe(UuidHelper::toBase58($uuid))
->and($reference->value->equals($uuid))->toBeTrue();
});
@ -49,7 +66,7 @@ it('throws exception when setting invalid reference type', function () {
// GIVEN
$cast = new OrderReferenceCast();
$model = \Mockery::mock(Model::class);
$value = Uuid::uuid7()->toString();
$value = UuidHelper::toBase58(Uuid::uuid7());
// WHEN
$cast->set($model, 'reference', $value, []);
@ -71,5 +88,5 @@ it('sets a reference', function () {
$value = $cast->set($model, 'reference', $reference, []);
// THEN
expect($value)->toBe($uuid->toString());
expect($value)->toBe(UuidHelper::toBase58($uuid));
});

View file

@ -7,6 +7,7 @@ namespace Tests\Unit\Domain\Models\ValueObjects;
use Domain\Models\ValueObjects\OrderReference;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Shared\Helpers\UuidHelper;
describe('OrderReference', function () {
function getNewOrderReference(UuidInterface $value): OrderReference
@ -22,9 +23,51 @@ describe('OrderReference', function () {
$vo = getNewOrderReference($uuidV7);
// THEN
expect($vo)->toBeInstanceOf(OrderReference::class);
expect((string) $vo)->toBe($uuidV7->toString());
expect($vo->value)->toBe($uuidV7);
expect($vo)->toBeInstanceOf(OrderReference::class)
->and((string) $vo)->toBe(UuidHelper::toBase58($uuidV7))
->and($vo->value)->toBe($uuidV7);
});
it('can instantiate from a valid base-58 UUID v7 string', function () {
// GIVEN
$uuidV7 = Uuid::uuid7();
$base58 = UuidHelper::toBase58($uuidV7);
// WHEN
$vo = OrderReference::fromBase58($base58);
// THEN
expect($vo)->toBeInstanceOf(OrderReference::class)
->and((string) $vo)->toBe($base58)
->and($vo->value->equals($uuidV7))->toBeTrue();
});
it('can instantiate from a valid UUID v7 string via fromString', function () {
// GIVEN
$uuidV7 = Uuid::uuid7();
$uuidString = $uuidV7->toString();
// WHEN
$vo = OrderReference::fromString($uuidString);
// THEN
expect($vo)->toBeInstanceOf(OrderReference::class)
->and((string) $vo)->toBe(UuidHelper::toBase58($uuidV7))
->and($vo->value->equals($uuidV7))->toBeTrue();
});
it('can instantiate from a valid base-58 string via fromString', function () {
// GIVEN
$uuidV7 = Uuid::uuid7();
$base58 = UuidHelper::toBase58($uuidV7);
// WHEN
$vo = OrderReference::fromString($base58);
// THEN
expect($vo)->toBeInstanceOf(OrderReference::class)
->and((string) $vo)->toBe($base58)
->and($vo->value->equals($uuidV7))->toBeTrue();
});
it('throws exception if provided UUID is not v7', function () {
@ -37,4 +80,27 @@ describe('OrderReference', function () {
// THEN
expect($action)->toThrow(\DomainException::class, 'value must be a valid UUID v7');
});
it('throws exception if base-58 string is invalid', function () {
// GIVEN
$invalidBase58 = 'invalid-base58-string!';
// WHEN
$action = fn () => OrderReference::fromBase58($invalidBase58);
// THEN
expect($action)->toThrow(\DomainException::class, 'Order reference must be a valid base-58 string');
});
it('throws exception if base-58 string is not from a UUID v7', function () {
// GIVEN
$uuidV4 = Uuid::uuid4();
$base58 = UuidHelper::toBase58($uuidV4);
// WHEN
$action = fn () => OrderReference::fromBase58($base58);
// THEN
expect($action)->toThrow(\DomainException::class, 'value must be a valid UUID v7');
});
});