[摘要]or /i, , implode(" ", $sql), 1);}whereBasic - 编译最基本的where用法 protected function whereBasi...
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'].' ?';
}whereIn - 编译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.')';
}whereNotIn - 编译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.')';
}whereNull - 编译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';
}whereNotNull - 编译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';
}compileGroups - 编译group by语句
protected function compileGroups(Builder $query, $groups) {
// 连接$groups, 返回group by语句
return 'group by '.implode(', ', $groups);
}compileOrders - 编译order by语句
protected function compileOrders(Builder $query, $orders) {
// 连接每一个$order与其$direction, 然后返回order by语句
return 'order by '.implode(', ', array_map(function ($order) {
return $order['column'].' '.$order['direction'];
}, $orders));
}Grammar告一段落, 接下来是入口文件, Model类, 一个魔术世界
Model.php
<?php
/**
* 入口文件, 数据库表的父类
*/
class Model {
// SQL命令构建器, Builder类
protected $builder;
// 数据库返回的数据存在这里
protected $data;
// 数据库表名, 选填, 默认为类名
protected $table;
// 主键, 二选一($unique)
protected $identity;
// unique key, 二选一($identity)
protected $unique;getTable - 获取数据库表名, 没有设置返回false
public function getTable() {
return isset($this->table) ? $this->table : false;
}getIdentity - 获取主键名, 没有返回假
public function getIdentity() {
return isset($this->identity) ? $this->identity : false;
}getUnique - 获取unique key名, 没有返回假
public function getUnique() {
// 检测是否存在unique key, 不存在返回假, 存在就在检查是否数组, 不是就装入数组再返回
return isset($this->unique) ? is_array($this->unique) ? $this->unique : [$this->unique] : false;
}check - 检查必须预设的实例属性
public function check() {
// 如果数据库表的名称和Model的子类相同,可以选择不填,默认直接取类的名称
if(!$this->getTable())
$this->table = get_class($this);
// 跳出提醒必须设置$identity或$unique其中一项
if(!$this->getIdentity() && !$this->getUnique())
throw new Exception('One of $identity or $unique should be assigned in Model "'.get_called_class().'"');
}set/getBuilder - 设置或读取Builder实例
// 设置Builder实例
public function setBuilder(Builder $builder) {
$this->builder = $builder;
return $this;
}
// 获取Builder实例
public function getBuilder() {
return $this->builder;
}setData - 设置数据库数据
public function setData($data) {
$this->data = $data;
return $this;
}construct - 创建实例后的第一步
function construct() {
// 检查设定是否正确
$this->check();
// 新建一个Builder实例
$this->setBuilder(new Builder);
// 设置构建器的主表名称
$this->getBuilder()->from($this->table);
// 将Model实例带入Builder
$this->getBuilder()->setModel($this);
}魔术方法
callStatic - 如果找不到静态函数的时候自动运行下面的逻辑
static public function callStatic($method, $args = null) {
// 这是一个伪静态, 创建一个实例
$instance = new static;
// 在$instance->builder之中, 寻找函数$method, 并附上参数$args
return call_user_func_array([$instance->builder, $method], $args);
}call - 如果找不到函数的时候自动运行下面的逻辑
public function call($method, $args) {
// 在$this->builder之中, 寻找函数$method, 并附上参数$args
return call_user_func_array([$this->builder, $method], $args);
}debugInfo - 在调试的时候隐藏多余的信息, 只留下数据库返回的数据
public function debugInfo() {
// 也不懂算不算bug, 该方法强制要求返回的数据类型必须是array数组
// 但是就算我强行转换(casting)后返回的数据依然是对象(object)
return (array)$this->data;
}get - 当调用对象的属性时, 强制调用这个魔术方法
// 为了避免局外人可以访问Model类的属性
// 为了避免对象属性和表的字段名字相同
public function get($field) {
// 如果调用的属性是Model类内的逻辑
// 直接返回该属性的值
if(get_called_class()==="Model")
return $this->$field;
// 反之, 则检查$data内是否存在该属性, 没有的话跳出错误
if(!isset($this->data->$field))
throw new Exception("column '$field' is not exists in table '$this->table'");
// 如果存在,由于返回的数据都是存在$data里, 所以要这样调用
return $this->data->$field;
}set - 当想修改的对象属性时, 强制调用这个魔术方法
public function set($field, $value) {
// 如果调用的属性是Model类内的逻辑
// 直接赋值该属性
if(get_called_class()==="Model")
return $this->$field = $value;
// 反之, 则检查$data内是否存在该属性, 没有的话跳出错误
if(!isset($this->data->$field))
throw new Exception("column '$field' is not exists in table '$this->table'");
// 如果存在,由于返回的数据都是存在$data里, 所以要这样赋值
return $this->data->$field = $value;
}进阶的Buider类封装没错, 我又把Builder(微)封装了
find - 使用主键查寻数据
/**
* @param int $id 主键
* @return Model subclass 返回一个Model的子类数据
*/
public static function find($id) {
// 这是一个伪静态, 创建一个实例
$self = new static;
// 该函数只适用于设置了主键的表, 如果没有设置, 跳出错误
if(!$self->getIdentity())
throw new Exception("Table's identity key should be assgined");
return $self->where($self->identity, $id)->first();
}还有更多, 看心情更...
本期疑问
1.) 缺少的Builder函数如果有人愿意提供例子就好了, 进阶复杂的语句那就更好了, 直接写然后再分享给我那就最好了
2.) 有些函数或结构可能没有效率或者白白添加服务器压力, 但我写的顺了可能没看见, 请指出
3.) 有人能告诉我laravel是怎么解决pdo 2100个最大参数(bind)的问题吗? 源代码把我看蒙了
以上就是如何写一个属于自己的数据库封装(3)的详细内容,更多请关注php中文网其它相关文章!
学习教程快速掌握从入门到精通的SQL知识。
关键词:如何写一个属于自己的数据库封装(3)