简体   繁体   中英

Laravel multiple where condition in whereHas callback

I am creating a general search function with Laravel 5.2 and I want to display all the books in which occurs the searched keyword in: the book's title, book's subject, book's plot, book's author name, book's author surname; I thought that this code would work:

$results = Book::whereHas('author', function ($query) use ($keyword)
                     {
                          $query->where('surname', 'LIKE', '%'.$keyword.'%')
                               ->orWhere('name', 'LIKE', '%'.$keyword.'%');
                     })
                     ->orWhere('title', 'LIKE', '%'.$keyword.'%')
                     ->orWhere('plot', 'LIKE', '%'.$keyword.'%')
                     ->orWhere('subject', 'LIKE', '%'.$keyword.'%')
                     ->get();

But when I use as a keyword the name of an author, I get as a results the whole library. Instead if I enter the surname of an author it works perfectly.

I found out this solution, that is not optimal in my opinion, but at least it works:

$results = Book::whereHas('author', function ($query) use ($keyword)
                     {
                          $query->where('surname', 'LIKE', '%'.$keyword.'%');
                     })
                     ->orWhereHas('author', function ($query) use ($keyword)
                     {
                          $query->where('name', 'LIKE', '%'.$keyword.'%');
                     })
                     ->orWhere('title', 'LIKE', '%'.$keyword.'%')
                     ->orWhere('plot', 'LIKE', '%'.$keyword.'%')
                     ->orWhere('subject', 'LIKE', '%'.$keyword.'%')
                     ->get();

Any suggestions? Thank you in advance for your help!

The issue is that the where clauses added in your closure aren't the only where clauses being applied to the subquery. The whereHas() method generates a subquery that starts with a where clause on the ids for the relationship. Because of this, your subquery isn't just where x or y , it is actually where x and y or z .

Given this set of where clauses, and the order of operations for logical operators, if the z condition is true (your 'name' condition), the whole where clause will return true, meaning the constraint on only looking at related objects is completely ignored. Since the constraint on related objects is ignored, the has condition will be true for every record (if 'name' matches any record).

Below is an example of your logical conditions:

// first boolean is the related keys check
// second boolean is the surname check
// third boolean is the name check

// this is your current logic
// as you can see, this returns true even when looking at an
// author not even related to the book.
var_export(false && false || true); // true

// this is what your logic needs to be
var_export(false && (false || true)); // false

So, to solve this issue, you need to wrap your or conditions in parentheses, so they're evaluated as you intended. You can do this by passing a closure to the where() method, and then any conditions added inside the closure will be inside parentheses:

$results = Book::whereHas('author', function ($query) use ($keyword) {
        $query->where(function ($q) use ($keyword) {
            $q->where('surname', 'LIKE', '%'.$keyword.'%')
                ->orWhere('name', 'LIKE', '%'.$keyword.'%');
        });
    })
    ->orWhere('title', 'LIKE', '%'.$keyword.'%')
    ->orWhere('plot', 'LIKE', '%'.$keyword.'%')
    ->orWhere('subject', 'LIKE', '%'.$keyword.'%')
    ->get();

have you tried creating a local query scope for your author/user model such as named() then applying that

public function scopeNamed($query)
{
    return $query->where('surname', 'LIKE', '%'.$keyword.'%')
                 ->orWhere('name', 'LIKE', '%'.$keyword.'%');
}

Then your query

$results = Book::whereHas('author', function ($query) use ($keyword)
                 {
                      $query->named();
                 })
                 ->orWhere('title', 'LIKE', '%'.$keyword.'%')
                 ->orWhere('plot', 'LIKE', '%'.$keyword.'%')
                 ->orWhere('subject', 'LIKE', '%'.$keyword.'%')
                 ->get();

https://laravel.com/docs/master/eloquent#query-scopes

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM