1、更换编辑器

2、内核升级
This commit is contained in:
2016-12-10 12:21:08 +08:00
parent 766c26d25d
commit 06939cb0f1
389 changed files with 5944 additions and 41188 deletions

View File

@@ -450,9 +450,9 @@ class App
// 监听app_init
Hook::listen('app_init');
self::$init = $config;
self::$init = true;
}
return self::$init;
return Config::get();
}
/**

View File

@@ -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();
}
}
/**

View File

@@ -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);
}

View File

@@ -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);

View 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;
}
}
/**

View File

@@ -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]'];

View File

@@ -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;
}
}

View File

@@ -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'], '.');
}

View File

@@ -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 {

View File

@@ -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);
}
/**

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -54,7 +54,7 @@ class File
}
$depr = $depr ? "---------------------------------------------------------------\r\n" : '';
$info = '';
if (App::$debug) {
// 获取基本信息
if (isset($_SERVER['HTTP_HOST'])) {

View File

@@ -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);
}
}
}

View 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();
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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);
}
}

View 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;
}
}
}

View 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()
{
}
}

View 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()
{
}
}

View File

@@ -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) {
// 如果模板文件名为空 按照默认规则定位