justcntt
justcntt

Reputation: 237

Why OrWhereHas not work correctly in Laravel

I creating API to filter restaurants. When user find restaurant by district, I want show restaurants at this district and restaurants ship food to this district. So, I used orWhereHas.

function searchRestaurant(Request $request) {
    $city = $request->input('city');
    $district = $request->input('district');
    $category = $request->input('category');
    $fee = $request->input('fee');

    $restaurants = new Restaurant();

    if($district) {
        $restaurants = $restaurants->where('district_id', $district)
            ->orWhereHas('shipdistricts', function($q) use ($district) {
           $q->where('id', $district);
        });
    }
    elseif($city && !$district) {
        $restaurants = $restaurants->where('city_id', $city);
    }

    if($category){
        $restaurants = $restaurants->whereHas('categories', function($q) use ($category) {
            $q->where('id', $category);
        });
    }

    return response()->json([
        'success' => true,
        'data' => $restaurants->get()->unique()->toArray()
    ]);
}

Restaurant Model

public function shipdistricts()
{
    return $this->belongsToMany(District::class, 'restaurant_districts', 'restaurant_id');
}
public function categories()
{
    return $this->belongsToMany(Category::class,'restaurant_categories_relation','restaurant_id');
}

But when I request category, the result not correctly. Why? Sorry my English is not good!

Upvotes: 1

Views: 2702

Answers (2)

Namoshek
Namoshek

Reputation: 6544

The problem is most likely that you are not encapsulating your orWhere() properly. This will yield a query that isn't very obvious and only functions due to the precedence of AND over OR.

So you basically have to wrap the district/city conditionals in a where(function ($query) { }) block. Together with the nice usage of when($condition, $callback), this yields a result like this:

function searchRestaurant(Request $request)
{
    $city = $request->input('city');
    $district = $request->input('district');
    $category = $request->input('category');
    $fee = $request->input('fee');

    $restaurants = Restaurant::query()
        ->when($district, function ($query, $bool) use ($district) {
            $query->where(function (query) use ($district) {
                $query->where('district_id', $district)
                    ->orWhereHas('shipdistricts', function ($query) use ($district) {
                        $query->where('id', $district);
                    });
            });
        })
        ->when($city && !$district, function ($query, $bool) use ($city) {
            $query->where('city_id', $city);
        })
        ->when($category, function ($query, $bool) use ($category) {
            $query->whereHas('categories', function ($query) use ($category) {
                $query->where('id', $category);
            });
        })
        ->get();

    return response()->json([
        'success' => true,
        'data' => $restaurants->toArray(),
    ]);
}

You are not required to use unique() on the result either as there can only be one instance per Restaurant anyway.

Upvotes: 3

Irwing Reza
Irwing Reza

Reputation: 371

I believe that your issue is that you created a new $restaurants = new Restaurant(); and you are trying to query. Please use this query :

$query = Restaurant::query();

Upvotes: 0

Related Questions