雄辩的“查询生成器”和“或”运算符抵消了我的全局作用域-Laravel

tom*_*rod 8 php scope laravel eloquent

?我需要的:

我正在开发一个应用程序,Laravel 5.4并且我希望global scope它允许我根据创建它们的用户来过滤应用程序的不同元素。


我的全球范围:

我有一堂课BaseEloquentModel.phpEloquent我的所有模型都从该课扩展而来。我有global scope如下内容:

protected static function boot()
{
    parent::boot();
    static::addGlobalScope('', function(\Illuminate\Database\Eloquent\Builder $builder) use($userId) {
        /**
        * I get the name of the table with <code>(with(new static))->getTable()</code> 
        * and then filter the query for the <b>user_id</b> field
        */
        $builder->where(
            (with(new static))->getTable() . '.user_id', 
            '=',
            $userId
        );
    });
}
Run Code Online (Sandbox Code Playgroud)

?问题

当我有这样的查询,使用or运算符时,全局作用域被“中和”:

$qBuilderCars = Car::whereRaw("name like ? or id = ?", [
    '%' . $searchCriteria. '%',
    $searchCriteria
]);
Run Code Online (Sandbox Code Playgroud)

如果在toSql()上调用该方法 $qBuilderCars,则会看到它“正确”地AND运算符添加到查询的末尾。

select * from `cars` where name like ? or id = ? and user_id = ?
Run Code Online (Sandbox Code Playgroud)

也许您已经注意到了我的问题...在这种情况下cars,如果元素的生成器使用了OR运算符,则全局作用域将无济于事,因为之间没有括号where name like ? or id = ?。因此,产生的查询将类似于以下内容:

select * from `cars` where name like ? (or id = ? and user_id = ?)
Run Code Online (Sandbox Code Playgroud)

因此,此查询将返回所有名称匹配或ID为已接收并由用户创建的汽车。

当我需要的是:

select * from `cars` where (name like ? or id = ?) and user_id = ?
Run Code Online (Sandbox Code Playgroud)

我的尝试

我试图改变我的方法global scope,试图使AND我在查询中添加最严格的运算符,但没有成功。

我无法将括号手动添加到所有应用程序的查询中,所以...有没有办法将全局范围内的全局括号添加到builder


解决方案

解决方案是在所有原始查询中添加括号。

  • ?? 您可以看到我认为最成功的@Styx解决方案

  • 我还将留下我的答案,该答案直接在中起作用global scope,并且我认为有趣的是能够看到\Illuminate\Database\Eloquent\Builder对象的工作原理

Sty*_*tyx 2

好吧,看来您添加括号的解决方案是最好的解决方法,但我有一个建议,如何做稍微更好的方法。

  1. 创建新类QueryBuilder。例如,在\App\Models\命名空间(app/Models/文件夹)中:

    namespace App\Models;
    
    use Illuminate\Database\Query\Builder as EloquentQueryBuilder;
    
    class QueryBuilder extends EloquentQueryBuilder {
    
      public function whereRaw($sql, $bindings = [], $boolean = 'and')
      {
        return parent::whereRaw('('.$sql.')', $bindings, $boolean);
      }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将此代码添加到您的BaseEloquentModel类中:

    use Illuminate\Database\Eloquent\Model;
    use App\Models\QueryBuilder; // <-- addition
    
    class BaseEloquentModel extends Model {
      // ...
      protected function newBaseQueryBuilder()
      {
        $connection = $this->getConnection();
    
        return new QueryBuilder(
            $connection,
            $connection->getQueryGrammar(),
            $connection->getPostProcessor()
        );
      }
      // ...
    }
    
    Run Code Online (Sandbox Code Playgroud)

现在,所有whereRaw()调用都会自动在查询周​​围添加括号。