From bcad70a71467f8118b3b804fbabe41caf14c4cd0 Mon Sep 17 00:00:00 2001 From: molong Date: Thu, 26 Jan 2017 23:45:27 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81tp=E5=86=85=E6=A0=B8=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=202=E3=80=81=E6=97=B6=E9=97=B4=E6=A0=BC=E5=BC=8Fbug?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/common/model/Content.php | 2 + application/common/model/Document.php | 2 + application/config.php | 3 +- core/base.php | 2 +- core/convention.php | 2 + core/helper.php | 37 +- core/lang/zh-cn.php | 1 + core/library/think/App.php | 4 +- core/library/think/Cache.php | 4 +- core/library/think/Db.php | 13 +- core/library/think/Model.php | 488 ++++++++++++------ core/library/think/Paginator.php | 137 ++++- core/library/think/Request.php | 8 +- core/library/think/Validate.php | 4 +- core/library/think/db/Builder.php | 12 +- core/library/think/db/Connection.php | 48 +- core/library/think/db/Query.php | 188 ++++--- core/library/think/db/connector/Mysql.php | 2 + core/library/think/db/connector/Pgsql.php | 1 + core/library/think/db/connector/Sqlite.php | 2 + core/library/think/db/connector/Sqlsrv.php | 2 +- core/library/think/model/Collection.php | 79 +++ core/library/think/model/Merge.php | 44 +- core/library/think/model/Relation.php | 42 +- .../think/model/relation/BelongsTo.php | 54 +- .../think/model/relation/BelongsToMany.php | 102 ++-- core/library/think/model/relation/HasMany.php | 123 ++--- .../think/model/relation/HasManyThrough.php | 62 ++- core/library/think/model/relation/HasOne.php | 94 ++-- .../think/model/relation/MorphMany.php | 92 ++-- core/library/think/model/relation/MorphTo.php | 79 +-- .../library/think/model/relation/OneToOne.php | 140 +++-- core/library/think/template/taglib/Cx.php | 8 +- core/library/traits/model/SoftDelete.php | 2 +- template/default/content/article/list.html | 4 +- 35 files changed, 1233 insertions(+), 654 deletions(-) create mode 100644 core/library/think/model/Collection.php diff --git a/application/common/model/Content.php b/application/common/model/Content.php index 87d71579..3cf6d272 100644 --- a/application/common/model/Content.php +++ b/application/common/model/Content.php @@ -22,6 +22,8 @@ class Content extends Base{ protected $type = array( 'id' => 'integer', 'cover_id' => 'integer', + 'create_time' => 'integer', + 'update_time' => 'integer', ); protected function setUidAttr(){ diff --git a/application/common/model/Document.php b/application/common/model/Document.php index 3516a678..d581ea8e 100644 --- a/application/common/model/Document.php +++ b/application/common/model/Document.php @@ -30,6 +30,8 @@ class Document extends Base{ 'level' => 'integer', 'comment' => 'integer', 'view' => 'integer', + 'create_time' => 'integer', + 'update_time' => 'integer', ); protected function setUidAttr(){ diff --git a/application/config.php b/application/config.php index f7ba181b..d9c6b947 100644 --- a/application/config.php +++ b/application/config.php @@ -97,10 +97,11 @@ return array( // 日志保存目录 'path' => LOG_PATH, ), + 'app_trace' => true, // 页面Trace信息 'trace' => array( //支持Html,Console 设为false则不显示 - 'type' => 'Html', + 'type' => 'Console', ), 'view_replace_str' => array( diff --git a/core/base.php b/core/base.php index fdbd5d90..cbe288bf 100644 --- a/core/base.php +++ b/core/base.php @@ -9,7 +9,7 @@ // | Author: liu21st // +---------------------------------------------------------------------- -define('THINK_VERSION', '5.0.5beta'); +define('THINK_VERSION', '5.0.5'); define('THINK_START_TIME', microtime(true)); define('THINK_START_MEM', memory_get_usage()); define('EXT', '.php'); diff --git a/core/convention.php b/core/convention.php index c66ef583..8152066b 100644 --- a/core/convention.php +++ b/core/convention.php @@ -107,6 +107,8 @@ return [ 'request_cache' => false, // 请求缓存有效期 'request_cache_expire' => null, + // 全局请求缓存排除规则 + 'request_cache_except' => [], // +---------------------------------------------------------------------- // | 模板设置 diff --git a/core/helper.php b/core/helper.php index 83dbddda..543942be 100644 --- a/core/helper.php +++ b/core/helper.php @@ -23,6 +23,7 @@ use think\exception\HttpResponseException; use think\Lang; use think\Loader; use think\Log; +use think\Model; use think\Request; use think\Response; use think\Session; @@ -329,7 +330,7 @@ if (!function_exists('cookie')) { Cookie::clear($value); } elseif ('' === $value) { // 获取 - return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name); + return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name, $option); } elseif (is_null($value)) { // 删除 return Cookie::delete($name); @@ -548,3 +549,37 @@ if (!function_exists('token')) { return ''; } } + +if (!function_exists('load_relation')) { + /** + * 延迟预载入关联查询 + * @param mixed $resultSet 数据集 + * @param mixed $relation 关联 + * @return array + */ + function load_relation($resultSet, $relation) + { + $item = current($resultSet); + if ($item instanceof Model) { + $item->eagerlyResultSet($resultSet, $relation); + } + return $resultSet; + } +} + +if (!function_exists('collection')) { + /** + * 数组转换为数据集对象 + * @param array $resultSet 数据集数组 + * @return \think\model\Collection|\think\Collection + */ + function collection($resultSet) + { + $item = current($resultSet); + if ($item instanceof Model) { + return \think\model\Collection::make($resultSet); + } else { + return \think\Collection::make($resultSet); + } + } +} diff --git a/core/lang/zh-cn.php b/core/lang/zh-cn.php index 6d33b0ce..b837bc41 100644 --- a/core/lang/zh-cn.php +++ b/core/lang/zh-cn.php @@ -63,4 +63,5 @@ return [ 'route name not exists' => '路由标识不存在(或参数不够)', 'invalid request' => '非法请求', 'bind attr has exists' => '模型的属性已经存在', + 'relation data not exists' => '关联数据不存在', ]; diff --git a/core/library/think/App.php b/core/library/think/App.php index 77721112..963d8b35 100644 --- a/core/library/think/App.php +++ b/core/library/think/App.php @@ -118,7 +118,7 @@ class App // 监听app_begin Hook::listen('app_begin', $dispatch); // 请求缓存检查 - $request->cache($config['request_cache'], $config['request_cache_expire']); + $request->cache($config['request_cache'], $config['request_cache_expire'], $config['request_cache_except']); switch ($dispatch['type']) { case 'redirect': @@ -336,7 +336,7 @@ class App $request->module($module); $config = self::init($module); // 模块请求缓存检查 - $request->cache($config['request_cache'], $config['request_cache_expire']); + $request->cache($config['request_cache'], $config['request_cache_expire'], $config['request_cache_except']); } else { throw new HttpException(404, 'module not exists:' . $module); } diff --git a/core/library/think/Cache.php b/core/library/think/Cache.php index 12107091..9e4b30ef 100644 --- a/core/library/think/Cache.php +++ b/core/library/think/Cache.php @@ -81,9 +81,9 @@ class Cache * @param string $name 缓存标识 * @return Driver */ - public static function store($name) + public static function store($name = '') { - if ('complex' == Config::get('cache.type')) { + if ('' !== $name && 'complex' == Config::get('cache.type')) { self::connect(Config::get('cache.' . $name), strtolower($name)); } return self::$handler; diff --git a/core/library/think/Db.php b/core/library/think/Db.php index a29dac3e..00f719e0 100644 --- a/core/library/think/Db.php +++ b/core/library/think/Db.php @@ -13,7 +13,6 @@ namespace think; use think\db\Connection; use think\db\Query; -use think\paginator\Collection as PaginatorCollection; /** * Class Db @@ -25,21 +24,21 @@ use think\paginator\Collection as PaginatorCollection; * @method Query union(mixed $union, boolean $all = false) static UNION查询 * @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT * @method Query order(mixed $field, string $order = null) static 查询ORDER - * @method Query cache(mixed $key = true , integer $expire = null) static 设置查询缓存 + * @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存 * @method mixed value(string $field) static 获取某个字段的值 * @method array column(string $field, string $key = '') static 获取某个列的值 * @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询 - * @method mixed find(mixed $data = []) static 查询单个记录 - * @method mixed select(mixed $data = []) static 查询多个记录 + * @method mixed find(mixed $data = null) static 查询单个记录 + * @method mixed select(mixed $data = null) static 查询多个记录 * @method integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录 * @method integer insertGetId(array $data, boolean $replace = false, string $sequence = null) static 插入一条记录并返回自增ID * @method integer insertAll(array $dataSet) static 插入多条记录 * @method integer update(array $data) static 更新记录 - * @method integer delete(mixed $data = []) static 删除记录 + * @method integer delete(mixed $data = null) static 删除记录 * @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据 - * @method mixed query(string $sql, array $bind = [], boolean $fetch = false, boolean $master = false, mixed $class = false) static SQL查询 + * @method mixed query(string $sql, array $bind = [], boolean $fetch = false, boolean $master = false, mixed $class = null) static SQL查询 * @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行 - * @method PaginatorCollection paginate(integer $listRows = 15, mixed $simple = false, array $config = []) static 分页查询 + * @method Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询 * @method mixed transaction(callable $callback) static 执行数据库事务 * @method void startTrans() static 启动事务 * @method void commit() static 用于非自动提交状态下面的查询提交 diff --git a/core/library/think/Model.php b/core/library/think/Model.php index 0491e551..085ca053 100644 --- a/core/library/think/Model.php +++ b/core/library/think/Model.php @@ -14,6 +14,7 @@ namespace think; use InvalidArgumentException; use think\db\Query; use think\Exception\ValidateException; +use think\model\Collection; use think\model\Relation; use think\model\relation\BelongsTo; use think\model\relation\BelongsToMany; @@ -22,23 +23,11 @@ use think\model\relation\HasManyThrough; use think\model\relation\HasOne; use think\model\relation\MorphMany; use think\model\relation\MorphTo; -use think\paginator\Collection as PaginatorCollection; /** * Class Model * @package think - * @method static PaginatorCollection paginate(integer $listRows = 15, boolean $simple = false, array $config = []) 分页查询 - * @method static mixed value($field, $default = null) 得到某个字段的值 - * @method static array column($field, $key = '') 得到某个列的数组 - * @method static integer count($field = '*') COUNT查询 - * @method static integer sum($field = '*') SUM查询 - * @method static integer min($field = '*') MIN查询 - * @method static integer max($field = '*') MAX查询 - * @method static integer avg($field = '*') AVG查询 - * @method static setField($field, $value = '') - * @method static Query where($field, $op = null, $condition = null) 指定AND查询条件 - * @method static static findOrFail($data = null) 查找单条记录 如果不存在则抛出异常 - * + * @mixin Query */ abstract class Model implements \JsonSerializable, \ArrayAccess { @@ -107,6 +96,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess protected $batchValidate = false; // 查询数据集对象 protected $resultSetType; + // 关联自动写入 + protected $relationWrite; // protected static $db; @@ -153,6 +144,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $this->dateFormat = $this->db(false)->getConfig('datetime_format'); } + if (is_null($this->resultSetType)) { + $this->resultSetType = $this->db(false)->getConfig('resultset_type'); + } // 执行初始化操作 $this->initialize(); } @@ -195,7 +189,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } // 全局作用域 if ($baseQuery && method_exists($this, 'base')) { - call_user_func_array([$this, 'base'], [ & self::$links[$model]]); + call_user_func_array([$this, 'base'], [& self::$links[$model]]); } // 返回当前模型的数据库查询对象 return self::$links[$model]; @@ -221,12 +215,13 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * @return void */ protected static function init() - {} + { + } /** * 设置数据对象值 * @access public - * @param mixed $data 数据或者属性名 + * @param mixed $data 数据或者属性名 * @param mixed $value 值 * @return $this */ @@ -273,9 +268,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 修改器 设置数据对象值 * @access public - * @param string $name 属性名 - * @param mixed $value 属性值 - * @param array $data 数据 + * @param string $name 属性名 + * @param mixed $value 属性值 + * @param array $data 数据 * @return $this */ public function setAttr($name, $value, $data = []) @@ -308,7 +303,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 自动写入时间戳 * @access public - * @param string $name 时间戳字段 + * @param string $name 时间戳字段 * @return mixed */ protected function autoWriteTimestamp($name) @@ -330,7 +325,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $value = $_SERVER['REQUEST_TIME']; break; } - } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), ['datetime', 'date', 'timestamp'])) { + } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ + 'datetime', + 'date', + 'timestamp' + ]) + ) { $value = $this->formatDateTime($_SERVER['REQUEST_TIME'], $this->dateFormat); } else { $value = $this->formatDateTime($_SERVER['REQUEST_TIME'], $this->dateFormat, true); @@ -341,9 +341,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 时间日期字段格式化处理 * @access public - * @param mixed $time 时间日期表达式 - * @param mixed $format 日期格式 - * @param bool $timestamp 是否进行时间戳转换 + * @param mixed $time 时间日期表达式 + * @param mixed $format 日期格式 + * @param bool $timestamp 是否进行时间戳转换 * @return mixed */ protected function formatDateTime($time, $format, $timestamp = false) @@ -359,8 +359,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 数据写入 类型转换 * @access public - * @param mixed $value 值 - * @param string|array $type 要转换的类型 + * @param mixed $value 值 + * @param string|array $type 要转换的类型 * @return mixed */ protected function writeTransform($value, $type) @@ -438,7 +438,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess // 类型转换 $value = $this->readTransform($value, $this->type[$name]); } elseif (in_array($name, [$this->createTime, $this->updateTime])) { - if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), ['datetime', 'date', 'timestamp'])) { + if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ + 'datetime', + 'date', + 'timestamp' + ]) + ) { $value = $this->formatDateTime(strtotime($value), $this->dateFormat); } else { $value = $this->formatDateTime($value, $this->dateFormat); @@ -462,8 +467,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 数据读取 类型转换 * @access public - * @param mixed $value 值 - * @param string|array $type 要转换的类型 + * @param mixed $value 值 + * @param string|array $type 要转换的类型 * @return mixed */ protected function readTransform($value, $type) @@ -523,8 +528,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 设置需要追加的输出属性 * @access public - * @param array $append 属性列表 - * @param bool $override 是否覆盖 + * @param array $append 属性列表 + * @param bool $override 是否覆盖 * @return $this */ public function append($append = [], $override = false) @@ -536,9 +541,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 设置附加关联对象的属性 * @access public - * @param string $relation 关联方法 - * @param string|array $append 追加属性名 + * @param string $relation 关联方法 + * @param string|array $append 追加属性名 * @return $this + * @throws Exception */ public function appendRelationAttr($relation, $append) { @@ -562,8 +568,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 设置需要隐藏的输出属性 * @access public - * @param array $hidden 属性列表 - * @param bool $override 是否覆盖 + * @param array $hidden 属性列表 + * @param bool $override 是否覆盖 * @return $this */ public function hidden($hidden = [], $override = false) @@ -574,8 +580,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 设置需要输出的属性 + * @access public * @param array $visible - * @param bool $override 是否覆盖 + * @param bool $override 是否覆盖 * @return $this */ public function visible($visible = [], $override = false) @@ -584,6 +591,56 @@ abstract class Model implements \JsonSerializable, \ArrayAccess return $this; } + /** + * 解析隐藏及显示属性 + * @access protected + * @param array $attrs 属性 + * @param array $result 结果集 + * @param bool $visible + * @return array + */ + protected function parseAttr($attrs, &$result, $visible = true) + { + $array = []; + foreach ($attrs as $key => $val) { + if (is_array($val)) { + if ($visible) { + $array[] = $key; + } + $result[$key] = $val; + } elseif (strpos($val, '.')) { + list($key, $name) = explode('.', $val); + if ($visible) { + $array[] = $key; + } + $result[$key][] = $name; + } else { + $array[] = $val; + } + } + return $array; + } + + /** + * 转换子模型对象 + * @access protected + * @param Model|Collection $model + * @param $visible + * @param $hidden + * @param $key + * @return array + */ + protected function subToArray($model, $visible, $hidden, $key) + { + // 关联模型对象 + if (isset($visible[$key])) { + $model->visible($visible[$key]); + } elseif (isset($hidden[$key])) { + $model->hidden($hidden[$key]); + } + return $model->toArray(); + } + /** * 转换当前模型对象为数组 * @access public @@ -591,13 +648,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess */ public function toArray() { - $item = []; - - //过滤属性 + $item = []; + $visible = []; + $hidden = []; + // 过滤属性 if (!empty($this->visible)) { - $data = array_intersect_key($this->data, array_flip($this->visible)); + $array = $this->parseAttr($this->visible, $visible); + $data = array_intersect_key($this->data, array_flip($array)); } elseif (!empty($this->hidden)) { - $data = array_diff_key($this->data, array_flip($this->hidden)); + $array = $this->parseAttr($this->hidden, $hidden, false); + $data = array_diff_key($this->data, array_flip($array)); } else { $data = $this->data; } @@ -605,12 +665,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess foreach ($data as $key => $val) { if ($val instanceof Model || $val instanceof Collection) { // 关联模型对象 - $item[$key] = $val->toArray(); + $item[$key] = $this->subToArray($val, $visible, $hidden, $key); } elseif (is_array($val) && reset($val) instanceof Model) { // 关联模型数据集 $arr = []; foreach ($val as $k => $value) { - $arr[$k] = $value->toArray(); + $arr[$k] = $this->subToArray($value, $visible, $hidden, $k); } $item[$key] = $arr; } else { @@ -620,8 +680,19 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } // 追加属性(必须定义获取器) if (!empty($this->append)) { - foreach ($this->append as $name) { - $item[$name] = $this->getAttr($name); + foreach ($this->append as $key => $name) { + if (is_array($name)) { + // 追加关联对象属性 + $relation = $this->getAttr($key); + $item[$key] = $relation->append($name)->toArray(); + } elseif (strpos($name, '.')) { + list($key, $attr) = explode('.', $name); + // 追加关联对象属性 + $relation = $this->getAttr($key); + $item[$key] = $relation->append([$attr])->toArray(); + } else { + $item[$name] = $this->getAttr($name); + } } } return !empty($item) ? $item : []; @@ -630,7 +701,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 转换当前模型对象为JSON字符串 * @access public - * @param integer $options json参数 + * @param integer $options json参数 * @return string */ public function toJson($options = JSON_UNESCAPED_UNICODE) @@ -641,7 +712,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 转换当前模型数据集为数据集对象 * @access public - * @param array|Collection $collection 数据集 + * @param array|Collection $collection 数据集 * @return Collection */ public function toCollection($collection) @@ -649,7 +720,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess if ($this->resultSetType) { if ('collection' == $this->resultSetType) { $collection = new Collection($collection); - } else { + } elseif (false !== strpos($this->resultSetType, '\\')) { $class = $this->resultSetType; $collection = new $class($collection); } @@ -657,6 +728,21 @@ abstract class Model implements \JsonSerializable, \ArrayAccess return $collection; } + /** + * 关联数据一起更新 + * @access public + * @param mixed $relation 关联 + * @return $this + */ + public function together($relation) + { + if (is_string($relation)) { + $relation = explode(',', $relation); + } + $this->relationWrite = $relation; + return $this; + } + /** * 获取模型对象的主键 * @access public @@ -694,9 +780,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 保存当前数据对象 * @access public - * @param array $data 数据 - * @param array $where 更新条件 - * @param string $sequence 自增序列名 + * @param array $data 数据 + * @param array $where 更新条件 + * @param string $sequence 自增序列名 * @return integer|false */ public function save($data = [], $where = [], $sequence = null) @@ -715,8 +801,32 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } } + // 自动关联写入 + if (!empty($this->relationWrite)) { + $relation = []; + foreach ($this->relationWrite as $key => $name) { + if (!is_numeric($key)) { + $relation[$key] = []; + foreach ($name as $val) { + if (isset($this->data[$val])) { + $relation[$key][$val] = $this->data[$val]; + unset($this->data[$val]); + } + } + } elseif (isset($this->data[$name])) { + $relation[$name] = $this->data[$name]; + if (!$this->isUpdate) { + unset($this->data[$name]); + } + } + } + } + // 检测字段 if (!empty($this->field)) { + if (true === $this->field) { + $this->field = $this->db(false)->getTableInfo('', 'fields'); + } foreach ($this->data as $key => $val) { if (!in_array($key, $this->field)) { unset($this->data[$key]); @@ -775,7 +885,33 @@ abstract class Model implements \JsonSerializable, \ArrayAccess unset($data[$pk]); } + // 关联更新 + if (isset($relation)) { + foreach ($relation as $name => $val) { + if (isset($data[$name])) { + unset($data[$name]); + } + } + } + + // 模型更新 $result = $this->db()->where($where)->update($data); + + // 关联更新 + if (isset($relation)) { + foreach ($relation as $name => $val) { + if ($val instanceof Model) { + $val->save(); + } else { + unset($this->data[$name]); + $model = $this->getAttr($name); + if ($model instanceof Model) { + $model->save($val); + } + } + } + } + // 清空change $this->change = []; // 更新回调 @@ -802,6 +938,15 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $this->data[$pk] = $insertId; } } + + // 关联写入 + if (isset($relation)) { + foreach ($relation as $name => $val) { + $method = Loader::parseName($name, 1, false); + $this->$method()->save($val); + } + } + // 标记为更新 $this->isUpdate = true; // 清空change @@ -818,9 +963,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 保存多个数据到当前数据对象 * @access public - * @param array $dataSet 数据 - * @param boolean $replace 是否自动识别更新和写入 + * @param array $dataSet 数据 + * @param boolean $replace 是否自动识别更新和写入 * @return array|false + * @throws \Exception */ public function saveAll($dataSet, $replace = true) { @@ -860,13 +1006,13 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 设置允许写入的字段 * @access public - * @param bool|array $field 允许写入的字段 如果为true只允许写入数据表字段 + * @param mixed $field 允许写入的字段 如果为true只允许写入数据表字段 * @return $this */ public function allowField($field) { - if (true === $field) { - $field = $this->db(false)->getTableInfo('', 'fields'); + if (is_string($field)) { + $field = explode(',', $field); } $this->field = $field; return $this; @@ -918,7 +1064,29 @@ abstract class Model implements \JsonSerializable, \ArrayAccess return false; } - $result = $this->db()->delete($this->data); + // 删除条件 + $pk = $this->getPk(); + if (isset($this->data[$pk])) { + $where = [$pk => $this->data[$pk]]; + } elseif (!empty($this->updateWhere)) { + $where = $this->updateWhere; + } else { + $where = null; + } + + // 删除当前模型数据 + $result = $this->db()->where($where)->delete(); + + // 关联删除 + if (!empty($this->relationWrite)) { + foreach ($this->relationWrite as $key => $name) { + $name = is_numeric($key) ? $name : $key; + $model = $this->getAttr($name); + if ($model instanceof Model) { + $model->delete(); + } + } + } $this->trigger('after_delete', $this); return $result; @@ -939,8 +1107,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 设置字段验证 * @access public - * @param array|string|bool $rule 验证规则 true表示自动读取验证器类 - * @param array $msg 提示信息 + * @param array|string|bool $rule 验证规则 true表示自动读取验证器类 + * @param array $msg 提示信息 * @param bool $batch 批量验证 * @return $this */ @@ -973,8 +1141,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 自动验证数据 * @access protected - * @param array $data 验证数据 - * @param mixed $rule 验证规则 + * @param array $data 验证数据 + * @param mixed $rule 验证规则 * @param bool $batch 批量验证 * @return bool */ @@ -1025,9 +1193,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 注册回调方法 * @access public - * @param string $event 事件名 - * @param callable $callback 回调方法 - * @param bool $override 是否覆盖 + * @param string $event 事件名 + * @param callable $callback 回调方法 + * @param bool $override 是否覆盖 * @return void */ public static function event($event, $callback, $override = false) @@ -1042,8 +1210,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 触发事件 * @access protected - * @param string $event 事件名 - * @param mixed $params 传入参数(引用) + * @param string $event 事件名 + * @param mixed $params 传入参数(引用) * @return bool */ protected function trigger($event, &$params) @@ -1051,7 +1219,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess if (isset(self::$event[$this->class][$event])) { foreach (self::$event[$this->class][$event] as $callback) { if (is_callable($callback)) { - $result = call_user_func_array($callback, [ & $params]); + $result = call_user_func_array($callback, [& $params]); if (false === $result) { return false; } @@ -1064,8 +1232,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 写入数据 * @access public - * @param array $data 数据数组 - * @param array|true $field 允许字段 + * @param array $data 数据数组 + * @param array|true $field 允许字段 * @return $this */ public static function create($data = [], $field = null) @@ -1081,9 +1249,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 更新数据 * @access public - * @param array $data 数据数组 - * @param array $where 更新条件 - * @param array|true $field 允许字段 + * @param array $data 数据数组 + * @param array $where 更新条件 + * @param array|true $field 允许字段 * @return $this */ public static function update($data = [], $where = [], $field = null) @@ -1129,9 +1297,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 分析查询表达式 * @access public - * @param mixed $data 主键列表或者查询条件(闭包) - * @param string $with 关联预查询 - * @param bool $cache 是否缓存 + * @param mixed $data 主键列表或者查询条件(闭包) + * @param string $with 关联预查询 + * @param bool $cache 是否缓存 * @return Query */ protected static function parseQuery(&$data, $with, $cache) @@ -1141,7 +1309,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $result = $result->where($data); $data = null; } elseif ($data instanceof \Closure) { - call_user_func_array($data, [ & $result]); + call_user_func_array($data, [& $result]); $data = null; } elseif ($data instanceof Query) { $result = $data->with($with)->cache($cache); @@ -1164,7 +1332,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $query->where($data); $data = null; } elseif ($data instanceof \Closure) { - call_user_func_array($data, [ & $query]); + call_user_func_array($data, [& $query]); $data = null; } elseif (is_null($data)) { return 0; @@ -1183,9 +1351,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 命名范围 * @access public - * @param string|array|Closure $name 命名范围名称 逗号分隔 - * @param mixed ...$params 参数调用 - * @return Model + * @param string|array|\Closure $name 命名范围名称 逗号分隔 + * @internal mixed ...$params 参数调用 + * @return Model|Query */ public static function scope($name) { @@ -1213,7 +1381,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 设置是否使用全局查询范围 - * @param bool $use 是否启用全局查询范围 + * @param bool $use 是否启用全局查询范围 * @access public * @return Model */ @@ -1227,29 +1395,32 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 根据关联条件查询当前模型 * @access public - * @param string $relation 关联方法名 - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 + * @param string $relation 关联方法名 + * @param mixed $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 * @return Model */ public static function has($relation, $operator = '>=', $count = 1, $id = '*') { $model = new static(); - return $model->$relation()->has($model, $operator, $count, $id); + if (is_array($operator) || $operator instanceof \Closure) { + return $model->$relation()->hasWhere($operator); + } + return $model->$relation()->has($operator, $count, $id); } /** * 根据关联条件查询当前模型 * @access public - * @param string $relation 关联方法名 - * @param mixed $where 查询条件(数组或者闭包) + * @param string $relation 关联方法名 + * @param mixed $where 查询条件(数组或者闭包) * @return Model */ public static function hasWhere($relation, $where = []) { $model = new static(); - return $model->$relation()->hasWhere($model, $where); + return $model->$relation()->hasWhere($where); } /** @@ -1281,8 +1452,19 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $relations = explode(',', $relations); } - foreach ($relations as $relation) { - $this->data[$relation] = $this->$relation()->getRelation(); + foreach ($relations as $key => $relation) { + $subRelation = ''; + $closure = null; + if ($relation instanceof \Closure) { + // 支持闭包查询过滤关联条件 + $closure = $relation; + $relation = $key; + } + if (strpos($relation, '.')) { + list($relation, $subRelation) = explode('.', $relation, 2); + } + $method = Loader::parseName($relation, 1, false); + $this->data[$relation] = $this->$method()->getRelation($subRelation, $closure); } return $this; } @@ -1290,12 +1472,11 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 预载入关联查询 返回数据集 * @access public - * @param array $resultSet 数据集 - * @param string $relation 关联名 - * @param string $class 数据集对象名 为空表示数组 + * @param array $resultSet 数据集 + * @param string $relation 关联名 * @return array */ - public function eagerlyResultSet(&$resultSet, $relation, $class = '') + public function eagerlyResultSet(&$resultSet, $relation) { $relations = is_string($relation) ? explode(',', $relation) : $relation; foreach ($relations as $key => $relation) { @@ -1306,22 +1487,21 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $relation = $key; } if (strpos($relation, '.')) { - list($relation, $subRelation) = explode('.', $relation); + list($relation, $subRelation) = explode('.', $relation, 2); } $relation = Loader::parseName($relation, 1, false); - $this->$relation()->eagerlyResultSet($resultSet, $relation, $subRelation, $closure, $class); + $this->$relation()->eagerlyResultSet($resultSet, $relation, $subRelation, $closure); } } /** * 预载入关联查询 返回模型对象 * @access public - * @param Model $result 数据对象 - * @param string $relation 关联名 - * @param string $class 数据集对象名 为空表示数组 + * @param Model $result 数据对象 + * @param string $relation 关联名 * @return Model */ - public function eagerlyResult(&$result, $relation, $class = '') + public function eagerlyResult(&$result, $relation) { $relations = is_string($relation) ? explode(',', $relation) : $relation; @@ -1333,18 +1513,18 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $relation = $key; } if (strpos($relation, '.')) { - list($relation, $subRelation) = explode('.', $relation); + list($relation, $subRelation) = explode('.', $relation, 2); } $relation = Loader::parseName($relation, 1, false); - $this->$relation()->eagerlyResult($result, $relation, $subRelation, $closure, $class); + $this->$relation()->eagerlyResult($result, $relation, $subRelation, $closure); } } /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param string|array $relation 关联名 + * @param Model $result 数据对象 + * @param string|array $relation 关联名 * @return void */ public function relationCount(&$result, $relation) @@ -1363,14 +1543,28 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } } + /** + * 获取模型的默认外键名 + * @access public + * @param string $name 模型名 + * @return string + */ + protected function getForeignKey($name) + { + if (strpos($name, '\\')) { + $name = basename(str_replace('\\', '/', $name)); + } + return Loader::parseName($name) . '_id'; + } + /** * HAS ONE 关联定义 * @access public - * @param string $model 模型名 + * @param string $model 模型名 * @param string $foreignKey 关联外键 - * @param string $localKey 关联主键 - * @param array $alias 别名定义 - * @param string $joinType JOIN类型 + * @param string $localKey 关联主键 + * @param array $alias 别名定义(已经废弃) + * @param string $joinType JOIN类型 * @return HasOne */ public function hasOne($model, $foreignKey = '', $localKey = '', $alias = [], $joinType = 'INNER') @@ -1378,97 +1572,93 @@ abstract class Model implements \JsonSerializable, \ArrayAccess // 记录当前关联信息 $model = $this->parseModel($model); $localKey = $localKey ?: $this->getPk(); - $foreignKey = $foreignKey ?: Loader::parseName($this->name) . '_id'; - return new HasOne($this, $model, $foreignKey, $localKey, $alias, $joinType); + $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); + return new HasOne($this, $model, $foreignKey, $localKey, $joinType); } /** * BELONGS TO 关联定义 * @access public - * @param string $model 模型名 + * @param string $model 模型名 * @param string $foreignKey 关联外键 - * @param string $otherKey 关联主键 - * @param array $alias 别名定义 - * @param string $joinType JOIN类型 + * @param string $localKey 关联主键 + * @param array $alias 别名定义(已经废弃) + * @param string $joinType JOIN类型 * @return BelongsTo */ - public function belongsTo($model, $foreignKey = '', $otherKey = '', $alias = [], $joinType = 'INNER') + public function belongsTo($model, $foreignKey = '', $localKey = '', $alias = [], $joinType = 'INNER') { // 记录当前关联信息 $model = $this->parseModel($model); - $foreignKey = $foreignKey ?: Loader::parseName(basename(str_replace('\\', '/', $model))) . '_id'; - $otherKey = $otherKey ?: (new $model)->getPk(); - return new BelongsTo($this, $model, $foreignKey, $otherKey, $alias, $joinType); + $foreignKey = $foreignKey ?: $this->getForeignKey($model); + $localKey = $localKey ?: (new $model)->getPk(); + return new BelongsTo($this, $model, $foreignKey, $localKey, $joinType); } /** * HAS MANY 关联定义 * @access public - * @param string $model 模型名 + * @param string $model 模型名 * @param string $foreignKey 关联外键 - * @param string $localKey 关联主键 - * @param array $alias 别名定义 + * @param string $localKey 关联主键 * @return HasMany */ - public function hasMany($model, $foreignKey = '', $localKey = '', $alias = []) + public function hasMany($model, $foreignKey = '', $localKey = '') { // 记录当前关联信息 $model = $this->parseModel($model); $localKey = $localKey ?: $this->getPk(); - $foreignKey = $foreignKey ?: Loader::parseName($this->name) . '_id'; - return new HasMany($this, $model, $foreignKey, $localKey, $alias); + $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); + return new HasMany($this, $model, $foreignKey, $localKey); } /** * HAS MANY 远程关联定义 * @access public - * @param string $model 模型名 - * @param string $through 中间模型名 + * @param string $model 模型名 + * @param string $through 中间模型名 * @param string $foreignKey 关联外键 * @param string $throughKey 关联外键 - * @param string $localKey 关联主键 - * @param array $alias 别名定义 + * @param string $localKey 关联主键 * @return HasManyThrough */ - public function hasManyThrough($model, $through, $foreignKey = '', $throughKey = '', $localKey = '', $alias = []) + public function hasManyThrough($model, $through, $foreignKey = '', $throughKey = '', $localKey = '') { // 记录当前关联信息 $model = $this->parseModel($model); $through = $this->parseModel($through); $localKey = $localKey ?: $this->getPk(); - $foreignKey = $foreignKey ?: Loader::parseName($this->name) . '_id'; - $name = Loader::parseName(basename(str_replace('\\', '/', $through))); - $throughKey = $throughKey ?: $name . '_id'; - return new HasManyThrough($this, $model, $through, $foreignKey, $throughKey, $localKey, $alias); + $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); + $throughKey = $throughKey ?: $this->getForeignKey($through); + return new HasManyThrough($this, $model, $through, $foreignKey, $throughKey, $localKey); } /** * BELONGS TO MANY 关联定义 * @access public - * @param string $model 模型名 - * @param string $table 中间表名 + * @param string $model 模型名 + * @param string $table 中间表名 * @param string $foreignKey 关联外键 - * @param string $localKey 当前模型关联键 - * @param array $alias 别名定义 + * @param string $localKey 当前模型关联键 * @return BelongsToMany */ - public function belongsToMany($model, $table = '', $foreignKey = '', $localKey = '', $alias = []) + public function belongsToMany($model, $table = '', $foreignKey = '', $localKey = '') { // 记录当前关联信息 $model = $this->parseModel($model); $name = Loader::parseName(basename(str_replace('\\', '/', $model))); $table = $table ?: $this->db(false)->getTable(Loader::parseName($this->name) . '_' . $name); $foreignKey = $foreignKey ?: $name . '_id'; - $localKey = $localKey ?: Loader::parseName($this->name) . '_id'; - return new BelongsToMany($this, $model, $table, $foreignKey, $localKey, $alias); + $localKey = $localKey ?: $this->getForeignKey($this->name); + return new BelongsToMany($this, $model, $table, $foreignKey, $localKey); } /** * MORPH MANY 关联定义 * @access public - * @param string $model 模型名 - * @param string|array $morph 多态字段信息 - * @param string $type 多态类型 + * @param string $model 模型名 + * @param string|array $morph 多态字段信息 + * @param string $type 多态类型 * @return MorphMany */ public function morphMany($model, $morph = null, $type = '') @@ -1492,8 +1682,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * MORPH TO 关联定义 * @access public - * @param string|array $morph 多态字段信息 - * @param array $alias 多态别名定义 + * @param string|array $morph 多态字段信息 + * @param array $alias 多态别名定义 * @return MorphTo */ public function morphTo($morph = null, $alias = []) @@ -1547,8 +1737,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 修改器 设置数据对象的值 * @access public - * @param string $name 名称 - * @param mixed $value 值 + * @param string $name 名称 + * @param mixed $value 值 * @return void */ public function __set($name, $value) @@ -1641,6 +1831,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 模型事件快捷方法 + * @param $callback + * @param bool $override */ protected static function beforeInsert($callback, $override = false) { diff --git a/core/library/think/Paginator.php b/core/library/think/Paginator.php index d2fe79d2..0c1bea8a 100644 --- a/core/library/think/Paginator.php +++ b/core/library/think/Paginator.php @@ -11,14 +11,19 @@ namespace think; -use think\paginator\Collection as PaginatorCollection; +use ArrayAccess; +use ArrayIterator; +use Countable; +use IteratorAggregate; +use JsonSerializable; +use Traversable; -abstract class Paginator +abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable { /** @var bool 是否为简洁模式 */ protected $simple = false; - /** @var PaginatorCollection 数据集 */ + /** @var Collection 数据集 */ protected $items; /** @var integer 当前页 */ @@ -44,7 +49,7 @@ abstract class Paginator 'fragment' => '', ]; - protected function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = []) + public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = []) { $this->options = array_merge($this->options, $options); @@ -53,10 +58,11 @@ abstract class Paginator $this->simple = $simple; $this->listRows = $listRows; + if (!$items instanceof Collection) { + $items = Collection::make($items); + } + if ($simple) { - if (!$items instanceof Collection) { - $items = Collection::make($items); - } $this->currentPage = $this->setCurrentPage($currentPage); $this->hasMore = count($items) > ($this->listRows); $items = $items->slice(0, $this->listRows); @@ -66,8 +72,7 @@ abstract class Paginator $this->currentPage = $this->setCurrentPage($currentPage); $this->hasMore = $this->currentPage < $this->lastPage; } - - $this->items = PaginatorCollection::make($items, $this); + $this->items = $items; } /** @@ -77,12 +82,11 @@ abstract class Paginator * @param bool $simple * @param null $total * @param array $options - * @return PaginatorCollection + * @return Paginator */ public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = []) { - $paginator = new static($items, $listRows, $currentPage, $total, $simple, $options); - return $paginator->items; + return new static($items, $listRows, $currentPage, $total, $simple, $options); } protected function setCurrentPage($currentPage) @@ -253,4 +257,113 @@ abstract class Paginator * @return mixed */ abstract public function render(); + + public function items() + { + return $this->items->all(); + } + + public function getCollection() + { + return $this->items; + } + + public function isEmpty() + { + return $this->items->isEmpty(); + } + + /** + * Retrieve an external iterator + * @return Traversable An instance of an object implementing Iterator or + * Traversable + */ + public function getIterator() + { + return new ArrayIterator($this->items->all()); + } + + /** + * Whether a offset exists + * @param mixed $offset + * @return bool + */ + public function offsetExists($offset) + { + return $this->items->offsetExists($offset); + } + + /** + * Offset to retrieve + * @param mixed $offset + * @return mixed + */ + public function offsetGet($offset) + { + return $this->items->offsetGet($offset); + } + + /** + * Offset to set + * @param mixed $offset + * @param mixed $value + */ + public function offsetSet($offset, $value) + { + $this->items->offsetSet($offset, $value); + } + + /** + * Offset to unset + * @param mixed $offset + * @return void + * @since 5.0.0 + */ + public function offsetUnset($offset) + { + $this->items->offsetUnset($offset); + } + + /** + * Count elements of an object + */ + public function count() + { + return $this->items->count(); + } + + public function __toString() + { + return (string) $this->render(); + } + + public function toArray() + { + try { + $total = $this->total(); + } catch (Exception $e) { + $total = null; + } + + return [ + 'total' => $total, + 'per_page' => $this->listRows(), + 'current_page' => $this->currentPage(), + 'data' => $this->items->toArray() + ]; + } + + /** + * Specify data which should be serialized to JSON + */ + public function jsonSerialize() + { + return $this->toArray(); + } + + public function __call($name, $arguments) + { + return call_user_func_array([$this->getCollection(), $name], $arguments); + } + } diff --git a/core/library/think/Request.php b/core/library/think/Request.php index b72a5aaf..d39153fd 100644 --- a/core/library/think/Request.php +++ b/core/library/think/Request.php @@ -1502,9 +1502,10 @@ class Request * @access public * @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id * @param mixed $expire 缓存有效期 + * @param array $except 缓存排除 * @return void */ - public function cache($key, $expire = null) + public function cache($key, $expire = null, $except = []) { if (false !== $key && $this->isGet() && !$this->isCheckCache) { // 标记请求缓存检查 @@ -1516,6 +1517,11 @@ class Request if ($key instanceof \Closure) { $key = call_user_func_array($key, [$this]); } elseif (true === $key) { + foreach ($except as $rule) { + if (0 === strpos($this->url(), $rule)) { + return; + } + } // 自动缓存功能 $key = '__URL__'; } elseif (strpos($key, '|')) { diff --git a/core/library/think/Validate.php b/core/library/think/Validate.php index e91ee338..9a6f58ab 100644 --- a/core/library/think/Validate.php +++ b/core/library/think/Validate.php @@ -568,7 +568,7 @@ class Validate break; case 'ip': // 是否为IP地址 - $result = $this->filter($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6); + $result = $this->filter($value, [FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6]); break; case 'url': // 是否为一个URL地址 @@ -591,7 +591,7 @@ class Validate break; case 'boolean': // 是否为布尔值 - $result = in_array($value, [0, 1, true, false]); + $result = in_array($value, [true, false, 0, 1, '0', '1'], true); break; case 'array': // 是否为数组 diff --git a/core/library/think/db/Builder.php b/core/library/think/db/Builder.php index 3e7a60e9..380fe239 100644 --- a/core/library/think/db/Builder.php +++ b/core/library/think/db/Builder.php @@ -112,8 +112,8 @@ abstract class Builder $result[$item] = $val; } else { $key = str_replace('.', '_', $key); - $this->query->bind($key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR); - $result[$item] = ':' . $key; + $this->query->bind('__data__' . $key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR); + $result[$item] = ':__data__' . $key; } } elseif (is_object($val) && method_exists($val, '__toString')) { // 对象数据写入 @@ -523,10 +523,12 @@ abstract class Builder $array = []; foreach ($order as $key => $val) { if (is_numeric($key)) { - if (false === strpos($val, '(')) { - $array[] = $this->parseKey($val, $options); - } elseif ('[rand]' == $val) { + if ('[rand]' == $val) { $array[] = $this->parseRand(); + } elseif (false === strpos($val, '(')) { + $array[] = $this->parseKey($val, $options); + } else { + $array[] = $val; } } else { $sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : ''; diff --git a/core/library/think/db/Connection.php b/core/library/think/db/Connection.php index 4e18f812..b1f730bf 100644 --- a/core/library/think/db/Connection.php +++ b/core/library/think/db/Connection.php @@ -13,7 +13,6 @@ namespace think\db; use PDO; use PDOStatement; -use think\Collection; use think\Db; use think\db\exception\BindParamException; use think\Debug; @@ -51,8 +50,6 @@ abstract class Connection protected $linkRead; protected $linkWrite; - // 查询结果类型 - protected $resultSetType = 'array'; // 查询结果类型 protected $fetchType = PDO::FETCH_ASSOC; // 字段属性大小写 @@ -61,6 +58,8 @@ abstract class Connection protected static $event = []; // 查询对象 protected $query = []; + // 使用Builder类 + protected $builder; // 数据库连接参数配置 protected $config = [ // 数据库类型 @@ -151,6 +150,20 @@ abstract class Connection return $this->query[$model]; } + /** + * 获取当前连接器类对应的Builder类 + * @access public + * @return string + */ + public function getBuilder() + { + if (!empty($this->builder)) { + return $this->builder; + } else { + return $this->getConfig('builder') ?: '\\think\\db\\builder\\' . ucfirst($this->getConfig('type')); + } + } + /** * 调用Query类的查询方法 * @access public @@ -270,10 +283,7 @@ abstract class Connection } // 记录当前字段属性大小写设置 $this->attrCase = $params[PDO::ATTR_CASE]; - // 记录数据集返回类型 - if (isset($config['resultset_type'])) { - $this->resultSetType = $config['resultset_type']; - } + // 数据返回类型 if (isset($config['result_type'])) { $this->fetchType = $config['result_type']; @@ -511,11 +521,13 @@ abstract class Connection protected function bindParam($bind) { foreach ($bind as $key => $val) { - if (is_numeric($key)) { - $key = $key + 1; + $param = is_numeric($key) ? $key + 1 : ':' . $key; + if (is_array($val)) { + array_unshift($val, $param); + $result = call_user_func_array([$this->PDOStatement, 'bindParam'], $val); + } else { + $result = $this->PDOStatement->bindValue($param, $val); } - array_unshift($val, $key); - $result = call_user_func_array([$this->PDOStatement, 'bindParam'], $val); if (!$result) { $param = array_shift($val); throw new BindParamException( @@ -529,11 +541,11 @@ abstract class Connection } /** - * 获得数据集 + * 获得数据集数组 * @access protected * @param bool $pdo 是否返回PDOStatement * @param bool $procedure 是否存储过程 - * @return mixed + * @return array */ protected function getResult($pdo = false, $procedure = false) { @@ -547,11 +559,6 @@ abstract class Connection } $result = $this->PDOStatement->fetchAll($this->fetchType); $this->numRows = count($result); - - if ('collection' == $this->resultSetType) { - // 返回数据集Collection对象 - $result = new Collection($result); - } return $result; } @@ -745,7 +752,10 @@ abstract class Connection */ public function close() { - $this->linkID = null; + $this->linkID = null; + $this->linkWrite = null; + $this->linkRead = null; + $this->links = []; } /** diff --git a/core/library/think/db/Query.php b/core/library/think/db/Query.php index 24f5ce2a..26a52c85 100644 --- a/core/library/think/db/Query.php +++ b/core/library/think/db/Query.php @@ -124,8 +124,7 @@ class Query */ protected function setBuilder() { - $builder = $this->connection->getConfig('builder') ?: $this->connection->getConfig('type'); - $class = false !== strpos($builder, '\\') ? $builder : '\\think\\db\\builder\\' . ucfirst($builder); + $class = $this->connection->getBuilder(); $this->builder = new $class($this->connection, $this); } @@ -229,8 +228,8 @@ class Query /** * 执行语句 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 + * @param string $sql sql指令 + * @param array $bind 参数绑定 * @return int * @throws BindParamException * @throws PDOException @@ -243,7 +242,7 @@ class Query /** * 获取最近插入的ID * @access public - * @param string $sequence 自增序列名 + * @param string $sequence 自增序列名 * @return string */ public function getLastInsID($sequence = null) @@ -390,7 +389,7 @@ class Query * @access public * @param string $field 字段名 * @param mixed $default 默认值 - * @param bool $force 强制转为数字类型 + * @param bool $force 强制转为数字类型 * @return mixed */ public function value($field, $default = null, $force = false) @@ -509,10 +508,17 @@ class Query * COUNT查询 * @access public * @param string $field 字段名 - * @return integer + * @return integer|string */ public function count($field = '*') { + if (isset($this->options['group'])) { + // 支持GROUP + $options = $this->getOptions(); + $subSql = $this->options($options)->field('count(' . $field . ')')->bind($this->bind)->buildSql(); + return $this->table([$subSql => '_group_count_'])->value('COUNT(*) AS tp_count', 0, true); + } + return $this->value('COUNT(' . $field . ') AS tp_count', 0, true); } @@ -694,7 +700,7 @@ class Query * 获取Join表名及别名 支持 * ['prefix_table或者子查询'=>'alias'] 'prefix_table alias' 'table alias' * @access public - * @param array|string $join + * @param array|string $join * @return array|string */ protected function getJoinTable($join, &$alias = null) @@ -817,8 +823,8 @@ class Query /** * 字段值增长 * @access public - * @param string|array $field 字段名 - * @param integer $step 增长值 + * @param string|array $field 字段名 + * @param integer $step 增长值 * @return $this */ public function inc($field, $step = 1) @@ -833,8 +839,8 @@ class Query /** * 字段值减少 * @access public - * @param string|array $field 字段名 - * @param integer $step 增长值 + * @param string|array $field 字段名 + * @param integer $step 增长值 * @return $this */ public function dec($field, $step = 1) @@ -849,8 +855,8 @@ class Query /** * 使用表达式设置数据 * @access public - * @param string $field 字段名 - * @param string $value 字段值 + * @param string $field 字段名 + * @param string $value 字段值 * @return $this */ public function exp($field, $value) @@ -975,8 +981,8 @@ class Query /** * 指定Null查询条件 * @access public - * @param mixed $field 查询字段 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNull($field, $logic = 'AND') @@ -988,8 +994,8 @@ class Query /** * 指定NotNull查询条件 * @access public - * @param mixed $field 查询字段 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotNull($field, $logic = 'AND') @@ -1001,8 +1007,8 @@ class Query /** * 指定Exists查询条件 * @access public - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereExists($condition, $logic = 'AND') @@ -1014,8 +1020,8 @@ class Query /** * 指定NotExists查询条件 * @access public - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotExists($condition, $logic = 'AND') @@ -1027,9 +1033,9 @@ class Query /** * 指定In查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereIn($field, $condition, $logic = 'AND') @@ -1041,9 +1047,9 @@ class Query /** * 指定NotIn查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotIn($field, $condition, $logic = 'AND') @@ -1055,9 +1061,9 @@ class Query /** * 指定Like查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereLike($field, $condition, $logic = 'AND') @@ -1069,9 +1075,9 @@ class Query /** * 指定NotLike查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotLike($field, $condition, $logic = 'AND') @@ -1083,9 +1089,9 @@ class Query /** * 指定Between查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereBetween($field, $condition, $logic = 'AND') @@ -1097,9 +1103,9 @@ class Query /** * 指定NotBetween查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotBetween($field, $condition, $logic = 'AND') @@ -1111,9 +1117,9 @@ class Query /** * 指定Exp查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereExp($field, $condition, $logic = 'AND') @@ -1169,8 +1175,10 @@ class Query $this->options['multi'][$logic][$field][] = $where[$field]; } elseif (is_null($condition)) { // 字段相等查询 - $where[$field] = ['eq', $op]; - $this->options['multi'][$logic][$field][] = $where[$field]; + $where[$field] = ['eq', $op]; + if ('AND' != $logic) { + $this->options['multi'][$logic][$field][] = $where[$field]; + } } else { $where[$field] = [$op, $condition, isset($param[2]) ? $param[2] : null]; if ('exp' == strtolower($op) && isset($param[2]) && is_array($param[2])) { @@ -1200,8 +1208,8 @@ class Query /** * 检查是否存在一个字段多次查询条件 * @access public - * @param string $field 查询字段 - * @param string $logic 查询逻辑 and or xor + * @param string $field 查询字段 + * @param string $logic 查询逻辑 and or xor * @return bool */ private function checkMultiField($field, $logic) @@ -1212,8 +1220,8 @@ class Query /** * 去除某个查询条件 * @access public - * @param string $field 查询字段 - * @param string $logic 查询逻辑 and or xor + * @param string $field 查询字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function removeWhereField($field, $logic = 'AND') @@ -1228,7 +1236,7 @@ class Query /** * 去除查询参数 * @access public - * @param string|bool $option 参数名 true 表示去除所有参数 + * @param string|bool $option 参数名 true 表示去除所有参数 * @return $this */ public function removeOption($option = true) @@ -1278,14 +1286,14 @@ class Query * @param int|array $listRows 每页数量 数组表示配置参数 * @param int|bool $simple 是否简洁模式或者总记录数 * @param array $config 配置参数 - * page:当前页, - * path:url路径, - * query:url额外参数, - * fragment:url锚点, - * var_page:分页变量, - * list_rows:每页数量 - * type:分页类名 - * @return \think\paginator\Collection + * page:当前页, + * path:url路径, + * query:url额外参数, + * fragment:url锚点, + * var_page:分页变量, + * list_rows:每页数量 + * type:分页类名 + * @return \think\Paginator * @throws DbException */ public function paginate($listRows = null, $simple = false, $config = []) @@ -1315,9 +1323,9 @@ class Query if (!isset($total) && !$simple) { $options = $this->getOptions(); - if (isset($options['order'])) { - unset($this->options['order']); - } + + unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']); + $bind = $this->bind; $total = $this->count(); $results = $this->options($options)->bind($bind)->page($page, $listRows)->select(); @@ -1902,15 +1910,19 @@ class Query } } $this->via(); - $this->options['with'] = $with; + if (isset($this->options['with'])) { + $this->options['with'] = array_merge($this->options['with'], $with); + } else { + $this->options['with'] = $with; + } return $this; } /** * 关联统计 * @access public - * @param string|array $relation 关联方法名 - * @param bool $subQuery 是否使用子查询 + * @param string|array $relation 关联方法名 + * @param bool $subQuery 是否使用子查询 * @return $this */ public function withCount($relation, $subQuery = true) @@ -1967,12 +1979,22 @@ class Query /** * 设置关联查询 * @access public - * @param string $relation 关联名称 + * @param string|array $relation 关联名称 * @return $this */ public function relation($relation) { - $this->options['relation'] = $relation; + if (empty($relation)) { + return $this; + } + if (is_string($relation)) { + $relation = explode(',', $relation); + } + if (isset($this->options['relation'])) { + $this->options['relation'] = array_mrege($this->options['relation'], $relation); + } else { + $this->options['relation'] = $relation; + } return $this; } @@ -2280,29 +2302,35 @@ class Query // 数据列表读取后的处理 if (!empty($this->model)) { // 生成模型对象 - $model = $this->model; + $modelName = $this->model; if (count($resultSet) > 0) { foreach ($resultSet as $key => $result) { /** @var Model $result */ - $result = new $model($result); - $result->isUpdate(true); + $model = new $modelName($result); + $model->isUpdate(true); + // 关联查询 if (!empty($options['relation'])) { - $result->relationQuery($options['relation']); + $model->relationQuery($options['relation']); } // 关联统计 if (!empty($options['with_count'])) { - $result->relationCount($result, $options['with_count']); + $model->relationCount($model, $options['with_count']); } - $resultSet[$key] = $result; + $resultSet[$key] = $model; } if (!empty($options['with'])) { // 预载入 - $result->eagerlyResultSet($resultSet, $options['with'], is_object($resultSet) ? get_class($resultSet) : ''); + $model->eagerlyResultSet($resultSet, $options['with']); } + // 模型数据集转换 + $resultSet = $model->toCollection($resultSet); + } else { + $resultSet = (new $modelName)->toCollection($resultSet); } - // 模型数据集转换 - $resultSet = (new $model)->toCollection($resultSet); + } elseif ('collection' == $this->connection->getConfig('resultset_type')) { + // 返回Collection对象 + $resultSet = new Collection($resultSet); } // 返回结果处理 if (!empty($options['fail']) && count($resultSet) == 0) { @@ -2394,7 +2422,7 @@ class Query } // 预载入查询 if (!empty($options['with'])) { - $data->eagerlyResult($data, $options['with'], is_object($result) ? get_class($result) : ''); + $data->eagerlyResult($data, $options['with']); } // 关联统计 if (!empty($options['with_count'])) { @@ -2670,8 +2698,8 @@ class Query /** * 注册回调方法 * @access public - * @param string $event 事件名 - * @param callable $callback 回调方法 + * @param string $event 事件名 + * @param callable $callback 回调方法 * @return void */ public static function event($event, $callback) @@ -2682,8 +2710,8 @@ class Query /** * 触发事件 * @access protected - * @param string $event 事件名 - * @param mixed $options 当前查询参数 + * @param string $event 事件名 + * @param mixed $options 当前查询参数 * @return bool */ protected function trigger($event, $options = []) diff --git a/core/library/think/db/connector/Mysql.php b/core/library/think/db/connector/Mysql.php index 31435694..0a14e48e 100644 --- a/core/library/think/db/connector/Mysql.php +++ b/core/library/think/db/connector/Mysql.php @@ -21,6 +21,8 @@ use think\Log; class Mysql extends Connection { + protected $builder = '\\think\\db\\builder\\Mysql'; + /** * 解析pdo连接的dsn信息 * @access protected diff --git a/core/library/think/db/connector/Pgsql.php b/core/library/think/db/connector/Pgsql.php index 4f8756ca..04c3e782 100644 --- a/core/library/think/db/connector/Pgsql.php +++ b/core/library/think/db/connector/Pgsql.php @@ -19,6 +19,7 @@ use think\db\Connection; */ class Pgsql extends Connection { + protected $builder = '\\think\\db\\builder\\Pgsql'; /** * 解析pdo连接的dsn信息 diff --git a/core/library/think/db/connector/Sqlite.php b/core/library/think/db/connector/Sqlite.php index 4a08c740..a0e0873c 100644 --- a/core/library/think/db/connector/Sqlite.php +++ b/core/library/think/db/connector/Sqlite.php @@ -20,6 +20,8 @@ use think\db\Connection; class Sqlite extends Connection { + protected $builder = '\\think\\db\\builder\\Sqlite'; + /** * 解析pdo连接的dsn信息 * @access protected diff --git a/core/library/think/db/connector/Sqlsrv.php b/core/library/think/db/connector/Sqlsrv.php index 18148051..20d3491d 100644 --- a/core/library/think/db/connector/Sqlsrv.php +++ b/core/library/think/db/connector/Sqlsrv.php @@ -25,7 +25,7 @@ class Sqlsrv extends Connection PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_STRINGIFY_FETCHES => false, ]; - + protected $builder = '\\think\\db\\builder\\Sqlsrv'; /** * 解析pdo连接的dsn信息 * @access protected diff --git a/core/library/think/model/Collection.php b/core/library/think/model/Collection.php new file mode 100644 index 00000000..08e11ad8 --- /dev/null +++ b/core/library/think/model/Collection.php @@ -0,0 +1,79 @@ + +// +---------------------------------------------------------------------- + +namespace think\model; + +use think\Collection as BaseCollection; +use think\Model; + +class Collection extends BaseCollection +{ + /** + * 延迟预载入关联查询 + * @access public + * @param mixed $relation 关联 + * @return $this + */ + public function load($relation) + { + $item = current($this->items); + $item->eagerlyResultSet($this->items, $relation); + return $this; + } + + /** + * 设置需要隐藏的输出属性 + * @access public + * @param array $hidden 属性列表 + * @param bool $override 是否覆盖 + * @return $this + */ + public function hidden($hidden = [], $override = false) + { + $this->each(function ($model) use ($hidden, $override) { + /** @var Model $model */ + $model->hidden($hidden, $override); + }); + return $this; + } + + /** + * 设置需要输出的属性 + * @param array $visible + * @param bool $override 是否覆盖 + * @return $this + */ + public function visible($visible = [], $override = false) + { + $this->each(function ($model) use ($visible, $override) { + /** @var Model $model */ + $model->visible($visible, $override); + }); + return $this; + } + + /** + * 设置需要追加的输出属性 + * @access public + * @param array $append 属性列表 + * @param bool $override 是否覆盖 + * @return $this + */ + public function append($append = [], $override = false) + { + $this->each(function ($model) use ($append, $override) { + /** @var Model $model */ + $model->append($append, $override); + }); + return $this; + } + +} diff --git a/core/library/think/model/Merge.php b/core/library/think/model/Merge.php index b876c2f3..47d54923 100644 --- a/core/library/think/model/Merge.php +++ b/core/library/think/model/Merge.php @@ -11,7 +11,6 @@ namespace think\model; -use think\Db; use think\db\Query; use think\Model; @@ -40,9 +39,9 @@ class Merge extends Model /** * 查找单条记录 * @access public - * @param mixed $data 主键值或者查询条件(闭包) - * @param string $with 关联预查询 - * @param bool $cache 是否缓存 + * @param mixed $data 主键值或者查询条件(闭包) + * @param string|array $with 关联预查询 + * @param bool $cache 是否缓存 * @return \think\Model */ public static function get($data = null, $with = [], $cache = false) @@ -78,11 +77,11 @@ class Merge extends Model /** * 获取关联模型的字段 并解决混淆 * @access protected - * @param \think\db\Query $query 查询对象 - * @param string $name 模型名称 - * @param string $table 关联表名称 - * @param array $map 字段映射 - * @param array $fields 查询字段 + * @param \think\db\Query $query 查询对象 + * @param string $name 模型名称 + * @param string $table 关联表名称 + * @param array $map 字段映射 + * @param array $fields 查询字段 * @return array */ protected static function getModelField($query, $name, $table = '', $map = [], $fields = []) @@ -104,8 +103,9 @@ class Merge extends Model /** * 查找所有记录 * @access public - * @param mixed $data 主键列表或者查询条件(闭包) - * @param string $with 关联预查询 + * @param mixed $data 主键列表或者查询条件(闭包) + * @param array|string $with 关联预查询 + * @param bool $cache * @return array|false|string */ public static function all($data = null, $with = [], $cache = false) @@ -118,10 +118,10 @@ class Merge extends Model /** * 处理写入的模型数据 * @access public - * @param string $model 模型名称 - * @param array $data 数据 - * @param bool $insert 是否新增 - * @return void + * @param string $model 模型名称 + * @param array $data 数据 + * @param bool $insert 是否新增 + * @return array */ protected function parseData($model, $data, $insert = false) { @@ -144,10 +144,11 @@ class Merge extends Model /** * 保存模型数据 以及关联数据 * @access public - * @param mixed $data 数据 - * @param array $where 更新条件 - * @param string $sequence 自增序列名 - * @return integer|false + * @param mixed $data 数据 + * @param array $where 更新条件 + * @param string $sequence 自增序列名 + * @return false|int + * @throws \Exception */ public function save($data = [], $where = [], $sequence = null) { @@ -158,7 +159,7 @@ class Merge extends Model } // 数据对象赋值 foreach ($data as $key => $value) { - $this->setAttr($key, $value); + $this->setAttr($key, $value, $data); } if (!empty($where)) { $this->isUpdate = true; @@ -278,7 +279,8 @@ class Merge extends Model /** * 删除当前的记录 并删除关联数据 * @access public - * @return integer + * @return int + * @throws \Exception */ public function delete() { diff --git a/core/library/think/model/Relation.php b/core/library/think/model/Relation.php index e940a78d..3d56091b 100644 --- a/core/library/think/model/Relation.php +++ b/core/library/think/model/Relation.php @@ -15,24 +15,24 @@ use think\db\Query; use think\Exception; use think\Model; +/** + * Class Relation + * @package think\model + * + * @mixin Query + */ abstract class Relation { // 父模型对象 protected $parent; /** @var Model 当前关联的模型类 */ protected $model; + /** @var Query 关联模型查询对象 */ + protected $query; // 关联表外键 protected $foreignKey; // 关联表主键 protected $localKey; - // 数据表别名 - protected $alias; - // 当前关联的JOIN类型 - protected $joinType; - // 关联模型查询对象 - protected $query; - // 关联查询条件 - protected $where; // 关联查询参数 protected $option; // 基础查询 @@ -71,25 +71,12 @@ abstract class Relation /** * 封装关联数据集 * @access public - * @param array $resultSet 数据集 - * @param string $class 数据集类名 + * @param array $resultSet 数据集 * @return mixed */ - protected function resultSetBuild($resultSet, $class = '') + protected function resultSetBuild($resultSet) { - return $class ? new $class($resultSet) : $resultSet; - } - - /** - * 设置当前关联定义的数据表别名 - * @access public - * @param array $alias 别名定义 - * @return $this - */ - public function setAlias($alias) - { - $this->alias = $alias; - return $this; + return (new $this->model)->toCollection($resultSet); } /** @@ -103,6 +90,13 @@ abstract class Relation return $this; } + /** + * 执行基础查询(进执行一次) + * @access protected + * @return void + */ + abstract protected function baseQuery(); + public function __call($method, $args) { if ($this->query) { diff --git a/core/library/think/model/relation/BelongsTo.php b/core/library/think/model/relation/BelongsTo.php index 8f26d033..d1c4b9bd 100644 --- a/core/library/think/model/relation/BelongsTo.php +++ b/core/library/think/model/relation/BelongsTo.php @@ -11,6 +11,7 @@ namespace think\model\relation; +use think\Loader; use think\Model; class BelongsTo extends OneToOne @@ -22,29 +23,32 @@ class BelongsTo extends OneToOne * @param string $model 模型名 * @param string $foreignKey 关联外键 * @param string $localKey 关联主键 - * @param array $alias 别名定义 * @param string $joinType JOIN类型 */ - public function __construct(Model $parent, $model, $foreignKey, $localKey, $alias = [], $joinType = 'INNER') + public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER') { $this->parent = $parent; $this->model = $model; $this->foreignKey = $foreignKey; $this->localKey = $localKey; - $this->alias = $alias; $this->joinType = $joinType; $this->query = (new $model)->db(); } /** * 延迟获取关联数据 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 * @access public + * @return array|false|\PDOStatement|string|Model */ - public function getRelation() + public function getRelation($subRelation = '', $closure = null) { $foreignKey = $this->foreignKey; - $localKey = $this->localKey; - return $this->query->where($localKey, $this->parent->$foreignKey)->find(); + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + return $this->query->where($this->localKey, $this->parent->$foreignKey)->relation($subRelation)->find(); } /** @@ -54,10 +58,9 @@ class BelongsTo extends OneToOne * @param string $relation 当前关联名 * @param string $subRelation 子关联名 * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 * @return void */ - protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure, $class) + protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -71,26 +74,29 @@ class BelongsTo extends OneToOne } if (!empty($range)) { - $this->where[$localKey] = ['in', $range]; - $data = $this->eagerlyWhere($this, [ + $data = $this->eagerlyWhere($this, [ $localKey => [ 'in', $range, ], ], $localKey, $relation, $subRelation, $closure); - + // 关联属性名 + $attr = Loader::parseName($relation); // 关联数据封装 foreach ($resultSet as $result) { - if (!isset($data[$result->$foreignKey])) { - $data[$result->$foreignKey] = []; + // 关联模型 + if (!isset($data[$result->$localKey])) { + $relationModel = null; + } else { + $relationModel = $data[$result->$localKey]; } - $relationModel = $this->resultSetBuild($data[$result->$foreignKey], $class); - if (!empty($this->bindAttr)) { + + if ($relationModel && !empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($relationModel, $result, $this->bindAttr); } // 设置关联属性 - $result->setAttr($relation, $relationModel); + $result->setAttr($attr, $relationModel); } } } @@ -102,25 +108,25 @@ class BelongsTo extends OneToOne * @param string $relation 当前关联名 * @param string $subRelation 子关联名 * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 * @return void */ - protected function eagerlyOne(&$result, $relation, $subRelation, $closure, $class) + protected function eagerlyOne(&$result, $relation, $subRelation, $closure) { $localKey = $this->localKey; $foreignKey = $this->foreignKey; $data = $this->eagerlyWhere($this, [$localKey => $result->$foreignKey], $localKey, $relation, $subRelation, $closure); - // 关联数据封装 - if (!isset($data[$result->$foreignKey])) { - $data[$result->$foreignKey] = []; + // 关联模型 + if (!isset($data[$result->$localKey])) { + $relationModel = null; + } else { + $relationModel = $data[$result->$localKey]; } - $relationModel = $this->resultSetBuild($data[$result->$foreignKey], $class); - if (!empty($this->bindAttr)) { + if ($relationModel && !empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($relationModel, $result, $this->bindAttr); } // 设置关联属性 - $result->setAttr($relation, $relationModel); + $result->setAttr(Loader::parseName($relation), $relationModel); } } diff --git a/core/library/think/model/relation/BelongsToMany.php b/core/library/think/model/relation/BelongsToMany.php index 428620a2..c85a8c0c 100644 --- a/core/library/think/model/relation/BelongsToMany.php +++ b/core/library/think/model/relation/BelongsToMany.php @@ -11,9 +11,9 @@ namespace think\model\relation; -use think\Db; use think\db\Query; use think\Exception; +use think\Loader; use think\Model; use think\model\Pivot; use think\model\Relation; @@ -26,37 +26,40 @@ class BelongsToMany extends Relation /** * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $table 中间表名 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $table 中间表名 * @param string $foreignKey 关联模型外键 - * @param string $localKey 当前模型关联键 - * @param array $alias 别名定义 + * @param string $localKey 当前模型关联键 */ - public function __construct(Model $parent, $model, $table, $foreignKey, $localKey, $alias = []) + public function __construct(Model $parent, $model, $table, $foreignKey, $localKey) { $this->parent = $parent; $this->model = $model; $this->foreignKey = $foreignKey; $this->localKey = $localKey; $this->middle = $table; - $this->alias = $alias; $this->query = (new $model)->db(); } /** * 延迟获取关联数据 - * @access public + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return false|\PDOStatement|string|\think\Collection */ - public function getRelation() + public function getRelation($subRelation = '', $closure = null) { $foreignKey = $this->foreignKey; $localKey = $this->localKey; $middle = $this->middle; + if ($closure) { + call_user_func_array($closure, [& $this->query]); + } // 关联查询 $pk = $this->parent->getPk(); $condition['pivot.' . $localKey] = $this->parent->$pk; - $result = $this->belongsToManyQuery($middle, $foreignKey, $localKey, $condition)->select(); + $result = $this->belongsToManyQuery($middle, $foreignKey, $localKey, $condition)->relation($subRelation)->select(); foreach ($result as $set) { $pivot = []; foreach ($set->getData() as $key => $val) { @@ -76,14 +79,13 @@ class BelongsToMany extends Relation /** * 预载入关联查询(数据集) * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class) + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -105,14 +107,15 @@ class BelongsToMany extends Relation $range, ], ], $relation, $subRelation); - + // 关联属性名 + $attr = Loader::parseName($relation); // 关联数据封装 foreach ($resultSet as $result) { if (!isset($data[$result->$pk])) { $data[$result->$pk] = []; } - $result->setAttr($relation, $this->resultSetBuild($data[$result->$pk], $class)); + $result->setAttr($attr, $this->resultSetBuild($data[$result->$pk])); } } } @@ -120,14 +123,13 @@ class BelongsToMany extends Relation /** * 预载入关联查询(单个数据) * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) + public function eagerlyResult(&$result, $relation, $subRelation, $closure) { $pk = $result->getPk(); if (isset($result->$pk)) { @@ -139,15 +141,15 @@ class BelongsToMany extends Relation if (!isset($data[$pk])) { $data[$pk] = []; } - $result->setAttr($relation, $this->resultSetBuild($data[$pk], $class)); + $result->setAttr(Loader::parseName($relation), $this->resultSetBuild($data[$pk])); } } /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 * @return integer */ public function relationCount($result, $closure) @@ -164,20 +166,25 @@ class BelongsToMany extends Relation /** * 获取关联统计子查询 * @access public - * @param \Closure $closure 闭包 + * @param \Closure $closure 闭包 * @return string */ public function getRelationCountQuery($closure) { - return $this->belongsToManyQuery($this->middle, $this->foreignKey, $this->localKey, ['pivot.' . $this->localKey => ['exp', '=' . $this->parent->getTable() . '.' . $this->parent->getPk()]])->fetchSql()->count(); + return $this->belongsToManyQuery($this->middle, $this->foreignKey, $this->localKey, [ + 'pivot.' . $this->localKey => [ + 'exp', + '=' . $this->parent->getTable() . '.' . $this->parent->getPk() + ] + ])->fetchSql()->count(); } /** * 多对多 关联模型预查询 * @access public - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param string $subRelation 子关联 + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param string $subRelation 子关联 * @return array */ protected function eagerlyManyToMany($where, $relation, $subRelation = '') @@ -207,10 +214,10 @@ class BelongsToMany extends Relation /** * BELONGS TO MANY 关联查询 * @access public - * @param string $table 中间表名 - * @param string $foreignKey 关联模型关联键 - * @param string $localKey 当前模型关联键 - * @param array $condition 关联查询条件 + * @param string $table 中间表名 + * @param string $foreignKey 关联模型关联键 + * @param string $localKey 当前模型关联键 + * @param array $condition 关联查询条件 * @return Query */ protected function belongsToManyQuery($table, $foreignKey, $localKey, $condition = []) @@ -227,8 +234,8 @@ class BelongsToMany extends Relation /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 - * @param array $pivot 中间表额外数据 + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param array $pivot 中间表额外数据 * @return integer */ public function save($data, array $pivot = []) @@ -240,9 +247,9 @@ class BelongsToMany extends Relation /** * 批量保存当前关联数据对象 * @access public - * @param array $dataSet 数据集 - * @param array $pivot 中间表额外数据 - * @param bool $samePivot 额外数据是否相同 + * @param array $dataSet 数据集 + * @param array $pivot 中间表额外数据 + * @param bool $samePivot 额外数据是否相同 * @return integer */ public function saveAll(array $dataSet, array $pivot = [], $samePivot = false) @@ -262,9 +269,10 @@ class BelongsToMany extends Relation /** * 附加关联的一个中间表数据 * @access public - * @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键 - * @param array $pivot 中间表额外数据 - * @return integer + * @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键 + * @param array $pivot 中间表额外数据 + * @return int + * @throws Exception */ public function attach($data, $pivot = []) { @@ -304,8 +312,8 @@ class BelongsToMany extends Relation /** * 解除关联的一个中间表数据 * @access public - * @param integer|array $data 数据 可以使用关联对象的主键 - * @param bool $relationDel 是否同时删除关联表数据 + * @param integer|array $data 数据 可以使用关联对象的主键 + * @param bool $relationDel 是否同时删除关联表数据 * @return integer */ public function detach($data = null, $relationDel = false) diff --git a/core/library/think/model/relation/HasMany.php b/core/library/think/model/relation/HasMany.php index c9235bac..3094bb55 100644 --- a/core/library/think/model/relation/HasMany.php +++ b/core/library/think/model/relation/HasMany.php @@ -13,6 +13,7 @@ namespace think\model\relation; use think\Db; use think\db\Query; +use think\Loader; use think\Model; use think\model\Relation; @@ -21,42 +22,44 @@ class HasMany extends Relation /** * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 * @param string $foreignKey 关联外键 - * @param string $localKey 关联主键 - * @param array $alias 别名定义 + * @param string $localKey 关联主键 */ - public function __construct(Model $parent, $model, $foreignKey, $localKey, $alias = []) + public function __construct(Model $parent, $model, $foreignKey, $localKey) { $this->parent = $parent; $this->model = $model; $this->foreignKey = $foreignKey; $this->localKey = $localKey; - $this->alias = $alias; $this->query = (new $model)->db(); } /** * 延迟获取关联数据 - * @access public + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return false|\PDOStatement|string|\think\Collection */ - public function getRelation() + public function getRelation($subRelation = '', $closure = null) { - return $this->select(); + if ($closure) { + call_user_func_array($closure, [& $this->query]); + } + return $this->relation($subRelation)->select(); } /** * 预载入关联查询 - * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class) + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) { $localKey = $this->localKey; $range = []; @@ -68,35 +71,34 @@ class HasMany extends Relation } if (!empty($range)) { - $this->where[$this->foreignKey] = ['in', $range]; - $data = $this->eagerlyOneToMany($this, [ + $data = $this->eagerlyOneToMany($this, [ $this->foreignKey => [ 'in', $range, ], ], $relation, $subRelation, $closure); - + // 关联属性名 + $attr = Loader::parseName($relation); // 关联数据封装 foreach ($resultSet as $result) { if (!isset($data[$result->$localKey])) { $data[$result->$localKey] = []; } - $result->setAttr($relation, $this->resultSetBuild($data[$result->$localKey], $class)); + $result->setAttr($attr, $this->resultSetBuild($data[$result->$localKey])); } } } /** * 预载入关联查询 - * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) + public function eagerlyResult(&$result, $relation, $subRelation, $closure) { $localKey = $this->localKey; @@ -106,15 +108,15 @@ class HasMany extends Relation if (!isset($data[$result->$localKey])) { $data[$result->$localKey] = []; } - $result->setAttr($relation, $this->resultSetBuild($data[$result->$localKey], $class)); + $result->setAttr(Loader::parseName($relation), $this->resultSetBuild($data[$result->$localKey])); } } /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 * @return integer */ public function relationCount($result, $closure) @@ -123,7 +125,7 @@ class HasMany extends Relation $count = 0; if (isset($result->$localKey)) { if ($closure) { - call_user_func_array($closure, [ & $this->query]); + call_user_func_array($closure, [& $this->query]); } $count = $this->query->where([$this->foreignKey => $result->$localKey])->count(); } @@ -133,26 +135,31 @@ class HasMany extends Relation /** * 创建关联统计子查询 * @access public - * @param \Closure $closure 闭包 + * @param \Closure $closure 闭包 * @return string */ public function getRelationCountQuery($closure) { if ($closure) { - call_user_func_array($closure, [ & $this->query]); + call_user_func_array($closure, [& $this->query]); } - return $this->query->where([$this->foreignKey => ['exp', '=' . $this->parent->getTable() . '.' . $this->parent->getPk()]])->fetchSql()->count(); + return $this->query->where([ + $this->foreignKey => [ + 'exp', + '=' . $this->parent->getTable() . '.' . $this->parent->getPk() + ] + ])->fetchSql()->count(); } /** * 一对多 关联模型预查询 * @access public - * @param object $model 关联模型对象 - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param string $subRelation 子关联 - * @param bool $closure + * @param object $model 关联模型对象 + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param bool $closure * @return array */ protected function eagerlyOneToMany($model, $where, $relation, $subRelation = '', $closure = false) @@ -160,7 +167,7 @@ class HasMany extends Relation $foreignKey = $this->foreignKey; // 预载入关联查询 支持嵌套预载入 if ($closure) { - call_user_func_array($closure, [ & $model]); + call_user_func_array($closure, [& $model]); } $list = $model->where($where)->with($subRelation)->select(); @@ -175,7 +182,7 @@ class HasMany extends Relation /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 * @return integer */ public function save($data) @@ -192,7 +199,7 @@ class HasMany extends Relation /** * 批量保存当前关联数据对象 * @access public - * @param array $dataSet 数据集 + * @param array $dataSet 数据集 * @return integer */ public function saveAll(array $dataSet) @@ -207,16 +214,15 @@ class HasMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param Model $model 模型对象 - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 * @return Query */ - public function has($model, $operator = '>=', $count = 1, $id = '*') + public function has($operator = '>=', $count = 1, $id = '*') { $table = $this->query->getTable(); - return $model->db()->alias('a') + return $this->parent->db()->alias('a') ->join($table . ' b', 'a.' . $this->localKey . '=b.' . $this->foreignKey, $this->joinType) ->group('b.' . $this->foreignKey) ->having('count(' . $id . ')' . $operator . $count); @@ -225,24 +231,25 @@ class HasMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param Model $model 模型对象 - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) * @return Query */ - public function hasWhere($model, $where = []) + public function hasWhere($where = []) { - $table = $this->query->getTable(); + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); + $relation = basename(str_replace('\\', '/', $this->model)); if (is_array($where)) { foreach ($where as $key => $val) { if (false === strpos($key, '.')) { - $where['b.' . $key] = $val; + $where[$relation . '.' . $key] = $val; unset($where[$key]); } } } - return $model->db()->alias('a') - ->field('a.*') - ->join($table . ' b', 'a.' . $this->localKey . '=b.' . $this->foreignKey, $this->joinType) + return $this->parent->db()->alias($model) + ->field($model . '.*') + ->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey) ->where($where); } @@ -254,9 +261,7 @@ class HasMany extends Relation protected function baseQuery() { if (empty($this->baseQuery)) { - if (isset($this->where)) { - $this->query->where($this->where); - } elseif (isset($this->parent->{$this->localKey})) { + if (isset($this->parent->{$this->localKey})) { // 关联查询带入关联条件 $this->query->where($this->foreignKey, $this->parent->{$this->localKey}); } diff --git a/core/library/think/model/relation/HasManyThrough.php b/core/library/think/model/relation/HasManyThrough.php index 2585f354..e1518972 100644 --- a/core/library/think/model/relation/HasManyThrough.php +++ b/core/library/think/model/relation/HasManyThrough.php @@ -26,16 +26,15 @@ class HasManyThrough extends Relation /** * 架构函数 - * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $through 中间模型名 - * @param string $firstkey 关联外键 - * @param string $secondKey 关联外键 - * @param string $localKey 关联主键 - * @param array $alias 别名定义 + * @access public + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $through 中间模型名 + * @param string $foreignKey 关联外键 + * @param string $throughKey 关联外键 + * @param string $localKey 关联主键 */ - public function __construct(Model $parent, $model, $through, $foreignKey, $throughKey, $localKey, $alias = []) + public function __construct(Model $parent, $model, $through, $foreignKey, $throughKey, $localKey) { $this->parent = $parent; $this->model = $model; @@ -43,54 +42,61 @@ class HasManyThrough extends Relation $this->foreignKey = $foreignKey; $this->throughKey = $throughKey; $this->localKey = $localKey; - $this->alias = $alias; $this->query = (new $model)->db(); } /** * 延迟获取关联数据 - * @access public + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return false|\PDOStatement|string|\think\Collection */ - public function getRelation() + public function getRelation($subRelation = '', $closure = null) { - return $this->select(); + if ($closure) { + call_user_func_array($closure, [& $this->query]); + } + return $this->relation($subRelation)->select(); } /** * 预载入关联查询 * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @param string $class 数据集对象名 为空表示数组 * @return void */ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class) - {} + { + } /** * 预载入关联查询 返回模型对象 * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @param string $class 数据集对象名 为空表示数组 * @return void */ public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) - {} + { + } /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 * @return integer */ public function relationCount($result, $closure) - {} + { + } /** * 执行基础查询(进执行一次) diff --git a/core/library/think/model/relation/HasOne.php b/core/library/think/model/relation/HasOne.php index 41667e2a..2c0cf350 100644 --- a/core/library/think/model/relation/HasOne.php +++ b/core/library/think/model/relation/HasOne.php @@ -11,6 +11,8 @@ namespace think\model\relation; +use think\db\Query; +use think\Loader; use think\Model; class HasOne extends OneToOne @@ -18,72 +20,74 @@ class HasOne extends OneToOne /** * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 * @param string $foreignKey 关联外键 - * @param string $localKey 关联主键 - * @param array $alias 别名定义 - * @param string $joinType JOIN类型 + * @param string $localKey 关联主键 + * @param string $joinType JOIN类型 */ - public function __construct(Model $parent, $model, $foreignKey, $localKey, $alias = [], $joinType = 'INNER') + public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER') { $this->parent = $parent; $this->model = $model; $this->foreignKey = $foreignKey; $this->localKey = $localKey; - $this->alias = $alias; $this->joinType = $joinType; $this->query = (new $model)->db(); } /** * 延迟获取关联数据 - * @access public + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return array|false|\PDOStatement|string|Model */ - public function getRelation() + public function getRelation($subRelation = '', $closure = null) { // 执行关联定义方法 $localKey = $this->localKey; - + if ($closure) { + call_user_func_array($closure, [& $this->query]); + } // 判断关联类型执行查询 - return $this->query->where($this->foreignKey, $this->parent->$localKey)->find(); + return $this->query->where($this->foreignKey, $this->parent->$localKey)->relation($subRelation)->find(); } /** * 根据关联条件查询当前模型 * @access public - * @param Model $model 模型对象 - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) * @return Query */ - public function hasWhere($model, $where = []) + public function hasWhere($where = []) { - $table = $this->query->getTable(); + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); + $relation = basename(str_replace('\\', '/', $this->model)); if (is_array($where)) { foreach ($where as $key => $val) { if (false === strpos($key, '.')) { - $where['b.' . $key] = $val; + $where[$relation . '.' . $key] = $val; unset($where[$key]); } } } - return $model->db()->alias('a') - ->field('a.*') - ->join($table . ' b', 'a.' . $this->localKey . '=b.' . $this->foreignKey, $this->joinType) + return $this->parent->db()->alias($model) + ->field($model . '.*') + ->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $this->joinType) ->where($where); } /** * 预载入关联查询(数据集) * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure, $class) + protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -97,26 +101,28 @@ class HasOne extends OneToOne } if (!empty($range)) { - $this->where[$foreignKey] = ['in', $range]; - $data = $this->eagerlyWhere($this, [ + $data = $this->eagerlyWhere($this, [ $foreignKey => [ 'in', $range, ], ], $foreignKey, $relation, $subRelation, $closure); - + // 关联属性名 + $attr = Loader::parseName($relation); // 关联数据封装 foreach ($resultSet as $result) { + // 关联模型 if (!isset($data[$result->$localKey])) { - $data[$result->$localKey] = []; + $relationModel = null; + } else { + $relationModel = $data[$result->$localKey]; } - $relationModel = $this->resultSetBuild($data[$result->$localKey], $class); - if (!empty($this->bindAttr)) { + if ($relationModel && !empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($relationModel, $result, $this->bindAttr); } // 设置关联属性 - $result->setAttr($relation, $relationModel); + $result->setAttr($attr, $relationModel); } } } @@ -124,28 +130,30 @@ class HasOne extends OneToOne /** * 预载入关联查询(数据) * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - protected function eagerlyOne(&$result, $relation, $subRelation, $closure, $class) + protected function eagerlyOne(&$result, $relation, $subRelation, $closure) { $localKey = $this->localKey; $foreignKey = $this->foreignKey; $data = $this->eagerlyWhere($this, [$foreignKey => $result->$localKey], $foreignKey, $relation, $subRelation, $closure); - // 关联数据封装 + + // 关联模型 if (!isset($data[$result->$localKey])) { - $data[$result->$localKey] = []; + $relationModel = null; + } else { + $relationModel = $data[$result->$localKey]; } - $relationModel = $this->resultSetBuild($data[$result->$localKey], $class); - if (!empty($this->bindAttr)) { + + if ($relationModel && !empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($relationModel, $result, $this->bindAttr); } - $result->setAttr($relation, $relationModel); + $result->setAttr(Loader::parseName($relation), $relationModel); } } diff --git a/core/library/think/model/relation/MorphMany.php b/core/library/think/model/relation/MorphMany.php index abb32ebd..3fc8179c 100644 --- a/core/library/think/model/relation/MorphMany.php +++ b/core/library/think/model/relation/MorphMany.php @@ -13,6 +13,7 @@ namespace think\model\relation; use think\Db; use think\db\Query; +use think\Loader; use think\Model; use think\model\Relation; @@ -27,11 +28,11 @@ class MorphMany extends Relation /** * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $morphKey 关联外键 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $morphKey 关联外键 * @param string $morphType 多态字段名 - * @param string $type 多态类型 + * @param string $type 多态类型 */ public function __construct(Model $parent, $model, $morphKey, $morphType, $type) { @@ -45,24 +46,28 @@ class MorphMany extends Relation /** * 延迟获取关联数据 - * @access public + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return false|\PDOStatement|string|\think\Collection */ - public function getRelation() + public function getRelation($subRelation = '', $closure = null) { - return $this->select(); + if ($closure) { + call_user_func_array($closure, [& $this->query]); + } + return $this->relation($subRelation)->select(); } /** * 预载入关联查询 * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class) + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) { $morphType = $this->morphType; $morphKey = $this->morphKey; @@ -77,19 +82,18 @@ class MorphMany extends Relation } if (!empty($range)) { - $this->where[$morphKey] = ['in', $range]; - $this->where[$morphType] = $type; - $data = $this->eagerlyMorphToMany([ + $data = $this->eagerlyMorphToMany([ $morphKey => ['in', $range], $morphType => $type, ], $relation, $subRelation, $closure); - + // 关联属性名 + $attr = Loader::parseName($relation); // 关联数据封装 foreach ($resultSet as $result) { if (!isset($data[$result->$pk])) { $data[$result->$pk] = []; } - $result->setAttr($relation, $this->resultSetBuild($data[$result->$pk], $class)); + $result->setAttr($attr, $this->resultSetBuild($data[$result->$pk])); } } } @@ -97,27 +101,29 @@ class MorphMany extends Relation /** * 预载入关联查询 * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) + public function eagerlyResult(&$result, $relation, $subRelation, $closure) { $pk = $result->getPk(); if (isset($result->$pk)) { - $data = $this->eagerlyMorphToMany([$this->morphKey => $result->$pk, $this->morphType => $this->type], $relation, $subRelation, $closure); - $result->setAttr($relation, $this->resultSetBuild($data[$result->$pk], $class)); + $data = $this->eagerlyMorphToMany([ + $this->morphKey => $result->$pk, + $this->morphType => $this->type + ], $relation, $subRelation, $closure); + $result->setAttr(Loader::parseName($relation), $this->resultSetBuild($data[$result->$pk])); } } /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 * @return integer */ public function relationCount($result, $closure) @@ -126,7 +132,7 @@ class MorphMany extends Relation $count = 0; if (isset($result->$pk)) { if ($closure) { - call_user_func_array($closure, [ & $this->query]); + call_user_func_array($closure, [& $this->query]); } $count = $this->query->where([$this->morphKey => $result->$pk, $this->morphType => $this->type])->count(); } @@ -136,32 +142,38 @@ class MorphMany extends Relation /** * 获取关联统计子查询 * @access public - * @param \Closure $closure 闭包 + * @param \Closure $closure 闭包 * @return string */ public function getRelationCountQuery($closure) { if ($closure) { - call_user_func_array($closure, [ & $this->query]); + call_user_func_array($closure, [& $this->query]); } - return $this->query->where([$this->morphKey => ['exp', '=' . $this->parent->getTable() . '.' . $this->parent->getPk()], $this->morphType => $this->type])->fetchSql()->count(); + return $this->query->where([ + $this->morphKey => [ + 'exp', + '=' . $this->parent->getTable() . '.' . $this->parent->getPk() + ], + $this->morphType => $this->type + ])->fetchSql()->count(); } /** * 多态一对多 关联模型预查询 - * @access public - * @param object $model 关联模型对象 - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param string $subRelation 子关联 + * @access public + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param bool|\Closure $closure 闭包 * @return array */ protected function eagerlyMorphToMany($where, $relation, $subRelation = '', $closure = false) { // 预载入关联查询 支持嵌套预载入 if ($closure) { - call_user_func_array($closure, [ & $this]); + call_user_func_array($closure, [& $this]); } $list = $this->query->where($where)->with($subRelation)->select(); $morphKey = $this->morphKey; @@ -176,7 +188,7 @@ class MorphMany extends Relation /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 * @return integer */ public function save($data) @@ -196,7 +208,7 @@ class MorphMany extends Relation /** * 批量保存当前关联数据对象 * @access public - * @param array $dataSet 数据集 + * @param array $dataSet 数据集 * @return integer */ public function saveAll(array $dataSet) diff --git a/core/library/think/model/relation/MorphTo.php b/core/library/think/model/relation/MorphTo.php index e6676574..5e1867f2 100644 --- a/core/library/think/model/relation/MorphTo.php +++ b/core/library/think/model/relation/MorphTo.php @@ -11,6 +11,7 @@ namespace think\model\relation; +use think\Exception; use think\Loader; use think\Model; use think\model\Relation; @@ -20,14 +21,16 @@ class MorphTo extends Relation // 多态字段 protected $morphKey; protected $morphType; + // 多态别名 + protected $alias; /** * 架构函数 * @access public - * @param Model $parent 上级模型对象 + * @param Model $parent 上级模型对象 * @param string $morphType 多态字段名 - * @param string $morphKey 外键名 - * @param array $alias 多态别名定义 + * @param string $morphKey 外键名 + * @param array $alias 多态别名定义 */ public function __construct(Model $parent, $morphType, $morphKey, $alias = []) { @@ -39,9 +42,11 @@ class MorphTo extends Relation /** * 延迟获取关联数据 - * @access public + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return mixed */ - public function getRelation() + public function getRelation($subRelation = '', $closure = null) { $morphKey = $this->morphKey; $morphType = $this->morphType; @@ -49,7 +54,7 @@ class MorphTo extends Relation $model = $this->parseModel($this->parent->$morphType); // 主键数据 $pk = $this->parent->$morphKey; - return (new $model)->find($pk); + return (new $model)->relation($subRelation)->find($pk); } /** @@ -72,17 +77,29 @@ class MorphTo extends Relation return $model; } + /** + * 设置多态别名 + * @access public + * @param array $alias 别名定义 + * @return $this + */ + public function setAlias($alias) + { + $this->alias = $alias; + return $this; + } + /** * 预载入关联查询 * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void + * @throws Exception */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class) + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) { $morphKey = $this->morphKey; $morphType = $this->morphType; @@ -95,6 +112,8 @@ class MorphTo extends Relation } if (!empty($range)) { + // 关联属性名 + $attr = Loader::parseName($relation); foreach ($range as $key => $val) { // 多态类型映射 $model = $this->parseModel($key); @@ -107,10 +126,12 @@ class MorphTo extends Relation } foreach ($resultSet as $result) { if ($key == $result->$morphType) { + // 关联模型 if (!isset($data[$result->$morphKey])) { - $data[$result->$morphKey] = []; + throw new Exception('relation data not exists :' . $this->model); + } else { + $result->setAttr($attr, $data[$result->$morphKey]); } - $result->setAttr($relation, $this->resultSetBuild($data[$result->$morphKey], $class)); } } } @@ -120,14 +141,13 @@ class MorphTo extends Relation /** * 预载入关联查询 * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) + public function eagerlyResult(&$result, $relation, $subRelation, $closure) { $morphKey = $this->morphKey; $morphType = $this->morphType; @@ -139,20 +159,21 @@ class MorphTo extends Relation /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 * @return integer */ public function relationCount($result, $closure) - {} + { + } /** * 多态MorphTo 关联模型预查询 - * @access public - * @param object $model 关联模型对象 - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param string $subRelation 子关联 + * @access public + * @param object $model 关联模型对象 + * @param string $relation 关联名 + * @param $result + * @param string $subRelation 子关联 * @return void */ protected function eagerlyMorphToOne($model, $relation, &$result, $subRelation = '') @@ -163,7 +184,7 @@ class MorphTo extends Relation if ($data) { $data->isUpdate(true); } - $result->setAttr($relation, $data ?: null); + $result->setAttr(Loader::parseName($relation), $data ?: null); } /** diff --git a/core/library/think/model/relation/OneToOne.php b/core/library/think/model/relation/OneToOne.php index ef21bee0..18b92225 100644 --- a/core/library/think/model/relation/OneToOne.php +++ b/core/library/think/model/relation/OneToOne.php @@ -17,27 +17,46 @@ use think\Loader; use think\Model; use think\model\Relation; +/** + * Class OneToOne + * @package think\model\relation + * + */ abstract class OneToOne extends Relation { - // 预载入方式 - protected $eagerlyType = 0; + // 预载入方式 0 -JOIN 1 -IN + protected $eagerlyType = 1; + // 当前关联的JOIN类型 + protected $joinType; // 要绑定的属性 protected $bindAttr = []; + /** + * 设置join类型 + * @access public + * @param string $type JOIN类型 + * @return $this + */ + public function joinType($type) + { + $this->joinType = $type; + return $this; + } + /** * 预载入关联查询(JOIN方式) * @access public - * @param Query $query 查询对象 - * @param string $relation 关联名 - * @param string $subRelation 子关联 - * @param \Closure $closure 闭包条件 - * @param bool $first + * @param Query $query 查询对象 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param \Closure $closure 闭包条件 + * @param bool $first * @return void */ public function eagerly(Query $query, $relation, $subRelation, $closure, $first) { $name = Loader::parseName(basename(str_replace('\\', '/', $query->getModel()))); - $alias = isset($this->alias[$name]) ? $this->alias[$name] : $name; + $alias = $name; if ($first) { $table = $query->getTable(); $query->table([$table => $alias]); @@ -52,8 +71,7 @@ abstract class OneToOne extends Relation // 预载入封装 $joinTable = $this->query->getTable(); - $joinName = Loader::parseName(basename(str_replace('\\', '/', $this->model))); - $joinAlias = isset($this->alias[$joinName]) ? $this->alias[$joinName] : $relation; + $joinAlias = $relation; $query->via($joinAlias); if ($this instanceof BelongsTo) { @@ -64,9 +82,8 @@ abstract class OneToOne extends Relation if ($closure) { // 执行闭包查询 - call_user_func_array($closure, [ & $query]); - //指定获取关联的字段 - //需要在 回调中 调方法 withField 方法,如 + call_user_func_array($closure, [& $query]); + // 使用withField指定获取关联的字段,如 // $query->where(['id'=>1])->withField('id,name'); if ($query->getOptions('with_field')) { $field = $query->getOptions('with_field'); @@ -80,21 +97,40 @@ abstract class OneToOne extends Relation $query->field($field, false, $joinTable, $joinAlias, $relation . '__'); } + /** + * 预载入关联查询(数据集) + * @param array $resultSet + * @param string $relation + * @param string $subRelation + * @param \Closure $closure + * @return mixed + */ + abstract protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure); + + /** + * 预载入关联查询(数据) + * @param Model $result + * @param string $relation + * @param string $subRelation + * @param \Closure $closure + * @return mixed + */ + abstract protected function eagerlyOne(&$result, $relation, $subRelation, $closure); + /** * 预载入关联查询(数据集) * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class) + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) { if (1 == $this->eagerlyType) { // IN查询 - $this->eagerlySet($resultSet, $relation, $subRelation, $closure, $class); + $this->eagerlySet($resultSet, $relation, $subRelation, $closure); } else { // 模型关联组装 foreach ($resultSet as $result) { @@ -106,18 +142,17 @@ abstract class OneToOne extends Relation /** * 预载入关联查询(数据) * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) + public function eagerlyResult(&$result, $relation, $subRelation, $closure) { if (1 == $this->eagerlyType) { // IN查询 - $this->eagerlyOne($result, $relation, $subRelation, $closure, $class); + $this->eagerlyOne($result, $relation, $subRelation, $closure); } else { // 模型关联组装 $this->match($this->model, $relation, $result); @@ -127,7 +162,7 @@ abstract class OneToOne extends Relation /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 * @return integer */ public function save($data) @@ -144,8 +179,8 @@ abstract class OneToOne extends Relation /** * 设置预载入方式 * @access public - * @param integer $type 预载入方式 0 JOIN查询 1 IN查询 - * @return this + * @param integer $type 预载入方式 0 JOIN查询 1 IN查询 + * @return $this */ public function setEagerlyType($type) { @@ -167,8 +202,8 @@ abstract class OneToOne extends Relation /** * 绑定关联表的属性到父模型属性 * @access public - * @param mixed $attr 要绑定的属性列表 - * @return this + * @param mixed $attr 要绑定的属性列表 + * @return $this */ public function bind($attr) { @@ -182,19 +217,20 @@ abstract class OneToOne extends Relation /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 * @return integer */ public function relationCount($result, $closure) - {} + { + } /** * 一对一 关联模型预查询拼装 * @access public - * @param string $model 模型名称 - * @param string $relation 关联名 - * @param Model $result 模型对象实例 + * @param string $model 模型名称 + * @param string $relation 关联名 + * @param Model $result 模型对象实例 * @return void */ protected function match($model, $relation, &$result) @@ -215,16 +251,17 @@ abstract class OneToOne extends Relation $this->bindAttr($relationModel, $result, $this->bindAttr); } } - $result->setAttr($relation, !isset($relationModel) ? null : $relationModel->isUpdate(true)); + $result->setAttr(Loader::parseName($relation), !isset($relationModel) ? null : $relationModel->isUpdate(true)); } /** * 绑定关联属性到父模型 * @access protected - * @param Model $model 关联模型对象 - * @param Model $result 父模型对象 - * @param array $bindAttr 绑定属性 + * @param Model $model 关联模型对象 + * @param Model $result 父模型对象 + * @param array $bindAttr 绑定属性 * @return void + * @throws Exception */ protected function bindAttr($model, &$result, $bindAttr) { @@ -241,26 +278,29 @@ abstract class OneToOne extends Relation /** * 一对一 关联模型预查询(IN方式) * @access public - * @param object $model 关联模型对象 - * @param array $where 关联预查询条件 - * @param string $key 关联键名 - * @param string $relation 关联名 - * @param string $subRelation 子关联 - * @param bool $closure + * @param object $model 关联模型对象 + * @param array $where 关联预查询条件 + * @param string $key 关联键名 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param bool|\Closure $closure * @return array */ protected function eagerlyWhere($model, $where, $key, $relation, $subRelation = '', $closure = false) { // 预载入关联查询 支持嵌套预载入 if ($closure) { - call_user_func_array($closure, [ & $model]); + call_user_func_array($closure, [& $model]); + if ($field = $model->getOptions('with_field')) { + $model->field($field)->removeOption('with_field'); + } } $list = $model->where($where)->with($subRelation)->select(); // 组装模型数据 $data = []; foreach ($list as $set) { - $data[$set->$key][] = $set; + $data[$set->$key] = $set; } return $data; } diff --git a/core/library/think/template/taglib/Cx.php b/core/library/think/template/taglib/Cx.php index 1a23c764..af7a54c8 100644 --- a/core/library/think/template/taglib/Cx.php +++ b/core/library/think/template/taglib/Cx.php @@ -98,7 +98,7 @@ class Cx extends Taglib $name = $this->autoBuildVar($name); } - $parseStr .= 'if(is_array(' . $name . ') || ' . $name . ' instanceof \think\Collection): $' . $key . ' = 0;'; + $parseStr .= 'if(is_array(' . $name . ') || ' . $name . ' instanceof \think\Collection || ' . $name . ' instanceof \think\Paginator): $' . $key . ' = 0;'; // 设置了输出数组长度 if (0 != $offset || 'null' != $length) { $parseStr .= '$__LIST__ = is_array(' . $name . ') ? array_slice(' . $name . ',' . $offset . ',' . $length . ', true) : ' . $name . '->slice(' . $offset . ',' . $length . ', true); '; @@ -158,7 +158,7 @@ class Cx extends Taglib } else { $name = $this->autoBuildVar($name); } - $parseStr .= 'if(is_array(' . $name . ') || ' . $name . ' instanceof \think\Collection): '; + $parseStr .= 'if(is_array(' . $name . ') || ' . $name . ' instanceof \think\Collection || ' . $name . ' instanceof \think\Paginator): '; // 设置了输出数组长度 if (0 != $offset || 'null' != $length) { if (!isset($var)) { @@ -431,7 +431,7 @@ class Cx extends Taglib { $name = $tag['name']; $name = $this->autoBuildVar($name); - $parseStr = 'isEmpty())): ?>' . $content . ''; + $parseStr = 'isEmpty())): ?>' . $content . ''; return $parseStr; } @@ -448,7 +448,7 @@ class Cx extends Taglib { $name = $tag['name']; $name = $this->autoBuildVar($name); - $parseStr = 'isEmpty()))): ?>' . $content . ''; + $parseStr = 'isEmpty()))): ?>' . $content . ''; return $parseStr; } diff --git a/core/library/traits/model/SoftDelete.php b/core/library/traits/model/SoftDelete.php index 1a69c2c5..8781544f 100644 --- a/core/library/traits/model/SoftDelete.php +++ b/core/library/traits/model/SoftDelete.php @@ -63,7 +63,7 @@ trait SoftDelete $this->data[$name] = $this->autoWriteTimestamp($name); $result = $this->isUpdate()->save(); } else { - $result = $this->db()->delete($this->data); + $result = $this->db(false)->delete($this->data); } $this->trigger('after_delete', $this); diff --git a/template/default/content/article/list.html b/template/default/content/article/list.html index 8525930f..52c8c0a1 100644 --- a/template/default/content/article/list.html +++ b/template/default/content/article/list.html @@ -24,8 +24,8 @@
  • - {$item['create_time']|date='d',###} - {$item['create_time']|date='m',###} + {$item['create_time']|date='Y-m-d H:i:s',###} + {$item['create_time']|date='Y-m-d H:i:s',###}
    {$item['title']|msubstr=###,0,80}