-
Notifications
You must be signed in to change notification settings - Fork 41
Models with (Efficient) Uuid PK cannot be lazy loaded #60
Description
Disclaimer: I know it's not recommended to use Laravel Model Uuids / Efficient Uuids as PK. I would happily discuss the situation if you open for a quick architectural debate, but let's keep the following example short. Err kinda.
Imagine I have three tables:
Carts
have a unique customer id.CartGroups
: eachCart
might have multipleCartGroups
Partners
: eachCartGroup
is representing aPartner
(aka seller or shop) in the customer'sCart
.
Carts
and CartGroups
are native to the microservice (API) that handles them, they have unsigned BigInt id
s (as PK). Partners
, on the other hand, come from a different service and stored as-is for caching purposes. They have EfficientUuid
id
s, therefore CartGroups
have an EfficientUuid
foreign key (partnerId
) pointing to them.
Migration files:
Schema::create('partners', function (Blueprint $table) {
$table->efficientUuid('id')->primary();
$table->string('name', 255);
$table->string('logoUrl', 255);
$table->timestamps();
});
Schema::create('carts', function (Blueprint $table) {
$table->id();
$table->char('customerId', 40)->unique();
$table->timestamps();
});
Schema::create('cartGroups', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('cartId');
$table->efficientUuid('partnerId');
$table->timestamps();
$table->foreign('cartId')->references('id')->on('carts')
->cascadeOnDelete()
->cascadeOnUpdate();
$table->foreign('partnerId')->references('id')->on('partners')
->cascadeOnDelete()
->cascadeOnUpdate();
});
Model definitions:
class Partner extends Model
{
use GeneratesUuid;
public $incrementing = false;
protected $keyType = 'string';
protected $table = 'partners';
protected $fillable = ['id', 'name','logoUrl'];
protected $casts = [
'id' => EfficientUuid::class,
];
public function uuidColumns(): array
{
return ['id'];
}
}
class Cart extends Model
{
protected $table = 'carts';
protected $fillable = ['customerId'];
public function cartGroups(): HasMany
{
return $this->hasMany(CartGroup::class, 'cartId')->orderBy('id');
}
}
class CartGroup extends Model
{
protected $table = 'cartGroups';
protected $fillable = ['cartId', 'partnerId'];
protected $casts = [
'partnerId' => EfficientUuid::class,
];
public function uuidColumns(): array
{
return ['partnerId'];
}
public function cart(): BelongsTo
{
return $this->belongsTo(Cart::class, 'cartId');
}
public function partner(): BelongsTo
{
return $this->belongsTo(Partner::class, 'partnerId');
}
}
A simple controller to fetch a complete Cart
(with all relations using with
) for a given customerId
:
class GetCartController
{
public function __invoke(string $customerId): JsonResponse
{
return new JsonResponse([
'data' => Cart::query()
->with(['cartGroups.partner'])
->where('customerId', $customerId)
->firstOrFail()
]);
}
}
Tables are seeded with the following data:
INSERT INTO `partners` (`id`, `name`, `logoUrl`, `created_at`, `updated_at`) VALUES
(UNHEX('624A5B9B62F351CE8E3C0D25BD61AA11'), 'X Shop', 'x_logo.jpg', '2023-03-29 21:32:08', '2023-03-29 21:32:08'),
(UNHEX('ACAD4FC645AF5C47A7A9D69FE069AFAD'), 'Y Shop', 'y_logo.jpg', '2023-03-29 21:32:08', '2023-03-29 21:32:08');
INSERT INTO `carts` (`id`, `customerId`, `created_at`, `updated_at`) VALUES
(1, 'abc123', '2023-03-29 21:32:08', '2023-03-29 21:32:08');
INSERT INTO `cartGroups` (`id`, `cartId`, `partnerId`, `created_at`, `updated_at`) VALUES
(1, 1, UNHEX('624A5B9B62F351CE8E3C0D25BD61AA11'), '2023-03-29 21:32:08', '2023-03-29 21:32:08'),
(2, 1, UNHEX('ACAD4FC645AF5C47A7A9D69FE069AFAD'), '2023-03-29 21:32:08', '2023-03-29 21:32:08');
This is the result (without timestamps):
{
"data": {
"id": 1,
"customerId": "abc123",
"cart_groups": [
{
"id": 1,
"cartId": 1,
"partnerId": "624a5b9b-62f3-51ce-8e3c-0d25bd61aa11",
"partner": null
},
{
"id": 2,
"cartId": 1,
"partnerId": "acad4fc6-45af-5c47-a7a9-d69fe069afad",
"partner": null
}
]
}
}
And these are the generated queries as observed by Telescope:
select * from `carts` where `customerId` = 'abc123' limit 1
select * from `cartGroups` where `cartGroups`.`cartId` in (1) order by `id` asc
select * from `partners` where `partners`.`id` in (
'624a5b9b-62f3-51ce-8e3c-0d25bd61aa11',
'acad4fc6-45af-5c47-a7a9-d69fe069afad'
)
So no Partners
loaded as the query uses uuids as string instead of binary.
Spent the day looking for others with similar problems and possible solutions, haven't found any, unfortunately. I'm not sure how to proceed, as I'm fairly new to Laravel / Eloquent. Also I understand this might be a feature request for a very narrow use case, but I'd happily implement it myself, if you'd be so kind to point me the right direction. ;)
Cheers.