怎么写一个属于自己的数据库封装(3)时间:2025/3/12作者:未知来源:盾怪网教程人气: [摘要]is_null($value)) list($operator, $value) = [=, $operator];// 最简单原始的条件查询, 所以$type值为Basic$type = &quo... is_null($value)) list($operator, $value) = ['=', $operator];
// 最简单原始的条件查询, 所以$type值为Basic
$type = "Basic";
// 将处理过的条件存入$wheres
$this->wheres[] = compact('type', 'column', 'operator', 'value', 'boolean');
// 将字段需要匹配的值存入$bindings中的where
$this->addBinding($value, 'where');
}
// 返回Builder实例
return $this;
} where的各种变形 // 所有逻辑同where(), 不过这是or where
public function orWhere($column, $operator = null, $value = null) {
return $this->where($column, $operator, $value, 'or');
}
/**
* where in 语法, 字段匹配多个值
* @param string $column 字段
* @param array $values 一组字段需匹配的值
* @param string $boolean 默认为and
* @param boolean $not 默认为假, 真为排除所有$value里的数据
* @return Builder 返回Builder实例
*/
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(), 不过这是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');
}
/**
* where $coulmn is null 语法, 搜索字段为空的数据
* @param string $column 字段
* @param string $boolean 默认为and
* @param boolean $not 默认为假, 真为排除所有字段为空的数据
* @return Builder 返回Builder实例
*/
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(), 不过这是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');
}orderBy - order by 语句, 决定返回的数据排列 /**
* @param string/array $column 字段
* @param string $direction 排序,默认为asc, 顺序
* @return Builder 返回Builder实例
*/
public function orderBy($column, $direction = 'asc') {
// 局限性在于必须声明顺序或逆序
if(is_array($column)) {
foreach ($column as $key => $value)
$this->orderBy($key, $value);
}else {
// 简单判定后直接存入$orders, $direction输入错误不跳错误直接选择顺序
$this->orders[] = [
'column' => $column,
'direction' => strtolower($direction) == 'desc' ? 'desc' : 'asc',
];
}
// 返回Builder实例
return $this;
}辅助函数 - array_flatten这是我自己写的一个函数, 用于抚平多维数组 什么意思呢, 就是将多维数组整成一维数组 function array_flatten(array $array) {
$return = array();
array_walk_recursive($array, function($a) use (&$return) {
$return[] = $a;
});
return $return;
}例子 $a = array('this', 'is', array('a', 'multidimentional', array('array') ), 'to', 'make', 'the', 'tutotal', array('more', 'easier', 'to', 'understand') );
dd(array_flatten($a));返回 array (size=13)
0 => string 'this' (length=4)
1 => string 'is' (length=2)
2 => string 'a' (length=1)
3 => string 'multidimentional' (length=16)
4 => string 'array' (length=5)
5 => string 'to' (length=2)
6 => string 'make' (length=4)
7 => string 'the' (length=3)
8 => string 'tutotal' (length=7)
9 => string 'more' (length=4)
10 => string 'easier' (length=6)
11 => string 'to' (length=2)
12 => string 'understand' (length=10) groupBy - group by 语句, 整合数据 /**
* @param string/array $groups 字段
* @return Builder 返回Builder实例
*/
public function groupBy(...$groups) {
if(empty($this->groups)) $this->groups = [];
$this->groups = array_merge($this->groups, array_flatten($groups));
// 返回Builder实例
return $this;
}limit - 限制返回的数据量, sqlsrv的写法不同, 有兴趣知道的可以留言 public function limit($value) {
// 如果$value大于零这条函数才生效
if ($value >= 0) $this->limit = $value;
return $this;
}
// limit函数的别名, 增加函数链的可读性
public function take($value) {
return $this->limit($value);
}offset - 跳过指定的数据量, sqlsrv的写法不同, 有兴趣知道的可以留言 public function offset($value) {
// 如果$value大于零这条函数才生效
if ($value >= 0) $this->offset = $value;
return $this;
}
// offset函数的别名, 增加函数链的可读性
public function skip($value) {
return $this->offset($value);
}get - 读取数据库数据 重头戏来了, 之前所有的函数都是返回Builder实例的,这意味着可再编辑 这是一个总结, 将前端的函数链请求一并处理了 // 返回一组数据库数据, 可以在这里设定想返回的字段, 但是select()的优先度最高
public function get($columns = ['*']) {
// 如果Builder的$columns依然为空, 那么就用该函数的$columns, 反之则使用select()所声明的字段
if (is_null($this->columns)) $this->columns = $columns;
// 如果Builder的$orders依然为空, 那么就默认第一个字段顺序
// 发现一个莫名的bug, 可能是我理解错了, 不加 order by 1数据返回竟然不是按照主键(第一个字段)排序
// 所以以防万一加一个默认
if (is_null($this->orders)) $this->orderBy(1);
// 将Grammar类生成的语句,和处理过的字段所对应的值,都交给Connector类, 让它与数据库进行通信,返回数据
// 注意这里的三个函数
// read() 不用说Connector篇介绍过了
// compileSelect()是用来编译生成查询语句
// getBindings()用来获取收在$bindings中条件的值, 下方会有说明
$results = $this->connector->read($this->grammar->compileSelect($this), $this->getBindings());
// 返回一组数据库数据,如果查询为空,返回空数组
// cast()下方会有说明
return $this->cast($results);
}
// get函数的别名, 增加函数链的可读性
public function all($columns = ['*']) {
return $this->get($columns);
}getBindings - 返回所有$bindings中的值 public function getBindings() {
// 抚平多维数组成一维数组后再返回
return array_flatten($this->bindings);
}cast - 转化返回的数据类型为自身的Model子类 在基本思路篇的最终效果小节有提到过数据的可操作性, 核心代码就是这里 如果看不明白这里没关系, 暂时跳过, 等看完Model.php就能理解了(吧?) public function cast($results){
// 获取Model子类的名称
$class = get_class($this->model);
// 新建一个Model子类
$model = new $class();
// 如果获得的数据库数据是数组
if (gettype($results)=="array") {
$arr = [];
// 循环数据
foreach ($results as $result)
// 再调用本函数
$arr[] = $this->cast($result);
// 返回经过转化的数据数组
return $arr;
// 如果获得的数据库数据是对象
}elseif(gettype($results)=="object"){
// 存入数据对象
$model->setData($results);
// 加入主键或unique key以实现数据的可操作性
// 如果表存在主键和返回的数据中有主键的字段
if($model->getIdentity() && isset($results->{$model->getIdentity()})) {
$model->where($model->getIdentity(), $results->{$model->getIdentity()});
// 如果表存在unique key和返回的数据中有unique key的字段
}elseif($model->getUnique() && array_check($model->getUnique(),$results)) {
foreach ($model->getUnique() as $key)
$model->where($key, $results->$key);
// 改写和删除操作仅仅在符合以上两种条件其中之一的时候
// 反之, 开启写保护不允许改写
}else {
// 其实还可以考虑直接复制query
// 但变数太多干脆直接一棍子打死
$model->getBuilder()->writeLock = true;
}
// 返回该实例
return $model;
}
// 如果转化失败返回false
return false;
}first - 仅取头一条数据, 所以返回的是对象, 而get返回的是数组,里头多条对象 /**
* @param array $columns 如果Builder的$columns依然为空, 那么就用该函数的$columns, 反之则使用select()所声明的字段
* @return boolean/Model 查询为空返回false, 反之则返回附带数据的表类
*/
public function first($columns = ['*']) {
$results = $this->take(1)->get($columns);
return empty($results) ? false : $results[0];
}setModel - 设置Model实例 public function setModel(Model $model) {
$this->model = $model;
return $this;
}告一段落, 接下来编译SQL语句, Grammar类
Grammar.php<?php
/**
* 数据库语法生成
*/
class Grammar {
// 构建查询语句所可能出现的各种SQL语法
// 注意, 它们的顺序是对应着各自在SQL语句中合法的位置
// sqlsrv略微不同
protected $selectComponents = [
'distinct',
'columns',
'from',
'joins',
'wheres',
'groups',
'orders',
'limit',
'offset',
];concatenate - 排除编译后可能存在空的值,然后连接整句SQL语句 protected function concatenate($segments) {
return implode(' ', array_filter($segments, function ($value) {
return (string) $value !== '';
}));
}compileSelect - 编译SQL查询语句 // 还记得Builder->get()中的compileSelect()吗?
public function compileSelect(Builder $query) {
// concatenate()排除编译后可能存在空的值,然后连接整句SQL语句
// 去掉可能存在的前后端空格再返回
return trim($this->concatenate($this->compileComponents($query)));
}compileComponents - 循环$selectComponents, 根据不同的语法局部编译对应的语句 protected function compileComponents(Builder $query) {
$sql = [];
// 循环$selectComponents
foreach ($this->selectComponents as $component) {
// 如果Builder实例中对应的函数曾经被调用,那意味着对应的语法非空
if (!is_null($query->$component)) {
$method = 'compile'.ucfirst($component);
// 编译该语法并将之收入$sql
$sql[$component] = $this->$method($query, $query->$component);
}
}
// 返回$sql数组
return $sql;
}compileDistinct - 编译distinct语句 protected function compileDistinct(Builder $query, $distinct) {
return $distinct ? 'select distinct' : 'select';
}compileLimit - 编译limit语句 protected function compileLimit(Builder $query, $limit) {
return "limit $limit";
}compileOffset - 编译offset语句 protected function compileOffset(Builder $query, $offset) {
return "offset $offset";
}compileColumns - 编译需查询的字段 protected function compileColumns(Builder $query, $columns) {
return implode(', ', $columns);
}compileFrom - 编译生成表名 protected function compileFrom(Builder $query, $table) {
return 'from '.$table;
}compileJoins - 编译join语句 protected function compileJoins(Builder $query, $joins) {
$sql = [];
foreach ($joins as $join) {
// 如果存在多个副键和主键
if(is_array($join->foregin) && is_array($join->primary)) {
$on = [];
// 循环键的数量, 将之与对应的主键组合
for($i=0; $i<sizeof($join->foregin); $i++)
$on[] = $join->foregin[$i]." = ".$join->primary[$i];
// 最后再将所有句子用and连接
$on = implode(' and ', $on);
} else {
//反之, 直接连接即可
$on = "$join->foregin = $join->primary";
}
// 附上join的类型和副表
$sql[] = trim("{$join->type} join {$join->table} on $on");
}
// 连接再返回
return implode(' ', $sql);
}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 关键词:如何写一个属于自己的数据库封装(3) | |