• 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
    )
    )
    虽然很多人都知道这个,我还是希望这能带来些许启发。

    那么还有没有更多更灵活的方式呢?

    ————————————————
    原文作者:chuoke
    转自链接:https://learnku.com/articles/36743
    版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请保留以上作者信息和原文链接。

  • 相关阅读:
    【BZOJ2959】—长跑(LCT维护双连通分量+并查集)
    【BZOJ5394】【Ynoi2016】—炸脖龙(树状数组+广义欧拉定理)
    【BZOJ2588】【Spoj10628】—Count on a tree(主席树)
    SCOI2019爆零记+总结反思
    【SCOI2019】—DAY1T1平台跳跃(打表+高精度)
    省选模板复习—【字符串】
    省选模板复习—【数据结构】
    【BZOJ3572】【HNOI2014】—世界树(虚树+倍增+dp)
    python paramiko 模块
    python sys模块
  • 原文地址:https://www.cnblogs.com/xiami2046/p/13415930.html
Copyright © 2020-2023  润新知