Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel orWhereHas not working as expected

I'm trying to use the orWhereHas method in Laravel with nested relations, but I am not getting the result I expect. Am I misunderstanding how to use this method?

My relationships are set up as follows:

Product
    -> hasOne uniqueItem
        -> hasMany fulfillmentCenterUniqueItems
    -> hasMany skus
        -> hasOne uniqueItem
            -> hasMany fulfillmentCenterUniqueItems

I'm trying to test out the whereHas and orWhereHas methods by retrieving products from the database that contain uniqueItems that have a uniqueItemFulfillmentCenter with id = 7089 OR products that contain a sku, that contains a uniqueItem, that has a uniqueItemFulfillmentCenter with id = 7412.

Based on the data in my database, the result of this query should be two products. Product IDs 105 and 239.

Here's the Eloquent code I'm using:

$product = Spire_models\Product
    ::whereHas('uniqueItem.fulfillmentCenterUniqueItems', function ($query)
    {
            $query->where('id', 7089);
    })
    ->orWhereHas('skus.uniqueItem.fulfillmentCenterUniqueItems', function ($query)
    {
            $query->where('id', 7412);
    })
    ->get()->toArray();

For some reason, this is only returning product ID 105, instead of 105 and 239. The generated sql from this function is:

select * from `products` where `products`.`deleted_at` is null and (select count(*) from `unique_items` where `products`.`unique_item_id` = `unique_items`.`id` and (select count(*) from `fulfillment_center_unique_items` where `fulfillment_center_unique_items`.`unique_item_id` = `unique_items`.`id` and `id` = 7089 and `fulfillment_center_unique_items`.`deleted_at` is null) >= 1 and `unique_items`.`deleted_at` is null) >= 1 and (select count(*) from `skus` where `skus`.`product_id` = `products`.`id` and (select count(*) from `unique_items` where `skus`.`unique_item_id` = `unique_items`.`id` or (select count(*) from `fulfillment_center_unique_items` where `fulfillment_center_unique_items`.`unique_item_id` = `unique_items`.`id` and `id` = 7412 and `fulfillment_center_unique_items`.`deleted_at` is null) >= 1 and `unique_items`.`deleted_at` is null) >= 1 and `skus`.`deleted_at` is null) >= 1

Is this sql being generated incorrectly, or am I misusing the orWhereHas method? To me it does not look like the OR statement is being placed correctly in the sql.

If I remove the orWhereHas method, things works as expected. For example, if I run this:

$product = Spire_models\Product
    ::whereHas('uniqueItem.fulfillmentCenterUniqueItems', function ($query)
    {
            $query->where('id', 7089);
    })
    ->get()->toArray();

I correctly get back product ID 105. If I run this:

$product = Spire_models\Product
    ::whereHas('skus.uniqueItem.fulfillmentCenterUniqueItems', function ($query)
    {
            $query->where('id', 7412);
    })
    ->get()->toArray();

I correctly get back product ID 239. So the individual pieces of the query work correctly, but it seems when I try to combine these with an orWhereHas, I get unexpected results. Any idea why?

EDIT

As per the comments, it looks like this is a bug. I was able to temporarily work around it by rewriting the code to use where and orWhere. Here's the temporary solution:

$product = Spire_models\Product
    ::where(function ($query)
    {
            $query->whereHas('uniqueItem.fulfillmentCenterUniqueItems', function ($query)
            {
                    $query->where('id', 7089);
            });
    })
    ->orWhere(function ($query)
    {
            $query->whereHas('skus.uniqueItem.fulfillmentCenterUniqueItems', function ($query)
            {
                    $query->where('id', 7412);
            });
    })
    ->get()->toArray();
like image 390
flyingL123 Avatar asked Jan 30 '15 17:01

flyingL123


1 Answers

It was a bug and is fixed by now with this PR https://github.com/laravel/framework/pull/8171

It's been OK since version 5.0.21

like image 173
Jarek Tkaczyk Avatar answered Oct 15 '22 16:10

Jarek Tkaczyk