更新内核,API接口开发的一些尝试,后期会增加API接口开发这块

This commit is contained in:
2017-05-22 20:48:10 +08:00
parent a4d58f9f09
commit 426195eb90
47 changed files with 1339 additions and 513 deletions

View File

@@ -0,0 +1,15 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace app\api\controller;
use api\BaseAuth;
class AccessToken extends BaseAuth {
}

View File

@@ -0,0 +1,26 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace app\api\controller;
use api\BaseRest;
class Base extends BaseRest {
// 允许访问的请求类型
protected $restMethodList = 'get|post|put|delete|patch|head|options';
//业务错误码的映射表
public $errMap = [
0 => 'success', //没有错误
1001 => '参数错误',
9999 => '自定义错误', //让程序给出的自定义错误
];
//是否开启权限认证
public $apiAuth = true;
}

View File

@@ -8,10 +8,30 @@
// +----------------------------------------------------------------------
namespace app\api\controller;
use app\common\controller\User;
class Index extends Api{
use think\Request;
public function index(){
class Index extends Base {
public $apiAuth = true;
// 允许访问的请求类型
protected $restMethodList = 'get|post|';
/**
* get的响应
* @param Request $request
* @return mixed
*/
public function getResponse(Request $request) {
return $this->sendError(1001, 'THIS IS GET', 400);
}
/**
* post的响应
* @param Request $request
* @return mixed
*/
public function postResponse(Request $request) {
return $this->sendSuccess('THIS IS POST');
}
}

View File

@@ -9,7 +9,7 @@
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
define('THINK_VERSION', '5.0.7');
define('THINK_VERSION', '5.0.9');
define('THINK_START_TIME', microtime(true));
define('THINK_START_MEM', memory_get_usage());
define('EXT', '.php');

View File

@@ -5,8 +5,6 @@ return [
// | 应用设置
// +----------------------------------------------------------------------
// 应用命名空间
'app_namespace' => 'app',
// 应用调试模式
'app_debug' => true,
// 应用Trace
@@ -91,6 +89,8 @@ return [
'url_route_must' => false,
// 域名部署
'url_domain_deploy' => false,
// 默认Host地址
'default_host' => '',
// 域名根如thinkphp.cn
'url_domain_root' => '',
// 是否自动转换URL中的控制器和操作名

View File

@@ -192,7 +192,7 @@ if (!function_exists('db')) {
* @param bool $force 是否强制重新连接
* @return \think\db\Query
*/
function db($name = '', $config = [], $force = true)
function db($name = '', $config = [], $force = false)
{
return Db::connect($config, $force)->name($name);
}
@@ -354,22 +354,25 @@ if (!function_exists('cache')) {
{
if (is_array($options)) {
// 缓存操作的同时初始化
Cache::connect($options);
$cache = Cache::connect($options);
} elseif (is_array($name)) {
// 缓存初始化
return Cache::connect($name);
} else {
$cache = Cache::init();
}
if (is_null($name)) {
return Cache::clear($value);
return $cache->clear($value);
} elseif ('' === $value) {
// 获取缓存
return 0 === strpos($name, '?') ? Cache::has(substr($name, 1)) : Cache::get($name);
return 0 === strpos($name, '?') ? $cache->has(substr($name, 1)) : $cache->get($name);
} elseif (is_null($value)) {
// 删除缓存
return Cache::rm($name);
return $cache->rm($name);
} elseif (0 === strpos($name, '?') && '' !== $value) {
$expire = is_numeric($options) ? $options : null;
return Cache::remember(substr($name, 1), $value, $expire);
return $cache->remember(substr($name, 1), $value, $expire);
} else {
// 缓存数据
if (is_array($options)) {
@@ -378,9 +381,9 @@ if (!function_exists('cache')) {
$expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间
}
if (is_null($tag)) {
return Cache::set($name, $value, $expire);
return $cache->set($name, $value, $expire);
} else {
return Cache::tag($tag)->set($name, $value, $expire);
return $cache->tag($tag)->set($name, $value, $expire);
}
}
}

View File

@@ -414,6 +414,11 @@ class App
public static function initCommon()
{
if (empty(self::$init)) {
if (defined('APP_NAMESPACE')) {
self::$namespace = APP_NAMESPACE;
}
Loader::addNamespace(self::$namespace, APP_PATH);
// 初始化应用
$config = self::init();
self::$suffix = $config['class_suffix'];
@@ -433,9 +438,6 @@ class App
}
}
// 注册应用命名空间
self::$namespace = $config['app_namespace'];
Loader::addNamespace($config['app_namespace'], APP_PATH);
if (!empty($config['root_namespace'])) {
Loader::addNamespace($config['root_namespace']);
}

View File

@@ -51,28 +51,29 @@ class Cache
self::$instance[$name] = new $class($options);
}
}
self::$handler = self::$instance[$name];
return self::$handler;
return self::$instance[$name];
}
/**
* 自动初始化缓存
* @access public
* @param array $options 配置数组
* @return void
* @return Driver
*/
public static function init(array $options = [])
{
if (is_null(self::$handler)) {
// 自动初始化缓存
if (!empty($options)) {
self::connect($options);
$connect = self::connect($options);
} elseif ('complex' == Config::get('cache.type')) {
self::connect(Config::get('cache.default'));
$connect = self::connect(Config::get('cache.default'));
} else {
self::connect(Config::get('cache'));
$connect = self::connect(Config::get('cache'));
}
self::$handler = $connect;
}
return self::$handler;
}
/**
@@ -84,9 +85,9 @@ class Cache
public static function store($name = '')
{
if ('' !== $name && 'complex' == Config::get('cache.type')) {
self::connect(Config::get('cache.' . $name), strtolower($name));
return self::connect(Config::get('cache.' . $name), strtolower($name));
}
return self::$handler;
return self::init();
}
/**
@@ -97,9 +98,8 @@ class Cache
*/
public static function has($name)
{
self::init();
self::$readTimes++;
return self::$handler->has($name);
return self::init()->has($name);
}
/**
@@ -111,9 +111,8 @@ class Cache
*/
public static function get($name, $default = false)
{
self::init();
self::$readTimes++;
return self::$handler->get($name, $default);
return self::init()->get($name, $default);
}
/**
@@ -126,9 +125,8 @@ class Cache
*/
public static function set($name, $value, $expire = null)
{
self::init();
self::$writeTimes++;
return self::$handler->set($name, $value, $expire);
return self::init()->set($name, $value, $expire);
}
/**
@@ -140,9 +138,8 @@ class Cache
*/
public static function inc($name, $step = 1)
{
self::init();
self::$writeTimes++;
return self::$handler->inc($name, $step);
return self::init()->inc($name, $step);
}
/**
@@ -154,9 +151,8 @@ class Cache
*/
public static function dec($name, $step = 1)
{
self::init();
self::$writeTimes++;
return self::$handler->dec($name, $step);
return self::init()->dec($name, $step);
}
/**
@@ -167,9 +163,8 @@ class Cache
*/
public static function rm($name)
{
self::init();
self::$writeTimes++;
return self::$handler->rm($name);
return self::init()->rm($name);
}
/**
@@ -180,9 +175,8 @@ class Cache
*/
public static function clear($tag = null)
{
self::init();
self::$writeTimes++;
return self::$handler->clear($tag);
return self::init()->clear($tag);
}
/**
@@ -193,10 +187,9 @@ class Cache
*/
public static function pull($name)
{
self::init();
self::$readTimes++;
self::$writeTimes++;
return self::$handler->pull($name);
return self::init()->pull($name);
}
/**
@@ -209,9 +202,8 @@ class Cache
*/
public static function remember($name, $value, $expire = null)
{
self::init();
self::$readTimes++;
return self::$handler->remember($name, $value, $expire);
return self::init()->remember($name, $value, $expire);
}
/**
@@ -224,8 +216,7 @@ class Cache
*/
public static function tag($name, $keys = null, $overlay = false)
{
self::init();
return self::$handler->tag($name, $keys, $overlay);
return self::init()->tag($name, $keys, $overlay);
}
}

View File

@@ -135,22 +135,35 @@ class Cookie
* @param string|null $prefix cookie前缀
* @return mixed
*/
public static function get($name, $prefix = null)
public static function get($name = '', $prefix = null)
{
!isset(self::$init) && self::init();
$prefix = !is_null($prefix) ? $prefix : self::$config['prefix'];
$name = $prefix . $name;
if (isset($_COOKIE[$name])) {
$value = $_COOKIE[$name];
$key = $prefix . $name;
if ('' == $name) {
// 获取全部
if ($prefix) {
$value = [];
foreach ($_COOKIE as $k => $val) {
if (0 === strpos($k, $prefix)) {
$value[$k] = $val;
}
}
} else {
$value = $_COOKIE;
}
} elseif (isset($_COOKIE[$key])) {
$value = $_COOKIE[$key];
if (0 === strpos($value, 'think:')) {
$value = substr($value, 6);
$value = json_decode($value, true);
array_walk_recursive($value, 'self::jsonFormatProtect', 'decode');
}
return $value;
} else {
return;
$value = null;
}
return $value;
}
/**

View File

@@ -36,7 +36,7 @@ use think\db\Query;
* @method integer update(array $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 = null) static SQL查询
* @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询
* @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行
* @method Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询
* @method mixed transaction(callable $callback) static 执行数据库事务
@@ -44,6 +44,8 @@ use think\db\Query;
* @method void commit() static 用于非自动提交状态下面的查询提交
* @method void rollback() static 事务回滚
* @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句
* @method string quote(string $str) static SQL指令安全过滤
* @method string getLastInsID($sequence = null) static 获取最近插入的ID
*/
class Db
{

View File

@@ -38,7 +38,7 @@ class Debug
}
/**
* 统计某个区间的时间(微秒)使用情况
* 统计某个区间的时间(微秒)使用情况 返回值以秒为单位
* @param string $start 开始标签
* @param string $end 结束标签
* @param integer|string $dec 小数位
@@ -53,7 +53,7 @@ class Debug
}
/**
* 统计从开始到统计时的时间(微秒)使用情况
* 统计从开始到统计时的时间(微秒)使用情况 返回值以秒为单位
* @param integer|string $dec 小数位
* @return integer
*/

View File

@@ -25,6 +25,10 @@ class Lang
protected static $langCookieExpire = 3600;
// 允许语言列表
protected static $allowLangList = [];
// Accept-Language转义为对应语言包名称 系统默认配置
protected static $acceptLanguage = [
'zh-hans-cn' => 'zh-cn',
];
// 设定当前的语言
public static function range($range = '')
@@ -34,6 +38,7 @@ class Lang
} else {
self::$range = $range;
}
return self::$range;
}
/**
@@ -93,7 +98,6 @@ class Lang
/**
* 获取语言定义(不区分大小写)
* @param string|null $name 语言变量
* @param array $vars 变量替换
* @param string $range 语言作用域
* @return mixed
*/
@@ -152,26 +156,25 @@ class Lang
{
// 自动侦测设置获取语言选择
$langSet = '';
if (isset($_GET[self::$langDetectVar])) {
// url中设置了语言变量
$langSet = strtolower($_GET[self::$langDetectVar]);
Cookie::set(self::$langCookieVar, $langSet, self::$langCookieExpire);
} elseif (Cookie::get(self::$langCookieVar)) {
// 获取上次用户的选择
$langSet = strtolower(Cookie::get(self::$langCookieVar));
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
// 自动侦测浏览器语言
preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
$langSet = strtolower($matches[1]);
Cookie::set(self::$langCookieVar, $langSet, self::$langCookieExpire);
$langSet = strtolower($matches[1]);
$acceptLangs = Config::get('header_accept_lang');
if (isset($acceptLangs[$langSet])) {
$langSet = $acceptLangs[$langSet];
} elseif (isset(self::$acceptLanguage[$langSet])) {
$langSet = self::$acceptLanguage[$langSet];
}
}
if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) {
// 合法的语言
self::$range = $langSet ?: self::$range;
}
if ('zh-hans-cn' == self::$range) {
self::$range = 'zh-cn';
}
return self::$range;
}

View File

@@ -22,6 +22,7 @@ use think\model\relation\HasMany;
use think\model\relation\HasManyThrough;
use think\model\relation\HasOne;
use think\model\relation\MorphMany;
use think\model\relation\MorphOne;
use think\model\relation\MorphTo;
/**
@@ -31,10 +32,12 @@ use think\model\relation\MorphTo;
*/
abstract class Model implements \JsonSerializable, \ArrayAccess
{
// 数据库对象池
// 数据库查询对象池
protected static $links = [];
// 数据库配置
protected $connection = [];
// 父关联模型对象
protected $parent;
// 数据库查询对象
protected $query;
// 当前模型名称
@@ -63,8 +66,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
protected $append = [];
// 数据信息
protected $data = [];
// 记录改变字段
protected $change = [];
// 原始数据
protected $origin = [];
// 关联模型
protected $relation = [];
// 保存自动完成列表
protected $auto = [];
@@ -86,8 +91,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
protected $isUpdate = false;
// 更新条件
protected $updateWhere;
// 当前执行的关联对象
protected $relation;
// 验证失败是否抛出异常
protected $failException = false;
// 全局查询范围
@@ -98,8 +101,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
protected $resultSetType;
// 关联自动写入
protected $relationWrite;
//
protected static $db;
/**
* 初始化过的模型.
@@ -120,9 +121,11 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
} else {
$this->data = $data;
}
// 记录原始数据
$this->origin = $this->data;
// 当前类名
$this->class = get_class($this);
$this->class = get_called_class();
if (empty($this->name)) {
// 当前模型名
@@ -136,63 +139,94 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
if (is_null($this->autoWriteTimestamp)) {
// 自动写入时间戳
$this->autoWriteTimestamp = $this->db(false)->getConfig('auto_timestamp');
$this->autoWriteTimestamp = $this->getQuery()->getConfig('auto_timestamp');
}
if (is_null($this->dateFormat)) {
// 设置时间戳格式
$this->dateFormat = $this->db(false)->getConfig('datetime_format');
$this->dateFormat = $this->getQuery()->getConfig('datetime_format');
}
if (is_null($this->resultSetType)) {
$this->resultSetType = $this->db(false)->getConfig('resultset_type');
$this->resultSetType = $this->getQuery()->getConfig('resultset_type');
}
// 执行初始化操作
$this->initialize();
}
/**
* 获取当前模型的数据库查询对象
* @access public
* @param bool $baseQuery 是否调用全局查询范围
* 创建模型的查询对象
* @access protected
* @return Query
*/
public function db($baseQuery = true)
protected function buildQuery()
{
$model = $this->class;
if (!isset(self::$links[$model])) {
// 合并数据库配置
if (!empty($this->connection)) {
if (is_array($this->connection)) {
$connection = array_merge(Config::get('database'), $this->connection);
} else {
$connection = $this->connection;
}
// 合并数据库配置
if (!empty($this->connection)) {
if (is_array($this->connection)) {
$connection = array_merge(Config::get('database'), $this->connection);
} else {
$connection = [];
$connection = $this->connection;
}
// 设置当前模型 确保查询返回模型对象
$query = Db::connect($connection)->getQuery($model, $this->query);
// 设置当前数据表和模型名
if (!empty($this->table)) {
$query->setTable($this->table);
} else {
$query->name($this->name);
}
if (!empty($this->pk)) {
$query->pk($this->pk);
}
self::$links[$model] = $query;
} else {
$connection = [];
}
$con = Db::connect($connection);
// 设置当前模型 确保查询返回模型对象
$queryClass = $this->query ?: $con->getConfig('query');
$query = new $queryClass($con, $this->class);
// 设置当前数据表和模型名
if (!empty($this->table)) {
$query->setTable($this->table);
} else {
$query->name($this->name);
}
if (!empty($this->pk)) {
$query->pk($this->pk);
}
return $query;
}
/**
* 获取当前模型的查询对象
* @access public
* @param bool $buildNewQuery 创建新的查询对象
* @return Query
*/
public function getQuery($buildNewQuery = false)
{
if ($buildNewQuery) {
return $this->buildQuery();
} elseif (!isset(self::$links[$this->class])) {
// 创建模型查询对象
self::$links[$this->class] = $this->buildQuery();
}
return self::$links[$this->class];
}
/**
* 获取当前模型的数据库查询对象
* @access public
* @param bool $useBaseQuery 是否调用全局查询范围
* @param bool $buildNewQuery 创建新的查询对象
* @return Query
*/
public function db($useBaseQuery = true, $buildNewQuery = true)
{
$query = $this->getQuery($buildNewQuery);
// 全局作用域
if ($baseQuery && method_exists($this, 'base')) {
call_user_func_array([$this, 'base'], [ & self::$links[$model]]);
if ($useBaseQuery && method_exists($this, 'base')) {
call_user_func_array([$this, 'base'], [ & $query]);
}
// 返回当前模型的数据库查询对象
return self::$links[$model];
return $query;
}
/**
@@ -218,6 +252,29 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
{
}
/**
* 设置父关联对象
* @access public
* @param Model $model 模型对象
* @return $this
*/
public function setParent($model)
{
$this->parent = $model;
return $this;
}
/**
* 获取父关联对象
* @access public
* @return Model
*/
public function getParent()
{
return $this->parent;
}
/**
* 设置数据对象值
* @access public
@@ -260,6 +317,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
return $this->data;
} elseif (array_key_exists($name, $this->data)) {
return $this->data[$name];
} elseif (array_key_exists($name, $this->relation)) {
return $this->relation[$name];
} else {
throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name);
}
@@ -282,26 +341,48 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
// 检测修改器
$method = 'set' . Loader::parseName($name, 1) . 'Attr';
if (method_exists($this, $method)) {
$value = $this->$method($value, array_merge($data, $this->data));
$value = $this->$method($value, array_merge($this->data, $data));
} elseif (isset($this->type[$name])) {
// 类型转换
$value = $this->writeTransform($value, $this->type[$name]);
}
}
// 标记字段更改
if (!isset($this->data[$name])) {
$this->change[] = $name;
} elseif (is_scalar($value) && is_scalar($this->data[$name]) && 0 !== strcmp($this->data[$name], $value)) {
$this->change[] = $name;
} elseif (!is_object($value) && $value != $this->data[$name]) {
$this->change[] = $name;
}
// 设置数据对象属性
$this->data[$name] = $value;
return $this;
}
/**
* 获取当前模型的关联模型数据
* @access public
* @param string $name 关联方法名
* @return mixed
*/
public function getRelation($name = null)
{
if (is_null($name)) {
return $this->relation;
} elseif (array_key_exists($name, $this->relation)) {
return $this->relation[$name];
} else {
return;
}
}
/**
* 设置关联数据对象值
* @access public
* @param string $name 属性名
* @param mixed $value 属性值
* @return $this
*/
public function setRelation($name, $value)
{
$this->relation[$name] = $value;
return $this;
}
/**
* 自动写入时间戳
* @access public
@@ -319,12 +400,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
case 'datetime':
case 'date':
$format = !empty($param) ? $param : $this->dateFormat;
$value = $this->formatDateTime($_SERVER['REQUEST_TIME'], $format);
$value = $this->formatDateTime(time(), $format);
break;
case 'timestamp':
case 'integer':
default:
$value = $_SERVER['REQUEST_TIME'];
$value = time();
break;
}
} elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [
@@ -333,9 +414,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
'timestamp',
])
) {
$value = $this->formatDateTime($_SERVER['REQUEST_TIME'], $this->dateFormat);
$value = $this->formatDateTime(time(), $this->dateFormat);
} else {
$value = $this->formatDateTime($_SERVER['REQUEST_TIME'], $this->dateFormat, true);
$value = $this->formatDateTime(time(), $this->dateFormat, true);
}
return $value;
}
@@ -367,6 +448,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/
protected function writeTransform($value, $type)
{
if (is_null($value)) {
return;
}
if (is_array($type)) {
list($type, $param) = $type;
} elseif (strpos($type, ':')) {
@@ -451,14 +536,13 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$value = $this->formatDateTime($value, $this->dateFormat);
}
} elseif ($notFound) {
$method = Loader::parseName($name, 1, false);
if (method_exists($this, $method) && $this->$method() instanceof Relation) {
// 清空之前的查询参数
$this->$method()->removeOption();
$relation = Loader::parseName($name, 1, false);
if (method_exists($this, $relation)) {
$modelRelation = $this->$relation();
// 不存在该字段 获取关联数据
$value = $this->$method()->getRelation();
$value = $this->getRelationData($modelRelation);
// 保存关联对象值
$this->data[$name] = $value;
$this->relation[$name] = $value;
} else {
throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name);
}
@@ -466,6 +550,23 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
return $value;
}
/**
* 获取关联模型数据
* @access public
* @param Relation $modelRelation 模型关联对象
* @return mixed
*/
protected function getRelationData(Relation $modelRelation)
{
if ($this->parent && get_class($this->parent) == $modelRelation->getModel()) {
$value = $this->parent;
} else {
// 首先获取关联数据
$value = $modelRelation->getRelation();
}
return $value;
}
/**
* 数据读取 类型转换
* @access public
@@ -475,6 +576,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/
protected function readTransform($value, $type)
{
if (is_null($value)) {
return;
}
if (is_array($type)) {
list($type, $param) = $type;
} elseif (strpos($type, ':')) {
@@ -553,14 +658,23 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
if (is_string($append)) {
$append = explode(',', $append);
}
$model = $this->getAttr($relation);
$relation = Loader::parseName($relation, 1, false);
// 获取关联数据
if (isset($this->relation[$relation])) {
$model = $this->relation[$relation];
} else {
$model = $this->getRelationData($this->$relation());
}
if ($model instanceof Model) {
foreach ($append as $key => $attr) {
$key = is_numeric($key) ? $attr : $key;
if ($this->__isset($key)) {
if (isset($this->data[$key])) {
throw new Exception('bind attr has exists:' . $key);
} else {
$this->setAttr($key, $model->$attr);
$this->data[$key] = $model->$attr;
}
}
}
@@ -653,15 +767,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$item = [];
$visible = [];
$hidden = [];
$data = array_merge($this->data, $this->relation);
// 过滤属性
if (!empty($this->visible)) {
$array = $this->parseAttr($this->visible, $visible);
$data = array_intersect_key($this->data, array_flip($array));
$data = array_intersect_key($data, array_flip($array));
} elseif (!empty($this->hidden)) {
$array = $this->parseAttr($this->hidden, $hidden, false);
$data = array_diff_key($this->data, array_flip($array));
} else {
$data = $this->data;
$data = array_diff_key($data, array_flip($array));
}
foreach ($data as $key => $val) {
@@ -672,7 +787,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
// 关联模型数据集
$arr = [];
foreach ($val as $k => $value) {
$arr[$k] = $this->subToArray($value, $visible, $hidden, $k);
$arr[$k] = $this->subToArray($value, $visible, $hidden, $key);
}
$item[$key] = $arr;
} else {
@@ -754,10 +869,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
public function getPk($name = '')
{
if (!empty($name)) {
$table = $this->db(false)->getTable($name);
return $this->db(false)->getPk($table);
$table = $this->getQuery()->getTable($name);
return $this->getQuery()->getPk($table);
} elseif (empty($this->pk)) {
$this->pk = $this->db(false)->getPk();
$this->pk = $this->getQuery()->getPk();
}
return $this->pk;
}
@@ -807,31 +922,23 @@ 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]);
if (is_array($name)) {
if (key($name) === 0) {
$relation[$key] = [];
foreach ($name as $val) {
if (isset($this->data[$val])) {
$relation[$key][$val] = $this->data[$val];
unset($this->data[$val]);
}
}
} else {
$relation[$key] = $name;
}
} elseif (isset($this->relation[$name])) {
$relation[$name] = $this->relation[$name];
} 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]);
unset($this->data[$name]);
}
}
}
@@ -845,33 +952,25 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
}
$pk = $this->getPk();
if ($this->isUpdate) {
// 检测字段
$this->checkAllowField($this->data, array_merge($this->auto, $this->update));
// 自动更新
$this->autoCompleteData($this->update);
// 获取有更新的数据
$data = $this->getChangedData();
// 事件回调
if (false === $this->trigger('before_update', $this)) {
return false;
}
// 去除没有更新的字段
$data = [];
foreach ($this->data as $key => $val) {
if (in_array($key, $this->change) || $this->isPk($key)) {
$data[$key] = $val;
}
}
if (!empty($this->readonly)) {
// 只读字段不允许更新
foreach ($this->readonly as $key => $field) {
if (isset($data[$field])) {
unset($data[$field]);
}
}
}
if (empty($data) || (count($data) == 1 && is_string($pk) && isset($data[$pk]))) {
// 没有更新
// 关联更新
if (isset($relation)) {
$this->autoRelationUpdate($relation);
}
return 0;
} elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) {
// 自动写入更新时间
@@ -882,6 +981,13 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$where = $this->updateWhere;
}
// 保留主键数据
foreach ($this->data as $key => $val) {
if ($this->isPk($key)) {
$data[$key] = $val;
}
}
if (is_string($pk) && isset($data[$pk])) {
if (!isset($where[$pk])) {
unset($where);
@@ -890,40 +996,24 @@ 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);
$result = $this->getQuery()->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);
}
}
}
$this->autoRelationUpdate($relation);
}
// 清空change
$this->change = [];
// 更新回调
$this->trigger('after_update', $this);
} else {
// 检测字段
$this->checkAllowField($this->data, array_merge($this->auto, $this->insert));
// 自动写入
$this->autoCompleteData($this->insert);
// 自动写入创建时间和更新时间
if ($this->autoWriteTimestamp) {
if ($this->createTime && !isset($this->data[$this->createTime])) {
@@ -938,11 +1028,11 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
return false;
}
$result = $this->db()->insert($this->data);
$result = $this->getQuery()->insert($this->data);
// 获取自动增长主键
if ($result && is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) {
$insertId = $this->db()->getLastInsID($sequence);
$insertId = $this->getQuery()->getLastInsID($sequence);
if ($insertId) {
$this->data[$pk] = $insertId;
}
@@ -958,17 +1048,78 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
// 标记为更新
$this->isUpdate = true;
// 清空change
$this->change = [];
// 新增回调
$this->trigger('after_insert', $this);
}
// 写入回调
$this->trigger('after_write', $this);
// 重新记录原始数据
$this->origin = $this->data;
return $result;
}
protected function checkAllowField(&$data, $auto = [])
{
if (!empty($this->field)) {
if (true === $this->field) {
$this->field = $this->getQuery()->getTableInfo('', 'fields');
$field = $this->field;
} else {
$field = array_merge($this->field, $auto);
}
foreach ($data as $key => $val) {
if (!in_array($key, $field)) {
unset($data[$key]);
}
}
}
}
protected function autoRelationUpdate($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);
}
}
}
}
/**
* 获取变化的数据 并排除只读数据
* @access public
* @return array
*/
public function getChangedData()
{
$data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) {
if ((empty($b) || empty($b)) && $a !== $b) {
return 1;
}
return is_object($a) || $a != $b ? 1 : 0;
});
if (!empty($this->readonly)) {
// 只读字段不允许更新
foreach ($this->readonly as $key => $field) {
if (isset($data[$field])) {
unset($data[$field]);
}
}
}
return $data;
}
/**
* 保存多个数据到当前数据对象
* @access public
@@ -990,7 +1141,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
}
$result = [];
$db = $this->db();
$db = $this->getQuery();
$db->startTrans();
try {
$pk = $this->getPk();
@@ -1071,9 +1222,14 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$field = $value;
$value = null;
}
if (!in_array($field, $this->change)) {
$this->setAttr($field, !is_null($value) ? $value : (isset($this->data[$field]) ? $this->data[$field] : $value));
if (!isset($this->data[$field])) {
$default = null;
} else {
$default = $this->data[$field];
}
$this->setAttr($field, !is_null($value) ? $value : $default);
}
}
@@ -1099,7 +1255,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
}
// 删除当前模型数据
$result = $this->db()->where($where)->delete();
$result = $this->getQuery()->where($where)->delete();
// 关联删除
if (!empty($this->relationWrite)) {
@@ -1113,6 +1269,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
}
$this->trigger('after_delete', $this);
// 清空原始数据
$this->origin = [];
return $result;
}
@@ -1207,7 +1366,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
/**
* 返回模型的错误信息
* @access public
* @return string
* @return string|array
*/
public function getError()
{
@@ -1294,11 +1453,15 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
* @param mixed $data 主键值或者查询条件(闭包)
* @param array|string $with 关联预查询
* @param bool $cache 是否缓存
* @return static
* @return static|null
* @throws exception\DbException
*/
public static function get($data = null, $with = [], $cache = false)
public static function get($data, $with = [], $cache = false)
{
if (is_null($data)) {
return;
}
if (true === $with || is_int($with)) {
$cache = $with;
$with = [];
@@ -1366,7 +1529,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
} elseif ($data instanceof \Closure) {
call_user_func_array($data, [ & $query]);
$data = null;
} elseif (is_null($data)) {
} elseif (empty($data) && 0 !== $data) {
return 0;
}
$resultSet = $query->select($data);
@@ -1385,16 +1548,14 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
* @access public
* @param string|array|\Closure $name 命名范围名称 逗号分隔
* @internal mixed ...$params 参数调用
* @return Model|Query
* @return Query
*/
public static function scope($name)
{
if ($name instanceof Query) {
return $name;
}
$model = new static();
$params = func_get_args();
$params[0] = $model->db();
$model = new static();
$query = $model->db();
$params = func_get_args();
array_unshift($params, $query);
if ($name instanceof \Closure) {
call_user_func_array($name, $params);
} elseif (is_string($name)) {
@@ -1408,7 +1569,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
}
}
}
return $model;
return $query;
}
/**
@@ -1419,9 +1580,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/
public static function useGlobalScope($use)
{
$model = new static();
static::$db = $model->db($use);
return $model;
$model = new static();
return $model->db($use);
}
/**
@@ -1638,7 +1798,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$model = $this->parseModel($model);
$foreignKey = $foreignKey ?: $this->getForeignKey($model);
$localKey = $localKey ?: (new $model)->getPk();
return new BelongsTo($this, $model, $foreignKey, $localKey, $joinType);
$trace = debug_backtrace(false, 2);
$relation = Loader::parseName($trace[1]['function']);
return new BelongsTo($this, $model, $foreignKey, $localKey, $joinType, $relation);
}
/**
@@ -1693,7 +1855,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
// 记录当前关联信息
$model = $this->parseModel($model);
$name = Loader::parseName(basename(str_replace('\\', '/', $model)));
$table = $table ?: $this->db(false)->getTable(Loader::parseName($this->name) . '_' . $name);
$table = $table ?: $this->getQuery()->getTable(Loader::parseName($this->name) . '_' . $name);
$foreignKey = $foreignKey ?: $name . '_id';
$localKey = $localKey ?: $this->getForeignKey($this->name);
return new BelongsToMany($this, $model, $table, $foreignKey, $localKey);
@@ -1725,6 +1887,32 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
return new MorphMany($this, $model, $foreignKey, $morphType, $type);
}
/**
* MORPH One 关联定义
* @access public
* @param string $model 模型名
* @param string|array $morph 多态字段信息
* @param string $type 多态类型
* @return MorphOne
*/
public function morphOne($model, $morph = null, $type = '')
{
// 记录当前关联信息
$model = $this->parseModel($model);
if (is_null($morph)) {
$trace = debug_backtrace(false, 2);
$morph = Loader::parseName($trace[1]['function']);
}
$type = $type ?: Loader::parseName($this->name);
if (is_array($morph)) {
list($morphType, $foreignKey) = $morph;
} else {
$morphType = $morph . '_type';
$foreignKey = $morph . '_id';
}
return new MorphOne($this, $model, $foreignKey, $morphType, $type);
}
/**
* MORPH TO 关联定义
* @access public
@@ -1734,9 +1922,11 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/
public function morphTo($morph = null, $alias = [])
{
$trace = debug_backtrace(false, 2);
$relation = Loader::parseName($trace[1]['function']);
if (is_null($morph)) {
$trace = debug_backtrace(false, 2);
$morph = Loader::parseName($trace[1]['function']);
$morph = $relation;
}
// 记录当前关联信息
if (is_array($morph)) {
@@ -1745,17 +1935,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$morphType = $morph . '_type';
$foreignKey = $morph . '_id';
}
return new MorphTo($this, $morphType, $foreignKey, $alias);
return new MorphTo($this, $morphType, $foreignKey, $alias, $relation);
}
public function __call($method, $args)
{
if (isset(static::$db)) {
$query = static::$db;
static::$db = null;
} else {
$query = $this->db();
}
$query = $this->db(true, false);
if (method_exists($this, 'scope' . $method)) {
// 动态调用命名范围
@@ -1768,16 +1953,21 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
}
}
public static function __callStatic($method, $params)
public static function __callStatic($method, $args)
{
if (isset(static::$db)) {
$query = static::$db;
static::$db = null;
} else {
$query = (new static())->db();
}
$model = new static();
$query = $model->db();
return call_user_func_array([$query, $method], $params);
if (method_exists($model, 'scope' . $method)) {
// 动态调用命名范围
$method = 'scope' . $method;
array_unshift($args, $query);
call_user_func_array([$model, $method], $args);
return $query;
} else {
return call_user_func_array([$query, $method], $args);
}
}
/**
@@ -1812,7 +2002,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
public function __isset($name)
{
try {
if (array_key_exists($name, $this->data)) {
if (array_key_exists($name, $this->data) || array_key_exists($name, $this->relation)) {
return true;
} else {
$this->getAttr($name);
@@ -1832,7 +2022,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/
public function __unset($name)
{
unset($this->data[$name]);
unset($this->data[$name], $this->relation[$name]);
}
public function __toString()

View File

@@ -273,6 +273,23 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
return $this->items->isEmpty();
}
/**
* 给每个元素执行个回调
*
* @param callable $callback
* @return $this
*/
public function each(callable $callback)
{
foreach ($this->items as $key => $item) {
if ($callback($item, $key) === false) {
break;
}
}
return $this;
}
/**
* Retrieve an external iterator
* @return Traversable An instance of an object implementing <b>Iterator</b> or
@@ -349,6 +366,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
'total' => $total,
'per_page' => $this->listRows(),
'current_page' => $this->currentPage(),
'last_page' => $this->lastPage,
'data' => $this->items->toArray(),
];
}

View File

@@ -88,20 +88,18 @@ class Request
* @var array 资源类型
*/
protected $mimeType = [
'xml' => 'application/xml,text/xml,application/x-xml',
'json' => 'application/json,text/x-json,application/jsonrequest,text/json',
'js' => 'text/javascript,application/javascript,application/x-javascript',
'css' => 'text/css',
'rss' => 'application/rss+xml',
'yaml' => 'application/x-yaml,text/yaml',
'atom' => 'application/atom+xml',
'pdf' => 'application/pdf',
'text' => 'text/plain',
'png' => 'image/png',
'jpg' => 'image/jpg,image/jpeg,image/pjpeg',
'gif' => 'image/gif',
'csv' => 'text/csv',
'html' => 'text/html,application/xhtml+xml,*/*',
'xml' => 'application/xml,text/xml,application/x-xml',
'json' => 'application/json,text/x-json,application/jsonrequest,text/json',
'js' => 'text/javascript,application/javascript,application/x-javascript',
'css' => 'text/css',
'rss' => 'application/rss+xml',
'yaml' => 'application/x-yaml,text/yaml',
'atom' => 'application/atom+xml',
'pdf' => 'application/pdf',
'text' => 'text/plain',
'image' => 'image/png,image/jpg,image/jpeg,image/pjpeg,image/gif,image/webp,image/*',
'csv' => 'text/csv',
'html' => 'text/html,application/xhtml+xml,*/*',
];
protected $content;
@@ -134,6 +132,7 @@ class Request
if (is_null($this->filter)) {
$this->filter = Config::get('default_filter');
}
// 保存 php://input
$this->input = file_get_contents('php://input');
}
@@ -802,12 +801,26 @@ class Request
public function cookie($name = '', $default = null, $filter = '')
{
if (empty($this->cookie)) {
$this->cookie = $_COOKIE;
$this->cookie = Cookie::get();
}
if (is_array($name)) {
return $this->cookie = array_merge($this->cookie, $name);
} elseif (!empty($name)) {
$data = Cookie::has($name) ? Cookie::get($name) : $default;
} else {
$data = $this->cookie;
}
return $this->input($this->cookie, $name, $default, $filter);
// 解析过滤器
$filter = $this->getFilter($filter, $default);
if (is_array($data)) {
array_walk_recursive($data, [$this, 'filterValue'], $filter);
reset($data);
} else {
$this->filterValue($data, $name, $filter);
}
return $data;
}
/**
@@ -984,18 +997,8 @@ class Request
}
// 解析过滤器
if (is_null($filter)) {
$filter = [];
} else {
$filter = $filter ?: $this->filter;
if (is_string($filter)) {
$filter = explode(',', $filter);
} else {
$filter = (array) $filter;
}
}
$filter = $this->getFilter($filter, $default);
$filter[] = $default;
if (is_array($data)) {
array_walk_recursive($data, [$this, 'filterValue'], $filter);
reset($data);
@@ -1024,6 +1027,23 @@ class Request
}
}
protected function getFilter($filter, $default)
{
if (is_null($filter)) {
$filter = [];
} else {
$filter = $filter ?: $this->filter;
if (is_string($filter) && false === strpos($filter, '/')) {
$filter = explode(',', $filter);
} else {
$filter = (array) $filter;
}
}
$filter[] = $default;
return $filter;
}
/**
* 递归过滤给定的值
* @param mixed $value 键值
@@ -1039,7 +1059,7 @@ class Request
// 调用函数或者方法过滤
$value = call_user_func($filter, $value);
} elseif (is_scalar($value)) {
if (strpos($filter, '/')) {
if (false !== strpos($filter, '/')) {
// 正则过滤
if (!preg_match($filter, $value)) {
// 匹配不成功返回默认值
@@ -1527,13 +1547,13 @@ class Request
}
}
// 自动缓存功能
$key = md5($this->host()) . '__URL__';
$key = '__URL__';
} elseif (strpos($key, '|')) {
list($key, $fun) = explode('|', $key);
}
// 特殊规则替换
if (false !== strpos($key, '__')) {
$key = str_replace(['__MODULE__', '__CONTROLLER__', '__ACTION__', '__URL__', ''], [$this->module, $this->controller, $this->action, md5($this->url())], $key);
$key = str_replace(['__MODULE__', '__CONTROLLER__', '__ACTION__', '__URL__', ''], [$this->module, $this->controller, $this->action, md5($this->url(true))], $key);
}
if (false !== strpos($key, ':')) {

View File

@@ -19,7 +19,6 @@ use think\response\Xml as XmlResponse;
class Response
{
// 原始数据
protected $data;
@@ -50,12 +49,12 @@ class Response
public function __construct($data = '', $code = 200, array $header = [], $options = [])
{
$this->data($data);
$this->header = $header;
$this->code = $code;
if (!empty($options)) {
$this->options = array_merge($this->options, $options);
}
$this->contentType($this->contentType, $this->charset);
$this->header = array_merge($this->header, $header);
$this->code = $code;
}
/**
@@ -113,7 +112,11 @@ class Response
http_response_code($this->code);
// 发送头部信息
foreach ($this->header as $name => $val) {
header($name . ':' . $val);
if (is_null($val)) {
header($name);
} else {
header($name . ':' . $val);
}
}
}

View File

@@ -842,7 +842,7 @@ class Route
}
$method = strtolower($request->method());
// 获取当前请求类型的路由规则
$rules = self::$rules[$method];
$rules = isset(self::$rules[$method]) ? self::$rules[$method] : [];
// 检测域名部署
if ($checkDomain) {
self::checkDomain($request, $rules, $method);
@@ -924,7 +924,7 @@ class Route
} else {
$str = $key;
}
if (is_string($str) && $str && 0 !== strpos(str_replace('|', '/', $url), $str)) {
if (is_string($str) && $str && 0 !== stripos(str_replace('|', '/', $url), $str)) {
continue;
}
self::setOption($option);
@@ -1184,9 +1184,9 @@ class Route
}
}
$pattern = array_merge(self::$rules['pattern'], $pattern);
if (false !== $match = self::match($url, $rule, $pattern, $merge)) {
if (false !== $match = self::match($url, $rule, $pattern)) {
// 匹配到路由规则
return self::parseRule($rule, $route, $url, $option, $match, $merge);
return self::parseRule($rule, $route, $url, $option, $match);
}
}
return false;

View File

@@ -56,7 +56,7 @@ class Session
$isDoStart = true;
}
if (isset($config['prefix'])) {
if (isset($config['prefix']) && (self::$prefix === '' || self::$prefix === null)) {
self::$prefix = $config['prefix'];
}
if (isset($config['var_session_id']) && isset($_REQUEST[$config['var_session_id']])) {

View File

@@ -235,7 +235,7 @@ class Url
$rootDomain = Config::get('url_domain_root');
if (true === $domain) {
// 自动判断域名
$domain = $request->host();
$domain = Config::get('default_host') ?: $request->host();
$domains = Route::rules('domain');
if ($domains) {
@@ -265,7 +265,7 @@ class Url
} else {
if (empty($rootDomain)) {
$host = $request->host();
$host = Config::get('default_host') ?: $request->host();
$rootDomain = substr_count($host, '.') > 1 ? substr(strstr($host, '.'), 1) : $host;
}
if (substr_count($domain, '.') < 2 && !strpos($domain, $rootDomain)) {
@@ -297,7 +297,7 @@ class Url
}
foreach ($pattern as $key => $val) {
if (isset($vars[$key])) {
$url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], $vars[$key], $url);
$url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], urlencode($vars[$key]), $url);
unset($vars[$key]);
$result = [$url, $domain, $suffix];
} elseif (2 == $val) {

View File

@@ -456,11 +456,13 @@ class Validate
* @access protected
* @param mixed $value 字段值
* @param mixed $rule 验证规则
* @param array $data 数据
* @return bool
*/
protected function egt($value, $rule)
protected function egt($value, $rule, $data)
{
return $value >= $rule;
$val = $this->getDataValue($data, $rule);
return !is_null($val) && $value >= $val;
}
/**
@@ -468,11 +470,13 @@ class Validate
* @access protected
* @param mixed $value 字段值
* @param mixed $rule 验证规则
* @param array $data 数据
* @return bool
*/
protected function gt($value, $rule)
protected function gt($value, $rule, $data)
{
return $value > $rule;
$val = $this->getDataValue($data, $rule);
return !is_null($val) && $value > $val;
}
/**
@@ -480,11 +484,13 @@ class Validate
* @access protected
* @param mixed $value 字段值
* @param mixed $rule 验证规则
* @param array $data 数据
* @return bool
*/
protected function elt($value, $rule)
protected function elt($value, $rule, $data)
{
return $value <= $rule;
$val = $this->getDataValue($data, $rule);
return !is_null($val) && $value <= $val;
}
/**
@@ -492,11 +498,13 @@ class Validate
* @access protected
* @param mixed $value 字段值
* @param mixed $rule 验证规则
* @param array $data 数据
* @return bool
*/
protected function lt($value, $rule)
protected function lt($value, $rule, $data)
{
return $value < $rule;
$val = $this->getDataValue($data, $rule);
return !is_null($val) && $value < $val;
}
/**
@@ -1180,13 +1188,15 @@ class Validate
/**
* 获取数据值
* @access protected
* @param array $data 数据
* @param string $key 数据标识 支持二维
* @param array $data 数据
* @param string $key 数据标识 支持二维
* @return mixed
*/
protected function getDataValue($data, $key)
{
if (strpos($key, '.')) {
if (is_numeric($key)) {
$value = $key;
} elseif (strpos($key, '.')) {
// 支持二维数组验证
list($name1, $name2) = explode('.', $key);
$value = isset($data[$name1][$name2]) ? $data[$name1][$name2] : null;

View File

@@ -113,7 +113,10 @@ class Memcache extends Driver
public function inc($name, $step = 1)
{
$key = $this->getCacheKey($name);
return $this->handler->increment($key, $step);
if ($this->handler->get($key)) {
return $this->handler->increment($key, $step);
}
return $this->handler->set($key, $step);
}
/**

View File

@@ -125,7 +125,10 @@ class Memcached extends Driver
public function inc($name, $step = 1)
{
$key = $this->getCacheKey($name);
return $this->handler->increment($key, $step);
if ($this->handler->get($key)) {
return $this->handler->increment($key, $step);
}
return $this->handler->set($key, $step);
}
/**

View File

@@ -27,7 +27,7 @@ abstract class Builder
// SQL表达式
protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%';
protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
@@ -88,7 +88,7 @@ abstract class Builder
}
// 获取绑定信息
$bind = $this->query->getFieldsBind($options);
$bind = $this->query->getFieldsBind($options['table']);
if ('*' == $options['field']) {
$fields = array_keys($bind);
} else {
@@ -222,6 +222,14 @@ abstract class Builder
protected function parseWhere($where, $options)
{
$whereStr = $this->buildWhere($where, $options);
if (!empty($options['soft_delete'])) {
// 附加软删除条件
list($field, $condition) = $options['soft_delete'];
$binds = $this->query->getFieldsBind($options['table']);
$whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : '';
$whereStr = $whereStr . $this->parseWhereItem($field, $condition, '', $options, $binds);
}
return empty($whereStr) ? '' : ' WHERE ' . $whereStr;
}
@@ -243,7 +251,7 @@ abstract class Builder
}
$whereStr = '';
$binds = $this->query->getFieldsBind($options);
$binds = $this->query->getFieldsBind($options['table']);
foreach ($where as $key => $val) {
$str = [];
foreach ($val as $field => $value) {
@@ -280,13 +288,7 @@ abstract class Builder
$whereStr .= empty($whereStr) ? substr(implode(' ', $str), strlen($key) + 1) : implode(' ', $str);
}
if (!empty($options['soft_delete'])) {
// 附加软删除条件
list($field, $condition) = $options['soft_delete'];
$whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : '';
$whereStr = $whereStr . $this->parseWhereItem($field, $condition, '', $options, $binds);
}
return $whereStr;
}
@@ -346,9 +348,14 @@ abstract class Builder
$whereStr = '';
if (in_array($exp, ['=', '<>', '>', '>=', '<', '<='])) {
// 比较运算 及 模糊匹配
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseValue($value, $field);
// 比较运算
if ($value instanceof \Closure) {
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value);
} else {
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseValue($value, $field);
}
} elseif ('LIKE' == $exp || 'NOT LIKE' == $exp) {
// 模糊匹配
if (is_array($value)) {
foreach ($value as $item) {
$array[] = $key . ' ' . $exp . ' ' . $this->parseValue($item, $field);
@@ -712,13 +719,14 @@ abstract class Builder
* @access public
* @param array $dataSet 数据集
* @param array $options 表达式
* @param bool $replace 是否replace
* @return string
*/
public function insertAll($dataSet, $options)
public function insertAll($dataSet, $options, $replace = false)
{
// 获取合法的字段
if ('*' == $options['field']) {
$fields = array_keys($this->query->getFieldsType($options));
$fields = array_keys($this->query->getFieldsType($options['table']));
} else {
$fields = $options['field'];
}
@@ -747,8 +755,9 @@ abstract class Builder
}
$fields = array_map([$this, 'parseKey'], array_keys(reset($dataSet)));
$sql = str_replace(
['%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
[
$replace ? 'REPLACE' : 'INSERT',
$this->parseTable($options['table'], $options),
implode(' , ', $fields),
implode(' UNION ALL ', $values),

View File

@@ -56,8 +56,6 @@ abstract class Connection
protected $attrCase = PDO::CASE_LOWER;
// 监听回调
protected static $event = [];
// 查询对象
protected $query = [];
// 使用Builder类
protected $builder;
// 数据库连接参数配置
@@ -137,19 +135,14 @@ abstract class Connection
}
/**
* 创建指定模型的查询对象
* @access public
* @param string $model 模型类名称
* @param string $queryClass 查询对象类名
* 获取新的查询对象
* @access protected
* @return Query
*/
public function getQuery($model = 'db', $queryClass = '')
protected function getQuery()
{
if (!isset($this->query[$model])) {
$class = $queryClass ?: $this->config['query'];
$this->query[$model] = new $class($this, 'db' == $model ? '' : $model);
}
return $this->query[$model];
$class = $this->config['query'];
return new $class($this);
}
/**
@@ -340,13 +333,9 @@ abstract class Connection
/**
* 执行查询 返回数据集
* @access public
* @param string $sql sql指令
* @param array $bind 参数绑定
* @param bool $master 是否在主服务器读操作
* @param bool $class 是否返回PDO对象
* @param string $sql sql指令
* @param array $bind 参数绑定
* @param boolean $master 是否在主服务器读操作
* @param bool $master 是否在主服务器读操作
* @param bool $pdo 是否返回PDO对象
* @return mixed
* @throws BindParamException
@@ -393,10 +382,15 @@ abstract class Connection
// 返回结果集
return $this->getResult($pdo, $procedure);
} catch (\PDOException $e) {
if ($this->config['break_reconnect'] && $this->isBreak($e)) {
if ($this->isBreak($e)) {
return $this->close()->query($sql, $bind, $master, $pdo);
}
throw new PDOException($e, $this->config, $this->getLastsql());
} catch (\ErrorException $e) {
if ($this->isBreak($e)) {
return $this->close()->query($sql, $bind, $master, $pdo);
}
throw $e;
}
}
@@ -451,10 +445,15 @@ abstract class Connection
$this->numRows = $this->PDOStatement->rowCount();
return $this->numRows;
} catch (\PDOException $e) {
if ($this->config['break_reconnect'] && $this->isBreak($e)) {
if ($this->isBreak($e)) {
return $this->close()->execute($sql, $bind);
}
throw new PDOException($e, $this->config, $this->getLastsql());
} catch (\ErrorException $e) {
if ($this->isBreak($e)) {
return $this->close()->execute($sql, $bind);
}
throw $e;
}
}
@@ -629,13 +628,25 @@ abstract class Connection
}
++$this->transTimes;
try {
if (1 == $this->transTimes) {
$this->linkID->beginTransaction();
} elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
$this->linkID->exec(
$this->parseSavepoint('trans' . $this->transTimes)
);
}
if (1 == $this->transTimes) {
$this->linkID->beginTransaction();
} elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
$this->linkID->exec(
$this->parseSavepoint('trans' . $this->transTimes)
);
} catch (\PDOException $e) {
if ($this->isBreak($e)) {
return $this->close()->startTrans();
}
throw $e;
} catch (\ErrorException $e) {
if ($this->isBreak($e)) {
return $this->close()->startTrans();
}
throw $e;
}
}
@@ -771,11 +782,35 @@ abstract class Connection
/**
* 是否断线
* @access protected
* @param \PDOException $e 异常
* @param \PDOException $e 异常对象
* @return bool
*/
protected function isBreak($e)
{
if (!$this->config['break_reconnect']) {
return false;
}
$info = [
'server has gone away',
'no connection to the server',
'Lost connection',
'is dead or not enabled',
'Error while sending',
'decryption failed or bad record mac',
'server closed the connection unexpectedly',
'SSL connection has been closed unexpectedly',
'Error writing data to the connection',
'Resource deadlock avoided',
];
$error = $e->getMessage();
foreach ($info as $msg) {
if (false !== stripos($error, $msg)) {
return true;
}
}
return false;
}
@@ -860,7 +895,6 @@ abstract class Connection
Debug::remark('queryEndTime', 'time');
$runtime = Debug::getRangeTime('queryStartTime', 'queryEndTime');
$sql = $sql ?: $this->getLastsql();
$log = $sql . ' [ RunTime:' . $runtime . 's ]';
$result = [];
// SQL性能分析
if ($this->config['sql_explain'] && 0 === stripos(trim($sql), 'select')) {
@@ -918,7 +952,7 @@ abstract class Connection
{
if (!empty($this->config['deploy'])) {
// 采用分布式数据库
if ($master) {
if ($master || $this->transTimes) {
if (!$this->linkWrite) {
$this->linkWrite = $this->multiConnect(true);
}

View File

@@ -401,7 +401,7 @@ class Query
if (empty($this->options['table'])) {
$this->options['table'] = $this->getTable();
}
$key = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options));
$key = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options) . serialize($this->bind));
$result = Cache::get($key);
}
if (false === $result) {
@@ -444,14 +444,16 @@ class Query
if (empty($this->options['table'])) {
$this->options['table'] = $this->getTable();
}
$guid = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options));
$guid = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options) . serialize($this->bind));
$result = Cache::get($guid);
}
if (false === $result) {
if (isset($this->options['field'])) {
unset($this->options['field']);
}
if ($key && '*' != $field) {
if (is_null($field)) {
$field = '*';
} elseif ($key && '*' != $field) {
$field = $key . ',' . $field;
}
$pdo = $this->field($field)->getPdo();
@@ -594,7 +596,7 @@ class Query
}
if ($lazyTime > 0) {
// 延迟写入
$guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition));
$guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition) . serialize($this->bind));
$step = $this->lazyWrite('inc', $guid, $step, $lazyTime);
if (false === $step) {
// 清空查询条件
@@ -623,7 +625,7 @@ class Query
}
if ($lazyTime > 0) {
// 延迟写入
$guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition));
$guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition) . serialize($this->bind));
$step = $this->lazyWrite('dec', $guid, $step, $lazyTime);
if (false === $step) {
// 清空查询条件
@@ -649,16 +651,16 @@ class Query
if (!Cache::has($guid . '_time')) {
// 计时开始
Cache::set($guid . '_time', $_SERVER['REQUEST_TIME'], 0);
Cache::$type($guid, $step, 0);
Cache::$type($guid, $step);
} elseif ($_SERVER['REQUEST_TIME'] > Cache::get($guid . '_time') + $lazyTime) {
// 删除缓存
$value = Cache::$type($guid, $step, 0);
$value = Cache::$type($guid, $step);
Cache::rm($guid);
Cache::rm($guid . '_time');
return 0 === $value ? false : $value;
} else {
// 更新缓存
Cache::$type($guid, $step, 0);
Cache::$type($guid, $step);
}
return false;
}
@@ -1132,6 +1134,7 @@ class Query
if ($field) {
$this->options['soft_delete'] = [$field, $condition ?: ['null', '']];
}
return $this;
}
/**
@@ -1773,21 +1776,21 @@ class Query
}
// 获取当前数据表字段信息
public function getTableFields($options)
public function getTableFields($table = '')
{
return $this->getTableInfo($options['table'], 'fields');
return $this->getTableInfo($table ?: $this->getOptions('table'), 'fields');
}
// 获取当前数据表字段类型
public function getFieldsType($options)
public function getFieldsType($table = '')
{
return $this->getTableInfo($options['table'], 'type');
return $this->getTableInfo($table ?: $this->getOptions('table'), 'type');
}
// 获取当前数据表绑定信息
public function getFieldsBind($options)
public function getFieldsBind($table = '')
{
$types = $this->getFieldsType($options);
$types = $this->getFieldsType($table);
$bind = [];
if ($types) {
foreach ($types as $key => $type) {
@@ -1900,6 +1903,9 @@ class Query
$closure = $relation;
$relation = $key;
$with[$key] = $key;
} elseif (is_array($relation)) {
$subRelation = $relation;
$relation = $key;
} elseif (is_string($relation) && strpos($relation, '.')) {
$with[$key] = $relation;
list($relation, $subRelation) = explode('.', $relation, 2);
@@ -1909,7 +1915,7 @@ class Query
$relation = Loader::parseName($relation, 1, false);
$model = $class->$relation();
if ($model instanceof OneToOne && 0 == $model->getEagerlyType()) {
$model->removeOption()->eagerly($this, $relation, $subRelation, $closure, $first);
$model->eagerly($this, $relation, $subRelation, $closure, $first);
$first = false;
} elseif ($closure) {
$with[$key] = $closure;
@@ -1997,7 +2003,7 @@ class Query
$relation = explode(',', $relation);
}
if (isset($this->options['relation'])) {
$this->options['relation'] = array_mrege($this->options['relation'], $relation);
$this->options['relation'] = array_merge($this->options['relation'], $relation);
} else {
$this->options['relation'] = $relation;
}
@@ -2111,9 +2117,10 @@ class Query
* 批量插入记录
* @access public
* @param mixed $dataSet 数据集
* @param boolean $replace 是否replace
* @return integer|string
*/
public function insertAll(array $dataSet)
public function insertAll(array $dataSet, $replace = false)
{
// 分析查询表达式
$options = $this->parseExpress();
@@ -2121,7 +2128,7 @@ class Query
return false;
}
// 生成SQL语句
$sql = $this->builder->insertAll($dataSet, $options);
$sql = $this->builder->insertAll($dataSet, $options, $replace);
// 获取参数绑定
$bind = $this->getBind();
if ($options['fetch_sql']) {
@@ -2203,7 +2210,7 @@ class Query
$options['where']['AND'] = $where;
}
} elseif (!isset($key) && is_string($pk) && isset($options['where']['AND'][$pk])) {
$key = $this->getCacheKey($options['where']['AND'][$pk], $options);
$key = $this->getCacheKey($options['where']['AND'][$pk], $options, $this->bind);
}
// 生成UPDATE SQL语句
@@ -2291,7 +2298,7 @@ class Query
// 判断查询缓存
$cache = $options['cache'];
unset($options['cache']);
$key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options));
$key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options) . serialize($this->bind));
$resultSet = Cache::get($key);
}
if (!$resultSet) {
@@ -2383,8 +2390,9 @@ class Query
* @access public
* @param mixed $value 缓存数据
* @param array $options 缓存参数
* @param array $bind 绑定参数
*/
protected function getCacheKey($value, $options)
protected function getCacheKey($value, $options, $bind = [])
{
if (is_scalar($value)) {
$data = $value;
@@ -2392,9 +2400,9 @@ class Query
$data = $value[1];
}
if (isset($data)) {
return 'think:' . $options['table'] . '|' . $data;
return 'think:' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data;
} else {
return md5(serialize($options));
return md5(serialize($options) . serialize($bind));
}
}
@@ -2422,7 +2430,7 @@ class Query
// AR模式分析主键条件
$this->parsePkWhere($data, $options);
} elseif (!empty($options['cache']) && true === $options['cache']['key'] && is_string($pk) && isset($options['where']['AND'][$pk])) {
$key = $this->getCacheKey($options['where']['AND'][$pk], $options);
$key = $this->getCacheKey($options['where']['AND'][$pk], $options, $this->bind);
}
$options['limit'] = 1;
@@ -2435,7 +2443,7 @@ class Query
} elseif (is_string($cache['key'])) {
$key = $cache['key'];
} elseif (!isset($key)) {
$key = md5(serialize($options));
$key = md5(serialize($options) . serialize($this->bind));
}
$result = Cache::get($key);
}
@@ -2646,7 +2654,7 @@ class Query
// AR模式分析主键条件
$this->parsePkWhere($data, $options);
} elseif (!isset($key) && is_string($pk) && isset($options['where']['AND'][$pk])) {
$key = $this->getCacheKey($options['where']['AND'][$pk], $options);
$key = $this->getCacheKey($options['where']['AND'][$pk], $options, $this->bind);
}
if (true !== $data && empty($options['where'])) {

View File

@@ -47,6 +47,9 @@ class Mysql extends Builder
$key = '`' . $key . '`';
}
if (isset($table)) {
if (strpos($table, '.')) {
$table = str_replace('.', '`.`', $table);
}
$key = '`' . $table . '`.' . $key;
}
return $key;

View File

@@ -21,7 +21,7 @@ class Sqlsrv extends Builder
protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%';
protected $selectInsertSql = 'SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%';
protected $updateSql = 'UPDATE %TABLE% SET %SET% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
/**
* order分析

View File

@@ -51,7 +51,7 @@ class Mysql extends Connection
*/
public function getFields($tableName)
{
$this->initConnect(true);
$this->initConnect(false);
list($tableName) = explode(' ', $tableName);
if (false === strpos($tableName, '`')) {
if (strpos($tableName, '.')) {
@@ -91,7 +91,7 @@ class Mysql extends Connection
*/
public function getTables($dbName = '')
{
$this->initConnect(true);
$this->initConnect(false);
$sql = !empty($dbName) ? 'SHOW TABLES FROM ' . $dbName : 'SHOW TABLES ';
// 调试开始
$this->debug(true);
@@ -130,17 +130,4 @@ class Mysql extends Connection
return true;
}
/**
* 是否断线
* @access protected
* @param \PDOException $e 异常对象
* @return bool
*/
protected function isBreak($e)
{
if (false !== stripos($e->getMessage(), 'server has gone away')) {
return true;
}
return false;
}
}

View File

@@ -44,7 +44,7 @@ class Pgsql extends Connection
*/
public function getFields($tableName)
{
$this->initConnect(true);
$this->initConnect(false);
list($tableName) = explode(' ', $tableName);
$sql = 'select fields_name as "field",fields_type as "type",fields_not_null as "null",fields_key_name as "key",fields_default as "default",fields_default as "extra" from table_msg(\'' . $tableName . '\');';
// 调试开始
@@ -78,7 +78,7 @@ class Pgsql extends Connection
*/
public function getTables($dbName = '')
{
$this->initConnect(true);
$this->initConnect(false);
$sql = "select tablename as Tables_in_test from pg_tables where schemaname ='public'";
// 调试开始
$this->debug(true);

View File

@@ -42,7 +42,7 @@ class Sqlite extends Connection
*/
public function getFields($tableName)
{
$this->initConnect(true);
$this->initConnect(false);
list($tableName) = explode(' ', $tableName);
$sql = 'PRAGMA table_info( ' . $tableName . ' )';
// 调试开始
@@ -76,7 +76,7 @@ class Sqlite extends Connection
*/
public function getTables($dbName = '')
{
$this->initConnect(true);
$this->initConnect(false);
$sql = "SELECT name FROM sqlite_master WHERE type='table' "
. "UNION ALL SELECT name FROM sqlite_temp_master "
. "WHERE type='table' ORDER BY name";

View File

@@ -49,7 +49,7 @@ class Sqlsrv extends Connection
*/
public function getFields($tableName)
{
$this->initConnect(true);
$this->initConnect(false);
list($tableName) = explode(' ', $tableName);
$sql = "SELECT column_name, data_type, column_default, is_nullable
FROM information_schema.tables AS t
@@ -99,7 +99,7 @@ class Sqlsrv extends Connection
*/
public function getTables($dbName = '')
{
$this->initConnect(true);
$this->initConnect(false);
$sql = "SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'

View File

@@ -76,7 +76,7 @@ class File
{
//检测日志文件大小,超过配置大小则备份日志文件重新生成
if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) {
rename($destination, dirname($destination) . DS . $_SERVER['REQUEST_TIME'] . '-' . basename($destination));
rename($destination, dirname($destination) . DS . time() . '-' . basename($destination));
$this->writed[$destination] = false;
}

View File

@@ -16,6 +16,20 @@ use think\Model;
class Collection extends BaseCollection
{
/**
* 返回数组中指定的一列
* @param string $column_key
* @param string|null $index_key
* @return array
*/
public function column($column_key, $index_key = null)
{
if (function_exists('array_column')) {
return array_column($this->toArray(), $column_key, $index_key);
}
return parent::column($column_key, $index_key);
}
/**
* 延迟预载入关联查询
* @access public

View File

@@ -11,6 +11,7 @@
namespace think\model;
use think\Db;
use think\db\Query;
use think\Model;
@@ -120,22 +121,19 @@ class Merge extends Model
* @access public
* @param string $model 模型名称
* @param array $data 数据
* @param bool $insert 是否新增
* @return array
*/
protected function parseData($model, $data, $insert = false)
protected function parseData($model, $data)
{
$item = [];
foreach ($data as $key => $val) {
if ($insert || in_array($key, $this->change) || $this->isPk($key)) {
if ($this->fk != $key && array_key_exists($key, $this->mapFields)) {
list($name, $key) = explode('.', $this->mapFields[$key]);
if ($model == $name) {
$item[$key] = $val;
}
} else {
if ($this->fk != $key && array_key_exists($key, $this->mapFields)) {
list($name, $key) = explode('.', $this->mapFields[$key]);
if ($model == $name) {
$item[$key] = $val;
}
} else {
$item[$key] = $val;
}
}
return $item;
@@ -174,6 +172,11 @@ class Merge extends Model
$this->setAttr($this->updateTime, null);
}
// 事件回调
if (false === $this->trigger('before_write', $this)) {
return false;
}
$db = $this->db();
$db->startTrans();
$pk = $this->getPk();
@@ -190,8 +193,16 @@ class Merge extends Model
$where = $this->updateWhere;
}
// 获取有更新的数据
$data = $this->getChangedData();
// 保留主键数据
foreach ($this->data as $key => $val) {
if ($this->isPk($key)) {
$data[$key] = $val;
}
}
// 处理模型数据
$data = $this->parseData($this->name, $this->data);
$data = $this->parseData($this->name, $data);
if (is_string($pk) && isset($data[$pk])) {
if (!isset($where[$pk])) {
unset($where);
@@ -207,14 +218,12 @@ class Merge extends Model
$name = is_int($key) ? $model : $key;
$table = is_int($key) ? $db->getTable($model) : $model;
// 处理关联模型数据
$data = $this->parseData($name, $this->data);
$query = new Query;
if ($query->table($table)->strict(false)->where($this->fk, $this->data[$this->getPk()])->update($data)) {
$data = $this->parseData($name, $data);
if (Db::table($table)->strict(false)->where($this->fk, $this->data[$this->getPk()])->update($data)) {
$result = 1;
}
}
// 清空change
$this->change = [];
// 新增回调
$this->trigger('after_update', $this);
} else {
@@ -231,7 +240,7 @@ class Merge extends Model
}
// 处理模型数据
$data = $this->parseData($this->name, $this->data, true);
$data = $this->parseData($this->name, $this->data);
// 写入主表数据
$result = $db->name($this->name)->strict(false)->insert($data);
if ($result) {
@@ -240,9 +249,6 @@ class Merge extends Model
if ($insertId) {
if (is_string($pk)) {
$this->data[$pk] = $insertId;
if ($this->fk == $pk) {
$this->change[] = $pk;
}
}
$this->data[$this->fk] = $insertId;
}
@@ -256,19 +262,20 @@ class Merge extends Model
$name = is_int($key) ? $model : $key;
$table = is_int($key) ? $db->getTable($model) : $model;
// 处理关联模型数据
$data = $this->parseData($name, $source, true);
$query = new Query;
$query->table($table)->strict(false)->insert($data);
$data = $this->parseData($name, $source);
Db::table($table)->strict(false)->insert($data);
}
}
// 标记为更新
$this->isUpdate = true;
// 清空change
$this->change = [];
// 新增回调
$this->trigger('after_insert', $this);
}
$db->commit();
// 写入回调
$this->trigger('after_write', $this);
$this->origin = $this->data;
return $result;
} catch (\Exception $e) {
$db->rollback();

View File

@@ -19,24 +19,26 @@ class Pivot extends Model
/** @var Model */
public $parent;
protected $autoWriteTimestamp = false;
/**
* 构函数
* 构函数
* @access public
* @param Model $parent
* @param array|object $data 数据
* @param string $table 中间数据表名
* @param Model $parent 上级模型
* @param array|object $data 数据
* @param string $table 中间数据表名
*/
public function __construct(Model $parent, $data = [], $table = '')
{
if (is_object($data)) {
$this->data = get_object_vars($data);
} else {
$this->data = $data;
}
$this->parent = $parent;
$this->table = $table;
if (is_null($this->name)) {
$this->name = $table;
}
parent::__construct($data);
$this->class = $this->name;
}
}

View File

@@ -33,8 +33,6 @@ abstract class Relation
protected $foreignKey;
// 关联表主键
protected $localKey;
// 关联查询参数
protected $option;
// 基础查询
protected $baseQuery;
@@ -79,15 +77,30 @@ abstract class Relation
return (new $this->model)->toCollection($resultSet);
}
/**
* 移除关联查询参数
* @access public
* @return $this
*/
public function removeOption()
protected function getQueryFields($model)
{
$this->query->removeOption();
return $this;
$fields = $this->query->getOptions('field');
return $this->getRelationQueryFields($fields, $model);
}
protected function getRelationQueryFields($fields, $model)
{
if ($fields) {
if (is_string($fields)) {
$fields = explode(',', $fields);
}
foreach ($fields as &$field) {
if (false === strpos($field, '.')) {
$field = $model . '.' . $field;
}
}
} else {
$fields = $model . '.*';
}
return $fields;
}
/**
@@ -105,10 +118,8 @@ abstract class Relation
$result = call_user_func_array([$this->query, $method], $args);
if ($result instanceof Query) {
$this->option = $result->getOptions();
return $this;
} else {
$this->option = [];
$this->baseQuery = false;
return $result;
}

View File

@@ -24,8 +24,9 @@ class BelongsTo extends OneToOne
* @param string $foreignKey 关联外键
* @param string $localKey 关联主键
* @param string $joinType JOIN类型
* @param string $relation 关联名
*/
public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER')
public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER', $relation = null)
{
$this->parent = $parent;
$this->model = $model;
@@ -33,6 +34,7 @@ class BelongsTo extends OneToOne
$this->localKey = $localKey;
$this->joinType = $joinType;
$this->query = (new $model)->db();
$this->relation = $relation;
}
/**
@@ -48,7 +50,16 @@ class BelongsTo extends OneToOne
if ($closure) {
call_user_func_array($closure, [ & $this->query]);
}
return $this->query->where($this->localKey, $this->parent->$foreignKey)->relation($subRelation)->find();
$relationModel = $this->query
->where($this->localKey, $this->parent->$foreignKey)
->relation($subRelation)
->find();
if ($relationModel) {
$relationModel->setParent(clone $this->parent);
}
return $relationModel;
}
/**
@@ -128,6 +139,8 @@ class BelongsTo extends OneToOne
$relationModel = null;
} else {
$relationModel = $data[$result->$foreignKey];
$relationModel->setParent(clone $result);
$relationModel->isUpdate(true);
}
if ($relationModel && !empty($this->bindAttr)) {
@@ -135,7 +148,7 @@ class BelongsTo extends OneToOne
$this->bindAttr($relationModel, $result, $this->bindAttr);
}
// 设置关联属性
$result->setAttr($attr, $relationModel);
$result->setRelation($attr, $relationModel);
}
}
}
@@ -159,13 +172,46 @@ class BelongsTo extends OneToOne
$relationModel = null;
} else {
$relationModel = $data[$result->$foreignKey];
$relationModel->setParent(clone $result);
$relationModel->isUpdate(true);
}
if ($relationModel && !empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($relationModel, $result, $this->bindAttr);
}
// 设置关联属性
$result->setAttr(Loader::parseName($relation), $relationModel);
$result->setRelation(Loader::parseName($relation), $relationModel);
}
/**
* 添加关联数据
* @access public
* @param Model $model 关联模型对象
* @return Model
*/
public function associate($model)
{
$foreignKey = $this->foreignKey;
$pk = $model->getPk();
$this->parent->setAttr($foreignKey, $model->$pk);
$this->parent->save();
return $this->parent->setRelation($this->relation, $model);
}
/**
* 注销关联数据
* @access public
* @return Model
*/
public function dissociate()
{
$foreignKey = $this->foreignKey;
$this->parent->setAttr($foreignKey, null);
$this->parent->save();
return $this->parent->setRelation($this->relation, null);
}
}

View File

@@ -24,8 +24,9 @@ class BelongsToMany extends Relation
{
// 中间表表名
protected $middle;
// 中间表模型
// 中间表模型名称
protected $pivotName;
// 中间表模型对象
protected $pivot;
/**
@@ -43,8 +44,14 @@ class BelongsToMany extends Relation
$this->model = $model;
$this->foreignKey = $foreignKey;
$this->localKey = $localKey;
$this->middle = $table;
$this->query = (new $model)->db();
if (false !== strpos($table, '\\')) {
$this->pivotName = $table;
$this->middle = basename(str_replace('\\', '/', $table));
} else {
$this->middle = $table;
}
$this->query = (new $model)->db();
$this->pivot = $this->newPivot();
}
/**
@@ -54,7 +61,7 @@ class BelongsToMany extends Relation
*/
public function pivot($pivot)
{
$this->pivot = $pivot;
$this->pivotName = $pivot;
return $this;
}
@@ -63,9 +70,9 @@ class BelongsToMany extends Relation
* @param $data
* @return mixed
*/
protected function newPivot($data)
protected function newPivot($data = [])
{
$pivot = $this->pivot ?: '\\think\\model\\Pivot';
$pivot = $this->pivotName ?: '\\think\\model\\Pivot';
return new $pivot($this->parent, $data, $this->middle);
}
@@ -102,7 +109,7 @@ class BelongsToMany extends Relation
// 关联查询
$pk = $this->parent->getPk();
$condition['pivot.' . $localKey] = $this->parent->$pk;
return $this->belongsToManyQuery($middle, $foreignKey, $localKey, $condition);
return $this->belongsToManyQuery($foreignKey, $localKey, $condition);
}
/**
@@ -114,7 +121,7 @@ class BelongsToMany extends Relation
public function getRelation($subRelation = '', $closure = null)
{
if ($closure) {
call_user_func_array($closure, [& $this->query]);
call_user_func_array($closure, [ & $this->query]);
}
$result = $this->buildQuery()->relation($subRelation)->select();
$this->hydratePivot($result);
@@ -260,7 +267,7 @@ class BelongsToMany extends Relation
$data[$result->$pk] = [];
}
$result->setAttr($attr, $this->resultSetBuild($data[$result->$pk]));
$result->setRelation($attr, $this->resultSetBuild($data[$result->$pk]));
}
}
}
@@ -286,7 +293,7 @@ class BelongsToMany extends Relation
if (!isset($data[$pk])) {
$data[$pk] = [];
}
$result->setAttr(Loader::parseName($relation), $this->resultSetBuild($data[$pk]));
$result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$pk]));
}
}
@@ -303,7 +310,7 @@ class BelongsToMany extends Relation
$count = 0;
if (isset($result->$pk)) {
$pk = $result->$pk;
$count = $this->belongsToManyQuery($this->middle, $this->foreignKey, $this->localKey, ['pivot.' . $this->localKey => $pk])->count();
$count = $this->belongsToManyQuery($this->foreignKey, $this->localKey, ['pivot.' . $this->localKey => $pk])->count();
}
return $count;
}
@@ -316,7 +323,7 @@ class BelongsToMany extends Relation
*/
public function getRelationCountQuery($closure)
{
return $this->belongsToManyQuery($this->middle, $this->foreignKey, $this->localKey, [
return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
'pivot.' . $this->localKey => [
'exp',
'=' . $this->parent->getTable() . '.' . $this->parent->getPk(),
@@ -335,7 +342,7 @@ class BelongsToMany extends Relation
protected function eagerlyManyToMany($where, $relation, $subRelation = '')
{
// 预载入关联查询 支持嵌套预载入
$list = $this->belongsToManyQuery($this->middle, $this->foreignKey, $this->localKey, $where)->with($subRelation)->select();
$list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where)->with($subRelation)->select();
// 组装模型数据
$data = [];
@@ -359,21 +366,23 @@ class BelongsToMany extends Relation
/**
* BELONGS TO MANY 关联查询
* @access public
* @param string $table 中间表名
* @param string $foreignKey 关联模型关联键
* @param string $localKey 当前模型关联键
* @param array $condition 关联查询条件
* @return Query
*/
protected function belongsToManyQuery($table, $foreignKey, $localKey, $condition = [])
protected function belongsToManyQuery($foreignKey, $localKey, $condition = [])
{
// 关联查询封装
$tableName = $this->query->getTable();
$relationFk = $this->query->getPk();
$query = $this->query->field($tableName . '.*')
$tableName = $this->query->getTable();
$table = $this->pivot->getTable();
$fields = $this->getQueryFields($tableName);
$query = $this->query->field($fields)
->field(true, false, $table, 'pivot', 'pivot__');
if (empty($this->baseQuery)) {
$relationFk = $this->query->getPk();
$query->join($table . ' pivot', 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk)
->where($condition);
}
@@ -450,7 +459,7 @@ class BelongsToMany extends Relation
$ids = (array) $id;
foreach ($ids as $id) {
$pivot[$this->foreignKey] = $id;
$this->query->table($this->middle)->insert($pivot, true);
$this->pivot->insert($pivot, true);
$result[] = $this->newPivot($pivot);
}
if (count($result) == 1) {
@@ -488,8 +497,7 @@ class BelongsToMany extends Relation
if (isset($id)) {
$pivot[$this->foreignKey] = is_array($id) ? ['in', $id] : $id;
}
$this->query->table($this->middle)->where($pivot)->delete();
$this->pivot->where($pivot)->delete();
// 删除关联表数据
if (isset($id) && $relationDel) {
$model = $this->model;
@@ -497,6 +505,55 @@ class BelongsToMany extends Relation
}
}
/**
* 数据同步
* @param array $ids
* @param bool $detaching
* @return array
*/
public function sync($ids, $detaching = true)
{
$changes = [
'attached' => [],
'detached' => [],
'updated' => [],
];
$pk = $this->parent->getPk();
$current = $this->pivot->where($this->localKey, $this->parent->$pk)
->column($this->foreignKey);
$records = [];
foreach ($ids as $key => $value) {
if (!is_array($value)) {
$records[$value] = [];
} else {
$records[$key] = $value;
}
}
$detach = array_diff($current, array_keys($records));
if ($detaching && count($detach) > 0) {
$this->detach($detach);
$changes['detached'] = $detach;
}
foreach ($records as $id => $attributes) {
if (!in_array($id, $current)) {
$this->attach($id, $attributes);
$changes['attached'][] = $id;
} elseif (count($attributes) > 0 &&
$this->attach($id, $attributes)
) {
$changes['updated'][] = $id;
}
}
return $changes;
}
/**
* 执行基础查询(进执行一次)
* @access protected
@@ -504,9 +561,10 @@ class BelongsToMany extends Relation
*/
protected function baseQuery()
{
if (empty($this->baseQuery)) {
$pk = $this->parent->getPk();
$this->query->join($this->middle . ' pivot', 'pivot.' . $this->foreignKey . '=' . $this->query->getTable() . '.' . $this->query->getPk())->where('pivot.' . $this->localKey, $this->parent->$pk);
if (empty($this->baseQuery) && $this->parent->getData()) {
$pk = $this->parent->getPk();
$table = $this->pivot->getTable();
$this->query->join($table . ' pivot', 'pivot.' . $this->foreignKey . '=' . $this->query->getTable() . '.' . $this->query->getPk())->where('pivot.' . $this->localKey, $this->parent->$pk);
$this->baseQuery = true;
}
}

View File

@@ -11,7 +11,6 @@
namespace think\model\relation;
use think\Db;
use think\db\Query;
use think\Loader;
use think\Model;
@@ -47,7 +46,14 @@ class HasMany extends Relation
if ($closure) {
call_user_func_array($closure, [ & $this->query]);
}
return $this->relation($subRelation)->select();
$list = $this->relation($subRelation)->select();
$parent = clone $this->parent;
foreach ($list as &$model) {
$model->setParent($parent);
}
return $list;
}
/**
@@ -84,7 +90,12 @@ class HasMany extends Relation
if (!isset($data[$result->$localKey])) {
$data[$result->$localKey] = [];
}
$result->setAttr($attr, $this->resultSetBuild($data[$result->$localKey]));
foreach ($data[$result->$localKey] as &$relationModel) {
$relationModel->setParent(clone $result);
}
$result->setRelation($attr, $this->resultSetBuild($data[$result->$localKey]));
}
}
}
@@ -108,7 +119,12 @@ class HasMany extends Relation
if (!isset($data[$result->$localKey])) {
$data[$result->$localKey] = [];
}
$result->setAttr(Loader::parseName($relation), $this->resultSetBuild($data[$result->$localKey]));
foreach ($data[$result->$localKey] as &$relationModel) {
$relationModel->setParent(clone $result);
}
$result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$result->$localKey]));
}
}

View File

@@ -11,7 +11,6 @@
namespace think\model\relation;
use think\Db;
use think\db\Query;
use think\Exception;
use think\Loader;
@@ -57,6 +56,7 @@ class HasManyThrough extends Relation
if ($closure) {
call_user_func_array($closure, [ & $this->query]);
}
return $this->relation($subRelation)->select();
}
@@ -96,8 +96,7 @@ class HasManyThrough extends Relation
* @return void
*/
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class)
{
}
{}
/**
* 预载入关联查询 返回模型对象
@@ -110,8 +109,7 @@ class HasManyThrough extends Relation
* @return void
*/
public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class)
{
}
{}
/**
* 关联统计
@@ -121,8 +119,7 @@ class HasManyThrough extends Relation
* @return integer
*/
public function relationCount($result, $closure)
{
}
{}
/**
* 执行基础查询(进执行一次)
@@ -131,12 +128,11 @@ class HasManyThrough extends Relation
*/
protected function baseQuery()
{
if (empty($this->baseQuery)) {
if (empty($this->baseQuery) && $this->parent->getData()) {
$through = $this->through;
$model = $this->model;
$alias = Loader::parseName(basename(str_replace('\\', '/', $model)));
$alias = Loader::parseName(basename(str_replace('\\', '/', $this->model)));
$throughTable = $through::getTable();
$pk = (new $this->model)->getPk();
$pk = (new $through)->getPk();
$throughKey = $this->throughKey;
$modelTable = $this->parent->getTable();
$this->query->field($alias . '.*')->alias($alias)

View File

@@ -50,7 +50,13 @@ class HasOne extends OneToOne
call_user_func_array($closure, [ & $this->query]);
}
// 判断关联类型执行查询
return $this->query->where($this->foreignKey, $this->parent->$localKey)->relation($subRelation)->find();
$relationModel = $this->query->where($this->foreignKey, $this->parent->$localKey)->relation($subRelation)->find();
if ($relationModel) {
$relationModel->setParent(clone $this->parent);
}
return $relationModel;
}
/**
@@ -132,13 +138,15 @@ class HasOne extends OneToOne
$relationModel = null;
} else {
$relationModel = $data[$result->$localKey];
}
if ($relationModel && !empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($relationModel, $result, $this->bindAttr);
$relationModel->setParent(clone $result);
$relationModel->isUpdate(true);
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($relationModel, $result, $this->bindAttr);
}
}
// 设置关联属性
$result->setAttr($attr, $relationModel);
$result->setRelation($attr, $relationModel);
}
}
}
@@ -163,13 +171,15 @@ class HasOne extends OneToOne
$relationModel = null;
} else {
$relationModel = $data[$result->$localKey];
$relationModel->setParent(clone $result);
$relationModel->isUpdate(true);
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($relationModel, $result, $this->bindAttr);
}
}
if ($relationModel && !empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($relationModel, $result, $this->bindAttr);
}
$result->setAttr(Loader::parseName($relation), $relationModel);
$result->setRelation(Loader::parseName($relation), $relationModel);
}
}

View File

@@ -11,7 +11,6 @@
namespace think\model\relation;
use think\Db;
use think\db\Query;
use think\Exception;
use think\Loader;
@@ -56,7 +55,14 @@ class MorphMany extends Relation
if ($closure) {
call_user_func_array($closure, [ & $this->query]);
}
return $this->relation($subRelation)->select();
$list = $this->relation($subRelation)->select();
$parent = clone $this->parent;
foreach ($list as &$model) {
$model->setParent($parent);
}
return $list;
}
/**
@@ -119,7 +125,11 @@ class MorphMany extends Relation
if (!isset($data[$result->$pk])) {
$data[$result->$pk] = [];
}
$result->setAttr($attr, $this->resultSetBuild($data[$result->$pk]));
foreach ($data[$result->$pk] as &$relationModel) {
$relationModel->setParent(clone $result);
$relationModel->isUpdate(true);
}
$result->setRelation($attr, $this->resultSetBuild($data[$result->$pk]));
}
}
}
@@ -141,7 +151,17 @@ class MorphMany extends Relation
$this->morphKey => $result->$pk,
$this->morphType => $this->type,
], $relation, $subRelation, $closure);
$result->setAttr(Loader::parseName($relation), $this->resultSetBuild($data[$result->$pk]));
if (!isset($data[$result->$pk])) {
$data[$result->$pk] = [];
}
foreach ($data[$result->$pk] as &$relationModel) {
$relationModel->setParent(clone $result);
$relationModel->isUpdate(true);
}
$result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$result->$pk]));
}
}
@@ -253,7 +273,7 @@ class MorphMany extends Relation
*/
protected function baseQuery()
{
if (empty($this->baseQuery)) {
if (empty($this->baseQuery) && $this->parent->getData()) {
$pk = $this->parent->getPk();
$map[$this->morphKey] = $this->parent->$pk;
$map[$this->morphType] = $this->type;

View File

@@ -0,0 +1,229 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\model\relation;
use think\db\Query;
use think\Exception;
use think\Loader;
use think\Model;
use think\model\Relation;
class MorphOne extends Relation
{
// 多态字段
protected $morphKey;
protected $morphType;
// 多态类型
protected $type;
/**
* 构造函数
* @access public
* @param Model $parent 上级模型对象
* @param string $model 模型名
* @param string $morphKey 关联外键
* @param string $morphType 多态字段名
* @param string $type 多态类型
*/
public function __construct(Model $parent, $model, $morphKey, $morphType, $type)
{
$this->parent = $parent;
$this->model = $model;
$this->type = $type;
$this->morphKey = $morphKey;
$this->morphType = $morphType;
$this->query = (new $model)->db();
}
/**
* 延迟获取关联数据
* @param string $subRelation 子关联名
* @param \Closure $closure 闭包查询条件
* @return false|\PDOStatement|string|\think\Collection
*/
public function getRelation($subRelation = '', $closure = null)
{
if ($closure) {
call_user_func_array($closure, [ & $this->query]);
}
$relationModel = $this->relation($subRelation)->find();
if ($relationModel) {
$relationModel->setParent(clone $this->parent);
}
return $relationModel;
}
/**
* 根据关联条件查询当前模型
* @access public
* @param string $operator 比较操作符
* @param integer $count 个数
* @param string $id 关联表的统计字段
* @param string $joinType JOIN类型
* @return Query
*/
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
{
return $this->parent;
}
/**
* 根据关联条件查询当前模型
* @access public
* @param mixed $where 查询条件(数组或者闭包)
* @return Query
*/
public function hasWhere($where = [])
{
throw new Exception('relation not support: hasWhere');
}
/**
* 预载入关联查询
* @access public
* @param array $resultSet 数据集
* @param string $relation 当前关联名
* @param string $subRelation 子关联名
* @param \Closure $closure 闭包
* @return void
*/
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
{
$morphType = $this->morphType;
$morphKey = $this->morphKey;
$type = $this->type;
$range = [];
foreach ($resultSet as $result) {
$pk = $result->getPk();
// 获取关联外键列表
if (isset($result->$pk)) {
$range[] = $result->$pk;
}
}
if (!empty($range)) {
$data = $this->eagerlyMorphToOne([
$morphKey => ['in', $range],
$morphType => $type,
], $relation, $subRelation, $closure);
// 关联属性名
$attr = Loader::parseName($relation);
// 关联数据封装
foreach ($resultSet as $result) {
if (!isset($data[$result->$pk])) {
$relationModel = null;
} else {
$relationModel = $data[$result->$pk];
$relationModel->setParent(clone $result);
$relationModel->isUpdate(true);
}
$result->setRelation($attr, $relationModel);
}
}
}
/**
* 预载入关联查询
* @access public
* @param Model $result 数据对象
* @param string $relation 当前关联名
* @param string $subRelation 子关联名
* @param \Closure $closure 闭包
* @return void
*/
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
{
$pk = $result->getPk();
if (isset($result->$pk)) {
$pk = $result->$pk;
$data = $this->eagerlyMorphToOne([
$this->morphKey => $pk,
$this->morphType => $this->type,
], $relation, $subRelation, $closure);
if (isset($data[$pk])) {
$relationModel = $data[$pk];
$relationModel->setParent(clone $result);
$relationModel->isUpdate(true);
} else {
$relationModel = null;
}
$result->setRelation(Loader::parseName($relation), $relationModel);
}
}
/**
* 多态一对一 关联模型预查询
* @access public
* @param array $where 关联预查询条件
* @param string $relation 关联名
* @param string $subRelation 子关联
* @param bool|\Closure $closure 闭包
* @return array
*/
protected function eagerlyMorphToOne($where, $relation, $subRelation = '', $closure = false)
{
// 预载入关联查询 支持嵌套预载入
if ($closure) {
call_user_func_array($closure, [ & $this]);
}
$list = $this->query->where($where)->with($subRelation)->find();
$morphKey = $this->morphKey;
// 组装模型数据
$data = [];
foreach ($list as $set) {
$data[$set->$morphKey][] = $set;
}
return $data;
}
/**
* 保存(新增)当前关联数据对象
* @access public
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
* @return Model|false
*/
public function save($data)
{
if ($data instanceof Model) {
$data = $data->getData();
}
// 保存关联表数据
$pk = $this->parent->getPk();
$model = new $this->model;
$data[$this->morphKey] = $this->parent->$pk;
$data[$this->morphType] = $this->type;
return $model->save($data) ? $model : false;
}
/**
* 执行基础查询(进执行一次)
* @access protected
* @return void
*/
protected function baseQuery()
{
if (empty($this->baseQuery) && $this->parent->getData()) {
$pk = $this->parent->getPk();
$map[$this->morphKey] = $this->parent->$pk;
$map[$this->morphType] = $this->type;
$this->query->where($map);
$this->baseQuery = true;
}
}
}

View File

@@ -23,6 +23,7 @@ class MorphTo extends Relation
protected $morphType;
// 多态别名
protected $alias;
protected $relation;
/**
* 构造函数
@@ -31,13 +32,15 @@ class MorphTo extends Relation
* @param string $morphType 多态字段名
* @param string $morphKey 外键名
* @param array $alias 多态别名定义
* @param string $relation 关联名
*/
public function __construct(Model $parent, $morphType, $morphKey, $alias = [])
public function __construct(Model $parent, $morphType, $morphKey, $alias = [], $relation = null)
{
$this->parent = $parent;
$this->morphType = $morphType;
$this->morphKey = $morphKey;
$this->alias = $alias;
$this->relation = $relation;
}
/**
@@ -53,8 +56,13 @@ class MorphTo extends Relation
// 多态模型
$model = $this->parseModel($this->parent->$morphType);
// 主键数据
$pk = $this->parent->$morphKey;
return (new $model)->relation($subRelation)->find($pk);
$pk = $this->parent->$morphKey;
$relationModel = (new $model)->relation($subRelation)->find($pk);
if ($relationModel) {
$relationModel->setParent(clone $this->parent);
}
return $relationModel;
}
/**
@@ -165,7 +173,11 @@ class MorphTo extends Relation
if (!isset($data[$result->$morphKey])) {
throw new Exception('relation data not exists :' . $this->model);
} else {
$result->setAttr($attr, $data[$result->$morphKey]);
$relationModel = $data[$result->$morphKey];
$relationModel->setParent(clone $result);
$relationModel->isUpdate(true);
$result->setRelation($attr, $relationModel);
}
}
}
@@ -217,9 +229,46 @@ class MorphTo extends Relation
$pk = $this->parent->{$this->morphKey};
$data = (new $model)->with($subRelation)->find($pk);
if ($data) {
$data->setParent(clone $result);
$data->isUpdate(true);
}
$result->setAttr(Loader::parseName($relation), $data ?: null);
$result->setRelation(Loader::parseName($relation), $data ?: null);
}
/**
* 添加关联数据
* @access public
* @param Model $model 关联模型对象
* @return Model
*/
public function associate($model)
{
$morphKey = $this->morphKey;
$morphType = $this->morphType;
$pk = $model->getPk();
$this->parent->setAttr($morphKey, $model->$pk);
$this->parent->setAttr($morphType, get_class($model));
$this->parent->save();
return $this->parent->setRelation($this->relation, $model);
}
/**
* 注销关联数据
* @access public
* @return Model
*/
public function dissociate()
{
$morphKey = $this->morphKey;
$morphType = $this->morphType;
$this->parent->setAttr($morphKey, null);
$this->parent->setAttr($morphType, null);
$this->parent->save();
return $this->parent->setRelation($this->relation, null);
}
/**
@@ -228,6 +277,5 @@ class MorphTo extends Relation
* @return void
*/
protected function baseQuery()
{
}
{}
}

View File

@@ -30,6 +30,8 @@ abstract class OneToOne extends Relation
protected $joinType;
// 要绑定的属性
protected $bindAttr = [];
// 关联方法名
protected $relation;
/**
* 设置join类型
@@ -243,13 +245,19 @@ abstract class OneToOne extends Relation
}
}
}
if (isset($list[$relation])) {
$relationModel = new $model($list[$relation]);
$relationModel->setParent(clone $result);
$relationModel->isUpdate(true);
if (!empty($this->bindAttr)) {
$this->bindAttr($relationModel, $result, $this->bindAttr);
}
} else {
$relationModel = null;
}
$result->setAttr(Loader::parseName($relation), !isset($relationModel) ? null : $relationModel->isUpdate(true));
$result->setRelation(Loader::parseName($relation), $relationModel);
}
/**
@@ -309,6 +317,5 @@ abstract class OneToOne extends Relation
* @return void
*/
protected function baseQuery()
{
}
{}
}

View File

@@ -30,7 +30,7 @@ trait SoftDelete
{
$model = new static();
$field = $model->getDeleteTimeField(true);
return $model->db(false);
return $model->getQuery();
}
/**
@@ -42,7 +42,7 @@ trait SoftDelete
{
$model = new static();
$field = $model->getDeleteTimeField(true);
return $model->db(false)
return $model->getQuery()
->useSoftDelete($field, ['not null', '']);
}
@@ -60,11 +60,10 @@ trait SoftDelete
$name = $this->getDeleteTimeField();
if (!$force) {
// 软删除
$this->change[] = $name;
$this->data[$name] = $this->autoWriteTimestamp($name);
$result = $this->isUpdate()->save();
} else {
$result = $this->db(false)->delete($this->data);
$result = $this->getQuery()->delete($this->data);
}
$this->trigger('after_delete', $this);
@@ -117,7 +116,7 @@ trait SoftDelete
$where[$pk] = $this->getData($pk);
}
// 恢复删除
return $this->db(false)
return $this->getQuery()
->useSoftDelete($name, ['not null', ''])
->where($where)
->update([$name => null]);
@@ -143,7 +142,7 @@ trait SoftDelete
*/
protected function getDeleteTimeField($read = false)
{
$field = isset($this->deleteTime) ? $this->deleteTime : 'delete_time';
$field = property_exists($this, 'deleteTime') && isset($this->deleteTime) ? $this->deleteTime : 'delete_time';
if (!strpos($field, '.')) {
$field = '__TABLE__.' . $field;
}

View File

@@ -79,7 +79,7 @@
<html>
<head>
<meta charset="UTF-8">
<title><?php echo lang('System Error'); ?></title>
<title><?php echo \think\Lang::get('System Error'); ?></title>
<meta name="robots" content="noindex,nofollow" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<style>