如何写一个属于自己的数据库封装(6) - 查询 - WHERE篇

2019-10-13  本文已影响0人  梅先森森森森森森

上一期 如何写一个属于自己的数据库封装(5) - 查询 - JOIN篇
下一期 如何写一个属于自己的数据库封装(7) - UPDATE篇

开始之前

WHERE 篇中, where 的各种应用方式都来自 Laravel
本篇将在 Builder.php 的每个 where 函数最后边给出例子方便理解


Builder.php

where 函数用法多样, 请参考例子

public function where($column, $operator = null, $value = null, $boolean = 'and') {
        // 判定$column是否为数组
        if (is_array($column)) {
            // 如果是数组, 循环调用 where()
            foreach ($column as $condition) {
                // 一组只有两个参数意味着 $operator 被省略了, 自动加上默认的 '='
                if (sizeof($condition)===2)
                    list($condition[1], $condition[2]) = ['=', $condition[1]];

                // 不存在第四个参数意味着这是默认的 and 条件
                if(!isset($condition[3]))
                    $condition[3] = $boolean;

                // 这种调用方式必须提供最少2种参数, $columns, $operator, $value, $boolean可以选择默认, '='
                $this->where($condition[0], $condition[1], $condition[2], $condition[3]);
            }
        // 判定$column是否为闭包函数
        }elseif ($column instanceof Closure) {
            // 如果是, 调用 whereNested()
            // whereNested() 实现了高级 where 语句, 开发者可以实现的 SQL 语句 如下
            // select * from `actor` where `actor_id` = '5' or (`actor_id` = '6' and `first_name` != 'JACK')
            return $this->whereNested($column, $boolean);
        }else {
            // 反之, 判定参数数量和$value是否为空, 如果是,这意味着用户省略了'=',自动添加
            if(func_num_args() == 2 || is_null($value))
                list($operator, $value) = ['=', $operator];

            // 最简单原始的条件查询, 所以 $type 值为Basic
            $type = "Basic";
            // column 如果没有附上表名, 那就自动加上
            if (!preg_match('/\./', $column))
                $column = $this->model->getTable().".$column";
            // 将处理过的条件存入 $wheres
            $this->wheres[] = compact('type', 'column', 'operator', 'value', 'boolean');
            // 将字段需要匹配的值存入 $bindings中的 where
            $this->addBinding($value, 'where');
        }
        // 返回 Builder 实例
        return $this;
    }


WHERE 的例子

1.以参数的方式 - 选择默认 '='

Actor::where('first_name', 'PENELOPE')->get();

2.以参数的方式 - 提供运算符

Actor::where('first_name', '!=', 'PENELOPE')->get();

3.以数组的方式 - 允许省略运算符和提供运算符两种方式混合使用

Actor::where([
            ['first_name', '!=', 'PENELOPE'],
            ['last_name', 'CHASE']
        ])->get();

4.允许多次调用

Actor::where('first_name', '!=', 'PENELOPE')
    ->where('last_name', 'CHASE')
    ->get();

5.以闭包函数来调用子句

select * from Actor where Actor.first_name = 'PENELOPE' and (Actor.last_name = 'PINKETT' or Actor.last_name = 'CRONYN')

Actor::where('first_name', 'PENELOPE')
    ->where(function ($builder) {
        $builder->where('last_name', 'PINKETT')
                ->orWhere('last_name', 'CRONYN'); // orWhere() 将在下方给出
    })
    ->get();


    public function whereNested(Closure $callback, $boolean = 'and') {
        // 创建一个新的 Builder 实例, 然后在闭包函数内应用它
        call_user_func($callback, $query = (new static)->from($this->from));

        // 这是一个嵌套
        $type = 'Nested';

        // 处理过的条件存入 $wheres
        $this->wheres[] = compact('type', 'query', 'boolean');

        // 将字段需要匹配的值存入 $bindings中的 where
        $this->addBinding($query->getBindings(), 'where');

        // 返回 Builder 实例
        return $this;
    }

    public function orWhere($column, $operator = null, $value = null) {
        return $this->where($column, $operator, $value, 'or');
    }

    public function whereBetween($column, array $values, $boolean = 'and', $not = false) {
        // 类型是 between
        $type = 'Between';

        // 处理过的条件存入 $wheres
        $this->wheres[] = compact('column', 'type', 'boolean', 'not');

        // 将字段需要匹配的值存入 $bindings中的 where
        $this->addBinding($values, 'where');

        // 返回 Builder 实例
        return $this;
    }


whereBetween 的例子

select * from Actor where Actor.actor_id between 5 and 8

Actor::whereBetween('actor_id', [5,8])->get();


    // 所有逻辑同 whereBetween(), 不过这是 or where between
    public function orWhereBetween($column, array $values) {
        return $this->whereBetween($column, $values, 'or');
    }

    // 所有逻辑同 whereBetween(), 不过这是 where not between
    public function whereNotBetween($column, array $values, $boolean = 'and')    {
        return $this->whereBetween($column, $values, $boolean, true);
    }

    // 所有逻辑同 whereNotBetween(), 不过这是 or where not between
    public function orWhereNotBetween($column, array $values)    {
        return $this->whereNotBetween($column, $values, 'or');
    }

    public function whereIn($column, $values, $boolean = 'and', $not = false) {
        // 判定条件查询的类型, false = where in ($value),true = where not in ($value)
        $type = $not ? 'NotIn' : 'In';
        // 将条件存入$wheres
        $this->wheres[] = compact('type', 'column', 'values', 'boolean');
        // 循环将字段需要匹配的值存入$bindings中的where
        foreach ($values as $value)
            $this->addBinding($value, 'where');

        // 返回Builder实例
        return $this;
    }


whereIn 的例子

select * from Actor where Actor.actor_id in (5, 6, 8)

Actor::whereIn('actor_id', [5, 6, 8])->get();


    // 所有逻辑同whereIn(), 不过这是or where in
    public function orWhereIn($column, $values) {
        return $this->whereIn($column, $values, 'or');
    }

    // 所有逻辑同whereIn(), 不过这是and where not in
    public function whereNotIn($column, $values, $boolean = 'and') {
        return $this->whereIn($column, $values, $boolean, true);
    }

    // 所有逻辑同whereNotIn(), 不过这是or where not in
    public function orWhereNotIn($column, $values) {
        return $this->whereNotIn($column, $values, 'or');
    }

    public function whereNull($column, $boolean = 'and', $not = false) {
        // 判定条件查询的类型, false = where $column is null,true = where $column is not null
        $type = $not ? 'NotNull' : 'Null';
        // 将条件存入 $wheres
        $this->wheres[] = compact('type', 'column', 'boolean');
        // 返回 Builder 实例
        return $this;
    }


whereNull 的例子

select * from Actor where Actor.last_name is null

Actor::whereNull('last_name')->get();


    // 所有逻辑同whereNull(), 不过这是or where $column is null
    public function orWhereNull($column) {
        return $this->whereNull($column, 'or');
    }

    // 所有逻辑同whereNull(), 不过这是and where $column is not null
    public function whereNotNull($column, $boolean = 'and') {
        return $this->whereNull($column, $boolean, true);
    }

    // 所有逻辑同whereNotNull(), 不过这是or where $column is not null
    public function orWhereNotNull($column) {
        return $this->whereNotNull($column, 'or');
    }

    public function whereColumn($first, $operator = null, $second = null, $boolean = 'and') {
        // 判定$column是否为数组
        if (is_array($first)) {
            // 如果是数组, 循环调用 whereColumn()
            foreach ($first as $condition) {
                // 一组只有两个参数意味着 $operator 被省略了, 自动加上默认的 '='
                if (sizeof($condition)===2)
                    list($condition[1], $condition[2]) = ['=', $condition[1]];

                // 不存在第四个参数意味着这是默认的 and 条件
                if(!isset($condition[3]))
                    $condition[3] = $boolean;

                // 这种调用方式必须提供最少2种参数, $columns, $operator, $value, $boolean可以选择默认, '='
                $this->whereColumn($condition[0], $condition[1], $condition[2], $condition[3]);
            }
        }else {
            // 反之, 判定参数数量和 $second 是否为空, 如果是,这意味着用户省略了'=',自动添加
            if(func_num_args() == 2 || is_null($second))
                list($operator, $second) = ['=', $operator];

            // 类型为 Column
            $type = 'Column';

            // 将条件存入 $wheres
            $this->wheres[] = compact(
                'type', 'first', 'operator', 'second', 'boolean'
            );
        }
        // 返回 Builder 实例
        return $this;
    }


whereColumn 的例子

select * from Actor where Actor.first_name = Actor.last_name

Actor::whereColumn('first_name', '=', 'last_name')->get()

或省略 '='

Actor::whereColumn('first_name', 'last_name')->get()


    // 所有逻辑同 whereColumn(), 不过这是 or where $column1 compare $column2
    public function orWhereColumn($first, $operator = null, $second = null) {
        return $this->whereColumn($first, $operator, $second, 'or');
    }


Grammar.php

配合注释味道更佳

    protected function compileWheres(Builder $query) {
        $sql = [];
        // 类似与compileComponents(), 循环Builder实例中的$wheres
        foreach ($query->wheres as $where) {
            // 根据不同的$type来进行编译
            $method = "where{$where['type']}";
            // 返回的部分语句先收入数组
            $sql[] = $where['boolean'].' '.$this->$method($query, $where);
        }
        // 最后将$sql数组连接起来, 删掉最前面的and或or在返回
        return 'where '.preg_replace('/and |or /i', '', implode(" ", $sql), 1);
    }

    protected function whereBasic(Builder $query, $where) {
        // 检测 $where[column] 是否存在小数点
        // 是, 就意味着前缀已经附带了表名
        // 否, 为之后的字段添上表名
        // 因为 join 存在副表, 所以部分 $where 可能有附带表名, 这时候就不用添加了
        $table = !preg_match('/\./', $where['column']) ? $query->from."." : '';
        // 返回添上表名的字段,和表达式, 再一个问号
        // 为何使用问号而不是:变量名? 因为:变量名存在太多局限性,不能标点符号,不能数字开头
        return $table.$where['column'].' '.$where['operator'].' ?';
    }

    protected function whereNested(Builder $query, $where) {
        return '('.str_replace('where ', '', $this->compileWheres($where['query'])).')';
    }

    protected function whereBetween(Builder $query, $where) {
        // 检测 $where[column] 是否存在小数点, 同理 whereBasic()
        $table = !preg_match('/\./', $where['column']) ? $query->from."." : '';
        // 检测 $not, 如果是 true, 添上 'not'
        $between = $where['not'] ? 'not between' : 'between';

        return $table.$where['column'].' '.$between.' ? and ?';
    }

    protected function whereIn(Builder $query, $where) {
        // 检测 $where[column] 是否存在小数点, 同理 whereBasic()
        $table = !preg_match('/\./', $where['column']) ? $query->from."." : '';
        // 有多少个匹配值那就连接多少个问号
        $values = implode(', ', array_fill(0, sizeof($where['values']), '?'));
        // 返回 where in 语句
        return $table.$where['column'].' in ('.$values.')';
    }

    protected function whereNotIn(Builder $query, $where) {
        // 检测 $where[column] 是否存在小数点, 同理 whereBasic()
        $table = !preg_match('/\./', $where['column']) ? $query->from."." : '';
        // 有多少个匹配值那就连接多少个问号
        $values = implode(', ', array_fill(0, sizeof($where['values']), '?'));
        // 返回 where not in 语句
        return $table.$where['column'].' not in ('.$values.')';
    }

    protected function whereNull(Builder $query, $where) {
        // 检测 $where[column] 是否存在小数点, 同理 whereBasic()
        $table = !preg_match('/\./', $where['column']) ? $query->from."." : '';
        // 返回 where is null 语句
        return $table.$where['column'].' is null';
    }

    protected function whereNotNull(Builder $query, $where) {
        // 检测 $where[column] 是否存在小数点, 同理 whereBasic()
        $table = !preg_match('/\./', $where['column']) ? $query->from."." : '';
        // 返回where is not null 语句
        return $table.$where['column'].' is not null';
    }

    protected function whereColumn(Builder $query, $where) {
        // 检测 $where[column] 是否存在小数点, 同理 whereBasic()
        $table = !preg_match('/\./', $where['first']) ? $query->from."." : '';

        // 为了避免开发者疏忽, $first 和 $second 其中一个没有加上前缀
        if($table !== '')
            $where['second'] = str_replace("$table.", '', $where['second']);

        return $table.$where['first'].' '.$where['operator'].' '.$table.$where['second'];
    }

完整代码

源代码放在coding.net里, 自己领

上一期 如何写一个属于自己的数据库封装(5) - 查询 - JOIN篇
下一期 如何写一个属于自己的数据库封装(7) - UPDATE篇

上一篇 下一篇

猜你喜欢

热点阅读