上一期 如何写一个属于自己的数据库封装(5) - 查询 - JOIN篇
下一期 如何写一个属于自己的数据库封装(7) - UPDATE篇
开始之前
WHERE 篇中, where 的各种应用方式都来自 Laravel
本篇将在 Builder.php 的每个 where 函数最后边给出例子方便理解
Builder.php
- where - 基本的 where 用法
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();
- whereNested - 实现嵌套式的 where 语句
whereNested() 仅供内部函数使用以实现闭包函数内的 where 子句, 所以没有例子
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;
}
- orWhere - 所有逻辑同where(), 不过这是or where
例子参照上面的 where 函数
public function orWhere($column, $operator = null, $value = null) {
return $this->where($column, $operator, $value, 'or');
}
- whereBetween - 字段匹配限定范围内的值
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 的各种变体
// 所有逻辑同 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');
}
- whereIn - 字段匹配多个值
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 的各种变体
// 所有逻辑同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');
}
- whereNull - where $coulmn is null 语法, 搜索字段为空的数据
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 的各种变体
// 所有逻辑同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');
}
- whereColumn - 用于比较字段, 比方说筛选出支出大于收入的记录
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 的变体
// 所有逻辑同 whereColumn(), 不过这是 or where $column1 compare $column2
public function orWhereColumn($first, $operator = null, $second = null) {
return $this->whereColumn($first, $operator, $second, 'or');
}
Grammar.php
配合注释味道更佳
- compileWheres - 编译where语句
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);
}
- whereBasic - 编译最基本的 where 用法
protected function whereBasic(Builder $query, $where) {
// 检测 $where[column] 是否存在小数点
// 是, 就意味着前缀已经附带了表名
// 否, 为之后的字段添上表名
// 因为 join 存在副表, 所以部分 $where 可能有附带表名, 这时候就不用添加了
$table = !preg_match('/\./', $where['column']) ? $query->from."." : '';
// 返回添上表名的字段,和表达式, 再一个问号
// 为何使用问号而不是:变量名? 因为:变量名存在太多局限性,不能标点符号,不能数字开头
return $table.$where['column'].' '.$where['operator'].' ?';
}
- whereNested - 编译 where 嵌套分组用法
protected function whereNested(Builder $query, $where) {
return '('.str_replace('where ', '', $this->compileWheres($where['query'])).')';
}
- where between - 编译 where between 用法
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 ?';
}
- where in - 编译 where in 用法
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.')';
}
- where not in - 编译 where not in 用法
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.')';
}
- where null - 编译 where null 用法
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';
}
- where not null - 编译 where not 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';
}
- where column - 编译 where column 用法
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篇