1、更换编辑器
2、内核升级
This commit is contained in:
@@ -450,9 +450,9 @@ class App
|
||||
// 监听app_init
|
||||
Hook::listen('app_init');
|
||||
|
||||
self::$init = $config;
|
||||
self::$init = true;
|
||||
}
|
||||
return self::$init;
|
||||
return Config::get();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -84,6 +84,10 @@ class Log
|
||||
public static function record($msg, $type = 'log')
|
||||
{
|
||||
self::$log[$type][] = $msg;
|
||||
if (IS_CLI && count(self::$log[$type]) > 100) {
|
||||
// 命令行下面日志写入改进
|
||||
self::save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace think;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use think\Cache;
|
||||
use think\Collection;
|
||||
use think\Config;
|
||||
use think\Db;
|
||||
use think\db\Query;
|
||||
@@ -20,6 +21,13 @@ use think\Exception;
|
||||
use think\Exception\ValidateException;
|
||||
use think\Loader;
|
||||
use think\model\Relation;
|
||||
use think\model\relation\BelongsTo;
|
||||
use think\model\relation\BelongsToMany;
|
||||
use think\model\relation\HasMany;
|
||||
use think\model\relation\HasManyThrough;
|
||||
use think\model\relation\HasOne;
|
||||
use think\model\relation\MorphMany;
|
||||
use think\model\relation\MorphTo;
|
||||
use think\paginator\Collection as PaginatorCollection;
|
||||
|
||||
/**
|
||||
@@ -103,6 +111,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
protected $useGlobalScope = true;
|
||||
// 是否采用批量验证
|
||||
protected $batchValidate = false;
|
||||
// 查询数据集对象
|
||||
protected $resultSetType;
|
||||
//
|
||||
protected static $db;
|
||||
|
||||
/**
|
||||
* 初始化过的模型.
|
||||
@@ -186,26 +198,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
return self::$links[$model];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关联模型实例
|
||||
* @access protected
|
||||
* @param string|array $relation 关联查询
|
||||
* @return Relation|Query
|
||||
*/
|
||||
protected function relation($relation = null)
|
||||
{
|
||||
if (!is_null($relation)) {
|
||||
// 执行关联查询
|
||||
return $this->db()->relation($relation);
|
||||
}
|
||||
|
||||
// 获取关联对象实例
|
||||
if (is_null($this->relation)) {
|
||||
$this->relation = new Relation($this);
|
||||
}
|
||||
return $this->relation;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化模型
|
||||
* @access protected
|
||||
@@ -300,7 +292,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
}
|
||||
|
||||
// 标记字段更改
|
||||
if (!isset($this->data[$name]) || (0 !== strcmp($this->data[$name], $value) && !in_array($name, $this->change))) {
|
||||
if (isset($this->data[$name]) && is_scalar($this->data[$name]) && is_scalar($value) && 0 !== strcmp($this->data[$name], $value)) {
|
||||
$this->change[] = $name;
|
||||
} elseif (!isset($this->data[$name]) || $value != $this->data[$name]) {
|
||||
$this->change[] = $name;
|
||||
}
|
||||
// 设置数据对象属性
|
||||
@@ -421,9 +415,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
$value = $this->readTransform($value, $this->type[$name]);
|
||||
} elseif ($notFound) {
|
||||
$method = Loader::parseName($name, 1);
|
||||
if (method_exists($this, $method) && !method_exists('\think\Model', $method)) {
|
||||
if (method_exists($this, $method) && $this->$method() instanceof Relation) {
|
||||
// 不存在该字段 获取关联数据
|
||||
$value = $this->relation()->getRelation($method);
|
||||
$value = $this->$method()->getRelation();
|
||||
// 保存关联对象值
|
||||
$this->data[$name] = $value;
|
||||
} else {
|
||||
@@ -493,11 +487,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
* 设置需要追加的输出属性
|
||||
* @access public
|
||||
* @param array $append 属性列表
|
||||
* @param bool $override 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function append($append = [])
|
||||
public function append($append = [], $override = false)
|
||||
{
|
||||
$this->append = $append;
|
||||
$this->append = $override ? $append : array_merge($this->append, $append);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -505,22 +500,24 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
* 设置需要隐藏的输出属性
|
||||
* @access public
|
||||
* @param array $hidden 属性列表
|
||||
* @param bool $override 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function hidden($hidden = [])
|
||||
public function hidden($hidden = [], $override = false)
|
||||
{
|
||||
$this->hidden = $hidden;
|
||||
$this->hidden = $override ? $hidden : array_merge($this->hidden, $hidden);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要输出的属性
|
||||
* @param array $visible
|
||||
* @param bool $override 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function visible($visible = [])
|
||||
public function visible($visible = [], $override = false)
|
||||
{
|
||||
$this->visible = $visible;
|
||||
$this->visible = $override ? $visible : array_merge($this->visible, $visible);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -578,6 +575,25 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
return json_encode($this->toArray(), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换当前模型数据集为数据集对象
|
||||
* @access public
|
||||
* @param array|Collection $collection 数据集
|
||||
* @return Collection
|
||||
*/
|
||||
public function toCollection($collection)
|
||||
{
|
||||
if ($this->resultSetType) {
|
||||
if ('collection' == $this->resultSetType) {
|
||||
$collection = new Collection($collection);
|
||||
} else {
|
||||
$class = $this->resultSetType;
|
||||
$collection = new $class($collection);
|
||||
}
|
||||
}
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型对象的主键
|
||||
* @access public
|
||||
@@ -717,7 +733,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
$result = $this->db()->insert($this->data);
|
||||
|
||||
// 获取自动增长主键
|
||||
if ($result && is_string($pk) && !isset($this->data[$pk])) {
|
||||
if ($result && is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) {
|
||||
$insertId = $this->db()->getLastInsID($sequence);
|
||||
if ($insertId) {
|
||||
$this->data[$pk] = $insertId;
|
||||
@@ -1140,8 +1156,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
*/
|
||||
public static function useGlobalScope($use)
|
||||
{
|
||||
$model = new static();
|
||||
$model->useGlobalScope = $use;
|
||||
$model = new static();
|
||||
self::$db = $model->db($use);
|
||||
return $model;
|
||||
}
|
||||
|
||||
@@ -1157,18 +1173,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
public static function has($relation, $operator = '>=', $count = 1, $id = '*')
|
||||
{
|
||||
$model = new static();
|
||||
$info = $model->$relation()->getRelationInfo();
|
||||
$table = $info['model']::getTable();
|
||||
switch ($info['type']) {
|
||||
case Relation::HAS_MANY:
|
||||
return $model->db()->alias('a')
|
||||
->join($table . ' b', 'a.' . $info['localKey'] . '=b.' . $info['foreignKey'], $info['joinType'])
|
||||
->group('b.' . $info['foreignKey'])
|
||||
->having('count(' . $id . ')' . $operator . $count);
|
||||
case Relation::HAS_MANY_THROUGH: // TODO
|
||||
default:
|
||||
return $model;
|
||||
}
|
||||
return $model->$relation()->has($model, $operator, $count, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1181,27 +1186,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
public static function hasWhere($relation, $where = [])
|
||||
{
|
||||
$model = new static();
|
||||
$info = $model->$relation()->getRelationInfo();
|
||||
switch ($info['type']) {
|
||||
case Relation::HAS_ONE:
|
||||
case Relation::HAS_MANY:
|
||||
$table = $info['model']::getTable();
|
||||
if (is_array($where)) {
|
||||
foreach ($where as $key => $val) {
|
||||
if (false === strpos($key, '.')) {
|
||||
$where['b.' . $key] = $val;
|
||||
unset($where[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $model->db()->alias('a')
|
||||
->field('a.*')
|
||||
->join($table . ' b', 'a.' . $info['localKey'] . '=b.' . $info['foreignKey'], $info['joinType'])
|
||||
->where($where);
|
||||
case Relation::HAS_MANY_THROUGH: // TODO
|
||||
default:
|
||||
return $model;
|
||||
}
|
||||
return $model->$relation()->hasWhere($model, $where);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1232,9 +1217,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
if (is_string($relations)) {
|
||||
$relations = explode(',', $relations);
|
||||
}
|
||||
$this->relation();
|
||||
|
||||
foreach ($relations as $relation) {
|
||||
$this->data[$relation] = $this->relation->getRelation($relation);
|
||||
$this->data[$relation] = $this->$relation()->getRelation();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
@@ -1244,11 +1229,24 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 关联名
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return array
|
||||
*/
|
||||
public function eagerlyResultSet($resultSet, $relation)
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $class = '')
|
||||
{
|
||||
return $this->relation()->eagerlyResultSet($resultSet, $relation);
|
||||
$relations = is_string($relation) ? explode(',', $relation) : $relation;
|
||||
foreach ($relations as $key => $relation) {
|
||||
$subRelation = '';
|
||||
$closure = false;
|
||||
if ($relation instanceof \Closure) {
|
||||
$closure = $relation;
|
||||
$relation = $key;
|
||||
}
|
||||
if (strpos($relation, '.')) {
|
||||
list($relation, $subRelation) = explode('.', $relation);
|
||||
}
|
||||
$this->$relation()->eagerlyResultSet($resultSet, $relation, $subRelation, $closure, $class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1256,11 +1254,25 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 关联名
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return Model
|
||||
*/
|
||||
public function eagerlyResult($result, $relation)
|
||||
public function eagerlyResult(&$result, $relation, $class = '')
|
||||
{
|
||||
return $this->relation()->eagerlyResult($result, $relation);
|
||||
$relations = is_string($relation) ? explode(',', $relation) : $relation;
|
||||
|
||||
foreach ($relations as $key => $relation) {
|
||||
$subRelation = '';
|
||||
$closure = false;
|
||||
if ($relation instanceof \Closure) {
|
||||
$closure = $relation;
|
||||
$relation = $key;
|
||||
}
|
||||
if (strpos($relation, '.')) {
|
||||
list($relation, $subRelation) = explode('.', $relation);
|
||||
}
|
||||
$this->$relation()->eagerlyResult($result, $relation, $subRelation, $closure, $class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1271,7 +1283,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
* @param string $localKey 关联主键
|
||||
* @param array $alias 别名定义
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Relation
|
||||
* @return HasOne
|
||||
*/
|
||||
public function hasOne($model, $foreignKey = '', $localKey = '', $alias = [], $joinType = 'INNER')
|
||||
{
|
||||
@@ -1279,7 +1291,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
$model = $this->parseModel($model);
|
||||
$localKey = $localKey ?: $this->getPk();
|
||||
$foreignKey = $foreignKey ?: Loader::parseName($this->name) . '_id';
|
||||
return $this->relation()->hasOne($model, $foreignKey, $localKey, $alias, $joinType);
|
||||
return new HasOne($this, $model, $foreignKey, $localKey, $alias, $joinType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1290,7 +1302,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
* @param string $otherKey 关联主键
|
||||
* @param array $alias 别名定义
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Relation
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function belongsTo($model, $foreignKey = '', $otherKey = '', $alias = [], $joinType = 'INNER')
|
||||
{
|
||||
@@ -1298,7 +1310,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
$model = $this->parseModel($model);
|
||||
$foreignKey = $foreignKey ?: Loader::parseName(basename(str_replace('\\', '/', $model))) . '_id';
|
||||
$otherKey = $otherKey ?: (new $model)->getPk();
|
||||
return $this->relation()->belongsTo($model, $foreignKey, $otherKey, $alias, $joinType);
|
||||
return new BelongsTo($this, $model, $foreignKey, $otherKey, $alias, $joinType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1308,7 +1320,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* @param array $alias 别名定义
|
||||
* @return Relation
|
||||
* @return HasMany
|
||||
*/
|
||||
public function hasMany($model, $foreignKey = '', $localKey = '', $alias = [])
|
||||
{
|
||||
@@ -1316,7 +1328,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
$model = $this->parseModel($model);
|
||||
$localKey = $localKey ?: $this->getPk();
|
||||
$foreignKey = $foreignKey ?: Loader::parseName($this->name) . '_id';
|
||||
return $this->relation()->hasMany($model, $foreignKey, $localKey, $alias);
|
||||
return new HasMany($this, $model, $foreignKey, $localKey, $alias);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1328,7 +1340,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
* @param string $throughKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* @param array $alias 别名定义
|
||||
* @return Relation
|
||||
* @return HasManyThrough
|
||||
*/
|
||||
public function hasManyThrough($model, $through, $foreignKey = '', $throughKey = '', $localKey = '', $alias = [])
|
||||
{
|
||||
@@ -1339,7 +1351,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
$foreignKey = $foreignKey ?: Loader::parseName($this->name) . '_id';
|
||||
$name = Loader::parseName(basename(str_replace('\\', '/', $through)));
|
||||
$throughKey = $throughKey ?: $name . '_id';
|
||||
return $this->relation()->hasManyThrough($model, $through, $foreignKey, $throughKey, $localKey, $alias);
|
||||
return new HasManyThrough($this, $model, $through, $foreignKey, $throughKey, $localKey, $alias);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1350,7 +1362,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 当前模型关联键
|
||||
* @param array $alias 别名定义
|
||||
* @return Relation
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function belongsToMany($model, $table = '', $foreignKey = '', $localKey = '', $alias = [])
|
||||
{
|
||||
@@ -1360,29 +1372,33 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
$table = $table ?: $this->db()->getTable(Loader::parseName($this->name) . '_' . $name);
|
||||
$foreignKey = $foreignKey ?: $name . '_id';
|
||||
$localKey = $localKey ?: Loader::parseName($this->name) . '_id';
|
||||
return $this->relation()->belongsToMany($model, $table, $foreignKey, $localKey, $alias);
|
||||
return new BelongsToMany($this, $model, $table, $foreignKey, $localKey, $alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* MORPH MANY 关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string|array $morph 多态字段信息
|
||||
* @param string $type 多态类型
|
||||
* @return Relation
|
||||
* @param string $model 模型名
|
||||
* @param string|array $morph 多态字段信息
|
||||
* @param string $type 多态类型
|
||||
* @return MorphMany
|
||||
*/
|
||||
public function morphMany($model, $morph, $type = '')
|
||||
public function morphMany($model, $morph = null, $type = '')
|
||||
{
|
||||
// 记录当前关联信息
|
||||
$model = $this->parseModel($model);
|
||||
$type = $type ?: Loader::parseName($this->name);
|
||||
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 $this->relation()->morphMany($model, $foreignKey, $morphType, $type);
|
||||
return new MorphMany($this, $model, $foreignKey, $morphType, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1390,7 +1406,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
* @access public
|
||||
* @param string|array $morph 多态字段信息
|
||||
* @param array $alias 多态别名定义
|
||||
* @return Relation
|
||||
* @return MorphTo
|
||||
*/
|
||||
public function morphTo($morph = null, $alias = [])
|
||||
{
|
||||
@@ -1405,7 +1421,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
$morphType = $morph . '_type';
|
||||
$foreignKey = $morph . '_id';
|
||||
}
|
||||
return $this->relation()->morphTo($morphType, $foreignKey, $alias);
|
||||
return new MorphTo($this, $morphType, $foreignKey, $alias);
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
@@ -1424,7 +1440,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
|
||||
|
||||
public static function __callStatic($method, $params)
|
||||
{
|
||||
$query = (new static())->db();
|
||||
if (isset(static::$db)) {
|
||||
$query = static::$db;
|
||||
} else {
|
||||
$query = (new static())->db();
|
||||
}
|
||||
|
||||
return call_user_func_array([$query, $method], $params);
|
||||
}
|
||||
|
||||
|
||||
@@ -852,7 +852,7 @@ class Request
|
||||
$keys = array_keys($file);
|
||||
$count = count($file['name']);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
if (empty($file['tmp_name'][$i])) {
|
||||
if (empty($file['tmp_name'][$i]) || !is_file($file['tmp_name'][$i])) {
|
||||
continue;
|
||||
}
|
||||
$temp['key'] = $key;
|
||||
@@ -866,7 +866,7 @@ class Request
|
||||
if ($file instanceof File) {
|
||||
$array[$key] = $file;
|
||||
} else {
|
||||
if (empty($file['tmp_name'])) {
|
||||
if (empty($file['tmp_name']) || !is_file($file['tmp_name'])) {
|
||||
continue;
|
||||
}
|
||||
$array[$key] = (new File($file['tmp_name']))->setUploadInfo($file);
|
||||
|
||||
@@ -133,7 +133,9 @@ class Response
|
||||
Hook::listen('response_end', $this);
|
||||
|
||||
// 清空当次请求有效的数据
|
||||
Session::flush();
|
||||
if (!($this instanceof RedirectResponse)) {
|
||||
Session::flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,7 +284,11 @@ class Response
|
||||
*/
|
||||
public function getHeader($name = '')
|
||||
{
|
||||
return !empty($name) ? $this->header[$name] : $this->header;
|
||||
if (!empty($name)) {
|
||||
return isset($this->header[$name]) ? $this->header[$name] : null;
|
||||
} else {
|
||||
return $this->header;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -770,6 +770,10 @@ class Route
|
||||
}
|
||||
}
|
||||
if (!empty($item)) {
|
||||
if (isset($panDomain)) {
|
||||
// 保存当前泛域名
|
||||
$request->route(['__domain__' => $panDomain]);
|
||||
}
|
||||
if (isset($item['[bind]'])) {
|
||||
// 解析子域名部署规则
|
||||
list($rule, $option, $pattern) = $item['[bind]'];
|
||||
|
||||
@@ -114,7 +114,9 @@ class Session
|
||||
if (is_null(self::$init)) {
|
||||
self::init();
|
||||
} elseif (false === self::$init) {
|
||||
session_start();
|
||||
if (PHP_SESSION_ACTIVE != session_status()) {
|
||||
session_start();
|
||||
}
|
||||
self::$init = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1072,7 +1072,7 @@ class Template
|
||||
$module = isset($module) ? $module : Request::instance()->module();
|
||||
$path = $this->config['view_base'] . ($module ? $module . DS : '');
|
||||
} else {
|
||||
$path = $this->config['view_path'];
|
||||
$path = isset($module) ? APP_PATH . $module . DS . basename($this->config['view_path']) . DS : $this->config['view_path'];
|
||||
}
|
||||
$template = $path . $template . '.' . ltrim($this->config['view_suffix'], '.');
|
||||
}
|
||||
|
||||
@@ -408,6 +408,9 @@ class Validate
|
||||
return $message;
|
||||
} elseif (true !== $result) {
|
||||
// 返回自定义错误信息
|
||||
if (is_string($result) && false !== strpos($result, ':')) {
|
||||
$result = str_replace([':attribute', ':rule'], [$title, (string) $rule], $result);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
$i++;
|
||||
@@ -419,13 +422,21 @@ class Validate
|
||||
/**
|
||||
* 验证是否和某个字段的值一致
|
||||
* @access protected
|
||||
* @param mixed $value 字段值
|
||||
* @param mixed $value 字段值
|
||||
* @param mixed $rule 验证规则
|
||||
* @param array $data 数据
|
||||
* @param string $field 字段名
|
||||
* @return bool
|
||||
*/
|
||||
protected function confirm($value, $rule, $data)
|
||||
protected function confirm($value, $rule, $data, $field = '')
|
||||
{
|
||||
if ('' == $rule) {
|
||||
if (strpos($field, '_confirm')) {
|
||||
$rule = strstr($field, '_confirm', true);
|
||||
} else {
|
||||
$rule = $field . '_confirm';
|
||||
}
|
||||
}
|
||||
return $this->getDataValue($data, $rule) == $value;
|
||||
}
|
||||
|
||||
@@ -1198,7 +1209,7 @@ class Validate
|
||||
$msg = $title . '规则错误';
|
||||
}
|
||||
|
||||
if (is_string($msg) && strpos($msg, '{%')) {
|
||||
if (is_string($msg) && 0 === strpos($msg, '{%')) {
|
||||
$msg = Lang::get(substr($msg, 2, -1));
|
||||
}
|
||||
|
||||
@@ -1244,7 +1255,7 @@ class Validate
|
||||
|
||||
public static function __callStatic($method, $params)
|
||||
{
|
||||
$class = new static;
|
||||
$class = self::make();
|
||||
if (method_exists($class, $method)) {
|
||||
return call_user_func_array([$class, $method], $params);
|
||||
} else {
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\Loader;
|
||||
use think\Request;
|
||||
|
||||
class View
|
||||
{
|
||||
// 视图实例
|
||||
@@ -34,7 +37,21 @@ class View
|
||||
{
|
||||
// 初始化模板引擎
|
||||
$this->engine((array) $engine);
|
||||
$this->replace = $replace;
|
||||
// 基础替换字符串
|
||||
$request = Request::instance();
|
||||
$base = $request->root();
|
||||
$root = strpos($base, '.') ? ltrim(dirname($base), DS) : $base;
|
||||
if ('' != $root) {
|
||||
$root = '/' . ltrim($root, '/');
|
||||
}
|
||||
$baseReplace = [
|
||||
'__ROOT__' => $root,
|
||||
'__URL__' => $base . '/' . $request->module() . '/' . Loader::parseName($request->controller()),
|
||||
'__STATIC__' => $root . '/static',
|
||||
'__CSS__' => $root . '/static/css',
|
||||
'__JS__' => $root . '/static/js',
|
||||
];
|
||||
$this->replace = array_merge($baseReplace, (array) $replace);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,22 +37,33 @@ abstract class Builder
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Connection $connection 数据库连接对象实例
|
||||
* @param Connection $connection 数据库连接对象实例
|
||||
* @param Query $query 数据库查询对象实例
|
||||
*/
|
||||
public function __construct(Connection $connection)
|
||||
public function __construct(Connection $connection, Query $query)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前的Query对象实例
|
||||
* @access protected
|
||||
* @param Query $query 当前查询对象实例
|
||||
* 获取当前的连接对象实例
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function setQuery(Query $query)
|
||||
public function getConnection()
|
||||
{
|
||||
$this->query = $query;
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的Query对象实例
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +101,7 @@ abstract class Builder
|
||||
$result = [];
|
||||
foreach ($data as $key => $val) {
|
||||
$item = $this->parseKey($key, $options);
|
||||
if (!in_array($key, $fields, true)) {
|
||||
if (false === strpos($key, '.') && !in_array($key, $fields, true)) {
|
||||
if ($options['strict']) {
|
||||
throw new Exception('fields not exists:[' . $key . ']');
|
||||
}
|
||||
@@ -100,9 +111,10 @@ abstract class Builder
|
||||
$result[$item] = 'NULL';
|
||||
} elseif (is_scalar($val)) {
|
||||
// 过滤非标量数据
|
||||
if ($this->query->isBind(substr($val, 1))) {
|
||||
if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) {
|
||||
$result[$item] = $val;
|
||||
} else {
|
||||
$key = str_replace('.', '_', $key);
|
||||
$this->query->bind($key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
|
||||
$result[$item] = ':' . $key;
|
||||
}
|
||||
@@ -182,6 +194,9 @@ abstract class Builder
|
||||
$item = [];
|
||||
foreach ((array) $tables as $key => $table) {
|
||||
if (!is_numeric($key)) {
|
||||
if (strpos($key, '@think')) {
|
||||
$key = strstr($key, '@think', true);
|
||||
}
|
||||
$key = $this->parseSqlTable($key);
|
||||
$item[] = $this->parseKey($key) . ' ' . $this->parseKey($table);
|
||||
} else {
|
||||
@@ -422,7 +437,7 @@ abstract class Builder
|
||||
$info = $type[$key];
|
||||
}
|
||||
if (isset($info)) {
|
||||
if (is_numeric($value) && strtotime($value)) {
|
||||
if (is_string($value)) {
|
||||
$value = strtotime($value) ?: $value;
|
||||
}
|
||||
|
||||
|
||||
@@ -489,10 +489,7 @@ abstract class Connection
|
||||
$result = $this->PDOStatement->fetchAll($this->fetchType);
|
||||
$this->numRows = count($result);
|
||||
|
||||
if (!empty($class)) {
|
||||
// 返回指定数据集对象类
|
||||
$result = new $class($result);
|
||||
} elseif ('collection' == $this->resultSetType) {
|
||||
if ('collection' == $this->resultSetType) {
|
||||
// 返回数据集Collection对象
|
||||
$result = new Collection($result);
|
||||
}
|
||||
|
||||
@@ -27,13 +27,15 @@ use think\exception\PDOException;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
use think\model\relation\BelongsTo;
|
||||
use think\model\relation\HasOne;
|
||||
use think\Paginator;
|
||||
|
||||
class Query
|
||||
{
|
||||
// 数据库Connection对象实例
|
||||
protected $connection;
|
||||
// 数据库驱动类型
|
||||
// 数据库Builder对象实例
|
||||
protected $builder;
|
||||
// 当前模型类名称
|
||||
protected $model;
|
||||
@@ -51,6 +53,8 @@ class Query
|
||||
protected $bind = [];
|
||||
// 数据表信息
|
||||
protected static $info = [];
|
||||
// 回调事件
|
||||
private static $event = [];
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
@@ -61,9 +65,10 @@ class Query
|
||||
public function __construct(Connection $connection = null, $model = '')
|
||||
{
|
||||
$this->connection = $connection ?: Db::connect([], true);
|
||||
$this->builder = $this->connection->getConfig('builder') ?: $this->connection->getConfig('type');
|
||||
$this->prefix = $this->connection->getConfig('prefix');
|
||||
$this->model = $model;
|
||||
// 设置当前连接的Builder对象
|
||||
$this->setBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,9 +116,42 @@ class Query
|
||||
public function connect($config)
|
||||
{
|
||||
$this->connection = Db::connect($config);
|
||||
$this->setBuilder();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前的数据库Builder对象
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function setBuilder()
|
||||
{
|
||||
$builder = $this->connection->getConfig('builder') ?: $this->connection->getConfig('type');
|
||||
$class = false !== strpos($builder, '\\') ? $builder : '\\think\\db\\builder\\' . ucfirst($builder);
|
||||
$this->builder = new $class($this->connection, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的模型对象名
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getModel()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的builder实例对象
|
||||
* @access public
|
||||
* @return Builder
|
||||
*/
|
||||
public function getBuilder()
|
||||
{
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定默认的数据表名(不含前缀)
|
||||
* @access public
|
||||
@@ -350,24 +388,6 @@ class Query
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的builder实例对象
|
||||
* @access protected
|
||||
* @return Builder
|
||||
*/
|
||||
protected function builder()
|
||||
{
|
||||
static $builder = [];
|
||||
$driver = $this->builder;
|
||||
if (!isset($builder[$driver])) {
|
||||
$class = false !== strpos($driver, '\\') ? $driver : '\\think\\db\\builder\\' . ucfirst($driver);
|
||||
$builder[$driver] = new $class($this->connection);
|
||||
}
|
||||
// 设置当前查询对象
|
||||
$builder[$driver]->setQuery($this);
|
||||
return $builder[$driver];
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到某个字段的值
|
||||
* @access public
|
||||
@@ -457,6 +477,9 @@ class Query
|
||||
$key1 = array_shift($fields);
|
||||
$key2 = $fields ? array_shift($fields) : '';
|
||||
$key = $key ?: $key1;
|
||||
if (strpos($key, '.')) {
|
||||
list($alias, $key) = explode('.', $key);
|
||||
}
|
||||
foreach ($resultSet as $val) {
|
||||
if ($count > 2) {
|
||||
$result[$val[$key]] = $val;
|
||||
@@ -704,6 +727,9 @@ class Query
|
||||
}
|
||||
}
|
||||
if (isset($alias)) {
|
||||
if (isset($this->options['alias'][$table])) {
|
||||
$table = $table . '@think' . uniqid();
|
||||
}
|
||||
$table = [$table => $alias];
|
||||
$this->alias($table);
|
||||
}
|
||||
@@ -976,6 +1002,20 @@ class Query
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除某个查询参数
|
||||
* @access public
|
||||
* @param string $option 参数名
|
||||
* @return $this
|
||||
*/
|
||||
public function removeOption($option)
|
||||
{
|
||||
if (isset($this->options[$option])) {
|
||||
unset($this->options[$option]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定查询数量
|
||||
* @access public
|
||||
@@ -1071,7 +1111,6 @@ class Query
|
||||
if (is_string($table)) {
|
||||
if (strpos($table, ')')) {
|
||||
// 子查询
|
||||
$table = $table;
|
||||
} elseif (strpos($table, ',')) {
|
||||
$tables = explode(',', $table);
|
||||
$table = [];
|
||||
@@ -1086,7 +1125,8 @@ class Query
|
||||
}
|
||||
} elseif (strpos($table, ' ')) {
|
||||
list($table, $alias) = explode(' ', $table);
|
||||
$table = [$table => $alias];
|
||||
|
||||
$table = [$table => $alias];
|
||||
$this->alias($table);
|
||||
}
|
||||
} else {
|
||||
@@ -1292,18 +1332,6 @@ class Query
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定数据集返回对象
|
||||
* @access public
|
||||
* @param string $class 指定返回的数据集对象类名
|
||||
* @return $this
|
||||
*/
|
||||
public function fetchClass($class)
|
||||
{
|
||||
$this->options['fetch_class'] = $class;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置从主服务器读取数据
|
||||
* @access public
|
||||
@@ -1435,6 +1463,11 @@ class Query
|
||||
$tableName = $this->parseSqlTable($tableName);
|
||||
}
|
||||
|
||||
// 修正子查询作为表名的问题
|
||||
if (strpos($tableName, ')')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
list($guid) = explode(' ', $tableName);
|
||||
$db = $this->getConfig('database');
|
||||
if (!isset(self::$info[$db . '.' . $guid])) {
|
||||
@@ -1601,13 +1634,14 @@ class Query
|
||||
$with = explode(',', $with);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$first = true;
|
||||
$currentModel = $this->model;
|
||||
|
||||
/** @var Model $class */
|
||||
$class = new $currentModel;
|
||||
foreach ($with as $key => $relation) {
|
||||
$closure = false;
|
||||
$subRelation = '';
|
||||
$closure = false;
|
||||
if ($relation instanceof \Closure) {
|
||||
// 支持闭包查询过滤关联条件
|
||||
$closure = $relation;
|
||||
@@ -1620,48 +1654,9 @@ class Query
|
||||
|
||||
/** @var Relation $model */
|
||||
$model = $class->$relation();
|
||||
$info = $model->getRelationInfo();
|
||||
if (in_array($info['type'], [Relation::HAS_ONE, Relation::BELONGS_TO])) {
|
||||
if (0 == $i) {
|
||||
$name = Loader::parseName(basename(str_replace('\\', '/', $currentModel)));
|
||||
$table = $this->getTable();
|
||||
$alias = isset($info['alias'][$name]) ? $info['alias'][$name] : $name;
|
||||
$this->table([$table => $alias]);
|
||||
if (isset($this->options['field'])) {
|
||||
$field = $this->options['field'];
|
||||
unset($this->options['field']);
|
||||
} else {
|
||||
$field = true;
|
||||
}
|
||||
$this->field($field, false, $table, $alias);
|
||||
}
|
||||
// 预载入封装
|
||||
$joinTable = $model->getTable();
|
||||
$joinName = Loader::parseName(basename(str_replace('\\', '/', $info['model'])));
|
||||
$joinAlias = isset($info['alias'][$joinName]) ? $info['alias'][$joinName] : $relation;
|
||||
$this->via($joinAlias);
|
||||
|
||||
if (Relation::HAS_ONE == $info['type']) {
|
||||
$this->join($joinTable . ' ' . $joinAlias, $alias . '.' . $info['localKey'] . '=' . $joinAlias . '.' . $info['foreignKey'], $info['joinType']);
|
||||
} else {
|
||||
$this->join($joinTable . ' ' . $joinAlias, $alias . '.' . $info['foreignKey'] . '=' . $joinAlias . '.' . $info['localKey'], $info['joinType']);
|
||||
}
|
||||
|
||||
if ($closure) {
|
||||
// 执行闭包查询
|
||||
call_user_func_array($closure, [ & $this]);
|
||||
//指定获取关联的字段
|
||||
//需要在 回调中 调方法 withField 方法,如
|
||||
// $query->where(['id'=>1])->withField('id,name');
|
||||
if (!empty($this->options['with_field'])) {
|
||||
$field = $this->options['with_field'];
|
||||
unset($this->options['with_field']);
|
||||
}
|
||||
} elseif (isset($info['option']['field'])) {
|
||||
$field = $info['option']['field'];
|
||||
}
|
||||
$this->field($field, false, $joinTable, $joinAlias, $relation . '__');
|
||||
$i++;
|
||||
if ($model instanceof HasOne || $model instanceof BelongsTo) {
|
||||
$model->eagerly($this, $relation, $subRelation, $closure, $first);
|
||||
$first = false;
|
||||
} elseif ($closure) {
|
||||
$with[$key] = $closure;
|
||||
}
|
||||
@@ -1771,7 +1766,7 @@ class Query
|
||||
// 分析查询表达式
|
||||
$options = $this->parseExpress();
|
||||
// 生成SQL语句
|
||||
$sql = $this->builder()->insert($data, $options, $replace);
|
||||
$sql = $this->builder->insert($data, $options, $replace);
|
||||
// 获取参数绑定
|
||||
$bind = $this->getBind();
|
||||
if ($options['fetch_sql']) {
|
||||
@@ -1781,6 +1776,9 @@ class Query
|
||||
|
||||
// 执行操作
|
||||
$result = $this->execute($sql, $bind);
|
||||
if ($result) {
|
||||
$this->trigger('after_insert', $options);
|
||||
}
|
||||
if ($getLastInsID) {
|
||||
$sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null);
|
||||
return $this->getLastInsID($sequence);
|
||||
@@ -1815,7 +1813,7 @@ class Query
|
||||
return false;
|
||||
}
|
||||
// 生成SQL语句
|
||||
$sql = $this->builder()->insertAll($dataSet, $options);
|
||||
$sql = $this->builder->insertAll($dataSet, $options);
|
||||
// 获取参数绑定
|
||||
$bind = $this->getBind();
|
||||
if ($options['fetch_sql']) {
|
||||
@@ -1841,7 +1839,7 @@ class Query
|
||||
$options = $this->parseExpress();
|
||||
// 生成SQL语句
|
||||
$table = $this->parseSqlTable($table);
|
||||
$sql = $this->builder()->selectInsert($fields, $table, $options);
|
||||
$sql = $this->builder->selectInsert($fields, $table, $options);
|
||||
// 获取参数绑定
|
||||
$bind = $this->getBind();
|
||||
if ($options['fetch_sql']) {
|
||||
@@ -1899,7 +1897,7 @@ class Query
|
||||
$key = 'think:' . $options['table'] . '|' . $options['where']['AND'][$pk];
|
||||
}
|
||||
// 生成UPDATE SQL语句
|
||||
$sql = $this->builder()->update($data, $options);
|
||||
$sql = $this->builder->update($data, $options);
|
||||
// 获取参数绑定
|
||||
$bind = $this->getBind();
|
||||
if ($options['fetch_sql']) {
|
||||
@@ -1912,7 +1910,11 @@ class Query
|
||||
Cache::rm($key);
|
||||
}
|
||||
// 执行操作
|
||||
return '' == $sql ? 0 : $this->execute($sql, $bind);
|
||||
$result = '' == $sql ? 0 : $this->execute($sql, $bind);
|
||||
if ($result) {
|
||||
$this->trigger('after_update', $options);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1954,19 +1956,22 @@ class Query
|
||||
}
|
||||
if (!$resultSet) {
|
||||
// 生成查询SQL
|
||||
$sql = $this->builder()->select($options);
|
||||
$sql = $this->builder->select($options);
|
||||
// 获取参数绑定
|
||||
$bind = $this->getBind();
|
||||
if ($options['fetch_sql']) {
|
||||
// 获取实际执行的SQL语句
|
||||
return $this->connection->getRealSql($sql, $bind);
|
||||
}
|
||||
// 执行查询操作
|
||||
$resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_class']);
|
||||
if ($resultSet = $this->trigger('before_select', $options)) {
|
||||
} else {
|
||||
// 执行查询操作
|
||||
$resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_class']);
|
||||
|
||||
if ($resultSet instanceof \PDOStatement) {
|
||||
// 返回PDOStatement对象
|
||||
return $resultSet;
|
||||
if ($resultSet instanceof \PDOStatement) {
|
||||
// 返回PDOStatement对象
|
||||
return $resultSet;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($cache)) {
|
||||
@@ -1979,12 +1984,11 @@ class Query
|
||||
}
|
||||
}
|
||||
|
||||
// 返回结果处理
|
||||
if (count($resultSet) > 0) {
|
||||
// 数据列表读取后的处理
|
||||
if (!empty($this->model)) {
|
||||
// 生成模型对象
|
||||
$model = $this->model;
|
||||
// 数据列表读取后的处理
|
||||
if (!empty($this->model)) {
|
||||
// 生成模型对象
|
||||
$model = $this->model;
|
||||
if (count($resultSet) > 0) {
|
||||
foreach ($resultSet as $key => $result) {
|
||||
/** @var Model $result */
|
||||
$result = new $model($result);
|
||||
@@ -1995,12 +1999,16 @@ class Query
|
||||
}
|
||||
$resultSet[$key] = $result;
|
||||
}
|
||||
if (!empty($options['with']) && $result instanceof Model) {
|
||||
if (!empty($options['with'])) {
|
||||
// 预载入
|
||||
$resultSet = $result->eagerlyResultSet($resultSet, $options['with'], is_object($resultSet) ? get_class($resultSet) : '');
|
||||
$result->eagerlyResultSet($resultSet, $options['with'], is_object($resultSet) ? get_class($resultSet) : '');
|
||||
}
|
||||
}
|
||||
} elseif (!empty($options['fail'])) {
|
||||
// 模型数据集转换
|
||||
$resultSet = (new $model)->toCollection($resultSet);
|
||||
}
|
||||
// 返回结果处理
|
||||
if (!empty($options['fail']) && count($resultSet) == 0) {
|
||||
$this->throwNotFound($options);
|
||||
}
|
||||
return $resultSet;
|
||||
@@ -2045,19 +2053,24 @@ class Query
|
||||
}
|
||||
if (!$result) {
|
||||
// 生成查询SQL
|
||||
$sql = $this->builder()->select($options);
|
||||
$sql = $this->builder->select($options);
|
||||
// 获取参数绑定
|
||||
$bind = $this->getBind();
|
||||
if ($options['fetch_sql']) {
|
||||
// 获取实际执行的SQL语句
|
||||
return $this->connection->getRealSql($sql, $bind);
|
||||
}
|
||||
// 执行查询
|
||||
$result = $this->query($sql, $bind, $options['master'], $options['fetch_class']);
|
||||
|
||||
if ($result instanceof \PDOStatement) {
|
||||
// 返回PDOStatement对象
|
||||
return $result;
|
||||
// 事件回调
|
||||
if ($result = $this->trigger('before_find', $options)) {
|
||||
} else {
|
||||
// 执行查询
|
||||
$result = $this->query($sql, $bind, $options['master'], $options['fetch_class']);
|
||||
|
||||
if ($result instanceof \PDOStatement) {
|
||||
// 返回PDOStatement对象
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($cache)) {
|
||||
@@ -2241,7 +2254,7 @@ class Query
|
||||
throw new Exception('delete without condition');
|
||||
}
|
||||
// 生成删除SQL语句
|
||||
$sql = $this->builder()->delete($options);
|
||||
$sql = $this->builder->delete($options);
|
||||
// 获取参数绑定
|
||||
$bind = $this->getBind();
|
||||
if ($options['fetch_sql']) {
|
||||
@@ -2255,7 +2268,11 @@ class Query
|
||||
Cache::rm($key);
|
||||
}
|
||||
// 执行操作
|
||||
return $this->execute($sql, $bind);
|
||||
$result = $this->execute($sql, $bind);
|
||||
if ($result) {
|
||||
$this->trigger('after_delete', $options);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2345,4 +2362,32 @@ class Query
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册回调方法
|
||||
* @access public
|
||||
* @param string $event 事件名
|
||||
* @param callable $callback 回调方法
|
||||
* @return void
|
||||
*/
|
||||
public static function event($event, $callback)
|
||||
{
|
||||
self::$event[$event] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
* @access protected
|
||||
* @param string $event 事件名
|
||||
* @param mixed $options 当前查询参数
|
||||
* @return bool
|
||||
*/
|
||||
protected function trigger($event, $options = [])
|
||||
{
|
||||
$result = false;
|
||||
if (isset(self::$event[$event])) {
|
||||
$callback = self::$event[$event];
|
||||
$result = call_user_func_array($callback, [$options, $this]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class File
|
||||
}
|
||||
|
||||
$depr = $depr ? "---------------------------------------------------------------\r\n" : '';
|
||||
|
||||
$info = '';
|
||||
if (App::$debug) {
|
||||
// 获取基本信息
|
||||
if (isset($_SERVER['HTTP_HOST'])) {
|
||||
|
||||
@@ -11,34 +11,18 @@
|
||||
|
||||
namespace think\model;
|
||||
|
||||
use think\Db;
|
||||
use think\db\Query;
|
||||
use think\Exception;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
use think\model\Pivot;
|
||||
|
||||
class Relation
|
||||
abstract class Relation
|
||||
{
|
||||
const HAS_ONE = 1;
|
||||
const HAS_MANY = 2;
|
||||
const BELONGS_TO = 3;
|
||||
const BELONGS_TO_MANY = 4;
|
||||
const HAS_MANY_THROUGH = 5;
|
||||
const MORPH_TO = 6;
|
||||
const MORPH_MANY = 7;
|
||||
|
||||
// 父模型对象
|
||||
protected $parent;
|
||||
/** @var Model 当前关联的模型类 */
|
||||
protected $model;
|
||||
// 中间表模型
|
||||
protected $middle;
|
||||
// 当前关联类型
|
||||
protected $type;
|
||||
// 关联表外键
|
||||
protected $foreignKey;
|
||||
// 中间关联表外键
|
||||
protected $throughKey;
|
||||
// 关联表主键
|
||||
protected $localKey;
|
||||
// 数据表别名
|
||||
@@ -51,291 +35,39 @@ class Relation
|
||||
protected $where;
|
||||
// 关联查询参数
|
||||
protected $option;
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $model 上级模型对象
|
||||
*/
|
||||
public function __construct(Model $model)
|
||||
{
|
||||
$this->parent = $model;
|
||||
}
|
||||
// 基础查询
|
||||
protected $baseQuery;
|
||||
|
||||
/**
|
||||
* 获取关联的所属模型
|
||||
* @access public
|
||||
* @return Model
|
||||
*/
|
||||
public function getModel()
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的关联模型类
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getModel()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关联的查询对象
|
||||
* @access public
|
||||
* @return Query
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析模型的完整命名空间
|
||||
* @access public
|
||||
* @param string $model 模型名(或者完整类名)
|
||||
* @return string
|
||||
*/
|
||||
protected function parseModel($model)
|
||||
{
|
||||
if (isset($this->alias[$model])) {
|
||||
$model = $this->alias[$model];
|
||||
}
|
||||
if (false === strpos($model, '\\')) {
|
||||
$path = explode('\\', get_class($this->parent));
|
||||
array_pop($path);
|
||||
array_push($path, Loader::parseName($model, 1));
|
||||
$model = implode('\\', $path);
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前关联信息
|
||||
* @access public
|
||||
* @param string $name 关联信息
|
||||
* @return array|string|integer
|
||||
*/
|
||||
public function getRelationInfo($name = '')
|
||||
{
|
||||
$info = [
|
||||
'type' => $this->type,
|
||||
'model' => $this->model,
|
||||
'middle' => $this->middle,
|
||||
'foreignKey' => $this->foreignKey,
|
||||
'localKey' => $this->localKey,
|
||||
'alias' => $this->alias,
|
||||
'joinType' => $this->joinType,
|
||||
'option' => $this->option,
|
||||
];
|
||||
return $name ? $info[$name] : $info;
|
||||
}
|
||||
|
||||
// 获取关联数据
|
||||
public function getRelation($name)
|
||||
{
|
||||
// 执行关联定义方法
|
||||
$relation = $this->parent->$name();
|
||||
$foreignKey = $this->foreignKey;
|
||||
$localKey = $this->localKey;
|
||||
$middle = $this->middle;
|
||||
|
||||
// 判断关联类型执行查询
|
||||
switch ($this->type) {
|
||||
case self::HAS_ONE:
|
||||
$result = $relation->where($foreignKey, $this->parent->$localKey)->find();
|
||||
break;
|
||||
case self::BELONGS_TO:
|
||||
$result = $relation->where($localKey, $this->parent->$foreignKey)->find();
|
||||
break;
|
||||
case self::HAS_MANY:
|
||||
$result = $relation->select();
|
||||
break;
|
||||
case self::HAS_MANY_THROUGH:
|
||||
$result = $relation->select();
|
||||
break;
|
||||
case self::BELONGS_TO_MANY:
|
||||
// 关联查询
|
||||
$pk = $this->parent->getPk();
|
||||
$condition['pivot.' . $localKey] = $this->parent->$pk;
|
||||
$result = $this->belongsToManyQuery($relation->getQuery(), $middle, $foreignKey, $localKey, $condition)->select();
|
||||
foreach ($result as $set) {
|
||||
$pivot = [];
|
||||
foreach ($set->getData() as $key => $val) {
|
||||
if (strpos($key, '__')) {
|
||||
list($name, $attr) = explode('__', $key, 2);
|
||||
if ('pivot' == $name) {
|
||||
$pivot[$attr] = $val;
|
||||
unset($set->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
$set->pivot = new Pivot($pivot, $this->middle);
|
||||
}
|
||||
break;
|
||||
case self::MORPH_MANY:
|
||||
$result = $relation->select();
|
||||
break;
|
||||
case self::MORPH_TO:
|
||||
// 多态模型
|
||||
$model = $this->parseModel($this->parent->$middle);
|
||||
// 主键数据
|
||||
$pk = $this->parent->$foreignKey;
|
||||
$result = (new $model)->find($pk);
|
||||
break;
|
||||
default:
|
||||
// 直接返回
|
||||
$result = $relation;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询 返回数据集
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 关联名
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return array
|
||||
*/
|
||||
public function eagerlyResultSet($resultSet, $relation, $class = '')
|
||||
{
|
||||
/** @var array $relations */
|
||||
$relations = is_string($relation) ? explode(',', $relation) : $relation;
|
||||
|
||||
foreach ($relations as $key => $relation) {
|
||||
$subRelation = '';
|
||||
$closure = false;
|
||||
if ($relation instanceof \Closure) {
|
||||
$closure = $relation;
|
||||
$relation = $key;
|
||||
}
|
||||
if (strpos($relation, '.')) {
|
||||
list($relation, $subRelation) = explode('.', $relation);
|
||||
}
|
||||
// 执行关联方法
|
||||
$model = $this->parent->$relation();
|
||||
// 获取关联信息
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
$middle = $this->middle;
|
||||
switch ($this->type) {
|
||||
case self::HAS_ONE:
|
||||
case self::BELONGS_TO:
|
||||
foreach ($resultSet as $result) {
|
||||
// 模型关联组装
|
||||
$this->match($this->model, $relation, $result);
|
||||
}
|
||||
break;
|
||||
case self::HAS_MANY:
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$localKey)) {
|
||||
$range[] = $result->$localKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$this->where[$foreignKey] = ['in', $range];
|
||||
$data = $this->eagerlyOneToMany($model, [
|
||||
$foreignKey => [
|
||||
'in',
|
||||
$range,
|
||||
],
|
||||
], $relation, $subRelation, $closure);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
$data[$result->$localKey] = [];
|
||||
}
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$result->$localKey], $class));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case self::BELONGS_TO_MANY:
|
||||
$pk = $resultSet[0]->getPk();
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$pk)) {
|
||||
$range[] = $result->$pk;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
// 查询关联数据
|
||||
$data = $this->eagerlyManyToMany($model, [
|
||||
'pivot.' . $localKey => [
|
||||
'in',
|
||||
$range,
|
||||
],
|
||||
], $relation, $subRelation);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$pk])) {
|
||||
$data[$result->$pk] = [];
|
||||
}
|
||||
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$result->$pk], $class));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case self::MORPH_MANY:
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
$pk = $result->getPk();
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$pk)) {
|
||||
$range[] = $result->$pk;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$this->where[$foreignKey] = ['in', $range];
|
||||
$this->where[$localKey] = $middle;
|
||||
$data = $this->eagerlyMorphToMany($model, [
|
||||
$foreignKey => ['in', $range],
|
||||
$localKey => $middle,
|
||||
], $relation, $subRelation, $closure);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$pk])) {
|
||||
$data[$result->$pk] = [];
|
||||
}
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$result->$pk], $class));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case self::MORPH_TO:
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (!empty($result->$foreignKey)) {
|
||||
$range[$result->$middle][] = $result->$foreignKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
foreach ($range as $key => $val) {
|
||||
// 多态类型映射
|
||||
$model = $this->parseModel($key);
|
||||
$obj = new $model;
|
||||
$pk = $obj->getPk();
|
||||
$list = $obj->all($val, $subRelation);
|
||||
$data = [];
|
||||
foreach ($list as $k => $vo) {
|
||||
$data[$vo->$pk] = $vo;
|
||||
}
|
||||
foreach ($resultSet as $result) {
|
||||
if ($key == $result->$middle) {
|
||||
if (!isset($data[$result->$foreignKey])) {
|
||||
$data[$result->$foreignKey] = [];
|
||||
}
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$result->$foreignKey], $class));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $resultSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装关联数据集
|
||||
* @access public
|
||||
@@ -348,212 +80,6 @@ class Relation
|
||||
return $class ? new $class($resultSet) : $resultSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询 返回模型对象
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 关联名
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return Model
|
||||
*/
|
||||
public function eagerlyResult($result, $relation, $class = '')
|
||||
{
|
||||
$relations = is_string($relation) ? explode(',', $relation) : $relation;
|
||||
|
||||
foreach ($relations as $key => $relation) {
|
||||
$subRelation = '';
|
||||
$closure = false;
|
||||
if ($relation instanceof \Closure) {
|
||||
$closure = $relation;
|
||||
$relation = $key;
|
||||
}
|
||||
if (strpos($relation, '.')) {
|
||||
list($relation, $subRelation) = explode('.', $relation);
|
||||
}
|
||||
// 执行关联方法
|
||||
$model = $this->parent->$relation();
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
$middle = $this->middle;
|
||||
switch ($this->type) {
|
||||
case self::HAS_ONE:
|
||||
case self::BELONGS_TO:
|
||||
// 模型关联组装
|
||||
$this->match($this->model, $relation, $result);
|
||||
break;
|
||||
case self::HAS_MANY:
|
||||
if (isset($result->$localKey)) {
|
||||
$data = $this->eagerlyOneToMany($model, [$foreignKey => $result->$localKey], $relation, $subRelation, $closure);
|
||||
// 关联数据封装
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
$data[$result->$localKey] = [];
|
||||
}
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$result->$localKey], $class));
|
||||
}
|
||||
break;
|
||||
case self::BELONGS_TO_MANY:
|
||||
$pk = $result->getPk();
|
||||
if (isset($result->$pk)) {
|
||||
$pk = $result->$pk;
|
||||
// 查询管理数据
|
||||
$data = $this->eagerlyManyToMany($model, ['pivot.' . $localKey => $pk], $relation, $subRelation);
|
||||
|
||||
// 关联数据封装
|
||||
if (!isset($data[$pk])) {
|
||||
$data[$pk] = [];
|
||||
}
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$pk], $class));
|
||||
}
|
||||
break;
|
||||
case self::MORPH_MANY:
|
||||
$pk = $result->getPk();
|
||||
if (isset($result->$pk)) {
|
||||
$data = $this->eagerlyMorphToMany($model, [$foreignKey => $result->$pk, $localKey => $middle], $relation, $subRelation, $closure);
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$result->$pk], $class));
|
||||
}
|
||||
break;
|
||||
case self::MORPH_TO:
|
||||
// 多态类型映射
|
||||
$model = $this->parseModel($result->{$this->middle});
|
||||
$this->eagerlyMorphToOne($model, $relation, $result, $subRelation);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 一对一 关联模型预查询拼装
|
||||
* @access public
|
||||
* @param string $model 模型名称
|
||||
* @param string $relation 关联名
|
||||
* @param Model $result 模型对象实例
|
||||
* @return void
|
||||
*/
|
||||
protected function match($model, $relation, &$result)
|
||||
{
|
||||
// 重新组装模型数据
|
||||
foreach ($result->getData() as $key => $val) {
|
||||
if (strpos($key, '__')) {
|
||||
list($name, $attr) = explode('__', $key, 2);
|
||||
if ($name == $relation) {
|
||||
$list[$name][$attr] = $val;
|
||||
unset($result->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result->setAttr($relation, !isset($list[$relation]) ? null : (new $model($list[$relation]))->isUpdate(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 一对多 关联模型预查询
|
||||
* @access public
|
||||
* @param object $model 关联模型对象
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @param bool $closure
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyOneToMany($model, $where, $relation, $subRelation = '', $closure = false)
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $model]);
|
||||
}
|
||||
$list = $model->where($where)->with($subRelation)->select();
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
foreach ($list as $set) {
|
||||
$data[$set->$foreignKey][] = $set;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 多对多 关联模型预查询
|
||||
* @access public
|
||||
* @param object $model 关联模型对象
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyManyToMany($model, $where, $relation, $subRelation = '')
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
$localKey = $this->localKey;
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
$list = $this->belongsToManyQuery($model->getQuery(), $this->middle, $foreignKey, $localKey, $where)->with($subRelation)->select();
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
foreach ($list as $set) {
|
||||
$pivot = [];
|
||||
foreach ($set->getData() as $key => $val) {
|
||||
if (strpos($key, '__')) {
|
||||
list($name, $attr) = explode('__', $key, 2);
|
||||
if ('pivot' == $name) {
|
||||
$pivot[$attr] = $val;
|
||||
unset($set->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
$set->pivot = new Pivot($pivot, $this->middle);
|
||||
$data[$pivot[$localKey]][] = $set;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 多态MorphTo 关联模型预查询
|
||||
* @access public
|
||||
* @param object $model 关联模型对象
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyMorphToOne($model, $relation, &$result, $subRelation = '')
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
$pk = $this->parent->{$this->foreignKey};
|
||||
$data = (new $model)->with($subRelation)->find($pk);
|
||||
if ($data) {
|
||||
$data->isUpdate(true);
|
||||
}
|
||||
$result->setAttr($relation, $data ?: null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多态一对多 关联模型预查询
|
||||
* @access public
|
||||
* @param object $model 关联模型对象
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyMorphToMany($model, $where, $relation, $subRelation = '', $closure = false)
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $model]);
|
||||
}
|
||||
$list = $model->getQuery()->where($where)->with($subRelation)->select();
|
||||
$foreignKey = $this->foreignKey;
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
foreach ($list as $set) {
|
||||
$data[$set->$foreignKey][] = $set;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前关联定义的数据表别名
|
||||
* @access public
|
||||
@@ -566,363 +92,23 @@ class Relation
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* HAS ONE 关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* @param array $alias 别名定义
|
||||
* @param string $joinType JOIN类型
|
||||
* @return $this
|
||||
*/
|
||||
public function hasOne($model, $foreignKey, $localKey, $alias = [], $joinType = 'INNER')
|
||||
{
|
||||
$this->type = self::HAS_ONE;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->alias = $alias;
|
||||
$this->joinType = $joinType;
|
||||
$this->query = (new $model)->db();
|
||||
// 返回关联的模型对象
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* BELONGS TO 关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $otherKey 关联主键
|
||||
* @param array $alias 别名定义
|
||||
* @param string $joinType JOIN类型
|
||||
* @return $this
|
||||
*/
|
||||
public function belongsTo($model, $foreignKey, $otherKey, $alias = [], $joinType = 'INNER')
|
||||
{
|
||||
// 记录当前关联信息
|
||||
$this->type = self::BELONGS_TO;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $otherKey;
|
||||
$this->alias = $alias;
|
||||
$this->joinType = $joinType;
|
||||
$this->query = (new $model)->db();
|
||||
// 返回关联的模型对象
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* HAS MANY 关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* @param array $alias 别名定义
|
||||
* @return $this
|
||||
*/
|
||||
public function hasMany($model, $foreignKey, $localKey, $alias)
|
||||
{
|
||||
// 记录当前关联信息
|
||||
$this->type = self::HAS_MANY;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->alias = $alias;
|
||||
$this->query = (new $model)->db();
|
||||
// 返回关联的模型对象
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* HAS MANY 远程关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string $through 中间模型名
|
||||
* @param string $firstkey 关联外键
|
||||
* @param string $secondKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* @param array $alias 别名定义
|
||||
* @return $this
|
||||
*/
|
||||
public function hasManyThrough($model, $through, $foreignKey, $throughKey, $localKey, $alias)
|
||||
{
|
||||
// 记录当前关联信息
|
||||
$this->type = self::HAS_MANY_THROUGH;
|
||||
$this->model = $model;
|
||||
$this->middle = $through;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->throughKey = $throughKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->alias = $alias;
|
||||
$this->query = (new $model)->db();
|
||||
// 返回关联的模型对象
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* BELONGS TO MANY 关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string $table 中间表名
|
||||
* @param string $foreignKey 关联模型外键
|
||||
* @param string $localKey 当前模型关联键
|
||||
* @param array $alias 别名定义
|
||||
* @return $this
|
||||
*/
|
||||
public function belongsToMany($model, $table, $foreignKey, $localKey, $alias)
|
||||
{
|
||||
// 记录当前关联信息
|
||||
$this->type = self::BELONGS_TO_MANY;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->middle = $table;
|
||||
$this->alias = $alias;
|
||||
$this->query = (new $model)->db();
|
||||
// 返回关联的模型对象
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* MORPH_MANY 关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string $id 关联外键
|
||||
* @param string $morphType 多态字段名
|
||||
* @param string $type 多态类型
|
||||
* @return $this
|
||||
*/
|
||||
public function morphMany($model, $foreignKey, $morphType, $type)
|
||||
{
|
||||
// 记录当前关联信息
|
||||
$this->type = self::MORPH_MANY;
|
||||
$this->model = $model;
|
||||
$this->middle = $type;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $morphType;
|
||||
$this->query = (new $model)->db();
|
||||
// 返回关联的模型对象
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* MORPH_TO 关联定义
|
||||
* @access public
|
||||
* @param string $morphType 多态字段名
|
||||
* @param string $foreignKey 外键名
|
||||
* @param array $alias 多态别名定义
|
||||
* @return $this
|
||||
*/
|
||||
public function morphTo($morphType, $foreignKey, $alias)
|
||||
{
|
||||
// 记录当前关联信息
|
||||
$this->type = self::MORPH_TO;
|
||||
$this->middle = $morphType;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->alias = $alias;
|
||||
// 返回关联的模型对象
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* BELONGS TO MANY 关联查询
|
||||
* @access public
|
||||
* @param object $model 关联模型对象
|
||||
* @param string $table 中间表名
|
||||
* @param string $foreignKey 关联模型关联键
|
||||
* @param string $localKey 当前模型关联键
|
||||
* @param array $condition 关联查询条件
|
||||
* @return \think\db\Query|string
|
||||
*/
|
||||
protected function belongsToManyQuery($model, $table, $foreignKey, $localKey, $condition = [])
|
||||
{
|
||||
// 关联查询封装
|
||||
$tableName = $model->getTable();
|
||||
$relationFk = $model->getPk();
|
||||
return $model->field($tableName . '.*')
|
||||
->field(true, false, $table, 'pivot', 'pivot__')
|
||||
->join($table . ' pivot', 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk)
|
||||
->where($condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @return integer
|
||||
*/
|
||||
public function save($data, array $pivot = [])
|
||||
{
|
||||
// 判断关联类型
|
||||
switch ($this->type) {
|
||||
case self::HAS_ONE:
|
||||
case self::BELONGS_TO:
|
||||
case self::HAS_MANY:
|
||||
if ($data instanceof Model) {
|
||||
$data = $data->getData();
|
||||
}
|
||||
// 保存关联表数据
|
||||
$data[$this->foreignKey] = $this->parent->{$this->localKey};
|
||||
$model = new $this->model;
|
||||
return $model->save($data);
|
||||
case self::BELONGS_TO_MANY:
|
||||
// 保存关联表/中间表数据
|
||||
return $this->attach($data, $pivot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存当前关联数据对象
|
||||
* @access public
|
||||
* @param array $dataSet 数据集
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @return integer
|
||||
*/
|
||||
public function saveAll(array $dataSet, array $pivot = [])
|
||||
{
|
||||
$result = false;
|
||||
foreach ($dataSet as $key => $data) {
|
||||
// 判断关联类型
|
||||
switch ($this->type) {
|
||||
case self::HAS_MANY:
|
||||
$data[$this->foreignKey] = $this->parent->{$this->localKey};
|
||||
$result = $this->save($data);
|
||||
break;
|
||||
case self::BELONGS_TO_MANY:
|
||||
// TODO
|
||||
$result = $this->attach($data, !empty($pivot) ? $pivot[$key] : []);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 附加关联的一个中间表数据
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @return integer
|
||||
*/
|
||||
public function attach($data, $pivot = [])
|
||||
{
|
||||
if (is_array($data)) {
|
||||
// 保存关联表数据
|
||||
$model = new $this->model;
|
||||
$model->save($data);
|
||||
$id = $model->getLastInsID();
|
||||
} elseif (is_numeric($data) || is_string($data)) {
|
||||
// 根据关联表主键直接写入中间表
|
||||
$id = $data;
|
||||
} elseif ($data instanceof Model) {
|
||||
// 根据关联表主键直接写入中间表
|
||||
$relationFk = $data->getPk();
|
||||
$id = $data->$relationFk;
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
// 保存中间表数据
|
||||
$pk = $this->parent->getPk();
|
||||
$pivot[$this->localKey] = $this->parent->$pk;
|
||||
$pivot[$this->foreignKey] = $id;
|
||||
$query = clone $this->parent->db();
|
||||
return $query->table($this->middle)->insert($pivot);
|
||||
} else {
|
||||
throw new Exception('miss relation data');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除关联的一个中间表数据
|
||||
* @access public
|
||||
* @param integer|array $data 数据 可以使用关联对象的主键
|
||||
* @param bool $relationDel 是否同时删除关联表数据
|
||||
* @return integer
|
||||
*/
|
||||
public function detach($data, $relationDel = false)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
$id = $data;
|
||||
} elseif (is_numeric($data) || is_string($data)) {
|
||||
// 根据关联表主键直接写入中间表
|
||||
$id = $data;
|
||||
} elseif ($data instanceof Model) {
|
||||
// 根据关联表主键直接写入中间表
|
||||
$relationFk = $data->getPk();
|
||||
$id = $data->$relationFk;
|
||||
}
|
||||
// 删除中间表数据
|
||||
$pk = $this->parent->getPk();
|
||||
$pivot[$this->localKey] = $this->parent->$pk;
|
||||
if (isset($id)) {
|
||||
$pivot[$this->foreignKey] = is_array($id) ? ['in', $id] : $id;
|
||||
}
|
||||
$query = clone $this->parent->db();
|
||||
$query->table($this->middle)->where($pivot)->delete();
|
||||
|
||||
// 删除关联表数据
|
||||
if (isset($id) && $relationDel) {
|
||||
$model = $this->model;
|
||||
$model::destroy($id);
|
||||
}
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
static $baseQuery = [];
|
||||
if ($this->query) {
|
||||
if (empty($baseQuery[$this->type])) {
|
||||
$baseQuery[$this->type] = true;
|
||||
switch ($this->type) {
|
||||
case self::HAS_MANY:
|
||||
if (isset($this->where)) {
|
||||
$this->query->where($this->where);
|
||||
} elseif (isset($this->parent->{$this->localKey})) {
|
||||
// 关联查询带入关联条件
|
||||
$this->query->where($this->foreignKey, $this->parent->{$this->localKey});
|
||||
}
|
||||
break;
|
||||
case self::HAS_MANY_THROUGH:
|
||||
$through = $this->middle;
|
||||
$model = $this->model;
|
||||
$alias = Loader::parseName(basename(str_replace('\\', '/', $model)));
|
||||
$throughTable = $through::getTable();
|
||||
$pk = (new $this->model)->getPk();
|
||||
$throughKey = $this->throughKey;
|
||||
$modelTable = $this->parent->getTable();
|
||||
$this->query->field($alias . '.*')->alias($alias)
|
||||
->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
|
||||
->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
|
||||
->where($throughTable . '.' . $this->foreignKey, $this->parent->{$this->localKey});
|
||||
break;
|
||||
case self::BELONGS_TO_MANY:
|
||||
$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);
|
||||
break;
|
||||
case self::MORPH_MANY:
|
||||
$pk = $this->parent->getPk();
|
||||
$map[$this->foreignKey] = $this->parent->$pk;
|
||||
$map[$this->localKey] = $this->middle;
|
||||
$this->query->where($map);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 执行基础查询
|
||||
$this->baseQuery();
|
||||
|
||||
$result = call_user_func_array([$this->query, $method], $args);
|
||||
if ($result instanceof \think\db\Query) {
|
||||
if ($result instanceof Query) {
|
||||
$this->option = $result->getOptions();
|
||||
return $this;
|
||||
} else {
|
||||
$this->option = [];
|
||||
$baseQuery = false;
|
||||
$this->option = [];
|
||||
$this->baseQuery = false;
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
51
core/library/think/model/relation/BelongsTo.php
Normal file
51
core/library/think/model/relation/BelongsTo.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2016 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\Model;
|
||||
use think\model\relation\OneToOne;
|
||||
|
||||
class BelongsTo extends OneToOne
|
||||
{
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $otherKey 关联主键
|
||||
* @param array $alias 别名定义
|
||||
* @param string $joinType JOIN类型
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $foreignKey, $localKey, $alias = [], $joinType = 'INNER')
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->alias = $alias;
|
||||
$this->joinType = $joinType;
|
||||
$this->query = (new $model)->db();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @access public
|
||||
*/
|
||||
public function getRelation()
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
$localKey = $this->localKey;
|
||||
return $this->query->where($localKey, $this->parent->$foreignKey)->find();
|
||||
}
|
||||
|
||||
}
|
||||
313
core/library/think/model/relation/BelongsToMany.php
Normal file
313
core/library/think/model/relation/BelongsToMany.php
Normal file
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2016 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;
|
||||
use think\db\Query;
|
||||
use think\Model;
|
||||
use think\model\Pivot;
|
||||
use think\model\Relation;
|
||||
|
||||
class BelongsToMany extends Relation
|
||||
{
|
||||
// 中间表模型
|
||||
protected $middle;
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $table 中间表名
|
||||
* @param string $foreignKey 关联模型外键
|
||||
* @param string $localKey 当前模型关联键
|
||||
* @param array $alias 别名定义
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $table, $foreignKey, $localKey, $alias = [])
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->middle = $table;
|
||||
$this->alias = $alias;
|
||||
$this->query = (new $model)->db();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @access public
|
||||
*/
|
||||
public function getRelation()
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
$localKey = $this->localKey;
|
||||
$middle = $this->middle;
|
||||
// 关联查询
|
||||
$pk = $this->parent->getPk();
|
||||
$condition['pivot.' . $localKey] = $this->parent->$pk;
|
||||
$result = $this->belongsToManyQuery($middle, $foreignKey, $localKey, $condition)->select();
|
||||
foreach ($result as $set) {
|
||||
$pivot = [];
|
||||
foreach ($set->getData() as $key => $val) {
|
||||
if (strpos($key, '__')) {
|
||||
list($name, $attr) = explode('__', $key, 2);
|
||||
if ('pivot' == $name) {
|
||||
$pivot[$attr] = $val;
|
||||
unset($set->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
$set->pivot = new Pivot($pivot, $this->middle);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$pk = $resultSet[0]->getPk();
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$pk)) {
|
||||
$range[] = $result->$pk;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
// 查询关联数据
|
||||
$data = $this->eagerlyManyToMany([
|
||||
'pivot.' . $localKey => [
|
||||
'in',
|
||||
$range,
|
||||
],
|
||||
], $relation, $subRelation);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$pk])) {
|
||||
$data[$result->$pk] = [];
|
||||
}
|
||||
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$result->$pk], $class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(单个数据)
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$pk = $result->getPk();
|
||||
if (isset($result->$pk)) {
|
||||
$pk = $result->$pk;
|
||||
// 查询管理数据
|
||||
$data = $this->eagerlyManyToMany(['pivot.' . $localKey => $pk], $relation, $subRelation);
|
||||
|
||||
// 关联数据封装
|
||||
if (!isset($data[$pk])) {
|
||||
$data[$pk] = [];
|
||||
}
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$pk], $class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多对多 关联模型预查询
|
||||
* @access public
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyManyToMany($where, $relation, $subRelation = '')
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
$localKey = $this->localKey;
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
$list = $this->belongsToManyQuery($this->middle, $foreignKey, $localKey, $where)->with($subRelation)->select();
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
foreach ($list as $set) {
|
||||
$pivot = [];
|
||||
foreach ($set->getData() as $key => $val) {
|
||||
if (strpos($key, '__')) {
|
||||
list($name, $attr) = explode('__', $key, 2);
|
||||
if ('pivot' == $name) {
|
||||
$pivot[$attr] = $val;
|
||||
unset($set->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
$set->pivot = new Pivot($pivot, $this->middle);
|
||||
$data[$pivot[$localKey]][] = $set;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = [])
|
||||
{
|
||||
// 关联查询封装
|
||||
$tableName = $this->query->getTable();
|
||||
$relationFk = $this->query->getPk();
|
||||
return $this->query->field($tableName . '.*')
|
||||
->field(true, false, $table, 'pivot', 'pivot__')
|
||||
->join($table . ' pivot', 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk)
|
||||
->where($condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @return integer
|
||||
*/
|
||||
public function save($data, array $pivot = [])
|
||||
{
|
||||
// 保存关联表/中间表数据
|
||||
return $this->attach($data, $pivot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存当前关联数据对象
|
||||
* @access public
|
||||
* @param array $dataSet 数据集
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @return integer
|
||||
*/
|
||||
public function saveAll(array $dataSet, array $pivot = [])
|
||||
{
|
||||
$result = false;
|
||||
foreach ($dataSet as $key => $data) {
|
||||
$result = $this->attach($data, !empty($pivot) ? $pivot[$key] : []);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 附加关联的一个中间表数据
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @return integer
|
||||
*/
|
||||
public function attach($data, $pivot = [])
|
||||
{
|
||||
if (is_array($data)) {
|
||||
// 保存关联表数据
|
||||
$model = new $this->model;
|
||||
$model->save($data);
|
||||
$id = $model->getLastInsID();
|
||||
} elseif (is_numeric($data) || is_string($data)) {
|
||||
// 根据关联表主键直接写入中间表
|
||||
$id = $data;
|
||||
} elseif ($data instanceof Model) {
|
||||
// 根据关联表主键直接写入中间表
|
||||
$relationFk = $data->getPk();
|
||||
$id = $data->$relationFk;
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
// 保存中间表数据
|
||||
$pk = $this->parent->getPk();
|
||||
$pivot[$this->localKey] = $this->parent->$pk;
|
||||
$pivot[$this->foreignKey] = $id;
|
||||
return $this->query->table($this->middle)->insert($pivot);
|
||||
} else {
|
||||
throw new Exception('miss relation data');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除关联的一个中间表数据
|
||||
* @access public
|
||||
* @param integer|array $data 数据 可以使用关联对象的主键
|
||||
* @param bool $relationDel 是否同时删除关联表数据
|
||||
* @return integer
|
||||
*/
|
||||
public function detach($data, $relationDel = false)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
$id = $data;
|
||||
} elseif (is_numeric($data) || is_string($data)) {
|
||||
// 根据关联表主键直接写入中间表
|
||||
$id = $data;
|
||||
} elseif ($data instanceof Model) {
|
||||
// 根据关联表主键直接写入中间表
|
||||
$relationFk = $data->getPk();
|
||||
$id = $data->$relationFk;
|
||||
}
|
||||
// 删除中间表数据
|
||||
$pk = $this->parent->getPk();
|
||||
$pivot[$this->localKey] = $this->parent->$pk;
|
||||
if (isset($id)) {
|
||||
$pivot[$this->foreignKey] = is_array($id) ? ['in', $id] : $id;
|
||||
}
|
||||
$this->query->table($this->middle)->where($pivot)->delete();
|
||||
|
||||
// 删除关联表数据
|
||||
if (isset($id) && $relationDel) {
|
||||
$model = $this->model;
|
||||
$model::destroy($id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
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);
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
235
core/library/think/model/relation/HasMany.php
Normal file
235
core/library/think/model/relation/HasMany.php
Normal file
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2016 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;
|
||||
use think\db\Query;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
class HasMany extends Relation
|
||||
{
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* @param array $alias 别名定义
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $foreignKey, $localKey, $alias = [])
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->alias = $alias;
|
||||
$this->query = (new $model)->db();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @access public
|
||||
*/
|
||||
public function getRelation()
|
||||
{
|
||||
return $this->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$localKey)) {
|
||||
$range[] = $result->$localKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$this->where[$foreignKey] = ['in', $range];
|
||||
$data = $this->eagerlyOneToMany($this, [
|
||||
$foreignKey => [
|
||||
'in',
|
||||
$range,
|
||||
],
|
||||
], $relation, $subRelation, $closure);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
$data[$result->$localKey] = [];
|
||||
}
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$result->$localKey], $class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
if (isset($result->$localKey)) {
|
||||
$data = $this->eagerlyOneToMany($this, [$foreignKey => $result->$localKey], $relation, $subRelation, $closure);
|
||||
// 关联数据封装
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
$data[$result->$localKey] = [];
|
||||
}
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$result->$localKey], $class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 一对多 关联模型预查询
|
||||
* @access public
|
||||
* @param object $model 关联模型对象
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @param bool $closure
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyOneToMany($model, $where, $relation, $subRelation = '', $closure = false)
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $model]);
|
||||
}
|
||||
$list = $model->where($where)->with($subRelation)->select();
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
foreach ($list as $set) {
|
||||
$data[$set->$foreignKey][] = $set;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @return integer
|
||||
*/
|
||||
public function save($data)
|
||||
{
|
||||
if ($data instanceof Model) {
|
||||
$data = $data->getData();
|
||||
}
|
||||
// 保存关联表数据
|
||||
$data[$this->foreignKey] = $this->parent->{$this->localKey};
|
||||
$model = new $this->model;
|
||||
return $model->save($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存当前关联数据对象
|
||||
* @access public
|
||||
* @param array $dataSet 数据集
|
||||
* @return integer
|
||||
*/
|
||||
public function saveAll(array $dataSet)
|
||||
{
|
||||
$result = false;
|
||||
foreach ($dataSet as $key => $data) {
|
||||
$result = $this->save($data);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param Model $model 模型对象
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @return Query
|
||||
*/
|
||||
public function has($model, $operator = '>=', $count = 1, $id = '*')
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
return $model->db()->alias('a')
|
||||
->join($table . ' b', 'a.' . $this->localKey . '=b.' . $this->foreignKey, $this->joinType)
|
||||
->group('b.' . $this->foreignKey)
|
||||
->having('count(' . $id . ')' . $operator . $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param Model $model 模型对象
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($model, $where = [])
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
if (is_array($where)) {
|
||||
foreach ($where as $key => $val) {
|
||||
if (false === strpos($key, '.')) {
|
||||
$where['b.' . $key] = $val;
|
||||
unset($where[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $model->db()->alias('a')
|
||||
->field('a.*')
|
||||
->join($table . ' b', 'a.' . $this->localKey . '=b.' . $this->foreignKey, $this->joinType)
|
||||
->where($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{
|
||||
if (empty($this->baseQuery)) {
|
||||
if (isset($this->where)) {
|
||||
$this->query->where($this->where);
|
||||
} elseif (isset($this->parent->{$this->localKey})) {
|
||||
// 关联查询带入关联条件
|
||||
$this->query->where($this->foreignKey, $this->parent->{$this->localKey});
|
||||
}
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
108
core/library/think/model/relation/HasManyThrough.php
Normal file
108
core/library/think/model/relation/HasManyThrough.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2016 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;
|
||||
use think\db\Query;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
class HasManyThrough extends Relation
|
||||
{
|
||||
// 中间关联表外键
|
||||
protected $throughKey;
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $through 中间模型名
|
||||
* @param string $firstkey 关联外键
|
||||
* @param string $secondKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* @param array $alias 别名定义
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $through, $foreignKey, $throughKey, $localKey, $alias = [])
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->middle = $through;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->throughKey = $throughKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->alias = $alias;
|
||||
$this->query = (new $model)->db();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @access public
|
||||
*/
|
||||
public function getRelation()
|
||||
{
|
||||
return $this->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询 返回模型对象
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{
|
||||
if (empty($this->baseQuery)) {
|
||||
$through = $this->middle;
|
||||
$model = $this->model;
|
||||
$alias = Loader::parseName(basename(str_replace('\\', '/', $model)));
|
||||
$throughTable = $through::getTable();
|
||||
$pk = (new $this->model)->getPk();
|
||||
$throughKey = $this->throughKey;
|
||||
$modelTable = $this->parent->getTable();
|
||||
$this->query->field($alias . '.*')->alias($alias)
|
||||
->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
|
||||
->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
|
||||
->where($throughTable . '.' . $this->foreignKey, $this->parent->{$this->localKey});
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
76
core/library/think/model/relation/HasOne.php
Normal file
76
core/library/think/model/relation/HasOne.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2016 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\Model;
|
||||
use think\model\relation\OneToOne;
|
||||
|
||||
class HasOne extends OneToOne
|
||||
{
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* @param array $alias 别名定义
|
||||
* @param string $joinType JOIN类型
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $foreignKey, $localKey, $alias = [], $joinType = 'INNER')
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->alias = $alias;
|
||||
$this->joinType = $joinType;
|
||||
$this->query = (new $model)->db();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @access public
|
||||
*/
|
||||
public function getRelation()
|
||||
{
|
||||
// 执行关联定义方法
|
||||
$localKey = $this->localKey;
|
||||
|
||||
// 判断关联类型执行查询
|
||||
return $this->query->where($this->foreignKey, $this->parent->$localKey)->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param Model $model 模型对象
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($model, $where = [])
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
if (is_array($where)) {
|
||||
foreach ($where as $key => $val) {
|
||||
if (false === strpos($key, '.')) {
|
||||
$where['b.' . $key] = $val;
|
||||
unset($where[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $model->db()->alias('a')
|
||||
->field('a.*')
|
||||
->join($table . ' b', 'a.' . $this->localKey . '=b.' . $this->foreignKey, $this->joinType)
|
||||
->where($where);
|
||||
}
|
||||
}
|
||||
195
core/library/think/model/relation/MorphMany.php
Normal file
195
core/library/think/model/relation/MorphMany.php
Normal file
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2016 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;
|
||||
use think\db\Query;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
class MorphMany 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @access public
|
||||
*/
|
||||
public function getRelation()
|
||||
{
|
||||
return $this->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class)
|
||||
{
|
||||
$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)) {
|
||||
$this->where[$morphKey] = ['in', $range];
|
||||
$this->where[$morphType] = $type;
|
||||
$data = $this->eagerlyMorphToMany([
|
||||
$morphKey => ['in', $range],
|
||||
$morphType => $type,
|
||||
], $relation, $subRelation, $closure);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$pk])) {
|
||||
$data[$result->$pk] = [];
|
||||
}
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$result->$pk], $class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class)
|
||||
{
|
||||
$morphType = $this->morphType;
|
||||
$morphKey = $this->morphKey;
|
||||
$type = $this->type;
|
||||
$pk = $result->getPk();
|
||||
if (isset($result->$pk)) {
|
||||
$data = $this->eagerlyMorphToMany([$morphKey => $result->$pk, $morphType => $type], $relation, $subRelation, $closure);
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$result->$pk], $class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多态一对多 关联模型预查询
|
||||
* @access public
|
||||
* @param object $model 关联模型对象
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyMorphToMany($where, $relation, $subRelation = '', $closure = false)
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this]);
|
||||
}
|
||||
$list = $this->query->where($where)->with($subRelation)->select();
|
||||
$morphKey = $this->morphKey;
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
foreach ($list as $set) {
|
||||
$data[$set->$morphKey][] = $set;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @return integer
|
||||
*/
|
||||
public function save($data)
|
||||
{
|
||||
if ($data instanceof Model) {
|
||||
$data = $data->getData();
|
||||
}
|
||||
// 保存关联表数据
|
||||
$pk = $this->parent->getPk();
|
||||
|
||||
$data[$this->morphKey] = $this->parent->$pk;
|
||||
$data[$this->morphType] = $this->type;
|
||||
$model = new $this->model;
|
||||
return $model->save($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存当前关联数据对象
|
||||
* @access public
|
||||
* @param array $dataSet 数据集
|
||||
* @return integer
|
||||
*/
|
||||
public function saveAll(array $dataSet)
|
||||
{
|
||||
$result = false;
|
||||
foreach ($dataSet as $key => $data) {
|
||||
$result = $this->save($data);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{
|
||||
if (empty($this->baseQuery)) {
|
||||
$pk = $this->parent->getPk();
|
||||
$map[$this->morphKey] = $this->parent->$pk;
|
||||
$map[$this->morphType] = $this->type;
|
||||
$this->query->where($map);
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
167
core/library/think/model/relation/MorphTo.php
Normal file
167
core/library/think/model/relation/MorphTo.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2016 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\Loader;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
class MorphTo extends Relation
|
||||
{
|
||||
// 多态字段
|
||||
protected $morphKey;
|
||||
protected $morphType;
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $morphType 多态字段名
|
||||
* @param string $morphKey 外键名
|
||||
* @param array $alias 多态别名定义
|
||||
*/
|
||||
public function __construct(Model $parent, $morphType, $morphKey, $alias = [])
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->morphType = $morphType;
|
||||
$this->morphKey = $morphKey;
|
||||
$this->alias = $alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @access public
|
||||
*/
|
||||
public function getRelation()
|
||||
{
|
||||
$morphKey = $this->morphKey;
|
||||
$morphType = $this->morphType;
|
||||
// 多态模型
|
||||
$model = $this->parseModel($this->parent->$morphType);
|
||||
// 主键数据
|
||||
$pk = $this->parent->$morphKey;
|
||||
return (new $model)->find($pk);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析模型的完整命名空间
|
||||
* @access public
|
||||
* @param string $model 模型名(或者完整类名)
|
||||
* @return string
|
||||
*/
|
||||
protected function parseModel($model)
|
||||
{
|
||||
if (isset($this->alias[$model])) {
|
||||
$model = $this->alias[$model];
|
||||
}
|
||||
if (false === strpos($model, '\\')) {
|
||||
$path = explode('\\', get_class($this->parent));
|
||||
array_pop($path);
|
||||
array_push($path, Loader::parseName($model, 1));
|
||||
$model = implode('\\', $path);
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class)
|
||||
{
|
||||
$morphKey = $this->morphKey;
|
||||
$morphType = $this->morphType;
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (!empty($result->$morphKey)) {
|
||||
$range[$result->$morphType][] = $result->$morphKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
foreach ($range as $key => $val) {
|
||||
// 多态类型映射
|
||||
$model = $this->parseModel($key);
|
||||
$obj = new $model;
|
||||
$pk = $obj->getPk();
|
||||
$list = $obj->all($val, $subRelation);
|
||||
$data = [];
|
||||
foreach ($list as $k => $vo) {
|
||||
$data[$vo->$pk] = $vo;
|
||||
}
|
||||
foreach ($resultSet as $result) {
|
||||
if ($key == $result->$morphType) {
|
||||
if (!isset($data[$result->$morphKey])) {
|
||||
$data[$result->$morphKey] = [];
|
||||
}
|
||||
$result->setAttr($relation, $this->resultSetBuild($data[$result->$morphKey], $class));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class)
|
||||
{
|
||||
$morphKey = $this->morphKey;
|
||||
$morphType = $this->morphType;
|
||||
// 多态类型映射
|
||||
$model = $this->parseModel($result->{$this->morphType});
|
||||
$this->eagerlyMorphToOne($model, $relation, $result, $subRelation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多态MorphTo 关联模型预查询
|
||||
* @access public
|
||||
* @param object $model 关联模型对象
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @return void
|
||||
*/
|
||||
protected function eagerlyMorphToOne($model, $relation, &$result, $subRelation = '')
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
$pk = $this->parent->{$this->morphKey};
|
||||
$data = (new $model)->with($subRelation)->find($pk);
|
||||
if ($data) {
|
||||
$data->isUpdate(true);
|
||||
}
|
||||
$result->setAttr($relation, $data ?: null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{
|
||||
}
|
||||
}
|
||||
161
core/library/think/model/relation/OneToOne.php
Normal file
161
core/library/think/model/relation/OneToOne.php
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2016 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\Loader;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
use think\model\relation\BelongsTo;
|
||||
|
||||
abstract class OneToOne extends Relation
|
||||
{
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @param \Closure $closure 闭包条件
|
||||
* @param bool $first
|
||||
* @return void
|
||||
*/
|
||||
public function eagerly(Query $query, $relation, $subRelation, $closure, $first)
|
||||
{
|
||||
$name = Loader::parseName(basename(str_replace('\\', '/', $query->getModel())));
|
||||
$alias = isset($this->alias[$name]) ? $this->alias[$name] : $name;
|
||||
if ($first) {
|
||||
$table = $query->getTable();
|
||||
$query->table([$table => $alias]);
|
||||
if ($query->getOptions('field')) {
|
||||
$field = $query->getOptions('field');
|
||||
$query->removeOption('field');
|
||||
} else {
|
||||
$field = true;
|
||||
}
|
||||
$query->field($field, false, $table, $alias);
|
||||
}
|
||||
|
||||
// 预载入封装
|
||||
$joinTable = $this->query->getTable();
|
||||
$joinName = Loader::parseName(basename(str_replace('\\', '/', $this->model)));
|
||||
$joinAlias = isset($this->alias[$joinName]) ? $this->alias[$joinName] : $relation;
|
||||
$query->via($joinAlias);
|
||||
|
||||
if ($this instanceof BelongsTo) {
|
||||
$query->join($joinTable . ' ' . $joinAlias, $alias . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey, $this->joinType);
|
||||
} else {
|
||||
$query->join($joinTable . ' ' . $joinAlias, $alias . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey, $this->joinType);
|
||||
}
|
||||
|
||||
if ($closure) {
|
||||
// 执行闭包查询
|
||||
call_user_func_array($closure, [ & $query]);
|
||||
//指定获取关联的字段
|
||||
//需要在 回调中 调方法 withField 方法,如
|
||||
// $query->where(['id'=>1])->withField('id,name');
|
||||
if ($query->getOptions('with_field')) {
|
||||
$field = $query->getOptions('with_field');
|
||||
$query->removeOption('with_field');
|
||||
}
|
||||
} elseif (isset($this->option['field'])) {
|
||||
$field = $this->option['field'];
|
||||
} else {
|
||||
$field = true;
|
||||
}
|
||||
$query->field($field, false, $joinTable, $joinAlias, $relation . '__');
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation)
|
||||
{
|
||||
foreach ($resultSet as $result) {
|
||||
// 模型关联组装
|
||||
$this->match($this->model, $relation, $result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询 返回模型对象
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $class 数据集对象名 为空表示数组
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation)
|
||||
{
|
||||
// 模型关联组装
|
||||
$this->match($this->model, $relation, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 一对一 关联模型预查询拼装
|
||||
* @access public
|
||||
* @param string $model 模型名称
|
||||
* @param string $relation 关联名
|
||||
* @param Model $result 模型对象实例
|
||||
* @return void
|
||||
*/
|
||||
protected function match($model, $relation, &$result)
|
||||
{
|
||||
// 重新组装模型数据
|
||||
foreach ($result->getData() as $key => $val) {
|
||||
if (strpos($key, '__')) {
|
||||
list($name, $attr) = explode('__', $key, 2);
|
||||
if ($name == $relation) {
|
||||
$list[$name][$attr] = $val;
|
||||
unset($result->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result->setAttr($relation, !isset($list[$relation]) ? null : (new $model($list[$relation]))->isUpdate(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @return integer
|
||||
*/
|
||||
public function save($data)
|
||||
{
|
||||
if ($data instanceof Model) {
|
||||
$data = $data->getData();
|
||||
}
|
||||
// 保存关联表数据
|
||||
$data[$this->foreignKey] = $this->parent->{$this->localKey};
|
||||
$model = new $this->model;
|
||||
return $model->save($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ class Php
|
||||
{
|
||||
// 模板引擎参数
|
||||
protected $config = [
|
||||
// 视图基础目录(集中式)
|
||||
'view_base' => '',
|
||||
// 模板起始路径
|
||||
'view_path' => '',
|
||||
// 模板文件后缀
|
||||
@@ -109,20 +111,26 @@ class Php
|
||||
$this->config['view_path'] = App::$modulePath . 'view' . DS;
|
||||
}
|
||||
|
||||
$request = Request::instance();
|
||||
// 获取视图根目录
|
||||
if (strpos($template, '@')) {
|
||||
// 跨模块调用
|
||||
list($module, $template) = explode('@', $template);
|
||||
$path = APP_PATH . $module . DS . 'view' . DS;
|
||||
}
|
||||
if ($this->config['view_base']) {
|
||||
// 基础视图目录
|
||||
$module = isset($module) ? $module : $request->module();
|
||||
$path = $this->config['view_base'] . ($module ? $module . DS : '');
|
||||
} else {
|
||||
$path = $this->config['view_path'];
|
||||
$path = isset($module) ? APP_PATH . $module . DS . 'view' . DS : $this->config['view_path'];
|
||||
}
|
||||
|
||||
// 分析模板文件规则
|
||||
$request = Request::instance();
|
||||
$controller = Loader::parseName($request->controller());
|
||||
$depr = $this->config['view_depr'];
|
||||
$depr = $this->config['view_depr'];
|
||||
if (0 !== strpos($template, '/')) {
|
||||
$template = str_replace(['/', ':'], $depr, $template);
|
||||
}
|
||||
|
||||
$controller = Loader::parseName($request->controller());
|
||||
if ($controller) {
|
||||
if ('' == $template) {
|
||||
// 如果模板文件名为空 按照默认规则定位
|
||||
|
||||
Reference in New Issue
Block a user