From f40fb831f4a6d26edb24313ce118507ee9742d00 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 24 Apr 2016 14:29:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Merge=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E7=B1=BB=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library/think/Model.php | 43 +++--- library/think/model/Merge.php | 258 ++++++++++++++++++++++++++++++++++ 2 files changed, 284 insertions(+), 17 deletions(-) create mode 100644 library/think/model/Merge.php diff --git a/library/think/Model.php b/library/think/Model.php index 008dcf66..3e96afff 100644 --- a/library/think/Model.php +++ b/library/think/Model.php @@ -509,17 +509,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess */ public static function get($data = '', $with = [], $cache = false) { - $db = self::db(); - if ($data instanceof \Closure) { - call_user_func_array($data, [ & $db]); - $data = []; - } elseif ($data instanceof Query) { - return $data->with($with)->cache($cache)->find(); - } - - $result = self::with($with)->cache($cache)->find($data); - - return $result; + $query = self::parseQuery($data, $with, $cache); + return $query->find($data); } /** @@ -527,18 +518,34 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * @access public * @param mixed $data 主键列表或者查询条件(闭包) * @param string $with 关联预查询 + * @param bool $cache 是否缓存 * @return array|false|string */ - public static function all($data = [], $with = []) + public static function all($data = [], $with = [], $cache = false) { - $db = self::db(); + $query = self::parseQuery($data, $with, $cache); + return $query->select($data); + } + + /** + * 分析查询表达式 + * @access public + * @param mixed $data 主键列表或者查询条件(闭包) + * @param string $with 关联预查询 + * @param bool $cache 是否缓存 + * @return \think\db\Query + */ + protected static function parseQuery(&$data, $with, $cache) + { + $result = self::with($with)->cache($cache); if ($data instanceof \Closure) { - call_user_func_array($data, [ & $db]); + call_user_func_array($data, [ & $result]); $data = []; } elseif ($data instanceof Query) { - return $data->with($with)->select(); + $result = $data->with($with)->cache($cache); + $data = []; } - return self::with($with)->select($data); + return $result; } /** @@ -559,8 +566,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess foreach ($resultSet as $data) { $result = $data->delete(); } + return $result; + } else { + return false; } - return $result; } /** diff --git a/library/think/model/Merge.php b/library/think/model/Merge.php new file mode 100644 index 00000000..4279f152 --- /dev/null +++ b/library/think/model/Merge.php @@ -0,0 +1,258 @@ + +// +---------------------------------------------------------------------- + +namespace think\model; + +use think\Db; +use think\Model; + +class Merge extends Model +{ + + protected static $relationModel = []; // HAS ONE 关联的模型列表 + protected $fk = ''; // 外键名 默认为主表名_id + protected $mapFields = []; // 需要处理的模型映射字段,避免混淆 array( id => 'user.id' ) + + /** + * 架构函数 + * @access public + * @param array|object $data 数据 + */ + public function __construct($data = []) + { + parent::__construct($data); + + // 设置默认外键名 仅支持单一外键 + if (empty($this->fk)) { + $this->fk = strtolower($this->name) . '_id'; + } + } + + /** + * 查找单条记录 + * @access public + * @param mixed $data 主键值或者查询条件(闭包) + * @param string $with 关联预查询 + * @param bool $cache 是否缓存 + * @return \think\Model + */ + public static function get($data = '', $with = [], $cache = false) + { + $query = self::parseQuery($data, $with, $cache); + $query = self::attachQuery($query); + return $query->find($data); + } + + /** + * 附加查询表达式 + * @access protected + * @param \think\db\Query $query 查询对象 + * @return \think\db\Query + */ + protected static function attachQuery($query) + { + $master = basename(str_replace('\\', '/', get_called_class())); + $class = new static(); + $fields = self::getModelField($master, '', $class->mapFields); + $query->alias($master)->field($fields); + + foreach (static::$relationModel as $key => $model) { + $name = is_int($key) ? $model : $key; + $table = is_int($key) ? self::db()->name($name)->getTable() : $model; + $query->join($table . ' ' . $name, $name . '.' . $class->fk . '=' . $master . '.' . $class->pk); + $fields = self::getModelField($name, $table, $class->mapFields); + $query->field($fields); + } + return $query; + } + + /** + * 获取关联模型的字段 并解决混淆 + * @access protected + * @param string $name 模型名称 + * @param string $table 关联表名称 + * @param array $map 字段映射 + * @return array + */ + protected static function getModelField($name, $table = '', $map = []) + { + // 获取模型的字段信息 + $fields = self::db()->getTableInfo($table, 'fields'); + $array = []; + foreach ($fields as $field) { + if ($key = array_search($name . '.' . $field, $map)) { + // 需要处理映射字段 + $array[] = $name . '.' . $field . ' AS ' . $key; + } else { + $array[] = $field; + } + } + return $array; + } + + /** + * 查找所有记录 + * @access public + * @param mixed $data 主键列表或者查询条件(闭包) + * @param string $with 关联预查询 + * @return array|false|string + */ + public static function all($data = [], $with = [], $cache = false) + { + $query = self::parseQuery($data, $with, $cache); + $query = self::attachQuery($query); + return $query->select($data); + } + + /** + * 处理写入的模型数据 + * @access public + * @param string $model 模型名称 + * @param array $data 数据 + * @param bool $insert 是否新增 + * @return void + */ + protected function parseData($model, $data, $insert = false) + { + $item = []; + foreach ($data as $key => $val) { + if ($insert || in_array($key, $this->change) || $this->isPk($key)) { + if (array_key_exists($key, $this->mapFields)) { + list($name, $key) = explode('.', $this->mapFields[$key]); + if ($model == $name) { + $item[$key] = $val; + } + } else { + $item[$key] = $val; + } + } + } + return $item; + } + + /** + * 保存模型数据 以及关联数据 + * @access public + * @param mixed $data 数据 + * @param array $where 更新条件 + * @return mixed + */ + public function save($data = [], $where = [], $getInsertId = true) + { + if (!empty($data)) { + // 数据对象赋值 + foreach ($data as $key => $value) { + $this->__set($key, $value); + } + if (!empty($where)) { + $this->isUpdate = true; + } + } + // 数据自动验证 + if (!$this->validateData()) { + return false; + } + // 数据自动完成 + $this->autoCompleteData($this->auto); + // 处理模型数据 + $data = $this->parseData($this->name, $this->data); + + self::db()->startTrans(); + try { + if ($this->isUpdate) { + // 自动写入 + $this->autoCompleteData($this->update); + + if (false === $this->trigger('before_update', $this)) { + return false; + } + + // 写入主表数据 + $result = self::db()->strict(false)->update($data); + + // 写入附表数据 + foreach (static::$relationModel as $key => $model) { + $name = is_int($key) ? $model : $key; + $table = is_int($key) ? self::db()->name($model)->getTable() : $model; + // 处理关联模型数据 + $data = $this->parseData($name, $this->data); + self::db()->table($table)->strict(false)->where($this->fk, $this->data[$this->pk])->update($data); + } + // 新增回调 + $this->trigger('after_update', $this); + } else { + // 自动写入 + $this->autoCompleteData($this->insert); + + if (false === $this->trigger('before_insert', $this)) { + return false; + } + + // 写入主表数据 + $result = self::db()->name($this->name)->strict(false)->insert($this->data); + if ($result) { + $insertId = self::db()->getLastInsID(); + // 写入外键数据 + $this->data[$this->fk] = $insertId; + + // 写入附表数据 + foreach (static::$relationModel as $key => $model) { + $name = is_int($key) ? $model : $key; + $table = is_int($key) ? self::db()->name($model)->getTable() : $model; + // 处理关联模型数据 + $data = $this->parseData($name, $this->data, true); + self::db()->table($table)->strict(false)->insert($data); + } + } + // 新增回调 + $this->trigger('after_insert', $this); + } + self::db()->commit(); + return $result; + } catch (\PDOException $e) { + self::db()->rollback(); + return false; + } + } + + /** + * 删除当前的记录 并删除关联数据 + * @access public + * @return integer + */ + public function delete() + { + if (false === $this->trigger('before_delete', $this)) { + return false; + } + self::db()->startTrans(); + try { + $result = self::db()->delete($this->data); + if ($result) { + // 获取主键数据 + $pk = $this->data[$this->pk]; + + // 删除关联数据 + foreach (static::$relationModel as $key => $model) { + $table = is_int($key) ? self::db()->name($model)->getTable() : $model; + self::db()->table($table)->where($this->fk, $pk)->delete(); + } + } + $this->trigger('after_delete', $this); + self::db()->commit(); + return $result; + } catch (\PDOException $e) { + self::db()->rollback(); + return false; + } + } + +}