2016-03-03 69 views
0

我正在用Laravel 5.2创建一个通用搜索功能,并且我想要显示出现搜索关键字的所有书籍:书的标题,书的主题,书的情节,书的作者姓名,书的作者姓氏; 我认为这个代码将工作:Laravel multiple where where has has callback

$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(); 

但是,当我作为关键字使用作者的名字,我得到的结果全库。 相反,如果我输入作者的姓氏,它完美的作品。

我发现了这个解决方案,这是不是最佳的在我看来,但至少它的工作原理:

$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(); 

有什么建议? 预先感谢您的帮助!

+0

也许创建搜索(关键字)一些额外的索引表?如果你想“一些”速度使用redis而不是mysql。 – Hardy

+0

@ Hardy谢谢你的建议,但实际上只是一个学校的项目;自从我发现了拉拉维尔的这种有趣的行为之后,我想知道原因,无非就是如此。 – AskMan

回答

4

问题是在你的闭包中添加的where子句不是where子句应用于子查询的唯一地方。 whereHas()方法生成一个以关系的id的where子句开始的子查询。正因为如此,你的子查询不仅仅是where x or y,它实际上是where x and y or z

给定这组where子句以及逻辑运算符的操作顺序,如果z条件为真(您的'名称'条件),则整个where子句将返回true,这意味着仅查看相关的约束对象完全被忽略。由于相关对象的约束被忽略,因此每个记录的has条件将为真(如果'name'匹配任何记录)。

下面是你的逻辑条件的一个例子:

// 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 

所以,要解决这个问题,你需要用你的or条件括号,所以你预期他们正在评估。您可以通过封闭传递给where()方法做到这一点,然后关闭内添加任何条件将括号内:

$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(); 
+0

非常感谢聪明的回答,我真的很感激! – AskMan

-2

你尝试创建您的作者/用户模式的本地查询的范围,例如命名为(),然后应用该

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

然后将查询

$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

+3

不投票没有评论解释为什么 – ExoticChimp

+0

我想这是因为,即使这个答案对于展示如何创建一个范围是有用的,它并没有做任何事情来解决问题。原始代码和这个答案中的代码在功能上是等同的。你只是移动了定义'where'子句的地方。 – patricus