Skip to content
This repository has been archived by the owner on Apr 23, 2024. It is now read-only.

Models with (Efficient) Uuid PK cannot be lazy loaded #60

Open
bspanyik opened this issue Mar 30, 2023 · 0 comments
Open

Models with (Efficient) Uuid PK cannot be lazy loaded #60

bspanyik opened this issue Mar 30, 2023 · 0 comments

Comments

@bspanyik
Copy link

bspanyik commented Mar 30, 2023

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: each Cart might have multiple CartGroups
  • Partners: each CartGroup is representing a Partner (aka seller or shop) in the customer's Cart.

Carts and CartGroups are native to the microservice (API) that handles them, they have unsigned BigInt ids (as PK). Partners, on the other hand, come from a different service and stored as-is for caching purposes. They have EfficientUuid ids, 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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant