Laravel 的 where or 查询
我使用 Laravel 开发已经半年多,两个项目。Laravel 完善的文档,以及强大的社区,几乎可以应付所有疑问,这使我很少去看低层代码实现逻辑。尽管能很好的实现逻辑,但是对代码的把控还不是很好,就在前几天,我需要写一个带有 OR 条件的查询,当时让我煞费苦心,我搜索了很多信息,都没有查到,所有关于稍微复杂一点的 OR 查询都是在讲解这个匿名函数实现:
DB::table('users')
->where('name', '=', 'John')
->where(function ($query) {
$query->where('votes', '>', 100)
->orWhere('title', '=', 'Admin');
})
->get();
而这并不能满足我,也许是我的关键词不对,没有找到那个完美的答案,并且我想要更多更灵活的方式。
我要实现的 SQL 大概是这样的:
SELECT * FROM user
WHERE
group_id = 'group id'
AND (
name = 'name'
OR mobile_number = 'mobile number'
OR email = 'email'
OR `score` > 1000
)
这是一类很常见的业务逻辑,可能你会觉得很简单,我也知道怎么去实现:
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();
但是实际的逻辑并不是这样的,我还需要去判断是否有对应的参数,才需要把对应查询条件写入,就像这样:
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 肯定不会仅仅提供这种方式,于是我决定看一眼低层代码,很幸运,我有新的发现:
/**
* 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 条件的查询,关键的就是让查询正确分组。
另外一个关键代码:
if (is_numeric($key) && is_array($value)) {
$query->{$method}(...array_values($value));
}
数组参数会被展开,看到这个,我想我的代码可以写成这样:
$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)
展开。
也可以这样:
$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 是这样的
select
*
from
`users`
where
`group_id` = 'group id'
and (
(
`name` = 'name'
or `mobile_number` = 'mobile number'
or `email` = 'email'
OR `score` > 1000
)
)
虽然很多人都知道这个,我还是希望这能带来些许启发。
那么还有没有更多更灵活的方式呢?
本作品采用《CC 协议》,转载必须注明作者和本文链接
这个写法也可以吧
我是用这种方式处理的 看起来会更直观
写法好几种 哪种可读性更强就用哪种 我一般用这种
这种写法仿佛看到了ThinkPHP,一般就用普通的回调函数写法,也是文档上介绍的写法。
这样写看起来很简洁,但实际可读性不高,不利于中大型项目,无形中增加了代码量。其次我个人认为,DB的写法偏TP5。而写出优雅易于理解的代码,远远要好于看起来炫酷,理解费时费力的方法。Eloquent ORM 这个可以去了解一下,相对TP5的模型,laravel这方面做得实在好太多了。感觉你把一个简单的查询写复杂了,如果稍微复杂一点,不知道你会怎么去写
这种新写法也不错,简洁明了
不错,我喜欢用数组方式