DEV Community

marius-ciclistu
marius-ciclistu

Posted on • Originally published at marius-ciclistu.Medium on

Maravel-Rest-Wizard Alternative To latestOfMany (CanBeOneOfMany)


Maravel-Rest-Wizard

Here is the inconvenient truth about latestOfMany() (\Illuminate\Database\Eloquent\Relations\Concerns\CanBeOneOfMany) at scale, how it can lead to timeouts, and how Maravel-Rest-Wizard can offer an alternative to it (inspired by this issue).

Given this relation definition:

public function latestProduct(): HasOneThrough
{
    return $this->products()->one()->latestOfMany();
}
Enter fullscreen mode Exit fullscreen mode

The root of the issue lies in how standard Eloquent eager loads these relationships. When you call Operation::with('latestProduct')the resulting eager loading SQL is:

SELECT 
    `products`.*, 
    `operations_products_pivot`.`operation_id` AS `laravel_through_key` 
FROM `products` 
INNER JOIN `operations_products_pivot` 
    ON `operations_products_pivot`.`product_id` = `products`.`id` 
WHERE `operations_products_pivot`.`operation_id` IN (3748918, 3748911, ...)
AND `products`.`id` IN (
    SELECT MAX(`product_id`) 
    FROM `operations_products_pivot`
    -- extra where would go here if the above linked issue would be solved
    INNER JOIN `products` AS `sub_products` ON `sub_products`.`id` = `operations_products_pivot`.`product_id`
    WHERE `sub_products`.`currency` = 'EUR' 
    -- end of Fix
    GROUP BY `operation_id`
);
Enter fullscreen mode Exit fullscreen mode

In a standard dataset, this could work. But imagine running this query on a table with multiple million rows. In simple words, timeouts will happen sooner or later.

The approach of Maravel-REST-Wizard (via MaravelQL) when a relation is defined based on that aggregated column:

public function latestProduct(): HasOne
{
    return $this->hasOne(Product::class, 'id', 'products__id_max');
}

// or segregated relations in Maravel/Maravelith

protected function segregatedRelationsDefinitionMap(): array
{
    return [
        'products' => fn(): HasManyThrough => $this->hasManyThrough(
            Product::class,
            OperationProductPivot::class,
            'operation_id',
            'id',
            'id',
            'product_id'
        ),
        'latestProduct' => fn(): HasOne => $this->hasOne(Product::class, 'id', 'products__id_max'),
    ];
}
Enter fullscreen mode Exit fullscreen mode

GET /operations?aggregates[maxsRelations][products][]=id&aggregates[maxsRelationsFilters][products][currency][in][]=EUR&withRelations[]=latestProduct

This translates into:

-- 13.52 ms on 3.7 M rows, sql: 
select `operations`.*, (
    select max(`products`.`id`) 
    from `products` inner join `operations_products_pivot` 
     on `operations_products_pivot`.`product_id` = `products`.`id`
    where `operations`.`id` = `operations_products_pivot`.`operation_id`
     and `products`.`currency` = 'EUR'
) as `products__id_max` from `operations` order by `created_at` desc limit 10 offset 0;
-- eager load sql:
select * from `products` where `id` in (61107, ...);

Enter fullscreen mode Exit fullscreen mode

and results in:

        [{
            "id": 3748918,
            "parent_id": 3,
            "client_id": 68378,
            "currency": "EUR",
            "value": "27.00",
            "created_at": "2024-01-17 11:04:57",
            "updated_at": "2026-02-19 16:40:57",
            "products__id_max": null,
            "latestProduct": null, // or latest_product
            "primary_key_identifier": 3748918
        },
        {
            "id": 3748911,
            "parent_id": 3,
            "client_id": 91963,
            "currency": "EUR",
            "value": "24.00",
            "created_at": "2024-01-17 11:04:57",
            "updated_at": "2025-08-04 13:26:03",
            "products__id_max": 61107,
            "latestProduct": { ... }, // or latest_product
            "primary_key_identifier": 3748911
        }, ...]
Enter fullscreen mode Exit fullscreen mode

Top comments (0)