ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
我使用 Laravel 开发已经半年多,两个项目。Laravel 完善的文档,以及强大的社区,几乎可以应付所有疑问,这使我很少去看低层代码实现逻辑。尽管能很好的实现逻辑,但是对代码的把控还不是很好,就在前几天,我需要写一个带有 OR 条件的查询,当时让我煞费苦心,我搜索了很多信息,都没有查到,所有关于稍微复杂一点的 OR 查询都是在讲解这个匿名函数实现: ~~~php DB::table('users') ->where('name', '=', 'John') ->where(function ($query) { $query->where('votes', '>', 100) ->orWhere('title', '=', 'Admin'); }) ->get(); ~~~ 而这并不能满足我,也许是我的关键词不对,没有找到那个完美的答案,并且我想要更多更灵活的方式。 我要实现的 SQL 大概是这样的: ~~~sql SELECT * FROM user WHERE group_id = 'group id' AND ( name = 'name' OR mobile_number = 'mobile number' OR email = 'email' OR `score` > 1000 ) ~~~ 这是一类很常见的业务逻辑,可能你会觉得很简单,我也知道怎么去实现: ~~~php DB::table('users') ->where('group_id', 'group id') ->where(function ($query) { $query->where('name', 'name') ->orWhere('mobile_number', '=', 'mobile number') ->orWhere('email', '=', 'email') ->orWhere('score', '>', '1000'); }) ->get(); ~~~ 但是实际的逻辑并不是这样的,我还需要去判断是否有对应的参数,才需要把对应查询条件写入,就像这样: ~~~php DB::table('users') ->where('group_id', 'group id') ->where(function ($query) { if ($params['name']) { $query->orWhere('name', $params['name']) } if ($params['mobile_number']) { $query->orWhere('mobile_number', $params['mobile_number']) } if ($params['email']) { $query->orWhere('email', $params['email']) } if ($params['score']) { $query->orWhere('score', '>', $params['score']) } }) ->get(); ~~~ 我知道这可行,一直都是这样写的,但我觉得强大的 Laravel 肯定不会仅仅提供这种方式,于是我决定看一眼低层代码,很幸运,我有新的发现: ~~~php /** * Add a basic where clause to the query. * * @param \Closure|string|array $column * @param mixed $operator * @param mixed $value * @param string $boolean * @return $this */ public function where($column, $operator = null, $value = null, $boolean = 'and') { // If the column is an array, we will assume it is an array of key-value pairs // and can add them each as a where clause. We will maintain the boolean we // received when the method was called and pass it into the nested where. if (is_array($column)) { return $this->addArrayOfWheres($column, $boolean); } // Rest of code ... } /** * Add an array of where clauses to the query. * * @param array $column * @param string $boolean * @param string $method * @return $this */ protected function addArrayOfWheres($column, $boolean, $method = 'where') { return $this->whereNested(function ($query) use ($column, $method, $boolean) { foreach ($column as $key => $value) { if (is_numeric($key) && is_array($value)) { $query->{$method}(...array_values($value)); } else { $query->$method($key, '=', $value, $boolean); } } }, $boolean); } ~~~ `where`方法中,我只保留了需要重点关注的一段代码,如果条件满足,将会进入`addArrayOfWheres`方法,在这里解析以数组方式传递进来的条件参数,并且该组条件会被分组,也就是会用`()`包起来。有两种方式可以让查询条件分组,一是数组传参,二是匿名函数。类似我这种 OR 条件的查询,关键的就是让查询正确分组。 另外一个关键代码: ~~~php if (is_numeric($key) && is_array($value)) { $query->{$method}(...array_values($value)); } ~~~ 数组参数会被展开,看到这个,我想我的代码可以写成这样: ~~~php $orWhere = []; if ($params['name']) { $orWhere[] = ['name', '=', $params['name'], 'OR']; } if ($params['mobile_number']) { $orWhere[] = ['mobile_number', '=', $params['mobile_number'], 'OR']; } if ($params['email']) { $orWhere[] = ['email', '=', $params['email'], 'OR']; } if ($params['score']) { $orWhere[] = ['score', '>', $params['score'], 'OR']; } DB::table('users') ->where('group_id', 'group id') ->where($orWhere) ->get(); ~~~ `$orWhere`会被分组且被`...array_values($value)`展开。 也可以这样: ~~~php $orWhere = []; if ($params['name']) { $orWhere['name'] = $params['name']; } if ($params['mobile_number']) { $orWhere['mobile_number'] = $params['mobile_number']; } if ($params['email']) { $orWhere['email'] = $params['email']; } if ($params['score']) { $orWhere[] = ['score', '>', 1000, 'OR']; } DB::table('users') ->where('group_id', 'group id') ->where(function ($query) use ($orWhere) { $query->orWhere($orWhere); }) ->get(); ~~~ 最终的 sql 是这样的 ~~~sql select * from `users` where `group_id` = 'group id' and ( ( `name` = 'name' or `mobile_number` = 'mobile number' or `email` = 'email' OR `score` > 1000 ) ) ~~~ 虽然很多人都知道这个,我还是希望这能带来些许启发。