tp5源码分析之模型
数据模型(Model) 主要实现对单个数据表的操作,包括
数据表整体操作
数据表字段操作
数据表的数据操作
1-1 数据模型的创建
Model->__construct()
数据模型的构造函数,创建数据模型对象
public function __construct($data = [])
{
if (is_object($data)) {
$this->data = get_object_vars($data);
} else {
$this->data = $data;
}
// 当前类名
$this->class = get_class($this);
if (empty($this->name)) {
// 当前模型名
$this->name = basename(str_replace("\", "/", $this->class));
}
if (is_null($this->autoWriteTimestamp)) {
// 自动写入时间戳
$this->autoWriteTimestamp = $this->db()->getConfig("auto_timestamp");
}
// 执行初始化操作
$this->initialize();
}
$model->initialize()
模型的初始化操作,使用标志位确保只进行一次初始化
protected function initialize()
{
$class = get_class($this);
if (!isset(static::$initialized[$class])) {
static::$initialized[$class] = true;
static::init();
}
}
Model::init()
数据表模型的初始化接口
protected static function init()
{}
1-2 数据库的连接
$model->db()
数据库连接 ,建立模型使用的数据库连接对象
public function db()
{
$model = $this->class;
if (!isset(self::$links[$model])) {
// 设置当前模型 确保查询返回模型对象
$query = Db::connect($this->connection)->model($model);
// 设置当前数据表和模型名
if (!empty($this->table)) {
$query->setTable($this->table);
} else {
$query->name($this->name);
}
if (!empty($this->field)) {
if (true === $this->field) {
$type = $query->getTableInfo("", "type");
} else {
$type = [];
foreach ((array) $this->field as $key => $val) {
if (is_int($key)) {
$key = $val;
$val = "varchar";
}
$type[$key] = $val;
}
}
$query->setFieldType($type);
$this->field = array_keys($type);
$query->allowField($this->field);
}
if (!empty($this->pk)) {
$query->pk($this->pk);
}
self::$links[$model] = $query;
}
// 返回当前模型的数据库查询对象
return self::$links[$model];
}
2-1数据表主键操作
$model->getPk()
获取模型对应数据表的主键
public function getPk($name = "")
{
if (!empty($name)) {
$table = $this->db()->getTable($name);
return $this->db()->getPk($table);
} elseif (empty($this->pk)) {
$this->pk = $this->db()->getPk();
}
return $this->pk;
}
$model->isPk()
判断一个字段是否为数据表的主键
protected function isPk($key)
{
$pk = $this->getPk();
if (is_string($pk) && $pk == $key) {
return true;
} elseif (is_array($pk) && in_array($key, $pk)) {
return true;
}
return false;
}
3-1 字段的输出控制
$model->hidden()
设置隐藏输出的字段
public function hidden($hidden = [])
{
$this->hidden = $hidden;
return $this;
}
$model->visible()
设置需要输出的字段
public function visible($visible = [])
{
$this->visible = $visible;
return $this;
}
$model->append()
设置追加输出的字段
public function append($append = [])
{
$this->append = $append;
return $this;
}
3-2 字段的写入操作
$model->allowField()
设置允许写入的字段
public function allowField($field)
{
if (true === $field) {
$field = $this->db()->getTableInfo("", "type");
$this->db()->setFieldType($field);
$field = array_keys($field);
}
$this->field = $field;
return $this;
}
$model->auto()
设置自动完成的字段
public function auto($fields)
{
$this->auto = $fields;
return $this;
}
$model->validate()
设置需要验证的字段
public function validate($rule = true, $msg = [])
{
if (is_array($rule)) {
$this->validate = [
"rule" => $rule,
"msg" => $msg,
];
} else {
$this->validate = true === $rule ? $this->name : $rule;
}
return $this;
}
4-1 获取或设置模型的数据
$model->data()
public function data($data, $value = null)
{
if (is_string($data)) {
$this->data[$data] = $value;
} else {
// 清空数据
$this->data = [];
if (is_object($data)) {
$data = get_object_vars($data);
}
if (true === $value) {
// 数据对象赋值
foreach ($data as $key => $value) {
$this->setAttr($key, $value, $data);
}
} else {
$this->data = $data;
}
}
return $this;
}
4-2 获取对象原始数据
$model->getData()
public function getData($name = null)
{
if (is_null($name)) {
return $this->data;
} elseif (array_key_exists($name, $this->data)) {
return $this->data[$name];
} else {
throw new InvalidArgumentException("property not exists:" . $this->class . "->" . $name);
}
}
4-3 数据转换
$model->toArray()
模型数据转换为数组格式
public function toArray()
{
$item = [];
//过滤属性
if (!empty($this->visible)) {
$data = array_intersect_key($this->data, array_flip($this->visible));
} elseif (!empty($this->hidden)) {
$data = array_diff_key($this->data, array_flip($this->hidden));
} else {
$data = $this->data;
}
foreach ($data as $key => $val) {
if ($val instanceof Model || $val instanceof Collection) {
// 关联模型对象
$item[$key] = $val->toArray();
} elseif (is_array($val) && reset($val) instanceof Model) {
// 关联模型数据集
$arr = [];
foreach ($val as $k => $value) {
$arr[$k] = $value->toArray();
}
$item[$key] = $arr;
} else {
// 模型属性
$item[$key] = $this->getAttr($key);
}
}
// 追加属性(必须定义获取器)
if (!empty($this->append)) {
foreach ($this->append as $name) {
$item[$name] = $this->getAttr($name);
}
}
return !empty($item) ? $item : [];
}
$model->toJson()
模型数据转换为json格式
public function toJson($options = JSON_UNESCAPED_UNICODE)
{
return json_encode($this->toArray(), $options);
}
$model-readTransform()
数据读取时 类型自动转换
protected function readTransform($value, $type)
{
if (is_array($type)) {
list($type, $param) = $type;
} elseif (strpos($type, ":")) {
list($type, $param) = explode(":", $type, 2);
}
switch ($type) {
case "integer":
$value = (int) $value;
break;
case "float":
if (empty($param)) {
$value = (float) $value;
} else {
$value = (float) number_format($value, $param);
}
break;
case "boolean":
$value = (bool) $value;
break;
case "timestamp":
$format = !empty($param) ? $param : $this->dateFormat;
$value = date($format, $value);
break;
case "datetime":
$format = !empty($param) ? $param : $this->dateFormat;
$value = date($format, strtotime($value));
break;
case "json":
$value = json_decode($value, true);
break;
case "array":
$value = is_null($value) ? [] : json_decode($value, true);
break;
case "object":
$value = empty($value) ? new stdClass() : json_decode($value);
break;
case "serialize":
$value = unserialize($value);
break;
}
return $value;
}
$model->getAttr()
数据获取 自动转换获取器
public function getAttr($name)
{
try {
$notFound = false;
$value = $this->getData($name);
} catch (InvalidArgumentException $e) {
$notFound = true;
$value = null;
}
// 检测属性获取器
$method = "get" . Loader::parseName($name, 1) . "Attr";
if (method_exists($this, $method)) {
$value = $this->$method($value, $this->data);
} elseif (isset($this->type[$name])) {
// 类型转换
$value = $this->readTransform($value, $this->type[$name]);
} elseif ($notFound) {
if (method_exists($this, $name) && !method_exists(" hinkModel", $name)) {
// 不存在该字段 获取关联数据
$value = $this->relation()->getRelation($name);
// 保存关联对象值
$this->data[$name] = $value;
} else {
throw new InvalidArgumentException("property not exists:" . $this->class . "->" . $name);
}
}
return $value;
}
$model->writeTransform()
数据写入 类型转换
protected function writeTransform($value, $type)
{
if (is_array($type)) {
list($type, $param) = $type;
} elseif (strpos($type, ":")) {
list($type, $param) = explode(":", $type, 2);
}
switch ($type) {
case "integer":
$value = (int) $value;
break;
case "float":
if (empty($param)) {
$value = (float) $value;
} else {
$value = (float) number_format($value, $param);
}
break;
case "boolean":
$value = (bool) $value;
break;
case "timestamp":
if (!is_numeric($value)) {
$value = strtotime($value);
}
break;
case "datetime":
$format = !empty($param) ? $param : $this->dateFormat;
$value = date($format, is_numeric($value) ? $value : strtotime($value));
break;
case "object":
if (is_object($value)) {
$value = json_encode($value, JSON_FORCE_OBJECT);
}
break;
case "array":
$value = (array) $value;
case "json":
$option = !empty($param) ? (int) $param : JSON_UNESCAPED_UNICODE;
$value = json_encode($value, $option);
break;
case "serialize":
$value = serialize($value);
break;
}
return $value;
}
$model->setAttr()
数据写入 自动转换 修改器
public function setAttr($name, $value, $data = [])
{
if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) {
// 自动写入的时间戳字段
$value = $this->autoWriteTimestamp($name);
} else {
// 检测修改器
$method = "set" . Loader::parseName($name, 1) . "Attr";
if (method_exists($this, $method)) {
$value = $this->$method($value, array_merge($data, $this->data));
} elseif (isset($this->type[$name])) {
// 类型转换
$value = $this->writeTransform($value, $this->type[$name]);
}
}
// 标记字段更改
if (!isset($this->data[$name]) || ($this->data[$name] != $value && !in_array($name, $this->change))) {
$this->change[] = $name;
}
// 设置数据对象属性
$this->data[$name] = $value;
return $this;
}
$model->autoWrite()
自动写入时间戳
protected function autoWriteTimestamp($name)
{
if (isset($this->type[$name])) {
$type = $this->type[$name];
if (strpos($type, ":")) {
list($type, $param) = explode(":", $type, 2);
}
switch ($type) {
case "datetime":
case "date":
$format = !empty($param) ? $param : $this->dateFormat;
$value = date($format, $_SERVER["REQUEST_TIME"]);
break;
case "timestamp":
case "int":
$value = $_SERVER["REQUEST_TIME"];
break;
}
} elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), ["datetime", "date", "timestamp"])) {
$value = date($this->dateFormat, $_SERVER["REQUEST_TIME"]);
} else {
$value = $_SERVER["REQUEST_TIME"];
}
return $value;
}
4-4 模型数据与数据表的操作
Model::create()
写入数据
public static function create($data = [])
{
$model = new static();
$model->isUpdate(false)->save($data, []);
return $model;
}
Model::update()
更新数据
public static function update($data = [], $where = [])
{
$model = new static();
$result = $model->isUpdate(true)->save($data, $where);
return $model;
}
Model::destroy()
删除数据
public static function destroy($data)
{
$model = new static();
$query = $model->db();
if (is_array($data) && key($data) !== 0) {
$query->where($data);
$data = null;
} elseif ($data instanceof Closure) {
call_user_func_array($data, [ & $query]);
$data = null;
} elseif (is_null($data)) {
return 0;
}
$resultSet = $query->select($data);
$count = 0;
if ($resultSet) {
foreach ($resultSet as $data) {
$result = $data->delete();
$count += $result;
}
}
return $count;
}
$model->save()
更新数据
public function save($data = [], $where = [], $sequence = null)
{
if (!empty($data)) {
// 数据自动验证
if (!$this->validateData($data)) {
return false;
}
// 数据对象赋值
foreach ($data as $key => $value) {
$this->setAttr($key, $value, $data);
}
if (!empty($where)) {
$this->isUpdate = true;
}
}
// 检测字段
if (!empty($this->field)) {
foreach ($this->data as $key => $val) {
if (!in_array($key, $this->field) && !array_key_exists($key, $this->field)) {
unset($this->data[$key]);
}
}
}
// 数据自动完成
$this->autoCompleteData($this->auto);
// 自动写入更新时间
if ($this->autoWriteTimestamp && $this->updateTime) {
$this->setAttr($this->updateTime, null);
}
// 事件回调
if (false === $this->trigger("before_write", $this)) {
return false;
}
if ($this->isUpdate) {
// 自动更新
$this->autoCompleteData($this->update);
// 事件回调
if (false === $this->trigger("before_update", $this)) {
return false;
}
// 去除没有更新的字段
$data = [];
foreach ($this->data as $key => $val) {
if (in_array($key, $this->change) || $this->isPk($key)) {
$data[$key] = $val;
}
}
if (!empty($this->readonly)) {
// 只读字段不允许更新
foreach ($this->readonly as $key => $field) {
if (isset($data[$field])) {
unset($data[$field]);
}
}
}
if (empty($where) && !empty($this->updateWhere)) {
$where = $this->updateWhere;
}
if (!empty($where)) {
$pk = $this->getPk();
if (is_string($pk) && isset($data[$pk])) {
unset($data[$pk]);
}
}
$result = $this->db()->where($where)->update($data);
// 清空change
$this->change = [];
// 更新回调
$this->trigger("after_update", $this);
} else {
// 自动写入
$this->autoCompleteData($this->insert);
// 自动写入创建时间
if ($this->autoWriteTimestamp && $this->createTime) {
$this->setAttr($this->createTime, null);
}
if (false === $this->trigger("before_insert", $this)) {
return false;
}
$result = $this->db()->insert($this->data);
// 获取自动增长主键
if ($result) {
$insertId = $this->db()->getLastInsID($sequence);
$pk = $this->getPk();
if (is_string($pk) && $insertId) {
$this->data[$pk] = $insertId;
}
}
// 标记为更新
$this->isUpdate = true;
// 清空change
$this->change = [];
// 新增回调
$this->trigger("after_insert", $this);
}
// 写入回调
$this->trigger("after_write", $this);
return $result;
}
$model->saveAll()
更新多条记录
public function saveAll($dataSet, $replace = true)
{
if ($this->validate) {
// 数据批量验证
$validate = $this->validate;
foreach ($dataSet as $data) {
if (!$this->validate($validate)->validateData($data)) {
return false;
}
}
}
$result = [];
$db = $this->db();
$db->startTrans();
try {
$pk = $this->getPk();
if (is_string($pk) && $replace) {
$auto = true;
}
foreach ($dataSet as $key => $data) {
if (!empty($auto) && isset($data[$pk])) {
$result[$key] = self::update($data);
} else {
$result[$key] = self::create($data);
}
}
$db->commit();
return $result;
} catch (Exception $e) {
$db->rollback();
throw $e;
}
}
$model->delte()
删除当前记录
public function delete()
{
if (false === $this->trigger("before_delete", $this)) {
return false;
}
$result = $this->db()->delete($this->data);
$this->trigger("after_delete", $this);
return $result;
}
$model->get()
查找单条记录
public static function get($data = null, $with = [], $cache = false)
{
$query = static::parseQuery($data, $with, $cache);
return $query->find($data);
}
$model->all()
查找多条记录
public static function all($data = null, $with = [], $cache = false)
{
$query = static::parseQuery($data, $with, $cache);
return $query->select($data);
}
5-1 关联查询对象创建
$model->relation()
创建关联对象
protected function relation($relation = null)
{
if (!is_null($relation)) {
// 执行关联查询
return $this->db()->relation($relation);
}
// 获取关联对象实例
if (is_null($this->relation)) {
$this->relation = new Relation($this);
}
return $this->relation;
}
5-2 表的关联操作
$model->hasOne()
一对一关联
public function hasOne($model, $foreignKey = "", $localKey = "", $alias = [], $joinType = "INNER")
{
// 记录当前关联信息
$model = $this->parseModel($model);
$localKey = $localKey ?: $this->getPk();
$foreignKey = $foreignKey ?: Loader::parseName($this->name) . "_id";
return $this->relation()->hasOne($model, $foreignKey, $localKey, $alias, $joinType);
}
$model->hasMany()
一对多
public function hasMany($model, $foreignKey = "", $localKey = "", $alias = [])
{
// 记录当前关联信息
$model = $this->parseModel($model);
$localKey = $localKey ?: $this->getPk();
$foreignKey = $foreignKey ?: Loader::parseName($this->name) . "_id";
return $this->relation()->hasMany($model, $foreignKey, $localKey, $alias);
}
$model->hasManyThrough()
远程一对多
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。
- 上一篇: thinkphp5.0路径中去除index.php的方式
- 下一篇: Java+jsp实现简单登录