From 2c80ae3f1033488972c06d7a3900e93e602d70fb Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 25 Jan 2019 15:19:15 +0800 Subject: [PATCH 001/365] =?UTF-8?q?2.0=E7=89=88=E6=9C=AC=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=20=E5=9F=BA=E4=BA=8EPHP7.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 15 +- composer.json | 4 +- src/CacheInterface.php | 18 - src/CacheItem.php | 207 ++ src/Collection.php | 124 +- src/Db.php | 177 +- src/Model.php | 599 ++--- src/Paginator.php | 123 +- src/db/Builder.php | 403 ++- src/db/Connection.php | 1194 ++++----- src/db/Expression.php | 7 +- src/db/Fetch.php | 467 ++++ src/db/Mongo.php | 162 +- src/db/Query.php | 2225 ++++++++--------- src/db/Where.php | 25 +- src/db/builder/Mysql.php | 29 +- src/db/builder/Pgsql.php | 22 +- src/db/builder/Sqlite.php | 21 +- src/db/builder/Sqlsrv.php | 25 +- src/db/connector/Mysql.php | 79 +- src/db/connector/Pgsql.php | 24 +- src/db/connector/Sqlite.php | 24 +- src/db/connector/Sqlsrv.php | 22 +- src/{db => }/exception/BindParamException.php | 18 +- .../exception/ClassNotFoundException.php | 4 +- .../exception/DataNotFoundException.php | 14 +- src/{db => }/exception/DbException.php | 15 +- .../exception/ModelNotFoundException.php | 13 +- src/{db => }/exception/PDOException.php | 15 +- src/model/Collection.php | 117 +- src/model/Pivot.php | 14 +- src/model/Relation.php | 48 +- src/model/concern/Attribute.php | 344 +-- src/model/concern/Conversion.php | 154 +- src/model/concern/ModelEvent.php | 159 +- src/model/concern/OptimLock.php | 137 + src/model/concern/RelationShip.php | 312 ++- src/model/concern/SoftDelete.php | 105 +- src/model/concern/TimeStamp.php | 49 +- src/model/relation/BelongsTo.php | 111 +- src/model/relation/BelongsToMany.php | 254 +- src/model/relation/HasMany.php | 140 +- src/model/relation/HasManyThrough.php | 77 +- src/model/relation/HasOne.php | 102 +- src/model/relation/MorphMany.php | 141 +- src/model/relation/MorphOne.php | 79 +- src/model/relation/MorphTo.php | 73 +- src/model/relation/OneToOne.php | 119 +- src/paginator/Collection.php | 74 - src/paginator/driver/Bootstrap.php | 20 +- 50 files changed, 4496 insertions(+), 4208 deletions(-) delete mode 100644 src/CacheInterface.php create mode 100644 src/CacheItem.php create mode 100644 src/db/Fetch.php rename src/{db => }/exception/BindParamException.php (67%) rename src/{db => }/exception/ClassNotFoundException.php (90%) rename src/{db => }/exception/DataNotFoundException.php (75%) rename src/{db => }/exception/DbException.php (75%) rename src/{db => }/exception/ModelNotFoundException.php (75%) rename src/{db => }/exception/PDOException.php (79%) create mode 100644 src/model/concern/OptimLock.php delete mode 100644 src/paginator/Collection.php diff --git a/README.md b/README.md index 392269f..fb36df7 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # think-orm -基于PHP5.6+ 的ORM实现,主要特性: +基于PHP7.1+ 的ORM实现,主要特性: -- 基于ThinkPHP5.1的ORM独立封装 +- 基于ThinkPHP的ORM独立封装 - 支持Mysql、Pgsql、Sqlite、SqlServer、Oracle和Mongodb - 支持Db类和查询构造器 - 支持事务 @@ -41,7 +41,6 @@ Db::table('user') Db类增加的(静态)方法包括: - `setConfig` 设置全局配置信息 - `getConfig` 获取数据库配置信息 -- `setQuery` 设置数据库Query类名称 - `setCacheHandler` 设置缓存对象Handler(必须支持get、set及rm方法) - `getSqlLog` 用于获取当前请求的SQL日志信息(包含连接信息) @@ -61,7 +60,7 @@ class User extends Model ~~~php use app\index\model\User; -$user = User::get(1); +$user = User::find(1); $user->name = 'thinkphp'; $user->save(); ~~~ @@ -110,7 +109,7 @@ $user->save(); * 模型用法 ```php - $user = User::get(1); + $user = User::find(1); echo $user->id; echo $user->name; ``` @@ -142,7 +141,7 @@ $user->save(); * 模型用法 ```php - $user = User::get(1); + $user = User::find(1); $user->name = 'topthink'; $user->email = 'topthink@qq.com'; $user->save(); @@ -150,7 +149,7 @@ $user->save(); * 或者使用 ```php - $user = User::get(1); + $user = User::find(1); $user->save([ 'name' => 'topthink', 'email' => 'topthink@qq.com', @@ -173,7 +172,7 @@ $user->save(); * 模型用法 ```php - $user = User::get(1); + $user = User::find(1); $user->delete(); ``` * 或者静态实现 diff --git a/composer.json b/composer.json index ef50573..fd69f45 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,9 @@ } ], "require": { - "php": ">=5.6.0" + "php": ">=7.1.0", + "psr/cache": "~1.0", + "psr/simple-cache": "^1.0", }, "autoload": { "psr-4": { diff --git a/src/CacheInterface.php b/src/CacheInterface.php deleted file mode 100644 index 782ee24..0000000 --- a/src/CacheInterface.php +++ /dev/null @@ -1,18 +0,0 @@ - - * @package think - */ -interface CacheInterface -{ - function get($name, $default = false); - - function set($name, $value, $expire = null); - - function rm($name); -} \ No newline at end of file diff --git a/src/CacheItem.php b/src/CacheItem.php new file mode 100644 index 0000000..e79476d --- /dev/null +++ b/src/CacheItem.php @@ -0,0 +1,207 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\cache; + +use DateInterval; +use DateTime; +use DateTimeInterface; +use Psr\Cache\CacheItemInterface; +use think\exception\InvalidArgumentException; + +class CacheItem implements CacheItemInterface +{ + /** + * 缓存Key + * @var string + */ + protected $key; + + /** + * 缓存内容 + * @var mixed + */ + protected $value; + + /** + * 过期时间 + * @var int|DateTimeInterface + */ + protected $expire; + + /** + * 缓存tag + * @var string + */ + protected $tag; + + /** + * 缓存是否命中 + * @var bool + */ + protected $isHit = false; + + public function __construct(string $key = null) + { + $this->key = $key; + } + + /** + * 为此缓存项设置「键」 + * @access public + * @param string $key + * @return $this + */ + public function setKey(string $key) + { + $this->key = $key; + return $this; + } + + /** + * 返回当前缓存项的「键」 + * @access public + * @return string + */ + public function getKey() + { + return $this->key; + } + + /** + * 返回当前缓存项的有效期 + * @access public + * @return DateTimeInterface|int|null + */ + public function getExpire() + { + if ($this->expire instanceof DateTimeInterface) { + return $this->expire; + } + + return $this->expire ? $this->expire - time() : null; + } + + /** + * 获取缓存Tag + * @access public + * @return string + */ + public function getTag() + { + return $this->tag; + } + + /** + * 凭借此缓存项的「键」从缓存系统里面取出缓存项 + * @access public + * @return mixed + */ + public function get() + { + return $this->value; + } + + /** + * 确认缓存项的检查是否命中 + * @access public + * @return bool + */ + public function isHit(): bool + { + return $this->isHit; + } + + /** + * 为此缓存项设置「值」 + * @access public + * @param mixed $value + * @return $this + */ + public function set($value) + { + $this->value = $value; + $this->isHit = true; + return $this; + } + + /** + * 为此缓存项设置所属标签 + * @access public + * @param string $tag + * @return $this + */ + public function tag(string $tag = null) + { + $this->tag = $tag; + return $this; + } + + /** + * 设置缓存项的有效期 + * @access public + * @param mixed $expire + * @return $this + */ + public function expire($expire) + { + if (is_null($expire)) { + $this->expire = null; + } elseif (is_numeric($expire) || $expire instanceof DateInterval) { + $this->expiresAfter($expire); + } elseif ($expire instanceof DateTimeInterface) { + $this->expire = $expire; + } else { + throw new InvalidArgumentException('not support datetime'); + } + + return $this; + } + + /** + * 设置缓存项的准确过期时间点 + * @access public + * @param DateTimeInterface $expiration + * @return $this + */ + public function expiresAt($expiration) + { + if ($expiration instanceof DateTimeInterface) { + $this->expire = $expiration; + } else { + throw new InvalidArgumentException('not support datetime'); + } + + return $this; + } + + /** + * 设置缓存项的过期时间 + * @access public + * @param int|DateInterval $timeInterval + * @return $this + * @throws InvalidArgumentException + */ + public function expiresAfter($timeInterval) + { + if ($timeInterval instanceof DateInterval) { + $this->expire = (int) DateTime::createFromFormat('U', time())->add($timeInterval)->format('U'); + } elseif (is_numeric($timeInterval)) { + $this->expire = $timeInterval + time(); + } else { + throw new InvalidArgumentException('not support datetime'); + } + + return $this; + } + +} diff --git a/src/Collection.php b/src/Collection.php index dcf15e0..17c735e 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -2,12 +2,13 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: zhangyajun <448901948@qq.com> // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think; @@ -19,6 +20,10 @@ use JsonSerializable; class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable { + /** + * 数据集数据 + * @var array + */ protected $items = []; public function __construct($items = []) @@ -33,21 +38,22 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 是否为空 + * @access public * @return bool */ - public function isEmpty() + public function isEmpty(): bool { return empty($this->items); } - public function toArray() + public function toArray(): array { return array_map(function ($value) { return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value; }, $this->items); } - public function all() + public function all(): array { return $this->items; } @@ -55,6 +61,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 合并数组 * + * @access public * @param mixed $items * @return static */ @@ -63,16 +70,6 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria return new static(array_merge($this->items, $this->convertToArray($items))); } - /** - * 交换数组中的键和值 - * - * @return static - */ - public function flip() - { - return new static(array_flip($this->items)); - } - /** * 按指定键整理数据 * @@ -81,7 +78,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * @param string $indexKey 键名 * @return array */ - public function dictionary($items = null, &$indexKey = null) + public function dictionary($items = null, string &$indexKey = null) { if ($items instanceof self || $items instanceof Paginator) { $items = $items->all(); @@ -108,7 +105,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * @param string $indexKey 指定比较的键名 * @return static */ - public function diff($items, $indexKey = null) + public function diff($items, string $indexKey = null) { if ($this->isEmpty() || is_scalar($this->items[0])) { return new static(array_diff($this->items, $this->convertToArray($items))); @@ -136,7 +133,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * @param string $indexKey 指定比较的键名 * @return static */ - public function intersect($items, $indexKey = null) + public function intersect($items, string $indexKey = null) { if ($this->isEmpty() || is_scalar($this->items[0])) { return new static(array_diff($this->items, $this->convertToArray($items))); @@ -156,30 +153,32 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria return new static($intersect); } + /** + * 交换数组中的键和值 + * + * @access public + * @return static + */ + public function flip() + { + return new static(array_flip($this->items)); + } + /** * 返回数组中所有的键名 * * @access public - * @return array + * @return static */ public function keys() { - $current = current($this->items); - - if (is_scalar($current)) { - $array = $this->items; - } elseif (is_array($current)) { - $array = $current; - } else { - $array = $current->toArray(); - } - - return array_keys($array); + return new static(array_keys($this->items)); } /** * 删除数组的最后一个元素(出栈) * + * @access public * @return mixed */ public function pop() @@ -190,6 +189,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 通过使用用户自定义函数,以字符串返回数组 * + * @access public * @param callable $callback * @param mixed $initial * @return mixed @@ -202,6 +202,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 以相反的顺序返回数组。 * + * @access public * @return static */ public function reverse() @@ -212,6 +213,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 删除数组中首个元素,并返回被删除元素的值 * + * @access public * @return mixed */ public function shift() @@ -221,11 +223,12 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 在数组结尾插入一个元素 - * @param mixed $value - * @param mixed $key + * @access public + * @param mixed $value + * @param mixed $key * @return void */ - public function push($value, $key = null) + public function push($value, string $key = null): void { if (is_null($key)) { $this->items[] = $value; @@ -237,11 +240,12 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 把一个数组分割为新的数组块. * + * @access public * @param int $size * @param bool $preserveKeys * @return static */ - public function chunk($size, $preserveKeys = false) + public function chunk(int $size, bool $preserveKeys = false) { $chunks = []; @@ -254,11 +258,12 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 在数组开头插入一个元素 + * @access public * @param mixed $value * @param mixed $key * @return void */ - public function unshift($value, $key = null) + public function unshift($value, string $key = null): void { if (is_null($key)) { array_unshift($this->items, $value); @@ -270,6 +275,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 给每个元素执行个回调 * + * @access public * @param callable $callback * @return $this */ @@ -288,9 +294,21 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria return $this; } + /** + * 用回调函数处理数组中的元素 + * @access public + * @param callable|null $callback + * @return static + */ + public function map(callable $callback) + { + return new static(array_map($callback, $this->items)); + } + /** * 用回调函数过滤数组中的元素 - * @param callable|null $callback + * @access public + * @param callable|null $callback * @return static */ public function filter(callable $callback = null) @@ -310,7 +328,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * @param mixed $value 数据 * @return static */ - public function where($field, $operator, $value = null) + public function where(string $field, $operator, $value = null) { if (is_null($value)) { $value = $operator; @@ -365,14 +383,15 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria } /** - * 返回数组中指定的一列 - * @param mixed $column_key - * @param mixed $index_key + * 返回数据中指定的一列 + * @access public + * @param string $columnKey 键名 + * @param string $indexKey 作为索引值的列 * @return array */ - public function column($column_key, $index_key = null) + public function column(string $columnKey, string $indexKey = null) { - return array_column($this->items, $column_key, $index_key); + return array_column($this->items, $columnKey, $indexKey); } /** @@ -401,26 +420,22 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * @access public * @param string $field 排序字段 * @param string $order 排序 - * @param bool $intSort 是否为数字排序 * @return $this */ - public function order($field, $order = null, $intSort = true) + public function order(string $field, string $order = null) { - return $this->sort(function ($a, $b) use ($field, $order, $intSort) { + return $this->sort(function ($a, $b) use ($field, $order) { $fieldA = isset($a[$field]) ? $a[$field] : null; $fieldB = isset($b[$field]) ? $b[$field] : null; - if ($intSort) { - return 'desc' == strtolower($order) ? $fieldB >= $fieldA : $fieldA >= $fieldB; - } else { - return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB); - } + return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB); }); } /** * 将数组打乱 * + * @access public * @return static */ public function shuffle() @@ -435,12 +450,13 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 截取数组 * + * @access public * @param int $offset * @param int $length * @param bool $preserveKeys * @return static */ - public function slice($offset, $length = null, $preserveKeys = false) + public function slice(int $offset, int $length = null, bool $preserveKeys = false) { return new static(array_slice($this->items, $offset, $length, $preserveKeys)); } @@ -491,10 +507,10 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 转换当前数据集为JSON字符串 * @access public - * @param integer $options json参数 + * @param integer $options json参数 * @return string */ - public function toJson($options = JSON_UNESCAPED_UNICODE) + public function toJson(int $options = JSON_UNESCAPED_UNICODE) { return json_encode($this->toArray(), $options); } @@ -507,14 +523,16 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 转换成数组 * + * @access public * @param mixed $items * @return array */ - protected function convertToArray($items) + protected function convertToArray($items): array { if ($items instanceof self) { return $items->all(); } + return (array) $items; } } diff --git a/src/Db.php b/src/Db.php index b69695a..bc9961f 100644 --- a/src/Db.php +++ b/src/Db.php @@ -2,50 +2,19 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think; -use think\db\Query; - -/** - * Class Db - * @package think - * @method Query table(string $table) static 指定数据表(含前缀) - * @method Query name(string $name) static 指定数据表(不含前缀) - * @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件 - * @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询 - * @method Query union(mixed $union, boolean $all = false) static UNION查询 - * @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT - * @method Query order(mixed $field, string $order = null) static 查询ORDER - * @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存 - * @method mixed value(string $field) static 获取某个字段的值 - * @method array column(string $field, string $key = '') static 获取某个列的值 - * @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询 - * @method mixed find(mixed $data = null) static 查询单个记录 - * @method mixed select(mixed $data = null) static 查询多个记录 - * @method integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录 - * @method integer insertGetId(array $data, boolean $replace = false, string $sequence = null) static 插入一条记录并返回自增ID - * @method integer insertAll(array $dataSet) static 插入多条记录 - * @method integer update(array $data) static 更新记录 - * @method integer delete(mixed $data = null) static 删除记录 - * @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据 - * @method \Generator cursor(mixed $data = null) static 使用游标查找记录 - * @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询 - * @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行 - * @method Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询 - * @method mixed transaction(callable $callback) static 执行数据库事务 - * @method void startTrans() static 启动事务 - * @method void commit() static 用于非自动提交状态下面的查询提交 - * @method void rollback() static 事务回滚 - * @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句 - * @method string getLastInsID($sequence = null) static 获取最近插入的ID - */ +use Psr\SimpleCache\CacheInterface; +use think\db\Connection; + class Db { /** @@ -54,99 +23,145 @@ class Db */ protected static $config = []; - /** - * 查询类名 - * @var string - */ - protected static $query; - /** * 查询类自动映射 * @var array */ protected static $queryMap = [ - 'mongo' => '\\think\\db\Mongo', + 'mongo' => '\\think\\db\\Mongo', ]; - /** - * 查询次数 - * @var integer - */ - public static $queryTimes = 0; - - /** - * 执行次数 - * @var integer - */ - public static $executeTimes = 0; - /** * 缓存对象 * @var object */ protected static $cacheHandler; - public static function setConfig($config = []) + public static function setConfig($config) { - self::$config = array_merge(self::$config, $config); + self::$config = $config; } public static function getConfig($name = null) { - if ($name) { - return isset(self::$config[$name]) ? self::$config[$name] : null; - } else { - return self::$config; - } + return $name ? (self::$config[$name] ?? null) : self::$config; } - public static function setQuery($query) + public static function setCacheHandler(CacheInterface $cacheHandler) + { + self::$cacheHandler = $cacheHandler; + } + + public static function getCacheHandler() + { + return self::$cacheHandler; + } + + /** + * 创建一个新的查询对象 + * @access public + * @param string $query 查询对象类名 + * @param mixed $connection 连接配置信息 + * @return mixed + */ + public static function buildQuery($query, $connection = []) { - self::$query = $query; + $connection = Connection::instance(self::parseConfig($connection)); + return new $query($connection); } /** * 字符串命名风格转换 * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 - * @param string $name 字符串 - * @param integer $type 转换类型 - * @param bool $ucfirst 首字母是否大写(驼峰规则) + * @access public + * @param string $name 字符串 + * @param integer $type 转换类型 + * @param bool $ucfirst 首字母是否大写(驼峰规则) * @return string */ - public static function parseName($name, $type = 0, $ucfirst = true) + public static function parseName(string $name = null, int $type = 0, bool $ucfirst = true): string { if ($type) { $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) { return strtoupper($match[1]); }, $name); return $ucfirst ? ucfirst($name) : lcfirst($name); - } else { - return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); } + + return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); } - public static function setCacheHandler($cacheHandler) + /** + * 获取类名(不包含命名空间) + * @access public + * @param string|object $class + * @return string + */ + public static function classBaseName($class): string { - self::$cacheHandler = $cacheHandler; + $class = is_object($class) ? get_class($class) : $class; + return basename(str_replace('\\', '/', $class)); } - public static function getCacheHandler() + /** + * 数据库连接参数解析 + * @access private + * @param mixed $config + * @return array + */ + private static function parseConfig($config): array { - return self::$cacheHandler; + if (empty($config)) { + $config = self::$config; + } elseif (is_string($config) && false === strpos($config, '/')) { + // 支持读取配置参数 + $config = self::$config[$config] ?? static::$config; + } + + return is_string($config) ? self::parseDsnConfig($config) : $config; } - public static function __callStatic($method, $args) + /** + * DSN解析 + * 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1¶m2=val2#utf8 + * @access private + * @param string $dsnStr + * @return array + */ + private static function parseDsnConfig(string $dsnStr): array { - if (!self::$query) { - $type = strtolower(self::getConfig('type')); + $info = parse_url($dsnStr); - $class = isset(self::$queryMap[$type]) ? self::$queryMap[$type] : '\\think\\db\\Query'; + if (!$info) { + return []; + } - self::$query = $class; + $dsn = [ + 'type' => $info['scheme'], + 'username' => $info['user'] ?? '', + 'password' => $info['pass'] ?? '', + 'hostname' => $info['host'] ?? '', + 'hostport' => $info['port'] ?? '', + 'database' => !empty($info['path']) ? ltrim($info['path'], '/') : '', + 'charset' => $info['fragment'] ?? 'utf8', + ]; + + if (isset($info['query'])) { + parse_str($info['query'], $dsn['params']); + } else { + $dsn['params'] = []; } - $class = self::$query; + return $dsn; + } + + public static function __callStatic($method, $args) + { + $type = strtolower(self::getConfig('type')); + $class = isset(self::$queryMap[$type]) ? self::$queryMap[$type] : '\\think\\db\\Query'; + + $query = static::buildQuery($class, self::$config); - return call_user_func_array([new $class, $method], $args); + return call_user_func_array([$query, $method], $args); } } diff --git a/src/Model.php b/src/Model.php index fb50f9b..d237d71 100644 --- a/src/Model.php +++ b/src/Model.php @@ -2,24 +2,27 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think; +use ArrayAccess; +use JsonSerializable; +use think\Db; use think\db\Query; /** * Class Model * @package think * @mixin Query - * @method \think\Model withAttr(array $name,\Closure $closure) 动态定义获取器 */ -abstract class Model implements \JsonSerializable, \ArrayAccess +abstract class Model implements JsonSerializable, ArrayAccess { use model\concern\Attribute; use model\concern\RelationShip; @@ -52,10 +55,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess private $updateWhere; /** - * 数据库配置信息 - * @var array|string + * 数据库配置 + * @var string */ - protected $connection = []; + protected $connection; /** * 数据库查询对象类名 @@ -99,24 +102,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess */ protected static $initialized = []; - /** - * 是否从主库读取(主从分布式有效) - * @var array - */ - protected static $readMaster; - /** * 查询对象实例 * @var Query */ protected $queryInstance; - /** - * 错误信息 - * @var mixed - */ - protected $error; - /** * 软删除字段默认值 * @var mixed @@ -129,20 +120,22 @@ abstract class Model implements \JsonSerializable, \ArrayAccess */ protected $globalScope = []; + /** + * 延迟保存信息 + * @var bool + */ + private $lazySave = false; + /** * 架构函数 * @access public - * @param array|object $data 数据 + * @param array $data 数据 */ - public function __construct($data = []) + public function __construct(array $data = []) { - if (is_object($data)) { - $this->data = get_object_vars($data); - } else { - $this->data = $data; - } + $this->data = $data; - if ($this->disuse) { + if ($this->data) { // 废弃字段 foreach ((array) $this->disuse as $key) { if (array_key_exists($key, $this->data)) { @@ -176,10 +169,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $this->dateFormat = $config['datetime_format']; } - if (is_null($this->resultSetType)) { - $this->resultSetType = $config['resultset_type']; - } - if (is_null($this->query)) { // 设置查询对象 $this->query = $config['query']; @@ -190,41 +179,34 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $this->connection = array_merge($config, $this->connection); } - if ($this->observerClass) { - // 注册模型观察者 - static::observe($this->observerClass); - } - // 执行初始化操作 $this->initialize(); } /** - * 是否从主库读取数据(主从分布有效) + * 获取当前模型名称 * @access public - * @param bool $all 是否所有模型有效 - * @return $this + * @return string */ - public function readMaster($all = false) + public function getName(): string { - $model = $all ? '*' : static::class; - - static::$readMaster[$model] = true; - - return $this; + return $this->name; } /** * 创建新的模型实例 * @access public - * @param array|object $data 数据 - * @param bool $isUpdate 是否为更新 - * @param mixed $where 更新条件 + * @param array $data 数据 + * @param bool $isUpdate 是否为更新 + * @param mixed $where 更新条件 * @return Model */ - public function newInstance($data = [], $isUpdate = false, $where = null) + public function newInstance(array $data = [], bool $isUpdate = false, $where = null): Model { - return (new static($data))->isUpdate($isUpdate, $where); + $model = (new static($data))->isUpdate($isUpdate, $where); + + $model->trigger('after_read'); + return $model; } /** @@ -232,40 +214,29 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * @access protected * @return Query */ - protected function buildQuery() + protected function buildQuery(): Query { - // 设置当前模型 确保查询返回模型对象 - $class = $this->query; - $query = (new $class())->connect($this->connection) - ->model($this) - ->json($this->json, $this->jsonAssoc) - ->setJsonFieldType($this->jsonType); + $query = Db::buildQuery($this->query, $this->connection); - if (isset(static::$readMaster['*']) || isset(static::$readMaster[static::class])) { - $query->master(true); - } + $query->model($this) + ->name($this->name) + ->json($this->json, $this->jsonAssoc) + ->setFieldType($this->schema); - // 设置当前数据表和模型名 if (!empty($this->table)) { $query->table($this->table); - } else { - $query->name($this->name); } - if (!empty($this->pk)) { - $query->pk($this->pk); - } - - return $query; + return $query->pk($this->pk); } /** * 获取当前模型的数据库查询对象 * @access public - * @param Query $query 查询对象实例 + * @param Query $query 查询对象实例 * @return $this */ - public function setQuery($query) + public function setQuery(Query $query) { $this->queryInstance = $query; return $this; @@ -274,10 +245,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 获取当前模型的数据库查询对象 * @access public - * @param bool|array $useBaseQuery 是否调用全局查询范围(或者指定查询范围名称) + * @param array|false $scope 使用的全局查询范围 * @return Query */ - public function db($useBaseQuery = true) + public function db($scope = []): Query { if ($this->queryInstance) { return $this->queryInstance; @@ -291,13 +262,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } // 全局作用域 - if (true === $useBaseQuery && method_exists($this, 'base')) { - call_user_func_array([$this, 'base'], [ & $query]); - } - - $globalScope = is_array($useBaseQuery) && $useBaseQuery ? $useBaseQuery : $this->globalScope; + $globalScope = is_array($scope) && $scope ? $scope : $this->globalScope; - if ($globalScope && false !== $useBaseQuery) { + if ($globalScope && false !== $scope) { $query->scope($globalScope); } @@ -307,12 +274,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 初始化模型 - * @access protected + * @access private * @return void */ - protected function initialize() + private function initialize(): void { if (!isset(static::$initialized[static::class])) { + if ($this->observerClass) { + // 注册模型观察者 + static::observe($this->observerClass); + } static::$initialized[static::class] = true; static::init(); } @@ -323,16 +294,44 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * @access protected * @return void */ - protected static function init() + protected static function init(): void + {} + + /** + * 数据自动完成 + * @access protected + * @param array $auto 要自动更新的字段列表 + * @return void + */ + protected function autoCompleteData(array $auto = []): void + { + foreach ($auto as $field => $value) { + if (is_integer($field)) { + $field = $value; + $value = null; + } + + if (!isset($this->data[$field])) { + $default = null; + } else { + $default = $this->data[$field]; + } + + $this->setAttr($field, !is_null($value) ? $value : $default); + } + + } + + protected function checkData(array &$data): void {} /** * 更新是否强制写入数据 而不做比较 * @access public - * @param bool $force + * @param bool $force * @return $this */ - public function force($force = true) + public function force(bool $force = true) { $this->force = $force; return $this; @@ -343,7 +342,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * @access public * @return bool */ - public function isForce() + public function isForce(): bool { return $this->force; } @@ -354,7 +353,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * @param bool $replace * @return $this */ - public function replace($replace = true) + public function replace(bool $replace = true) { $this->replace = $replace; return $this; @@ -366,7 +365,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * @param bool $exists * @return $this */ - public function exists($exists) + public function exists(bool $exists = true) { $this->exists = $exists; return $this; @@ -377,55 +376,57 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * @access public * @return bool */ - public function isExists() + public function isExists(): bool { return $this->exists; } /** - * 数据自动完成 - * @access protected - * @param array $auto 要自动更新的字段列表 - * @return void + * 判断模型是否为空 + * @access public + * @return bool */ - protected function autoCompleteData($auto = []) + public function isEmpty(): bool { - foreach ($auto as $field => $value) { - if (is_integer($field)) { - $field = $value; - $value = null; - } + return empty($this->data); + } - if (!isset($this->data[$field])) { - $default = null; - } else { - $default = $this->data[$field]; + /** + * 延迟保存当前数据对象 + * @access public + * @param array|bool $data 数据 + * @return void + */ + public function lazySave($data = []) + { + if (false === $data) { + $this->lazySave = false; + } else { + if (is_array($data)) { + $this->setAttrs($data); } - $this->setAttr($field, !is_null($value) ? $value : $default); + $this->lazySave = true; } } /** * 保存当前数据对象 * @access public - * @param array $data 数据 - * @param array $where 更新条件 - * @param string $sequence 自增序列名 - * @return false + * @param array $data 数据 + * @param string $sequence 自增序列名 + * @return bool */ - public function save($data = [], $where = [], $sequence = null) + public function save(array $data = [], string $sequence = null): bool { - if (is_string($data)) { - $sequence = $data; - $data = []; - } + // 数据对象赋值 + $this->setAttrs($data); - if (!$this->checkBeforeSave($data, $where)) { + if ($this->isEmpty() || false === $this->trigger('before_write')) { return false; } - $result = $this->exists ? $this->updateData($where) : $this->insertData($sequence); + $result = $this->exists ? $this->updateData() : $this->insertData($sequence); if (false === $result) { return false; @@ -435,59 +436,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $this->trigger('after_write'); // 重新记录原始数据 - $this->origin = $this->data; - $this->set = []; - - return true; - } - - /** - * 解析查询条件 - * @access protected - * @param array|null $where 保存条件 - * @return array|null - */ - protected static function parseWhere($where) - { - if (is_array($where) && key($where) !== 0) { - $item = []; - foreach ($where as $key => $val) { - $item[] = [$key, '=', $val]; - } - return $item; - } - return $where; - } - - /** - * 写入之前检查数据 - * @access protected - * @param array $data 数据 - * @param array $where 保存条件 - * @return bool - */ - protected function checkBeforeSave($data, $where) - { - if (!empty($data)) { - - // 数据对象赋值 - foreach ($data as $key => $value) { - $this->setAttr($key, $value, $data); - } - - if (!empty($where)) { - $this->exists = true; - $this->updateWhere = self::parseWhere($where); - } - } - - // 数据自动完成 - $this->autoCompleteData($this->auto); - - // 事件回调 - if (false === $this->trigger('before_write')) { - return false; - } + $this->origin = $this->data; + $this->set = []; + $this->lazySave = false; return true; } @@ -495,73 +446,79 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 检查数据是否允许写入 * @access protected - * @param array $autoFields 自动完成的字段列表 + * @param array $append 自动完成的字段列表 * @return array */ - protected function checkAllowFields($append = []) + protected function checkAllowFields(array $append = []): array { // 检测字段 - if (empty($this->field) || true === $this->field) { - $query = $this->db(false); - $table = $this->table ?: $query->getTable(); + if (empty($this->field)) { + if ($this->schema) { + $this->field = array_keys($this->schema); + } else { + $query = $this->db(); + $table = $this->table ?: $query->getTable(); - $this->field = $query->getConnection()->getTableFields($table); + $this->field = $query->getConnection()->getTableFields($table); + } - $field = $this->field; - } else { - $field = array_merge($this->field, $append); + return $this->field; + } - if ($this->autoWriteTimestamp) { - array_push($field, $this->createTime, $this->updateTime); - } + $field = array_merge($this->field, $append); + + if ($this->autoWriteTimestamp) { + array_push($field, $this->createTime, $this->updateTime); } if ($this->disuse) { // 废弃字段 - $field = array_diff($field, (array) $this->disuse); + $field = array_diff($field, $this->disuse); } + return $field; } /** * 保存写入数据 * @access protected - * @param array $where 保存条件 - * @return int|false + * @return bool */ - protected function updateData($where) + protected function updateData(): bool { // 自动更新 - $this->autoCompleteData($this->update); + $auto = array_merge($this->auto, $this->update); + + $this->autoCompleteData($auto); // 事件回调 if (false === $this->trigger('before_update')) { return false; } + $this->checkData($this->data); + // 获取有更新的数据 $data = $this->getChangedData(); if (empty($data)) { // 关联更新 - if (isset($this->relationWrite)) { + if (!empty($this->relationWrite)) { $this->autoRelationUpdate(); } - return 0; - } elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) { + return false; + } + + if ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) { // 自动写入更新时间 $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); $this->data[$this->updateTime] = $data[$this->updateTime]; } - if (empty($where) && !empty($this->updateWhere)) { - $where = $this->updateWhere; - } - // 检查允许字段 - $allowFields = $this->checkAllowFields(array_merge($this->auto, $this->update)); + $allowFields = $this->checkAllowFields($auto); // 保留主键数据 foreach ($this->data as $key => $val) { @@ -570,7 +527,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } } - $pk = $this->getPk(); + $pk = $this->getPk(); + $array = []; foreach ((array) $pk as $key) { if (isset($data[$key])) { @@ -581,32 +539,34 @@ abstract class Model implements \JsonSerializable, \ArrayAccess if (!empty($array)) { $where = $array; + } else { + $where = $this->updateWhere; } - if ($this->relationWrite) { - foreach ($this->relationWrite as $name => $val) { - if (is_array($val)) { - foreach ($val as $key) { - if (isset($data[$key])) { - unset($data[$key]); - } - } + foreach ((array) $this->relationWrite as $name => $val) { + if (!is_array($val)) { + continue; + } + + foreach ($val as $key) { + if (isset($data[$key])) { + unset($data[$key]); } } } - $db = $this->db(false); + // 模型更新 + $db = $this->db(); $db->startTrans(); try { - // 模型更新 - $result = $db->where($where) + $db->where($where) ->strict(false) ->field($allowFields) ->update($data); // 关联更新 - if (isset($this->relationWrite)) { + if (!empty($this->relationWrite)) { $this->autoRelationUpdate(); } @@ -615,7 +575,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess // 更新回调 $this->trigger('after_update'); - return $result; + return true; } catch (\Exception $e) { $db->rollback(); throw $e; @@ -625,25 +585,37 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 新增写入数据 * @access protected - * @param string $sequence 自增名 - * @return int|false + * @param string $sequence 自增名 + * @return bool */ - protected function insertData($sequence) + protected function insertData(string $sequence = null): bool { // 自动写入 - $this->autoCompleteData($this->insert); + $auto = array_merge($this->auto, $this->insert); + + $this->autoCompleteData($auto); // 时间戳自动写入 - $this->checkTimeStampWrite(); + if ($this->autoWriteTimestamp) { + if ($this->createTime && !isset($this->data[$this->createTime])) { + $this->data[$this->createTime] = $this->autoWriteTimestamp($this->createTime); + } + + if ($this->updateTime && !isset($this->data[$this->updateTime])) { + $this->data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); + } + } if (false === $this->trigger('before_insert')) { return false; } + $this->checkData($this->data); + // 检查允许字段 - $allowFields = $this->checkAllowFields(array_merge($this->auto, $this->insert)); + $allowFields = $this->checkAllowFields($auto); - $db = $this->db(false); + $db = $this->db(); $db->startTrans(); try { @@ -663,71 +635,25 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } // 关联写入 - if (isset($this->relationWrite)) { + if (!empty($this->relationWrite)) { $this->autoRelationInsert(); } $db->commit(); - // 标记为更新 + // 标记数据已经存在 $this->exists = true; // 新增回调 $this->trigger('after_insert'); - return $result; + return true; } catch (\Exception $e) { $db->rollback(); throw $e; } } - /** - * 字段值(延迟)增长 - * @access public - * @param string $field 字段名 - * @param integer $step 增长值 - * @param integer $lazyTime 延时时间(s) - * @return integer|true - * @throws Exception - */ - public function setInc($field, $step = 1, $lazyTime = 0) - { - // 读取更新条件 - $where = $this->getWhere(); - - $result = $this->db(false)->where($where)->setInc($field, $step, $lazyTime); - - if (true !== $result) { - $this->data[$field] += $step; - } - - return $result; - } - - /** - * 字段值(延迟)增长 - * @access public - * @param string $field 字段名 - * @param integer $step 增长值 - * @param integer $lazyTime 延时时间(s) - * @return integer|true - * @throws Exception - */ - public function setDec($field, $step = 1, $lazyTime = 0) - { - // 读取更新条件 - $where = $this->getWhere(); - - $result = $this->db(false)->where($where)->setDec($field, $step, $lazyTime); - - if (true !== $result) { - $this->data[$field] -= $step; - } - - return $result; - } - /** * 获取当前的更新条件 * @access protected @@ -752,16 +678,14 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 保存多个数据到当前数据对象 * @access public - * @param array $dataSet 数据 - * @param boolean $replace 是否自动识别更新和写入 - * @return Collection|false + * @param array $dataSet 数据 + * @param boolean $replace 是否自动识别更新和写入 + * @return Collection * @throws \Exception */ - public function saveAll($dataSet, $replace = true) + public function saveAll(array $dataSet, bool $replace = true): Collection { - $result = []; - - $db = $this->db(false); + $db = $this->db(); $db->startTrans(); try { @@ -771,9 +695,11 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $auto = true; } + $result = []; + foreach ($dataSet as $key => $data) { - if (!empty($auto) && isset($data[$pk])) { - $result[$key] = self::update($data, [], $this->field); + if ($this->exists || (!empty($auto) && isset($data[$pk]))) { + $result[$key] = self::update($data, $this->field); } else { $result[$key] = self::create($data, $this->field, $this->replace); } @@ -791,21 +717,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 是否为更新数据 * @access public - * @param mixed $update - * @param mixed $where + * @param bool $update + * @param mixed $where * @return $this */ - public function isUpdate($update = true, $where = null) + public function isUpdate(bool $update = true, $where = null) { - if (is_bool($update)) { - $this->exists = $update; + $this->exists = $update; - if (!empty($where)) { - $this->updateWhere = $where; - } - } else { - $this->exists = true; - $this->updateWhere = $update; + if (!empty($where)) { + $this->updateWhere = $where; } return $this; @@ -816,21 +737,21 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * @access public * @return bool */ - public function delete() + public function delete(): bool { - if (!$this->exists || false === $this->trigger('before_delete')) { + if (!$this->exists || $this->isEmpty() || false === $this->trigger('before_delete')) { return false; } // 读取更新条件 $where = $this->getWhere(); - $db = $this->db(false); + $db = $this->db(); $db->startTrans(); try { // 删除当前模型数据 - $db->where($where)->delete(); + $result = $db->where($where)->delete(); // 关联删除 if (!empty($this->relationWrite)) { @@ -841,7 +762,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $this->trigger('after_delete'); - $this->exists = false; + $this->exists = false; + $this->lazySave = false; return true; } catch (\Exception $e) { @@ -853,10 +775,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 设置自动完成的字段( 规则通过修改器定义) * @access public - * @param array $fields 需要自动完成的字段 + * @param array $fields 需要自动完成的字段 * @return $this */ - public function auto($fields) + public function auto(array $fields) { $this->auto = $fields; @@ -867,19 +789,19 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * 写入数据 * @access public * @param array $data 数据数组 - * @param array|true $field 允许字段 + * @param array $field 允许字段 * @param bool $replace 使用Replace * @return static */ - public static function create($data = [], $field = null, $replace = false) + public static function create(array $data, array $allowField = [], bool $replace = false): Model { $model = new static(); - if (!empty($field)) { - $model->allowField($field); + if (!empty($allowField)) { + $model->allowField($allowField); } - $model->isUpdate(false)->replace($replace)->save($data, []); + $model->isUpdate(false)->replace($replace)->save($data); return $model; } @@ -887,20 +809,19 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 更新数据 * @access public - * @param array $data 数据数组 - * @param array $where 更新条件 - * @param array|true $field 允许字段 - * @return $this + * @param array $data 数据数组 + * @param array $allowField 允许字段 + * @return static */ - public static function update($data = [], $where = [], $field = null) + public static function update(array $data, array $allowField = []) { $model = new static(); - if (!empty($field)) { - $model->allowField($field); + if (!empty($allowField)) { + $model->allowField($allowField); } - $result = $model->isUpdate(true)->save($data, $where); + $model->isUpdate(true)->save($data); return $model; } @@ -908,19 +829,22 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 删除记录 * @access public - * @param mixed $data 主键列表 支持闭包查询条件 + * @param mixed $data 主键列表 支持闭包查询条件 + * @param bool $force 是否强制删除 * @return bool */ - public static function destroy($data) + public static function destroy($data, bool $force = false): bool { + if (empty($data) && 0 !== $data) { + return false; + } + $model = new static(); $query = $model->db(); - if (empty($data) && 0 !== $data) { - return false; - } elseif (is_array($data) && key($data) !== 0) { - $query->where(self::parseWhere($data)); + if (is_array($data) && key($data) !== 0) { + $query->where($data); $data = null; } elseif ($data instanceof \Closure) { $data($query); @@ -929,25 +853,13 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $resultSet = $query->select($data); - if ($resultSet) { - foreach ($resultSet as $data) { - $data->delete(); - } + foreach ($resultSet as $data) { + $data->force($force)->delete(); } return true; } - /** - * 获取错误信息 - * @access public - * @return mixed - */ - public function getError() - { - return $this->error; - } - /** * 解序列化后处理 */ @@ -967,11 +879,11 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 修改器 设置数据对象的值 * @access public - * @param string $name 名称 - * @param mixed $value 值 + * @param string $name 名称 + * @param mixed $value 值 * @return void */ - public function __set($name, $value) + public function __set(string $name, $value): void { $this->setAttr($name, $value); } @@ -979,10 +891,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 获取器 获取数据对象的值 * @access public - * @param string $name 名称 + * @param string $name 名称 * @return mixed */ - public function __get($name) + public function __get(string $name) { return $this->getAttr($name); } @@ -990,25 +902,21 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 检测数据对象的值 * @access public - * @param string $name 名称 - * @return boolean + * @param string $name 名称 + * @return bool */ - public function __isset($name) + public function __isset(string $name): bool { - try { - return !is_null($this->getAttr($name)); - } catch (InvalidArgumentException $e) { - return false; - } + return !is_null($this->getAttr($name)); } /** * 销毁数据对象的值 * @access public - * @param string $name 名称 + * @param string $name 名称 * @return void */ - public function __unset($name) + public function __unset(string $name): void { unset($this->data[$name], $this->relation[$name]); } @@ -1019,7 +927,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $this->setAttr($name, $value); } - public function offsetExists($name) + public function offsetExists($name): bool { return $this->__isset($name); } @@ -1035,16 +943,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } /** - * 设置是否使用全局查询范围 - * @param bool|array $use 是否启用全局查询范围(或者用数组指定查询范围名称) + * 设置使用的全局查询范围 * @access public + * @param array|false $scope 启用的全局查询范围 * @return Query */ - public static function useGlobalScope($use) + public static function useGlobalScope($scope) { $model = new static(); - return $model->db($use); + return $model->db($scope); } public function __call($method, $args) @@ -1062,4 +970,15 @@ abstract class Model implements \JsonSerializable, \ArrayAccess return call_user_func_array([$model->db(), $method], $args); } + + /** + * 析构方法 + * @access public + */ + public function __destruct() + { + if ($this->lazySave) { + $this->save(); + } + } } diff --git a/src/Paginator.php b/src/Paginator.php index cbc6568..15035ad 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -2,12 +2,13 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: zhangyajun <448901948@qq.com> // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think; @@ -20,28 +21,52 @@ use Traversable; abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable { - /** @var bool 是否为简洁模式 */ + /** + * 是否简洁模式 + * @var bool + */ protected $simple = false; - /** @var Collection 数据集 */ + /** + * 数据集 + * @var Collection + */ protected $items; - /** @var integer 当前页 */ + /** + * 当前页 + * @var integer + */ protected $currentPage; - /** @var integer 最后一页 */ + /** + * 最后一页 + * @var integer + */ protected $lastPage; - /** @var integer|null 数据总数 */ + /** + * 数据总数 + * @var integer|null + */ protected $total; - /** @var integer 每页的数量 */ + /** + * 每页数量 + * @var integer + */ protected $listRows; - /** @var bool 是否有下一页 */ + /** + * 是否有下一页 + * @var bool + */ protected $hasMore; - /** @var array 一些配置 */ + /** + * 分页配置 + * @var array + */ protected $options = [ 'var_page' => 'page', 'path' => '/', @@ -76,11 +101,12 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J } /** + * @access public * @param $items * @param $listRows * @param null $currentPage - * @param bool $simple * @param null $total + * @param bool $simple * @param array $options * @return Paginator */ @@ -89,7 +115,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J return new static($items, $listRows, $currentPage, $total, $simple, $options); } - protected function setCurrentPage($currentPage) + protected function setCurrentPage(int $currentPage): int { if (!$this->simple && $currentPage > $this->lastPage) { return $this->lastPage > 0 ? $this->lastPage : 1; @@ -101,10 +127,11 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 获取页码对应的链接 * - * @param $page + * @access protected + * @param int $page * @return string */ - protected function url($page) + protected function url(int $page): string { if ($page <= 0) { $page = 1; @@ -124,7 +151,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J $url = $path; if (!empty($parameters)) { - $url .= '?' . urldecode(http_build_query($parameters, null, '&')); + $url .= '?' . http_build_query($parameters, null, '&'); } return $url . $this->buildFragment(); @@ -132,13 +159,14 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 自动获取当前页码 - * @param string $varPage - * @param int $default + * @access public + * @param string $varPage + * @param int $default * @return int */ - public static function getCurrentPage($varPage = 'page', $default = 1) + public static function getCurrentPage(string $varPage = 'page', int $default = 1): int { - $page = isset($_REQUEST[$varPage]) ? $_REQUEST[$varPage] : 1; + $page = $_REQUEST[$varPage] ?? 1; if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) { return $page; @@ -149,9 +177,10 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 自动获取当前的path + * @access public * @return string */ - public static function getCurrentPath() + public static function getCurrentPath(): string { if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { $url = $_SERVER['HTTP_X_REWRITE_URL']; @@ -166,7 +195,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J return strpos($url, '?') ? strstr($url, '?', true) : $url; } - public function total() + public function total(): int { if ($this->simple) { throw new \DomainException('not support total'); @@ -175,17 +204,17 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J return $this->total; } - public function listRows() + public function listRows(): int { return $this->listRows; } - public function currentPage() + public function currentPage(): int { return $this->currentPage; } - public function lastPage() + public function lastPage(): int { if ($this->simple) { throw new \DomainException('not support last'); @@ -196,9 +225,10 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 数据是否足够分页 - * @return boolean + * @access public + * @return bool */ - public function hasPages() + public function hasPages(): bool { return !(1 == $this->currentPage && !$this->hasMore); } @@ -206,11 +236,12 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 创建一组分页链接 * + * @access public * @param int $start * @param int $end * @return array */ - public function getUrlRange($start, $end) + public function getUrlRange(int $start, int $end): array { $urls = []; @@ -224,6 +255,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 设置URL锚点 * + * @access public * @param string|null $fragment * @return $this */ @@ -237,6 +269,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 添加URL参数 * + * @access public * @param array|string $key * @param string|null $value * @return $this @@ -261,15 +294,17 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 构造锚点字符串 * + * @access public * @return string */ - protected function buildFragment() + protected function buildFragment(): string { return $this->options['fragment'] ? '#' . $this->options['fragment'] : ''; } /** * 渲染分页html + * @access public * @return mixed */ abstract public function render(); @@ -284,7 +319,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J return $this->items; } - public function isEmpty() + public function isEmpty(): bool { return $this->items->isEmpty(); } @@ -292,6 +327,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 给每个元素执行个回调 * + * @access public * @param callable $callback * @return $this */ @@ -312,6 +348,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Retrieve an external iterator + * @access public * @return Traversable An instance of an object implementing Iterator or * Traversable */ @@ -322,7 +359,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Whether a offset exists - * @param mixed $offset + * @access public + * @param mixed $offset * @return bool */ public function offsetExists($offset) @@ -332,7 +370,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Offset to retrieve - * @param mixed $offset + * @access public + * @param mixed $offset * @return mixed */ public function offsetGet($offset) @@ -342,8 +381,9 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Offset to set - * @param mixed $offset - * @param mixed $value + * @access public + * @param mixed $offset + * @param mixed $value */ public function offsetSet($offset, $value) { @@ -352,9 +392,10 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Offset to unset - * @param mixed $offset + * @access public + * @param mixed $offset * @return void - * @since 5.0.0 + * @since 5.0.0 */ public function offsetUnset($offset) { @@ -364,7 +405,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Count elements of an object */ - public function count() + public function count(): int { return $this->items->count(); } @@ -374,7 +415,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J return (string) $this->render(); } - public function toArray() + public function toArray(): array { try { $total = $this->total(); @@ -401,7 +442,15 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J public function __call($name, $arguments) { - return call_user_func_array([$this->getCollection(), $name], $arguments); + $collection = $this->getCollection(); + + $result = call_user_func_array([$collection, $name], $arguments); + + if ($result === $collection) { + return $this; + } + + return $result; } } diff --git a/src/db/Builder.php b/src/db/Builder.php index 3810dfe..cd4a625 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -2,12 +2,13 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\db; @@ -20,7 +21,7 @@ abstract class Builder protected $connection; // 查询表达式映射 - protected $exp = ['EQ' => '=', 'NEQ' => '<>', 'GT' => '>', 'EGT' => '>=', 'LT' => '<', 'ELT' => '<=', 'NOTLIKE' => 'NOT LIKE', 'NOTIN' => 'NOT IN', 'NOTBETWEEN' => 'NOT BETWEEN', 'NOTEXISTS' => 'NOT EXISTS', 'NOTNULL' => 'NOT NULL', 'NOTBETWEEN TIME' => 'NOT BETWEEN TIME']; + protected $exp = ['NOTLIKE' => 'NOT LIKE', 'NOTIN' => 'NOT IN', 'NOTBETWEEN' => 'NOT BETWEEN', 'NOTEXISTS' => 'NOT EXISTS', 'NOTNULL' => 'NOT NULL', 'NOTBETWEEN TIME' => 'NOT BETWEEN TIME']; // 查询表达式解析 protected $parser = [ @@ -37,20 +38,20 @@ abstract class Builder ]; // SQL表达式 - protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT%%LOCK%%COMMENT%'; + protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT% %LOCK%%COMMENT%'; protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; - protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + protected $updateSql = 'UPDATE %TABLE% SET %SET%%JOIN%%WHERE%%ORDER%%LIMIT% %LOCK%%COMMENT%'; - protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + protected $deleteSql = 'DELETE FROM %TABLE%%USING%%JOIN%%WHERE%%ORDER%%LIMIT% %LOCK%%COMMENT%'; /** * 架构函数 * @access public - * @param Connection $connection 数据库连接对象实例 + * @param Connection $connection 数据库连接对象实例 */ public function __construct(Connection $connection) { @@ -62,7 +63,7 @@ abstract class Builder * @access public * @return Connection */ - public function getConnection() + public function getConnection(): Connection { return $this->connection; } @@ -70,11 +71,11 @@ abstract class Builder /** * 注册查询表达式解析 * @access public - * @param string $name 解析方法 - * @param array $parser 匹配表达式数据 + * @param string $name 解析方法 + * @param array $parser 匹配表达式数据 * @return $this */ - public function bindParser($name, $parser) + public function bindParser(string $name, array $parser) { $this->parser[$name] = $parser; return $this; @@ -89,7 +90,7 @@ abstract class Builder * @param array $bind 参数绑定 * @return array */ - protected function parseData(Query $query, $data = [], $fields = [], $bind = []) + protected function parseData(Query $query, array $data = [], array $fields = [], array $bind = []): array { if (empty($data)) { return []; @@ -99,7 +100,7 @@ abstract class Builder // 获取绑定信息 if (empty($bind)) { - $bind = $this->connection->getFieldsBind($options['table']); + $bind = $query->getFieldBindType(); } if (empty($fields)) { @@ -113,17 +114,13 @@ abstract class Builder $result = []; foreach ($data as $key => $val) { - if ('*' != $options['field'] && !in_array($key, $fields, true)) { - continue; - } - $item = $this->parseKey($query, $key, true); if ($val instanceof Expression) { $result[$item] = $val->getValue(); continue; } elseif (!is_scalar($val) && (in_array($key, (array) $query->getOptions('json')) || 'json' == $this->connection->getFieldsType($options['table'], $key))) { - $val = json_encode($val, JSON_UNESCAPED_UNICODE); + $val = json_encode($val); } elseif (is_object($val) && method_exists($val, '__toString')) { // 对象数据写入 $val = $val->__toString(); @@ -147,8 +144,6 @@ abstract class Builder case 'DEC': $result[$item] = $item . ' - ' . floatval($val[1]); break; - case 'EXP': - throw new Exception('not support data:[' . $val[0] . ']'); } } elseif (is_scalar($val)) { // 过滤非标量数据 @@ -168,7 +163,7 @@ abstract class Builder * @param array $bind 绑定数据 * @return string */ - protected function parseDataBind(Query $query, $key, $data, $bind = []) + protected function parseDataBind(Query $query, string $key, $data, array $bind = []): string { if ($data instanceof Expression) { return $data->getValue(); @@ -187,19 +182,19 @@ abstract class Builder * @param bool $strict 严格检测 * @return string */ - public function parseKey(Query $query, $key, $strict = false) + public function parseKey(Query $query, $key, bool $strict = false): string { - return $key instanceof Expression ? $key->getValue() : $key; + return $key; } /** * field分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $fields + * @param Query $query 查询对象 + * @param mixed $fields 字段名 * @return string */ - protected function parseField(Query $query, $fields) + protected function parseField(Query $query, $fields): string { if ('*' == $fields || empty($fields)) { $fieldsStr = '*'; @@ -208,7 +203,9 @@ abstract class Builder $array = []; foreach ($fields as $key => $field) { - if (!is_numeric($key)) { + if ($field instanceof Expression) { + $array[] = $field->getValue(); + } elseif (!is_numeric($key)) { $array[] = $this->parseKey($query, $key) . ' AS ' . $this->parseKey($query, $field, true); } else { $array[] = $this->parseKey($query, $field); @@ -224,27 +221,24 @@ abstract class Builder /** * table分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $tables + * @param Query $query 查询对象 + * @param mixed $tables 表名 * @return string */ - protected function parseTable(Query $query, $tables) + protected function parseTable(Query $query, $tables): string { $item = []; $options = $query->getOptions(); foreach ((array) $tables as $key => $table) { - if (!is_numeric($key)) { - $key = $this->connection->parseSqlTable($key); + if ($table instanceof Expression) { + $item[] = $table->getValue(); + } elseif (!is_numeric($key)) { $item[] = $this->parseKey($query, $key) . ' ' . $this->parseKey($query, $table); + } elseif (isset($options['alias'][$table])) { + $item[] = $this->parseKey($query, $table) . ' ' . $this->parseKey($query, $options['alias'][$table]); } else { - $table = $this->connection->parseSqlTable($table); - - if (isset($options['alias'][$table])) { - $item[] = $this->parseKey($query, $table) . ' ' . $this->parseKey($query, $options['alias'][$table]); - } else { - $item[] = $this->parseKey($query, $table); - } + $item[] = $this->parseKey($query, $table); } } @@ -254,11 +248,11 @@ abstract class Builder /** * where分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $where 查询条件 + * @param Query $query 查询对象 + * @param mixed $where 查询条件 * @return string */ - protected function parseWhere(Query $query, $where) + protected function parseWhere(Query $query, array $where): string { $options = $query->getOptions(); $whereStr = $this->buildWhere($query, $where); @@ -267,7 +261,7 @@ abstract class Builder // 附加软删除条件 list($field, $condition) = $options['soft_delete']; - $binds = $this->connection->getFieldsBind($options['table']); + $binds = $query->getFieldBindType(); $whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : ''; $whereStr = $whereStr . $this->parseWhereItem($query, $field, $condition, '', $binds); } @@ -278,19 +272,19 @@ abstract class Builder /** * 生成查询条件SQL * @access public - * @param Query $query 查询对象 - * @param mixed $where - * @param array $options + * @param Query $query 查询对象 + * @param mixed $where 查询条件 * @return string */ - public function buildWhere(Query $query, $where) + public function buildWhere(Query $query, array $where): string { if (empty($where)) { $where = []; } $whereStr = ''; - $binds = $this->connection->getFieldsBind($query->getOptions('table')); + + $binds = $query->getFieldBindType(); foreach ($where as $logic => $val) { $str = []; @@ -361,7 +355,7 @@ abstract class Builder } // where子单元分析 - protected function parseWhereItem(Query $query, $field, $val, $rule = '', $binds = []) + protected function parseWhereItem(Query $query, $field, $val, $rule = '', array $binds = []): string { // 字段分析 $key = $field ? $this->parseKey($query, $field, true) : ''; @@ -404,22 +398,20 @@ abstract class Builder $value = $value->__toString(); } - if (strpos($field, '->')) { - $jsonType = $query->getJsonFieldType($field); - $bindType = $this->connection->getFieldBindType($jsonType); - } else { - $bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR; - } + $bindType = $binds[$field] ?? PDO::PARAM_STR; if (is_scalar($value) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) { - $name = $query->bind($value, $bindType); - $value = ':' . $name; + if (is_string($value) && 0 === strpos($value, ':') && $query->isBind(substr($value, 1))) { + } else { + $name = $query->bind($value, $bindType); + $value = ':' . $name; + } } // 解析查询表达式 foreach ($this->parser as $fun => $parse) { if (in_array($exp, $parse)) { - $whereStr = $this->$fun($query, $key, $exp, $value, $field, $bindType, isset($val[2]) ? $val[2] : 'AND'); + $whereStr = $this->$fun($query, $key, $exp, $value, $field, $bindType, $val[2] ?? 'AND'); break; } } @@ -443,7 +435,7 @@ abstract class Builder * @param string $logic * @return string */ - protected function parseLike(Query $query, $key, $exp, $value, $field, $bindType, $logic) + protected function parseLike(Query $query, string $key, string $exp, $value, string $field, int $bindType, string $logic): string { // 模糊匹配 if (is_array($value)) { @@ -471,7 +463,7 @@ abstract class Builder * @param integer $bindType * @return string */ - protected function parseExp(Query $query, $key, $exp, Expression $value, $field, $bindType) + protected function parseExp(Query $query, string $key, string $exp, Expression $value, string $field, int $bindType): string { // 表达式查询 return '( ' . $key . ' ' . $value->getValue() . ' )'; @@ -488,30 +480,30 @@ abstract class Builder * @param integer $bindType * @return string */ - protected function parseColumn(Query $query, $key, $exp, array $value, $field, $bindType) + protected function parseColumn(Query $query, string $key, $exp, array $value, string $field, int $bindType): string { // 字段比较查询 - list($op, $field2) = $value; + list($op, $field) = $value; - if (!in_array($op, ['=', '<>', '>', '>=', '<', '<='])) { + if (!in_array(trim($op), ['=', '<>', '>', '>=', '<', '<='])) { throw new Exception('where express error:' . var_export($value, true)); } - return '( ' . $key . ' ' . $op . ' ' . $this->parseKey($query, $field2, true) . ' )'; + return '( ' . $key . ' ' . $op . ' ' . $this->parseKey($query, $field, true) . ' )'; } /** * Null查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseNull(Query $query, $key, $exp, $value, $field, $bindType) + protected function parseNull(Query $query, string $key, string $exp, $value, string $field, int $bindType): string { // NULL 查询 return $key . ' IS ' . $exp; @@ -520,15 +512,15 @@ abstract class Builder /** * 范围查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseBetween(Query $query, $key, $exp, $value, $field, $bindType) + protected function parseBetween(Query $query, string $key, string $exp, $value, string $field, int $bindType): string { // BETWEEN 查询 $data = is_array($value) ? $value : explode(',', $value); @@ -542,15 +534,15 @@ abstract class Builder /** * Exists查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseExists(Query $query, $key, $exp, $value, $field, $bindType) + protected function parseExists(Query $query, string $key, string $exp, $value, string $field, int $bindType): string { // EXISTS 查询 if ($value instanceof \Closure) { @@ -567,15 +559,15 @@ abstract class Builder /** * 时间比较查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseTime(Query $query, $key, $exp, $value, $field, $bindType) + protected function parseTime(Query $query, string $key, string $exp, $value, string $field, int $bindType): string { return $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($query, $value, $field, $bindType); } @@ -583,15 +575,15 @@ abstract class Builder /** * 大小比较查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseCompare(Query $query, $key, $exp, $value, $field, $bindType) + protected function parseCompare(Query $query, string $key, string $exp, $value, string $field, int $bindType): string { if (is_array($value)) { throw new Exception('where express error:' . $exp . var_export($value, true)); @@ -608,15 +600,15 @@ abstract class Builder /** * 时间范围查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseBetweenTime(Query $query, $key, $exp, $value, $field, $bindType) + protected function parseBetweenTime(Query $query, string $key, string $exp, $value, string $field, int $bindType): string { if (is_string($value)) { $value = explode(',', $value); @@ -632,25 +624,28 @@ abstract class Builder /** * IN查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseIn(Query $query, $key, $exp, $value, $field, $bindType) + protected function parseIn(Query $query, string $key, string $exp, $value, string $field, int $bindType): string { // IN 查询 if ($value instanceof \Closure) { $value = $this->parseClosure($query, $value, false); + } elseif ($value instanceof Expression) { + $value = $value->getValue(); } else { $value = array_unique(is_array($value) ? $value : explode(',', $value)); $array = []; - foreach ($value as $k => $v) { + foreach ($value as $v) { + $name = $query->bind($v, $bindType); $array[] = ':' . $name; } @@ -666,12 +661,12 @@ abstract class Builder /** * 闭包子查询 * @access protected - * @param Query $query 查询对象 - * @param \Closure $call - * @param bool $show + * @param Query $query 查询对象 + * @param \Closure $call + * @param bool $show * @return string */ - protected function parseClosure(Query $query, $call, $show = true) + protected function parseClosure(Query $query, \Closure $call, bool $show = true): string { $newQuery = $query->newQuery()->setConnection($this->connection); $call($newQuery); @@ -682,13 +677,13 @@ abstract class Builder /** * 日期时间条件解析 * @access protected - * @param Query $query 查询对象 - * @param string $value - * @param string $key - * @param integer $bindType + * @param Query $query 查询对象 + * @param mixed $value + * @param string $key + * @param integer $bindType * @return string */ - protected function parseDateTime(Query $query, $value, $key, $bindType = null) + protected function parseDateTime(Query $query, $value, string $key, int $bindType): string { $options = $query->getOptions(); @@ -731,11 +726,11 @@ abstract class Builder /** * limit分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $limit + * @param Query $query 查询对象 + * @param mixed $limit * @return string */ - protected function parseLimit(Query $query, $limit) + protected function parseLimit(Query $query, string $limit): string { return (!empty($limit) && false === strpos($limit, '(')) ? ' LIMIT ' . $limit . ' ' : ''; } @@ -743,35 +738,28 @@ abstract class Builder /** * join分析 * @access protected - * @param Query $query 查询对象 - * @param array $join + * @param Query $query 查询对象 + * @param array $join * @return string */ - protected function parseJoin(Query $query, $join) + protected function parseJoin(Query $query, array $join): string { $joinStr = ''; - if (!empty($join)) { - foreach ($join as $item) { - list($table, $type, $on) = $item; + foreach ($join as $item) { + list($table, $type, $on) = $item; - $condition = []; + if (strpos($on, '=')) { + list($val1, $val2) = explode('=', $on, 2); - foreach ((array) $on as $val) { - if ($val instanceof Expression) { - $condition[] = $val->getValue(); - } elseif (strpos($val, '=')) { - list($val1, $val2) = explode('=', $val, 2); - $condition[] = $this->parseKey($query, $val1) . '=' . $this->parseKey($query, $val2); - } else { - $condition[] = $val; - } - } + $condition = $this->parseKey($query, $val1) . '=' . $this->parseKey($query, $val2); + } else { + $condition = $on; + } - $table = $this->parseTable($query, $table); + $table = $this->parseTable($query, $table); - $joinStr .= ' ' . $type . ' JOIN ' . $table . ' ON ' . implode(' AND ', $condition); - } + $joinStr .= ' ' . $type . ' JOIN ' . $table . ' ON ' . $condition; } return $joinStr; @@ -780,11 +768,11 @@ abstract class Builder /** * order分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $order + * @param Query $query 查询对象 + * @param array $order * @return string */ - protected function parseOrder(Query $query, $order) + protected function parseOrder(Query $query, array $order): string { foreach ($order as $key => $val) { if ($val instanceof Expression) { @@ -817,11 +805,11 @@ abstract class Builder * orderField分析 * @access protected * @param Query $query 查询对象 - * @param mixed $key + * @param string $key * @param array $val * @return string */ - protected function parseOrderField($query, $key, $val) + protected function parseOrderField(Query $query, string $key, array $val): string { if (isset($val['sort'])) { $sort = $val['sort']; @@ -833,11 +821,10 @@ abstract class Builder $sort = strtoupper($sort); $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : ''; - $options = $query->getOptions(); - $bind = $this->connection->getFieldsBind($options['table']); + $bind = $query->getFieldBindType(); - foreach ($val as $k => $item) { - $val[$k] = $this->parseDataBind($query, $key, $item, $bind); + foreach ($val as $item) { + $val[] = $this->parseDataBind($query, $key, $item, $bind); } return 'field(' . $this->parseKey($query, $key, true) . ',' . implode(',', $val) . ')' . $sort; @@ -846,23 +833,35 @@ abstract class Builder /** * group分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $group + * @param Query $query 查询对象 + * @param mixed $group * @return string */ - protected function parseGroup(Query $query, $group) + protected function parseGroup(Query $query, $group): string { - return !empty($group) ? ' GROUP BY ' . $this->parseKey($query, $group) : ''; + if (empty($group)) { + return ''; + } + + if (is_string($group)) { + $group = explode(',', $group); + } + + foreach ($group as $key) { + $val[] = $this->parseKey($query, $key); + } + + return ' GROUP BY ' . implode(',', $val); } /** * having分析 * @access protected - * @param Query $query 查询对象 - * @param string $having + * @param Query $query 查询对象 + * @param string $having * @return string */ - protected function parseHaving(Query $query, $having) + protected function parseHaving(Query $query, string $having): string { return !empty($having) ? ' HAVING ' . $having : ''; } @@ -874,7 +873,7 @@ abstract class Builder * @param string $comment * @return string */ - protected function parseComment(Query $query, $comment) + protected function parseComment(Query $query, string $comment): string { if (false !== strpos($comment, '*/')) { $comment = strstr($comment, '*/', true); @@ -886,11 +885,11 @@ abstract class Builder /** * distinct分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $distinct + * @param Query $query 查询对象 + * @param mixed $distinct * @return string */ - protected function parseDistinct(Query $query, $distinct) + protected function parseDistinct(Query $query, bool $distinct): string { return !empty($distinct) ? ' DISTINCT ' : ''; } @@ -898,11 +897,11 @@ abstract class Builder /** * union分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $union + * @param Query $query 查询对象 + * @param array $union * @return string */ - protected function parseUnion(Query $query, $union) + protected function parseUnion(Query $query, array $union): string { if (empty($union)) { return ''; @@ -915,7 +914,7 @@ abstract class Builder if ($u instanceof \Closure) { $sql[] = $type . ' ' . $this->parseClosure($query, $u); } elseif (is_string($u)) { - $sql[] = $type . ' ( ' . $this->connection->parseSqlTable($u) . ' )'; + $sql[] = $type . ' ( ' . $u . ' )'; } } @@ -925,18 +924,18 @@ abstract class Builder /** * index分析,可在操作链中指定需要强制使用的索引 * @access protected - * @param Query $query 查询对象 - * @param mixed $index + * @param Query $query 查询对象 + * @param mixed $index * @return string */ - protected function parseForce(Query $query, $index) + protected function parseForce(Query $query, $index): string { if (empty($index)) { return ''; } if (is_array($index)) { - $index = join(",", $index); + $index = join(',', $index); } return sprintf(" FORCE INDEX ( %s ) ", $index); @@ -945,15 +944,17 @@ abstract class Builder /** * 设置锁机制 * @access protected - * @param Query $query 查询对象 - * @param bool|string $lock + * @param Query $query 查询对象 + * @param bool|string $lock * @return string */ - protected function parseLock(Query $query, $lock = false) + protected function parseLock(Query $query, $lock = false): string { if (is_bool($lock)) { return $lock ? ' FOR UPDATE ' : ''; - } elseif (is_string($lock) && !empty($lock)) { + } + + if (is_string($lock) && !empty($lock)) { return ' ' . trim($lock) . ' '; } } @@ -961,10 +962,11 @@ abstract class Builder /** * 生成查询SQL * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 + * @param bool $one 是否仅获取一个记录 * @return string */ - public function select(Query $query) + public function select(Query $query, bool $one = false): string { $options = $query->getOptions(); @@ -979,7 +981,7 @@ abstract class Builder $this->parseGroup($query, $options['group']), $this->parseHaving($query, $options['having']), $this->parseOrder($query, $options['order']), - $this->parseLimit($query, $options['limit']), + $this->parseLimit($query, $one ? '1' : $options['limit']), $this->parseUnion($query, $options['union']), $this->parseLock($query, $options['lock']), $this->parseComment($query, $options['comment']), @@ -991,11 +993,11 @@ abstract class Builder /** * 生成Insert SQL * @access public - * @param Query $query 查询对象 - * @param bool $replace 是否replace + * @param Query $query 查询对象 + * @param bool $replace 是否replace * @return string */ - public function insert(Query $query, $replace = false) + public function insert(Query $query, bool $replace = false): string { $options = $query->getOptions(); @@ -1023,12 +1025,12 @@ abstract class Builder /** * 生成insertall SQL * @access public - * @param Query $query 查询对象 - * @param array $dataSet 数据集 - * @param bool $replace 是否replace + * @param Query $query 查询对象 + * @param array $dataSet 数据集 + * @param bool $replace 是否replace * @return string */ - public function insertAll(Query $query, $dataSet, $replace = false) + public function insertAll(Query $query, array $dataSet, bool $replace = false): string { $options = $query->getOptions(); @@ -1038,11 +1040,12 @@ abstract class Builder } else { $allowFields = $options['field']; } + // 获取绑定信息 - $bind = $this->connection->getFieldsBind($options['table']); + $bind = $query->getFieldBindType(); - foreach ($dataSet as $data) { - $data = $this->parseData($query, $data, $allowFields, $bind); + foreach ($dataSet as $k => $data) { + $data = $this->parseData($query, $data, $allowFields, $bind, '_' . $k); $values[] = 'SELECT ' . implode(',', array_values($data)); @@ -1072,17 +1075,13 @@ abstract class Builder /** * 生成slect insert SQL * @access public - * @param Query $query 查询对象 - * @param array $fields 数据 - * @param string $table 数据表 + * @param Query $query 查询对象 + * @param array $fields 数据 + * @param string $table 数据表 * @return string */ - public function selectInsert(Query $query, $fields, $table) + public function selectInsert(Query $query, array $fields, string $table): string { - if (is_string($fields)) { - $fields = explode(',', $fields); - } - foreach ($fields as &$field) { $field = $this->parseKey($query, $field, true); } @@ -1093,10 +1092,10 @@ abstract class Builder /** * 生成update SQL * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return string */ - public function update(Query $query) + public function update(Query $query): string { $options = $query->getOptions(); @@ -1129,10 +1128,10 @@ abstract class Builder /** * 生成delete SQL * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return string */ - public function delete(Query $query) + public function delete(Query $query): string { $options = $query->getOptions(); diff --git a/src/db/Connection.php b/src/db/Connection.php index 05f0b01..c5246b7 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -2,26 +2,30 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\db; -use Exception; use InvalidArgumentException; use PDO; use PDOStatement; +use think\cache\CacheItem; use think\Db; -use think\db\exception\BindParamException; -use think\db\exception\PDOException; +use think\Debug; +use think\Exception; +use think\exception\BindParamException; +use think\exception\PDOException; abstract class Connection { - const PARAM_FLOAT = 21; + const PARAM_FLOAT = 21; + protected static $instance = []; /** @var PDOStatement PDO操作实例 */ protected $PDOStatement; @@ -35,7 +39,14 @@ abstract class Connection // 错误信息 protected $error = ''; + /** + * 查询次数 + * @var integer + */ + protected static $queryTimes = 0; + protected $queryStartTime; + /** @var PDO[] 数据库连接ID 支持多个连接 */ protected $links = []; @@ -55,7 +66,7 @@ abstract class Connection // 数据表信息 protected static $info = []; - // 数据库日志 + // 数据表信息 protected static $log = []; // 使用Builder类 @@ -94,10 +105,10 @@ abstract class Connection 'master_num' => 1, // 指定从服务器序号 'slave_no' => '', + // 模型写入后自动读取主服务器 + 'read_master' => false, // 是否严格检查字段是否存在 'fields_strict' => true, - // 数据集返回类型 - 'resultset_type' => '', // 自动写入时间戳字段 'auto_timestamp' => false, // 时间字段取出后的默认时间格式 @@ -110,9 +121,9 @@ abstract class Connection 'query' => '\\think\\db\\Query', // 是否需要断线重连 'break_reconnect' => false, - // 数据字段缓存路径 + // 断线标识字符串 + 'break_match_str' => [], 'schema_path' => '', - // 模型类后缀 'class_suffix' => false, ]; @@ -125,15 +136,41 @@ abstract class Connection PDO::ATTR_EMULATE_PREPARES => false, ]; + // 参数绑定类型映射 + protected $bindType = [ + 'string' => PDO::PARAM_STR, + 'str' => PDO::PARAM_STR, + 'integer' => PDO::PARAM_INT, + 'int' => PDO::PARAM_INT, + 'boolean' => PDO::PARAM_BOOL, + 'bool' => PDO::PARAM_BOOL, + 'float' => self::PARAM_FLOAT, + ]; + + // 服务器断线标识字符 + protected $breakMatchStr = [ + 'server has gone away', + 'no connection to the server', + 'Lost connection', + 'is dead or not enabled', + 'Error while sending', + 'decryption failed or bad record mac', + 'server closed the connection unexpectedly', + 'SSL connection has been closed unexpectedly', + 'Error writing data to the connection', + 'Resource deadlock avoided', + 'failed with errno', + ]; + // 绑定参数 protected $bind = []; /** * 架构函数 读取数据库配置信息 - * @access protected - * @param array $config 数据库配置数组 + * @access public + * @param array $config 数据库配置数组 */ - protected function __construct(array $config = []) + public function __construct(array $config = []) { if (!empty($config)) { $this->config = array_merge($this->config, $config); @@ -144,7 +181,6 @@ abstract class Connection $this->builder = new $class($this); $this->cache = Db::getCacheHandler(); - // 执行初始化操作 $this->initialize(); } @@ -154,40 +190,35 @@ abstract class Connection * @access protected * @return void */ - protected function initialize() + protected function initialize(): void {} /** * 取得数据库连接类实例 * @access public - * @param mixed $config 连接配置 - * @param bool|string $name 连接标识 true 强制重新连接 + * @param mixed $config 连接配置 + * @param bool|string $name 连接标识 true 强制重新连接 * @return Connection * @throws Exception */ - public static function instance($config = [], $name = false) + public static function instance($config = [], $name = false): Connection { if (false === $name) { $name = md5(serialize($config)); } if (true === $name || !isset(self::$instance[$name])) { - // 解析连接参数 支持数组和字符串 - $options = self::parseConfig($config); - if (empty($options['type'])) { + if (empty($config['type'])) { throw new InvalidArgumentException('Undefined db type'); } - $class = false !== strpos($options['type'], '\\') ? $options['type'] : '\\think\\db\\connector\\' . ucwords($options['type']); - // 记录初始化信息 - self::$log[] = '[ DB ] INIT ' . $options['type']; - if (true === $name) { $name = md5(serialize($config)); } + $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\db\\connector\\' . ucwords($config['type']); - self::$instance[$name] = new $class($options); + self::$instance[$name] = new $class($config); } return self::$instance[$name]; @@ -198,7 +229,7 @@ abstract class Connection * @access public * @return string */ - public function getBuilderClass() + public function getBuilderClass(): string { if (!empty($this->builderClassName)) { return $this->builderClassName; @@ -210,7 +241,7 @@ abstract class Connection /** * 设置当前的数据库Builder对象 * @access protected - * @param Builder $builder + * @param Builder $builder * @return void */ protected function setBuilder(Builder $builder) @@ -225,36 +256,26 @@ abstract class Connection * @access public * @return Builder */ - public function getBuilder() + public function getBuilder(): Builder { return $this->builder; } - /** - * 获取连接对象 - * @access public - * @return object|null - */ - public function getLinkID() - { - return $this->linkID ?: null; - } - /** * 解析pdo连接的dsn信息 * @access protected - * @param array $config 连接信息 + * @param array $config 连接信息 * @return string */ - abstract protected function parseDsn($config); + abstract protected function parseDsn(array $config); /** * 取得数据表的字段信息 * @access public - * @param string $tableName + * @param string $tableName * @return array */ - abstract public function getFields($tableName); + abstract public function getFields(string $tableName); /** * 取得数据库的表信息 @@ -262,23 +283,23 @@ abstract class Connection * @param string $dbName * @return array */ - abstract public function getTables($dbName); + abstract public function getTables(string $dbName); /** * SQL性能分析 * @access protected - * @param string $sql + * @param string $sql * @return array */ - abstract protected function getExplain($sql); + abstract protected function getExplain(string $sql); /** * 对返数据表字段信息进行大小写转换出来 * @access public - * @param array $info 字段信息 + * @param array $info 字段信息 * @return array */ - public function fieldCase($info) + public function fieldCase(array $info): array { // 字段大小写转换 switch ($this->attrCase) { @@ -299,12 +320,14 @@ abstract class Connection /** * 获取字段绑定类型 * @access public - * @param string $type 字段类型 + * @param string $type 字段类型 * @return integer */ - public function getFieldBindType($type) + public function getFieldBindType(string $type): int { - if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) { + if (in_array($type, ['integer', 'string', 'float', 'boolean', 'bool', 'int', 'str'])) { + $bind = $this->bindType[$type]; + } elseif (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) { $bind = PDO::PARAM_STR; } elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) { $bind = self::PARAM_FLOAT; @@ -319,32 +342,14 @@ abstract class Connection return $bind; } - /** - * 将SQL语句中的__TABLE_NAME__字符串替换成带前缀的表名(小写) - * @access public - * @param string $sql sql语句 - * @return string - */ - public function parseSqlTable($sql) - { - if (false !== strpos($sql, '__')) { - $prefix = $this->getConfig('prefix'); - $sql = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function ($match) use ($prefix) { - return $prefix . strtolower($match[1]); - }, $sql); - } - - return $sql; - } - /** * 获取数据表信息 * @access public - * @param mixed $tableName 数据表名 留空自动获取 - * @param string $fetch 获取信息类型 包括 fields type bind pk + * @param mixed $tableName 数据表名 留空自动获取 + * @param string $fetch 获取信息类型 包括 fields type bind pk * @return mixed */ - public function getTableInfo($tableName, $fetch = '') + public function getTableInfo($tableName, string $fetch = '') { if (is_array($tableName)) { $tableName = key($tableName) ?: current($tableName); @@ -353,8 +358,6 @@ abstract class Connection if (strpos($tableName, ',')) { // 多表不获取字段信息 return false; - } else { - $tableName = $this->parseSqlTable($tableName); } // 修正子查询作为表名的问题 @@ -373,7 +376,8 @@ abstract class Connection if (!isset(self::$info[$schema])) { // 读取缓存 $cacheFile = $this->config['schema_path'] . $schema . '.php'; - if (is_file($cacheFile)) { + + if (!$this->config['debug'] && is_file($cacheFile)) { $info = include $cacheFile; } else { $info = $this->getFields($tableName); @@ -386,6 +390,7 @@ abstract class Connection // 记录字段类型 $type[$key] = $val['type']; $bind[$key] = $this->getFieldBindType($val['type']); + if (!empty($val['primary'])) { $pk[] = $key; } @@ -407,7 +412,7 @@ abstract class Connection /** * 获取数据表的主键 * @access public - * @param string $tableName 数据表名 + * @param string $tableName 数据表名 * @return string|array */ public function getPk($tableName) @@ -415,20 +420,42 @@ abstract class Connection return $this->getTableInfo($tableName, 'pk'); } - // 获取当前数据表字段信息 - public function getTableFields($tableName) + /** + * 获取数据表字段信息 + * @access public + * @param string $tableName 数据表名 + * @return array + */ + public function getTableFields($tableName): array { return $this->getTableInfo($tableName, 'fields'); } - // 获取当前数据表字段类型 - public function getFieldsType($tableName) + /** + * 获取数据表字段类型 + * @access public + * @param string $tableName 数据表名 + * @param string $field 字段名 + * @return array|string + */ + public function getFieldsType($tableName, string $field = null) { - return $this->getTableInfo($tableName, 'type'); + $result = $this->getTableInfo($tableName, 'type'); + + if ($field && isset($result[$field])) { + return $result[$field]; + } + + return $result; } - // 获取当前数据表绑定信息 - public function getFieldsBind($tableName) + /** + * 获取数据表绑定信息 + * @access public + * @param string $tableName 数据表名 + * @return array + */ + public function getFieldsBind($tableName): array { return $this->getTableInfo($tableName, 'bind'); } @@ -436,40 +463,35 @@ abstract class Connection /** * 获取数据库的配置参数 * @access public - * @param string $config 配置名称 + * @param string $config 配置名称 * @return mixed */ - public function getConfig($config = '') + public function getConfig(string $config = '') { - return $config ? $this->config[$config] : $this->config; + return $config ? ($this->config[$config] ?? null) : $this->config; } /** * 设置数据库的配置参数 * @access public - * @param string|array $config 配置名称 - * @param mixed $value 配置值 + * @param array $config 配置 * @return void */ - public function setConfig($config, $value = '') + public function setConfig($config): void { - if (is_array($config)) { - $this->config = array_merge($this->config, $config); - } else { - $this->config[$config] = $value; - } + $this->config = array_merge($this->config, $config); } /** * 连接数据库方法 * @access public - * @param array $config 连接参数 - * @param integer $linkNum 连接序号 - * @param array|bool $autoConnection 是否自动连接主数据库(用于分布式) + * @param array $config 连接参数 + * @param integer $linkNum 连接序号 + * @param array|bool $autoConnection 是否自动连接主数据库(用于分布式) * @return PDO * @throws Exception */ - public function connect(array $config = [], $linkNum = 0, $autoConnection = false) + public function connect(array $config = [], $linkNum = 0, $autoConnection = false): PDO { if (isset($this->links[$linkNum])) { return $this->links[$linkNum]; @@ -491,6 +513,10 @@ abstract class Connection // 记录当前字段属性大小写设置 $this->attrCase = $params[PDO::ATTR_CASE]; + if (!empty($config['break_match_str'])) { + $this->breakMatchStr = array_merge($this->breakMatchStr, (array) $config['break_match_str']); + } + try { if (empty($config['dsn'])) { $config['dsn'] = $this->parseDsn($config); @@ -510,7 +536,7 @@ abstract class Connection return $this->links[$linkNum]; } catch (\PDOException $e) { if ($autoConnection) { - $this->log('[ ERR ] ' . $e->getMessage()); + $this->log($e->getMessage(), 'error'); return $this->connect($autoConnection, $linkNum); } else { throw $e; @@ -522,7 +548,7 @@ abstract class Connection * 释放查询结果 * @access public */ - public function free() + public function free(): void { $this->PDOStatement = null; } @@ -544,57 +570,21 @@ abstract class Connection /** * 执行查询 使用生成器返回数据 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param bool $master 是否在主服务器读操作 - * @param Model $model 模型对象实例 - * @param array $condition 查询条件 - * @param mixed $relation 关联查询 + * @param Query $query 查询对象 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param Model $model 模型对象实例 + * @param array $condition 查询条件 * @return \Generator */ - public function getCursor($sql, $bind = [], $master = false, $model = null, $condition = null, $relation = null) + public function getCursor(Query $query, string $sql, array $bind = [], $model = null, $condition = null) { - $this->initConnect($master); - - // 记录SQL语句 - $this->queryStr = $sql; - - $this->bind = $bind; - - Db::$queryTimes++; - - // 调试开始 - $this->debug(true); - - // 预处理 - $this->PDOStatement = $this->linkID->prepare($sql); - - // 是否为存储过程调用 - $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); - - // 参数绑定 - if ($procedure) { - $this->bindParam($bind); - } else { - $this->bindValue($bind); - } - - // 执行查询 - $this->PDOStatement->execute(); - - // 调试结束 - $this->debug(false, '', $master); + $this->queryPDOStatement($query, $sql, $bind); // 返回结果集 while ($result = $this->PDOStatement->fetch($this->fetchType)) { if ($model) { - $instance = $model->newInstance($result, $condition); - - if ($relation) { - $instance->relationQuery($relation); - } - - yield $instance; + yield $model->newInstance($result, $condition); } else { yield $result; } @@ -604,29 +594,84 @@ abstract class Connection /** * 执行查询 返回数据集 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param bool $master 是否在主服务器读操作 - * @param bool $pdo 是否返回PDO对象 + * @param Query $query 查询对象 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param bool $cache 是否支持缓存 * @return array * @throws BindParamException * @throws \PDOException * @throws \Exception + * @throws \Throwable */ - public function query($sql, $bind = [], $master = false, $pdo = false) + public function query(Query $query, string $sql, array $bind = [], bool $cache = false): array { - $this->initConnect($master); + // 分析查询表达式 + $options = $query->parseOptions(); - if (!$this->linkID) { - return false; + if ($cache && !empty($options['cache'])) { + $cacheItem = $options['cache']; + $resultSet = $this->cache->get($cacheItem->getKey()); + + if (false !== $resultSet) { + return $resultSet; + } + } + + $master = !empty($options['master']) ? true : false; + $procedure = !empty($options['procedure']) ? true : in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); + + $this->getPDOStatement($sql, $bind, $master, $procedure); + + $resultSet = $this->getResult($procedure); + + if (isset($cacheItem) && $resultSet) { + // 缓存数据集 + $cacheItem->set($resultSet); + $this->cacheData($cacheItem); } + return $resultSet; + } + + /** + * 执行查询但只返回PDOStatement对象 + * @access public + * @param Query $query 查询对象 + * @return \PDOStatement + */ + public function pdo(Query $query) + { + $bind = $query->getBind(); + // 生成查询SQL + $sql = $this->builder->select($query); + + return $this->queryPDOStatement($query, $sql, $bind); + } + + /** + * 执行查询但只返回PDOStatement对象 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param bool $master 是否在主服务器读操作 + * @param bool $procedure 是否为存储过程调用 + * @return PDOStatement + * @throws BindParamException + * @throws \PDOException + * @throws \Exception + * @throws \Throwable + */ + public function getPDOStatement(string $sql, array $bind = [], bool $master = false, bool $procedure = false): PDOStatement + { + $this->initConnect($master); + // 记录SQL语句 $this->queryStr = $sql; $this->bind = $bind; - Db::$queryTimes++; + self::$queryTimes++; try { // 调试开始 @@ -635,9 +680,6 @@ abstract class Connection // 预处理 $this->PDOStatement = $this->linkID->prepare($sql); - // 是否为存储过程调用 - $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); - // 参数绑定 if ($procedure) { $this->bindParam($bind); @@ -651,110 +693,55 @@ abstract class Connection // 调试结束 $this->debug(false, '', $master); - // 返回结果集 - return $this->getResult($pdo, $procedure); - } catch (\PDOException $e) { - if ($this->isBreak($e)) { - return $this->close()->query($sql, $bind, $master, $pdo); - } - - throw new PDOException($e, $this->config, $this->getLastsql()); - } catch (\Throwable $e) { + return $this->PDOStatement; + } catch (\Throwable | \Exception $e) { if ($this->isBreak($e)) { - return $this->close()->query($sql, $bind, $master, $pdo); + return $this->close()->getPDOStatement($sql, $bind, $master, $procedure); } - throw $e; - } catch (\Exception $e) { - if ($this->isBreak($e)) { - return $this->close()->query($sql, $bind, $master, $pdo); + if ($e instanceof \PDOException) { + throw new PDOException($e, $this->config, $this->getLastsql()); + } else { + throw $e; } - - throw $e; } } /** * 执行语句 * @access public + * @param Query $query 查询对象 * @param string $sql sql指令 * @param array $bind 参数绑定 - * @param Query $query 查询对象 * @return int * @throws BindParamException * @throws \PDOException * @throws \Exception + * @throws \Throwable */ - public function execute($sql, $bind = [], Query $query = null) + public function execute(Query $query, string $sql, array $bind = []): int { - $this->initConnect(true); - - if (!$this->linkID) { - return false; - } - - // 记录SQL语句 - $this->queryStr = $sql; + $this->queryPDOStatement($query->master(true), $sql, $bind); - $this->bind = $bind; - - Db::$executeTimes++; - try { - // 调试开始 - $this->debug(true); - - // 预处理 - $this->PDOStatement = $this->linkID->prepare($sql); - - // 是否为存储过程调用 - $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); - - // 参数绑定 - if ($procedure) { - $this->bindParam($bind); - } else { - $this->bindValue($bind); - } - - // 执行语句 - $this->PDOStatement->execute(); - - // 调试结束 - $this->debug(false, '', true); - - if ($query && !empty($this->config['deploy']) && !empty($this->config['read_master'])) { - $query->readMaster(); - } - - $this->numRows = $this->PDOStatement->rowCount(); - - return $this->numRows; - } catch (\PDOException $e) { - if ($this->isBreak($e)) { - return $this->close()->execute($sql, $bind, $query); - } + $this->numRows = $this->PDOStatement->rowCount(); - throw new PDOException($e, $this->config, $this->getLastsql()); - } catch (\Throwable $e) { - if ($this->isBreak($e)) { - return $this->close()->execute($sql, $bind, $query); - } + return $this->numRows; + } - throw $e; - } catch (\Exception $e) { - if ($this->isBreak($e)) { - return $this->close()->execute($sql, $bind, $query); - } + protected function queryPDOStatement(Query $query, string $sql, array $bind = []) + { + $options = $query->parseOptions(); + $master = !empty($options['master']) ? true : false; + $procedure = !empty($options['procedure']) ? true : in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); - throw $e; - } + return $this->getPDOStatement($sql, $bind, $master, $procedure); } /** * 查找单条记录 * @access public - * @param Query $query 查询对象 - * @return array|null|\PDOStatement|string + * @param Query $query 查询对象 + * @return array|null * @throws DbException * @throws ModelNotFoundException * @throws DataNotFoundException @@ -762,28 +749,20 @@ abstract class Connection public function find(Query $query) { // 分析查询表达式 - $options = $query->getOptions(); + $options = $query->parseOptions(); $pk = $query->getPk($options); + $data = $options['data']; - $data = $options['data']; - - $query->setOption('limit', 1); - - if ($this->cache && empty($options['fetch_sql']) && !empty($options['cache'])) { + if ($this->cache && !empty($options['cache'])) { // 判断查询缓存 - $cache = $options['cache']; + $cacheItem = $options['cache']; - if (is_string($cache['key'])) { - $key = $cache['key']; - } elseif (!isset($key)) { + if (!$cacheItem->getKey()) { $key = $this->getCacheKey($query, $data); + $cacheItem->setKey($key); } - $result = $this->cache->get($key); - - if (false !== $result) { - return $result; - } + $options['key'] = $cacheItem->getKey(); } if (is_string($pk) && !is_array($data)) { @@ -795,37 +774,34 @@ abstract class Connection } $data = $item; } - $query->setOption('data', $data); - // 生成查询SQL - $sql = $this->builder->select($query); - - $query->removeOption('limit'); + $query->setOption('data', $data); - $bind = $query->getBind(); + if ($this->cache && isset($options['key'])) { + $result = $this->cache->get($options['key']); - if (!empty($options['fetch_sql'])) { - // 获取实际执行的SQL语句 - return $this->getRealSql($sql, $bind); + if (false !== $result) { + return $result; + } } + // 生成查询SQL + $sql = $this->builder->select($query, true); + // 事件回调 - if ($result = $query->trigger('before_find')) { - } else { - // 执行查询 - $resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_pdo']); + $result = $query->trigger('before_find'); - if ($resultSet instanceof \PDOStatement) { - // 返回PDOStatement对象 - return $resultSet; - } + if (!$result) { + // 执行查询 + $resultSet = $this->query($query, $sql, $query->getBind()); - $result = isset($resultSet[0]) ? $resultSet[0] : null; + $result = $resultSet[0] ?? null; } - if (isset($cache) && $result) { + if ($this->cache && isset($cacheItem) && $result) { // 缓存数据 - $this->cacheData($key, $result, $cache); + $cacheItem->set($result); + $this->cacheData($cacheItem); } return $result; @@ -834,90 +810,60 @@ abstract class Connection /** * 使用游标查询记录 * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return \Generator */ public function cursor(Query $query) { // 分析查询表达式 - $options = $query->getOptions(); + $options = $query->parseOptions(); // 生成查询SQL $sql = $this->builder->select($query); - $bind = $query->getBind(); - - $condition = isset($options['where']['AND']) ? $options['where']['AND'] : null; - $relation = isset($options['relaltion']) ? $options['relation'] : null; + $condition = $options['where']['AND'] ?? null; // 执行查询操作 - return $this->getCursor($sql, $bind, $options['master'], $query->getModel(), $condition, $relation); - } - - /** - * 获取缓存数据 - * @access protected - * @param Query $query 查询对象 - * @param mixed $cache 缓存设置 - * @param array $options 缓存 - * @return mixed - */ - protected function getCacheData(Query $query, $cache, $data, &$key = null) - { - // 判断查询缓存 - $key = is_string($cache['key']) ? $cache['key'] : $this->getCacheKey($query, $data); - - return $this->cache->get($key); + return $this->getCursor($sql, $query->getBind(), $options['master'], $query->getModel(), $condition); } /** * 查找记录 * @access public - * @param Query $query 查询对象 - * @return array|\PDOStatement|string + * @param Query $query 查询对象 + * @return array * @throws DbException * @throws ModelNotFoundException * @throws DataNotFoundException */ - public function select(Query $query) + public function select(Query $query): array { // 分析查询表达式 - $options = $query->getOptions(); + $options = $query->parseOptions(); - if ($this->cache && empty($options['fetch_sql']) && !empty($options['cache'])) { - // 判断查询缓存 - $resultSet = $this->getCacheData($query, $options['cache'], null, $key); + if (!empty($options['cache'])) { + $cacheItem = $options['cache']; + $resultSet = $this->getCacheData($cacheItem); if (false !== $resultSet) { return $resultSet; } - } // 生成查询SQL $sql = $this->builder->select($query); - $bind = $query->getBind(); - - if (!empty($options['fetch_sql'])) { - // 获取实际执行的SQL语句 - return $this->getRealSql($sql, $bind); - } + $resultSet = $query->trigger('before_select'); - if ($resultSet = $query->trigger('before_select')) { - } else { + if (!$resultSet) { // 执行查询操作 - $resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_pdo']); - - if ($resultSet instanceof \PDOStatement) { - // 返回PDOStatement对象 - return $resultSet; - } + $resultSet = $this->query($query, $sql, $query->getBind()); } - if ($this->cache && !empty($options['cache']) && false !== $resultSet) { + if (isset($cacheItem) && false !== $resultSet) { // 缓存数据集 - $this->cacheData($key, $resultSet, $options['cache']); + $cacheItem->set($resultSet); + $this->cacheData($cacheItem); } return $resultSet; @@ -926,32 +872,25 @@ abstract class Connection /** * 插入记录 * @access public - * @param Query $query 查询对象 - * @param boolean $replace 是否replace - * @param boolean $getLastInsID 返回自增主键 - * @param string $sequence 自增序列名 - * @return integer|string + * @param Query $query 查询对象 + * @param boolean $replace 是否replace + * @param boolean $getLastInsID 返回自增主键 + * @param string $sequence 自增序列名 + * @return integer */ - public function insert(Query $query, $replace = false, $getLastInsID = false, $sequence = null) + public function insert(Query $query, bool $replace = false, bool $getLastInsID = false, string $sequence = null) { // 分析查询表达式 - $options = $query->getOptions(); + $options = $query->parseOptions(); // 生成SQL语句 $sql = $this->builder->insert($query, $replace); - $bind = $query->getBind(); - - if (!empty($options['fetch_sql'])) { - // 获取实际执行的SQL语句 - return $this->getRealSql($sql, $bind); - } - // 执行操作 - $result = '' == $sql ? 0 : $this->execute($sql, $bind, $query); + $result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind()); if ($result) { - $sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null); + $sequence = $sequence ?: ($options['sequence'] ?? null); $lastInsId = $this->getLastInsID($sequence); $data = $options['data']; @@ -978,21 +917,21 @@ abstract class Connection /** * 批量插入记录 * @access public - * @param Query $query 查询对象 - * @param mixed $dataSet 数据集 - * @param bool $replace 是否replace - * @param integer $limit 每次写入数据限制 - * @return integer|string + * @param Query $query 查询对象 + * @param mixed $dataSet 数据集 + * @param bool $replace 是否replace + * @param integer $limit 每次写入数据限制 + * @return integer * @throws \Exception * @throws \Throwable */ - public function insertAll(Query $query, $dataSet = [], $replace = false, $limit = null) + public function insertAll(Query $query, array $dataSet = [], bool $replace = false, int $limit = null): int { if (!is_array(reset($dataSet))) { - return false; + return 0; } - $options = $query->getOptions(); + $options = $query->parseOptions(); if ($limit) { // 分批写入 自动启动事务支持 @@ -1003,82 +942,57 @@ abstract class Connection $count = 0; foreach ($array as $item) { - $sql = $this->builder->insertAll($query, $item, $replace); - $bind = $query->getBind(); - if (!empty($options['fetch_sql'])) { - $fetchSql[] = $this->getRealSql($sql, $bind); - } else { - $count += $this->execute($sql, $bind, $query); - } + $sql = $this->builder->insertAll($query, $item, $replace); + $count += $this->execute($query, $sql, $query->getBind()); } // 提交事务 $this->commit(); - } catch (\Exception $e) { - $this->rollback(); - throw $e; - } catch (\Throwable $e) { + } catch (\Exception | \Throwable $e) { $this->rollback(); throw $e; } - return isset($fetchSql) ? implode(';', $fetchSql) : $count; + return $count; } - $sql = $this->builder->insertAll($query, $dataSet, $replace); - $bind = $query->getBind(); + $sql = $this->builder->insertAll($query, $dataSet, $replace); - if (!empty($options['fetch_sql'])) { - // 获取实际执行的SQL语句 - return $this->getRealSql($sql, $bind); - } - // 执行操作 - return $this->execute($sql, $bind, $query); + return $this->execute($query, $sql, $query->getBind()); } /** * 通过Select方式插入记录 * @access public - * @param Query $query 查询对象 - * @param string $fields 要插入的数据表字段名 - * @param string $table 要插入的数据表名 - * @return integer|string + * @param Query $query 查询对象 + * @param array $fields 要插入的数据表字段名 + * @param string $table 要插入的数据表名 + * @return integer * @throws PDOException */ - public function selectInsert(Query $query, $fields, $table) + public function selectInsert(Query $query, array $fields, string $table): int { // 分析查询表达式 - $options = $query->getOptions(); - - // 生成SQL语句 - $table = $this->parseSqlTable($table); - $sql = $this->builder->selectInsert($query, $fields, $table); - $bind = $query->getBind(); - - if (!empty($options['fetch_sql'])) { - // 获取实际执行的SQL语句 - return $this->getRealSql($sql, $bind); - } - // 执行操作 - return $this->execute($sql, $bind, $query); + return $this->execute($query, $sql, $query->getBind()); } /** * 更新记录 * @access public - * @param Query $query 查询对象 - * @return integer|string + * @param Query $query 查询对象 + * @return integer * @throws Exception * @throws PDOException */ - public function update(Query $query) + public function update(Query $query): int { - $options = $query->getOptions(); + $options = $query->parseOptions(); - if (isset($options['cache']) && is_string($options['cache']['key'])) { - $key = $options['cache']['key']; + if ($this->cache && isset($options['cache'])) { + $cacheItem = $options['cache']; + $key = $cacheItem->getKey(); } $pk = $query->getPk($options); @@ -1090,6 +1004,7 @@ abstract class Connection $where[$pk] = [$pk, '=', $data[$pk]]; if (!isset($key)) { $key = $this->getCacheKey($query, $data[$pk]); + } unset($data[$pk]); } elseif (is_array($pk)) { @@ -1124,26 +1039,25 @@ abstract class Connection $query->setOption('data', $data); // 生成UPDATE SQL语句 - $sql = $this->builder->update($query); - $bind = $query->getBind(); - - if (!empty($options['fetch_sql'])) { - // 获取实际执行的SQL语句 - return $this->getRealSql($sql, $bind); - } + $sql = $this->builder->update($query); // 检测缓存 - if ($this->cache && isset($key) && $this->cache->get($key)) { - // 删除缓存 - $this->cache->rm($key); + if ($this->cache) { + if (isset($key) && $this->cache->get($key)) { + // 删除缓存 + $this->cache->delete($key); + } elseif (isset($cacheItem) && $cacheItem->getTag()) { + $this->cache->clear($cacheItem->getTag()); + } + } // 执行操作 - $result = '' == $sql ? 0 : $this->execute($sql, $bind, $query); + $result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind()); if ($result) { - if (is_string($pk) && isset($where[$pk])) { - $data[$pk] = $where[$pk]; + if (is_string($pk) && isset($options['where']['AND'][$pk])) { + $data[$pk] = $options['where']['AND'][$pk]; } elseif (is_string($pk) && isset($key) && strpos($key, '|')) { list($a, $val) = explode('|', $key); $data[$pk] = $val; @@ -1159,28 +1073,25 @@ abstract class Connection /** * 删除记录 * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return int * @throws Exception * @throws PDOException */ - public function delete(Query $query) + public function delete(Query $query): int { // 分析查询表达式 - $options = $query->getOptions(); + $options = $query->parseOptions(); $pk = $query->getPk($options); $data = $options['data']; - if (isset($options['cache']) && is_string($options['cache']['key'])) { - $key = $options['cache']['key']; + if (isset($options['cache'])) { + $cacheItem = $options['cache']; + $key = $cacheItem->getKey(); } elseif (!is_null($data) && true !== $data && !is_array($data)) { $key = $this->getCacheKey($query, $data); - } elseif (is_string($pk) && isset($options['where']['AND'])) { - foreach ($options['where']['AND'] as $val) { - if (is_array($val) && $val[0] == $pk) { - $key = $this->getCacheKey($query, $val); - } - } + } elseif (is_string($pk) && isset($options['where']['AND'][$pk])) { + $key = $this->getCacheKey($query, $options['where']['AND'][$pk]); } if (true !== $data && empty($options['where'])) { @@ -1191,21 +1102,20 @@ abstract class Connection // 生成删除SQL语句 $sql = $this->builder->delete($query); - $bind = $query->getBind(); + // 检测缓存 - if (!empty($options['fetch_sql'])) { - // 获取实际执行的SQL语句 - return $this->getRealSql($sql, $bind); - } + if ($this->cache) { + if (isset($key) && $this->cache->get($key)) { + // 删除缓存 + $this->cache->delete($key); + } elseif (isset($cacheItem) && $cacheItem->getTag()) { + $this->cache->clear($cacheItem->getTag()); + } - // 检测缓存 - if ($this->cache && isset($key) && $this->cache->get($key)) { - // 删除缓存 - $this->cache->rm($key); } // 执行操作 - $result = $this->execute($sql, $bind, $query); + $result = $this->execute($query, $sql, $query->getBind()); if ($result) { if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) { @@ -1225,25 +1135,14 @@ abstract class Connection /** * 得到某个字段的值 * @access public - * @param Query $query 查询对象 - * @param string $field 字段名 - * @param bool $default 默认值 + * @param Query $query 查询对象 + * @param string $field 字段名 + * @param mixed $default 默认值 * @return mixed */ - public function value(Query $query, $field, $default = null) + public function value(Query $query, string $field, $default = null) { - $options = $query->getOptions(); - - if ($this->cache && empty($options['fetch_sql']) && !empty($options['cache'])) { - // 判断查询缓存 - $cache = $options['cache']; - $result = $this->getCacheData($query, $cache, null, $key); - - if (false !== $result) { - return $result; - } - - } + $options = $query->parseOptions(); if (isset($options['field'])) { $query->removeOption('field'); @@ -1254,10 +1153,18 @@ abstract class Connection } $query->setOption('field', $field); - $query->setOption('limit', 1); + + if (!empty($options['cache'])) { + $cacheItem = $options['cache']; + $result = $this->getCacheData($cacheItem); + + if (false !== $result) { + return $result; + } + } // 生成查询SQL - $sql = $this->builder->select($query); + $sql = $this->builder->select($query, true); if (isset($options['field'])) { $query->setOption('field', $options['field']); @@ -1265,95 +1172,90 @@ abstract class Connection $query->removeOption('field'); } - $query->removeOption('limit'); - - $bind = $query->getBind(); - - if (!empty($options['fetch_sql'])) { - // 获取实际执行的SQL语句 - return $this->getRealSql($sql, $bind); - } - // 执行查询操作 - $pdo = $this->query($sql, $bind, $options['master'], true); + $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']); $result = $pdo->fetchColumn(); - if (isset($cache) && false !== $result) { + if (isset($cacheItem) && false !== $result) { // 缓存数据 - $this->cacheData($key, $result, $cache); + $cacheItem->set($result); + $this->cacheData($cacheItem); } return false !== $result ? $result : $default; } /** - * 得到某个列的数组 + * 得到某个字段的值 * @access public - * @param Query $query 查询对象 - * @param string $field 字段名 多个字段用逗号分隔 - * @param string $key 索引 - * @return array + * @param Query $query 查询对象 + * @param string $aggregate 聚合方法 + * @param mixed $field 字段名 + * @param bool $force 强制转为数字类型 + * @return mixed */ - public function column(Query $query, $field, $key = '') + public function aggregate(Query $query, string $aggregate, $field, bool $force = false) { - $options = $query->getOptions(); + $field = $aggregate . '(' . $this->builder->parseKey($query, $field) . ') AS tp_' . strtolower($aggregate); - if ($this->cache && empty($options['fetch_sql']) && !empty($options['cache'])) { - // 判断查询缓存 - $cache = $options['cache']; + $result = $this->value($query, $field, 0); - $guid = is_string($cache['key']) ? $cache['key'] : $this->getCacheKey($query, $field); - $result = $this->cache->get($guid); + return $force ? (float) $result : $result; + } - if (false !== $result) { - return $result; - } - } + /** + * 得到某个列的数组 + * @access public + * @param Query $query 查询对象 + * @param string $field 字段名 多个字段用逗号分隔 + * @param string $key 索引 + * @return array + */ + public function column(Query $query, string $field, string $key = ''): array + { + $options = $query->parseOptions(); if (isset($options['field'])) { $query->removeOption('field'); } - if (is_null($field)) { - $field = ['*']; - } elseif (is_string($field)) { - $field = array_map('trim', explode(',', $field)); + if ($key && '*' != $field) { + $field = $key . ',' . $field; } - if ($key && ['*'] != $field) { - array_unshift($field, $key); - $field = array_unique($field); - } + $field = array_map('trim', explode(',', $field)); $query->setOption('field', $field); + if (!empty($options['cache'])) { + // 判断查询缓存 + $cacheItem = $options['cache']; + $result = $this->getCacheData($cacheItem); + + if (false !== $result) { + return $result; + } + } + // 生成查询SQL $sql = $this->builder->select($query); - // 还原field参数 if (isset($options['field'])) { $query->setOption('field', $options['field']); } else { $query->removeOption('field'); } - $bind = $query->getBind(); - - if (!empty($options['fetch_sql'])) { - // 获取实际执行的SQL语句 - return $this->getRealSql($sql, $bind); - } - // 执行查询操作 - $pdo = $this->query($sql, $bind, $options['master'], true); + $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']); if (1 == $pdo->columnCount()) { $result = $pdo->fetchAll(PDO::FETCH_COLUMN); } else { $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC); - if (['*'] == $field && $key) { + if ('*' == $field && $key) { $result = array_column($resultSet, null, $key); } elseif ($resultSet) { $fields = array_keys($resultSet[0]); @@ -1380,84 +1282,40 @@ abstract class Connection } } - if (isset($cache) && isset($guid)) { + if (isset($cacheItem)) { // 缓存数据 - $this->cacheData($guid, $result, $cache); + $cacheItem->set($result); + $this->cacheData($cacheItem); } return $result; } - /** - * 得到某个字段的值 - * @access public - * @param Query $query 查询对象 - * @param string $aggregate 聚合方法 - * @param string $field 字段名 - * @return mixed - */ - public function aggregate(Query $query, $aggregate, $field) - { - if (is_string($field) && 0 === stripos($field, 'DISTINCT ')) { - list($distinct, $field) = explode(' ', $field); - } - - $field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS tp_' . strtolower($aggregate); - - return $this->value($query, $field, 0); - } - - /** - * 执行查询但只返回PDOStatement对象 - * @access public - * @return \PDOStatement|string - */ - public function pdo(Query $query) - { - // 分析查询表达式 - $options = $query->getOptions(); - - // 生成查询SQL - $sql = $this->builder->select($query); - - $bind = $query->getBind(); - - if (!empty($options['fetch_sql'])) { - // 获取实际执行的SQL语句 - return $this->getRealSql($sql, $bind); - } - - // 执行查询操作 - return $this->query($sql, $bind, $options['master'], true); - } - /** * 根据参数绑定组装最终的SQL语句 便于调试 * @access public - * @param string $sql 带参数绑定的sql语句 - * @param array $bind 参数绑定列表 + * @param string $sql 带参数绑定的sql语句 + * @param array $bind 参数绑定列表 * @return string */ - public function getRealSql($sql, array $bind = []) + public function getRealSql(string $sql, array $bind = []): string { - if (is_array($sql)) { - $sql = implode(';', $sql); - } - foreach ($bind as $key => $val) { $value = is_array($val) ? $val[0] : $val; $type = is_array($val) ? $val[1] : PDO::PARAM_STR; - if (PDO::PARAM_INT == $type || self::PARAM_FLOAT == $type) { + if (self::PARAM_FLOAT == $type) { $value = (float) $value; - } elseif (PDO::PARAM_STR == $type) { + } elseif (PDO::PARAM_STR == $type && is_string($value)) { $value = '\'' . addslashes($value) . '\''; + } elseif (PDO::PARAM_INT == $type && '' === $value) { + $value = 0; } // 判断占位符 $sql = is_numeric($key) ? substr_replace($sql, $value, strpos($sql, '?'), 1) : - str_replace(':' . $key, $value, $sql); + substr_replace($sql, $value, strpos($sql, ':' . $key), strlen(':' . $key)); } return rtrim($sql); @@ -1468,11 +1326,11 @@ abstract class Connection * 支持 ['name'=>'value','id'=>123] 对应命名占位符 * 或者 ['value',123] 对应问号占位符 * @access public - * @param array $bind 要绑定的参数列表 + * @param array $bind 要绑定的参数列表 * @return void * @throws BindParamException */ - protected function bindValue(array $bind = []) + protected function bindValue(array $bind = []): void { foreach ($bind as $key => $val) { // 占位符 @@ -1505,11 +1363,11 @@ abstract class Connection /** * 存储过程的输入输出参数绑定 * @access public - * @param array $bind 要绑定的参数列表 + * @param array $bind 要绑定的参数列表 * @return void * @throws BindParamException */ - protected function bindParam($bind) + protected function bindParam(array $bind): void { foreach ($bind as $key => $val) { $param = is_numeric($key) ? $key + 1 : ':' . $key; @@ -1537,17 +1395,11 @@ abstract class Connection /** * 获得数据集数组 * @access protected - * @param bool $pdo 是否返回PDOStatement - * @param bool $procedure 是否存储过程 + * @param bool $procedure 是否存储过程 * @return array */ - protected function getResult($pdo = false, $procedure = false) + protected function getResult(bool $procedure = false): array { - if ($pdo) { - // 返回PDOStatement对象处理 - return $this->PDOStatement; - } - if ($procedure) { // 存储过程返回结果 return $this->procedure(); @@ -1565,7 +1417,7 @@ abstract class Connection * @access protected * @return array */ - protected function procedure() + protected function procedure(): array { $item = []; @@ -1584,13 +1436,13 @@ abstract class Connection /** * 执行数据库事务 * @access public - * @param callable $callback 数据操作方法回调 + * @param callable $callback 数据操作方法回调 * @return mixed * @throws PDOException * @throws \Exception * @throws \Throwable */ - public function transaction($callback) + public function transaction(callable $callback) { $this->startTrans(); @@ -1602,51 +1454,12 @@ abstract class Connection $this->commit(); return $result; - } catch (\Exception $e) { - $this->rollback(); - throw $e; - } catch (\Throwable $e) { + } catch (\Exception | \Throwable $e) { $this->rollback(); throw $e; } } - /** - * 启动XA事务 - * @access public - * @param string $xid XA事务id - * @return void - */ - public function startTransXa($xid) - {} - - /** - * 预编译XA事务 - * @access public - * @param string $xid XA事务id - * @return void - */ - public function prepareXa($xid) - {} - - /** - * 提交XA事务 - * @access public - * @param string $xid XA事务id - * @return void - */ - public function commitXa($xid) - {} - - /** - * 回滚XA事务 - * @access public - * @param string $xid XA事务id - * @return void - */ - public function rollbackXa($xid) - {} - /** * 启动事务 * @access public @@ -1654,12 +1467,9 @@ abstract class Connection * @throws \PDOException * @throws \Exception */ - public function startTrans() + public function startTrans(): void { $this->initConnect(true); - if (!$this->linkID) { - return false; - } ++$this->transTimes; @@ -1674,7 +1484,7 @@ abstract class Connection } catch (\Exception $e) { if ($this->isBreak($e)) { --$this->transTimes; - return $this->close()->startTrans(); + $this->close()->startTrans(); } throw $e; } @@ -1686,7 +1496,7 @@ abstract class Connection * @return void * @throws PDOException */ - public function commit() + public function commit(): void { $this->initConnect(true); @@ -1703,7 +1513,7 @@ abstract class Connection * @return void * @throws PDOException */ - public function rollback() + public function rollback(): void { $this->initConnect(true); @@ -1722,27 +1532,29 @@ abstract class Connection * 是否支持事务嵌套 * @return bool */ - protected function supportSavepoint() + protected function supportSavepoint(): bool { return false; } /** * 生成定义保存点的SQL - * @param $name + * @access protected + * @param $name * @return string */ - protected function parseSavepoint($name) + protected function parseSavepoint(string $name): string { return 'SAVEPOINT ' . $name; } /** * 生成回滚到保存点的SQL - * @param $name + * @access protected + * @param $name * @return string */ - protected function parseSavepointRollBack($name) + protected function parseSavepointRollBack(string $name): string { return 'ROLLBACK TO SAVEPOINT ' . $name; } @@ -1751,11 +1563,12 @@ abstract class Connection * 批处理执行SQL语句 * 批处理的指令都认为是execute操作 * @access public - * @param array $sqlArray SQL批处理指令 - * @param array $bind 参数绑定 - * @return boolean + * @param Query $query 查询对象 + * @param array $sqlArray SQL批处理指令 + * @param array $bind 参数绑定 + * @return bool */ - public function batchQuery($sqlArray = [], $bind = []) + public function batchQuery(Query $query, array $sqlArray = [], array $bind = []): bool { if (!is_array($sqlArray)) { return false; @@ -1766,7 +1579,7 @@ abstract class Connection try { foreach ($sqlArray as $sql) { - $this->execute($sql, $bind); + $this->execute($query, $sql, $bind); } // 提交事务 $this->commit(); @@ -1781,22 +1594,11 @@ abstract class Connection /** * 获得查询次数 * @access public - * @param boolean $execute 是否包含所有查询 * @return integer */ - public function getQueryTimes($execute = false) + public function getQueryTimes(): int { - return $execute ? Db::$queryTimes + Db::$executeTimes : Db::$queryTimes; - } - - /** - * 获得执行次数 - * @access public - * @return integer - */ - public function getExecuteTimes() - { - return Db::$executeTimes; + return self::$queryTimes; } /** @@ -1811,41 +1613,31 @@ abstract class Connection $this->linkRead = null; $this->links = []; + $this->free(); + return $this; } /** * 是否断线 * @access protected - * @param \PDOException|\Exception $e 异常对象 + * @param \PDOException|\Exception $e 异常对象 * @return bool */ - protected function isBreak($e) + protected function isBreak($e): bool { if (!$this->config['break_reconnect']) { return false; } - $info = [ - 'server has gone away', - 'no connection to the server', - 'Lost connection', - 'is dead or not enabled', - 'Error while sending', - 'decryption failed or bad record mac', - 'server closed the connection unexpectedly', - 'SSL connection has been closed unexpectedly', - 'Error writing data to the connection', - 'Resource deadlock avoided', - ]; - $error = $e->getMessage(); - foreach ($info as $msg) { + foreach ($this->breakMatchStr as $msg) { if (false !== stripos($error, $msg)) { return true; } } + return false; } @@ -1854,7 +1646,7 @@ abstract class Connection * @access public * @return string */ - public function getLastSql() + public function getLastSql(): string { return $this->getRealSql($this->queryStr, $this->bind); } @@ -1862,10 +1654,10 @@ abstract class Connection /** * 获取最近插入的ID * @access public - * @param string $sequence 自增序列名 + * @param string $sequence 自增序列名 * @return string */ - public function getLastInsID($sequence = null) + public function getLastInsID(string $sequence = null): string { return $this->linkID->lastInsertId($sequence); } @@ -1875,7 +1667,7 @@ abstract class Connection * @access public * @return integer */ - public function getNumRows() + public function getNumRows(): int { return $this->numRows; } @@ -1885,7 +1677,7 @@ abstract class Connection * @access public * @return string */ - public function getError() + public function getError(): string { if ($this->PDOStatement) { $error = $this->PDOStatement->errorInfo(); @@ -1909,13 +1701,14 @@ abstract class Connection * @param bool $master 主从标记 * @return void */ - protected function debug($start, $sql = '', $master = false) + protected function debug(bool $start, string $sql = '', bool $master = false): void { if (!empty($this->config['debug'])) { // 开启数据库调试模式 if ($start) { $this->queryStartTime = microtime(true); } else { + // 记录操作结束时间 $runtime = number_format((microtime(true) - $this->queryStartTime), 6); $sql = $sql ?: $this->getLastsql(); $result = []; @@ -1934,10 +1727,10 @@ abstract class Connection /** * 监听SQL执行 * @access public - * @param callable $callback 回调方法 + * @param callable $callback 回调方法 * @return void */ - public function listen($callback) + public function listen(callable $callback): void { self::$event[] = $callback; } @@ -1946,12 +1739,12 @@ abstract class Connection * 触发SQL事件 * @access protected * @param string $sql SQL语句 - * @param float $runtime SQL运行时间 + * @param string $runtime SQL运行时间 * @param mixed $explain SQL分析 * @param bool $master 主从标记 - * @return bool + * @return void */ - protected function triggerSql($sql, $runtime, $explain = [], $master = false) + protected function triggerSql(string $sql, string $runtime, array $explain = [], bool $master = false): void { if (!empty(self::$event)) { foreach (self::$event as $callback) { @@ -1989,10 +1782,10 @@ abstract class Connection /** * 初始化数据库连接 * @access protected - * @param boolean $master 是否主服务器 + * @param boolean $master 是否主服务器 * @return void */ - protected function initConnect($master = true) + protected function initConnect(bool $master = true): void { if (!empty($this->config['deploy'])) { // 采用分布式数据库 @@ -2018,10 +1811,10 @@ abstract class Connection /** * 连接分布式服务器 * @access protected - * @param boolean $master 主服务器 + * @param boolean $master 主服务器 * @return PDO */ - protected function multiConnect($master = false) + protected function multiConnect(bool $master = false): PDO { $_config = []; @@ -2054,14 +1847,14 @@ abstract class Connection if ($m != $r) { $dbMaster = []; foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { - $dbMaster[$name] = isset($_config[$name][$m]) ? $_config[$name][$m] : $_config[$name][0]; + $dbMaster[$name] = $_config[$name][$m] ?? $_config[$name][0]; } } $dbConfig = []; foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { - $dbConfig[$name] = isset($_config[$name][$r]) ? $_config[$name][$r] : $_config[$name][0]; + $dbConfig[$name] = $_config[$name][$r] ?? $_config[$name][0]; } return $this->connect($dbConfig, $r, $r == $m ? false : $dbMaster); @@ -2082,14 +1875,33 @@ abstract class Connection /** * 缓存数据 - * @access public - * @param string $key 缓存标识 - * @param mixed $data 缓存数据 - * @param array $config 缓存参数 + * @access protected + * @param CacheItem $cacheItem 缓存Item + */ + protected function cacheData(CacheItem $cacheItem): void + { + + if ($cacheItem->getTag()) { + $this->cache->tag($cacheItem->getTag()); + } + + $this->cache->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire()); + + } + + /** + * 获取缓存数据 + * @access protected + * @param Query $query 查询对象 + * @param mixed $cache 缓存设置 + * @param array $data 缓存数据 + * @param string $key 缓存Key + * @return mixed */ - protected function cacheData($key, $data, $config = []) + protected function getCacheData(CacheItem $cacheItem) { - $this->cache->set($key, $data, $config['expire']); + // 判断查询缓存 + return $this->cache->get($cacheItem->getKey()); } /** @@ -2099,11 +1911,11 @@ abstract class Connection * @param mixed $value 缓存数据 * @return string */ - protected function getCacheKey(Query $query, $value) + protected function getCacheKey(Query $query, $value): string { if (is_scalar($value)) { $data = $value; - } elseif (is_array($value) && isset($value[1], $value[2]) && in_array($value[1], ['=', 'eq'])) { + } elseif (is_array($value) && isset($value[1], $value[2]) && '=' == $value[1]) { $data = $value[2]; } @@ -2116,64 +1928,8 @@ abstract class Connection try { return md5($prefix . serialize($query->getOptions()) . serialize($query->getBind(false))); } catch (\Exception $e) { - return; - } - } - - /** - * 数据库连接参数解析 - * @access private - * @param mixed $config - * @return array - */ - private static function parseConfig($config) - { - if (empty($config)) { - $config = Db::getConfig(); - } elseif (is_string($config) && false === strpos($config, '/')) { - // 支持读取配置参数 - $config = Db::getConfig($config); + throw new Exception('closure not support cache(true)'); } - - if (is_string($config)) { - return self::parseDsnConfig($config); - } else { - return $config; - } - } - - /** - * DSN解析 - * 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1¶m2=val2#utf8 - * @access private - * @param string $dsnStr - * @return array - */ - private static function parseDsnConfig($dsnStr) - { - $info = parse_url($dsnStr); - - if (!$info) { - return []; - } - - $dsn = [ - 'type' => $info['scheme'], - 'username' => isset($info['user']) ? $info['user'] : '', - 'password' => isset($info['pass']) ? $info['pass'] : '', - 'hostname' => isset($info['host']) ? $info['host'] : '', - 'hostport' => isset($info['port']) ? $info['port'] : '', - 'database' => !empty($info['path']) ? ltrim($info['path'], '/') : '', - 'charset' => isset($info['fragment']) ? $info['fragment'] : 'utf8', - ]; - - if (isset($info['query'])) { - parse_str($info['query'], $dsn['params']); - } else { - $dsn['params'] = []; - } - - return $dsn; } } diff --git a/src/db/Expression.php b/src/db/Expression.php index f1b92ab..3e6a56b 100644 --- a/src/db/Expression.php +++ b/src/db/Expression.php @@ -2,12 +2,13 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\db; @@ -26,7 +27,7 @@ class Expression * @param string $value * @return void */ - public function __construct($value) + public function __construct(string $value) { $this->value = $value; } @@ -36,7 +37,7 @@ class Expression * * @return string */ - public function getValue() + public function getValue(): string { return $this->value; } diff --git a/src/db/Fetch.php b/src/db/Fetch.php new file mode 100644 index 0000000..387a796 --- /dev/null +++ b/src/db/Fetch.php @@ -0,0 +1,467 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db; + +use think\Db; +use think\Exception; + +class Fetch +{ + /** + * 查询对象 + * @var Query + */ + protected $query; + + /** + * Connection对象 + * @var Connection + */ + protected $connection; + + /** + * Builder对象 + * @var Builder + */ + protected $builder; + + /** + * 创建一个查询SQL获取对象 + * + * @param Query $query 查询对象 + */ + public function __construct(Query $query) + { + $this->query = $query; + $this->connection = $query->getConnection(); + $this->builder = $this->connection->getBuilder(); + } + + /** + * 聚合查询 + * @access public + * @param string $aggregate 聚合方法 + * @param string $field 字段名 + * @return string + */ + public function aggregate(string $aggregate, string $field): string + { + $options = $this->query->parseOptions(); + + $field = $aggregate . '(' . $this->builder->parseKey($this->query, $field) . ') AS tp_' . strtolower($aggregate); + + return $this->value($field); + } + + /** + * 得到某个字段的值 + * @access public + * @param string $field 字段名 + * @return string + */ + public function value(string $field): string + { + $options = $this->query->parseOptions(); + + if (isset($options['field'])) { + $this->query->removeOption('field'); + } + + if (is_string($field)) { + $field = array_map('trim', explode(',', $field)); + } + + $this->query->setOption('field', $field); + + // 生成查询SQL + $sql = $this->builder->select($this->query, true); + + if (isset($options['field'])) { + $this->query->setOption('field', $options['field']); + } else { + $this->query->removeOption('field'); + } + + return $this->fetch($sql); + } + + /** + * 得到某个列的数组 + * @access public + * @param string $field 字段名 多个字段用逗号分隔 + * @param string $key 索引 + * @return string + */ + public function column(string $field, string $key = ''): string + { + $options = $this->query->parseOptions(); + + if (isset($options['field'])) { + $this->query->removeOption('field'); + } + + if ($key && '*' != $field) { + $field = $key . ',' . $field; + } + + $field = array_map('trim', explode(',', $field)); + + $this->query->setOption('field', $field); + + // 生成查询SQL + $sql = $this->builder->select($this->query); + + if (isset($options['field'])) { + $this->query->setOption('field', $options['field']); + } else { + $this->query->removeOption('field'); + } + + return $this->fetch($sql); + } + + /** + * 插入记录 + * @access public + * @param array $data 数据 + * @param boolean $replace 是否replace + * @return string + */ + public function insert(array $data = [], bool $replace = false): string + { + $options = $this->query->parseOptions(); + + $this->query->setOption('data', array_merge($options['data'], $data)); + + $sql = $this->builder->insert($this->query, $replace); + return $this->fetch($sql); + } + + /** + * 插入记录并获取自增ID + * @access public + * @param array $data 数据 + * @param boolean $replace 是否replace + * @return string + */ + public function insertGetId(array $data, bool $replace = false) + { + return $this->insert($data, $replace); + } + + /** + * 批量插入记录 + * @access public + * @param array $dataSet 数据集 + * @param boolean $replace 是否replace + * @param integer $limit 每次写入数据限制 + * @return string + */ + public function insertAll(array $dataSet = [], bool $replace = false, int $limit = null): string + { + $options = $this->query->parseOptions(); + + if (empty($dataSet)) { + $dataSet = $options['data']; + } + + if (empty($limit) && !empty($options['limit'])) { + $limit = $options['limit']; + } + + if ($limit) { + $array = array_chunk($dataSet, $limit, true); + + foreach ($array as $item) { + $sql = $this->builder->insertAll($this->query, $item, $replace); + $bind = $this->query->getBind(); + + $fetchSql[] = $this->getRealSql($sql, $bind); + } + + return implode(';', $fetchSql); + } + + $sql = $this->builder->insertAll($this->query, $dataSet, $replace); + return $this->fetch($sql); + } + + /** + * 通过Select方式插入记录 + * @access public + * @param array $fields 要插入的数据表字段名 + * @param string $table 要插入的数据表名 + * @return string + */ + public function selectInsert(array $fields, string $table): string + { + $this->query->parseOptions(); + $sql = $this->builder->selectInsert($this->query, $fields, $table); + return $this->fetch($sql); + } + + /** + * 更新记录 + * @access public + * @param mixed $data 数据 + * @return string + */ + public function update(array $data = []): string + { + $options = $this->query->parseOptions(); + + $this->query->setOption('data', array_merge($options['data'], $data)); + + if (isset($options['cache']) && is_string($options['cache']['key'])) { + $key = $options['cache']['key']; + } + + $pk = $this->query->getPk($options); + $data = $options['data']; + $options['pk'] = $pk; + + if (empty($options['where'])) { + // 如果存在主键数据 则自动作为更新条件 + if (is_string($pk) && isset($data[$pk])) { + $where[$pk] = [$pk, '=', $data[$pk]]; + unset($data[$pk]); + } elseif (is_array($pk)) { + // 增加复合主键支持 + foreach ($pk as $field) { + if (isset($data[$field])) { + $where[$field] = [$field, '=', $data[$field]]; + } else { + // 如果缺少复合主键数据则不执行 + throw new Exception('miss complex primary data'); + } + unset($data[$field]); + } + } + + if (!isset($where)) { + // 如果没有任何更新条件则不执行 + throw new Exception('miss update condition'); + } else { + $options['where']['AND'] = $where; + $this->query->setOption('where', ['AND' => $where]); + } + } + + // 更新数据 + $this->query->setOption('data', $data); + + // 生成UPDATE SQL语句 + $sql = $this->builder->update($this->query); + return $this->fetch($sql); + } + + /** + * 删除记录 + * @access public + * @param mixed $data 表达式 true 表示强制删除 + * @return string + */ + public function delete($data = null): string + { + $options = $this->query->parseOptions(); + + if (!is_null($data) && true !== $data) { + // AR模式分析主键条件 + $this->query->parsePkWhere($data); + } + + if (!empty($options['soft_delete'])) { + // 软删除 + list($field, $condition) = $options['soft_delete']; + if ($condition) { + $this->query->setOption('soft_delete', null); + $this->query->setOption('data', [$field => $condition]); + // 生成删除SQL语句 + $sql = $this->builder->delete($this->query); + return $this->fetch($sql); + } + } + + $this->query->setOption('data', $data); + // 生成删除SQL语句 + $sql = $this->builder->delete($this->query); + return $this->fetch($sql); + } + + /** + * 查找记录 返回SQL + * @access public + * @param mixed $data + * @return string + */ + public function select($data = null): string + { + $this->query->parseOptions(); + + if (!is_null($data)) { + // 主键条件分析 + $this->query->parsePkWhere($data); + } + + $this->query->setOption('data', $data); + // 生成查询SQL + $sql = $this->builder->select($this->query); + return $this->fetch($sql); + } + + /** + * 查找单条记录 返回SQL语句 + * @access public + * @param mixed $data + * @return string + */ + public function find($data = null): string + { + $this->query->parseOptions(); + + if (!is_null($data)) { + // AR模式分析主键条件 + $this->query->parsePkWhere($data); + } + + $this->query->setOption('data', $data); + + $this->query->setOption('limit', '1'); + + // 生成查询SQL + $sql = $this->builder->select($this->query); + + $this->query->removeOption('limit'); + + // 获取实际执行的SQL语句 + return $this->fetch($sql); + } + + /** + * 查找多条记录 如果不存在则抛出异常 + * @access public + * @param array|string|Query|\Closure $data + * @return string + */ + public function selectOrFail($data = null): string + { + return $this->failException(true)->select($data); + } + + /** + * 查找单条记录 如果不存在则抛出异常 + * @access public + * @param array|string|Query|\Closure $data + * @return string + */ + public function findOrFail($data = null): string + { + return $this->failException(true)->find($data); + } + + /** + * 获取实际的SQL语句 + * @access public + * @param string $sql + * @return string + */ + public function fetch(string $sql): string + { + $bind = $this->query->getBind(); + + return $this->connection->getRealSql($sql, $bind); + } + + /** + * COUNT查询 + * @access public + * @param string $field 字段名 + * @return string + */ + public function count(string $field = '*'): string + { + $options = $this->query->parseOptions(); + + if (!empty($options['group'])) { + // 支持GROUP + $bind = $this->query->getBind(); + $subSql = $this->query->options($options)->field('count(' . $field . ') AS think_count')->bind($bind)->buildSql(); + + $query = $this->query->newQuery()->table([$subSql => '_group_count_']); + + return $query->fetchsql()->aggregate('COUNT', '*'); + } else { + return $this->aggregate('COUNT', $field); + } + } + + /** + * SUM查询 + * @access public + * @param string $field 字段名 + * @return string + */ + public function sum(string $field): string + { + return $this->aggregate('SUM', $field, true); + } + + /** + * MIN查询 + * @access public + * @param string $field 字段名 + * @return string + */ + public function min(string $field): string + { + return $this->aggregate('MIN', $field); + } + + /** + * MAX查询 + * @access public + * @param string $field 字段名 + * @return string + */ + public function max(string $field): string + { + return $this->aggregate('MAX', $field); + } + + /** + * AVG查询 + * @access public + * @param string $field 字段名 + * @return string + */ + public function avg(string $field): string + { + return $this->aggregate('AVG', $field); + } + + public function __call($method, $args) + { + if (strtolower(substr($method, 0, 5)) == 'getby') { + // 根据某个字段获取记录 + $field = Db::parseName(substr($method, 5)); + return $this->where($field, '=', $args[0])->find(); + } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { + // 根据某个字段获取记录的某个值 + $name = Db::parseName(substr($method, 10)); + return $this->where($name, '=', $args[0])->value($args[1]); + } + + $result = call_user_func_array([$this->query, $method], $args); + return $result === $this->query ? $this : $result; + } +} diff --git a/src/db/Mongo.php b/src/db/Mongo.php index d705eaa..8225b0f 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -21,6 +21,7 @@ use MongoDB\Driver\Exception\RuntimeException; use MongoDB\Driver\Query as MongoQuery; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; +use think\Db; use think\db\connector\Mongo as MongoConnection; use think\db\Query; @@ -40,6 +41,7 @@ class Mongo extends Query } $this->prefix = $this->connection->getConfig('prefix'); + $this->cache = Db::getCacheHandler(); } /** @@ -49,7 +51,7 @@ class Mongo extends Query * @param string $logic 查询逻辑 and or xor * @return $this */ - public function removeWhereField($field, $logic = 'and') + public function removeWhereField(string $field, string $logic = 'AND') { $logic = '$' . strtoupper($logic); @@ -166,7 +168,7 @@ class Mongo extends Query * @access public * @return integer */ - public function count($field = null) + public function count(string $field = null) { $this->parseOptions(); @@ -183,7 +185,7 @@ class Mongo extends Query * @param bool $force 强制转为数字类型 * @return mixed */ - public function aggregate($aggregate, $field, $force = false) + public function aggregate(string $aggregate, $field, bool $force = false) { $this->parseOptions(); @@ -222,66 +224,6 @@ class Mongo extends Query return $result; } - /** - * 字段值(延迟)增长 - * @access public - * @param string $field 字段名 - * @param integer $step 增长值 - * @param integer $lazyTime 延时时间(s) - * @return integer|true - * @throws Exception - */ - public function setInc($field, $step = 1, $lazyTime = 0) - { - $condition = !empty($this->options['where']) ? $this->options['where'] : []; - - if (empty($condition)) { - // 没有条件不做任何更新 - throw new Exception('no data to update'); - } - - if ($lazyTime > 0) { - // 延迟写入 - $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); - $step = $this->lazyWrite($guid, $step, $lazyTime); - if (empty($step)) { - return true; // 等待下次写入 - } - } - - return $this->setField($field, ['$inc', $step]); - } - - /** - * 字段值(延迟)减少 - * @access public - * @param string $field 字段名 - * @param integer $step 减少值 - * @param integer $lazyTime 延时时间(s) - * @return integer|true - * @throws Exception - */ - public function setDec($field, $step = 1, $lazyTime = 0) - { - $condition = !empty($this->options['where']) ? $this->options['where'] : []; - - if (empty($condition)) { - // 没有条件不做任何更新 - throw new Exception('no data to update'); - } - - if ($lazyTime > 0) { - // 延迟写入 - $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); - $step = $this->lazyWrite($guid, -$step, $lazyTime); - if (empty($step)) { - return true; // 等待下次写入 - } - } - - return $this->setField($field, ['$inc', -1 * $step]); - } - /** * 字段值增长 * @access public @@ -289,30 +231,18 @@ class Mongo extends Query * @param integer $step 增长值 * @return $this */ - public function inc($field, $step = 1, $op = 'inc') + public function inc(string $field, int $step = 1, string $op = 'INC') { return parent::inc($field, $step, strtolower('$' . $op)); } - /** - * 字段值减少 - * @access public - * @param string|array $field 字段名 - * @param integer $step 减少值 - * @return $this - */ - public function dec($field, $step = 1) - { - return $this->inc($field, -1 * $step); - } - /** * 指定当前操作的collection * @access public * @param string $collection * @return $this */ - public function collection($collection) + public function collection(string $collection) { return $this->table($collection); } @@ -323,7 +253,7 @@ class Mongo extends Query * @param bool $cursor 是否返回 Cursor 对象 * @return $this */ - public function fetchCursor($cursor = true) + public function fetchCursor(bool $cursor = true) { $this->options['fetch_cursor'] = $cursor; return $this; @@ -347,7 +277,7 @@ class Mongo extends Query * @param bool $awaitData * @return $this */ - public function awaitData($awaitData) + public function awaitData(bool $awaitData) { $this->options['awaitData'] = $awaitData; return $this; @@ -359,7 +289,7 @@ class Mongo extends Query * @param integer $batchSize * @return $this */ - public function batchSize($batchSize) + public function batchSize(int $batchSize) { $this->options['batchSize'] = $batchSize; return $this; @@ -371,7 +301,7 @@ class Mongo extends Query * @param bool $exhaust * @return $this */ - public function exhaust($exhaust) + public function exhaust(bool $exhaust) { $this->options['exhaust'] = $exhaust; return $this; @@ -383,7 +313,7 @@ class Mongo extends Query * @param array $modifiers * @return $this */ - public function modifiers($modifiers) + public function modifiers(array $modifiers) { $this->options['modifiers'] = $modifiers; return $this; @@ -395,7 +325,7 @@ class Mongo extends Query * @param bool $noCursorTimeout * @return $this */ - public function noCursorTimeout($noCursorTimeout) + public function noCursorTimeout(bool $noCursorTimeout) { $this->options['noCursorTimeout'] = $noCursorTimeout; return $this; @@ -407,7 +337,7 @@ class Mongo extends Query * @param bool $oplogReplay * @return $this */ - public function oplogReplay($oplogReplay) + public function oplogReplay(bool $oplogReplay) { $this->options['oplogReplay'] = $oplogReplay; return $this; @@ -419,7 +349,7 @@ class Mongo extends Query * @param bool $partial * @return $this */ - public function partial($partial) + public function partial(bool $partial) { $this->options['partial'] = $partial; return $this; @@ -431,7 +361,7 @@ class Mongo extends Query * @param string $maxTimeMS * @return $this */ - public function maxTimeMS($maxTimeMS) + public function maxTimeMS(string $maxTimeMS) { $this->options['maxTimeMS'] = $maxTimeMS; return $this; @@ -443,7 +373,7 @@ class Mongo extends Query * @param array $collation * @return $this */ - public function collation($collation) + public function collation(array $collation) { $this->options['collation'] = $collation; return $this; @@ -452,11 +382,14 @@ class Mongo extends Query /** * 设置返回字段 * @access public - * @param array $field - * @param boolean $except 是否排除 + * @param mixed $field + * @param boolean $except 是否排除 + * @param string $tableName 数据表名 + * @param string $prefix 字段前缀 + * @param string $alias 别名前缀 * @return $this */ - public function field($field, $except = false, $tableName = '', $prefix = '', $alias = '') + public function field($field, bool $except = false, string $tableName = '', string $prefix = '', string $alias = '') { if (empty($field)) { return $this; @@ -493,9 +426,9 @@ class Mongo extends Query * @param integer $skip * @return $this */ - public function skip($skip) + public function skip(int $skip) { - $this->options['skip'] = intval($skip); + $this->options['skip'] = $skip; return $this; } @@ -505,7 +438,7 @@ class Mongo extends Query * @param bool $slaveOk * @return $this */ - public function slaveOk($slaveOk) + public function slaveOk(bool $slaveOk) { $this->options['slaveOk'] = $slaveOk; return $this; @@ -518,18 +451,15 @@ class Mongo extends Query * @param mixed $length 查询数量 * @return $this */ - public function limit($offset, $length = null) + public function limit(int $offset, int $length = null) { if (is_null($length)) { - if (is_numeric($offset)) { - $length = $offset; - $offset = 0; - } else { - list($offset, $length) = explode(',', $offset); - } + $length = $offset; + $offset = 0; } - $this->options['skip'] = intval($offset); - $this->options['limit'] = intval($length); + + $this->options['skip'] = $offset; + $this->options['limit'] = $length; return $this; } @@ -541,7 +471,7 @@ class Mongo extends Query * @param string $order * @return $this */ - public function order($field, $order = '') + public function order($field, string $order = '') { if (is_array($field)) { $this->options['sort'] = $field; @@ -557,7 +487,7 @@ class Mongo extends Query * @param bool $tailable * @return $this */ - public function tailable($tailable) + public function tailable(bool $tailable) { $this->options['tailable'] = $tailable; return $this; @@ -585,26 +515,30 @@ class Mongo extends Query */ public function parsePkWhere($data) { - $pk = $this->getPk(); + $pk = $this->getPk($this->options); if (is_string($pk)) { - // 根据主键查询 - if (is_array($data)) { - $where[$pk] = isset($data[$pk]) ? [$pk, '=', $data[$pk]] : [$pk, 'in', $data]; - } else { - $where[$pk] = strpos($data, ',') ? [$pk, 'IN', $data] : [$pk, '=', $data]; + // 获取数据表 + if (empty($this->options['table'])) { + $this->options['table'] = $this->getTable(); } - } - if (!empty($where)) { + $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table']; + + if (!empty($this->options['alias'][$table])) { + $alias = $this->options['alias'][$table]; + } + + $key = isset($alias) ? $alias . '.' . $pk : $pk; + // 根据主键查询 + $where[$pk] = is_array($data) ? [$key, 'in', $data] : [$key, '=', $data]; + if (isset($this->options['where']['$and'])) { $this->options['where']['$and'] = array_merge($this->options['where']['$and'], $where); } else { $this->options['where']['$and'] = $where; } } - - return; } /** diff --git a/src/db/Query.php b/src/db/Query.php index 1952e2f..a9d3327 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -2,62 +2,94 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\db; +use Closure; use PDO; +use PDOStatement; +use think\cache\CacheItem; use think\Collection; use think\Db; -use think\db\exception\BindParamException; -use think\db\exception\DataNotFoundException; -use think\db\exception\DbException; -use think\db\exception\ModelNotFoundException; -use think\db\exception\PDOException; use think\Exception; +use think\exception\BindParamException; +use think\exception\DataNotFoundException; +use think\exception\DbException; +use think\exception\ModelNotFoundException; +use think\exception\PDOException; use think\Model; +use think\model\Collection as ModelCollection; use think\model\Relation; use think\model\relation\OneToOne; use think\Paginator; class Query { - // 数据库Connection对象 - protected static $connections = []; - // 当前数据库Connection对象 + /** + * 当前数据库连接对象 + * @var Connection + */ protected $connection; - // 当前模型对象 + + /** + * 当前模型对象 + * @var Model + */ protected $model; - // 当前数据表名称(不含前缀) + + /** + * 当前数据表名称(不含前缀) + * @var string + */ protected $name = ''; - // 当前数据表主键 + + /** + * 当前数据表主键 + * @var string|array + */ protected $pk; - // 当前数据表前缀 + + /** + * 当前数据表前缀 + * @var string + */ protected $prefix = ''; - // 查询参数 + + /** + * 当前查询参数 + * @var array + */ protected $options = []; - // 参数绑定 + + /** + * 当前参数绑定 + * @var array + */ protected $bind = []; - // 回调事件 - private static $event = []; - // 扩展查询方法 - private static $extend = []; + /** + * 事件回调 + * @var array + */ + protected static $event = []; /** - * 读取主库的表 + * 扩展查询方法 * @var array */ - private static $readMaster = []; + protected static $extend = []; - // 日期查询快捷方式 - protected $timeExp = ['d' => 'today', 'w' => 'week', 'm' => 'month', 'y' => 'year']; - // 日期查询表达式 + /** + * 日期查询表达式 + * @var array + */ protected $timeRule = [ 'today' => ['today', 'tomorrow'], 'yesterday' => ['yesterday', 'today'], @@ -69,19 +101,22 @@ class Query 'last year' => ['last year 1/1', 'this year 1/1'], ]; + /** + * 日期查询快捷定义 + * @var array + */ + protected $timeExp = ['d' => 'today', 'w' => 'week', 'm' => 'month', 'y' => 'year']; + /** * 架构函数 * @access public */ public function __construct(Connection $connection = null) { - if (is_null($connection)) { - $this->connection = Connection::instance(); - } else { - $this->connection = $connection; - } + $this->connection = $connection; $this->prefix = $this->connection->getConfig('prefix'); + $this->cache = Db::getCacheHandler(); } /** @@ -94,16 +129,49 @@ class Query return new static($this->connection); } + /** + * 切换数据库连接 + * @access public + * @param mixed $config 连接配置 + * @param bool|string $name 连接标识 true 强制重新连接 + * @return $this|object + * @throws Exception + */ + public function connect($config = [], $name = false) + { + $this->connection = Connection::instance($config, $name); + + return $this; + } + + /** + * 数据库连接参数解析 + * @access private + * @param mixed $config + * @return array + */ + private function parseConfig($config): array + { + if (empty($config)) { + $config = $this->config; + } elseif (is_string($config) && false === strpos($config, '/')) { + // 支持读取配置参数 + $config = $this->config[$config] ?? $this->config; + } + + return is_string($config) ? $this->parseDsnConfig($config) : $config; + } + /** * 利用__call方法实现一些特殊的Model方法 * @access public - * @param string $method 方法名称 - * @param array $args 调用参数 + * @param string $method 方法名称 + * @param array $args 调用参数 * @return mixed * @throws DbException * @throws Exception */ - public function __call($method, $args) + public function __call(string $method, array $args) { if (isset(self::$extend[strtolower($method)])) { // 调用扩展查询方法 @@ -134,18 +202,18 @@ class Query call_user_func_array([$this->model, $method], $args); return $this; } else { - throw new Exception('method not exist:' . ($this->model ? get_class($this->model) : static::class) . '->' . $method); + throw new Exception('method not exist:' . static::class . '->' . $method); } } /** * 扩展查询方法 * @access public - * @param string|array $method 查询方法名 - * @param callable $callback + * @param string|array $method 查询方法名 + * @param callable $callback * @return void */ - public static function extend($method, $callback = null) + public static function extend($method, $callback = null): void { if (is_array($method)) { foreach ($method as $key => $val) { @@ -157,33 +225,32 @@ class Query } /** - * 设置当前的数据库Connection对象 + * 获取当前的数据库Connection对象 * @access public - * @param Connection $connection - * @return $this + * @return Connection */ - public function setConnection(Connection $connection) + public function getConnection(): Connection { - $this->connection = $connection; - $this->prefix = $this->connection->getConfig('prefix'); - - return $this; + return $this->connection; } /** - * 获取当前的数据库Connection对象 + * 设置当前的数据库Connection对象 * @access public - * @return Connection + * @param Connection $connection + * @return $this */ - public function getConnection() + public function setConnection(Connection $connection) { - return $this->connection; + $this->connection = $connection; + + return $this; } /** * 指定模型 * @access public - * @param Model $model 模型对象实例 + * @param Model $model 模型对象实例 * @return $this */ public function model(Model $model) @@ -203,39 +270,45 @@ class Query } /** - * 设置从主库读取数据 + * 指定当前数据表名(不含前缀) * @access public - * @param bool $all 是否所有表有效 + * @param string $name * @return $this */ - public function readMaster($all = false) + public function name(string $name) { - $table = $all ? '*' : $this->getTable(); - - static::$readMaster[$table] = true; - + $this->name = $name; return $this; } /** - * 指定当前数据表名(不含前缀) + * 获取当前的数据表名称 * @access public - * @param string $name - * @return $this + * @return string */ - public function name($name) + public function getName(): string { - $this->name = $name; - return $this; + return $this->name ?: $this->model->getName(); + } + + /** + * 获取数据库的配置参数 + * @access public + * @param string $name 参数名称 + * @return mixed + */ + public function getConfig(string $name = '') + { + return $this->connection->getConfig($name); } /** * 得到当前或者指定名称的数据表 * @access public - * @param string $name + * @param string $name * @return string */ - public function getTable($name = '') + public function getTable(string $name = ''): string { if (empty($name) && isset($this->options['table'])) { return $this->options['table']; @@ -247,64 +320,119 @@ class Query } /** - * 切换数据库连接 + * 获取数据表字段信息 * @access public - * @param mixed $config 连接配置 - * @param bool|string $name 连接标识 true 强制重新连接 - * @return $this - * @throws Exception + * @param string $tableName 数据表名 + * @return array */ - public function connect($config = [], $name = false) + public function getTableFields($tableName = ''): array { - $this->connection = Connection::instance($config, $name); - $query = $this->connection->getConfig('query'); + if ('' == $tableName) { + $tableName = $this->options['table'] ?? $this->getTable(); + } - if (__CLASS__ != trim($query, '\\')) { - return new $query($this->connection); + return $this->connection->getTableFields($tableName); + } + + /** + * 获取数据表字段类型 + * @access protected + * @param string $tableName 数据表名 + * @param string $field 字段名 + * @return array|string + */ + protected function getTableFieldsType($tableName = '', string $field = null) + { + if ('' == $tableName) { + $tableName = $this->options['table'] ?? $this->getTable(); } - $this->prefix = $this->connection->getConfig('prefix'); + return $this->connection->getFieldsType($tableName, $field); + } + /** + * 设置字段类型信息 + * @access public + * @param array $type 字段类型信息 + * @return $this + */ + public function setFieldType(array $type) + { + $this->options['field_type'] = $type; return $this; } + /** + * 获取字段类型信息 + * @access public + * @param string $field 字段名 + * @return string|null + */ + public function getFieldType(string $field = null) + { + $fieldType = !empty($this->options['field_type']) ? $this->options['field_type'] : $this->getTableFieldsType(); + + if (is_null($field)) { + return $fieldType; + } + + return $fieldType[$field] ?? null; + } + + /** + * 获取字段类型信息 + * @access public + * @param string $field 字段名 + * @return string|null + */ + public function getFieldBindType(string $field = null) + { + $fieldType = $this->getFieldType($field); + + if (is_null($field)) { + return array_map([$this->connection, 'getFieldBindType'], $fieldType); + } + + return $this->connection->getFieldBindType($fieldType); + } + /** * 执行查询 返回数据集 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param boolean $master 是否在主服务器读操作 - * @param bool|string $class 指定返回的数据集对象 - * @return mixed + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param boolean $master 是否在主服务器读操作 + * @param bool $pdo 是否返回PDO对象 + * @return array * @throws BindParamException * @throws PDOException */ - public function query($sql, $bind = [], $master = false, $class = false) + public function query(string $sql, array $bind = []): array { - return $this->connection->query($sql, $bind, $master, $class); + return $this->connection->query($this, $sql, $bind, true); } /** * 执行语句 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 + * @param string $sql sql指令 + * @param array $bind 参数绑定 * @return int * @throws BindParamException * @throws PDOException */ - public function execute($sql, $bind = []) + public function execute(string $sql, array $bind = []): int { - return $this->connection->execute($sql, $bind); + return $this->connection->execute($this, $sql, $bind); } /** * 监听SQL执行 * @access public - * @param callable $callback 回调方法 + * @param callable $callback 回调方法 * @return void */ - public function listen($callback) + public function listen(callable $callback): void { $this->connection->listen($callback); } @@ -312,10 +440,10 @@ class Query /** * 获取最近插入的ID * @access public - * @param string $sequence 自增序列名 + * @param string $sequence 自增序列名 * @return string */ - public function getLastInsID($sequence = null) + public function getLastInsID(string $sequence = null): string { return $this->connection->getLastInsID($sequence); } @@ -325,7 +453,7 @@ class Query * @access public * @return integer */ - public function getNumRows() + public function getNumRows(): int { return $this->connection->getNumRows(); } @@ -335,7 +463,7 @@ class Query * @access public * @return string */ - public function getLastSql() + public function getLastSql(): string { return $this->connection->getLastSql(); } @@ -351,70 +479,24 @@ class Query } /** - * 执行数据库事务 + * 获得查询次数 * @access public - * @param callable $callback 数据操作方法回调 - * @return mixed + * @return integer */ - public function transaction($callback) + public function getQueryTimes(): int { - return $this->connection->transaction($callback); + return $this->connection->getQueryTimes(); } /** - * 执行数据库Xa事务 + * 执行数据库事务 * @access public * @param callable $callback 数据操作方法回调 - * @param array $dbs 多个查询对象或者连接对象 * @return mixed - * @throws PDOException - * @throws \Exception - * @throws \Throwable */ - public function transactionXa($callback, array $dbs = []) + public function transaction(callable $callback) { - $xid = uniqid('xa'); - - if (empty($dbs)) { - $dbs[] = $this->getConnection(); - } - - foreach ($dbs as $key => $db) { - if ($db instanceof Query) { - $db = $db->getConnection(); - - $dbs[$key] = $db; - } - - $db->startTransXa($xid); - } - - try { - $result = null; - if (is_callable($callback)) { - $result = call_user_func_array($callback, [$this]); - } - - foreach ($dbs as $db) { - $db->prepareXa($xid); - } - - foreach ($dbs as $db) { - $db->commitXa($xid); - } - - return $result; - } catch (\Exception $e) { - foreach ($dbs as $db) { - $db->rollbackXa($xid); - } - throw $e; - } catch (\Throwable $e) { - foreach ($dbs as $db) { - $db->rollbackXa($xid); - } - throw $e; - } + return $this->connection->transaction($callback); } /** @@ -422,7 +504,7 @@ class Query * @access public * @return void */ - public function startTrans() + public function startTrans(): void { $this->connection->startTrans(); } @@ -433,7 +515,7 @@ class Query * @return void * @throws PDOException */ - public function commit() + public function commit(): void { $this->connection->commit(); } @@ -444,7 +526,7 @@ class Query * @return void * @throws PDOException */ - public function rollback() + public function rollback(): void { $this->connection->rollback(); } @@ -453,215 +535,81 @@ class Query * 批处理执行SQL语句 * 批处理的指令都认为是execute操作 * @access public - * @param array $sql SQL批处理指令 - * @return boolean - */ - public function batchQuery($sql = []) - { - return $this->connection->batchQuery($sql); - } - - /** - * 获取数据库的配置参数 - * @access public - * @param string $name 参数名称 - * @return boolean - */ - public function getConfig($name = '') - { - return $this->connection->getConfig($name); - } - - /** - * 获取数据表字段信息 - * @access public - * @param string $tableName 数据表名 - * @return array - */ - public function getTableFields($tableName = '') - { - if ('' == $tableName) { - $tableName = isset($this->options['table']) ? $this->options['table'] : $this->getTable(); - } - - return $this->connection->getTableFields($tableName); - } - - /** - * 获取数据表字段类型 - * @access public - * @param string $tableName 数据表名 - * @param string $field 字段名 - * @return array|string - */ - public function getFieldsType($tableName = '', $field = null) - { - if ('' == $tableName) { - $tableName = isset($this->options['table']) ? $this->options['table'] : $this->getTable(); - } - - return $this->connection->getFieldsType($tableName, $field); - } - - /** - * 是否允许返回空数据(或空模型) - * @access public - * @param bool $allowEmpty 是否允许为空 - * @return $this + * @param array $sql SQL批处理指令 + * @return bool */ - public function allowEmpty($allowEmpty = true) + public function batchQuery(array $sql = []): bool { - $this->options['allow_empty'] = $allowEmpty; - return $this; - } - - /** - * 得到分表的的数据表名 - * @access public - * @param array $data 操作的数据 - * @param string $field 分表依据的字段 - * @param array $rule 分表规则 - * @return string - */ - public function getPartitionTableName($data, $field, $rule = []) - { - // 对数据表进行分区 - if ($field && isset($data[$field])) { - $value = $data[$field]; - $type = $rule['type']; - switch ($type) { - case 'id': - // 按照id范围分表 - $step = $rule['expr']; - $seq = floor($value / $step) + 1; - break; - case 'year': - // 按照年份分表 - if (!is_numeric($value)) { - $value = strtotime($value); - } - $seq = date('Y', $value) - $rule['expr'] + 1; - break; - case 'mod': - // 按照id的模数分表 - $seq = ($value % $rule['num']) + 1; - break; - case 'md5': - // 按照md5的序列分表 - $seq = (ord(substr(md5($value), 0, 1)) % $rule['num']) + 1; - break; - default: - if (function_exists($type)) { - // 支持指定函数哈希 - $seq = (ord(substr($type($value), 0, 1)) % $rule['num']) + 1; - } else { - // 按照字段的首字母的值分表 - $seq = (ord($value{0}) % $rule['num']) + 1; - } - } - return $this->getTable() . '_' . $seq; - } - - // 当设置的分表字段不在查询条件或者数据中 - // 进行联合查询,必须设定 partition['num'] - $tableName = []; - for ($i = 0; $i < $rule['num']; $i++) { - $tableName[] = 'SELECT * FROM ' . $this->getTable() . '_' . ($i + 1); - } - - $tableName = '( ' . implode(" UNION ", $tableName) . ') AS ' . $this->name; - - return $tableName; + return $this->connection->batchQuery($this, $sql); } /** * 得到某个字段的值 * @access public - * @param string $field 字段名 - * @param mixed $default 默认值 + * @param string $field 字段名 + * @param mixed $default 默认值 * @return mixed */ - public function value($field, $default = null) + public function value(string $field, $default = null) { - $this->parseOptions(); - return $this->connection->value($this, $field, $default); } /** * 得到某个列的数组 * @access public - * @param string $field 字段名 多个字段用逗号分隔 - * @param string $key 索引 + * @param string $field 字段名 多个字段用逗号分隔 + * @param string $key 索引 * @return array */ - public function column($field, $key = '') + public function column(string $field, string $key = ''): array { - $this->parseOptions(); - return $this->connection->column($this, $field, $key); } /** * 聚合查询 - * @access public - * @param string $aggregate 聚合方法 - * @param string $field 字段名 - * @param bool $force 强制转为数字类型 + * @access protected + * @param string $aggregate 聚合方法 + * @param string|Expression $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ - public function aggregate($aggregate, $field, $force = false) + protected function aggregate(string $aggregate, $field, bool $force = false) { - $this->parseOptions(); - - $result = $this->connection->aggregate($this, $aggregate, $field); - - if (!empty($this->options['fetch_sql'])) { - return $result; - } elseif ($force) { - $result = (float) $result; - } - - return $result; + return $this->connection->aggregate($this, $aggregate, $field, $force); } /** * COUNT查询 * @access public - * @param string $field 字段名 - * @return integer|string + * @param string $field 字段名 + * @return int */ - public function count($field = '*') + public function count(string $field = '*'): int { if (!empty($this->options['group'])) { // 支持GROUP $options = $this->getOptions(); - $subSql = $this->options($options) - ->field('count(' . $field . ') AS think_count') - ->bind($this->bind) - ->buildSql(); + $subSql = $this->options($options)->field('count(' . $field . ') AS think_count')->bind($this->bind)->buildSql(); $query = $this->newQuery()->table([$subSql => '_group_count_']); - if (!empty($options['fetch_sql'])) { - $query->fetchSql(true); - } - $count = $query->aggregate('COUNT', '*'); } else { $count = $this->aggregate('COUNT', $field); } - return is_string($count) ? $count : (int) $count; + return (int) $count; } /** * SUM查询 * @access public * @param string $field 字段名 - * @return float|int + * @return float */ - public function sum($field) + public function sum($field): float { return $this->aggregate('SUM', $field, true); } @@ -669,11 +617,11 @@ class Query /** * MIN查询 * @access public - * @param string $field 字段名 - * @param bool $force 强制转为数字类型 + * @param string|Expression $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ - public function min($field, $force = true) + public function min($field, bool $force = true) { return $this->aggregate('MIN', $field, $force); } @@ -681,11 +629,11 @@ class Query /** * MAX查询 * @access public - * @param string $field 字段名 - * @param bool $force 强制转为数字类型 + * @param string|Expression $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ - public function max($field, $force = true) + public function max($field, bool $force = true) { return $this->aggregate('MAX', $field, $force); } @@ -693,179 +641,128 @@ class Query /** * AVG查询 * @access public - * @param string $field 字段名 - * @return float|int + * @param string|Expression $field 字段名 + * @return float */ - public function avg($field) + public function avg($field): float { return $this->aggregate('AVG', $field, true); } - /** - * 设置记录的某个字段值 - * 支持使用数据库字段和方法 - * @access public - * @param string|array $field 字段名 - * @param mixed $value 字段值 - * @return integer - */ - public function setField($field, $value = '') - { - $condition = !empty($this->options['where']) ? $this->options['where'] : []; - - if (empty($condition)) { - // 没有条件不做任何更新 - throw new Exception('no data to update'); - } - - if (is_array($field)) { - $data = $field; - } else { - $data[$field] = $value; - } - - return $this->update($data); - } - - /** - * 字段值(延迟)增长 - * @access public - * @param string $field 字段名 - * @param integer $step 增长值 - * @return integer|true - * @throws Exception - */ - public function setInc($field, $step = 1) - { - return $this->setField($field, ['inc', $step]); - } - - /** - * 字段值(延迟)减少 - * @access public - * @param string $field 字段名 - * @param integer $step 减少值 - * @return integer|true - * @throws Exception - */ - public function setDec($field, $step = 1) - { - return $this->setField($field, ['dec', $step]); - } - /** * 查询SQL组装 join * @access public - * @param mixed $join 关联的表名 - * @param mixed $condition 条件 - * @param string $type JOIN类型 + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param string $type JOIN类型 + * @param array $bind 参数绑定 * @return $this */ - public function join($join, $condition = null, $type = 'INNER') + public function join($join, string $condition = null, string $type = 'INNER', array $bind = []) { - if (empty($condition)) { - // 如果为组数,则循环调用join - foreach ($join as $key => $value) { - if (is_array($value) && 2 <= count($value)) { - $this->join($value[0], $value[1], isset($value[2]) ? $value[2] : $type); - } - } - } else { - $table = $this->getJoinTable($join); + $table = $this->getJoinTable($join); - $this->options['join'][] = [$table, strtoupper($type), $condition]; + if ($bind) { + $this->bindParams($condition, $bind); } + $this->options['join'][] = [$table, strtoupper($type), $condition]; + return $this; } /** * LEFT JOIN * @access public - * @param mixed $join 关联的表名 - * @param mixed $condition 条件 + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param array $bind 参数绑定 * @return $this */ - public function leftJoin($join, $condition = null) + public function leftJoin($join, string $condition = null, array $bind = []) { - return $this->join($join, $condition, 'LEFT'); + return $this->join($join, $condition, 'LEFT', $bind); } /** * RIGHT JOIN * @access public - * @param mixed $join 关联的表名 - * @param mixed $condition 条件 + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param array $bind 参数绑定 * @return $this */ - public function rightJoin($join, $condition = null) + public function rightJoin($join, string $condition = null, array $bind = []) { - return $this->join($join, $condition, 'RIGHT'); + return $this->join($join, $condition, 'RIGHT', $bind); } /** * FULL JOIN * @access public - * @param mixed $join 关联的表名 - * @param mixed $condition 条件 + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param array $bind 参数绑定 * @return $this */ - public function fullJoin($join, $condition = null) + public function fullJoin($join, string $condition = null, array $bind = []) { return $this->join($join, $condition, 'FULL'); } /** * 获取Join表名及别名 支持 - * ['prefix_table或者子查询'=>'alias'] 'prefix_table alias' 'table alias' - * @access public - * @param array|string $join - * @return array|string + * ['prefix_table或者子查询'=>'alias'] 'table alias' + * @access protected + * @param array|string $join + * @param string $alias + * @return string */ protected function getJoinTable($join, &$alias = null) { - // 传入的表名为数组 if (is_array($join)) { $table = $join; - } else { - $join = trim($join); + $alias = array_shift($join); + return $table; + } - if (false !== strpos($join, '(')) { - // 使用子查询 - $table = $join; - } else { - $prefix = $this->prefix; - if (strpos($join, ' ')) { - // 使用别名 - list($table, $alias) = explode(' ', $join); - } else { - $table = $join; - if (false === strpos($join, '.') && 0 !== strpos($join, '__')) { - $alias = $join; - } - } + $join = trim($join); - if ($prefix && false === strpos($table, '.') && 0 !== strpos($table, $prefix) && 0 !== strpos($table, '__')) { - $table = $this->getTable($table); + if (false !== strpos($join, '(')) { + // 使用子查询 + $table = $join; + } else { + // 使用别名 + if (strpos($join, ' ')) { + // 使用别名 + list($table, $alias) = explode(' ', $join); + } else { + $table = $join; + if (false === strpos($join, '.')) { + $alias = $join; } } - if (isset($alias) && $table != $alias) { - $table = [$table => $alias]; + if ($this->prefix && false === strpos($table, '.') && 0 !== strpos($table, $this->prefix)) { + $table = $this->getTable($table); } } + if (!empty($alias) && $table != $alias) { + $table = [$table => $alias]; + } + return $table; } /** * 查询SQL组装 union * @access public - * @param mixed $union - * @param boolean $all + * @param mixed $union + * @param boolean $all * @return $this */ - public function union($union, $all = false) + public function union($union, bool $all = false) { $this->options['union']['type'] = $all ? 'UNION ALL' : 'UNION'; @@ -892,23 +789,27 @@ class Query /** * 指定查询字段 支持字段排除和指定数据表 * @access public - * @param mixed $field - * @param boolean $except 是否排除 - * @param string $tableName 数据表名 - * @param string $prefix 字段前缀 - * @param string $alias 别名前缀 + * @param mixed $field + * @param boolean $except 是否排除 + * @param string $tableName 数据表名 + * @param string $prefix 字段前缀 + * @param string $alias 别名前缀 * @return $this */ - public function field($field, $except = false, $tableName = '', $prefix = '', $alias = '') + public function field($field, bool $except = false, string $tableName = '', string $prefix = '', string $alias = '') { if (empty($field)) { return $this; + } elseif ($field instanceof Expression) { + $this->options['field'][] = $field; + return $this; } if (is_string($field)) { if (preg_match('/[\<\'\"\(]/', $field)) { return $this->fieldRaw($field); } + $field = array_map('trim', explode(',', $field)); } @@ -950,7 +851,7 @@ class Query * @param string $field 字段名 * @return $this */ - public function fieldRaw($field) + public function fieldRaw(string $field) { $this->options['field'][] = $this->raw($field); @@ -960,17 +861,12 @@ class Query /** * 设置数据 * @access public - * @param mixed $field 字段名或者数据 - * @param mixed $value 字段值 + * @param array $data 数据 * @return $this */ - public function data($field, $value = null) + public function data(array $data) { - if (is_array($field)) { - $this->options['data'] = isset($this->options['data']) ? array_merge($this->options['data'], $field) : $field; - } else { - $this->options['data'][$field] = $value; - } + $this->options['data'] = $data; return $this; } @@ -978,23 +874,13 @@ class Query /** * 字段值增长 * @access public - * @param string|array $field 字段名 - * @param integer $step 增长值 + * @param string $field 字段名 + * @param integer $step 增长值 * @return $this */ - public function inc($field, $step = 1, $op = 'INC') + public function inc(string $field, int $step = 1, string $op = 'INC') { - $fields = is_string($field) ? explode(',', $field) : $field; - - foreach ($fields as $field => $val) { - if (is_numeric($field)) { - $field = $val; - } else { - $step = $val; - } - - $this->data($field, [$op, $step]); - } + $this->options['data'][$field] = [$op, $step]; return $this; } @@ -1002,35 +888,35 @@ class Query /** * 字段值减少 * @access public - * @param string|array $field 字段名 - * @param integer $step 增长值 + * @param string $field 字段名 + * @param integer $step 增长值 * @return $this */ - public function dec($field, $step = 1) + public function dec(string $field, int $step = 1) { - return $this->inc($field, $step, 'DEC'); + return $this->inc($field, $step, $lazyTime, 'DEC'); } /** * 使用表达式设置数据 * @access public - * @param string $field 字段名 - * @param string $value 字段值 + * @param string $field 字段名 + * @param string $value 字段值 * @return $this */ - public function exp($field, $value) + public function exp(string $field, string $value) { - $this->data($field, $this->raw($value)); + $this->options['data'][$field] = $this->raw($value); return $this; } /** * 使用表达式设置数据 * @access public - * @param mixed $value 表达式 + * @param string $value 表达式 * @return Expression */ - public function raw($value) + public function raw(string $value): Expression { return new Expression($value); } @@ -1038,69 +924,52 @@ class Query /** * 指定JOIN查询字段 * @access public - * @param string|array $table 数据表 - * @param string|array $field 查询字段 - * @param string|array $on JOIN条件 - * @param string $type JOIN类型 + * @param string|array $table 数据表 + * @param string|array $field 查询字段 + * @param string|array $on JOIN条件 + * @param string $type JOIN类型 * @return $this */ - public function view($join, $field = true, $on = null, $type = 'INNER') + public function view($join, $field = true, $on = null, string $type = 'INNER', array $bind = []) { $this->options['view'] = true; - if (is_array($join) && key($join) !== 0) { - foreach ($join as $key => $val) { - $this->view($key, $val[0], isset($val[1]) ? $val[1] : null, isset($val[2]) ? $val[2] : 'INNER'); - } + $fields = []; + $table = $this->getJoinTable($join, $alias); + + if (true === $field) { + $fields = $alias . '.*'; } else { - $fields = []; - $table = $this->getJoinTable($join, $alias); + if (is_string($field)) { + $field = explode(',', $field); + } - if (true === $field) { - $fields = $alias . '.*'; - } else { - if (is_string($field)) { - $field = explode(',', $field); - } - foreach ($field as $key => $val) { - if (is_numeric($key)) { - $fields[] = $alias . '.' . $val; - $this->options['map'][$val] = $alias . '.' . $val; + foreach ($field as $key => $val) { + if (is_numeric($key)) { + $fields[] = $alias . '.' . $val; + + $this->options['map'][$val] = $alias . '.' . $val; + } else { + if (preg_match('/[,=\.\'\"\(\s]/', $key)) { + $name = $key; } else { - if (preg_match('/[,=\.\'\"\(\s]/', $key)) { - $name = $key; - } else { - $name = $alias . '.' . $key; - } - $fields[] = $name . ' AS ' . $val; - $this->options['map'][$val] = $name; + $name = $alias . '.' . $key; } - } - } - $this->field($fields); + $fields[] = $name . ' AS ' . $val; - if ($on) { - $this->join($table, $on, $type); - } else { - $this->table($table); + $this->options['map'][$val] = $name; + } } } - return $this; - } + $this->field($fields); - /** - * 设置分表规则 - * @access public - * @param array $data 操作的数据 - * @param string $field 分表依据的字段 - * @param array $rule 分表规则 - * @return $this - */ - public function partition($data, $field, $rule = []) - { - $this->options['table'] = $this->getPartitionTableName($data, $field, $rule); + if ($on) { + $this->join($table, $on, $type, $bind); + } else { + $this->table($table); + } return $this; } @@ -1108,55 +977,29 @@ class Query /** * 指定AND查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 * @return $this */ public function where($field, $op = null, $condition = null) { + if ($field instanceof $this) { + $this->options['where'] = $field->getOptions('where'); + return $this; + } + $param = func_get_args(); array_shift($param); return $this->parseWhereExp('AND', $field, $op, $condition, $param); } - /** - * 指定表达式查询条件 - * @access public - * @param string $where 查询条件 - * @param array $bind 参数绑定 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereRaw($where, $bind = [], $logic = 'AND') - { - if ($bind) { - $this->bindParams($where, $bind); - } - - $this->options['where'][$logic][] = $this->raw($where); - - return $this; - } - - /** - * 指定表达式查询条件 OR - * @access public - * @param string $where 查询条件 - * @param array $bind 参数绑定 - * @return $this - */ - public function whereOrRaw($where, $bind = []) - { - return $this->whereRaw($where, $bind, 'OR'); - } - /** * 指定OR查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 * @return $this */ public function whereOr($field, $op = null, $condition = null) @@ -1169,9 +1012,9 @@ class Query /** * 指定XOR查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 * @return $this */ public function whereXor($field, $op = null, $condition = null) @@ -1184,11 +1027,11 @@ class Query /** * 指定Null查询条件 * @access public - * @param mixed $field 查询字段 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereNull($field, $logic = 'AND') + public function whereNull(string $field, string $logic = 'AND') { return $this->parseWhereExp($logic, $field, 'NULL', null, [], true); } @@ -1196,11 +1039,11 @@ class Query /** * 指定NotNull查询条件 * @access public - * @param mixed $field 查询字段 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereNotNull($field, $logic = 'AND') + public function whereNotNull(string $field, string $logic = 'AND') { return $this->parseWhereExp($logic, $field, 'NOTNULL', null, [], true); } @@ -1208,11 +1051,11 @@ class Query /** * 指定Exists查询条件 * @access public - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereExists($condition, $logic = 'AND') + public function whereExists($condition, string $logic = 'AND') { if (is_string($condition)) { $condition = $this->raw($condition); @@ -1225,11 +1068,11 @@ class Query /** * 指定NotExists查询条件 * @access public - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereNotExists($condition, $logic = 'AND') + public function whereNotExists($condition, string $logic = 'AND') { if (is_string($condition)) { $condition = $this->raw($condition); @@ -1242,12 +1085,12 @@ class Query /** * 指定In查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereIn($field, $condition, $logic = 'AND') + public function whereIn(string $field, $condition, string $logic = 'AND') { return $this->parseWhereExp($logic, $field, 'IN', $condition, [], true); } @@ -1255,12 +1098,12 @@ class Query /** * 指定NotIn查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereNotIn($field, $condition, $logic = 'AND') + public function whereNotIn(string $field, $condition, string $logic = 'AND') { return $this->parseWhereExp($logic, $field, 'NOT IN', $condition, [], true); } @@ -1268,12 +1111,12 @@ class Query /** * 指定Like查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereLike($field, $condition, $logic = 'AND') + public function whereLike(string $field, $condition, string $logic = 'AND') { return $this->parseWhereExp($logic, $field, 'LIKE', $condition, [], true); } @@ -1281,12 +1124,12 @@ class Query /** * 指定NotLike查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereNotLike($field, $condition, $logic = 'AND') + public function whereNotLike(string $field, $condition, string $logic = 'AND') { return $this->parseWhereExp($logic, $field, 'NOT LIKE', $condition, [], true); } @@ -1294,12 +1137,12 @@ class Query /** * 指定Between查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereBetween($field, $condition, $logic = 'AND') + public function whereBetween(string $field, $condition, string $logic = 'AND') { return $this->parseWhereExp($logic, $field, 'BETWEEN', $condition, [], true); } @@ -1307,12 +1150,12 @@ class Query /** * 指定NotBetween查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereNotBetween($field, $condition, $logic = 'AND') + public function whereNotBetween(string $field, $condition, string $logic = 'AND') { return $this->parseWhereExp($logic, $field, 'NOT BETWEEN', $condition, [], true); } @@ -1320,13 +1163,13 @@ class Query /** * 比较两个字段 * @access public - * @param string|array $field1 查询字段 - * @param string $operator 比较操作符 - * @param string $field2 比较字段 - * @param string $logic 查询逻辑 and or xor + * @param string $field1 查询字段 + * @param string $operator 比较操作符 + * @param string $field2 比较字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereColumn($field1, $operator, $field2 = null, $logic = 'AND') + public function whereColumn(string $field1, string $operator, string $field2 = null, string $logic = 'AND') { if (is_array($field1)) { foreach ($field1 as $item) { @@ -1346,11 +1189,11 @@ class Query /** * 设置软删除字段及条件 * @access public - * @param false|string $field 查询字段 - * @param mixed $condition 查询条件 + * @param string $field 查询字段 + * @param mixed $condition 查询条件 * @return $this */ - public function useSoftDelete($field, $condition = null) + public function useSoftDelete(string $field, $condition = null) { if ($field) { $this->options['soft_delete'] = [$field, $condition]; @@ -1362,13 +1205,13 @@ class Query /** * 指定Exp查询条件 * @access public - * @param mixed $field 查询字段 - * @param string $where 查询条件 - * @param array $bind 参数绑定 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereExp($field, $where, array $bind = [], $logic = 'AND') + public function whereExp(string $field, $where, array $bind = [], string $logic = 'AND') { if ($bind) { $this->bindParams($where, $bind); @@ -1380,31 +1223,52 @@ class Query } /** - * 分析查询表达式 + * 指定表达式查询条件 * @access public - * @param string $logic 查询逻辑 and or xor - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 - * @param array $param 查询参数 - * @param bool $strict 严格模式 + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @param string $logic 查询逻辑 and or xor * @return $this */ - protected function parseWhereExp($logic, $field, $op, $condition, $param = [], $strict = false) + public function whereRaw(string $where, array $bind = [], string $logic = 'AND') { - if ($field instanceof $this) { - $this->options['where'] = $field->getOptions('where'); - return $this; + if ($bind) { + $this->bindParams($where, $bind); } - $logic = strtoupper($logic); + $this->options['where'][$logic][] = $this->raw($where); - if ($field instanceof Where) { - $this->options['where'][$logic] = $field->parse(); - return $this; - } + return $this; + } + + /** + * 指定表达式查询条件 OR + * @access public + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @return $this + */ + public function whereOrRaw(string $where, array $bind = []) + { + return $this->whereRaw($where, $bind, 'OR'); + } + + /** + * 分析查询表达式 + * @access protected + * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @param array $param 查询参数 + * @param bool $strict 严格模式 + * @return $this + */ + protected function parseWhereExp(string $logic, $field, $op, $condition, array $param = [], bool $strict = false) + { + $logic = strtoupper($logic); - if (is_string($field) && !empty($this->options['via']) && !strpos($field, '.')) { + if (is_string($field) && !empty($this->options['via']) && false === strpos($field, '.')) { $field = $this->options['via'] . '.' . $field; } @@ -1412,16 +1276,15 @@ class Query return $this->whereRaw($field, is_array($op) ? $op : []); } elseif ($strict) { // 使用严格模式查询 - $where = [$field, $op, $condition]; + $where = [$field, $op, $condition, $logic]; } elseif (is_array($field)) { // 解析数组批量查询 return $this->parseArrayWhereItems($field, $logic); - } elseif ($field instanceof \Closure) { + } elseif ($field instanceof Closure) { $where = $field; - $field = ''; } elseif (is_string($field)) { if (preg_match('/[,=\<\'\"\(\s]/', $field)) { - return $this->whereRaw($field, $op); + return $this->whereRaw($field, is_array($op) ? $op : []); } elseif (is_string($op) && strtolower($op) == 'exp') { $bind = isset($param[2]) && is_array($param[2]) ? $param[2] : null; return $this->whereExp($field, $condition, $bind, $logic); @@ -1445,21 +1308,23 @@ class Query * @param mixed $op 查询表达式 * @param mixed $condition 查询条件 * @param array $param 查询参数 - * @return mixed + * @return array */ - protected function parseWhereItem($logic, $field, $op, $condition, $param = []) + protected function parseWhereItem(string $logic, $field, $op, $condition, array $param = []): array { if (is_array($op)) { // 同一字段多条件查询 array_unshift($param, $field); $where = $param; + } elseif (!is_string($op)) { + $where = [$field, '=', $op]; } elseif ($field && is_null($condition)) { if (in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) { // null查询 $where = [$field, $op, '']; - } elseif (in_array($op, ['=', 'eq', 'EQ', null], true)) { + } elseif ('=' == $op) { $where = [$field, 'NULL', '']; - } elseif (in_array($op, ['<>', 'neq', 'NEQ'], true)) { + } elseif ('<>' == $op) { $where = [$field, 'NOTNULL', '']; } else { // 字段相等查询 @@ -1468,7 +1333,7 @@ class Query } elseif (in_array(strtoupper($op), ['REGEXP', 'NOT REGEXP', 'EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) { $where = [$field, $op, is_string($condition) ? $this->raw($condition) : $condition]; } else { - $where = $field ? [$field, $op, $condition] : null; + $where = $field ? [$field, $op, $condition, isset($param[2]) ? $param[2] : null] : []; } return $where; @@ -1481,17 +1346,15 @@ class Query * @param string $logic 查询逻辑 and or xor * @return $this */ - protected function parseArrayWhereItems($field, $logic) + protected function parseArrayWhereItems(array $field, string $logic) { if (key($field) !== 0) { $where = []; foreach ($field as $key => $val) { if ($val instanceof Expression) { $where[] = [$key, 'exp', $val]; - } elseif (is_null($val)) { - $where[] = [$key, 'NULL', '']; } else { - $where[] = [$key, is_array($val) ? 'IN' : '=', $val]; + $where[] = is_null($val) ? [$key, 'NULL', ''] : [$key, is_array($val) ? 'IN' : '=', $val]; } } } else { @@ -1509,11 +1372,11 @@ class Query /** * 去除某个查询条件 * @access public - * @param string $field 查询字段 - * @param string $logic 查询逻辑 and or xor + * @param string $field 查询字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function removeWhereField($field, $logic = 'AND') + public function removeWhereField(string $field, string $logic = 'AND') { $logic = strtoupper($logic); @@ -1531,14 +1394,14 @@ class Query /** * 去除查询参数 * @access public - * @param string|bool $option 参数名 true 表示去除所有参数 + * @param string $option 参数名 留空去除所有参数 * @return $this */ - public function removeOption($option = true) + public function removeOption(string $option = '') { - if (true === $option) { + if ('' === $option) { $this->options = []; - } elseif (is_string($option) && isset($this->options[$option])) { + } elseif (isset($this->options[$option])) { unset($this->options[$option]); } @@ -1548,25 +1411,25 @@ class Query /** * 条件查询 * @access public - * @param mixed $condition 满足条件(支持闭包) - * @param \Closure|array $query 满足条件后执行的查询表达式(闭包或数组) - * @param \Closure|array $otherwise 不满足条件后执行 + * @param mixed $condition 满足条件(支持闭包) + * @param Closure|array $query 满足条件后执行的查询表达式(闭包或数组) + * @param Closure|array $otherwise 不满足条件后执行 * @return $this */ public function when($condition, $query, $otherwise = null) { - if ($condition instanceof \Closure) { + if ($condition instanceof Closure) { $condition = $condition($this); } if ($condition) { - if ($query instanceof \Closure) { + if ($query instanceof Closure) { $query($this, $condition); } elseif (is_array($query)) { $this->where($query); } } elseif ($otherwise) { - if ($otherwise instanceof \Closure) { + if ($otherwise instanceof Closure) { $otherwise($this, $condition); } elseif (is_array($otherwise)) { $this->where($otherwise); @@ -1579,17 +1442,13 @@ class Query /** * 指定查询数量 * @access public - * @param mixed $offset 起始位置 - * @param mixed $length 查询数量 + * @param int $offset 起始位置 + * @param int $length 查询数量 * @return $this */ - public function limit($offset, $length = null) + public function limit(int $offset, int $length = null) { - if (is_null($length) && strpos($offset, ',')) { - list($offset, $length) = explode(',', $offset); - } - - $this->options['limit'] = intval($offset) . ($length ? ',' . intval($length) : ''); + $this->options['limit'] = $offset . ($length ? ',' . $length : ''); return $this; } @@ -1597,26 +1456,23 @@ class Query /** * 指定分页 * @access public - * @param mixed $page 页数 - * @param mixed $listRows 每页数量 + * @param int $page 页数 + * @param int $listRows 每页数量 * @return $this */ - public function page($page, $listRows = null) + public function page(int $page, int $listRows = null) { - if (is_null($listRows) && strpos($page, ',')) { - list($page, $listRows) = explode(',', $page); - } - - $this->options['page'] = [intval($page), intval($listRows)]; + $this->options['page'] = [$page, $listRows]; return $this; } /** * 分页查询 - * @param int|array $listRows 每页数量 数组表示配置参数 - * @param int|bool $simple 是否简洁模式或者总记录数 - * @param array $config 配置参数 + * @access public + * @param int|array $listRows 每页数量 数组表示配置参数 + * @param int|bool $simple 是否简洁模式或者总记录数 + * @param array $config 配置参数 * page:当前页, * path:url路径, * query:url额外参数, @@ -1638,10 +1494,10 @@ class Query if (is_array($listRows)) { $config = array_merge($paginate, $listRows); - $listRows = $config['list_rows']; + $listRows = intval($config['list_rows']); } else { $config = array_merge($paginate, $config); - $listRows = $listRows ?: $config['list_rows']; + $listRows = intval($listRows ?: $config['list_rows']); } /** @var Paginator $class */ @@ -1653,7 +1509,7 @@ class Query $page = $page < 1 ? 1 : $page; - $config['path'] = isset($config['path']) ? $config['path'] : call_user_func([$class, 'getCurrentPath']); + $config['path'] = $config['path'] ?? call_user_func([$class, 'getCurrentPath']); if (!isset($total) && !$simple) { $options = $this->getOptions(); @@ -1676,10 +1532,23 @@ class Query return $class::make($results, $listRows, $page, $total, $simple, $config); } + /** + * 表达式方式指定当前操作的数据表 + * @access public + * @param mixed $table 表名 + * @return $this + */ + public function tableRaw(string $table) + { + $this->options['table'] = $this->raw($table); + + return $this; + } + /** * 指定当前操作的数据表 * @access public - * @param mixed $table 表名 + * @param mixed $table 表名 * @return $this */ public function table($table) @@ -1687,24 +1556,20 @@ class Query if (is_string($table)) { if (strpos($table, ')')) { // 子查询 - } elseif (strpos($table, ',')) { + } else { $tables = explode(',', $table); $table = []; foreach ($tables as $item) { - list($item, $alias) = explode(' ', trim($item)); - if ($alias) { + $item = trim($item); + if (strpos($item, ' ')) { + list($item, $alias) = explode(' ', $item); $this->alias([$item => $alias]); $table[$item] = $alias; } else { $table[] = $item; } } - } elseif (strpos($table, ' ')) { - list($table, $alias) = explode(' ', $table); - - $table = [$table => $alias]; - $this->alias($table); } } else { $tables = $table; @@ -1719,6 +1584,7 @@ class Query } } } + $this->options['table'] = $table; return $this; @@ -1727,7 +1593,7 @@ class Query /** * USING支持 用于多表删除 * @access public - * @param mixed $using + * @param mixed $using * @return $this */ public function using($using) @@ -1736,24 +1602,38 @@ class Query return $this; } + /** + * 存储过程调用 + * @access public + * @param bool $procedure + * @return $this + */ + public function procedure($procedure = true) + { + $this->options['procedure'] = $procedure; + return $this; + } + /** * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc']) * @access public - * @param string|array $field 排序字段 - * @param string $order 排序 + * @param string|array $field 排序字段 + * @param string $order 排序 * @return $this */ - public function order($field, $order = null) + public function order($field, string $order = '') { if (empty($field)) { return $this; + } elseif ($field instanceof Expression) { + $this->options['order'][] = $field; + return $this; } if (is_string($field)) { if (!empty($this->options['via'])) { $field = $this->options['via'] . '.' . $field; } - if (strpos($field, ',')) { $field = array_map('trim', explode(',', $field)); } else { @@ -1790,7 +1670,7 @@ class Query * @param array $bind 参数绑定 * @return $this */ - public function orderRaw($field, $bind = []) + public function orderRaw(string $field, array $bind = []) { if ($bind) { $this->bindParams($field, $bind); @@ -1802,14 +1682,14 @@ class Query } /** - * 指定Field排序 order('id',[1,2,3],'desc') + * 指定Field排序 orderField('id',[1,2,3],'desc') * @access public - * @param string|array $field 排序字段 - * @param string $values 排序值 - * @param string $order + * @param string $field 排序字段 + * @param array $values 排序值 + * @param string $order * @return $this */ - public function orderField($field, array $values = [], $order = '') + public function orderField(string $field, array $values, string $order = '') { if (!empty($values)) { $values['sort'] = $order; @@ -1834,30 +1714,39 @@ class Query /** * 查询缓存 * @access public - * @param mixed $key 缓存key - * @param integer|\DateTime $expire 缓存有效期 - * @param string $tag 缓存标签 + * @param mixed $key 缓存key + * @param integer|\DateTime $expire 缓存有效期 + * @param string $tag 缓存标签 * @return $this */ public function cache($key = true, $expire = null, $tag = null) { - // 增加快捷调用方式 cache(10) 等同于 cache(true, 10) - if ($key instanceof \DateTime || (is_numeric($key) && is_null($expire))) { - $expire = $key; - $key = true; + if (false === $key) { + return $this; } - if (false !== $key) { - $this->options['cache'] = ['key' => $key, 'expire' => $expire, 'tag' => $tag]; + if ($key instanceof CacheItem) { + $cacheItem = $key; + } else { + if ($key instanceof \DateTimeInterface || (is_int($key) && is_null($expire))) { + $expire = $key; + $key = true; + } + + $cacheItem = new CacheItem(true === $key ? null : $key); + $cacheItem->expire($expire); + $cacheItem->tag($tag); } + $this->options['cache'] = $cacheItem; + return $this; } /** * 指定group查询 * @access public - * @param string $group GROUP + * @param string|array $group GROUP * @return $this */ public function group($group) @@ -1869,10 +1758,10 @@ class Query /** * 指定having查询 * @access public - * @param string $having having + * @param string $having having * @return $this */ - public function having($having) + public function having(string $having) { $this->options['having'] = $having; return $this; @@ -1881,13 +1770,16 @@ class Query /** * 指定查询lock * @access public - * @param bool|string $lock 是否lock + * @param bool|string $lock 是否lock * @return $this */ public function lock($lock = false) { - $this->options['lock'] = $lock; - $this->options['master'] = true; + $this->options['lock'] = $lock; + + if ($lock) { + $this->options['master'] = true; + } return $this; } @@ -1895,10 +1787,10 @@ class Query /** * 指定distinct查询 * @access public - * @param string $distinct 是否唯一 + * @param bool $distinct 是否唯一 * @return $this */ - public function distinct($distinct) + public function distinct(bool $distinct = true) { $this->options['distinct'] = $distinct; return $this; @@ -1907,29 +1799,15 @@ class Query /** * 指定数据表别名 * @access public - * @param mixed $alias 数据表别名 + * @param array|string $alias 数据表别名 * @return $this */ public function alias($alias) { if (is_array($alias)) { - foreach ($alias as $key => $val) { - if (false !== strpos($key, '__')) { - $table = $this->connection->parseSqlTable($key); - } else { - $table = $key; - } - $this->options['alias'][$table] = $val; - } + $this->options['alias'] = $alias; } else { - if (isset($this->options['table'])) { - $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table']; - if (false !== strpos($table, '__')) { - $table = $this->connection->parseSqlTable($table); - } - } else { - $table = $this->getTable(); - } + $table = $this->getTable(); $this->options['alias'][$table] = $alias; } @@ -1940,10 +1818,10 @@ class Query /** * 指定强制索引 * @access public - * @param string $force 索引名称 + * @param string $force 索引名称 * @return $this */ - public function force($force) + public function force(string $force) { $this->options['force'] = $force; return $this; @@ -1952,69 +1830,77 @@ class Query /** * 查询注释 * @access public - * @param string $comment 注释 + * @param string $comment 注释 * @return $this */ - public function comment($comment) + public function comment(string $comment) { $this->options['comment'] = $comment; return $this; } /** - * 获取执行的SQL语句 + * 获取执行的SQL语句而不进行实际的查询 * @access public - * @param boolean $fetch 是否返回sql - * @return $this + * @param bool $fetch 是否返回sql + * @return $this|Fetch */ - public function fetchSql($fetch = true) + public function fetchSql(bool $fetch = true) { $this->options['fetch_sql'] = $fetch; + + if ($fetch) { + return new Fetch($this); + } + return $this; } /** - * 不主动获取数据集 + * 设置是否返回数据集对象 * @access public - * @param bool $pdo 是否返回 PDOStatement 对象 + * @param bool|string $collection 是否返回数据集对象 * @return $this */ - public function fetchPdo($pdo = true) + public function fetchCollection($collection = true) { - $this->options['fetch_pdo'] = $pdo; + $this->options['collection'] = $collection; return $this; } /** - * 设置是否返回数据集对象(支持设置数据集对象类名) + * 设置是否返回数组 * @access public - * @param bool|string $collection 是否返回数据集对象 + * @param bool $asArray 是否返回数组 * @return $this */ - public function fetchCollection($collection = true) + public function fetchArray(bool $asArray = true) { - $this->options['collection'] = $collection; + if ($asArray) { + $this->model = null; + } return $this; } /** * 设置从主服务器读取数据 * @access public + * @param bool $readMaster 是否从主服务器读取 * @return $this */ - public function master() + public function master(bool $readMaster = true) { - $this->options['master'] = true; + $this->options['master'] = $readMaster; return $this; } /** * 设置是否严格检查字段名 * @access public - * @param bool $strict 是否严格检查字段 + * @param bool $strict 是否严格检查字段 * @return $this */ - public function strict($strict = true) + public function strict(bool $strict = true) { $this->options['strict'] = $strict; return $this; @@ -2023,10 +1909,10 @@ class Query /** * 设置查询数据不存在是否抛出异常 * @access public - * @param bool $fail 数据不存在是否抛出异常 + * @param bool $fail 数据不存在是否抛出异常 * @return $this */ - public function failException($fail = true) + public function failException(bool $fail = true) { $this->options['fail'] = $fail; return $this; @@ -2035,10 +1921,10 @@ class Query /** * 设置自增序列名 * @access public - * @param string $sequence 自增序列名 + * @param string $sequence 自增序列名 * @return $this */ - public function sequence($sequence = null) + public function sequence(string $sequence = null) { $this->options['sequence'] = $sequence; return $this; @@ -2047,17 +1933,13 @@ class Query /** * 设置需要隐藏的输出属性 * @access public - * @param mixed $hidden 需要隐藏的字段名 + * @param array $hidden 需要隐藏的字段名 * @return $this */ - public function hidden($hidden) + public function hidden(array $hidden) { - if ($this->model) { - $this->options['hidden'] = $hidden; - return $this; - } - - return $this->field($hidden, true); + $this->options['hidden'] = $hidden; + return $this; } /** @@ -2084,24 +1966,6 @@ class Query return $this; } - /** - * 设置数据字段获取器 - * @access public - * @param string|array $name 字段名 - * @param callable $callback 闭包获取器 - * @return $this - */ - public function withAttr($name, $callback = null) - { - if (is_array($name)) { - $this->options['with_attr'] = $name; - } else { - $this->options['with_attr'][$name] = $callback; - } - - return $this; - } - /** * 设置JSON字段信息 * @access public @@ -2109,41 +1973,18 @@ class Query * @param bool $assoc 是否取出数组 * @return $this */ - public function json(array $json = [], $assoc = false) + public function json(array $json = [], bool $assoc = false) { $this->options['json'] = $json; $this->options['json_assoc'] = $assoc; return $this; } - /** - * 设置字段类型信息 - * @access public - * @param array $type 字段类型信息 - * @return $this - */ - public function setJsonFieldType(array $type) - { - $this->options['field_type'] = $type; - return $this; - } - - /** - * 获取字段类型信息 - * @access public - * @param string $field 字段名 - * @return string|null - */ - public function getJsonFieldType($field) - { - return isset($this->options['field_type'][$field]) ? $this->options['field_type'][$field] : null; - } - /** * 添加查询范围 * @access public - * @param array|string|\Closure $scope 查询范围定义 - * @param array $args 参数 + * @param array|string|Closure $scope 查询范围定义 + * @param array $args 参数 * @return $this */ public function scope($scope, ...$args) @@ -2151,7 +1992,7 @@ class Query // 查询范围的第一个参数始终是当前查询对象 array_unshift($args, $this); - if ($scope instanceof \Closure) { + if ($scope instanceof Closure) { call_user_func_array($scope, $args); return $this; } @@ -2177,23 +2018,23 @@ class Query /** * 指定数据表主键 * @access public - * @param string $pk 主键 + * @param string $pk 主键 * @return $this */ - public function pk($pk) + public function pk(string $pk) { $this->pk = $pk; return $this; } /** - * 查询日期或者时间 + * 添加日期或者时间查询规则 * @access public - * @param string $name 时间表达式 - * @param string|array $rule 时间范围 + * @param string $name 时间表达式 + * @param string|array $rule 时间范围 * @return $this */ - public function timeRule($name, $rule) + public function timeRule(string $name, $rule) { $this->timeRule[$name] = $rule; return $this; @@ -2202,12 +2043,13 @@ class Query /** * 查询日期或者时间 * @access public - * @param string $field 日期字段名 - * @param string|array $op 比较运算符或者表达式 - * @param string|array $range 比较范围 + * @param string $field 日期字段名 + * @param string|array $op 比较运算符或者表达式 + * @param string|array $range 比较范围 + * @param string $logic AND OR * @return $this */ - public function whereTime($field, $op, $range = null) + public function whereTime(string $field, $op, $range = null, string $logic = 'AND') { if (is_null($range)) { if (is_array($op)) { @@ -2227,35 +2069,114 @@ class Query $op = is_array($range) ? 'between' : '>='; } - $this->where($field, strtolower($op) . ' time', $range); + return $this->parseWhereExp($logic, $field, strtolower($op) . ' time', $range, [], true); + } + + /** + * 查询某个时间间隔数据 + * @access protected + * @param string $field 日期字段名 + * @param string $start 开始时间 + * @param string $interval 时间间隔单位 + * @param string $logic AND OR + * @return $this + */ + protected function whereTimeInterval(string $field, string $start, $interval = 'day', string $logic = 'AND') + { + $startTime = strtotime($start); + $endTime = strtotime('+1 ' . $interval, $startTime); - return $this; + return $this->parseWhereExp($logic, $field, 'between time', [$startTime, $endTime], [], true); + } + + /** + * 查询月数据 whereMonth('time_field', '2018-1') + * @access public + * @param string $field 日期字段名 + * @param string $month 月份信息 + * @param string $logic AND OR + * @return $this + */ + public function whereMonth(string $field, string $month, string $logic = 'AND') + { + return $this->whereTimeInterval($field, $month, 'month', $logic); + } + + /** + * 查询年数据 whereYear('time_field', '2018') + * @access public + * @param string $field 日期字段名 + * @param string $year 年份信息 + * @param string $logic AND OR + * @return $this + */ + public function whereYear(string $field, string $year, string $logic = 'AND') + { + return $this->whereTimeInterval($field, $year . '-1-1', 'year', $logic); + } + + /** + * 查询日数据 whereDay('time_field', '2018-1-1') + * @access public + * @param string $field 日期字段名 + * @param string $day 日期信息 + * @param string $logic AND OR + * @return $this + */ + public function whereDay(string $field, string $day, string $logic = 'AND') + { + return $this->whereTimeInterval($field, $day, 'day', $logic); } /** - * 查询日期或者时间范围 + * 查询日期或者时间范围 whereBetweenTime('time_field', '2018-1-1','2018-1-15') * @access public - * @param string $field 日期字段名 - * @param string $startTime 开始时间 - * @param string $endTime 结束时间 + * @param string $field 日期字段名 + * @param string $startTime 开始时间 + * @param string $endTime 结束时间 + * @param string $logic AND OR * @return $this */ - public function whereBetweenTime($field, $startTime, $endTime = null) + public function whereBetweenTime(string $field, $startTime, $endTime = null, string $logic = 'AND') { if (is_null($endTime)) { $time = is_string($startTime) ? strtotime($startTime) : $startTime; $endTime = strtotime('+1 day', $time); } - $this->where($field, 'between time', [$startTime, $endTime]); + return $this->parseWhereExp($logic, $field, 'between time', [$startTime, $endTime], [], true); + } - return $this; + /** + * 查询当前时间在两个时间字段范围 whereBetweenTimeField('start_time', 'end_time') + * @access public + * @param string $startField 开始时间字段 + * @param string $endField 结束时间字段 + * @return $this + */ + public function whereBetweenTimeField(string $startField, string $endField) + { + return $this->whereTime($startField, '<=', time()) + ->whereTime($endField, '>=', time()); + } + + /** + * 查询当前时间不在两个时间字段范围 whereNotBetweenTimeField('start_time', 'end_time') + * @access public + * @param string $startField 开始时间字段 + * @param string $endField 结束时间字段 + * @return $this + */ + public function whereNotBetweenTimeField(string $startField, string $endField) + { + return $this->whereTime($startField, '>', time()) + ->whereTime($endField, '<', time(), 'OR'); } /** * 获取当前数据表的主键 * @access public - * @param string|array $options 数据表名或者查询参数 + * @param string|array $options 数据表名或者查询参数 * @return string|array */ public function getPk($options = '') @@ -2277,7 +2198,7 @@ class Query * @param string $name 绑定标识 * @return $this|string */ - public function bind($value, $type = PDO::PARAM_STR, $name = null) + public function bind($value, int $type = PDO::PARAM_STR, string $name = null) { if (is_array($value)) { $this->bind = array_merge($this->bind, $value); @@ -2291,6 +2212,17 @@ class Query return $this; } + /** + * 检测参数是否已经绑定 + * @access public + * @param string $key 参数名 + * @return bool + */ + public function isBind($key) + { + return isset($this->bind[$key]); + } + /** * 参数绑定 * @access public @@ -2298,11 +2230,11 @@ class Query * @param array $bind 参数绑定 * @return void */ - protected function bindParams(&$sql, array $bind = []) + protected function bindParams(string &$sql, array $bind = []): void { foreach ($bind as $key => $value) { if (is_array($value)) { - $name = $this->bind($value[0], $value[1], isset($value[2]) ? $value[2] : null); + $name = $this->bind($value[0], $value[1], $value[2] ?? null); } else { $name = $this->bind($value); } @@ -2316,33 +2248,9 @@ class Query } /** - * 检测参数是否已经绑定 - * @access public - * @param string $key 参数名 - * @return bool - */ - public function isBind($key) - { - return isset($this->bind[$key]); - } - - /** - * 查询参数赋值 - * @access public - * @param string $name 参数名 - * @param mixed $value 值 - * @return $this - */ - public function option($name, $value) - { - $this->options[$name] = $value; - return $this; - } - - /** - * 查询参数赋值 + * 查询参数批量赋值 * @access protected - * @param array $options 表达式参数 + * @param array $options 表达式参数 * @return $this */ protected function options(array $options) @@ -2354,101 +2262,74 @@ class Query /** * 获取当前的查询参数 * @access public - * @param string $name 参数名 + * @param string $name 参数名 * @return mixed */ - public function getOptions($name = '') + public function getOptions(string $name = '') { if ('' === $name) { return $this->options; } - return isset($this->options[$name]) ? $this->options[$name] : null; + return $this->options[$name] ?? null; } /** * 设置当前的查询参数 * @access public - * @param string $option 参数名 - * @param mixed $value 参数值 + * @param string $option 参数名 + * @param mixed $value 参数值 * @return $this */ - public function setOption($option, $value) + public function setOption(string $option, $value) { $this->options[$option] = $value; return $this; } /** - * 设置关联查询JOIN预查询 + * 设置关联查询 * @access public - * @param string|array $with 关联方法名称 + * @param array $relation 关联名称 * @return $this */ - public function with($with) + public function relation(array $relation) { - if (empty($with)) { - return $this; - } - - if (is_string($with)) { - $with = explode(',', $with); + if ($relation) { + $this->options['relation'] = $relation; } - $first = true; - - /** @var Model $class */ - $class = $this->model; - foreach ($with as $key => $relation) { - $closure = null; - - if ($relation instanceof \Closure) { - // 支持闭包查询过滤关联条件 - $closure = $relation; - $relation = $key; - } elseif (is_array($relation)) { - $relation = $key; - } elseif (is_string($relation) && strpos($relation, '.')) { - list($relation, $subRelation) = explode('.', $relation, 2); - } - - /** @var Relation $model */ - $relation = Db::parseName($relation, 1, false); - $model = $class->$relation(); + return $this; + } - if ($model instanceof OneToOne && 0 == $model->getEagerlyType()) { - $table = $model->getTable(); - $model->removeOption() - ->table($table) - ->eagerly($this, $relation, true, '', $closure, $first); - $first = false; - } + /** + * 设置关联查询JOIN预查询 + * @access public + * @param array $with 关联方法名称(数组) + * @return $this + */ + public function with(array $with) + { + if ($with) { + $this->options['with'] = $with; } - $this->via(); - - $this->options['with'] = $with; - return $this; } /** - * 关联预载入 JOIN方式(不支持嵌套) + * 关联预载入 JOIN方式 * @access protected - * @param string|array $with 关联方法名 + * @param array $with 关联方法名 * @param string $joinType JOIN方式 * @return $this */ - public function withJoin($with, $joinType = '') + public function withJoin(array $with, string $joinType = '') { if (empty($with)) { return $this; } - if (is_string($with)) { - $with = explode(',', $with); - } - $first = true; /** @var Model $class */ @@ -2457,7 +2338,7 @@ class Query $closure = null; $field = true; - if ($relation instanceof \Closure) { + if ($relation instanceof Closure) { // 支持闭包查询过滤关联条件 $closure = $relation; $relation = $key; @@ -2465,7 +2346,7 @@ class Query $field = $relation; $relation = $key; } elseif (is_string($relation) && strpos($relation, '.')) { - list($relation, $subRelation) = explode('.', $relation, 2); + $relation = strstr($relation, '.', true); } /** @var Relation $model */ @@ -2488,6 +2369,33 @@ class Query return $this; } + /** + * 设置数据字段获取器 + * @access public + * @param string $name 字段名 + * @param callable $callback 闭包获取器 + * @return $this + */ + public function withAttr(string $name, callable $callback) + { + $this->options['with_attr'][$name] = $callback; + + return $this; + } + + /** + * 设置数据字段获取器 + * @access public + * @param array $attrs 字段获取器 + * @return $this + */ + public function withAttrs(array $attrs) + { + $this->options['with_attr'] = $attrs; + + return $this; + } + /** * 使用搜索器条件搜索字段 * @access public @@ -2496,18 +2404,18 @@ class Query * @param string $prefix 字段前缀标识 * @return $this */ - public function withSearch(array $fields, array $data = [], $prefix = '') + public function withSearch(array $fields, array $data = [], string $prefix = '') { foreach ($fields as $key => $field) { - if ($field instanceof \Closure) { - $field($this, isset($data[$key]) ? $data[$key] : null, $data, $prefix); + if ($field instanceof Closure) { + $field($this, $data[$key] ?? null, $data, $prefix); } elseif ($this->model) { // 检测搜索器 $fieldName = is_numeric($key) ? $field : $key; $method = 'search' . Db::parseName($fieldName, 1) . 'Attr'; if (method_exists($this->model, $method)) { - $this->model->$method($this, isset($data[$field]) ? $data[$field] : null, $data, $prefix); + $this->model->$method($this, $data[$field] ?? null, $data, $prefix); } } } @@ -2518,15 +2426,17 @@ class Query /** * 关联统计 * @access protected - * @param string|array $relation 关联方法名 + * @param array $relations 关联方法名 * @param string $aggregate 聚合查询方法 * @param string $field 字段 * @param bool $subQuery 是否使用子查询 * @return $this */ - protected function withAggregate($relation, $aggregate = 'count', $field = '*', $subQuery = true) + protected function withAggregate($relations, string $aggregate = 'count', $field = '*', bool $subQuery = true) { - $relations = is_string($relation) ? explode(',', $relation) : $relation; + if (is_string($relations)) { + $relations = explode(',', $relations); + } if (!$subQuery) { $this->options['with_count'][] = [$relations, $aggregate, $field]; @@ -2536,8 +2446,9 @@ class Query } foreach ($relations as $key => $relation) { - $closure = null; - if ($relation instanceof \Closure) { + $closure = $aggregateField = null; + + if ($relation instanceof Closure) { $closure = $relation; $relation = $key; } elseif (!is_int($key)) { @@ -2545,13 +2456,14 @@ class Query $relation = $key; } - if (!isset($aggregateField)) { + $relation = Db::parseName($relation, 1, false); + + $count = '(' . $this->model->$relation()->getRelationCountQuery($closure, $aggregate, $field, $aggregateField) . ')'; + + if (empty($aggregateField)) { $aggregateField = Db::parseName($relation) . '_' . $aggregate; } - $relation = Db::parseName($relation, 1, false); - $count = '(' . $this->model->$relation()->getRelationCountQuery($closure, $aggregate, $field) . ')'; - $this->field([$count => $aggregateField]); } } @@ -2566,7 +2478,7 @@ class Query * @param bool $subQuery 是否使用子查询 * @return $this */ - public function withCount($relation, $subQuery = true) + public function withCount($relation, bool $subQuery = true) { return $this->withAggregate($relation, 'count', '*', $subQuery); } @@ -2579,7 +2491,7 @@ class Query * @param bool $subQuery 是否使用子查询 * @return $this */ - public function withSum($relation, $field, $subQuery = true) + public function withSum($relation, string $field, bool $subQuery = true) { return $this->withAggregate($relation, 'sum', $field, $subQuery); } @@ -2592,7 +2504,7 @@ class Query * @param bool $subQuery 是否使用子查询 * @return $this */ - public function withMax($relation, $field, $subQuery = true) + public function withMax($relation, string $field, bool $subQuery = true) { return $this->withAggregate($relation, 'max', $field, $subQuery); } @@ -2605,7 +2517,7 @@ class Query * @param bool $subQuery 是否使用子查询 * @return $this */ - public function withMin($relation, $field, $subQuery = true) + public function withMin($relation, string $field, bool $subQuery = true) { return $this->withAggregate($relation, 'min', $field, $subQuery); } @@ -2618,7 +2530,7 @@ class Query * @param bool $subQuery 是否使用子查询 * @return $this */ - public function withAvg($relation, $field, $subQuery = true) + public function withAvg($relation, string $field, bool $subQuery = true) { return $this->withAggregate($relation, 'avg', $field, $subQuery); } @@ -2630,7 +2542,8 @@ class Query * $query->withField("id,name"); * }]) * - * @param string | array $field 指定获取的字段 + * @access public + * @param string | array $field 指定获取的字段 * @return $this */ public function withField($field) @@ -2643,55 +2556,28 @@ class Query /** * 设置当前字段添加的表别名 * @access public - * @param string $via + * @param string $via * @return $this */ - public function via($via = '') + public function via(string $via = '') { $this->options['via'] = $via; return $this; } - /** - * 设置关联查询 - * @access public - * @param string|array $relation 关联名称 - * @return $this - */ - public function relation($relation) - { - if (empty($relation)) { - return $this; - } - - if (is_string($relation)) { - $relation = explode(',', $relation); - } - - if (isset($this->options['relation'])) { - $this->options['relation'] = array_merge($this->options['relation'], $relation); - } else { - $this->options['relation'] = $relation; - } - - return $this; - } - /** * 插入记录 * @access public - * @param array $data 数据 - * @param boolean $replace 是否replace - * @param boolean $getLastInsID 返回自增主键 - * @param string $sequence 自增序列名 - * @return integer|string + * @param array $data 数据 + * @param boolean $replace 是否replace + * @param boolean $getLastInsID 返回自增主键 + * @param string $sequence 自增序列名 + * @return integer */ - public function insert(array $data = [], $replace = false, $getLastInsID = false, $sequence = null) + public function insert(array $data = [], bool $replace = false, bool $getLastInsID = false, string $sequence = null) { - $this->parseOptions(); - - $this->options['data'] = array_merge($this->options['data'], $data); + $this->options['data'] = array_merge($this->options['data'] ?? [], $data); return $this->connection->insert($this, $replace, $getLastInsID, $sequence); } @@ -2699,12 +2585,12 @@ class Query /** * 插入记录并获取自增ID * @access public - * @param array $data 数据 - * @param boolean $replace 是否replace - * @param string $sequence 自增序列名 + * @param array $data 数据 + * @param boolean $replace 是否replace + * @param string $sequence 自增序列名 * @return integer|string */ - public function insertGetId(array $data, $replace = false, $sequence = null) + public function insertGetId(array $data, bool $replace = false, string $sequence = null) { return $this->insert($data, $replace, true, $sequence); } @@ -2712,14 +2598,20 @@ class Query /** * 批量插入记录 * @access public - * @param array $dataSet 数据集 - * @param boolean $replace 是否replace - * @param integer $limit 每次写入数据限制 - * @return integer|string + * @param array $dataSet 数据集 + * @param boolean $replace 是否replace + * @param integer $limit 每次写入数据限制 + * @return integer */ - public function insertAll(array $dataSet, $replace = false, $limit = null) + public function insertAll(array $dataSet = [], bool $replace = false, int $limit = null): int { - $this->parseOptions(); + if (empty($dataSet)) { + $dataSet = $this->options['data'] ?? []; + } + + if (empty($limit) && !empty($this->options['limit'])) { + $limit = (int) $this->options['limit']; + } return $this->connection->insertAll($this, $dataSet, $replace, $limit); } @@ -2727,31 +2619,27 @@ class Query /** * 通过Select方式插入记录 * @access public - * @param string $fields 要插入的数据表字段名 - * @param string $table 要插入的数据表名 - * @return integer|string + * @param array $fields 要插入的数据表字段名 + * @param string $table 要插入的数据表名 + * @return integer * @throws PDOException */ - public function selectInsert($fields, $table) + public function selectInsert(array $fields, string $table): int { - $this->parseOptions(); - return $this->connection->selectInsert($this, $fields, $table); } /** * 更新记录 * @access public - * @param mixed $data 数据 - * @return integer|string + * @param mixed $data 数据 + * @return integer * @throws Exception * @throws PDOException */ - public function update(array $data = []) + public function update(array $data = []): int { - $this->parseOptions(); - - $this->options['data'] = array_merge($this->options['data'], $data); + $this->options['data'] = array_merge($this->options['data'] ?? [], $data); return $this->connection->update($this); } @@ -2759,15 +2647,13 @@ class Query /** * 删除记录 * @access public - * @param mixed $data 表达式 true 表示强制删除 + * @param mixed $data 表达式 true 表示强制删除 * @return int * @throws Exception * @throws PDOException */ - public function delete($data = null) + public function delete($data = null): int { - $this->parseOptions(); - if (!is_null($data) && true !== $data) { // AR模式分析主键条件 $this->parsePkWhere($data); @@ -2776,10 +2662,12 @@ class Query if (!empty($this->options['soft_delete'])) { // 软删除 list($field, $condition) = $this->options['soft_delete']; - unset($this->options['soft_delete']); - $this->options['data'] = [$field => $condition]; + if ($condition) { + unset($this->options['soft_delete']); + $this->options['data'] = [$field => $condition]; - return $this->connection->update($this); + return $this->connection->update($this); + } } $this->options['data'] = $data; @@ -2790,30 +2678,21 @@ class Query /** * 执行查询但只返回PDOStatement对象 * @access public - * @return \PDOStatement|string + * @return PDOStatement */ - public function getPdo() + public function getPdo(): PDOStatement { - $this->parseOptions(); - return $this->connection->pdo($this); } /** * 使用游标查找记录 * @access public - * @param array|string|Query|\Closure $data + * @param mixed $data * @return \Generator */ public function cursor($data = null) { - if ($data instanceof \Closure) { - $data($this); - $data = null; - } - - $this->parseOptions(); - if (!is_null($data)) { // 主键条件分析 $this->parsePkWhere($data); @@ -2829,27 +2708,15 @@ class Query /** * 查找记录 * @access public - * @param array|string|Query|\Closure $data - * @return Collection|array|\PDOStatement|string + * @param mixed $data + * @return Collection|array|ModelCollection * @throws DbException * @throws ModelNotFoundException * @throws DataNotFoundException */ public function select($data = null) { - if ($data instanceof Query) { - return $data->select(); - } elseif ($data instanceof \Closure) { - $data($this); - $data = null; - } - - $this->parseOptions(); - - if (false === $data) { - // 用于子查询 不查询只返回SQL - $this->options['fetch_sql'] = true; - } elseif (!is_null($data)) { + if (!is_null($data)) { // 主键条件分析 $this->parsePkWhere($data); } @@ -2858,8 +2725,9 @@ class Query $resultSet = $this->connection->select($this); - if (!empty($this->options['fetch_sql'])) { - return $resultSet; + // 返回结果处理 + if (!empty($this->options['fail']) && count($resultSet) == 0) { + $this->throwNotFound($this->options); } // 数据列表读取后的处理 @@ -2870,11 +2738,6 @@ class Query $this->resultSet($resultSet); } - // 返回结果处理 - if (!empty($this->options['fail']) && count($resultSet) == 0) { - $this->throwNotFound($this->options); - } - return $resultSet; } @@ -2884,14 +2747,14 @@ class Query * @param array $resultSet 数据集 * @return ModelCollection */ - protected function resultSetToModelCollection(array $resultSet) + protected function resultSetToModelCollection(array $resultSet): ModelCollection { if (!empty($this->options['collection']) && is_string($this->options['collection'])) { $collection = $this->options['collection']; } if (empty($resultSet)) { - return $this->model->toCollection([], isset($collection) ? $collection : null); + return $this->model->toCollection([], $collection ?? null); } // 检查动态获取器 @@ -2906,7 +2769,7 @@ class Query } } - $withRelationAttr = isset($withRelationAttr) ? $withRelationAttr : []; + $withRelationAttr = $withRelationAttr ?? []; foreach ($resultSet as $key => &$result) { // 数据转换为模型对象 @@ -2919,12 +2782,12 @@ class Query } if (!empty($this->options['with_join'])) { - // JOIN预载入 + // 预载入 $result->eagerlyResultSet($resultSet, $this->options['with_join'], $withRelationAttr, true); } // 模型数据集转换 - return $result->toCollection($resultSet, isset($collection) ? $collection : null); + return $this->model->toCollection($resultSet, $collection ?? null); } /** @@ -2933,7 +2796,7 @@ class Query * @param array $resultSet * @return void */ - protected function resultSet(&$resultSet) + protected function resultSet(array &$resultSet): void { if (!empty($this->options['json'])) { foreach ($resultSet as &$result) { @@ -2947,7 +2810,13 @@ class Query } } - if (!empty($this->options['collection']) || 'collection' == $this->connection->getConfig('resultset_type')) { + if (!empty($this->options['visible']) || !empty($this->options['hidden'])) { + foreach ($resultSet as &$result) { + $this->filterResult($result); + } + } + + if (!empty($this->options['collection'])) { // 返回Collection对象 $resultSet = new Collection($resultSet); } @@ -2956,23 +2825,14 @@ class Query /** * 查找单条记录 * @access public - * @param array|string|Query|\Closure $data - * @return array|null|\PDOStatement|string|Model + * @param mixed $data + * @return array|null|Model * @throws DbException * @throws ModelNotFoundException * @throws DataNotFoundException */ public function find($data = null) { - if ($data instanceof Query) { - return $data->find(); - } elseif ($data instanceof \Closure) { - $data($this); - $data = null; - } - - $this->parseOptions(); - if (!is_null($data)) { // AR模式分析主键条件 $this->parsePkWhere($data); @@ -2982,10 +2842,6 @@ class Query $result = $this->connection->find($this); - if (!empty($this->options['fetch_sql'])) { - return $result; - } - // 数据处理 if (empty($result)) { return $this->resultToEmpty(); @@ -3011,112 +2867,60 @@ class Query */ protected function resultToEmpty() { - if (!empty($this->options['allow_empty'])) { - return !empty($this->model) ? $this->model->newInstance([], $this->getModelUpdateCondition($this->options)) : []; - } elseif (!empty($this->options['fail'])) { + if (!empty($this->options['fail'])) { $this->throwNotFound($this->options); } - } - - /** - * 查找单条记录 - * @access public - * @param mixed $data 主键值或者查询条件(闭包) - * @param mixed $with 关联预查询 - * @param bool $cache 是否缓存 - * @param bool $failException 是否抛出异常 - * @return static|null - * @throws exception\DbException - */ - public function get($data, $with = [], $cache = false, $failException = false) - { - if (is_null($data)) { - return; - } - if (true === $with || is_int($with)) { - $cache = $with; - $with = []; - } - - return $this->parseQuery($data, $with, $cache) - ->failException($failException) - ->find($data); + return !empty($this->model) ? $this->model->newInstance([], true) : []; } /** - * 查找单条记录 如果不存在直接抛出异常 - * @access public - * @param mixed $data 主键值或者查询条件(闭包) - * @param mixed $with 关联预查询 - * @param bool $cache 是否缓存 - * @return static|null - * @throws exception\DbException + * 获取模型的更新条件 + * @access protected + * @param array $options 查询参数 */ - public function getOrFail($data, $with = [], $cache = false) + protected function getModelUpdateCondition(array $options) { - return $this->get($data, $with, $cache, true); + return $options['where']['AND'] ?? null; } /** - * 查找所有记录 - * @access public - * @param mixed $data 主键列表或者查询条件(闭包) - * @param array|string $with 关联预查询 - * @param bool $cache 是否缓存 - * @return static[]|false - * @throws exception\DbException + * 处理数据 + * @access protected + * @param array $result 查询数据 + * @return void */ - public function all($data = null, $with = [], $cache = false) + protected function result(array &$result): void { - if (true === $with || is_int($with)) { - $cache = $with; - $with = []; + if (!empty($this->options['json'])) { + $this->jsonResult($result, $this->options['json'], true); } - return $this->parseQuery($data, $with, $cache)->select($data); - } - - /** - * 分析查询表达式 - * @access public - * @param mixed $data 主键列表或者查询条件(闭包) - * @param string $with 关联预查询 - * @param bool $cache 是否缓存 - * @return Query - */ - protected function parseQuery(&$data, $with, $cache) - { - $result = $this->with($with)->cache($cache); - - if ((is_array($data) && key($data) !== 0) || $data instanceof Where) { - $result = $result->where($data); - $data = null; - } elseif ($data instanceof \Closure) { - $data($result); - $data = null; - } elseif ($data instanceof Query) { - $result = $data->with($with)->cache($cache); - $data = null; + if (!empty($this->options['with_attr'])) { + $this->getResultAttr($result, $this->options['with_attr']); } - return $result; + $this->filterResult($result); } /** - * 处理数据 + * 处理数据的可见和隐藏 * @access protected * @param array $result 查询数据 * @return void */ - protected function result(&$result) + protected function filterResult(&$result): void { - if (!empty($this->options['json'])) { - $this->jsonResult($result, $this->options['json'], true); - } - - if (!empty($this->options['with_attr'])) { - $this->getResultAttr($result, $this->options['with_attr']); + if (!empty($this->options['visible'])) { + foreach ($this->options['visible'] as $key) { + $array[] = $key; + } + $result = array_intersect_key($result, array_flip($array)); + } elseif (!empty($this->options['hidden'])) { + foreach ($this->options['hidden'] as $key) { + $array[] = $key; + } + $result = array_diff_key($result, array_flip($array)); } } @@ -3127,7 +2931,7 @@ class Query * @param array $withAttr 字段获取器 * @return void */ - protected function getResultAttr(&$result, $withAttr = []) + protected function getResultAttr(array &$result, array $withAttr = []): void { foreach ($withAttr as $name => $closure) { $name = Db::parseName($name); @@ -3137,10 +2941,10 @@ class Query list($key, $field) = explode('.', $name); if (isset($result[$key])) { - $result[$key][$field] = $closure(isset($result[$key][$field]) ? $result[$key][$field] : null, $result[$key]); + $result[$key][$field] = $closure($result[$key][$field] ?? null, $result[$key]); } } else { - $result[$name] = $closure(isset($result[$name]) ? $result[$name] : null, $result); + $result[$name] = $closure($result[$name] ?? null, $result); } } } @@ -3154,33 +2958,39 @@ class Query * @param array $withRelationAttr 关联获取器 * @return void */ - protected function jsonResult(&$result, $json = [], $assoc = false, $withRelationAttr = []) + protected function jsonResult(array &$result, array $json = [], bool $assoc = false, array $withRelationAttr = []): void { foreach ($json as $name) { - if (isset($result[$name])) { - $result[$name] = json_decode($result[$name], $assoc); + if (!isset($result[$name])) { + continue; + } - if (isset($withRelationAttr[$name])) { - foreach ($withRelationAttr[$name] as $key => $closure) { - $data = get_object_vars($result[$name]); - $result[$name]->$key = $closure(isset($result[$name]->$key) ? $result[$name]->$key : null, $data); - } - } + $result[$name] = json_decode($result[$name], $assoc); + + if (!isset($withRelationAttr[$name])) { + continue; + } + + foreach ($withRelationAttr[$name] as $key => $closure) { + $data = get_object_vars($result[$name]); + + $result[$name]->$key = $closure($result[$name]->$key ?? null, $data); } } } /** * 查询数据转换为模型对象 - * @access public - * @param array $result 查询数据 - * @param array $options 查询参数 - * @param bool $resultSet 是否为数据集查询 + * @access protected + * @param array $result 查询数据 + * @param array $options 查询参数 + * @param bool $resultSet 是否为数据集查询 * @param array $withRelationAttr 关联字段获取器 * @return void */ - protected function resultToModel(&$result, $options = [], $resultSet = false, $withRelationAttr = []) + protected function resultToModel(array &$result, array $options = [], bool $resultSet = false, array $withRelationAttr = []): void { + // 动态获取器 if (!empty($options['with_attr']) && empty($withRelationAttr)) { foreach ($options['with_attr'] as $name => $val) { if (strpos($name, '.')) { @@ -3192,11 +3002,12 @@ class Query } } + // JSON 数据处理 if (!empty($options['json'])) { $this->jsonResult($result, $options['json'], $options['json_assoc'], $withRelationAttr); } - $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options)); + $result = $this->model->newInstance($result, true, $resultSet ? null : $this->getModelUpdateCondition($options)); // 动态获取器 if (!empty($options['with_attr'])) { @@ -3238,23 +3049,14 @@ class Query } /** - * 获取模型的更新条件 + * 查询失败 抛出异常 * @access protected * @param array $options 查询参数 - */ - protected function getModelUpdateCondition(array $options) - { - return isset($options['where']['AND']) ? $options['where']['AND'] : null; - } - - /** - * 查询失败 抛出异常 - * @access public - * @param array $options 查询参数 + * @return void * @throws ModelNotFoundException * @throws DataNotFoundException */ - protected function throwNotFound($options = []) + protected function throwNotFound(array $options = []): void { if (!empty($this->model)) { $class = get_class($this->model); @@ -3268,8 +3070,8 @@ class Query /** * 查找多条记录 如果不存在则抛出异常 * @access public - * @param array|string|Query|\Closure $data - * @return array|\PDOStatement|string|Model + * @param array|string|Query|Closure $data + * @return array|PDOStatement|string|Model * @throws DbException * @throws ModelNotFoundException * @throws DataNotFoundException @@ -3282,8 +3084,8 @@ class Query /** * 查找单条记录 如果不存在则抛出异常 * @access public - * @param array|string|Query|\Closure $data - * @return array|\PDOStatement|string|Model + * @param array|string|Query|Closure $data + * @return array|PDOStatement|string|Model * @throws DbException * @throws ModelNotFoundException * @throws DataNotFoundException @@ -3293,31 +3095,17 @@ class Query return $this->failException(true)->find($data); } - /** - * 查找单条记录 如果不存在则抛出异常 - * @access public - * @param array|string|Query|\Closure $data - * @return array|\PDOStatement|string|Model - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException - */ - public function findOrEmpty($data = null) - { - return $this->allowEmpty(true)->find($data); - } - /** * 分批数据返回处理 * @access public - * @param integer $count 每次处理的数据数量 - * @param callable $callback 处理回调方法 - * @param string|array $column 分批处理的字段名 - * @param string $order 字段排序 - * @return boolean + * @param integer $count 每次处理的数据数量 + * @param callable $callback 处理回调方法 + * @param string|array $column 分批处理的字段名 + * @param string $order 字段排序 + * @return bool * @throws DbException */ - public function chunk($count, $callback, $column = null, $order = 'asc') + public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool { $options = $this->getOptions(); $column = $column ?: $this->getPk($options); @@ -3376,10 +3164,10 @@ class Query /** * 获取绑定的参数 并清空 * @access public - * @param bool $clear + * @param bool $clear * @return array */ - public function getBind($clear = true) + public function getBind(bool $clear = true): array { $bind = $this->bind; if ($clear) { @@ -3392,27 +3180,23 @@ class Query /** * 创建子查询SQL * @access public - * @param bool $sub + * @param bool $sub * @return string * @throws DbException */ - public function buildSql($sub = true) + public function buildSql(bool $sub = true): string { - return $sub ? '( ' . $this->select(false) . ' )' : $this->select(false); + return $sub ? '( ' . $this->fetchSql()->select() . ' )' : $this->fetchSql()->select(); } /** * 视图查询处理 - * @access public - * @param array $options 查询参数 + * @access protected + * @param array $options 查询参数 * @return void */ - protected function parseView(&$options) + protected function parseView(array &$options): void { - if (!isset($options['map'])) { - return; - } - foreach (['AND', 'OR'] as $logic) { if (isset($options['where'][$logic])) { foreach ($options['where'][$logic] as $key => $val) { @@ -3428,11 +3212,8 @@ class Query if (isset($options['order'])) { // 视图查询排序处理 - if (is_string($options['order'])) { - $options['order'] = explode(',', $options['order']); - } foreach ($options['order'] as $key => $val) { - if (is_numeric($key)) { + if (is_numeric($key) && is_string($val)) { if (strpos($val, ' ')) { list($field, $sort) = explode(' ', $val); if (array_key_exists($field, $options['map'])) { @@ -3454,59 +3235,44 @@ class Query /** * 把主键值转换为查询条件 支持复合主键 * @access public - * @param array|string $data 主键数据 + * @param array|string $data 主键数据 * @return void * @throws Exception */ - public function parsePkWhere($data) + public function parsePkWhere($data): void { $pk = $this->getPk($this->options); - // 获取当前数据表 - $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table']; + if (is_string($pk)) { + // 获取数据表 + if (empty($this->options['table'])) { + $this->options['table'] = $this->getTable(); + } - if (!empty($this->options['alias'][$table])) { - $alias = $this->options['alias'][$table]; - } + $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table']; + + if (!empty($this->options['alias'][$table])) { + $alias = $this->options['alias'][$table]; + } - if (is_string($pk)) { $key = isset($alias) ? $alias . '.' . $pk : $pk; // 根据主键查询 - if (is_array($data)) { - $where[$pk] = isset($data[$pk]) ? [$key, '=', $data[$pk]] : [$key, 'in', $data]; - } else { - $where[$pk] = strpos($data, ',') ? [$key, 'IN', $data] : [$key, '=', $data]; - } - } elseif (is_array($pk) && is_array($data) && !empty($data)) { - // 根据复合主键查询 - foreach ($pk as $key) { - if (isset($data[$key])) { - $attr = isset($alias) ? $alias . '.' . $key : $key; - $where[$key] = [$attr, '=', $data[$key]]; - } else { - throw new Exception('miss complex primary data'); - } - } - } + $where[$pk] = is_array($data) ? [$key, 'in', $data] : [$key, '=', $data]; - if (!empty($where)) { if (isset($this->options['where']['AND'])) { $this->options['where']['AND'] = array_merge($this->options['where']['AND'], $where); } else { $this->options['where']['AND'] = $where; } } - - return; } /** * 分析表达式(可用于查询或者写入操作) - * @access protected - * @param Query $query 查询对象 + * @access public * @return array */ - protected function parseOptions() + public function parseOptions(): array { $options = $this->getOptions(); @@ -3526,27 +3292,23 @@ class Query $options['field'] = '*'; } - foreach (['data', 'order'] as $name) { + foreach (['data', 'order', 'join', 'union'] as $name) { if (!isset($options[$name])) { $options[$name] = []; } } if (!isset($options['strict'])) { - $options['strict'] = $this->getConfig('fields_strict'); + $options['strict'] = $this->connection->getConfig('fields_strict'); } - foreach (['master', 'lock', 'fetch_pdo', 'distinct'] as $name) { + foreach (['master', 'lock', 'fetch_sql', 'distinct', 'procedure'] as $name) { if (!isset($options[$name])) { $options[$name] = false; } } - if (isset(static::$readMaster['*']) || (is_string($options['table']) && isset(static::$readMaster[$options['table']]))) { - $options['master'] = true; - } - - foreach (['join', 'union', 'group', 'having', 'limit', 'force', 'comment'] as $name) { + foreach (['group', 'having', 'limit', 'force', 'comment'] as $name) { if (!isset($options[$name])) { $options[$name] = ''; } @@ -3556,7 +3318,7 @@ class Query // 根据页数计算limit list($page, $listRows) = $options['page']; $page = $page > 0 ? $page : 1; - $listRows = $listRows > 0 ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20); + $listRows = $listRows ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20); $offset = $listRows * ($page - 1); $options['limit'] = $offset . ',' . $listRows; } @@ -3569,24 +3331,25 @@ class Query /** * 注册回调方法 * @access public - * @param string $event 事件名 - * @param callable $callback 回调方法 + * @param string $event 事件名 + * @param callable $callback 回调方法 * @return void */ - public static function event($event, $callback) + public static function event(string $event, callable $callback): void { self::$event[$event] = $callback; } /** * 触发事件 - * @access protected - * @param string $event 事件名 + * @access public + * @param string $event 事件名 * @return bool */ - public function trigger($event) + public function trigger(string $event) { $result = false; + if (isset(self::$event[$event])) { $result = call_user_func_array(self::$event[$event], [$this]); } diff --git a/src/db/Where.php b/src/db/Where.php index 1f4def6..b4ed682 100644 --- a/src/db/Where.php +++ b/src/db/Where.php @@ -2,12 +2,13 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\db; @@ -22,7 +23,7 @@ class Where implements ArrayAccess protected $where = []; /** - * 是否需要增加括号 + * 是否需要把查询条件两边增加括号 * @var bool */ protected $enclose = false; @@ -33,7 +34,7 @@ class Where implements ArrayAccess * @param array $where 查询条件数组 * @param bool $enclose 是否增加括号 */ - public function __construct(array $where = [], $enclose = false) + public function __construct(array $where = [], bool $enclose = false) { $this->where = $where; $this->enclose = $enclose; @@ -45,7 +46,7 @@ class Where implements ArrayAccess * @param bool $enclose * @return $this */ - public function enclose($enclose = true) + public function enclose(bool $enclose = true) { $this->enclose = $enclose; return $this; @@ -56,7 +57,7 @@ class Where implements ArrayAccess * @access public * @return array */ - public function parse() + public function parse(): array { $where = []; @@ -79,10 +80,10 @@ class Where implements ArrayAccess * 分析查询表达式 * @access protected * @param string $field 查询字段 - * @param array $where 查询表达式 + * @param array $where 查询条件 * @return array */ - protected function parseItem($field, $where = []) + protected function parseItem(string $field, array $where = []): array { $op = $where[0]; $condition = isset($where[1]) ? $where[1] : null; @@ -91,12 +92,12 @@ class Where implements ArrayAccess // 同一字段多条件查询 array_unshift($where, $field); } elseif (is_null($condition)) { - if (in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) { + if (is_string($op) && in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) { // null查询 $where = [$field, $op, '']; - } elseif (in_array($op, ['=', 'eq', 'EQ', null], true)) { + } elseif (is_null($op) || '=' == $op) { $where = [$field, 'NULL', '']; - } elseif (in_array($op, ['<>', 'neq', 'NEQ'], true)) { + } elseif ('<>' == $op) { $where = [$field, 'NOTNULL', '']; } else { // 字段相等查询 @@ -129,14 +130,14 @@ class Where implements ArrayAccess */ public function __get($name) { - return isset($this->where[$name]) ? $this->where[$name] : null; + return $this->where[$name] ?? null; } /** * 检测数据对象的值 * @access public * @param string $name 名称 - * @return boolean + * @return bool */ public function __isset($name) { diff --git a/src/db/builder/Mysql.php b/src/db/builder/Mysql.php index 85bab3e..9d52179 100644 --- a/src/db/builder/Mysql.php +++ b/src/db/builder/Mysql.php @@ -2,12 +2,13 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\db\builder; @@ -47,7 +48,7 @@ class Mysql extends Builder * @param bool $replace 是否replace * @return string */ - public function insertAll(Query $query, $dataSet, $replace = false) + public function insertAll(Query $query, array $dataSet, bool $replace = false): string { $options = $query->getOptions(); @@ -62,7 +63,7 @@ class Mysql extends Builder $bind = $this->connection->getFieldsBind($options['table']); foreach ($dataSet as $data) { - $data = $this->parseData($query, $data, $allowFields, $bind); + $data = $this->parseData($query, $data, $allowFields); $values[] = '( ' . implode(',', array_values($data)) . ' )'; @@ -93,12 +94,12 @@ class Mysql extends Builder * @access protected * @param Query $query 查询对象 * @param string $key - * @param string $exp - * @param Expression $value + * @param Expression $exp + * @param mixed $value * @param string $field * @return string */ - protected function parseRegexp(Query $query, $key, $exp, Expression $value, $field) + protected function parseRegexp(Query $query, string $key, string $exp, Expression $value, string $field): string { return $key . ' ' . $exp . ' ' . $value->getValue(); } @@ -106,14 +107,14 @@ class Mysql extends Builder /** * 字段和表名处理 * @access public - * @param Query $query 查询对象 - * @param string $key + * @param Query $query 查询对象 + * @param mixed $key 字段名 * @param bool $strict 严格检测 * @return string */ - public function parseKey(Query $query, $key, $strict = false) + public function parseKey(Query $query, $key, bool $strict = false): string { - if (is_numeric($key)) { + if (is_int($key)) { return $key; } elseif ($key instanceof Expression) { return $key->getValue(); @@ -125,7 +126,7 @@ class Mysql extends Builder // JSON字段支持 list($field, $name) = explode('->', $key, 2); - return 'json_extract(' . $field . ', \'$.' . str_replace('->', '.', $name) . '\')'; + return 'json_extract(' . $this->parseKey($query, $field) . ', \'$.' . str_replace('->', '.', $name) . '\')'; } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { list($table, $key) = explode('.', $key, 2); @@ -145,7 +146,7 @@ class Mysql extends Builder throw new Exception('not support data:' . $key); } - if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) { + if ('*' != $key && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) { $key = '`' . $key . '`'; } @@ -163,10 +164,10 @@ class Mysql extends Builder /** * 随机排序 * @access protected - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return string */ - protected function parseRand(Query $query) + protected function parseRand(Query $query): string { return 'rand()'; } diff --git a/src/db/builder/Pgsql.php b/src/db/builder/Pgsql.php index 65fb231..f09e22a 100644 --- a/src/db/builder/Pgsql.php +++ b/src/db/builder/Pgsql.php @@ -2,16 +2,18 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\db\builder; use think\db\Builder; +use think\db\Expression; use think\db\Query; /** @@ -26,11 +28,11 @@ class Pgsql extends Builder /** * limit分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $limit + * @param Query $query 查询对象 + * @param mixed $limit * @return string */ - public function parseLimit(Query $query, $limit) + public function parseLimit(Query $query, string $limit): string { $limitStr = ''; @@ -54,9 +56,9 @@ class Pgsql extends Builder * @param bool $strict 严格检测 * @return string */ - public function parseKey(Query $query, $key, $strict = false) + public function parseKey(Query $query, $key, bool $strict = false): string { - if (is_numeric($key)) { + if (is_int($key)) { return $key; } elseif ($key instanceof Expression) { return $key->getValue(); @@ -64,9 +66,9 @@ class Pgsql extends Builder $key = trim($key); - if (strpos($key, '$.') && false === strpos($key, '(')) { + if (strpos($key, '->') && false === strpos($key, '(')) { // JSON字段支持 - list($field, $name) = explode('$.', $key); + list($field, $name) = explode('->', $key); $key = $field . '->>\'' . $name . '\''; } elseif (strpos($key, '.')) { list($table, $key) = explode('.', $key, 2); @@ -93,10 +95,10 @@ class Pgsql extends Builder /** * 随机排序 * @access protected - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return string */ - protected function parseRand(Query $query) + protected function parseRand(Query $query): string { return 'RANDOM()'; } diff --git a/src/db/builder/Sqlite.php b/src/db/builder/Sqlite.php index bdbb31f..70c79ee 100644 --- a/src/db/builder/Sqlite.php +++ b/src/db/builder/Sqlite.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -12,6 +12,7 @@ namespace think\db\builder; use think\db\Builder; +use think\db\Expression; use think\db\Query; /** @@ -19,15 +20,14 @@ use think\db\Query; */ class Sqlite extends Builder { - /** * limit * @access public - * @param Query $query 查询对象 - * @param mixed $limit + * @param Query $query 查询对象 + * @param mixed $limit * @return string */ - public function parseLimit(Query $query, $limit) + public function parseLimit(Query $query, string $limit): string { $limitStr = ''; @@ -46,10 +46,10 @@ class Sqlite extends Builder /** * 随机排序 * @access protected - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return string */ - protected function parseRand(Query $query) + protected function parseRand(Query $query): string { return 'RANDOM()'; } @@ -58,19 +58,20 @@ class Sqlite extends Builder * 字段和表名处理 * @access public * @param Query $query 查询对象 - * @param mixed $key 字段名 + * @param string $key 字段名 * @param bool $strict 严格检测 * @return string */ - public function parseKey(Query $query, $key, $strict = false) + public function parseKey(Query $query, $key, bool $strict = false): string { - if (is_numeric($key)) { + if (is_int($key)) { return $key; } elseif ($key instanceof Expression) { return $key->getValue(); } $key = trim($key); + if (strpos($key, '.')) { list($table, $key) = explode('.', $key, 2); diff --git a/src/db/builder/Sqlsrv.php b/src/db/builder/Sqlsrv.php index 58da40a..1351435 100644 --- a/src/db/builder/Sqlsrv.php +++ b/src/db/builder/Sqlsrv.php @@ -12,6 +12,7 @@ namespace think\db\builder; use think\db\Builder; +use think\db\Expression; use think\db\Query; use think\Exception; @@ -30,11 +31,11 @@ class Sqlsrv extends Builder /** * order分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $order + * @param Query $query 查询对象 + * @param mixed $order * @return string */ - protected function parseOrder(Query $query, $order) + protected function parseOrder(Query $query, array $order): string { if (empty($order)) { return ' ORDER BY rand()'; @@ -65,10 +66,10 @@ class Sqlsrv extends Builder /** * 随机排序 * @access protected - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return string */ - protected function parseRand(Query $query) + protected function parseRand(Query $query): string { return 'rand()'; } @@ -81,9 +82,9 @@ class Sqlsrv extends Builder * @param bool $strict 严格检测 * @return string */ - public function parseKey(Query $query, $key, $strict = false) + public function parseKey(Query $query, $key, bool $strict = false): string { - if (is_numeric($key)) { + if (is_int($key)) { return $key; } elseif ($key instanceof Expression) { return $key->getValue(); @@ -110,7 +111,7 @@ class Sqlsrv extends Builder throw new Exception('not support data:' . $key); } - if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) { + if ('*' != $key && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) { $key = '[' . $key . ']'; } @@ -124,11 +125,11 @@ class Sqlsrv extends Builder /** * limit * @access protected - * @param Query $query 查询对象 - * @param mixed $limit + * @param Query $query 查询对象 + * @param mixed $limit * @return string */ - protected function parseLimit(Query $query, $limit) + protected function parseLimit(Query $query, string $limit): string { if (empty($limit)) { return ''; @@ -145,7 +146,7 @@ class Sqlsrv extends Builder return 'WHERE ' . $limitStr; } - public function selectInsert(Query $query, $fields, $table) + public function selectInsert(Query $query, array $fields, string $table): string { $this->selectSql = $this->selectInsertSql; diff --git a/src/db/connector/Mysql.php b/src/db/connector/Mysql.php index 4d1cf34..dbdf280 100644 --- a/src/db/connector/Mysql.php +++ b/src/db/connector/Mysql.php @@ -2,12 +2,13 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\db\connector; @@ -28,7 +29,7 @@ class Mysql extends Connection * @access protected * @return void */ - protected function initialize() + protected function initialize(): void { // Point类型支持 Query::extend('point', function ($query, $field, $value = null, $fun = 'GeomFromText', $type = 'POINT') { @@ -48,10 +49,10 @@ class Mysql extends Connection /** * 解析pdo连接的dsn信息 * @access protected - * @param array $config 连接信息 + * @param array $config 连接信息 * @return string */ - protected function parseDsn($config) + protected function parseDsn(array $config): string { if (!empty($config['socket'])) { $dsn = 'mysql:unix_socket=' . $config['socket']; @@ -72,10 +73,10 @@ class Mysql extends Connection /** * 取得数据表的字段信息 * @access public - * @param string $tableName + * @param string $tableName * @return array */ - public function getFields($tableName) + public function getFields(string $tableName): array { list($tableName) = explode(' ', $tableName); @@ -87,7 +88,7 @@ class Mysql extends Connection } $sql = 'SHOW COLUMNS FROM ' . $tableName; - $pdo = $this->query($sql, [], false, true); + $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; @@ -111,13 +112,13 @@ class Mysql extends Connection /** * 取得数据库的表信息 * @access public - * @param string $dbName + * @param string $dbName * @return array */ - public function getTables($dbName = '') + public function getTables(string $dbName = ''): array { $sql = !empty($dbName) ? 'SHOW TABLES FROM ' . $dbName : 'SHOW TABLES '; - $pdo = $this->query($sql, [], false, true); + $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; @@ -131,10 +132,10 @@ class Mysql extends Connection /** * SQL性能分析 * @access protected - * @param string $sql + * @param string $sql * @return array */ - protected function getExplain($sql) + protected function getExplain(string $sql): array { $pdo = $this->linkID->query("EXPLAIN " . $sql); $result = $pdo->fetch(PDO::FETCH_ASSOC); @@ -149,61 +150,9 @@ class Mysql extends Connection return $result; } - protected function supportSavepoint() + protected function supportSavepoint(): bool { return true; } - /** - * 启动XA事务 - * @access public - * @param string $xid XA事务id - * @return void - */ - public function startTransXa($xid) - { - $this->initConnect(true); - if (!$this->linkID) { - return false; - } - - $this->execute("XA START '$xid'"); - } - - /** - * 预编译XA事务 - * @access public - * @param string $xid XA事务id - * @return void - */ - public function prepareXa($xid) - { - $this->initConnect(true); - $this->execute("XA END '$xid'"); - $this->execute("XA PREPARE '$xid'"); - } - - /** - * 提交XA事务 - * @access public - * @param string $xid XA事务id - * @return void - */ - public function commitXa($xid) - { - $this->initConnect(true); - $this->execute("XA COMMIT '$xid'"); - } - - /** - * 回滚XA事务 - * @access public - * @param string $xid XA事务id - * @return void - */ - public function rollbackXa($xid) - { - $this->initConnect(true); - $this->execute("XA ROLLBACK '$xid'"); - } } diff --git a/src/db/connector/Pgsql.php b/src/db/connector/Pgsql.php index ab19122..0d068c4 100644 --- a/src/db/connector/Pgsql.php +++ b/src/db/connector/Pgsql.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -32,10 +32,10 @@ class Pgsql extends Connection /** * 解析pdo连接的dsn信息 * @access protected - * @param array $config 连接信息 + * @param array $config 连接信息 * @return string */ - protected function parseDsn($config) + protected function parseDsn(array $config): string { $dsn = 'pgsql:dbname=' . $config['database'] . ';host=' . $config['hostname']; @@ -49,15 +49,15 @@ class Pgsql extends Connection /** * 取得数据表的字段信息 * @access public - * @param string $tableName + * @param string $tableName * @return array */ - public function getFields($tableName) + public function getFields(string $tableName): array { list($tableName) = explode(' ', $tableName); $sql = 'select fields_name as "field",fields_type as "type",fields_not_null as "null",fields_key_name as "key",fields_default as "default",fields_default as "extra" from table_msg(\'' . $tableName . '\');'; - $pdo = $this->query($sql, [], false, true); + $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; @@ -81,13 +81,13 @@ class Pgsql extends Connection /** * 取得数据库的表信息 * @access public - * @param string $dbName + * @param string $dbName * @return array */ - public function getTables($dbName = '') + public function getTables(string $dbName = ''): array { $sql = "select tablename as Tables_in_test from pg_tables where schemaname ='public'"; - $pdo = $this->query($sql, [], false, true); + $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; @@ -101,15 +101,15 @@ class Pgsql extends Connection /** * SQL性能分析 * @access protected - * @param string $sql + * @param string $sql * @return array */ - protected function getExplain($sql) + protected function getExplain(string $sql): array { return []; } - protected function supportSavepoint() + protected function supportSavepoint(): bool { return true; } diff --git a/src/db/connector/Sqlite.php b/src/db/connector/Sqlite.php index f6779d9..96524b8 100644 --- a/src/db/connector/Sqlite.php +++ b/src/db/connector/Sqlite.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -25,10 +25,10 @@ class Sqlite extends Connection /** * 解析pdo连接的dsn信息 * @access protected - * @param array $config 连接信息 + * @param array $config 连接信息 * @return string */ - protected function parseDsn($config) + protected function parseDsn(array $config): string { $dsn = 'sqlite:' . $config['database']; @@ -38,15 +38,15 @@ class Sqlite extends Connection /** * 取得数据表的字段信息 * @access public - * @param string $tableName + * @param string $tableName * @return array */ - public function getFields($tableName) + public function getFields(string $tableName): array { list($tableName) = explode(' ', $tableName); $sql = 'PRAGMA table_info( ' . $tableName . ' )'; - $pdo = $this->query($sql, [], false, true); + $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; @@ -70,16 +70,16 @@ class Sqlite extends Connection /** * 取得数据库的表信息 * @access public - * @param string $dbName + * @param string $dbName * @return array */ - public function getTables($dbName = '') + public function getTables(string $dbName = ''): array { $sql = "SELECT name FROM sqlite_master WHERE type='table' " . "UNION ALL SELECT name FROM sqlite_temp_master " . "WHERE type='table' ORDER BY name"; - $pdo = $this->query($sql, [], false, true); + $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; @@ -93,15 +93,15 @@ class Sqlite extends Connection /** * SQL性能分析 * @access protected - * @param string $sql + * @param string $sql * @return array */ - protected function getExplain($sql) + protected function getExplain(string $sql): array { return []; } - protected function supportSavepoint() + protected function supportSavepoint(): bool { return true; } diff --git a/src/db/connector/Sqlsrv.php b/src/db/connector/Sqlsrv.php index b56654a..17225df 100644 --- a/src/db/connector/Sqlsrv.php +++ b/src/db/connector/Sqlsrv.php @@ -32,10 +32,10 @@ class Sqlsrv extends Connection /** * 解析pdo连接的dsn信息 * @access protected - * @param array $config 连接信息 + * @param array $config 连接信息 * @return string */ - protected function parseDsn($config) + protected function parseDsn(array $config): string { $dsn = 'sqlsrv:Database=' . $config['database'] . ';Server=' . $config['hostname']; @@ -49,14 +49,12 @@ class Sqlsrv extends Connection /** * 取得数据表的字段信息 * @access public - * @param string $tableName + * @param string $tableName * @return array */ - public function getFields($tableName) + public function getFields(string $tableName): array { list($tableName) = explode(' ', $tableName); - $tableNames = explode('.', $tableName); - $tableName = isset($tableNames[1]) ? $tableNames[1] : $tableNames[0]; $sql = "SELECT column_name, data_type, column_default, is_nullable FROM information_schema.tables AS t @@ -66,7 +64,7 @@ class Sqlsrv extends Connection AND t.table_name = c.table_name WHERE t.table_name = '$tableName'"; - $pdo = $this->query($sql, [], false, true); + $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; @@ -106,17 +104,17 @@ class Sqlsrv extends Connection /** * 取得数据表的字段信息 * @access public - * @param string $dbName + * @param string $dbName * @return array */ - public function getTables($dbName = '') + public function getTables(string $dbName = ''): array { $sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' "; - $pdo = $this->query($sql, [], false, true); + $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; @@ -130,10 +128,10 @@ class Sqlsrv extends Connection /** * SQL性能分析 * @access protected - * @param string $sql + * @param string $sql * @return array */ - protected function getExplain($sql) + protected function getExplain(string $sql): array { return []; } diff --git a/src/db/exception/BindParamException.php b/src/exception/BindParamException.php similarity index 67% rename from src/db/exception/BindParamException.php rename to src/exception/BindParamException.php index 274fe0a..b657a7a 100644 --- a/src/db/exception/BindParamException.php +++ b/src/exception/BindParamException.php @@ -2,14 +2,15 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: 麦当苗儿 // +---------------------------------------------------------------------- +declare (strict_types = 1); -namespace think\db\exception; +namespace think\exception; /** * PDO参数绑定异常 @@ -19,13 +20,14 @@ class BindParamException extends DbException /** * BindParamException constructor. - * @param string $message - * @param array $config - * @param string $sql - * @param array $bind - * @param int $code + * @access public + * @param string $message + * @param array $config + * @param string $sql + * @param array $bind + * @param int $code */ - public function __construct($message, $config, $sql, $bind, $code = 10502) + public function __construct(string $message, array $config, string $sql, array $bind, int $code = 10502) { $this->setData('Bind Param', $bind); parent::__construct($message, $config, $sql, $code); diff --git a/src/db/exception/ClassNotFoundException.php b/src/exception/ClassNotFoundException.php similarity index 90% rename from src/db/exception/ClassNotFoundException.php rename to src/exception/ClassNotFoundException.php index cd70dae..34d9320 100644 --- a/src/db/exception/ClassNotFoundException.php +++ b/src/exception/ClassNotFoundException.php @@ -9,12 +9,12 @@ // | Author: yunwuxin <448901948@qq.com> // +---------------------------------------------------------------------- -namespace think\db\exception; +namespace think\exception; class ClassNotFoundException extends \RuntimeException { protected $class; - public function __construct($message, $class = '') + public function __construct(string $message, string $class = '') { $this->message = $message; $this->class = $class; diff --git a/src/db/exception/DataNotFoundException.php b/src/exception/DataNotFoundException.php similarity index 75% rename from src/db/exception/DataNotFoundException.php rename to src/exception/DataNotFoundException.php index f987aab..89b1c09 100644 --- a/src/db/exception/DataNotFoundException.php +++ b/src/exception/DataNotFoundException.php @@ -2,14 +2,15 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: 麦当苗儿 // +---------------------------------------------------------------------- +declare (strict_types = 1); -namespace think\db\exception; +namespace think\exception; class DataNotFoundException extends DbException { @@ -17,11 +18,12 @@ class DataNotFoundException extends DbException /** * DbException constructor. - * @param string $message - * @param string $table - * @param array $config + * @access public + * @param string $message + * @param string $table + * @param array $config */ - public function __construct($message, $table = '', array $config = []) + public function __construct(string $message, string $table = '', array $config = []) { $this->message = $message; $this->table = $table; diff --git a/src/db/exception/DbException.php b/src/exception/DbException.php similarity index 75% rename from src/db/exception/DbException.php rename to src/exception/DbException.php index f2e99cb..93200a6 100644 --- a/src/db/exception/DbException.php +++ b/src/exception/DbException.php @@ -2,14 +2,14 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: 麦当苗儿 // +---------------------------------------------------------------------- -namespace think\db\exception; +namespace think\exception; use think\Exception; @@ -20,12 +20,13 @@ class DbException extends Exception { /** * DbException constructor. - * @param string $message - * @param array $config - * @param string $sql - * @param int $code + * @access public + * @param string $message + * @param array $config + * @param string $sql + * @param int $code */ - public function __construct($message, array $config, $sql, $code = 10500) + public function __construct(string $message, array $config = [], string $sql = '', int $code = 10500) { $this->message = $message; $this->code = $code; diff --git a/src/db/exception/ModelNotFoundException.php b/src/exception/ModelNotFoundException.php similarity index 75% rename from src/db/exception/ModelNotFoundException.php rename to src/exception/ModelNotFoundException.php index 43505a3..cb54b42 100644 --- a/src/db/exception/ModelNotFoundException.php +++ b/src/exception/ModelNotFoundException.php @@ -2,14 +2,15 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: 麦当苗儿 // +---------------------------------------------------------------------- +declare (strict_types = 1); -namespace think\db\exception; +namespace think\exception; class ModelNotFoundException extends DbException { @@ -17,10 +18,12 @@ class ModelNotFoundException extends DbException /** * 构造方法 - * @param string $message - * @param string $model + * @access public + * @param string $message + * @param string $model + * @param array $config */ - public function __construct($message, $model = '', array $config = []) + public function __construct(string $message, string $model = '', array $config = []) { $this->message = $message; $this->model = $model; diff --git a/src/db/exception/PDOException.php b/src/exception/PDOException.php similarity index 79% rename from src/db/exception/PDOException.php rename to src/exception/PDOException.php index f23d67e..314b521 100644 --- a/src/db/exception/PDOException.php +++ b/src/exception/PDOException.php @@ -2,14 +2,14 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: 麦当苗儿 // +---------------------------------------------------------------------- -namespace think\db\exception; +namespace think\exception; /** * PDO异常处理类 @@ -19,12 +19,13 @@ class PDOException extends DbException { /** * PDOException constructor. - * @param \PDOException $exception - * @param array $config - * @param string $sql - * @param int $code + * @access public + * @param \PDOException $exception + * @param array $config + * @param string $sql + * @param int $code */ - public function __construct(\PDOException $exception, array $config, $sql, $code = 10501) + public function __construct(\PDOException $exception, array $config = [], string $sql = '', int $code = 10501) { $error = $exception->errorInfo; diff --git a/src/model/Collection.php b/src/model/Collection.php index 2054acc..a80ecf3 100644 --- a/src/model/Collection.php +++ b/src/model/Collection.php @@ -2,12 +2,13 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: zhangyajun <448901948@qq.com> // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\model; @@ -19,10 +20,10 @@ class Collection extends BaseCollection /** * 延迟预载入关联查询 * @access public - * @param mixed $relation 关联 + * @param array $relation 关联 * @return $this */ - public function load($relation) + public function load(array $relation) { $item = current($this->items); $item->eagerlyResultSet($this->items, $relation); @@ -33,14 +34,13 @@ class Collection extends BaseCollection /** * 设置需要隐藏的输出属性 * @access public - * @param array $hidden 属性列表 - * @param bool $override 是否覆盖 + * @param array $hidden 属性列表 + * @param bool $override 是否覆盖 * @return $this */ - public function hidden($hidden = [], $override = false) + public function hidden(array $hidden, bool $override = false) { - $this->each(function ($model) use ($hidden, $override) { - /** @var Model $model */ + $this->each(function (Model $model) use ($hidden, $override) { $model->hidden($hidden, $override); }); @@ -49,14 +49,14 @@ class Collection extends BaseCollection /** * 设置需要输出的属性 - * @param array $visible - * @param bool $override 是否覆盖 + * @access public + * @param array $visible + * @param bool $override 是否覆盖 * @return $this */ - public function visible($visible = [], $override = false) + public function visible(array $visible, bool $override = false) { - $this->each(function ($model) use ($visible, $override) { - /** @var Model $model */ + $this->each(function (Model $model) use ($visible, $override) { $model->visible($visible, $override); }); @@ -66,14 +66,13 @@ class Collection extends BaseCollection /** * 设置需要追加的输出属性 * @access public - * @param array $append 属性列表 - * @param bool $override 是否覆盖 + * @param array $append 属性列表 + * @param bool $override 是否覆盖 * @return $this */ - public function append($append = [], $override = false) + public function append(array $append, bool $override = false) { - $this->each(function ($model) use ($append, $override) { - /** @var Model $model */ + $this->each(function (Model $model) use ($append, $override) { $model && $model->append($append, $override); }); @@ -97,4 +96,86 @@ class Collection extends BaseCollection return $this; } + /** + * 按指定键整理数据 + * + * @access public + * @param mixed $items 数据 + * @param string $indexKey 键名 + * @return array + */ + public function dictionary($items = null, string &$indexKey = null) + { + if ($items instanceof self || $items instanceof Paginator) { + $items = $items->all(); + } + + $items = is_null($items) ? $this->items : $items; + + if ($items && empty($indexKey)) { + $indexKey = $items[0]->getPk(); + } + + if (isset($indexKey) && is_string($indexKey)) { + return array_column($items, null, $indexKey); + } + + return $items; + } + + /** + * 比较数据集,返回差集 + * + * @access public + * @param mixed $items 数据 + * @param string $indexKey 指定比较的键名 + * @return static + */ + public function diff($items, string $indexKey = null) + { + if ($this->isEmpty()) { + return new static($items); + } + + $diff = []; + $dictionary = $this->dictionary($items, $indexKey); + + if (is_string($indexKey)) { + foreach ($this->items as $item) { + if (!isset($dictionary[$item[$indexKey]])) { + $diff[] = $item; + } + } + } + + return new static($diff); + } + + /** + * 比较数据集,返回交集 + * + * @access public + * @param mixed $items 数据 + * @param string $indexKey 指定比较的键名 + * @return static + */ + public function intersect($items, string $indexKey = null) + { + if ($this->isEmpty()) { + return new static([]); + } + + $intersect = []; + $dictionary = $this->dictionary($items, $indexKey); + + if (is_string($indexKey)) { + foreach ($this->items as $item) { + if (isset($dictionary[$item[$indexKey]])) { + $intersect[] = $item; + } + } + } + + return new static($intersect); + } } diff --git a/src/model/Pivot.php b/src/model/Pivot.php index 3efb718..d5e93d3 100644 --- a/src/model/Pivot.php +++ b/src/model/Pivot.php @@ -2,12 +2,13 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\model; @@ -24,11 +25,11 @@ class Pivot extends Model /** * 架构函数 * @access public - * @param Model $parent 上级模型 - * @param array|object $data 数据 - * @param string $table 中间数据表名 + * @param array $data 数据 + * @param Model $parent 上级模型 + * @param string $table 中间数据表名 */ - public function __construct(Model $parent = null, $data = [], $table = '') + public function __construct(array $data = [], $parent = null, string $table = '') { $this->parent = $parent; @@ -37,9 +38,6 @@ class Pivot extends Model } parent::__construct($data); - - // 当前类名 - $this->class = $this->name; } } diff --git a/src/model/Relation.php b/src/model/Relation.php index af1fe27..5b9f247 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -2,12 +2,13 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\model; @@ -43,7 +44,7 @@ abstract class Relation * @access public * @return Model */ - public function getParent() + public function getParent(): Model { return $this->parent; } @@ -53,7 +54,7 @@ abstract class Relation * @access public * @return Model */ - public function getModel() + public function getModel(): Model { return $this->query->getModel(); } @@ -64,7 +65,7 @@ abstract class Relation * @param bool $self 是否自关联 * @return $this */ - public function selfRelation($self = true) + public function selfRelation(bool $self = true) { $this->selfRelation = $self; return $this; @@ -75,7 +76,7 @@ abstract class Relation * @access public * @return bool */ - public function isSelfRelation() + public function isSelfRelation(): bool { return $this->selfRelation; } @@ -83,41 +84,40 @@ abstract class Relation /** * 封装关联数据集 * @access public - * @param array $resultSet 数据集 + * @param array $resultSet 数据集 * @return mixed */ - protected function resultSetBuild($resultSet) + protected function resultSetBuild(array $resultSet) { return (new $this->model)->toCollection($resultSet); } - protected function getQueryFields($model) + protected function getQueryFields(string $model) { $fields = $this->query->getOptions('field'); return $this->getRelationQueryFields($fields, $model); } - protected function getRelationQueryFields($fields, $model) + protected function getRelationQueryFields($fields, string $model) { - if ($fields) { + if (empty($fields) || '*' == $fields) { + return $model . '.*'; + } - if (is_string($fields)) { - $fields = explode(',', $fields); - } + if (is_string($fields)) { + $fields = explode(',', $fields); + } - foreach ($fields as &$field) { - if (false === strpos($field, '.')) { - $field = $model . '.' . $field; - } + foreach ($fields as &$field) { + if (false === strpos($field, '.')) { + $field = $model . '.' . $field; } - } else { - $fields = $model . '.*'; } return $fields; } - protected function getQueryWhere(&$where, $relation) + protected function getQueryWhere(array &$where, string $relation): void { foreach ($where as $key => &$val) { if (is_string($key)) { @@ -137,7 +137,7 @@ abstract class Relation * @throws Exception * @throws PDOException */ - public function delete($data = null) + public function delete($data = null): int { return $this->query->delete($data); } @@ -147,7 +147,7 @@ abstract class Relation * @access protected * @return void */ - protected function baseQuery() + protected function baseQuery(): void {} public function __call($method, $args) @@ -159,8 +159,8 @@ abstract class Relation $result = call_user_func_array([$this->query->getModel(), $method], $args); return $result === $this->query ? $this : $result; - } else { - throw new Exception('method not exists:' . __CLASS__ . '->' . $method); } + + throw new Exception('method not exists:' . __CLASS__ . '->' . $method); } } diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 9d9d098..4f9102f 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -2,17 +2,19 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\model\concern; use InvalidArgumentException; use think\Db; +use think\Exception; use think\model\Relation; trait Attribute @@ -27,25 +29,19 @@ trait Attribute * 数据表字段信息 留空则自动获取 * @var array */ - protected $field = []; + protected $schema = []; /** - * JSON数据表字段 + * 当前允许写入的字段 * @var array */ - protected $json = []; + protected $field = []; /** - * JSON数据表字段类型 + * 数据表字段类型 * @var array */ - protected $jsonType = []; - - /** - * JSON数据取出是否需要转换为数组 - * @var bool - */ - protected $jsonAssoc = false; + protected $type = []; /** * 数据表废弃字段 @@ -59,12 +55,6 @@ trait Attribute */ protected $readonly = []; - /** - * 数据表字段类型 - * @var array - */ - protected $type = []; - /** * 当前模型数据 * @var array @@ -77,6 +67,24 @@ trait Attribute */ private $origin = []; + /** + * JSON数据表字段 + * @var array + */ + protected $json = []; + + /** + * JSON数据取出是否需要转换为数组 + * @var bool + */ + protected $jsonAssoc = false; + + /** + * 是否严格字段大小写 + * @var bool + */ + protected $strict = true; + /** * 修改器执行记录 * @var array @@ -102,12 +110,13 @@ trait Attribute /** * 判断一个字段名是否为主键字段 * @access public - * @param string $key 名称 + * @param string $key 名称 * @return bool */ - protected function isPk($key) + protected function isPk(string $key): bool { $pk = $this->getPk(); + if (is_string($pk) && $pk == $key) { return true; } elseif (is_array($pk) && in_array($key, $pk)) { @@ -120,11 +129,12 @@ trait Attribute /** * 获取模型对象的主键值 * @access public - * @return integer + * @return mixed */ public function getKey() { $pk = $this->getPk(); + if (is_string($pk) && array_key_exists($pk, $this->data)) { return $this->data[$pk]; } @@ -135,15 +145,11 @@ trait Attribute /** * 设置允许写入的字段 * @access public - * @param mixed $field 允许写入的字段 如果为true只允许写入数据表字段 + * @param array $field 允许写入的字段 * @return $this */ - public function allowField($field) + public function allowField(array $field) { - if (is_string($field)) { - $field = explode(',', $field); - } - $this->field = $field; return $this; @@ -152,47 +158,62 @@ trait Attribute /** * 设置只读字段 * @access public - * @param mixed $field 只读字段 + * @param array $field 只读字段 * @return $this */ - public function readonly($field) + public function readOnly(array $field) { - if (is_string($field)) { - $field = explode(',', $field); - } - $this->readonly = $field; return $this; } + /** + * 获取实际的字段名 + * @access public + * @param string $name 字段名 + * @return $this + */ + protected function getRealFieldName($name) + { + return $this->strict ? $name : Db::parseName($name); + } + /** * 设置数据对象值 * @access public - * @param mixed $data 数据或者属性名 - * @param mixed $value 值 + * @param array $data 数据 + * @param bool $set 是否调用修改器 + * @param array $allow 允许的字段名 * @return $this */ - public function data($data, $value = null) + public function data(array $data, bool $set = false, array $allow = []) { - if (is_string($data)) { - $this->data[$data] = $value; - } else { - // 清空数据 - $this->data = []; + // 清空数据 + $this->data = []; - if (is_object($data)) { - $data = get_object_vars($data); + // 废弃字段 + foreach ($this->disuse as $key) { + if (array_key_exists($key, $data)) { + unset($data[$key]); } + } - if (true === $value) { - // 数据对象赋值 - foreach ($data as $key => $value) { - $this->setAttr($key, $value, $data); + if (!empty($allow)) { + $result = []; + foreach ($allow as $name) { + if (isset($data[$name])) { + $result[$name] = $data[$name]; } - } else { - $this->data = $data; } + $data = $result; + } + + if ($set) { + // 数据对象赋值 + $this->setAttrs($data); + } else { + $this->data = $data; } return $this; @@ -201,22 +222,15 @@ trait Attribute /** * 批量设置数据对象值 * @access public - * @param mixed $data 数据 - * @param bool $set 是否需要进行数据处理 + * @param mixed $data 数据 + * @param bool $set 是否需要进行数据处理 * @return $this */ - public function appendData($data, $set = false) + public function appendData(array $data, bool $set = false) { if ($set) { - // 进行数据处理 - foreach ($data as $key => $value) { - $this->setAttr($key, $value, $data); - } + $this->setAttrs($data); } else { - if (is_object($data)) { - $data = get_object_vars($data); - } - $this->data = array_merge($this->data, $data); } @@ -226,36 +240,40 @@ trait Attribute /** * 获取对象原始数据 如果不存在指定字段返回null * @access public - * @param string $name 字段名 留空获取全部 + * @param string $name 字段名 留空获取全部 * @return mixed */ - public function getOrigin($name = null) + public function getOrigin(string $name = null) { if (is_null($name)) { return $this->origin; - } else { - return array_key_exists($name, $this->origin) ? $this->origin[$name] : null; } + + return array_key_exists($name, $this->origin) ? $this->origin[$name] : null; } /** * 获取对象原始数据 如果不存在指定字段返回false * @access public - * @param string $name 字段名 留空获取全部 + * @param string $name 字段名 留空获取全部 * @return mixed * @throws InvalidArgumentException */ - public function getData($name = null) + public function getData(string $name = null) { if (is_null($name)) { return $this->data; - } elseif (array_key_exists($name, $this->data)) { - return $this->data[$name]; + } + + $fieldName = $this->getRealFieldName($name); + + if (array_key_exists($fieldName, $this->data)) { + return $this->data[$fieldName]; } elseif (array_key_exists($name, $this->relation)) { return $this->relation[$name]; - } else { - throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name); } + + throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name); } /** @@ -263,7 +281,7 @@ trait Attribute * @access public * @return array */ - public function getChangedData() + public function getChangedData(): array { if ($this->force) { $data = $this->data; @@ -277,12 +295,10 @@ trait Attribute }); } - if (!empty($this->readonly)) { - // 只读字段不允许更新 - foreach ($this->readonly as $key => $field) { - if (isset($data[$field])) { - unset($data[$field]); - } + // 只读字段不允许更新 + foreach ($this->readonly as $key => $field) { + if (isset($data[$field])) { + unset($data[$field]); } } @@ -290,21 +306,49 @@ trait Attribute } /** - * 修改器 设置数据对象值 + * 直接设置数据对象值 + * @access public + * @param string $name 属性名 + * @param mixed $value 值 + * @return void + */ + public function set(string $name, $value): void + { + $name = $this->getRealFieldName($name); + + $this->data[$name] = $value; + } + + /** + * 通过修改器 批量设置数据对象值 * @access public - * @param string $name 属性名 - * @param mixed $value 属性值 - * @param array $data 数据 + * @param array $data 数据 * @return void */ - public function setAttr($name, $value, $data = []) + public function setAttrs(array $data): void { + // 进行数据处理 + foreach ($data as $key => $value) { + $this->setAttr($key, $value, $data); + } + } + + /** + * 通过修改器 设置数据对象值 + * @access public + * @param string $name 属性名 + * @param mixed $value 属性值 + * @param array $data 数据 + * @return void + */ + public function setAttr(string $name, $value, array $data = []): void + { + $name = $this->getRealFieldName($name); + if (isset($this->set[$name])) { return; } - $isRelationData = false; - if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { // 自动写入的时间戳字段 $value = $this->autoWriteTimestamp($name); @@ -314,6 +358,8 @@ trait Attribute if (method_exists($this, $method)) { $value = $this->$method($value, array_merge($this->data, $data)); + + $this->set[$name] = true; } elseif (isset($this->type[$name])) { // 类型转换 $value = $this->writeTransform($value, $this->type[$name]); @@ -322,16 +368,15 @@ trait Attribute // 设置数据对象属性 $this->data[$name] = $value; - $this->set[$name] = true; } /** * 是否需要自动写入时间字段 * @access public - * @param bool $auto + * @param bool $auto * @return $this */ - public function isAutoWriteTimestamp($auto) + public function isAutoWriteTimestamp(bool $auto) { $this->autoWriteTimestamp = $auto; @@ -340,12 +385,14 @@ trait Attribute /** * 自动写入时间戳 - * @access public - * @param string $name 时间戳字段 + * @access protected + * @param string $name 时间戳字段 * @return mixed */ - protected function autoWriteTimestamp($name) + protected function autoWriteTimestamp(string $name) { + $value = time(); + if (isset($this->type[$name])) { $type = $this->type[$name]; @@ -356,25 +403,16 @@ trait Attribute switch ($type) { case 'datetime': case 'date': + case 'timestamp': $format = !empty($param) ? $param : $this->dateFormat; $format .= strpos($format, 'u') || false !== strpos($format, '\\') ? '' : '.u'; $value = $this->formatDateTime($format); break; - case 'timestamp': - case 'integer': - default: - $value = time(); - break; } - } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ - 'datetime', - 'date', - 'timestamp', - ])) { + } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), + ['datetime', 'date', 'timestamp'])) { $format = strpos($this->dateFormat, 'u') || false !== strpos($this->dateFormat, '\\') ? '' : '.u'; $value = $this->formatDateTime($this->dateFormat . $format); - } else { - $value = time(); } return $value; @@ -382,9 +420,9 @@ trait Attribute /** * 数据写入 类型转换 - * @access public - * @param mixed $value 值 - * @param string|array $type 要转换的类型 + * @access protected + * @param mixed $value 值 + * @param string|array $type 要转换的类型 * @return mixed */ protected function writeTransform($value, $type) @@ -445,12 +483,12 @@ trait Attribute /** * 获取器 获取数据对象的值 * @access public - * @param string $name 名称 - * @param array $item 数据 + * @param string $name 名称 + * @param array $item 数据 * @return mixed * @throws InvalidArgumentException */ - public function getAttr($name, &$item = null) + public function getAttr(string $name, array &$item = []) { try { $notFound = false; @@ -461,28 +499,26 @@ trait Attribute } // 检测属性获取器 - $fieldName = Db::parseName($name); + $fieldName = $this->getRealFieldName($name); $method = 'get' . Db::parseName($name, 1) . 'Attr'; if (isset($this->withAttr[$fieldName])) { - if ($notFound && $relation = $this->isRelationAttr($name)) { - $modelRelation = $this->$relation(); - $value = $this->getRelationData($modelRelation); + if ($notFound) { + $value = $this->getRelationValue($name); } $closure = $this->withAttr[$fieldName]; $value = $closure($value, $this->data); } elseif (method_exists($this, $method)) { - if ($notFound && $relation = $this->isRelationAttr($name)) { - $modelRelation = $this->$relation(); - $value = $this->getRelationData($modelRelation); + if ($notFound) { + $value = $this->getRelationValue($name); } $value = $this->$method($value, $this->data); - } elseif (isset($this->type[$name])) { + } elseif (isset($this->type[$fieldName])) { // 类型转换 - $value = $this->readTransform($value, $this->type[$name]); - } elseif ($this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { + $value = $this->readTransform($value, $this->type[$fieldName]); + } elseif ($this->autoWriteTimestamp && in_array($fieldName, [$this->createTime, $this->updateTime])) { if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ 'datetime', 'date', @@ -495,9 +531,23 @@ trait Attribute } elseif ($notFound) { $value = $this->getRelationAttribute($name, $item); } + return $value; } + protected function getRelationValue(string $name) + { + $relation = $this->isRelationAttr($name); + + if (false === $relation) { + return; + } + + $modelRelation = $this->$relation(); + + return $modelRelation instanceof Relation ? $this->getRelationData($modelRelation) : null; + } + /** * 获取关联属性值 * @access protected @@ -505,45 +555,40 @@ trait Attribute * @param array $item 数据 * @return mixed */ - protected function getRelationAttribute($name, &$item) + protected function getRelationAttribute(string $name, array &$item) { - $relation = $this->isRelationAttr($name); - - if ($relation) { - $modelRelation = $this->$relation(); - if ($modelRelation instanceof Relation) { - $value = $this->getRelationData($modelRelation); + $value = $this->getRelationValue($name); - if ($item && method_exists($modelRelation, 'getBindAttr') && $bindAttr = $modelRelation->getBindAttr()) { + if (!$value) { + throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name); + } - foreach ($bindAttr as $key => $attr) { - $key = is_numeric($key) ? $attr : $key; + if ($item && method_exists($modelRelation, 'getBindAttr') && $bindAttr = $modelRelation->getBindAttr()) { - if (isset($item[$key])) { - throw new Exception('bind attr has exists:' . $key); - } else { - $item[$key] = $value ? $value->getAttr($attr) : null; - } - } + foreach ($bindAttr as $key => $attr) { + $key = is_numeric($key) ? $attr : $key; - return false; + if (isset($item[$key])) { + throw new Exception('bind attr has exists:' . $key); } - // 保存关联对象值 - $this->relation[$name] = $value; - - return $value; + $item[$key] = $value ? $value->getAttr($attr) : null; } + + return false; } - throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name); + // 保存关联对象值 + $this->relation[$name] = $value; + + return $value; } /** * 数据读取 类型转换 - * @access public - * @param mixed $value 值 - * @param string|array $type 要转换的类型 + * @access protected + * @param mixed $value 值 + * @param string|array $type 要转换的类型 * @return mixed */ protected function readTransform($value, $type) @@ -594,7 +639,11 @@ trait Attribute $value = empty($value) ? new \stdClass() : json_decode($value); break; case 'serialize': - $value = unserialize($value); + try { + $value = unserialize($value); + } catch (\Exception $e) { + $value = null; + } break; default: if (false !== strpos($type, '\\')) { @@ -613,20 +662,21 @@ trait Attribute * @param callable $callback 闭包获取器 * @return $this */ - public function withAttribute($name, $callback = null) + public function withAttribute($name, callable $callback = null) { if (is_array($name)) { foreach ($name as $key => $val) { - $key = Db::parseName($key); + $key = $this->getRealFieldName($key); $this->withAttr[$key] = $val; } } else { - $name = Db::parseName($name); + $name = $this->getRealFieldName($name); $this->withAttr[$name] = $callback; } return $this; } + } diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index 8df518b..6afd78a 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -2,12 +2,13 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\model\concern; @@ -22,23 +23,38 @@ use think\model\Collection as ModelCollection; */ trait Conversion { - // 显示属性 + /** + * 数据输出显示的属性 + * @var array + */ protected $visible = []; - // 隐藏属性 + + /** + * 数据输出隐藏的属性 + * @var array + */ protected $hidden = []; - // 附加属性 + + /** + * 数据输出需要追加的属性 + * @var array + */ protected $append = []; - // 查询数据集对象 + + /** + * 数据集对象名 + * @var string + */ protected $resultSetType; /** * 设置需要附加的输出属性 * @access public - * @param array $append 属性列表 - * @param bool $override 是否覆盖 + * @param array $append 属性列表 + * @param bool $override 是否覆盖 * @return $this */ - public function append($append = [], $override = false) + public function append(array $append = [], bool $override = false) { $this->append = $override ? $append : array_merge($this->append, $append); @@ -48,18 +64,15 @@ trait Conversion /** * 设置附加关联对象的属性 * @access public - * @param string $attr 关联属性 - * @param string|array $append 追加属性名 + * @param string $attr 关联属性 + * @param string|array $append 追加属性名 * @return $this * @throws Exception */ - public function appendRelationAttr($attr, $append) + public function appendRelationAttr(string $attr, array $append) { - if (is_string($append)) { - $append = explode(',', $append); - } - $relation = Db::parseName($attr, 1, false); + if (isset($this->relation[$relation])) { $model = $this->relation[$relation]; } else { @@ -83,11 +96,11 @@ trait Conversion /** * 设置需要隐藏的输出属性 * @access public - * @param array $hidden 属性列表 - * @param bool $override 是否覆盖 + * @param array $hidden 属性列表 + * @param bool $override 是否覆盖 * @return $this */ - public function hidden($hidden = [], $override = false) + public function hidden(array $hidden = [], bool $override = false) { $this->hidden = $override ? $hidden : array_merge($this->hidden, $hidden); @@ -97,11 +110,11 @@ trait Conversion /** * 设置需要输出的属性 * @access public - * @param array $visible - * @param bool $override 是否覆盖 + * @param array $visible + * @param bool $override 是否覆盖 * @return $this */ - public function visible($visible = [], $override = false) + public function visible(array $visible = [], bool $override = false) { $this->visible = $override ? $visible : array_merge($this->visible, $visible); @@ -113,7 +126,7 @@ trait Conversion * @access public * @return array */ - public function toArray() + public function toArray(): array { $item = []; $visible = []; @@ -132,66 +145,71 @@ trait Conversion } foreach ($data as $key => $val) { - if ($val instanceof Model || $val instanceof ModelCollection) { - // 关联模型对象 - if (isset($visible[$key])) { - $val->visible($visible[$key]); - } elseif (isset($hidden[$key])) { - $val->hidden($hidden[$key]); - } - // 关联模型对象 - $item[$key] = $val->toArray(); - } else { - // 模型属性 - $item[$key] = $this->getAttr($key); - } + $item[$key] = $this->getArrayData($key, $val, $visible, $hidden); } // 追加属性(必须定义获取器) - if (!empty($this->append)) { - foreach ($this->append as $key => $name) { - if (is_array($name)) { - // 追加关联对象属性 - $relation = $this->getRelation($key); + foreach ($this->append as $key => $name) { + $this->appendAttrToArray($item, $key, $name); + } - if (!$relation) { - $relation = $this->getAttr($key); - $relation->visible($name); - } + return $item; + } - $item[$key] = $relation->append($name)->toArray(); + protected function appendAttrToArray(array &$item, $key, string $name) + { + if (is_array($name)) { + // 追加关联对象属性 + $relation = $this->getRelation($key); - } elseif (strpos($name, '.')) { - list($key, $attr) = explode('.', $name); - // 追加关联对象属性 - $relation = $this->getRelation($key); + if (!$relation) { + $relation = $this->getAttr($key); + $relation->visible($name); + } - if (!$relation) { - $relation = $this->getAttr($key); - $relation->visible([$attr]); - } + $item[$key] = $relation->append($name)->toArray(); + } elseif (strpos($name, '.')) { + list($key, $attr) = explode('.', $name); + // 追加关联对象属性 + $relation = $this->getRelation($key); - $item[$key] = $relation->append([$attr])->toArray(); + if (!$relation) { + $relation = $this->getAttr($key); + $relation->visible([$attr]); + } - } else { - $value = $this->getAttr($name, $item); - if (false !== $value) { - $item[$name] = $value; - } - } + $item[$key] = $relation->append([$attr])->toArray(); + } else { + $value = $this->getAttr($name, $item); + + $item[$name] = $value; + } + } + + protected function getArrayData(string $key, $val, array $visible, array $hidden) + { + if ($val instanceof Model || $val instanceof ModelCollection) { + // 关联模型对象 + if (isset($visible[$key])) { + $val->visible($visible[$key]); + } elseif (isset($hidden[$key])) { + $val->hidden($hidden[$key]); } + // 关联模型对象 + return $val->toArray(); } - return $item; + // 模型属性 + return $this->getAttr($key); } /** * 转换当前模型对象为JSON字符串 * @access public - * @param integer $options json参数 + * @param integer $options json参数 * @return string */ - public function toJson($options = JSON_UNESCAPED_UNICODE) + public function toJson(int $options = JSON_UNESCAPED_UNICODE): string { return json_encode($this->toArray(), $options); } @@ -225,7 +243,7 @@ trait Conversion * @param string $resultSetType 数据集类 * @return Collection */ - public function toCollection($collection, $resultSetType = null) + public function toCollection(iterable $collection, string $resultSetType = null): Collection { $resultSetType = $resultSetType ?: $this->resultSetType; @@ -241,12 +259,12 @@ trait Conversion /** * 解析隐藏及显示属性 * @access protected - * @param array $attrs 属性 - * @param array $result 结果集 - * @param bool $visible + * @param array $attrs 属性 + * @param array $result 结果集 + * @param bool $visible * @return array */ - protected function parseAttr($attrs, &$result, $visible = true) + protected function parseAttr(array $attrs, array &$result, bool $visible = true): array { $array = []; diff --git a/src/model/concern/ModelEvent.php b/src/model/concern/ModelEvent.php index 61de65b..2d2c094 100644 --- a/src/model/concern/ModelEvent.php +++ b/src/model/concern/ModelEvent.php @@ -2,12 +2,13 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\model\concern; @@ -18,14 +19,17 @@ use think\Db; */ trait ModelEvent { - // 回调事件 + /** + * 模型回调 + * @var array + */ private static $event = []; /** * 模型事件观察 * @var array */ - protected static $observe = ['before_write', 'after_write', 'before_insert', 'after_insert', 'before_update', 'after_update', 'before_delete', 'after_delete', 'before_restore', 'after_restore']; + protected static $observe = ['after_read', 'before_write', 'after_write', 'before_insert', 'after_insert', 'before_update', 'after_update', 'before_delete', 'after_delete', 'before_restore', 'after_restore']; /** * 绑定模型事件观察者类 @@ -37,33 +41,14 @@ trait ModelEvent * 是否需要事件响应 * @var bool */ - private $withEvent = true; - - /** - * 注册回调方法 - * @access public - * @param string $event 事件名 - * @param callable $callback 回调方法 - * @param bool $override 是否覆盖 - * @return void - */ - public static function event($event, $callback, $override = false) - { - $class = static::class; - - if ($override) { - self::$event[$class][$event] = []; - } - - self::$event[$class][$event][] = $callback; - } + protected $withEvent = true; /** * 清除回调方法 * @access public * @return void */ - public static function flushEvent() + public static function flush(): void { self::$event[static::class] = []; } @@ -71,16 +56,16 @@ trait ModelEvent /** * 注册一个模型观察者 * - * @param object|string $class + * @param string $class * @return void */ - public static function observe($class) + protected static function observe(string $class): void { foreach (static::$observe as $event) { - $eventFuncName = Db::parseName($event, 1, false); + $call = 'on' . Db::parseName($event, 1, false); - if (method_exists($class, $eventFuncName)) { - static::event($event, [$class, $eventFuncName]); + if (method_exists($class, $call)) { + self::$event[static::class][$event][] = [$class, $call]; } } } @@ -91,7 +76,7 @@ trait ModelEvent * @param bool $event 是否需要事件响应 * @return $this */ - public function withEvent($event) + public function withEvent(bool $event) { $this->withEvent = $event; return $this; @@ -100,10 +85,10 @@ trait ModelEvent /** * 触发事件 * @access protected - * @param string $event 事件名 + * @param string $event 事件名 * @return bool */ - protected function trigger($event) + protected function trigger(string $event): bool { $class = static::class; @@ -119,114 +104,4 @@ trait ModelEvent return true; } - - /** - * 模型before_insert事件快捷方法 - * @access protected - * @param callable $callback - * @param bool $override - */ - protected static function beforeInsert($callback, $override = false) - { - self::event('before_insert', $callback, $override); - } - - /** - * 模型after_insert事件快捷方法 - * @access protected - * @param callable $callback - * @param bool $override - */ - protected static function afterInsert($callback, $override = false) - { - self::event('after_insert', $callback, $override); - } - - /** - * 模型before_update事件快捷方法 - * @access protected - * @param callable $callback - * @param bool $override - */ - protected static function beforeUpdate($callback, $override = false) - { - self::event('before_update', $callback, $override); - } - - /** - * 模型after_update事件快捷方法 - * @access protected - * @param callable $callback - * @param bool $override - */ - protected static function afterUpdate($callback, $override = false) - { - self::event('after_update', $callback, $override); - } - - /** - * 模型before_write事件快捷方法 - * @access protected - * @param callable $callback - * @param bool $override - */ - protected static function beforeWrite($callback, $override = false) - { - self::event('before_write', $callback, $override); - } - - /** - * 模型after_write事件快捷方法 - * @access protected - * @param callable $callback - * @param bool $override - */ - protected static function afterWrite($callback, $override = false) - { - self::event('after_write', $callback, $override); - } - - /** - * 模型before_delete事件快捷方法 - * @access protected - * @param callable $callback - * @param bool $override - */ - protected static function beforeDelete($callback, $override = false) - { - self::event('before_delete', $callback, $override); - } - - /** - * 模型after_delete事件快捷方法 - * @access protected - * @param callable $callback - * @param bool $override - */ - protected static function afterDelete($callback, $override = false) - { - self::event('after_delete', $callback, $override); - } - - /** - * 模型before_restore事件快捷方法 - * @access protected - * @param callable $callback - * @param bool $override - */ - protected static function beforeRestore($callback, $override = false) - { - self::event('before_restore', $callback, $override); - } - - /** - * 模型after_restore事件快捷方法 - * @access protected - * @param callable $callback - * @param bool $override - */ - protected static function afterRestore($callback, $override = false) - { - self::event('after_restore', $callback, $override); - } } diff --git a/src/model/concern/OptimLock.php b/src/model/concern/OptimLock.php new file mode 100644 index 0000000..bea2718 --- /dev/null +++ b/src/model/concern/OptimLock.php @@ -0,0 +1,137 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\model\concern; + +use think\Exception; +use think\Model; + +/** + * 乐观锁 + */ +trait OptimLock +{ + protected function getOptimLockField() + { + return property_exists($this, 'optimLock') && isset($this->optimLock) ? $this->optimLock : 'lock_version'; + } + + /** + * 创建新的模型实例 + * @access public + * @param array $data 数据 + * @param bool $isUpdate 是否为更新 + * @param mixed $where 更新条件 + * @return Model + */ + public function newInstance(array $data = [], bool $isUpdate = false, $where = null): Model + { + // 缓存乐观锁 + $this->cacheLockVersion($data); + + return (new static($data))->isUpdate($isUpdate, $where); + } + + /** + * 数据检查 + * @access protected + * @param array $data 数据 + * @return void + */ + protected function checkData(array &$data = []): void + { + if ($this->isExists()) { + if (!$this->checkLockVersion($data)) { + throw new Exception('record has update'); + } + } else { + $this->recordLockVersion($data); + } + } + + /** + * 记录乐观锁 + * @access protected + * @param array $data 数据 + * @return void + */ + protected function recordLockVersion(&$data): void + { + $optimLock = $this->getOptimLockField(); + + if ($optimLock && !isset($data[$optimLock])) { + $data[$optimLock] = 0; + } + } + + /** + * 缓存乐观锁 + * @access protected + * @param array $data 数据 + * @return void + */ + protected function cacheLockVersion($data): void + { + $optimLock = $this->getOptimLockField(); + $pk = $this->getPk(); + + if ($optimLock && isset($data[$optimLock]) && is_string($pk) && isset($data[$pk])) { + $key = $this->getName() . '_' . $data[$pk] . '_lock_version'; + + $_SESSION[$key] = $data[$optimLock]; + } + } + + /** + * 检查乐观锁 + * @access protected + * @param array $data 数据 + * @return bool + */ + protected function checkLockVersion(array &$data): bool + { + // 检查乐观锁 + $id = $this->getKey(); + + if (empty($id)) { + return true; + } + + $key = $this->getName() . '_' . $id . '_lock_version'; + $optimLock = $this->getOptimLockField(); + + if ($optimLock && isset($_SESSION[$key])) { + $lockVer = $_SESSION[$key]; + $vo = $this->field($optimLock)->find($id); + $_SESSION[$key] = $lockVer; + $currVer = $vo[$optimLock]; + + if (isset($currVer)) { + if ($currVer > 0 && $lockVer != $currVer) { + // 记录已经更新 + return false; + } + + // 更新乐观锁 + $lockVer++; + + if ($data[$optimLock] != $lockVer) { + $data[$optimLock] = $lockVer; + } + + $_SESSION[$key] = $lockVer; + } + } + + return true; + } +} diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index c318262..d41421f 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -2,12 +2,13 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\model\concern; @@ -52,15 +53,15 @@ trait RelationShip * 关联自动写入信息 * @var array */ - protected $relationWrite; + protected $relationWrite = []; /** * 设置父关联对象 * @access public - * @param Model $model 模型对象 + * @param Model $model 模型对象 * @return $this */ - public function setParent($model) + public function setParent(Model $model) { $this->parent = $model; @@ -72,7 +73,7 @@ trait RelationShip * @access public * @return Model */ - public function getParent() + public function getParent(): Model { return $this->parent; } @@ -80,29 +81,29 @@ trait RelationShip /** * 获取当前模型的关联模型数据 * @access public - * @param string $name 关联方法名 + * @param string $name 关联方法名 * @return mixed */ - public function getRelation($name = null) + public function getRelation(string $name = null) { if (is_null($name)) { return $this->relation; - } elseif (array_key_exists($name, $this->relation)) { + } + + if (array_key_exists($name, $this->relation)) { return $this->relation[$name]; - } else { - return; } } /** * 设置关联数据对象值 * @access public - * @param string $name 属性名 - * @param mixed $value 属性值 - * @param array $data 数据 + * @param string $name 属性名 + * @param mixed $value 属性值 + * @param array $data 数据 * @return $this */ - public function setRelation($name, $value, $data = []) + public function setRelation(string $name, $value, array $data = []) { // 检测修改器 $method = 'set' . Db::parseName($name, 1) . 'Attr'; @@ -117,17 +118,44 @@ trait RelationShip } /** - * 关联数据一起更新 + * 查询当前模型的关联数据 * @access public - * @param mixed $relation 关联 - * @return $this + * @param array $relations 关联名 + * @return void */ - public function together($relation) + public function relationQuery(array $relations): void { - if (is_string($relation)) { - $relation = explode(',', $relation); + foreach ($relations as $key => $relation) { + $subRelation = ''; + $closure = null; + + if ($relation instanceof \Closure) { + // 支持闭包查询过滤关联条件 + $closure = $relation; + $relation = $key; + } + + if (is_array($relation)) { + $subRelation = $relation; + $relation = $key; + } elseif (strpos($relation, '.')) { + list($relation, $subRelation) = explode('.', $relation, 2); + } + + $method = Db::parseName($relation, 1, false); + + $this->relation[$relation] = $this->$method()->getRelation($subRelation, $closure); } + } + /** + * 关联数据写入 + * @access public + * @param array $relation 关联 + * @return $this + */ + public function together(array $relation) + { $this->together = $relation; $this->checkAutoRelationWrite(); @@ -145,74 +173,27 @@ trait RelationShip * @param string $joinType JOIN类型 * @return Query */ - public static function has($relation, $operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + public static function has(string $relation, string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query { - $relation = (new static())->$relation(); - - if (is_array($operator) || $operator instanceof \Closure) { - return $relation->hasWhere($operator); - } - - return $relation->has($operator, $count, $id, $joinType); + return (new static()) + ->$relation() + ->has($operator, $count, $id, $joinType); } /** * 根据关联条件查询当前模型 * @access public - * @param string $relation 关联方法名 - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 + * @param string $relation 关联方法名 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 * @return Query */ - public static function hasWhere($relation, $where = [], $fields = '*') + public static function hasWhere(string $relation, $where = [], string $fields = '*', string $joinType = ''): Query { - return (new static())->$relation()->hasWhere($where, $fields); - } - - /** - * 查询当前模型的关联数据 - * @access public - * @param string|array $relations 关联名 - * @param array $withRelationAttr 关联获取器 - * @return $this - */ - public function relationQuery($relations, $withRelationAttr = []) - { - if (is_string($relations)) { - $relations = explode(',', $relations); - } - - foreach ($relations as $key => $relation) { - $subRelation = ''; - $closure = null; - - if ($relation instanceof \Closure) { - // 支持闭包查询过滤关联条件 - $closure = $relation; - $relation = $key; - } - - if (is_array($relation)) { - $subRelation = $relation; - $relation = $key; - } elseif (strpos($relation, '.')) { - list($relation, $subRelation) = explode('.', $relation, 2); - } - - $method = Db::parseName($relation, 1, false); - $relationName = Db::parseName($relation); - - $relationResult = $this->$method(); - - if (isset($withRelationAttr[$relationName])) { - $relationResult->withAttr($withRelationAttr[$relationName]); - } - - $this->relation[$relation] = $relationResult->getRelation($subRelation, $closure); - - } - - return $this; + return (new static()) + ->$relation() + ->hasWhere($where, $fields, $joinType); } /** @@ -222,14 +203,12 @@ trait RelationShip * @param string $relation 关联名 * @param array $withRelationAttr 关联获取器 * @param bool $join 是否为JOIN方式 - * @return array + * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $withRelationAttr = [], $join = false) + public function eagerlyResultSet(array &$resultSet, array $relations, array $withRelationAttr = [], bool $join = false): void { - $relations = is_string($relation) ? explode(',', $relation) : $relation; - foreach ($relations as $key => $relation) { - $subRelation = ''; + $subRelation = []; $closure = null; if ($relation instanceof \Closure) { @@ -242,6 +221,8 @@ trait RelationShip $relation = $key; } elseif (strpos($relation, '.')) { list($relation, $subRelation) = explode('.', $relation, 2); + + $subRelation = [$subRelation]; } $relation = Db::parseName($relation, 1, false); @@ -260,18 +241,16 @@ trait RelationShip /** * 预载入关联查询 返回模型对象 * @access public - * @param Model $result 数据对象 - * @param string $relation 关联名 - * @param array $withRelationAttr 关联获取器 - * @param bool $join 是否为JOIN方式 - * @return Model + * @param Model $result 数据对象 + * @param array $relations 关联 + * @param array $withRelationAttr 关联获取器 + * @param bool $join 是否为JOIN方式 + * @return void */ - public function eagerlyResult(&$result, $relation, $withRelationAttr = [], $join = false) + public function eagerlyResult(Model $result, array $relations, array $withRelationAttr = [], bool $join = false): void { - $relations = is_string($relation) ? explode(',', $relation) : $relation; - foreach ($relations as $key => $relation) { - $subRelation = ''; + $subRelation = []; $closure = null; if ($relation instanceof \Closure) { @@ -284,6 +263,8 @@ trait RelationShip $relation = $key; } elseif (strpos($relation, '.')) { list($relation, $subRelation) = explode('.', $relation, 2); + + $subRelation = [$subRelation]; } $relation = Db::parseName($relation, 1, false); @@ -308,10 +289,10 @@ trait RelationShip * @param string $field 字段 * @return void */ - public function relationCount(&$result, $relations, $aggregate = 'sum', $field = '*') + public function relationCount(Model $result, array $relations, string $aggregate = 'sum', string $field = '*'): void { foreach ($relations as $key => $relation) { - $closure = null; + $closure = $name = null; if ($relation instanceof \Closure) { $closure = $relation; @@ -322,9 +303,9 @@ trait RelationShip } $relation = Db::parseName($relation, 1, false); - $count = $this->$relation()->relationCount($result, $closure, $aggregate, $field); + $count = $this->$relation()->relationCount($result, $closure, $aggregate, $field, $name); - if (!isset($name)) { + if (empty($name)) { $name = Db::parseName($relation) . '_' . $aggregate; } @@ -335,12 +316,12 @@ trait RelationShip /** * HAS ONE 关联定义 * @access public - * @param string $model 模型名 - * @param string $foreignKey 关联外键 - * @param string $localKey 当前主键 + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前主键 * @return HasOne */ - public function hasOne($model, $foreignKey = '', $localKey = '') + public function hasOne(string $model, string $foreignKey = '', string $localKey = ''): HasOne { // 记录当前关联信息 $model = $this->parseModel($model); @@ -353,18 +334,18 @@ trait RelationShip /** * BELONGS TO 关联定义 * @access public - * @param string $model 模型名 - * @param string $foreignKey 关联外键 - * @param string $localKey 关联主键 + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 关联主键 * @return BelongsTo */ - public function belongsTo($model, $foreignKey = '', $localKey = '') + public function belongsTo(string $model, string $foreignKey = '', string $localKey = ''): BelongsTo { // 记录当前关联信息 $model = $this->parseModel($model); - $foreignKey = $foreignKey ?: $this->getForeignKey($model); + $foreignKey = $foreignKey ?: $this->getForeignKey((new $model)->getName()); $localKey = $localKey ?: (new $model)->getPk(); - $trace = debug_backtrace(false, 2); + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); $relation = Db::parseName($trace[1]['function']); return new BelongsTo($this, $model, $foreignKey, $localKey, $relation); @@ -373,12 +354,12 @@ trait RelationShip /** * HAS MANY 关联定义 * @access public - * @param string $model 模型名 - * @param string $foreignKey 关联外键 - * @param string $localKey 当前主键 + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前主键 * @return HasMany */ - public function hasMany($model, $foreignKey = '', $localKey = '') + public function hasMany(string $model, string $foreignKey = '', string $localKey = ''): HasMany { // 记录当前关联信息 $model = $this->parseModel($model); @@ -391,21 +372,21 @@ trait RelationShip /** * HAS MANY 远程关联定义 * @access public - * @param string $model 模型名 - * @param string $through 中间模型名 - * @param string $foreignKey 关联外键 - * @param string $throughKey 关联外键 - * @param string $localKey 当前主键 + * @param string $model 模型名 + * @param string $through 中间模型名 + * @param string $foreignKey 关联外键 + * @param string $throughKey 关联外键 + * @param string $localKey 当前主键 * @return HasManyThrough */ - public function hasManyThrough($model, $through, $foreignKey = '', $throughKey = '', $localKey = '') + public function hasManyThrough(string $model, string $through, string $foreignKey = '', string $throughKey = '', string $localKey = ''): HasManyThrough { // 记录当前关联信息 $model = $this->parseModel($model); $through = $this->parseModel($through); $localKey = $localKey ?: $this->getPk(); $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); - $throughKey = $throughKey ?: $this->getForeignKey($through); + $throughKey = $throughKey ?: $this->getForeignKey((new $through)->getName()); return new HasManyThrough($this, $model, $through, $foreignKey, $throughKey, $localKey); } @@ -413,17 +394,17 @@ trait RelationShip /** * BELONGS TO MANY 关联定义 * @access public - * @param string $model 模型名 - * @param string $table 中间表名 - * @param string $foreignKey 关联外键 - * @param string $localKey 当前模型关联键 + * @param string $model 模型名 + * @param string $table 中间表名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前模型关联键 * @return BelongsToMany */ - public function belongsToMany($model, $table = '', $foreignKey = '', $localKey = '') + public function belongsToMany(string $model, string $table = '', string $foreignKey = '', string $localKey = ''): BelongsToMany { // 记录当前关联信息 $model = $this->parseModel($model); - $name = Db::parseName(basename(str_replace('\\', '/', $model))); + $name = Db::parseName(Db::classBaseName($model)); $table = $table ?: Db::parseName($this->name) . '_' . $name; $foreignKey = $foreignKey ?: $name . '_id'; $localKey = $localKey ?: $this->getForeignKey($this->name); @@ -434,18 +415,18 @@ trait RelationShip /** * MORPH One 关联定义 * @access public - * @param string $model 模型名 - * @param string|array $morph 多态字段信息 - * @param string $type 多态类型 + * @param string $model 模型名 + * @param string|array $morph 多态字段信息 + * @param string $type 多态类型 * @return MorphOne */ - public function morphOne($model, $morph = null, $type = '') + public function morphOne(string $model, $morph = null, string $type = ''): MorphOne { // 记录当前关联信息 $model = $this->parseModel($model); if (is_null($morph)) { - $trace = debug_backtrace(false, 2); + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); $morph = Db::parseName($trace[1]['function']); } @@ -464,18 +445,18 @@ trait RelationShip /** * MORPH MANY 关联定义 * @access public - * @param string $model 模型名 - * @param string|array $morph 多态字段信息 - * @param string $type 多态类型 + * @param string $model 模型名 + * @param string|array $morph 多态字段信息 + * @param string $type 多态类型 * @return MorphMany */ - public function morphMany($model, $morph = null, $type = '') + public function morphMany(string $model, $morph = null, string $type = ''): MorphMany { // 记录当前关联信息 $model = $this->parseModel($model); if (is_null($morph)) { - $trace = debug_backtrace(false, 2); + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); $morph = Db::parseName($trace[1]['function']); } @@ -494,13 +475,13 @@ trait RelationShip /** * MORPH TO 关联定义 * @access public - * @param string|array $morph 多态字段信息 - * @param array $alias 多态别名定义 + * @param string|array $morph 多态字段信息 + * @param array $alias 多态别名定义 * @return MorphTo */ - public function morphTo($morph = null, $alias = []) + public function morphTo($morph = null, array $alias = []): MorphTo { - $trace = debug_backtrace(false, 2); + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); $relation = Db::parseName($trace[1]['function']); if (is_null($morph)) { @@ -520,11 +501,11 @@ trait RelationShip /** * 解析模型的完整命名空间 - * @access public - * @param string $model 模型名(或者完整类名) + * @access protected + * @param string $model 模型名(或者完整类名) * @return string */ - protected function parseModel($model) + protected function parseModel(string $model): string { if (false === strpos($model, '\\')) { $path = explode('\\', static::class); @@ -538,14 +519,14 @@ trait RelationShip /** * 获取模型的默认外键名 - * @access public - * @param string $name 模型名 + * @access protected + * @param string $name 模型名 * @return string */ - protected function getForeignKey($name) + protected function getForeignKey(string $name): string { if (strpos($name, '\\')) { - $name = basename(str_replace('\\', '/', $name)); + $name = Db::classBaseName($name); } return Db::parseName($name) . '_id'; @@ -553,11 +534,11 @@ trait RelationShip /** * 检查属性是否为关联属性 如果是则返回关联方法名 - * @access public - * @param string $attr 关联属性名 + * @access protected + * @param string $attr 关联属性名 * @return string|false */ - protected function isRelationAttr($attr) + protected function isRelationAttr(string $attr) { $relation = Db::parseName($attr, 1, false); @@ -570,35 +551,33 @@ trait RelationShip /** * 智能获取关联模型数据 - * @access public - * @param Relation $modelRelation 模型关联对象 + * @access protected + * @param Relation $modelRelation 模型关联对象 * @return mixed */ protected function getRelationData(Relation $modelRelation) { if ($this->parent && !$modelRelation->isSelfRelation() && get_class($this->parent) == get_class($modelRelation->getModel())) { - $value = $this->parent; - } else { - // 获取关联数据 - $value = $modelRelation->getRelation(); + return $this->parent; } - return $value; + // 获取关联数据 + return $modelRelation->getRelation(); } /** * 关联数据自动写入检查 - * @access public + * @access protected * @return void */ - protected function checkAutoRelationWrite() + protected function checkAutoRelationWrite(): void { foreach ($this->together as $key => $name) { if (is_array($name)) { if (key($name) === 0) { $this->relationWrite[$key] = []; // 绑定关联属性 - foreach ((array) $name as $val) { + foreach ($name as $val) { if (isset($this->data[$val])) { $this->relationWrite[$key][$val] = $this->data[$val]; } @@ -618,18 +597,19 @@ trait RelationShip /** * 自动关联数据更新(针对一对一关联) - * @access public + * @access protected * @return void */ - protected function autoRelationUpdate() + protected function autoRelationUpdate(): void { foreach ($this->relationWrite as $name => $val) { if ($val instanceof Model) { - $val->save(); + $val->isUpdate()->save(); } else { $model = $this->getRelation($name); + if ($model instanceof Model) { - $model->save($val); + $model->isUpdate()->save($val); } } } @@ -637,10 +617,10 @@ trait RelationShip /** * 自动关联数据写入(针对一对一关联) - * @access public + * @access protected * @return void */ - protected function autoRelationInsert() + protected function autoRelationInsert(): void { foreach ($this->relationWrite as $name => $val) { $method = Db::parseName($name, 1, false); @@ -650,10 +630,10 @@ trait RelationShip /** * 自动关联数据删除(支持一对一及一对多关联) - * @access public + * @access protected * @return void */ - protected function autoRelationDelete() + protected function autoRelationDelete(): void { foreach ($this->relationWrite as $key => $name) { $name = is_numeric($key) ? $name : $key; diff --git a/src/model/concern/SoftDelete.php b/src/model/concern/SoftDelete.php index 0829e47..7e8fce6 100644 --- a/src/model/concern/SoftDelete.php +++ b/src/model/concern/SoftDelete.php @@ -1,4 +1,14 @@ +// +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\model\concern; @@ -18,9 +28,9 @@ trait SoftDelete /** * 判断当前实例是否被软删除 * @access public - * @return boolean + * @return bool */ - public function trashed() + public function trashed(): bool { $field = $this->getDeleteTimeField(); @@ -40,7 +50,7 @@ trait SoftDelete { $model = new static(); - return $model->withTrashedData(true)->db(false); + return $model->withTrashedData(true)->db(); } /** @@ -49,7 +59,7 @@ trait SoftDelete * @param bool $withTrashed 是否包含软删除数据 * @return $this */ - protected function withTrashedData($withTrashed) + protected function withTrashedData(bool $withTrashed) { $this->withTrashed = $withTrashed; return $this; @@ -60,18 +70,18 @@ trait SoftDelete * @access public * @return Query */ - public static function onlyTrashed() + public static function onlyTrashed(): Query { $model = new static(); $field = $model->getDeleteTimeField(true); if ($field) { return $model - ->db(false) + ->db() ->useSoftDelete($field, $model->getWithTrashedExp()); } - return $model->db(false); + return $model->db(); } /** @@ -79,28 +89,25 @@ trait SoftDelete * @access protected * @return array */ - protected function getWithTrashedExp() + protected function getWithTrashedExp(): array { - return is_null($this->defaultSoftDelete) ? - ['notnull', ''] : ['<>', $this->defaultSoftDelete]; + return is_null($this->defaultSoftDelete) ? ['notnull', ''] : ['<>', $this->defaultSoftDelete]; } /** * 删除当前的记录 * @access public - * @param bool $force 是否强制删除 * @return bool */ - public function delete($force = false) + public function delete(): bool { - if (!$this->isExists() || false === $this->trigger('before_delete', $this)) { + if (!$this->isExists() || $this->isEmpty() || false === $this->trigger('before_delete', $this)) { return false; } - $force = $force ?: $this->isForce(); - $name = $this->getDeleteTimeField(); + $name = $this->getDeleteTimeField(); - if ($name && !$force) { + if ($name && !$this->isForce()) { // 软删除 $this->data($name, $this->autoWriteTimestamp($name)); @@ -112,10 +119,12 @@ trait SoftDelete $where = $this->getWhere(); // 删除当前模型数据 - $this->db(false) + $result = $this->db() ->where($where) ->removeOption('soft_delete') ->delete(); + + $this->lazySave(false); } // 关联删除 @@ -133,14 +142,14 @@ trait SoftDelete /** * 删除记录 * @access public - * @param mixed $data 主键列表 支持闭包查询条件 - * @param bool $force 是否强制删除 + * @param mixed $data 主键列表 支持闭包查询条件 + * @param bool $force 是否强制删除 * @return bool */ - public static function destroy($data, $force = false) + public static function destroy($data, bool $force = false): bool { // 包含软删除数据 - $query = self::withTrashed(); + $query = (new static())->db(false); if (is_array($data) && key($data) !== 0) { $query->where($data); @@ -154,10 +163,8 @@ trait SoftDelete $resultSet = $query->select($data); - if ($resultSet) { - foreach ($resultSet as $data) { - $data->force($force)->delete(); - } + foreach ($resultSet as $result) { + $result->force($force)->delete(); } return true; @@ -166,46 +173,42 @@ trait SoftDelete /** * 恢复被软删除的记录 * @access public - * @param array $where 更新条件 + * @param array $where 更新条件 * @return bool */ - public function restore($where = []) + public function restore($where = []): bool { $name = $this->getDeleteTimeField(); - if ($name) { - if (false === $this->trigger('before_restore')) { - return false; - } + if (!$name || false === $this->trigger('before_restore')) { + return false; + } - if (empty($where)) { - $pk = $this->getPk(); - if (is_string($pk)) { - $where[] = [$pk, '=', $this->getData($pk)]; - } + if (empty($where)) { + $pk = $this->getPk(); + if (is_string($pk)) { + $where[] = [$pk, '=', $this->getData($pk)]; } + } - // 恢复删除 - $this->db(false) - ->where($where) - ->useSoftDelete($name, $this->getWithTrashedExp()) - ->update([$name => $this->defaultSoftDelete]); - - $this->trigger('after_restore'); + // 恢复删除 + $this->db(false) + ->where($where) + ->useSoftDelete($name, $this->getWithTrashedExp()) + ->update([$name => $this->defaultSoftDelete]); - return true; - } + $this->trigger('after_restore'); - return false; + return true; } /** * 获取软删除字段 - * @access public - * @param bool $read 是否查询操作 写操作的时候会自动去掉表别名 + * @access protected + * @param bool $read 是否查询操作 写操作的时候会自动去掉表别名 * @return string|false */ - protected function getDeleteTimeField($read = false) + protected function getDeleteTimeField(bool $read = false) { $field = property_exists($this, 'deleteTime') && isset($this->deleteTime) ? $this->deleteTime : 'delete_time'; @@ -213,7 +216,7 @@ trait SoftDelete return false; } - if (!strpos($field, '.')) { + if (false === strpos($field, '.')) { $field = '__TABLE__.' . $field; } @@ -231,7 +234,7 @@ trait SoftDelete * @param Query $query * @return void */ - protected function withNoTrashed($query) + protected function withNoTrashed(Query $query): void { $field = $this->getDeleteTimeField(true); diff --git a/src/model/concern/TimeStamp.php b/src/model/concern/TimeStamp.php index aa6dddb..c0db4d6 100644 --- a/src/model/concern/TimeStamp.php +++ b/src/model/concern/TimeStamp.php @@ -2,28 +2,45 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\model\concern; use DateTime; + /** * 自动时间戳 */ trait TimeStamp { - // 是否需要自动写入时间戳 如果设置为字符串 则表示时间字段的类型 + /** + * 是否需要自动写入时间戳 如果设置为字符串 则表示时间字段的类型 + * @var bool|string + */ protected $autoWriteTimestamp; - // 创建时间字段 + + /** + * 创建时间字段 false表示关闭 + * @var false|string + */ protected $createTime = 'create_time'; - // 更新时间字段 + + /** + * 更新时间字段 false表示关闭 + * @var false|string + */ protected $updateTime = 'update_time'; - // 时间字段取出后的默认时间格式 + + /** + * 时间字段显示格式 + * @var string + */ protected $dateFormat; /** @@ -31,10 +48,10 @@ trait TimeStamp * @access protected * @param mixed $format 日期格式 * @param mixed $time 时间日期表达式 - * @param bool $timestamp 是否进行时间戳转换 + * @param bool $timestamp 时间表达式是否为时间戳 * @return mixed */ - protected function formatDateTime($format, $time = 'now', $timestamp = false) + protected function formatDateTime($format, $time = 'now', bool $timestamp = false) { if (empty($time)) { return; @@ -46,9 +63,11 @@ trait TimeStamp return new $format($time); } - if ($timestamp) { + if ($time instanceof DateTime) { + $dateTime = $time; + } elseif ($timestamp) { $dateTime = new DateTime(); - $dateTime->setTimestamp($time); + $dateTime->setTimestamp((int) $time); } else { $dateTime = new DateTime($time); } @@ -56,16 +75,4 @@ trait TimeStamp return $dateTime->format($format); } - protected function checkTimeStampWrite() - { - // 自动写入创建时间和更新时间 - if ($this->autoWriteTimestamp) { - if ($this->createTime && !isset($this->data[$this->createTime])) { - $this->data[$this->createTime] = $this->autoWriteTimestamp($this->createTime); - } - if ($this->updateTime && !isset($this->data[$this->updateTime])) { - $this->data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); - } - } - } } diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 22c84cb..a8a7ef8 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -2,15 +2,17 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\model\relation; +use Closure; use think\Db; use think\Model; @@ -19,19 +21,18 @@ class BelongsTo extends OneToOne /** * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $foreignKey 关联外键 - * @param string $localKey 关联主键 - * @param string $relation 关联名 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 关联主键 + * @param string $relation 关联名 */ - public function __construct(Model $parent, $model, $foreignKey, $localKey, $relation = null) + public function __construct(Model $parent, string $model, string $foreignKey, string $localKey, string $relation = null) { $this->parent = $parent; $this->model = $model; $this->foreignKey = $foreignKey; $this->localKey = $localKey; - $this->joinType = 'INNER'; $this->query = (new $model)->db(); $this->relation = $relation; @@ -42,12 +43,12 @@ class BelongsTo extends OneToOne /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 * @access public + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包查询条件 * @return Model */ - public function getRelation($subRelation = '', $closure = null) + public function getRelation(array $subRelation = [], Closure $closure = null) { if ($closure) { $closure($this->query); @@ -71,15 +72,20 @@ class BelongsTo extends OneToOne /** * 创建关联统计子查询 * @access public - * @param \Closure $closure 闭包 + * @param Closure $closure 闭包 * @param string $aggregate 聚合查询方法 * @param string $field 字段 + * @param string $name 聚合字段别名 * @return string */ - public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*') + public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', &$name = ''): string { if ($closure) { $closure($this->query); + + if ($return && is_string($return)) { + $name = $return; + } } return $this->query @@ -88,19 +94,51 @@ class BelongsTo extends OneToOne ->$aggregate($field); } + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 + * @return integer + */ + public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*', string &$name = null) + { + $foreignKey = $this->foreignKey; + + if (!isset($result->$foreignKey)) { + return 0; + } + + if ($closure) { + $return = $closure($this->query); + + if ($resturn && is_string($return)) { + $name = $return; + } + } + + return $this->query + ->where($this->localKey, '=', $result->$foreignKey) + ->$aggregate($field); + } + /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ - public function has($operator = '>=', $count = 1, $id = '*') + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query { $table = $this->query->getTable(); - $model = basename(str_replace('\\', '/', get_class($this->parent))); - $relation = basename(str_replace('\\', '/', $this->model)); + $model = Db::classBaseName($this->parent); + $relation = Db::classBaseName($this->model); $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -116,15 +154,16 @@ class BelongsTo extends OneToOne /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 * @return Query */ - public function hasWhere($where = [], $fields = null) + public function hasWhere($where = [], $fields = null, string $joinType = ''): Query { $table = $this->query->getTable(); - $model = basename(str_replace('\\', '/', get_class($this->parent))); - $relation = basename(str_replace('\\', '/', $this->model)); + $model = Db::classBaseName($this->parent); + $relation = Db::classBaseName($this->model); if (is_array($where)) { $this->getQueryWhere($where, $relation); @@ -135,20 +174,20 @@ class BelongsTo extends OneToOne return $this->parent->db() ->alias($model) ->field($fields) - ->join($table . ' ' . $relation, $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $this->joinType) + ->join([$table => $relation], $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $joinType ?: $this->joinType) ->where($where); } /** * 预载入关联查询(数据集) - * @access public + * @access protected * @param array $resultSet 数据集 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 + * @param array $subRelation 子关联名 * @param \Closure $closure 闭包 * @return void */ - protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) + protected function eagerlySet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null): void { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -195,18 +234,20 @@ class BelongsTo extends OneToOne /** * 预载入关联查询(数据) - * @access public + * @access protected * @param Model $result 数据对象 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 + * @param array $subRelation 子关联名 * @param \Closure $closure 闭包 * @return void */ - protected function eagerlyOne(&$result, $relation, $subRelation, $closure) + protected function eagerlyOne(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void { $localKey = $this->localKey; $foreignKey = $this->foreignKey; + $this->query->removeWhereField($localKey); + $data = $this->eagerlyWhere([ [$localKey, '=', $result->$foreignKey], ], $localKey, $relation, $subRelation, $closure); @@ -232,10 +273,10 @@ class BelongsTo extends OneToOne /** * 添加关联数据 * @access public - * @param Model $model 关联模型对象 + * @param Model $model 关联模型对象 * @return Model */ - public function associate($model) + public function associate(Model $model): Model { $foreignKey = $this->foreignKey; $pk = $model->getPk(); @@ -251,7 +292,7 @@ class BelongsTo extends OneToOne * @access public * @return Model */ - public function dissociate() + public function dissociate(): Model { $foreignKey = $this->foreignKey; @@ -266,7 +307,7 @@ class BelongsTo extends OneToOne * @access protected * @return void */ - protected function baseQuery() + protected function baseQuery(): void { if (empty($this->baseQuery)) { if (isset($this->parent->{$this->foreignKey})) { diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 2fff121..04bf30f 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,6 +11,7 @@ namespace think\model\relation; +use Closure; use think\Collection; use think\Db; use think\db\Query; @@ -33,13 +34,13 @@ class BelongsToMany extends Relation /** * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $table 中间表名 - * @param string $foreignKey 关联模型外键 - * @param string $localKey 当前模型关联键 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $table 中间表名 + * @param string $foreignKey 关联模型外键 + * @param string $localKey 当前模型关联键 */ - public function __construct(Model $parent, $model, $table, $foreignKey, $localKey) + public function __construct(Model $parent, string $model, string $table, string $foreignKey, string $localKey) { $this->parent = $parent; $this->model = $model; @@ -48,7 +49,7 @@ class BelongsToMany extends Relation if (false !== strpos($table, '\\')) { $this->pivotName = $table; - $this->middle = basename(str_replace('\\', '/', $table)); + $this->middle = Db::classBaseName($table); } else { $this->middle = $table; } @@ -59,10 +60,11 @@ class BelongsToMany extends Relation /** * 设置中间表模型 - * @param $pivot + * @access public + * @param $pivot * @return $this */ - public function pivot($pivot) + public function pivot(string $pivot) { $this->pivotName = $pivot; return $this; @@ -74,48 +76,37 @@ class BelongsToMany extends Relation * @param string $name * @return $this */ - public function pivotDataName($name) + public function pivotDataName(string $name) { $this->pivotDataName = $name; return $this; } - /** - * 获取中间表更新条件 - * @param $data - * @return array - */ - protected function getUpdateWhere($data) - { - return [ - $this->localKey => $data[$this->localKey], - $this->foreignKey => $data[$this->foreignKey], - ]; - } - /** * 实例化中间表模型 - * @param $data + * @access public + * @param $data * @return Pivot * @throws Exception */ - protected function newPivot($data = [], $isUpdate = false) + protected function newPivot(array $data = []): Pivot { $class = $this->pivotName ?: '\\think\\model\\Pivot'; $pivot = new $class($data, $this->parent, $this->middle); if ($pivot instanceof Pivot) { - return $isUpdate ? $pivot->isUpdate(true, $this->getUpdateWhere($data)) : $pivot; + return $pivot; + } else { + throw new Exception('pivot model must extends: \think\model\Pivot'); } - - throw new Exception('pivot model must extends: \think\model\Pivot'); } /** * 合成中间表模型 - * @param array|Collection|Paginator $models + * @access protected + * @param array|Collection|Paginator $models */ - protected function hydratePivot($models) + protected function hydratePivot(iterable $models) { foreach ($models as $model) { $pivot = []; @@ -131,15 +122,16 @@ class BelongsToMany extends Relation } } - $model->setRelation($this->pivotDataName, $this->newPivot($pivot, true)); + $model->setRelation($this->pivotDataName, $this->newPivot($pivot)); } } /** * 创建关联查询Query对象 + * @access protected * @return Query */ - protected function buildQuery() + protected function buildQuery(): Query { $foreignKey = $this->foreignKey; $localKey = $this->localKey; @@ -154,11 +146,12 @@ class BelongsToMany extends Relation /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 + * @access public + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包查询条件 * @return Collection */ - public function getRelation($subRelation = '', $closure = null) + public function getRelation(array $subRelation = [], Closure $closure = null): Collection { if ($closure) { $closure($this->query); @@ -172,10 +165,11 @@ class BelongsToMany extends Relation /** * 重载select方法 - * @param null $data + * @access public + * @param mixed $data * @return Collection */ - public function select($data = null) + public function select($data = null): Collection { $result = $this->buildQuery()->select($data); $this->hydratePivot($result); @@ -185,12 +179,13 @@ class BelongsToMany extends Relation /** * 重载paginate方法 - * @param null $listRows - * @param bool $simple - * @param array $config + * @access public + * @param null $listRows + * @param bool $simple + * @param array $config * @return Paginator */ - public function paginate($listRows = null, $simple = false, $config = []) + public function paginate($listRows = null, $simple = false, $config = []): Paginator { $result = $this->buildQuery()->paginate($listRows, $simple, $config); $this->hydratePivot($result); @@ -200,12 +195,14 @@ class BelongsToMany extends Relation /** * 重载find方法 - * @param null $data + * @access public + * @param mixed $data * @return Model */ public function find($data = null) { $result = $this->buildQuery()->find($data); + if ($result) { $this->hydratePivot([$result]); } @@ -216,10 +213,10 @@ class BelongsToMany extends Relation /** * 查找多条记录 如果不存在则抛出异常 * @access public - * @param array|string|Query|\Closure $data + * @param array|string|Query|\Closure $data * @return Collection */ - public function selectOrFail($data = null) + public function selectOrFail($data = null): Collection { return $this->failException(true)->select($data); } @@ -227,10 +224,10 @@ class BelongsToMany extends Relation /** * 查找单条记录 如果不存在则抛出异常 * @access public - * @param array|string|Query|\Closure $data + * @param array|string|Query|\Closure $data * @return Model */ - public function findOrFail($data = null) + public function findOrFail($data = null): Model { return $this->failException(true)->find($data); } @@ -238,13 +235,13 @@ class BelongsToMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ - public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + public function has(string $operator = '>=', $count = 1, $id = '*', string $joinType = 'INNER'): Query { return $this->parent; } @@ -252,8 +249,8 @@ class BelongsToMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query * @throws Exception */ @@ -264,9 +261,10 @@ class BelongsToMany extends Relation /** * 设置中间表的查询条件 - * @param string $field - * @param null $op - * @param null $condition + * @access public + * @param string $field + * @param string $op + * @param mixed $condition * @return $this */ public function wherePivot($field, $op = null, $condition = null) @@ -280,11 +278,11 @@ class BelongsToMany extends Relation * @access public * @param array $resultSet 数据集 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -323,11 +321,11 @@ class BelongsToMany extends Relation * @access public * @param Model $result 数据对象 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure) + public function eagerlyResult(Model $result, string $relation, array $subRelation, Closure $closure = null): void { $pk = $result->getPk(); @@ -350,53 +348,79 @@ class BelongsToMany extends Relation /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return integer */ - public function relationCount($result, $closure) + public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): float { - $pk = $result->getPk(); - $count = 0; + $pk = $result->getPk(); - if (isset($result->$pk)) { - $pk = $result->$pk; - $count = $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ - ['pivot.' . $this->localKey, '=', $pk], - ])->count(); + if (!isset($result->$pk)) { + return 0; } - return $count; + $pk = $result->$pk; + + if ($closure) { + $return = $closure($this->query); + + if ($return && is_string($return)) { + $name = $return; + } + } + + return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ + ['pivot.' . $this->localKey, '=', $pk], + ])->$aggregate($field); } /** * 获取关联统计子查询 * @access public - * @param \Closure $closure 闭包 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return string */ - public function getRelationCountQuery($closure) + public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { + if ($closure) { + $return = $closure($this->query); + + if ($return && is_string($return)) { + $name = $return; + } + } + return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ [ - 'pivot.' . $this->localKey, 'exp', Db::raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()), + 'pivot.' . $this->localKey, 'exp', $this->query->raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()), ], - ])->fetchSql()->count(); + ])->fetchSql()->$aggregate($field); } /** * 多对多 关联模型预查询 - * @access public - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param string $subRelation 子关联 - * @param \Closure $closure 闭包 + * @access protected + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param array $subRelation 子关联 + * @param Closure $closure 闭包 * @return array */ - protected function eagerlyManyToMany($where, $relation, $subRelation = '', $closure = null) + protected function eagerlyManyToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null): array { + if ($closure) { + $closure($this->query); + } + // 预载入关联查询 支持嵌套预载入 - $list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where, $closure) + $list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where) ->with($subRelation) ->select(); @@ -414,7 +438,7 @@ class BelongsToMany extends Relation } } - $set->setRelation($this->pivotDataName, $this->newPivot($pivot, true)); + $set->setRelation($this->pivotDataName, $this->newPivot($pivot)); $data[$pivot[$this->localKey]][] = $set; } @@ -424,19 +448,14 @@ class BelongsToMany extends Relation /** * BELONGS TO MANY 关联查询 - * @access public - * @param string $foreignKey 关联模型关联键 - * @param string $localKey 当前模型关联键 - * @param array $condition 关联查询条件 - * @param \Closure $closure 闭包 + * @access protected + * @param string $foreignKey 关联模型关联键 + * @param string $localKey 当前模型关联键 + * @param array $condition 关联查询条件 * @return Query */ - protected function belongsToManyQuery($foreignKey, $localKey, $condition = [], $closure = null) + protected function belongsToManyQuery(string $foreignKey, string $localKey, array $condition = []): Query { - if ($closure) { - $closure($this->query); - } - // 关联查询封装 $tableName = $this->query->getTable(); $table = $this->pivot->getTable(); @@ -448,7 +467,7 @@ class BelongsToMany extends Relation if (empty($this->baseQuery)) { $relationFk = $this->query->getPk(); - $query->join($table . ' pivot', 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk) + $query->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk) ->where($condition); } @@ -458,8 +477,8 @@ class BelongsToMany extends Relation /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 - * @param array $pivot 中间表额外数据 + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param array $pivot 中间表额外数据 * @return array|Pivot */ public function save($data, array $pivot = []) @@ -471,12 +490,12 @@ class BelongsToMany extends Relation /** * 批量保存当前关联数据对象 * @access public - * @param array $dataSet 数据集 - * @param array $pivot 中间表额外数据 - * @param bool $samePivot 额外数据是否相同 + * @param array $dataSet 数据集 + * @param array $pivot 中间表额外数据 + * @param bool $samePivot 额外数据是否相同 * @return array|false */ - public function saveAll(array $dataSet, array $pivot = [], $samePivot = false) + public function saveAll(array $dataSet, array $pivot = [], bool $samePivot = false) { $result = []; @@ -496,12 +515,12 @@ class BelongsToMany extends Relation /** * 附加关联的一个中间表数据 * @access public - * @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键 - * @param array $pivot 中间表额外数据 + * @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键 + * @param array $pivot 中间表额外数据 * @return array|Pivot * @throws Exception */ - public function attach($data, $pivot = []) + public function attach($data, array $pivot = []) { if (is_array($data)) { if (key($data) === 0) { @@ -530,8 +549,9 @@ class BelongsToMany extends Relation $pivot[$this->foreignKey] = $id; $this->pivot->replace() ->exists(false) + ->data([]) ->save($pivot); - $result[] = $this->newPivot($pivot, true); + $result[] = $this->newPivot($pivot); } if (count($result) == 1) { @@ -549,8 +569,7 @@ class BelongsToMany extends Relation * 判断是否存在关联数据 * @access public * @param mixed $data 数据 可以使用关联模型对象 或者 关联对象的主键 - * @return Pivot - * @throws Exception + * @return Pivot|false */ public function attached($data) { @@ -571,11 +590,11 @@ class BelongsToMany extends Relation /** * 解除关联的一个中间表数据 * @access public - * @param integer|array $data 数据 可以使用关联对象的主键 - * @param bool $relationDel 是否同时删除关联表数据 + * @param integer|array $data 数据 可以使用关联对象的主键 + * @param bool $relationDel 是否同时删除关联表数据 * @return integer */ - public function detach($data = null, $relationDel = false) + public function detach($data = null, bool $relationDel = false): int { if (is_array($data)) { $id = $data; @@ -609,11 +628,12 @@ class BelongsToMany extends Relation /** * 数据同步 - * @param array $ids - * @param bool $detaching + * @access public + * @param array $ids + * @param bool $detaching * @return array */ - public function sync($ids, $detaching = true) + public function sync(array $ids, bool $detaching = true): array { $changes = [ 'attached' => [], @@ -661,14 +681,14 @@ class BelongsToMany extends Relation * @access protected * @return void */ - protected function baseQuery() + protected function baseQuery(): void { if (empty($this->baseQuery) && $this->parent->getData()) { $pk = $this->parent->getPk(); $table = $this->pivot->getTable(); $this->query - ->join($table . ' pivot', 'pivot.' . $this->foreignKey . '=' . $this->query->getTable() . '.' . $this->query->getPk()) + ->join([$table => 'pivot'], 'pivot.' . $this->foreignKey . '=' . $this->query->getTable() . '.' . $this->query->getPk()) ->where('pivot.' . $this->localKey, $this->parent->$pk); $this->baseQuery = true; } diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index aec4da6..dfedb3f 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -2,15 +2,18 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\model\relation; +use Closure; +use think\Collection; use think\Db; use think\db\Query; use think\Model; @@ -21,12 +24,12 @@ class HasMany extends Relation /** * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $foreignKey 关联外键 - * @param string $localKey 当前模型主键 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前模型主键 */ - public function __construct(Model $parent, $model, $foreignKey, $localKey) + public function __construct(Model $parent, string $model, string $foreignKey, string $localKey) { $this->parent = $parent; $this->model = $model; @@ -41,11 +44,12 @@ class HasMany extends Relation /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 + * @access public + * @param array $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 * @return \think\Collection */ - public function getRelation($subRelation = '', $closure = null) + public function getRelation(array $subRelation = [], Closure $closure = null): Collection { if ($closure) { $closure($this->query); @@ -70,11 +74,11 @@ class HasMany extends Relation * @access public * @param array $resultSet 数据集 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void { $localKey = $this->localKey; $range = []; @@ -116,20 +120,18 @@ class HasMany extends Relation * @access public * @param Model $result 数据对象 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure) + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void { $localKey = $this->localKey; if (isset($result->$localKey)) { - $pk = $result->$localKey; - $where = [ - [$this->foreignKey, '=', $pk], - ]; - $data = $this->eagerlyOneToMany($where, $relation, $subRelation, $closure); + $pk = $result->$localKey; + $where[] = [$this->foreignKey, '=', $pk]; + $data = $this->eagerlyOneToMany($where, $relation, $subRelation, $closure); // 关联数据封装 if (!isset($data[$pk])) { @@ -147,33 +149,43 @@ class HasMany extends Relation /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return integer */ - public function relationCount($result, $closure) + public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*', string &$name = null) { $localKey = $this->localKey; - $count = 0; - if (isset($result->$localKey)) { - if ($closure) { - $closure($this->query); - } + if (!isset($result->$localKey)) { + return 0; + } - $count = $this->query->where($this->foreignKey, '=', $result->$localKey)->count(); + if ($closure) { + $return = $closure($this->query); + if ($resturn && is_string($return)) { + $name = $return; + } } - return $count; + return $this->query + ->where($this->foreignKey, '=', $result->$localKey) + ->$aggregate($field); } /** * 创建关联统计子查询 * @access public - * @param \Closure $closure 闭包 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return string */ - public function getRelationCountQuery($closure) + public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { $closure($this->query); @@ -182,7 +194,7 @@ class HasMany extends Relation return $this->query ->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk()) ->fetchSql() - ->count(); + ->$aggregate($field); } /** @@ -190,11 +202,11 @@ class HasMany extends Relation * @access public * @param array $where 关联预查询条件 * @param string $relation 关联名 - * @param string $subRelation 子关联 + * @param array $subRelation 子关联 * @param \Closure $closure * @return array */ - protected function eagerlyOneToMany($where, $relation, $subRelation = '', $closure = null) + protected function eagerlyOneToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null): array { $foreignKey = $this->foreignKey; @@ -221,10 +233,21 @@ class HasMany extends Relation * 保存(新增)当前关联数据对象 * @access public * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 - * @param boolean $replace 是否自动识别更新和写入 * @return Model|false */ - public function save($data, $replace = true) + public function save($data, bool $replace = true) + { + $model = $this->make(); + + return $model->replace($replace)->save($data) ? $model : false; + } + + /** + * 创建关联对象实例 + * @param array $data + * @return Model + */ + public function make(array $data = []): Model { if ($data instanceof Model) { $data = $data->getData(); @@ -233,19 +256,17 @@ class HasMany extends Relation // 保存关联表数据 $data[$this->foreignKey] = $this->parent->{$this->localKey}; - $model = new $this->model; - - return $model->replace($replace)->save($data) ? $model : false; + return new $this->model($data); } /** * 批量保存当前关联数据对象 * @access public - * @param array $dataSet 数据集 + * @param array $dataSet 数据集 * @param boolean $replace 是否自动识别更新和写入 * @return array|false */ - public function saveAll(array $dataSet, $replace = true) + public function saveAll(array $dataSet, bool $replace = true) { $result = []; @@ -259,22 +280,23 @@ class HasMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ - public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query { - $table = $this->query->getTable(); - $model = basename(str_replace('\\', '/', get_class($this->parent))); - $relation = basename(str_replace('\\', '/', $this->model)); + $table = $this->query->getTable(); + + $model = Db::classBaseName($this->parent); + $relation = Db::classBaseName($this->model); return $this->parent->db() ->alias($model) ->field($model . '.*') - ->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType) + ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType ?: $this->joinType) ->group($relation . '.' . $this->foreignKey) ->having('count(' . $id . ')' . $operator . $count); } @@ -282,15 +304,16 @@ class HasMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 * @return Query */ - public function hasWhere($where = [], $fields = null) + public function hasWhere($where = [], $fields = null, string $joinType = ''): Query { $table = $this->query->getTable(); - $model = basename(str_replace('\\', '/', get_class($this->parent))); - $relation = basename(str_replace('\\', '/', $this->model)); + $model = Db::classBaseName($this->parent); + $relation = Db::classBaseName($this->model); if (is_array($where)) { $this->getQueryWhere($where, $relation); @@ -300,8 +323,9 @@ class HasMany extends Relation return $this->parent->db() ->alias($model) + ->group($model . '.' . $this->localKey) ->field($fields) - ->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey) + ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey) ->where($where); } @@ -310,7 +334,7 @@ class HasMany extends Relation * @access protected * @return void */ - protected function baseQuery() + protected function baseQuery(): void { if (empty($this->baseQuery)) { if (isset($this->parent->{$this->localKey})) { diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index 7108b62..4d59581 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,9 +11,11 @@ namespace think\model\relation; +use Closure; +use think\Collection; +use think\Db; use think\db\Query; use think\Exception; -use think\Db; use think\Model; use think\model\Relation; @@ -26,15 +28,15 @@ class HasManyThrough extends Relation /** * 架构函数 - * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $through 中间模型名 - * @param string $foreignKey 关联外键 - * @param string $throughKey 关联外键 - * @param string $localKey 当前主键 + * @access public + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $through 中间模型名 + * @param string $foreignKey 关联外键 + * @param string $throughKey 关联外键 + * @param string $localKey 当前主键 */ - public function __construct(Model $parent, $model, $through, $foreignKey, $throughKey, $localKey) + public function __construct(Model $parent, string $model, string $through, string $foreignKey, string $throughKey, string $localKey) { $this->parent = $parent; $this->model = $model; @@ -47,11 +49,12 @@ class HasManyThrough extends Relation /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 + * @access public + * @param array $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 * @return \think\Collection */ - public function getRelation($subRelation = '', $closure = null) + public function getRelation(array $subRelation = [], \Closure $closure = null): Collection { if ($closure) { $closure($this->query); @@ -65,13 +68,13 @@ class HasManyThrough extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ - public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + public function has(string $operator = '>=', int $count = 1, string $id = '*', $joinType = '') { return $this->parent; } @@ -79,11 +82,11 @@ class HasManyThrough extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = [], $fields = null) + public function hasWhere($where = [], $fields = null, $joinType = '') { throw new Exception('relation not support: hasWhere'); } @@ -91,35 +94,37 @@ class HasManyThrough extends Relation /** * 预载入关联查询 * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure): void {} /** * 预载入关联查询 返回模型对象 * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure) + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void {} /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 * @return integer */ - public function relationCount($result, $closure) + public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*') {} /** @@ -127,11 +132,11 @@ class HasManyThrough extends Relation * @access protected * @return void */ - protected function baseQuery() + protected function baseQuery(): void { if (empty($this->baseQuery) && $this->parent->getData()) { $through = $this->through; - $alias = Db::parseName(basename(str_replace('\\', '/', $this->model))); + $alias = Db::parseName(Db::classBaseName($this->model)); $throughTable = $through::getTable(); $pk = (new $through)->getPk(); $throughKey = $this->throughKey; diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 3d13455..8741d3a 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -2,15 +2,17 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\model\relation; +use Closure; use think\Db; use think\db\Query; use think\Model; @@ -20,18 +22,17 @@ class HasOne extends OneToOne /** * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $foreignKey 关联外键 - * @param string $localKey 当前模型主键 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前模型主键 */ - public function __construct(Model $parent, $model, $foreignKey, $localKey) + public function __construct(Model $parent, string $model, string $foreignKey, string $localKey) { $this->parent = $parent; $this->model = $model; $this->foreignKey = $foreignKey; $this->localKey = $localKey; - $this->joinType = 'INNER'; $this->query = (new $model)->db(); if (get_class($parent) == $model) { @@ -41,11 +42,12 @@ class HasOne extends OneToOne /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 + * @access public + * @param array $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 * @return Model */ - public function getRelation($subRelation = '', $closure = null) + public function getRelation(array $subRelation = [], Closure $closure = null) { $localKey = $this->localKey; @@ -73,12 +75,16 @@ class HasOne extends OneToOne * @param \Closure $closure 闭包 * @param string $aggregate 聚合查询方法 * @param string $field 字段 + * @param string $name 统计字段别名 * @return string */ - public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*') + public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $closure($this->query); + $return = $closure($this->query); + if ($resturn && is_string($return)) { + $name = $return; + } } return $this->query @@ -87,16 +93,50 @@ class HasOne extends OneToOne ->$aggregate($field); } + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 + * @return integer + */ + public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null) + { + $localKey = $this->localKey; + + if (!isset($result->$localKey)) { + return 0; + } + + if ($closure) { + $return = $closure($this->query); + if ($resturn && is_string($return)) { + $name = $return; + } + } + + return $this->query + ->where($this->foreignKey, '=', $result->$localKey) + ->$aggregate($field); + } + /** * 根据关联条件查询当前模型 * @access public + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ - public function has() + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query { $table = $this->query->getTable(); - $model = basename(str_replace('\\', '/', get_class($this->parent))); - $relation = basename(str_replace('\\', '/', $this->model)); + $model = Db::classBaseName($this->parent); + $relation = Db::classBaseName($this->model); $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -112,15 +152,16 @@ class HasOne extends OneToOne /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 * @return Query */ - public function hasWhere($where = [], $fields = null) + public function hasWhere($where = [], $fields = null, string $joinType = ''): Query { $table = $this->query->getTable(); - $model = basename(str_replace('\\', '/', get_class($this->parent))); - $relation = basename(str_replace('\\', '/', $this->model)); + $model = Db::classBaseName($this->parent); + $relation = Db::classBaseName($this->model); if (is_array($where)) { $this->getQueryWhere($where, $relation); @@ -131,20 +172,20 @@ class HasOne extends OneToOne return $this->parent->db() ->alias($model) ->field($fields) - ->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $this->joinType) + ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType ?: $this->joinType) ->where($where); } /** * 预载入关联查询(数据集) - * @access public + * @access protected * @param array $resultSet 数据集 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 + * @param array $subRelation 子关联名 * @param \Closure $closure 闭包 * @return void */ - protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) + protected function eagerlySet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null): void { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -191,18 +232,21 @@ class HasOne extends OneToOne /** * 预载入关联查询(数据) - * @access public + * @access protected * @param Model $result 数据对象 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 + * @param array $subRelation 子关联名 * @param \Closure $closure 闭包 * @return void */ - protected function eagerlyOne(&$result, $relation, $subRelation, $closure) + protected function eagerlyOne(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void { $localKey = $this->localKey; $foreignKey = $this->foreignKey; - $data = $this->eagerlyWhere([ + + $this->query->removeWhereField($foreignKey); + + $data = $this->eagerlyWhere([ [$foreignKey, '=', $result->$localKey], ], $foreignKey, $relation, $subRelation, $closure); @@ -228,7 +272,7 @@ class HasOne extends OneToOne * @access protected * @return void */ - protected function baseQuery() + protected function baseQuery(): void { if (empty($this->baseQuery)) { if (isset($this->parent->{$this->localKey})) { diff --git a/src/model/relation/MorphMany.php b/src/model/relation/MorphMany.php index 2e9f58d..6cf8a24 100644 --- a/src/model/relation/MorphMany.php +++ b/src/model/relation/MorphMany.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,6 +11,8 @@ namespace think\model\relation; +use Closure; +use think\Collection; use think\Db; use think\db\Query; use think\Exception; @@ -28,13 +30,13 @@ class MorphMany extends Relation /** * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $morphKey 关联外键 - * @param string $morphType 多态字段名 - * @param string $type 多态类型 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $morphKey 关联外键 + * @param string $morphType 多态字段名 + * @param string $type 多态类型 */ - public function __construct(Model $parent, $model, $morphKey, $morphType, $type) + public function __construct(Model $parent, string $model, string $morphKey, string $morphType, string $type) { $this->parent = $parent; $this->model = $model; @@ -46,11 +48,12 @@ class MorphMany extends Relation /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 + * @access public + * @param array $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 * @return \think\Collection */ - public function getRelation($subRelation = '', $closure = null) + public function getRelation(array $subRelation = [], Closure $closure = null): Collection { if ($closure) { $closure($this->query); @@ -71,13 +74,13 @@ class MorphMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ - public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '') { throw new Exception('relation not support: has'); } @@ -85,11 +88,11 @@ class MorphMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = [], $fields = null) + public function hasWhere($where = [], $fields = null, string $joinType = '') { throw new Exception('relation not support: hasWhere'); } @@ -99,11 +102,11 @@ class MorphMany extends Relation * @access public * @param array $resultSet 数据集 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 + * @param array $subRelation 子关联名 * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void { $morphType = $this->morphType; $morphKey = $this->morphKey; @@ -149,11 +152,11 @@ class MorphMany extends Relation * @access public * @param Model $result 数据对象 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 + * @param array $subRelation 子关联名 * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure) + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void { $pk = $result->getPk(); @@ -181,65 +184,75 @@ class MorphMany extends Relation /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return integer */ - public function relationCount($result, $closure) + public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*', string &$name = null) { - $pk = $result->getPk(); - $count = 0; + $pk = $result->getPk(); - if (isset($result->$pk)) { - if ($closure) { - $closure($this->query); - } + if (!isset($result->$pk)) { + return 0; + } + + if ($closure) { + $return = $closure($this->query); - $count = $this->query - ->where([ - [$this->morphKey, '=', $result->$pk], - [$this->morphType, '=', $this->type], - ]) - ->count(); + if ($return && is_string($return)) { + $name = $return; + } } - return $count; + return $this->query + ->where([ + [$this->morphKey, '=', $result->$pk], + [$this->morphType, '=', $this->type], + ]) + ->$aggregate($field); } /** * 获取关联统计子查询 * @access public - * @param \Closure $closure 闭包 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 * @return string */ - public function getRelationCountQuery($closure) + public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*'): string { if ($closure) { - $closure($this->query); + $return = $closure($this->query); + + if ($return && is_string($return)) { + $name = $return; + } } return $this->query - ->where([ - [$this->morphKey, 'exp', Db::raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk())], - [$this->morphType, '=', $this->type], - ]) + ->whereExp($this->morphKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk()) + ->where($this->morphType, '=', $this->type) ->fetchSql() - ->count(); + ->$aggregate($field); } /** * 多态一对多 关联模型预查询 - * @access public - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param string $subRelation 子关联 - * @param \Closure $closure 闭包 + * @access protected + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param array $subRelation 子关联 + * @param \Closure $closure 闭包 * @return array */ - protected function eagerlyMorphToMany($where, $relation, $subRelation = '', $closure = null) + protected function eagerlyMorphToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null): array { // 预载入关联查询 支持嵌套预载入 - $this->query->removeOptions('where'); + $this->query->removeOption('where'); if ($closure) { $closure($this->query); @@ -260,10 +273,22 @@ class MorphMany extends Relation /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 * @return Model|false */ public function save($data) + { + $model = $this->make(); + + return $model->save($data) ? $model : false; + } + + /** + * 创建关联对象实例 + * @param array $data + * @return Model + */ + public function make($data = []): Model { if ($data instanceof Model) { $data = $data->getData(); @@ -272,18 +297,16 @@ class MorphMany extends Relation // 保存关联表数据 $pk = $this->parent->getPk(); - $model = new $this->model; - $data[$this->morphKey] = $this->parent->$pk; $data[$this->morphType] = $this->type; - return $model->save($data) ? $model : false; + return new $this->model($data); } /** * 批量保存当前关联数据对象 * @access public - * @param array $dataSet 数据集 + * @param array $dataSet 数据集 * @return array|false */ public function saveAll(array $dataSet) @@ -302,7 +325,7 @@ class MorphMany extends Relation * @access protected * @return void */ - protected function baseQuery() + protected function baseQuery(): void { if (empty($this->baseQuery) && $this->parent->getData()) { $pk = $this->parent->getPk(); diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php index 6f9fe8c..6d0f3e3 100644 --- a/src/model/relation/MorphOne.php +++ b/src/model/relation/MorphOne.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,6 +11,7 @@ namespace think\model\relation; +use Closure; use think\Db; use think\db\Query; use think\Exception; @@ -28,13 +29,13 @@ class MorphOne extends Relation /** * 构造函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $morphKey 关联外键 - * @param string $morphType 多态字段名 - * @param string $type 多态类型 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $morphKey 关联外键 + * @param string $morphType 多态字段名 + * @param string $type 多态类型 */ - public function __construct(Model $parent, $model, $morphKey, $morphType, $type) + public function __construct(Model $parent, string $model, string $morphKey, string $morphType, string $type) { $this->parent = $parent; $this->model = $model; @@ -46,11 +47,12 @@ class MorphOne extends Relation /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 + * @access public + * @param array $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 * @return Model */ - public function getRelation($subRelation = '', $closure = null) + public function getRelation(array $subRelation = [], Closure $closure = null) { if ($closure) { $closure($this->query); @@ -70,13 +72,13 @@ class MorphOne extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ - public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '') { return $this->parent; } @@ -84,11 +86,11 @@ class MorphOne extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = [], $fields = null) + public function hasWhere($where = [], $fields = null, string $joinType = '') { throw new Exception('relation not support: hasWhere'); } @@ -98,11 +100,11 @@ class MorphOne extends Relation * @access public * @param array $resultSet 数据集 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 + * @param array $subRelation 子关联名 * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void { $morphType = $this->morphType; $morphKey = $this->morphKey; @@ -146,11 +148,11 @@ class MorphOne extends Relation * @access public * @param Model $result 数据对象 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 + * @param array $subRelation 子关联名 * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure) + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void { $pk = $result->getPk(); @@ -175,14 +177,14 @@ class MorphOne extends Relation /** * 多态一对一 关联模型预查询 - * @access public - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param string $subRelation 子关联 - * @param \Closure $closure 闭包 + * @access protected + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param array $subRelation 子关联 + * @param \Closure $closure 闭包 * @return array */ - protected function eagerlyMorphToOne($where, $relation, $subRelation = '', $closure = null) + protected function eagerlyMorphToOne(array $where, string $relation, array $subRelation = [], $closure = null): array { // 预载入关联查询 支持嵌套预载入 if ($closure) { @@ -205,22 +207,33 @@ class MorphOne extends Relation /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 * @return Model|false */ public function save($data) + { + $model = $this->make(); + return $model->save($data) ? $model : false; + } + + /** + * 创建关联对象实例 + * @param array $data + * @return Model + */ + public function make($data = []): Model { if ($data instanceof Model) { $data = $data->getData(); } + // 保存关联表数据 $pk = $this->parent->getPk(); - $model = new $this->model; - $data[$this->morphKey] = $this->parent->$pk; $data[$this->morphType] = $this->type; - return $model->save($data) ? $model : false; + + return new $this->model($data); } /** @@ -228,7 +241,7 @@ class MorphOne extends Relation * @access protected * @return void */ - protected function baseQuery() + protected function baseQuery(): void { if (empty($this->baseQuery) && $this->parent->getData()) { $pk = $this->parent->getPk(); diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index e72a451..0c1008c 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,6 +11,7 @@ namespace think\model\relation; +use Closure; use think\Db; use think\Exception; use think\Model; @@ -29,13 +30,13 @@ class MorphTo extends Relation /** * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $morphType 多态字段名 - * @param string $morphKey 外键名 - * @param array $alias 多态别名定义 - * @param string $relation 关联名 + * @param Model $parent 上级模型对象 + * @param string $morphType 多态字段名 + * @param string $morphKey 外键名 + * @param array $alias 多态别名定义 + * @param string $relation 关联名 */ - public function __construct(Model $parent, $morphType, $morphKey, $alias = [], $relation = null) + public function __construct(Model $parent, string $morphType, string $morphKey, array $alias = [], string $relation = null) { $this->parent = $parent; $this->morphType = $morphType; @@ -49,7 +50,7 @@ class MorphTo extends Relation * @access public * @return Model */ - public function getModel() + public function getModel(): Model { $morphType = $this->morphType; $model = $this->parseModel($this->parent->$morphType); @@ -59,11 +60,12 @@ class MorphTo extends Relation /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 + * @access public + * @param array $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 * @return Model */ - public function getRelation($subRelation = '', $closure = null) + public function getRelation(array $subRelation = [], Closure $closure = null) { $morphKey = $this->morphKey; $morphType = $this->morphType; @@ -86,13 +88,13 @@ class MorphTo extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ - public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '') { return $this->parent; } @@ -104,7 +106,7 @@ class MorphTo extends Relation * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = [], $fields = null) + public function hasWhere($where = [], $fields = null, string $joinType = '') { throw new Exception('relation not support: hasWhere'); } @@ -115,7 +117,7 @@ class MorphTo extends Relation * @param string $model 模型名(或者完整类名) * @return string */ - protected function parseModel($model) + protected function parseModel(string $model): string { if (isset($this->alias[$model])) { $model = $this->alias[$model]; @@ -137,7 +139,7 @@ class MorphTo extends Relation * @param array $alias 别名定义 * @return $this */ - public function setAlias($alias) + public function setAlias(array $alias) { $this->alias = $alias; @@ -159,12 +161,12 @@ class MorphTo extends Relation * @access public * @param array $resultSet 数据集 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 + * @param array $subRelation 子关联名 * @param \Closure $closure 闭包 * @return void * @throws Exception */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void { $morphKey = $this->morphKey; $morphType = $this->morphType; @@ -198,6 +200,7 @@ class MorphTo extends Relation // 关联模型 if (!isset($data[$result->$morphKey])) { $relationModel = null; + throw new Exception('relation data not exists :' . $this->model); } else { $relationModel = $data[$result->$morphKey]; $relationModel->setParent(clone $result); @@ -216,11 +219,11 @@ class MorphTo extends Relation * @access public * @param Model $result 数据对象 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 + * @param array $subRelation 子关联名 * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure) + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void { $morphKey = $this->morphKey; $morphType = $this->morphType; @@ -233,23 +236,25 @@ class MorphTo extends Relation /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 * @return integer */ - public function relationCount($result, $closure) + public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*') {} /** * 多态MorphTo 关联模型预查询 - * @access public - * @param object $model 关联模型对象 + * @access protected + * @param string $model 关联模型对象 * @param string $relation 关联名 * @param Model $result - * @param string $subRelation 子关联 + * @param array $subRelation 子关联 * @return void */ - protected function eagerlyMorphToOne($model, $relation, &$result, $subRelation = '') + protected function eagerlyMorphToOne(string $model, string $relation, Model $result, array $subRelation = []): void { // 预载入关联查询 支持嵌套预载入 $pk = $this->parent->{$this->morphKey}; @@ -266,11 +271,11 @@ class MorphTo extends Relation /** * 添加关联数据 * @access public - * @param Model $model 关联模型对象 - * @param string $type 多态类型 + * @param Model $model 关联模型对象 + * @param string $type 多态类型 * @return Model */ - public function associate($model, $type = '') + public function associate(Model $model, string $type = ''): Model { $morphKey = $this->morphKey; $morphType = $this->morphType; @@ -288,7 +293,7 @@ class MorphTo extends Relation * @access public * @return Model */ - public function dissociate() + public function dissociate(): Model { $morphKey = $this->morphKey; $morphType = $this->morphType; diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index 2b9d5ba..30fa5f6 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,6 +11,7 @@ namespace think\model\relation; +use Closure; use think\Db; use think\db\Query; use think\Exception; @@ -24,10 +25,8 @@ use think\model\Relation; */ abstract class OneToOne extends Relation { - // 预载入方式 0 -JOIN 1 -IN - protected $eagerlyType = 1; // 当前关联的JOIN类型 - protected $joinType; + protected $joinType = 'INNER'; // 要绑定的属性 protected $bindAttr = []; // 关联名 @@ -36,10 +35,10 @@ abstract class OneToOne extends Relation /** * 设置join类型 * @access public - * @param string $type JOIN类型 + * @param string $type JOIN类型 * @return $this */ - public function joinType($type) + public function joinType(string $type) { $this->joinType = $type; return $this; @@ -56,9 +55,9 @@ abstract class OneToOne extends Relation * @param bool $first * @return void */ - public function eagerly(Query $query, $relation, $field, $joinType, $closure, $first) + public function eagerly(Query $query, string $relation, $field = true, string $joinType = '', Closure $closure = null, bool $first = false): void { - $name = Db::parseName(basename(str_replace('\\', '/', get_class($this->parent)))); + $name = Db::parseName(Db::classBaseName($this->parent)); if ($first) { $table = $query->getTable(); @@ -104,37 +103,39 @@ abstract class OneToOne extends Relation /** * 预载入关联查询(数据集) - * @param array $resultSet - * @param string $relation - * @param string $subRelation - * @param \Closure $closure + * @access protected + * @param array $resultSet + * @param string $relation + * @param array $subRelation + * @param \Closure $closure * @return mixed */ - abstract protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure); + abstract protected function eagerlySet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null); /** * 预载入关联查询(数据) - * @param Model $result - * @param string $relation - * @param string $subRelation - * @param \Closure $closure + * @access protected + * @param Model $result + * @param string $relation + * @param array $subRelation + * @param \Closure $closure * @return mixed */ - abstract protected function eagerlyOne(&$result, $relation, $subRelation, $closure); + abstract protected function eagerlyOne(Model $result, string $relation, array $subRelation = [], Closure $closure = null); /** * 预载入关联查询(数据集) * @access public * @param array $resultSet 数据集 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 + * @param array $subRelation 子关联名 * @param \Closure $closure 闭包 * @param bool $join 是否为JOIN方式 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $join = false) + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, bool $join = false): void { - if ($join || 0 == $this->eagerlyType) { + if ($join) { // 模型JOIN关联组装 foreach ($resultSet as $result) { $this->match($this->model, $relation, $result); @@ -150,14 +151,14 @@ abstract class OneToOne extends Relation * @access public * @param Model $result 数据对象 * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 + * @param array $subRelation 子关联名 * @param \Closure $closure 闭包 * @param bool $join 是否为JOIN方式 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure, $join = false) + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, bool $join = false): void { - if (0 == $this->eagerlyType || $join) { + if ($join) { // 模型JOIN关联组装 $this->match($this->model, $relation, $result); } else { @@ -169,7 +170,7 @@ abstract class OneToOne extends Relation /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 * @return Model|false */ public function save($data) @@ -185,40 +186,14 @@ abstract class OneToOne extends Relation return $model->save($data) ? $model : false; } - /** - * 设置预载入方式 - * @access public - * @param integer $type 预载入方式 0 JOIN查询 1 IN查询 - * @return $this - */ - public function setEagerlyType($type) - { - $this->eagerlyType = $type; - - return $this; - } - - /** - * 获取预载入方式 - * @access public - * @return integer - */ - public function getEagerlyType() - { - return $this->eagerlyType; - } - /** * 绑定关联表的属性到父模型属性 * @access public - * @param mixed $attr 要绑定的属性列表 + * @param mixed $attr 要绑定的属性列表 * @return $this */ - public function bind($attr) + public function bind(array $attr) { - if (is_string($attr)) { - $attr = explode(',', $attr); - } $this->bindAttr = $attr; return $this; @@ -229,30 +204,20 @@ abstract class OneToOne extends Relation * @access public * @return array */ - public function getBindAttr() + public function getBindAttr(): array { return $this->bindAttr; } - /** - * 关联统计 - * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 - * @return integer - */ - public function relationCount($result, $closure) - {} - /** * 一对一 关联模型预查询拼装 * @access public - * @param string $model 模型名称 - * @param string $relation 关联名 - * @param Model $result 模型对象实例 + * @param string $model 模型名称 + * @param string $relation 关联名 + * @param Model $result 模型对象实例 * @return void */ - protected function match($model, $relation, &$result) + protected function match(string $model, string $relation, Model $result): void { // 重新组装模型数据 foreach ($result->getData() as $key => $val) { @@ -289,12 +254,12 @@ abstract class OneToOne extends Relation /** * 绑定关联属性到父模型 * @access protected - * @param Model $model 关联模型对象 - * @param Model $result 父模型对象 + * @param Model $model 关联模型对象 + * @param Model $result 父模型对象 * @return void * @throws Exception */ - protected function bindAttr($model, &$result) + protected function bindAttr(Model $model, Model $result): void { foreach ($this->bindAttr as $key => $attr) { $key = is_numeric($key) ? $attr : $key; @@ -309,14 +274,14 @@ abstract class OneToOne extends Relation /** * 一对一 关联模型预查询(IN方式) * @access public - * @param array $where 关联预查询条件 - * @param string $key 关联键名 - * @param string $relation 关联名 - * @param string $subRelation 子关联 - * @param \Closure $closure + * @param array $where 关联预查询条件 + * @param string $key 关联键名 + * @param string $relation 关联名 + * @param array $subRelation 子关联 + * @param \Closure $closure * @return array */ - protected function eagerlyWhere($where, $key, $relation, $subRelation = '', $closure = null) + protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null) { // 预载入关联查询 支持嵌套预载入 if ($closure) { diff --git a/src/paginator/Collection.php b/src/paginator/Collection.php deleted file mode 100644 index d3e9c93..0000000 --- a/src/paginator/Collection.php +++ /dev/null @@ -1,74 +0,0 @@ - -// +---------------------------------------------------------------------- - -namespace think\paginator; - -use Exception; -use think\Paginator; - -/** - * Class Collection - * @package think\paginator - * @method integer total() - * @method integer listRows() - * @method integer currentPage() - * @method string render() - * @method Paginator fragment($fragment) - * @method Paginator appends($key, $value) - * @method integer lastPage() - * @method boolean hasPages() - */ -class Collection extends \think\Collection -{ - - /** @var Paginator */ - protected $paginator; - - public function __construct($items = [], Paginator $paginator = null) - { - $this->paginator = $paginator; - parent::__construct($items); - } - - public static function make($items = [], Paginator $paginator = null) - { - return new static($items, $paginator); - } - - public function toArray() - { - if ($this->paginator) { - try { - $total = $this->total(); - } catch (Exception $e) { - $total = null; - } - - return [ - 'total' => $total, - 'per_page' => $this->listRows(), - 'current_page' => $this->currentPage(), - 'data' => parent::toArray(), - ]; - } else { - return parent::toArray(); - } - } - - public function __call($method, $args) - { - if ($this->paginator && method_exists($this->paginator, $method)) { - return call_user_func_array([$this->paginator, $method], $args); - } else { - throw new Exception('method not exists:' . __CLASS__ . '->' . $method); - } - } -} diff --git a/src/paginator/driver/Bootstrap.php b/src/paginator/driver/Bootstrap.php index de44202..e490e28 100644 --- a/src/paginator/driver/Bootstrap.php +++ b/src/paginator/driver/Bootstrap.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -21,7 +21,7 @@ class Bootstrap extends Paginator * @param string $text * @return string */ - protected function getPreviousButton($text = "«") + protected function getPreviousButton(string $text = "«"): string { if ($this->currentPage() <= 1) { @@ -40,7 +40,7 @@ class Bootstrap extends Paginator * @param string $text * @return string */ - protected function getNextButton($text = '»') + protected function getNextButton(string $text = '»'): string { if (!$this->hasMore) { return $this->getDisabledTextWrapper($text); @@ -55,7 +55,7 @@ class Bootstrap extends Paginator * 页码按钮 * @return string */ - protected function getLinks() + protected function getLinks(): string { if ($this->simple) { return ''; @@ -134,7 +134,7 @@ class Bootstrap extends Paginator * @param int $page * @return string */ - protected function getAvailablePageWrapper($url, $page) + protected function getAvailablePageWrapper(string $url, int $page): string { return '
  • ' . $page . '
  • '; } @@ -145,7 +145,7 @@ class Bootstrap extends Paginator * @param string $text * @return string */ - protected function getDisabledTextWrapper($text) + protected function getDisabledTextWrapper(string $text): string { return '
  • ' . $text . '
  • '; } @@ -156,7 +156,7 @@ class Bootstrap extends Paginator * @param string $text * @return string */ - protected function getActivePageWrapper($text) + protected function getActivePageWrapper(string $text): string { return '
  • ' . $text . '
  • '; } @@ -166,7 +166,7 @@ class Bootstrap extends Paginator * * @return string */ - protected function getDots() + protected function getDots(): string { return $this->getDisabledTextWrapper('...'); } @@ -177,7 +177,7 @@ class Bootstrap extends Paginator * @param array $urls * @return string */ - protected function getUrlLinks(array $urls) + protected function getUrlLinks(array $urls): string { $html = ''; @@ -195,7 +195,7 @@ class Bootstrap extends Paginator * @param int $page * @return string */ - protected function getPageLinkWrapper($url, $page) + protected function getPageLinkWrapper(string $url, int $page): string { if ($this->currentPage() == $page) { return $this->getActivePageWrapper($page); -- Gitee From 3c0c319e8b460b4f1c77b76467e0142e83a22b19 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 27 Jan 2019 14:42:17 +0800 Subject: [PATCH 002/365] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B9=90=E8=A7=82?= =?UTF-8?q?=E9=94=81=E6=9C=BA=E5=88=B6=20=E6=A8=A1=E5=9E=8B=E7=B1=BB?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0reflesh=E6=96=B9=E6=B3=95=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 84 ++++++++++++++++++++--------- src/db/Connection.php | 6 ++- src/db/builder/Pgsql.php | 1 - src/model/concern/OptimLock.php | 96 ++++++++------------------------- 4 files changed, 85 insertions(+), 102 deletions(-) diff --git a/src/Model.php b/src/Model.php index d237d71..32d211b 100644 --- a/src/Model.php +++ b/src/Model.php @@ -322,7 +322,10 @@ abstract class Model implements JsonSerializable, ArrayAccess } - protected function checkData(array &$data): void + protected function checkData(): void + {} + + protected function checkResult($result): void {} /** @@ -381,6 +384,26 @@ abstract class Model implements JsonSerializable, ArrayAccess return $this->exists; } + /** + * 刷新模型数据 + * @access public + * @param bool $relation 是否刷新关联数据 + * @return $this + */ + public function reflesh(bool $relation = false) + { + if ($this->exists) { + $this->data = $this->fetchArray()->find($this->getKey()); + $this->origin = $this->data; + + if ($relation) { + $this->relation = []; + } + } + + return $this; + } + /** * 判断模型是否为空 * @access public @@ -496,7 +519,7 @@ abstract class Model implements JsonSerializable, ArrayAccess return false; } - $this->checkData($this->data); + $this->checkData(); // 获取有更新的数据 $data = $this->getChangedData(); @@ -520,28 +543,7 @@ abstract class Model implements JsonSerializable, ArrayAccess // 检查允许字段 $allowFields = $this->checkAllowFields($auto); - // 保留主键数据 - foreach ($this->data as $key => $val) { - if ($this->isPk($key)) { - $data[$key] = $val; - } - } - - $pk = $this->getPk(); - $array = []; - - foreach ((array) $pk as $key) { - if (isset($data[$key])) { - $array[] = [$key, '=', $data[$key]]; - unset($data[$key]); - } - } - - if (!empty($array)) { - $where = $array; - } else { - $where = $this->updateWhere; - } + $where = $this->getUpdateWhere($data); foreach ((array) $this->relationWrite as $name => $val) { if (!is_array($val)) { @@ -560,11 +562,13 @@ abstract class Model implements JsonSerializable, ArrayAccess $db->startTrans(); try { - $db->where($where) + $result = $db->where($where) ->strict(false) ->field($allowFields) ->update($data); + $this->checkResult($result); + // 关联更新 if (!empty($this->relationWrite)) { $this->autoRelationUpdate(); @@ -582,6 +586,34 @@ abstract class Model implements JsonSerializable, ArrayAccess } } + protected function getUpdateWhere(array &$data): array + { + // 保留主键数据 + foreach ($this->data as $key => $val) { + if ($this->isPk($key)) { + $data[$key] = $val; + } + } + + $pk = $this->getPk(); + $array = []; + + foreach ((array) $pk as $key) { + if (isset($data[$key])) { + $array[] = [$key, '=', $data[$key]]; + unset($data[$key]); + } + } + + if (!empty($array)) { + $where = $array; + } else { + $where = $this->updateWhere; + } + + return $where; + } + /** * 新增写入数据 * @access protected @@ -610,7 +642,7 @@ abstract class Model implements JsonSerializable, ArrayAccess return false; } - $this->checkData($this->data); + $this->checkData(); // 检查允许字段 $allowFields = $this->checkAllowFields($auto); diff --git a/src/db/Connection.php b/src/db/Connection.php index c5246b7..cc42c9e 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -1197,7 +1197,11 @@ abstract class Connection */ public function aggregate(Query $query, string $aggregate, $field, bool $force = false) { - $field = $aggregate . '(' . $this->builder->parseKey($query, $field) . ') AS tp_' . strtolower($aggregate); + if (is_string($field) && 0 === stripos($field, 'DISTINCT ')) { + list($distinct, $field) = explode(' ', $field); + } + + $field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS tp_' . strtolower($aggregate); $result = $this->value($query, $field, 0); diff --git a/src/db/builder/Pgsql.php b/src/db/builder/Pgsql.php index f09e22a..399cec1 100644 --- a/src/db/builder/Pgsql.php +++ b/src/db/builder/Pgsql.php @@ -21,7 +21,6 @@ use think\db\Query; */ class Pgsql extends Builder { - protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; diff --git a/src/model/concern/OptimLock.php b/src/model/concern/OptimLock.php index bea2718..038c567 100644 --- a/src/model/concern/OptimLock.php +++ b/src/model/concern/OptimLock.php @@ -13,7 +13,6 @@ declare (strict_types = 1); namespace think\model\concern; use think\Exception; -use think\Model; /** * 乐观锁 @@ -25,113 +24,62 @@ trait OptimLock return property_exists($this, 'optimLock') && isset($this->optimLock) ? $this->optimLock : 'lock_version'; } - /** - * 创建新的模型实例 - * @access public - * @param array $data 数据 - * @param bool $isUpdate 是否为更新 - * @param mixed $where 更新条件 - * @return Model - */ - public function newInstance(array $data = [], bool $isUpdate = false, $where = null): Model - { - // 缓存乐观锁 - $this->cacheLockVersion($data); - - return (new static($data))->isUpdate($isUpdate, $where); - } - /** * 数据检查 * @access protected - * @param array $data 数据 * @return void */ - protected function checkData(array &$data = []): void + protected function checkData(): void { - if ($this->isExists()) { - if (!$this->checkLockVersion($data)) { - throw new Exception('record has update'); - } - } else { - $this->recordLockVersion($data); - } + $this->isExists() ? $this->updateLockVersion() : $this->recordLockVersion(); } /** * 记录乐观锁 * @access protected - * @param array $data 数据 * @return void */ - protected function recordLockVersion(&$data): void + protected function recordLockVersion(): void { $optimLock = $this->getOptimLockField(); - if ($optimLock && !isset($data[$optimLock])) { - $data[$optimLock] = 0; + if ($optimLock) { + $this->set($optimLock, 0); } } /** - * 缓存乐观锁 + * 更新乐观锁 * @access protected - * @param array $data 数据 * @return void */ - protected function cacheLockVersion($data): void + protected function updateLockVersion(): void { $optimLock = $this->getOptimLockField(); - $pk = $this->getPk(); - - if ($optimLock && isset($data[$optimLock]) && is_string($pk) && isset($data[$pk])) { - $key = $this->getName() . '_' . $data[$pk] . '_lock_version'; - $_SESSION[$key] = $data[$optimLock]; + if ($optimLock && $lockVer = $this->getOrigin($optimLock)) { + // 更新乐观锁 + $this->set($optimLock, $lockVer + 1); } } - /** - * 检查乐观锁 - * @access protected - * @param array $data 数据 - * @return bool - */ - protected function checkLockVersion(array &$data): bool + protected function getUpdateWhere(array &$data): array { - // 检查乐观锁 - $id = $this->getKey(); - - if (empty($id)) { - return true; - } - - $key = $this->getName() . '_' . $id . '_lock_version'; + $where = parent::getUpdateWhere($data); $optimLock = $this->getOptimLockField(); - if ($optimLock && isset($_SESSION[$key])) { - $lockVer = $_SESSION[$key]; - $vo = $this->field($optimLock)->find($id); - $_SESSION[$key] = $lockVer; - $currVer = $vo[$optimLock]; - - if (isset($currVer)) { - if ($currVer > 0 && $lockVer != $currVer) { - // 记录已经更新 - return false; - } - - // 更新乐观锁 - $lockVer++; + if ($optimLock && $lockVer = $this->getOrigin($optimLock)) { + $where[] = [$optimLock, '=', $lockVer]; + } - if ($data[$optimLock] != $lockVer) { - $data[$optimLock] = $lockVer; - } + return $where; + } - $_SESSION[$key] = $lockVer; - } + protected function checkResult($result): void + { + if (!$result) { + throw new Exception('record has update'); } - - return true; } + } -- Gitee From 2619efffc8bd64e8f2b84a6297faa744abe62beb Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 28 Jan 2019 08:21:30 +0800 Subject: [PATCH 003/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3composer.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index fd69f45..812ae01 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": ">=7.1.0", "psr/cache": "~1.0", - "psr/simple-cache": "^1.0", + "psr/simple-cache": "^1.0" }, "autoload": { "psr-4": { -- Gitee From eba0f4025f83f9d732be6d62e5c404f57d789731 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 29 Jan 2019 11:50:10 +0800 Subject: [PATCH 004/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Mongo.php | 17 +++++++++-------- src/db/connector/Mongo.php | 34 +++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/db/Mongo.php b/src/db/Mongo.php index 8225b0f..acffa87 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -80,7 +80,7 @@ class Mongo extends Query * @throws ConnectionException * @throws RuntimeException */ - public function mongoQuery($namespace, MongoQuery $query, ReadPreference $readPreference = null, $class = false, $typeMap = null) + public function mongoQuery(string $namespace, MongoQuery $query, ReadPreference $readPreference = null, $class = false, $typeMap = null) { return $this->connection->query($namespace, $query, $readPreference, $class, $typeMap); } @@ -99,7 +99,7 @@ class Mongo extends Query * @throws ConnectionException * @throws RuntimeException */ - public function command(Command $command, $dbName = '', ReadPreference $readPreference = null, $class = false, $typeMap = null) + public function command(Command $command, string $dbName = '', ReadPreference $readPreference = null, $class = false, $typeMap = null) { return $this->connection->command($command, $dbName, $readPreference, $class, $typeMap); } @@ -117,7 +117,7 @@ class Mongo extends Query * @throws RuntimeException * @throws BulkWriteException */ - public function mongoExecute($namespace, BulkWrite $bulk, WriteConcern $writeConcern = null) + public function mongoExecute(string $namespace, BulkWrite $bulk, WriteConcern $writeConcern = null) { return $this->connection->execute($namespace, $bulk, $writeConcern); } @@ -157,6 +157,7 @@ class Mongo extends Query { $cursor = $this->cmd('listCollections', null, $db); $result = []; + foreach ($cursor as $collection) { $result[] = $collection['name']; } @@ -168,7 +169,7 @@ class Mongo extends Query * @access public * @return integer */ - public function count(string $field = null) + public function count(string $field = null): int { $this->parseOptions(); @@ -190,7 +191,7 @@ class Mongo extends Query $this->parseOptions(); $result = $this->cmd('aggregate', [strtolower($aggregate), $field]); - $value = isset($result[0]['aggregate']) ? $result[0]['aggregate'] : 0; + $value = $result[0]['aggregate'] ?? 0; if ($force) { $value += 0; @@ -513,7 +514,7 @@ class Mongo extends Query * @return void * @throws Exception */ - public function parsePkWhere($data) + public function parsePkWhere($data): void { $pk = $this->getPk($this->options); @@ -576,10 +577,10 @@ class Mongo extends Query /** * 分析表达式(可用于查询或者写入操作) - * @access protected + * @access public * @return array */ - protected function parseOptions() + public function parseOptions(): array { $options = $this->options; diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index a6d3c33..4de67fe 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -42,6 +42,14 @@ class Mongo protected $mongo; // MongoDb Object protected $cursor; // MongoCursor Object + /** + * 查询次数 + * @var integer + */ + protected static $queryTimes = 0; + + protected $queryStartTime; + // 监听回调 protected static $event = []; /** @var PDO[] 数据库连接ID 支持多个连接 */ @@ -324,7 +332,7 @@ class Mongo public function query($namespace, MongoQuery $query, ReadPreference $readPreference = null, $class = false, $typeMap = null) { $this->initConnect(false); - Db::$queryTimes++; + self::$queryTimes++; if (false === strpos($namespace, '.')) { $namespace = $this->dbName . '.' . $namespace; @@ -361,7 +369,7 @@ class Mongo public function command(Command $command, $dbName = '', ReadPreference $readPreference = null, $class = false, $typeMap = null) { $this->initConnect(false); - Db::$queryTimes++; + self::$queryTimes++; $this->debug(true); @@ -447,7 +455,7 @@ class Mongo public function execute($namespace, BulkWrite $bulk, WriteConcern $writeConcern = null) { $this->initConnect(true); - Db::$executeTimes++; + self::$queryTimes++; if (false === strpos($namespace, '.')) { $namespace = $this->dbName . '.' . $namespace; @@ -757,7 +765,7 @@ class Mongo public function insert(Query $query, $replace = null, $getLastInsID = false) { // 分析查询表达式 - $options = $query->getOptions(); + $options = $query->parseOptions(); if (empty($options['data'])) { throw new Exception('miss data to insert'); @@ -765,7 +773,7 @@ class Mongo // 生成bulk对象 $bulk = $this->builder->insert($query, $replace); - $writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : null; + $writeConcern = $options['writeConcern'] ?? null; $writeResult = $this->execute($options['table'], $bulk, $writeConcern); $result = $writeResult->getInsertedCount(); @@ -826,7 +834,7 @@ class Mongo public function insertAll(Query $query, array $dataSet) { // 分析查询表达式 - $options = $query->getOptions(); + $options = $query->parseOptions(); if (!is_array(reset($dataSet))) { return false; @@ -854,7 +862,7 @@ class Mongo */ public function update(Query $query) { - $options = $query->getOptions(); + $options = $query->parseOptions(); $data = $options['data']; if (isset($options['cache']) && is_string($options['cache']['key'])) { @@ -936,7 +944,7 @@ class Mongo public function delete(Query $query) { // 分析查询表达式 - $options = $query->getOptions(); + $options = $query->parseOptions(); $pk = $query->getPk($options); $data = $options['data']; @@ -996,7 +1004,7 @@ class Mongo public function getCursor(Query $query) { // 分析查询表达式 - $options = $query->getOptions(); + $options = $query->parseOptions(); // 生成MongoQuery对象 $mongoQuery = $this->builder->select($query); @@ -1021,7 +1029,7 @@ class Mongo */ public function select(Query $query) { - $options = $query->getOptions(); + $options = $query->parseOptions(); $resultSet = false; if ($this->cache && !empty($options['cache'])) { // 判断查询缓存 @@ -1071,7 +1079,7 @@ class Mongo public function find(Query $query) { // 分析查询表达式 - $options = $query->getOptions(); + $options = $query->parseOptions(); $pk = $query->getPk($options); $data = $options['data']; if ($this->cache && !empty($options['cache']) && true === $options['cache']['key'] && is_string($pk) && isset($options['where']['$and'][$pk])) { @@ -1228,7 +1236,7 @@ class Mongo */ public function value(Query $query, $field, $default = null) { - $options = $query->getOptions(); + $options = $query->parseOptions(); $result = null; if ($this->cache && !empty($options['cache'])) { // 判断查询缓存 @@ -1280,7 +1288,7 @@ class Mongo */ public function column(Query $query, $field, $key = '') { - $options = $query->getOptions(); + $options = $query->parseOptions(); $result = false; if ($this->cache && !empty($options['cache'])) { // 判断查询缓存 -- Gitee From 15a34819367cf0f0ca1e60dd99f61c89a606e090 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 16 Feb 2019 11:39:33 +0800 Subject: [PATCH 005/365] =?UTF-8?q?composer.json=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/composer.json b/composer.json index fd69f45..25cdce2 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,10 @@ { "name": "topthink/think-orm", "description": "think orm", + "keywords": [ + "orm", + "database" + ], "license": "Apache-2.0", "authors": [ { -- Gitee From 3a9c2fadfb7e8837164829a996b2fad58c78bfca Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 29 Apr 2019 17:35:58 +0800 Subject: [PATCH 006/365] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 6 +- src/CacheItem.php | 2 +- src/Collection.php | 145 +- src/Db.php | 324 ++++- src/Model.php | 407 +++--- src/Paginator.php | 82 +- src/config.php | 14 +- src/db/Builder.php | 643 +++++---- src/db/Connection.php | 811 +++++------ src/db/Fetch.php | 115 +- src/db/Mongo.php | 231 ++- src/db/Query.php | 1430 ++++++++++--------- src/db/{Expression.php => Raw.php} | 5 +- src/db/Where.php | 7 +- src/db/builder/Mongo.php | 50 +- src/db/builder/Mysql.php | 273 +++- src/db/builder/Oracle.php | 32 +- src/db/builder/Pgsql.php | 17 +- src/db/builder/Sqlite.php | 8 +- src/db/builder/Sqlsrv.php | 46 +- src/db/connector/Mongo.php | 1072 ++++++-------- src/db/connector/Mysql.php | 30 +- src/db/connector/Oracle.php | 37 +- src/db/connector/Pgsql.php | 11 +- src/db/connector/Sqlite.php | 7 +- src/db/connector/Sqlsrv.php | 12 +- src/db/exception/BindParamException.php | 37 + src/db/exception/DataNotFoundException.php | 45 + src/db/exception/ModelNotFoundException.php | 46 + src/facade/Db.php | 74 + src/model/Collection.php | 53 +- src/model/Pivot.php | 14 +- src/model/Relation.php | 58 +- src/model/concern/Attribute.php | 167 +-- src/model/concern/Conversion.php | 161 +-- src/model/concern/ModelEvent.php | 66 +- src/model/concern/OptimLock.php | 4 +- src/model/concern/RelationShip.php | 52 +- src/model/concern/SoftDelete.php | 17 +- src/model/concern/TimeStamp.php | 105 ++ src/model/relation/BelongsTo.php | 23 +- src/model/relation/BelongsToMany.php | 82 +- src/model/relation/HasMany.php | 42 +- src/model/relation/HasManyThrough.php | 16 +- src/model/relation/HasOne.php | 24 +- src/model/relation/MorphMany.php | 50 +- src/model/relation/MorphOne.php | 34 +- src/model/relation/MorphTo.php | 37 +- src/model/relation/OneToOne.php | 42 +- src/paginator/driver/Bootstrap.php | 11 +- 50 files changed, 4018 insertions(+), 3059 deletions(-) rename src/db/{Expression.php => Raw.php} (97%) create mode 100644 src/db/exception/BindParamException.php create mode 100644 src/db/exception/DataNotFoundException.php create mode 100644 src/db/exception/ModelNotFoundException.php create mode 100644 src/facade/Db.php diff --git a/composer.json b/composer.json index a99c577..bf098af 100644 --- a/composer.json +++ b/composer.json @@ -13,9 +13,9 @@ } ], "require": { - "php": ">=7.1.0", - "psr/cache": "~1.0", - "psr/simple-cache": "^1.0" + "php": ">=7.1.0", + "topthink/think-cache": "~2.0", + "topthink/think-container":"~1.0" }, "autoload": { "psr-4": { diff --git a/src/CacheItem.php b/src/CacheItem.php index e79476d..c6fbcdd 100644 --- a/src/CacheItem.php +++ b/src/CacheItem.php @@ -15,8 +15,8 @@ namespace think\cache; use DateInterval; use DateTime; use DateTimeInterface; +use InvalidArgumentException; use Psr\Cache\CacheItemInterface; -use think\exception\InvalidArgumentException; class CacheItem implements CacheItemInterface { diff --git a/src/Collection.php b/src/Collection.php index 17c735e..f2c49a1 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -18,6 +18,9 @@ use Countable; use IteratorAggregate; use JsonSerializable; +/** + * 数据集管理类 + */ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable { /** @@ -62,7 +65,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * 合并数组 * * @access public - * @param mixed $items + * @param mixed $items 数据 * @return static */ public function merge($items) @@ -74,8 +77,8 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * 按指定键整理数据 * * @access public - * @param mixed $items 数据 - * @param string $indexKey 键名 + * @param mixed $items 数据 + * @param string $indexKey 键名 * @return array */ public function dictionary($items = null, string &$indexKey = null) @@ -101,8 +104,8 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * 比较数组,返回差集 * * @access public - * @param mixed $items 数据 - * @param string $indexKey 指定比较的键名 + * @param mixed $items 数据 + * @param string $indexKey 指定比较的键名 * @return static */ public function diff($items, string $indexKey = null) @@ -129,8 +132,8 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * 比较数组,返回交集 * * @access public - * @param mixed $items 数据 - * @param string $indexKey 指定比较的键名 + * @param mixed $items 数据 + * @param string $indexKey 指定比较的键名 * @return static */ public function intersect($items, string $indexKey = null) @@ -175,6 +178,16 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria return new static(array_keys($this->items)); } + /** + * 返回数组中所有的值组成的新 Collection 实例 + * @access public + * @return static + */ + public function values() + { + return new static(array_values($this->items)); + } + /** * 删除数组的最后一个元素(出栈) * @@ -190,7 +203,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * 通过使用用户自定义函数,以字符串返回数组 * * @access public - * @param callable $callback + * @param callable $callback 调用方法 * @param mixed $initial * @return mixed */ @@ -224,8 +237,8 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 在数组结尾插入一个元素 * @access public - * @param mixed $value - * @param mixed $key + * @param mixed $value 元素 + * @param string $key KEY * @return void */ public function push($value, string $key = null): void @@ -241,7 +254,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * 把一个数组分割为新的数组块. * * @access public - * @param int $size + * @param int $size 块大小 * @param bool $preserveKeys * @return static */ @@ -259,8 +272,8 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 在数组开头插入一个元素 * @access public - * @param mixed $value - * @param mixed $key + * @param mixed $value 元素 + * @param string $key KEY * @return void */ public function unshift($value, string $key = null): void @@ -276,7 +289,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * 给每个元素执行个回调 * * @access public - * @param callable $callback + * @param callable $callback 回调 * @return $this */ public function each(callable $callback) @@ -297,7 +310,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 用回调函数处理数组中的元素 * @access public - * @param callable|null $callback + * @param callable|null $callback 回调 * @return static */ public function map(callable $callback) @@ -308,7 +321,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 用回调函数过滤数组中的元素 * @access public - * @param callable|null $callback + * @param callable|null $callback 回调 * @return static */ public function filter(callable $callback = null) @@ -323,9 +336,9 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 根据字段条件过滤数组中的元素 * @access public - * @param string $field 字段名 - * @param mixed $operator 操作符 - * @param mixed $value 数据 + * @param string $field 字段名 + * @param mixed $operator 操作符 + * @param mixed $value 数据 * @return static */ public function where(string $field, $operator, $value = null) @@ -339,9 +352,9 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria if (strpos($field, '.')) { list($field, $relation) = explode('.', $field); - $result = isset($data[$field][$relation]) ? $data[$field][$relation] : null; + $result = $data[$field][$relation] ?? null; } else { - $result = isset($data[$field]) ? $data[$field] : null; + $result = $data[$field] ?? null; } switch ($operator) { @@ -382,6 +395,78 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria }); } + /** + * LIKE过滤 + * @access public + * @param string $field 字段名 + * @param string $value 数据 + * @return static + */ + public function whereLike(string $field, string $value) + { + return $this->where($field, 'like', $value); + } + + /** + * NOT LIKE过滤 + * @access public + * @param string $field 字段名 + * @param string $value 数据 + * @return static + */ + public function whereNotLike(string $field, string $value) + { + return $this->where($field, 'not like', $value); + } + + /** + * IN过滤 + * @access public + * @param string $field 字段名 + * @param array $value 数据 + * @return static + */ + public function whereIn(string $field, array $value) + { + return $this->where($field, 'in', $value); + } + + /** + * NOT IN过滤 + * @access public + * @param string $field 字段名 + * @param array $value 数据 + * @return static + */ + public function whereNotIn(string $field, array $value) + { + return $this->where($field, 'not in', $value); + } + + /** + * BETWEEN 过滤 + * @access public + * @param string $field 字段名 + * @param mixed $value 数据 + * @return static + */ + public function whereBetween(string $field, $value) + { + return $this->where($field, 'between', $value); + } + + /** + * NOT BETWEEN 过滤 + * @access public + * @param string $field 字段名 + * @param mixed $value 数据 + * @return static + */ + public function whereNotBetween(string $field, $value) + { + return $this->where($field, 'not between', $value); + } + /** * 返回数据中指定的一列 * @access public @@ -398,7 +483,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * 对数组排序 * * @access public - * @param callable|null $callback + * @param callable|null $callback 回调 * @return static */ public function sort(callable $callback = null) @@ -418,15 +503,15 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 指定字段排序 * @access public - * @param string $field 排序字段 - * @param string $order 排序 + * @param string $field 排序字段 + * @param string $order 排序 * @return $this */ public function order(string $field, string $order = null) { return $this->sort(function ($a, $b) use ($field, $order) { - $fieldA = isset($a[$field]) ? $a[$field] : null; - $fieldB = isset($b[$field]) ? $b[$field] : null; + $fieldA = $a[$field] ?? null; + $fieldB = $b[$field] ?? null; return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB); }); @@ -451,9 +536,9 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * 截取数组 * * @access public - * @param int $offset - * @param int $length - * @param bool $preserveKeys + * @param int $offset 起始位置 + * @param int $length 截取长度 + * @param bool $preserveKeys preserveKeys * @return static */ public function slice(int $offset, int $length = null, bool $preserveKeys = false) @@ -524,7 +609,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria * 转换成数组 * * @access public - * @param mixed $items + * @param mixed $items 数据 * @return array */ protected function convertToArray($items): array diff --git a/src/Db.php b/src/Db.php index bc9961f..bdeee65 100644 --- a/src/Db.php +++ b/src/Db.php @@ -12,155 +12,327 @@ declare (strict_types = 1); namespace think; -use Psr\SimpleCache\CacheInterface; +use Exception; +use InvalidArgumentException; +use think\Container; use think\db\Connection; +use think\db\Query; +use think\db\Raw; +use think\exception\DbException; +/** + * Class Db + * @package think + * @mixin Query + */ class Db { + /** + * 当前数据库连接对象 + * @var Connection + */ + protected $connection; + + /** + * 数据库连接实例 + * @var array + */ + protected $instance = []; + /** * 数据库配置 * @var array */ - protected static $config = []; + protected $config = []; /** - * 查询类自动映射 + * Event * @var array */ - protected static $queryMap = [ - 'mongo' => '\\think\\db\\Mongo', - ]; + protected $event = []; /** - * 缓存对象 - * @var object + * SQL监听 + * @var array */ - protected static $cacheHandler; + protected $listen = []; - public static function setConfig($config) + /** + * 查询次数 + * @var int + */ + protected $queryTimes = 0; + + /** + * 架构函数 + * @param array $config 连接配置 + * @access public + */ + public function __construct(array $config = []) { - self::$config = $config; + $this->config = $config; } - public static function getConfig($name = null) + public function setConfig(array $config): void { - return $name ? (self::$config[$name] ?? null) : self::$config; + $this->config = array_merge($this->config, $config); } - public static function setCacheHandler(CacheInterface $cacheHandler) + /** + * 切换数据库连接 + * @access public + * @param mixed $config 连接配置 + * @param bool|string $name 连接标识 true 强制重新连接 + * @return $this + */ + public function connect($config = [], $name = false) { - self::$cacheHandler = $cacheHandler; + $this->connection = $this->instance($this->parseConfig($config), $name); + return $this; } - public static function getCacheHandler() + /** + * 取得数据库连接类实例 + * @access public + * @param array $config 连接配置 + * @param bool|string $name 连接标识 true 强制重新连接 + * @return Connection + */ + public function instance(array $config = [], $name = false) { - return self::$cacheHandler; + if (false === $name) { + $name = md5(serialize($config)); + } + + if (true === $name || !isset($this->instance[$name])) { + + if (empty($config['type'])) { + throw new InvalidArgumentException('Undefined db type'); + } + + if (true === $name) { + $name = md5(serialize($config)); + } + + $this->instance[$name] = $this->factory($config['type'], '\\think\\db\\connector\\', $config); + } + + return $this->instance[$name]; } /** - * 创建一个新的查询对象 + * 创建工厂对象实例 * @access public - * @param string $query 查询对象类名 - * @param mixed $connection 连接配置信息 + * @param string $name 工厂类名 + * @param string $namespace 默认命名空间 + * @param array $args * @return mixed */ - public static function buildQuery($query, $connection = []) + public function factory(string $name, string $namespace = '', ...$args) { - $connection = Connection::instance(self::parseConfig($connection)); - return new $query($connection); + $class = false !== strpos($name, '\\') ? $name : $namespace . ucwords($name); + + if (class_exists($class)) { + return Container::getInstance()->invokeClass($class, $args); + } + + throw new Exception('class not exists:' . $class); } /** - * 字符串命名风格转换 - * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 + * 使用表达式设置数据 * @access public - * @param string $name 字符串 - * @param integer $type 转换类型 - * @param bool $ucfirst 首字母是否大写(驼峰规则) - * @return string + * @param string $value 表达式 + * @return Raw */ - public static function parseName(string $name = null, int $type = 0, bool $ucfirst = true): string + public function raw(string $value): Raw { - if ($type) { - $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) { - return strtoupper($match[1]); - }, $name); - return $ucfirst ? ucfirst($name) : lcfirst($name); - } + return new Raw($value); + } - return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); + /** + * 更新查询次数 + * @access public + * @return void + */ + public function updateQueryTimes(): void + { + $this->queryTimes++; } /** - * 获取类名(不包含命名空间) + * 重置查询次数 * @access public - * @param string|object $class - * @return string + * @return void */ - public static function classBaseName($class): string + public function clearQueryTimes(): void { - $class = is_object($class) ? get_class($class) : $class; - return basename(str_replace('\\', '/', $class)); + $this->queryTimes = 0; + } + + /** + * 获得查询次数 + * @access public + * @return integer + */ + public function getQueryTimes(): int + { + return $this->queryTimes; } /** * 数据库连接参数解析 * @access private - * @param mixed $config + * @param mixed $config * @return array */ - private static function parseConfig($config): array + private function parseConfig($config): array { if (empty($config)) { - $config = self::$config; - } elseif (is_string($config) && false === strpos($config, '/')) { + $config = $this->config; + } elseif (is_string($config) && isset($this->config[$config])) { // 支持读取配置参数 - $config = self::$config[$config] ?? static::$config; + $config = $this->config[$config]; } - return is_string($config) ? self::parseDsnConfig($config) : $config; + if (!is_array($config)) { + throw new DbException('database config error:' . $config); + } + + return $config; } /** - * DSN解析 - * 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1¶m2=val2#utf8 - * @access private - * @param string $dsnStr + * 获取数据库的配置参数 + * @access public + * @param string $name 参数名称 + * @return mixed + */ + public function getConfig(string $name = '') + { + return $name ? ($this->config[$name] ?? null) : $this->config; + } + + /** + * 创建一个新的查询对象 + * @access public + * @param string|array $connection 连接配置信息 + * @return mixed + */ + public function buildQuery($connection = []) + { + $connection = $this->instance($this->parseConfig($connection)); + return $this->newQuery($connection); + } + + /** + * 监听SQL执行 + * @access public + * @param callable $callback 回调方法 + * @return void + */ + public function listen(callable $callback): void + { + $this->listen[] = $callback; + } + + /** + * 获取监听SQL执行 + * @access public * @return array */ - private static function parseDsnConfig(string $dsnStr): array + public function getListen(): array { - $info = parse_url($dsnStr); + return $this->listen; + } - if (!$info) { - return []; + /** + * 注册回调方法 + * @access public + * @param string $event 事件名 + * @param callable $callback 回调方法 + * @return void + */ + public function event(string $event, callable $callback): void + { + $this->event[$event] = $callback; + } + + /** + * 触发事件 + * @access public + * @param string $event 事件名 + * @param mixed $params 传入参数 + * @param bool $once + * @return mixed + */ + public function trigger(string $event, $params = null, bool $once = false) + { + if (isset($this->event[$event])) { + return call_user_func_array($this->event[$event], [$this]); } - $dsn = [ - 'type' => $info['scheme'], - 'username' => $info['user'] ?? '', - 'password' => $info['pass'] ?? '', - 'hostname' => $info['host'] ?? '', - 'hostport' => $info['port'] ?? '', - 'database' => !empty($info['path']) ? ltrim($info['path'], '/') : '', - 'charset' => $info['fragment'] ?? 'utf8', - ]; - - if (isset($info['query'])) { - parse_str($info['query'], $dsn['params']); - } else { - $dsn['params'] = []; + return true; + } + + /** + * 创建一个新的查询对象 + * @access protected + * @param Connection $connection 连接对象 + * @return mixed + */ + protected function newQuery($connection = null) + { + /** @var Query $query */ + if (is_null($connection) && !$this->connection) { + $this->connect($this->config); } - return $dsn; + $connection = $connection ?: $this->connection; + $class = $connection->getQueryClass(); + $query = new $class($connection); + + $query->setDb($this); + + return $query; } - public static function __callStatic($method, $args) + /** + * 字符串命名风格转换 + * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 + * @access public + * @param string $name 字符串 + * @param integer $type 转换类型 + * @param bool $ucfirst 首字母是否大写(驼峰规则) + * @return string + */ + public function parseName(string $name = null, int $type = 0, bool $ucfirst = true): string { - $type = strtolower(self::getConfig('type')); - $class = isset(self::$queryMap[$type]) ? self::$queryMap[$type] : '\\think\\db\\Query'; + if ($type) { + $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) { + return strtoupper($match[1]); + }, $name); + return $ucfirst ? ucfirst($name) : lcfirst($name); + } + + return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); + } + + /** + * 获取类名(不包含命名空间) + * @access public + * @param string|object $class + * @return string + */ + public function classBaseName($class): string + { + $class = is_object($class) ? get_class($class) : $class; + return basename(str_replace('\\', '/', $class)); + } - $query = static::buildQuery($class, self::$config); + public function __call($method, $args) + { + $query = $this->newQuery($this->connection); return call_user_func_array([$query, $method], $args); } diff --git a/src/Model.php b/src/Model.php index 32d211b..30b466f 100644 --- a/src/Model.php +++ b/src/Model.php @@ -13,14 +13,46 @@ declare (strict_types = 1); namespace think; use ArrayAccess; +use Closure; use JsonSerializable; -use think\Db; use think\db\Query; +use think\facade\Db; /** * Class Model * @package think * @mixin Query + * @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件 + * @method Query whereTime(string $field, string $op, mixed $range = null) static 查询日期和时间 + * @method Query whereBetweenTime(string $field, mixed $startTime, mixed $endTime) static 查询日期或者时间范围 + * @method Query whereBetweenTimeField(string $startField, string $endField) static 查询当前时间在两个时间字段范围 + * @method Query whereYear(string $field, string $year = 'this year') static 查询某年 + * @method Query whereMonth(string $field, string $month = 'this month') static 查询某月 + * @method Query whereDay(string $field, string $day = 'today') static 查询某日 + * @method Query whereRaw(string $where, array $bind = []) static 表达式查询 + * @method Query whereExp(string $field, string $condition, array $bind = []) static 字段表达式查询 + * @method Query when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询 + * @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询 + * @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询 + * @method Query with(mixed $with) static 关联预载入 + * @method Query count(string $field) static Count统计查询 + * @method Query min(string $field) static Min统计查询 + * @method Query max(string $field) static Max统计查询 + * @method Query sum(string $field) static SUM统计查询 + * @method Query avg(string $field) static Avg统计查询 + * @method Query field(mixed $field, boolean $except = false) static 指定查询字段 + * @method Query fieldRaw(string $field, array $bind = []) static 指定查询字段 + * @method Query union(mixed $union, boolean $all = false) static UNION查询 + * @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT + * @method Query order(mixed $field, string $order = null) static 查询ORDER + * @method Query orderRaw(string $field, array $bind = []) static 查询ORDER + * @method Query cache(mixed $key = null, integer $expire = null) static 设置查询缓存 + * @method mixed value(string $field) static 获取某个字段的值 + * @method array column(string $field, string $key = '') static 获取某个列的值 + * @method Model find(mixed $data = null) static 查询单个记录 不存在返回Null + * @method Model findOrEmpty(mixed $data = null) static 查询单个记录 不存在返回空模型 + * @method \think\model\Collection select(mixed $data = null) static 查询多个记录 + * @method Model withAttr(array $name, \Closure $closure) 动态定义获取器 */ abstract class Model implements JsonSerializable, ArrayAccess { @@ -48,6 +80,12 @@ abstract class Model implements JsonSerializable, ArrayAccess */ private $replace = false; + /** + * 数据表后缀 + * @var string + */ + protected $suffix; + /** * 更新条件 * @var array @@ -60,12 +98,6 @@ abstract class Model implements JsonSerializable, ArrayAccess */ protected $connection; - /** - * 数据库查询对象类名 - * @var string - */ - protected $query; - /** * 模型名称 * @var string @@ -126,16 +158,43 @@ abstract class Model implements JsonSerializable, ArrayAccess */ private $lazySave = false; + /** + * Db对象 + * @var Db + */ + protected $db; + + /** + * 设置Connection信息 + * @access public + * @param mixed $connection 数据库配置 + * @return void + */ + public function setConnection($connection) + { + $this->connection = $connection; + } + + /** + * 获取Connection信息 + * @access public + * @return string|array + */ + public function getConnection() + { + return $this->connection; + } + /** * 架构函数 * @access public - * @param array $data 数据 + * @param array $data 数据 */ public function __construct(array $data = []) { $this->data = $data; - if ($this->data) { + if (!empty($this->data)) { // 废弃字段 foreach ((array) $this->disuse as $key) { if (array_key_exists($key, $this->data)) { @@ -147,36 +206,14 @@ abstract class Model implements JsonSerializable, ArrayAccess // 记录原始数据 $this->origin = $this->data; - $config = Db::getConfig(); - if (empty($this->name)) { // 当前模型名 $name = str_replace('\\', '/', static::class); $this->name = basename($name); - if (!empty($config['class_suffix'])) { - $suffix = basename(dirname($name)); - $this->name = substr($this->name, 0, -strlen($suffix)); - } - } - - if (is_null($this->autoWriteTimestamp)) { - // 自动写入时间戳 - $this->autoWriteTimestamp = $config['auto_timestamp']; } - if (is_null($this->dateFormat)) { - // 设置时间戳格式 - $this->dateFormat = $config['datetime_format']; - } - - if (is_null($this->query)) { - // 设置查询对象 - $this->query = $config['query']; - } - - if (!empty($this->connection) && is_array($this->connection)) { - // 设置模型的数据库连接 - $this->connection = array_merge($config, $this->connection); + if (static::$maker) { + call_user_func(static::$maker, $this); } // 执行初始化操作 @@ -196,65 +233,93 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 创建新的模型实例 * @access public - * @param array $data 数据 - * @param bool $isUpdate 是否为更新 - * @param mixed $where 更新条件 + * @param array $data 数据 + * @param mixed $where 更新条件 * @return Model */ - public function newInstance(array $data = [], bool $isUpdate = false, $where = null): Model + public function newInstance(array $data = [], $where = null): Model { - $model = (new static($data))->isUpdate($isUpdate, $where); + if (empty($data)) { + return new static(); + } + + $model = (new static($data))->exists(true); + $model->setUpdateWhere($where); + + $model->trigger('AfterRead'); - $model->trigger('after_read'); return $model; } /** - * 创建模型的查询对象 + * 设置模型的更新条件 * @access protected - * @return Query + * @param mixed $where 更新条件 + * @return void */ - protected function buildQuery(): Query + protected function setUpdateWhere($where): void { - $query = Db::buildQuery($this->query, $this->connection); - - $query->model($this) - ->name($this->name) - ->json($this->json, $this->jsonAssoc) - ->setFieldType($this->schema); - - if (!empty($this->table)) { - $query->table($this->table); - } - - return $query->pk($this->pk); + $this->updateWhere = $where; } /** - * 获取当前模型的数据库查询对象 + * 设置当前模型的数据库查询对象 * @access public - * @param Query $query 查询对象实例 + * @param Query $query 查询对象实例 * @return $this */ public function setQuery(Query $query) { - $this->queryInstance = $query; + $this->queryInstance = clone $query; return $this; } + /** + * 设置当前模型数据表的后缀 + * @access public + * @param string $suffix 数据表后缀 + * @return $this + */ + public function setSuffix(string $suffix) + { + $this->suffix = $suffix; + return $this; + } + + /** + * 获取当前模型的数据表后缀 + * @access public + * @return string + */ + public function getSuffix(): string + { + return $this->suffix ?: ''; + } + /** * 获取当前模型的数据库查询对象 * @access public - * @param array|false $scope 使用的全局查询范围 + * @param array|false $scope 使用的全局查询范围 * @return Query */ public function db($scope = []): Query { + /** @var Query $query */ if ($this->queryInstance) { - return $this->queryInstance; + $query = $this->queryInstance->removeOption(); + } else { + $query = Db::buildQuery($this->connection) + ->name($this->name . $this->suffix) + ->pk($this->pk); } - $query = $this->buildQuery(); + $query->model($this) + ->json($this->json, $this->jsonAssoc) + ->setFieldType(array_merge($this->schema, $this->jsonType)); + + if (!empty($this->table)) { + $query->table($this->table . $this->suffix); + } // 软删除 if (property_exists($this, 'withTrashed') && !$this->withTrashed) { @@ -262,9 +327,9 @@ abstract class Model implements JsonSerializable, ArrayAccess } // 全局作用域 - $globalScope = is_array($scope) && $scope ? $scope : $this->globalScope; + $globalScope = is_array($scope) && !empty($scope) ? $scope : $this->globalScope; - if ($globalScope && false !== $scope) { + if (!empty($globalScope) && false !== $scope) { $query->scope($globalScope); } @@ -282,7 +347,7 @@ abstract class Model implements JsonSerializable, ArrayAccess if (!isset(static::$initialized[static::class])) { if ($this->observerClass) { // 注册模型观察者 - static::observe($this->observerClass); + $this->observe($this->observerClass); } static::$initialized[static::class] = true; static::init(); @@ -294,13 +359,13 @@ abstract class Model implements JsonSerializable, ArrayAccess * @access protected * @return void */ - protected static function init(): void + protected static function init() {} /** * 数据自动完成 * @access protected - * @param array $auto 要自动更新的字段列表 + * @param array $auto 要自动更新的字段列表 * @return void */ protected function autoCompleteData(array $auto = []): void @@ -319,7 +384,6 @@ abstract class Model implements JsonSerializable, ArrayAccess $this->setAttr($field, !is_null($value) ? $value : $default); } - } protected function checkData(): void @@ -331,7 +395,7 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 更新是否强制写入数据 而不做比较 * @access public - * @param bool $force + * @param bool $force * @return $this */ public function force(bool $force = true) @@ -353,7 +417,7 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 新增数据是否使用Replace * @access public - * @param bool $replace + * @param bool $replace * @return $this */ public function replace(bool $replace = true) @@ -362,10 +426,30 @@ abstract class Model implements JsonSerializable, ArrayAccess return $this; } + /** + * 刷新模型数据 + * @access public + * @param bool $relation 是否刷新关联数据 + * @return $this + */ + public function refresh(bool $relation = false) + { + if ($this->exists) { + $this->data = $this->db()->fetchArray()->find($this->getKey()); + $this->origin = $this->data; + + if ($relation) { + $this->relation = []; + } + } + + return $this; + } + /** * 设置数据是否存在 * @access public - * @param bool $exists + * @param bool $exists * @return $this */ public function exists(bool $exists = true) @@ -384,26 +468,6 @@ abstract class Model implements JsonSerializable, ArrayAccess return $this->exists; } - /** - * 刷新模型数据 - * @access public - * @param bool $relation 是否刷新关联数据 - * @return $this - */ - public function reflesh(bool $relation = false) - { - if ($this->exists) { - $this->data = $this->fetchArray()->find($this->getKey()); - $this->origin = $this->data; - - if ($relation) { - $this->relation = []; - } - } - - return $this; - } - /** * 判断模型是否为空 * @access public @@ -417,10 +481,10 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 延迟保存当前数据对象 * @access public - * @param array|bool $data 数据 + * @param array|bool $data 数据 * @return void */ - public function lazySave($data = []) + public function lazySave($data = []): void { if (false === $data) { $this->lazySave = false; @@ -436,8 +500,8 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 保存当前数据对象 * @access public - * @param array $data 数据 - * @param string $sequence 自增序列名 + * @param array $data 数据 + * @param string $sequence 自增序列名 * @return bool */ public function save(array $data = [], string $sequence = null): bool @@ -445,7 +509,7 @@ abstract class Model implements JsonSerializable, ArrayAccess // 数据对象赋值 $this->setAttrs($data); - if ($this->isEmpty() || false === $this->trigger('before_write')) { + if ($this->isEmpty() || false === $this->trigger('BeforeWrite')) { return false; } @@ -456,7 +520,7 @@ abstract class Model implements JsonSerializable, ArrayAccess } // 写入回调 - $this->trigger('after_write'); + $this->trigger('AfterWrite'); // 重新记录原始数据 $this->origin = $this->data; @@ -469,18 +533,18 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 检查数据是否允许写入 * @access protected - * @param array $append 自动完成的字段列表 + * @param array $append 自动完成的字段列表 * @return array */ protected function checkAllowFields(array $append = []): array { // 检测字段 if (empty($this->field)) { - if ($this->schema) { - $this->field = array_keys($this->schema); + if (!empty($this->schema)) { + $this->field = array_keys(array_merge($this->schema, $this->jsonType)); } else { $query = $this->db(); - $table = $this->table ?: $query->getTable(); + $table = $this->table ? $this->table . $this->suffix : $query->getTable(); $this->field = $query->getConnection()->getTableFields($table); } @@ -494,7 +558,7 @@ abstract class Model implements JsonSerializable, ArrayAccess array_push($field, $this->createTime, $this->updateTime); } - if ($this->disuse) { + if (!empty($this->disuse)) { // 废弃字段 $field = array_diff($field, $this->disuse); } @@ -515,7 +579,7 @@ abstract class Model implements JsonSerializable, ArrayAccess $this->autoCompleteData($auto); // 事件回调 - if (false === $this->trigger('before_update')) { + if (false === $this->trigger('BeforeUpdate')) { return false; } @@ -535,17 +599,14 @@ abstract class Model implements JsonSerializable, ArrayAccess if ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) { // 自动写入更新时间 - $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); - + $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); $this->data[$this->updateTime] = $data[$this->updateTime]; } // 检查允许字段 $allowFields = $this->checkAllowFields($auto); - $where = $this->getUpdateWhere($data); - - foreach ((array) $this->relationWrite as $name => $val) { + foreach ($this->relationWrite as $name => $val) { if (!is_array($val)) { continue; } @@ -562,6 +623,7 @@ abstract class Model implements JsonSerializable, ArrayAccess $db->startTrans(); try { + $where = $this->getWhere(); $result = $db->where($where) ->strict(false) ->field($allowFields) @@ -577,7 +639,7 @@ abstract class Model implements JsonSerializable, ArrayAccess $db->commit(); // 更新回调 - $this->trigger('after_update'); + $this->trigger('AfterUpdate'); return true; } catch (\Exception $e) { @@ -586,38 +648,10 @@ abstract class Model implements JsonSerializable, ArrayAccess } } - protected function getUpdateWhere(array &$data): array - { - // 保留主键数据 - foreach ($this->data as $key => $val) { - if ($this->isPk($key)) { - $data[$key] = $val; - } - } - - $pk = $this->getPk(); - $array = []; - - foreach ((array) $pk as $key) { - if (isset($data[$key])) { - $array[] = [$key, '=', $data[$key]]; - unset($data[$key]); - } - } - - if (!empty($array)) { - $where = $array; - } else { - $where = $this->updateWhere; - } - - return $where; - } - /** * 新增写入数据 * @access protected - * @param string $sequence 自增名 + * @param string $sequence 自增名 * @return bool */ protected function insertData(string $sequence = null): bool @@ -638,7 +672,7 @@ abstract class Model implements JsonSerializable, ArrayAccess } } - if (false === $this->trigger('before_insert')) { + if (false === $this->trigger('BeforeInsert')) { return false; } @@ -653,7 +687,8 @@ abstract class Model implements JsonSerializable, ArrayAccess try { $result = $db->strict(false) ->field($allowFields) - ->insert($this->data, $this->replace, false, $sequence); + ->replace($this->replace) + ->insert($this->data, false, $sequence); // 获取自动增长主键 if ($result && $insertId = $db->getLastInsID($sequence)) { @@ -677,7 +712,7 @@ abstract class Model implements JsonSerializable, ArrayAccess $this->exists = true; // 新增回调 - $this->trigger('after_insert'); + $this->trigger('AfterInsert'); return true; } catch (\Exception $e) { @@ -688,16 +723,15 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 获取当前的更新条件 - * @access protected + * @access public * @return mixed */ - protected function getWhere() + public function getWhere() { - // 删除条件 $pk = $this->getPk(); if (is_string($pk) && isset($this->data[$pk])) { - $where[] = [$pk, '=', $this->data[$pk]]; + $where = [[$pk, '=', $this->data[$pk]]]; } elseif (!empty($this->updateWhere)) { $where = $this->updateWhere; } else { @@ -710,12 +744,12 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 保存多个数据到当前数据对象 * @access public - * @param array $dataSet 数据 - * @param boolean $replace 是否自动识别更新和写入 + * @param iterable $dataSet 数据 + * @param boolean $replace 是否自动识别更新和写入 * @return Collection * @throws \Exception */ - public function saveAll(array $dataSet, bool $replace = true): Collection + public function saveAll(iterable $dataSet, bool $replace = true): Collection { $db = $this->db(); $db->startTrans(); @@ -746,24 +780,6 @@ abstract class Model implements JsonSerializable, ArrayAccess } } - /** - * 是否为更新数据 - * @access public - * @param bool $update - * @param mixed $where - * @return $this - */ - public function isUpdate(bool $update = true, $where = null) - { - $this->exists = $update; - - if (!empty($where)) { - $this->updateWhere = $where; - } - - return $this; - } - /** * 删除当前的记录 * @access public @@ -771,7 +787,7 @@ abstract class Model implements JsonSerializable, ArrayAccess */ public function delete(): bool { - if (!$this->exists || $this->isEmpty() || false === $this->trigger('before_delete')) { + if (!$this->exists || $this->isEmpty() || false === $this->trigger('BeforeDelete')) { return false; } @@ -783,7 +799,7 @@ abstract class Model implements JsonSerializable, ArrayAccess try { // 删除当前模型数据 - $result = $db->where($where)->delete(); + $db->where($where)->delete(); // 关联删除 if (!empty($this->relationWrite)) { @@ -792,7 +808,7 @@ abstract class Model implements JsonSerializable, ArrayAccess $db->commit(); - $this->trigger('after_delete'); + $this->trigger('AfterDelete'); $this->exists = false; $this->lazySave = false; @@ -807,7 +823,7 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 设置自动完成的字段( 规则通过修改器定义) * @access public - * @param array $fields 需要自动完成的字段 + * @param array $fields 需要自动完成的字段 * @return $this */ public function auto(array $fields) @@ -820,9 +836,9 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 写入数据 * @access public - * @param array $data 数据数组 - * @param array $field 允许字段 - * @param bool $replace 使用Replace + * @param array $data 数据数组 + * @param array $allowField 允许字段 + * @param bool $replace 使用Replace * @return static */ public static function create(array $data, array $allowField = [], bool $replace = false): Model @@ -833,7 +849,7 @@ abstract class Model implements JsonSerializable, ArrayAccess $model->allowField($allowField); } - $model->isUpdate(false)->replace($replace)->save($data); + $model->replace($replace)->save($data); return $model; } @@ -841,11 +857,12 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 更新数据 * @access public - * @param array $data 数据数组 - * @param array $allowField 允许字段 + * @param array $data 数据数组 + * @param mixed $where 更新条件 + * @param array $allowField 允许字段 * @return static */ - public static function update(array $data, array $allowField = []) + public static function update(array $data, $where = [], array $allowField = []) { $model = new static(); @@ -853,7 +870,11 @@ abstract class Model implements JsonSerializable, ArrayAccess $model->allowField($allowField); } - $model->isUpdate(true)->save($data); + if (!empty($where)) { + $model->setUpdateWhere($where); + } + + $model->exists(true)->save($data); return $model; } @@ -861,8 +882,8 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 删除记录 * @access public - * @param mixed $data 主键列表 支持闭包查询条件 - * @param bool $force 是否强制删除 + * @param mixed $data 主键列表 支持闭包查询条件 + * @param bool $force 是否强制删除 * @return bool */ public static function destroy($data, bool $force = false): bool @@ -885,8 +906,8 @@ abstract class Model implements JsonSerializable, ArrayAccess $resultSet = $query->select($data); - foreach ($resultSet as $data) { - $data->force($force)->delete(); + foreach ($resultSet as $result) { + $result->force($force)->delete(); } return true; @@ -911,8 +932,8 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 修改器 设置数据对象的值 * @access public - * @param string $name 名称 - * @param mixed $value 值 + * @param string $name 名称 + * @param mixed $value 值 * @return void */ public function __set(string $name, $value): void @@ -923,7 +944,7 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 获取器 获取数据对象的值 * @access public - * @param string $name 名称 + * @param string $name 名称 * @return mixed */ public function __get(string $name) @@ -934,7 +955,7 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 检测数据对象的值 * @access public - * @param string $name 名称 + * @param string $name 名称 * @return bool */ public function __isset(string $name): bool @@ -945,7 +966,7 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 销毁数据对象的值 * @access public - * @param string $name 名称 + * @param string $name 名称 * @return void */ public function __unset(string $name): void @@ -977,7 +998,7 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 设置使用的全局查询范围 * @access public - * @param array|false $scope 启用的全局查询范围 + * @param array|false $scope 启用的全局查询范围 * @return Query */ public static function useGlobalScope($scope) @@ -987,6 +1008,20 @@ abstract class Model implements JsonSerializable, ArrayAccess return $model->db($scope); } + /** + * 切换后缀进行查询 + * @access public + * @param string $suffix 切换的表后缀 + * @return Model + */ + public static function change(string $suffix) + { + $model = new static(); + $model->setSuffix($suffix); + + return $model; + } + public function __call($method, $args) { if ('withattr' == strtolower($method)) { diff --git a/src/Paginator.php b/src/Paginator.php index 15035ad..1e0ab42 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -14,11 +14,17 @@ namespace think; use ArrayAccess; use ArrayIterator; +use Closure; use Countable; +use DomainException; use IteratorAggregate; use JsonSerializable; use Traversable; +/** + * 分页基础类 + * @method array all() + */ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable { /** @@ -74,7 +80,24 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J 'fragment' => '', ]; - public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = []) + /** + * 获取当前页码 + * @var Closure + */ + protected static $currentPageResolver; + + /** + * 获取当前路径 + * @var Closure + */ + protected static $currentPathResolver; + + /** + * @var Closure + */ + protected static $maker; + + public function __construct($items, int $listRows, int $currentPage = 1, int $total = null, bool $simple = false, array $options = []) { $this->options = array_merge($this->options, $options); @@ -102,15 +125,15 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * @access public - * @param $items - * @param $listRows - * @param null $currentPage - * @param null $total + * @param mixed $items + * @param int $listRows + * @param int $currentPage + * @param int $total * @param bool $simple * @param array $options * @return Paginator */ - public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = []) + public static function make($items, int $listRows, int $currentPage = 1, int $total = null, bool $simple = false, array $options = []) { return new static($items, $listRows, $currentPage, $total, $simple, $options); } @@ -128,7 +151,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J * 获取页码对应的链接 * * @access protected - * @param int $page + * @param int $page * @return string */ protected function url(int $page): string @@ -151,7 +174,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J $url = $path; if (!empty($parameters)) { - $url .= '?' . http_build_query($parameters, null, '&'); + $url .= '?' . http_build_query($parameters, '', '&'); } return $url . $this->buildFragment(); @@ -160,8 +183,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 自动获取当前页码 * @access public - * @param string $varPage - * @param int $default + * @param string $varPage + * @param int $default * @return int */ public static function getCurrentPage(string $varPage = 'page', int $default = 1): int @@ -198,7 +221,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J public function total(): int { if ($this->simple) { - throw new \DomainException('not support total'); + throw new DomainException('not support total'); } return $this->total; @@ -217,7 +240,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J public function lastPage(): int { if ($this->simple) { - throw new \DomainException('not support last'); + throw new DomainException('not support last'); } return $this->lastPage; @@ -237,8 +260,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J * 创建一组分页链接 * * @access public - * @param int $start - * @param int $end + * @param int $start + * @param int $end * @return array */ public function getUrlRange(int $start, int $end): array @@ -256,10 +279,10 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J * 设置URL锚点 * * @access public - * @param string|null $fragment + * @param string|null $fragment * @return $this */ - public function fragment($fragment) + public function fragment(string $fragment = null) { $this->options['fragment'] = $fragment; @@ -270,19 +293,12 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J * 添加URL参数 * * @access public - * @param array|string $key - * @param string|null $value + * @param array $append * @return $this */ - public function appends($key, $value = null) + public function appends(array $append) { - if (!is_array($key)) { - $queries = [$key => $value]; - } else { - $queries = $key; - } - - foreach ($queries as $k => $v) { + foreach ($append as $k => $v) { if ($k !== $this->options['var_page']) { $this->options['query'][$k] = $v; } @@ -328,7 +344,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J * 给每个元素执行个回调 * * @access public - * @param callable $callback + * @param callable $callback * @return $this */ public function each(callable $callback) @@ -360,7 +376,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Whether a offset exists * @access public - * @param mixed $offset + * @param mixed $offset * @return bool */ public function offsetExists($offset) @@ -371,7 +387,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Offset to retrieve * @access public - * @param mixed $offset + * @param mixed $offset * @return mixed */ public function offsetGet($offset) @@ -382,8 +398,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Offset to set * @access public - * @param mixed $offset - * @param mixed $value + * @param mixed $offset + * @param mixed $value */ public function offsetSet($offset, $value) { @@ -393,7 +409,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Offset to unset * @access public - * @param mixed $offset + * @param mixed $offset * @return void * @since 5.0.0 */ @@ -419,7 +435,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J { try { $total = $this->total(); - } catch (\DomainException $e) { + } catch (DomainException $e) { $total = null; } diff --git a/src/config.php b/src/config.php index 3a925bb..ababcf5 100644 --- a/src/config.php +++ b/src/config.php @@ -10,7 +10,7 @@ // +---------------------------------------------------------------------- namespace think; -Db::setConfig([ +facade\Db::setConfig([ // 数据库类型 'type' => '', // 服务器地址 @@ -43,24 +43,18 @@ Db::setConfig([ 'slave_no' => '', // 是否严格检查字段是否存在 'fields_strict' => true, - // 数据集返回类型 - 'resultset_type' => '', // 自动写入时间戳字段 'auto_timestamp' => false, // 时间字段取出后的默认时间格式 'datetime_format' => 'Y-m-d H:i:s', // 是否需要进行SQL性能分析 'sql_explain' => false, - // Builder类 - 'builder' => '', - // Query类 - 'query' => '\\think\\db\\Query', // 是否需要断线重连 'break_reconnect' => false, // 默认分页设置 - 'paginate' => [ - 'type' => 'bootstrap', + 'paginate' => [ + 'type' => 'bootstrap', 'var_page' => 'page', 'list_rows' => 15, - ] + ], ]); diff --git a/src/db/Builder.php b/src/db/Builder.php index cd4a625..d87cd53 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -12,18 +12,31 @@ declare (strict_types = 1); namespace think\db; +use Closure; use PDO; use think\Exception; +/** + * Db Builder + */ abstract class Builder { - // connection对象实例 + /** + * Connection对象 + * @var Connection + */ protected $connection; - // 查询表达式映射 + /** + * 查询表达式映射 + * @var array + */ protected $exp = ['NOTLIKE' => 'NOT LIKE', 'NOTIN' => 'NOT IN', 'NOTBETWEEN' => 'NOT BETWEEN', 'NOTEXISTS' => 'NOT EXISTS', 'NOTNULL' => 'NOT NULL', 'NOTBETWEEN TIME' => 'NOT BETWEEN TIME']; - // 查询表达式解析 + /** + * 查询表达式解析 + * @var array + */ protected $parser = [ 'parseCompare' => ['=', '<>', '>', '>=', '<', '<='], 'parseLike' => ['LIKE', 'NOT LIKE'], @@ -37,16 +50,35 @@ abstract class Builder 'parseColumn' => ['COLUMN'], ]; - // SQL表达式 - protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT% %LOCK%%COMMENT%'; + /** + * SELECT SQL表达式 + * @var string + */ + protected $selectSql = 'SELECT%DISTINCT%%EXTRA% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT% %LOCK%%COMMENT%'; - protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; + /** + * INSERT SQL表达式 + * @var string + */ + protected $insertSql = '%INSERT%%EXTRA% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; - protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; + /** + * INSERT ALL SQL表达式 + * @var string + */ + protected $insertAllSql = '%INSERT%%EXTRA% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; - protected $updateSql = 'UPDATE %TABLE% SET %SET%%JOIN%%WHERE%%ORDER%%LIMIT% %LOCK%%COMMENT%'; + /** + * UPDATE SQL表达式 + * @var string + */ + protected $updateSql = 'UPDATE%EXTRA% %TABLE% SET %SET%%JOIN%%WHERE%%ORDER%%LIMIT% %LOCK%%COMMENT%'; - protected $deleteSql = 'DELETE FROM %TABLE%%USING%%JOIN%%WHERE%%ORDER%%LIMIT% %LOCK%%COMMENT%'; + /** + * DELETE SQL表达式 + * @var string + */ + protected $deleteSql = 'DELETE%EXTRA% FROM %TABLE%%USING%%JOIN%%WHERE%%ORDER%%LIMIT% %LOCK%%COMMENT%'; /** * 架构函数 @@ -100,7 +132,7 @@ abstract class Builder // 获取绑定信息 if (empty($bind)) { - $bind = $query->getFieldBindType(); + $bind = $query->getFieldsBindType(); } if (empty($fields)) { @@ -116,20 +148,17 @@ abstract class Builder foreach ($data as $key => $val) { $item = $this->parseKey($query, $key, true); - if ($val instanceof Expression) { + if ($val instanceof Raw) { $result[$item] = $val->getValue(); continue; } elseif (!is_scalar($val) && (in_array($key, (array) $query->getOptions('json')) || 'json' == $this->connection->getFieldsType($options['table'], $key))) { $val = json_encode($val); - } elseif (is_object($val) && method_exists($val, '__toString')) { - // 对象数据写入 - $val = $val->__toString(); } if (false !== strpos($key, '->')) { - list($key, $name) = explode('->', $key); + list($key, $name) = explode('->', $key, 2); $item = $this->parseKey($query, $key); - $result[$item] = 'json_set(' . $item . ', \'$.' . $name . '\', ' . $this->parseDataBind($query, $key, $val, $bind) . ')'; + $result[$item] = 'json_set(' . $item . ', \'$.' . $name . '\', ' . $this->parseDataBind($query, $key . '->' . $name, $val, $bind) . ')'; } elseif (false === strpos($key, '.') && !in_array($key, $fields, true)) { if ($options['strict']) { throw new Exception('fields not exists:[' . $key . ']'); @@ -165,11 +194,11 @@ abstract class Builder */ protected function parseDataBind(Query $query, string $key, $data, array $bind = []): string { - if ($data instanceof Expression) { + if ($data instanceof Raw) { return $data->getValue(); } - $name = $query->bind($data, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR); + $name = $query->bindValue($data, $bind[$key] ?? PDO::PARAM_STR); return ':' . $name; } @@ -187,6 +216,18 @@ abstract class Builder return $key; } + /** + * 查询额外参数分析 + * @access protected + * @param Query $query 查询对象 + * @param string $extra 额外参数 + * @return string + */ + protected function parseExtra(Query $query, string $extra): string + { + return preg_match('/^[\w]+$/i', $extra) ? ' ' . strtoupper($extra) : ''; + } + /** * field分析 * @access protected @@ -196,14 +237,12 @@ abstract class Builder */ protected function parseField(Query $query, $fields): string { - if ('*' == $fields || empty($fields)) { - $fieldsStr = '*'; - } elseif (is_array($fields)) { + if (is_array($fields)) { // 支持 'field1'=>'field2' 这样的字段别名定义 $array = []; foreach ($fields as $key => $field) { - if ($field instanceof Expression) { + if ($field instanceof Raw) { $array[] = $field->getValue(); } elseif (!is_numeric($key)) { $array[] = $this->parseKey($query, $key) . ' AS ' . $this->parseKey($query, $field, true); @@ -213,6 +252,8 @@ abstract class Builder } $fieldsStr = implode(',', $array); + } else { + $fieldsStr = '*'; } return $fieldsStr; @@ -231,7 +272,7 @@ abstract class Builder $options = $query->getOptions(); foreach ((array) $tables as $key => $table) { - if ($table instanceof Expression) { + if ($table instanceof Raw) { $item[] = $table->getValue(); } elseif (!is_numeric($key)) { $item[] = $this->parseKey($query, $key) . ' ' . $this->parseKey($query, $table); @@ -261,9 +302,9 @@ abstract class Builder // 附加软删除条件 list($field, $condition) = $options['soft_delete']; - $binds = $query->getFieldBindType(); + $binds = $query->getFieldsBindType(); $whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : ''; - $whereStr = $whereStr . $this->parseWhereItem($query, $field, $condition, '', $binds); + $whereStr = $whereStr . $this->parseWhereItem($query, $field, $condition, $binds); } return empty($whereStr) ? '' : ' WHERE ' . $whereStr; @@ -284,126 +325,193 @@ abstract class Builder $whereStr = ''; - $binds = $query->getFieldBindType(); + $binds = $query->getFieldsBindType(); foreach ($where as $logic => $val) { - $str = []; + $str = $this->parseWhereLogic($query, $logic, $val, $binds); - foreach ($val as $value) { - if ($value instanceof Expression) { - $str[] = ' ' . $logic . ' ( ' . $value->getValue() . ' )'; - continue; - } + $whereStr .= empty($whereStr) ? substr(implode(' ', $str), strlen($logic) + 1) : implode(' ', $str); + } + + return $whereStr; + } - if (is_array($value)) { - if (key($value) !== 0) { - throw new Exception('where express error:' . var_export($value, true)); - } - $field = array_shift($value); - } elseif (!($value instanceof \Closure)) { + /** + * 不同字段使用相同查询条件(AND) + * @access protected + * @param Query $query 查询对象 + * @param string $logic Logic + * @param array $val 查询条件 + * @param array $binds 参数绑定 + * @return array + */ + protected function parseWhereLogic(Query $query, string $logic, array $val, array $binds = []): array + { + $where = []; + foreach ($val as $value) { + if ($value instanceof Raw) { + $where[] = ' ' . $logic . ' ( ' . $value->getValue() . ' )'; + continue; + } + + if (is_array($value)) { + if (key($value) !== 0) { throw new Exception('where express error:' . var_export($value, true)); } + $field = array_shift($value); + } elseif (!($value instanceof \Closure)) { + throw new Exception('where express error:' . var_export($value, true)); + } - if ($value instanceof \Closure) { - // 使用闭包查询 - $newQuery = $query->newQuery()->setConnection($this->connection); - $value($newQuery); - $whereClause = $this->buildWhere($query, $newQuery->getOptions('where')); - - if (!empty($whereClause)) { - $str[] = ' ' . $logic . ' ( ' . $whereClause . ' )'; - } - } elseif (is_array($field)) { - array_unshift($value, $field); - $str2 = []; - foreach ($value as $item) { - $str2[] = $this->parseWhereItem($query, array_shift($item), $item, $logic, $binds); - } - - $str[] = ' ' . $logic . ' ( ' . implode(' AND ', $str2) . ' )'; - } elseif (strpos($field, '|')) { - // 不同字段使用相同查询条件(OR) - $array = explode('|', $field); - $item = []; - - foreach ($array as $k) { - $item[] = $this->parseWhereItem($query, $k, $value, '', $binds); - } - - $str[] = ' ' . $logic . ' ( ' . implode(' OR ', $item) . ' )'; - } elseif (strpos($field, '&')) { - // 不同字段使用相同查询条件(AND) - $array = explode('&', $field); - $item = []; - - foreach ($array as $k) { - $item[] = $this->parseWhereItem($query, $k, $value, '', $binds); - } - - $str[] = ' ' . $logic . ' ( ' . implode(' AND ', $item) . ' )'; - } else { - // 对字段使用表达式查询 - $field = is_string($field) ? $field : ''; - $str[] = ' ' . $logic . ' ' . $this->parseWhereItem($query, $field, $value, $logic, $binds); - } + if ($value instanceof \Closure) { + // 使用闭包查询 + $where[] = $this->parseClousreWhere($query, $value, $logic); + } elseif (is_array($field)) { + $where[] = $this->parseMultiWhereField($query, $value, $field, $logic, $binds); + } elseif ($field instanceof Raw) { + $where[] = ' ' . $logic . ' ' . $this->parseWhereItem($query, $field, $value, $binds); + } elseif (strpos($field, '|')) { + $where[] = $this->parseFieldsOr($query, $value, $field, $logic, $binds); + } elseif (strpos($field, '&')) { + $where[] = $this->parseFieldsAnd($query, $value, $field, $logic, $binds); + } else { + // 对字段使用表达式查询 + $field = is_string($field) ? $field : ''; + $where[] = ' ' . $logic . ' ' . $this->parseWhereItem($query, $field, $value, $binds); } + } - $whereStr .= empty($whereStr) ? substr(implode(' ', $str), strlen($logic) + 1) : implode(' ', $str); + return $where; + } + + /** + * 不同字段使用相同查询条件(AND) + * @access protected + * @param Query $query 查询对象 + * @param mixed $value 查询条件 + * @param string $field 查询字段 + * @param string $logic Logic + * @param array $binds 参数绑定 + * @return string + */ + protected function parseFieldsAnd(Query $query, $value, string $field, string $logic, array $binds): string + { + $item = []; + + foreach (explode('&', $field) as $k) { + $item[] = $this->parseWhereItem($query, $k, $value, $binds); } - return $whereStr; + return ' ' . $logic . ' ( ' . implode(' AND ', $item) . ' )'; } - // where子单元分析 - protected function parseWhereItem(Query $query, $field, $val, $rule = '', array $binds = []): string + /** + * 不同字段使用相同查询条件(OR) + * @access protected + * @param Query $query 查询对象 + * @param mixed $value 查询条件 + * @param string $field 查询字段 + * @param string $logic Logic + * @param array $binds 参数绑定 + * @return string + */ + protected function parseFieldsOr(Query $query, $value, string $field, string $logic, array $binds): string { - // 字段分析 - $key = $field ? $this->parseKey($query, $field, true) : ''; + $item = []; - // 查询规则和条件 - if (!is_array($val)) { - $val = is_null($val) ? ['NULL', ''] : ['=', $val]; + foreach (explode('|', $field) as $k) { + $item[] = $this->parseWhereItem($query, $k, $value, $binds); } - list($exp, $value) = $val; + return ' ' . $logic . ' ( ' . implode(' OR ', $item) . ' )'; + } + + /** + * 闭包查询 + * @access protected + * @param Query $query 查询对象 + * @param Closure $value 查询条件 + * @param string $logic Logic + * @return string + */ + protected function parseClousreWhere(Query $query, Closure $value, string $logic): string + { + $newQuery = $query->newQuery()->setConnection($this->connection); + $value($newQuery); + $whereClause = $this->buildWhere($query, $newQuery->getOptions('where') ?: []); - // 对一个字段使用多个查询条件 - if (is_array($exp)) { - $item = array_pop($val); + if (!empty($whereClause)) { + $where = ' ' . $logic . ' ( ' . $whereClause . ' )'; + } - // 传入 or 或者 and - if (is_string($item) && in_array($item, ['AND', 'and', 'OR', 'or'])) { - $rule = $item; - } else { - array_push($val, $item); - } + return $where ?? ''; + } - foreach ($val as $k => $item) { - $str[] = $this->parseWhereItem($query, $field, $item, $rule, $binds); - } + /** + * 复合条件查询 + * @access protected + * @param Query $query 查询对象 + * @param mixed $value 查询条件 + * @param mixed $field 查询字段 + * @param string $logic Logic + * @param array $binds 参数绑定 + * @return string + */ + protected function parseMultiWhereField(Query $query, $value, $field, string $logic, array $binds): string + { + array_unshift($value, $field); - return '( ' . implode(' ' . $rule . ' ', $str) . ' )'; + $where = []; + foreach ($value as $item) { + $where[] = $this->parseWhereItem($query, array_shift($item), $item, $binds); } + return ' ' . $logic . ' ( ' . implode(' AND ', $where) . ' )'; + } + + /** + * where子单元分析 + * @access protected + * @param Query $query 查询对象 + * @param mixed $field 查询字段 + * @param array $val 查询条件 + * @param array $binds 参数绑定 + * @return string + */ + protected function parseWhereItem(Query $query, $field, array $val, array $binds = []): string + { + // 字段分析 + $key = $field ? $this->parseKey($query, $field, true) : ''; + + list($exp, $value) = $val; + // 检测操作符 + if (!is_string($exp)) { + throw new Exception('where express error:' . var_export($exp, true)); + } + $exp = strtoupper($exp); if (isset($this->exp[$exp])) { $exp = $this->exp[$exp]; } - if ($value instanceof Expression) { + if (is_string($field)) { + $bindType = $binds[$field] ?? PDO::PARAM_STR; + } else { + $bindType = PDO::PARAM_STR; + } + + if ($value instanceof Raw) { } elseif (is_object($value) && method_exists($value, '__toString')) { // 对象数据写入 $value = $value->__toString(); } - $bindType = $binds[$field] ?? PDO::PARAM_STR; - if (is_scalar($value) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) { if (is_string($value) && 0 === strpos($value, ':') && $query->isBind(substr($value, 1))) { } else { - $name = $query->bind($value, $bindType); + $name = $query->bindValue($value, $bindType); $value = ':' . $name; } } @@ -411,40 +519,36 @@ abstract class Builder // 解析查询表达式 foreach ($this->parser as $fun => $parse) { if (in_array($exp, $parse)) { - $whereStr = $this->$fun($query, $key, $exp, $value, $field, $bindType, $val[2] ?? 'AND'); - break; + return $this->$fun($query, $key, $exp, $value, $field, $bindType, $val[2] ?? 'AND'); } } - if (!isset($whereStr)) { - throw new Exception('where express error:' . $exp); - } - - return $whereStr; + throw new Exception('where express error:' . $exp); } /** * 模糊查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType - * @param string $logic + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param array $value + * @param string $field + * @param integer $bindType + * @param string $logic * @return string */ - protected function parseLike(Query $query, string $key, string $exp, $value, string $field, int $bindType, string $logic): string + protected function parseLike(Query $query, string $key, string $exp, $value, $field, int $bindType, string $logic): string { // 模糊匹配 if (is_array($value)) { + $array = []; foreach ($value as $item) { - $name = $query->bind($item, $bindType); + $name = $query->bindValue($item, $bindType); $array[] = $key . ' ' . $exp . ' :' . $name; } - $whereStr = '(' . implode($array, ' ' . strtoupper($logic) . ' ') . ')'; + $whereStr = '(' . implode(' ' . strtoupper($logic) . ' ', $array) . ')'; } else { $whereStr = $key . ' ' . $exp . ' ' . $value; } @@ -455,15 +559,15 @@ abstract class Builder /** * 表达式查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param Expression $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param array $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseExp(Query $query, string $key, string $exp, Expression $value, string $field, int $bindType): string + protected function parseExp(Query $query, string $key, string $exp, Raw $value, string $field, int $bindType): string { // 表达式查询 return '( ' . $key . ' ' . $value->getValue() . ' )'; @@ -472,12 +576,12 @@ abstract class Builder /** * 表达式查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param array $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param array $value + * @param string $field + * @param integer $bindType * @return string */ protected function parseColumn(Query $query, string $key, $exp, array $value, string $field, int $bindType): string @@ -495,15 +599,15 @@ abstract class Builder /** * Null查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseNull(Query $query, string $key, string $exp, $value, string $field, int $bindType): string + protected function parseNull(Query $query, string $key, string $exp, $value, $field, int $bindType): string { // NULL 查询 return $key . ' IS ' . $exp; @@ -512,21 +616,21 @@ abstract class Builder /** * 范围查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseBetween(Query $query, string $key, string $exp, $value, string $field, int $bindType): string + protected function parseBetween(Query $query, string $key, string $exp, $value, $field, int $bindType): string { // BETWEEN 查询 $data = is_array($value) ? $value : explode(',', $value); - $min = $query->bind($data[0], $bindType); - $max = $query->bind($data[1], $bindType); + $min = $query->bindValue($data[0], $bindType); + $max = $query->bindValue($data[1], $bindType); return $key . ' ' . $exp . ' :' . $min . ' AND :' . $max . ' '; } @@ -534,12 +638,12 @@ abstract class Builder /** * Exists查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ protected function parseExists(Query $query, string $key, string $exp, $value, string $field, int $bindType): string @@ -547,27 +651,27 @@ abstract class Builder // EXISTS 查询 if ($value instanceof \Closure) { $value = $this->parseClosure($query, $value, false); - } elseif ($value instanceof Expression) { + } elseif ($value instanceof Raw) { $value = $value->getValue(); } else { throw new Exception('where express error:' . $value); } - return $exp . ' (' . $value . ')'; + return $exp . ' ( ' . $value . ' )'; } /** * 时间比较查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseTime(Query $query, string $key, string $exp, $value, string $field, int $bindType): string + protected function parseTime(Query $query, string $key, string $exp, $value, $field, int $bindType): string { return $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($query, $value, $field, $bindType); } @@ -575,15 +679,15 @@ abstract class Builder /** * 大小比较查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseCompare(Query $query, string $key, string $exp, $value, string $field, int $bindType): string + protected function parseCompare(Query $query, string $key, string $exp, $value, $field, int $bindType): string { if (is_array($value)) { throw new Exception('where express error:' . $exp . var_export($value, true)); @@ -600,15 +704,15 @@ abstract class Builder /** * 时间范围查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseBetweenTime(Query $query, string $key, string $exp, $value, string $field, int $bindType): string + protected function parseBetweenTime(Query $query, string $key, string $exp, $value, $field, int $bindType): string { if (is_string($value)) { $value = explode(',', $value); @@ -624,34 +728,31 @@ abstract class Builder /** * IN查询 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param string $exp - * @param mixed $value - * @param string $field - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType * @return string */ - protected function parseIn(Query $query, string $key, string $exp, $value, string $field, int $bindType): string + protected function parseIn(Query $query, string $key, string $exp, $value, $field, int $bindType): string { // IN 查询 if ($value instanceof \Closure) { $value = $this->parseClosure($query, $value, false); - } elseif ($value instanceof Expression) { + } elseif ($value instanceof Raw) { $value = $value->getValue(); } else { $value = array_unique(is_array($value) ? $value : explode(',', $value)); - $array = []; foreach ($value as $v) { - - $name = $query->bind($v, $bindType); + $name = $query->bindValue($v, $bindType); $array[] = ':' . $name; } - $zone = implode(',', $array); - + $zone = implode(',', $array); $value = empty($zone) ? "''" : $zone; } @@ -661,9 +762,9 @@ abstract class Builder /** * 闭包子查询 * @access protected - * @param Query $query 查询对象 - * @param \Closure $call - * @param bool $show + * @param Query $query 查询对象 + * @param \Closure $call + * @param bool $show * @return string */ protected function parseClosure(Query $query, \Closure $call, bool $show = true): string @@ -677,10 +778,10 @@ abstract class Builder /** * 日期时间条件解析 * @access protected - * @param Query $query 查询对象 - * @param mixed $value - * @param string $key - * @param integer $bindType + * @param Query $query 查询对象 + * @param mixed $value + * @param string $key + * @param integer $bindType * @return string */ protected function parseDateTime(Query $query, $value, string $key, int $bindType): string @@ -702,23 +803,22 @@ abstract class Builder if (isset($type[$key])) { $info = $type[$key]; - } - - if (isset($info)) { if (is_string($value)) { $value = strtotime($value) ?: $value; } - if (preg_match('/(datetime|timestamp)/is', $info)) { - // 日期及时间戳类型 - $value = date('Y-m-d H:i:s', $value); - } elseif (preg_match('/(date)/is', $info)) { - // 日期及时间戳类型 - $value = date('Y-m-d', $value); + if (is_int($value)) { + if (preg_match('/(datetime|timestamp)/is', $info)) { + // 日期及时间戳类型 + $value = date('Y-m-d H:i:s', $value); + } elseif (preg_match('/(date)/is', $info)) { + // 日期及时间戳类型 + $value = date('Y-m-d', $value); + } } } - $name = $query->bind($value, $bindType); + $name = $query->bindValue($value, $bindType); return ':' . $name; } @@ -726,8 +826,8 @@ abstract class Builder /** * limit分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $limit + * @param Query $query 查询对象 + * @param mixed $limit * @return string */ protected function parseLimit(Query $query, string $limit): string @@ -738,8 +838,8 @@ abstract class Builder /** * join分析 * @access protected - * @param Query $query 查询对象 - * @param array $join + * @param Query $query 查询对象 + * @param array $join * @return string */ protected function parseJoin(Query $query, array $join): string @@ -768,14 +868,15 @@ abstract class Builder /** * order分析 * @access protected - * @param Query $query 查询对象 - * @param array $order + * @param Query $query 查询对象 + * @param array $order * @return string */ protected function parseOrder(Query $query, array $order): string { + $array = []; foreach ($order as $key => $val) { - if ($val instanceof Expression) { + if ($val instanceof Raw) { $array[] = $val->getValue(); } elseif (is_array($val) && preg_match('/^[\w\.]+$/', $key)) { $array[] = $this->parseOrderField($query, $key, $val); @@ -801,12 +902,23 @@ abstract class Builder return empty($array) ? '' : ' ORDER BY ' . implode(',', $array); } + /** + * 随机排序 + * @access protected + * @param Query $query 查询对象 + * @return string + */ + protected function parseRand(Query $query): string + { + return ''; + } + /** * orderField分析 * @access protected - * @param Query $query 查询对象 - * @param string $key - * @param array $val + * @param Query $query 查询对象 + * @param string $key + * @param array $val * @return string */ protected function parseOrderField(Query $query, string $key, array $val): string @@ -820,8 +932,7 @@ abstract class Builder $sort = strtoupper($sort); $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : ''; - - $bind = $query->getFieldBindType(); + $bind = $query->getFieldsBindType(); foreach ($val as $item) { $val[] = $this->parseDataBind($query, $key, $item, $bind); @@ -833,8 +944,8 @@ abstract class Builder /** * group分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $group + * @param Query $query 查询对象 + * @param mixed $group * @return string */ protected function parseGroup(Query $query, $group): string @@ -847,6 +958,7 @@ abstract class Builder $group = explode(',', $group); } + $val = []; foreach ($group as $key) { $val[] = $this->parseKey($query, $key); } @@ -857,7 +969,7 @@ abstract class Builder /** * having分析 * @access protected - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @param string $having * @return string */ @@ -869,7 +981,7 @@ abstract class Builder /** * comment分析 * @access protected - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @param string $comment * @return string */ @@ -885,8 +997,8 @@ abstract class Builder /** * distinct分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $distinct + * @param Query $query 查询对象 + * @param mixed $distinct * @return string */ protected function parseDistinct(Query $query, bool $distinct): string @@ -897,8 +1009,8 @@ abstract class Builder /** * union分析 * @access protected - * @param Query $query 查询对象 - * @param array $union + * @param Query $query 查询对象 + * @param array $union * @return string */ protected function parseUnion(Query $query, array $union): string @@ -924,8 +1036,8 @@ abstract class Builder /** * index分析,可在操作链中指定需要强制使用的索引 * @access protected - * @param Query $query 查询对象 - * @param mixed $index + * @param Query $query 查询对象 + * @param mixed $index * @return string */ protected function parseForce(Query $query, $index): string @@ -944,8 +1056,8 @@ abstract class Builder /** * 设置锁机制 * @access protected - * @param Query $query 查询对象 - * @param bool|string $lock + * @param Query $query 查询对象 + * @param bool|string $lock * @return string */ protected function parseLock(Query $query, $lock = false): string @@ -956,14 +1068,16 @@ abstract class Builder if (is_string($lock) && !empty($lock)) { return ' ' . trim($lock) . ' '; + } else { + return ''; } } /** * 生成查询SQL * @access public - * @param Query $query 查询对象 - * @param bool $one 是否仅获取一个记录 + * @param Query $query 查询对象 + * @param bool $one 是否仅获取一个记录 * @return string */ public function select(Query $query, bool $one = false): string @@ -971,10 +1085,11 @@ abstract class Builder $options = $query->getOptions(); return str_replace( - ['%TABLE%', '%DISTINCT%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'], + ['%TABLE%', '%DISTINCT%', '%EXTRA%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'], [ $this->parseTable($query, $options['table']), $this->parseDistinct($query, $options['distinct']), + $this->parseExtra($query, $options['extra']), $this->parseField($query, $options['field']), $this->parseJoin($query, $options['join']), $this->parseWhere($query, $options['where']), @@ -993,11 +1108,10 @@ abstract class Builder /** * 生成Insert SQL * @access public - * @param Query $query 查询对象 - * @param bool $replace 是否replace + * @param Query $query 查询对象 * @return string */ - public function insert(Query $query, bool $replace = false): string + public function insert(Query $query): string { $options = $query->getOptions(); @@ -1011,10 +1125,11 @@ abstract class Builder $values = array_values($data); return str_replace( - ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], + ['%INSERT%', '%TABLE%', '%EXTRA%', '%FIELD%', '%DATA%', '%COMMENT%'], [ - $replace ? 'REPLACE' : 'INSERT', + !empty($options['replace']) ? 'REPLACE' : 'INSERT', $this->parseTable($query, $options['table']), + $this->parseExtra($query, $options['extra']), implode(' , ', $fields), implode(' , ', $values), $this->parseComment($query, $options['comment']), @@ -1025,12 +1140,11 @@ abstract class Builder /** * 生成insertall SQL * @access public - * @param Query $query 查询对象 - * @param array $dataSet 数据集 - * @param bool $replace 是否replace + * @param Query $query 查询对象 + * @param array $dataSet 数据集 * @return string */ - public function insertAll(Query $query, array $dataSet, bool $replace = false): string + public function insertAll(Query $query, array $dataSet): string { $options = $query->getOptions(); @@ -1042,10 +1156,12 @@ abstract class Builder } // 获取绑定信息 - $bind = $query->getFieldBindType(); + $bind = $query->getFieldsBindType(); + $fields = []; + $values = []; foreach ($dataSet as $k => $data) { - $data = $this->parseData($query, $data, $allowFields, $bind, '_' . $k); + $data = $this->parseData($query, $data, $allowFields, $bind); $values[] = 'SELECT ' . implode(',', array_values($data)); @@ -1054,17 +1170,16 @@ abstract class Builder } } - $fields = []; - foreach ($insertFields as $field) { $fields[] = $this->parseKey($query, $field); } return str_replace( - ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], + ['%INSERT%', '%TABLE%', '%EXTRA%', '%FIELD%', '%DATA%', '%COMMENT%'], [ - $replace ? 'REPLACE' : 'INSERT', + !empty($options['replace']) ? 'REPLACE' : 'INSERT', $this->parseTable($query, $options['table']), + $this->parseExtra($query, $options['extra']), implode(' , ', $fields), implode(' UNION ALL ', $values), $this->parseComment($query, $options['comment']), @@ -1075,9 +1190,9 @@ abstract class Builder /** * 生成slect insert SQL * @access public - * @param Query $query 查询对象 - * @param array $fields 数据 - * @param string $table 数据表 + * @param Query $query 查询对象 + * @param array $fields 数据 + * @param string $table 数据表 * @return string */ public function selectInsert(Query $query, array $fields, string $table): string @@ -1092,28 +1207,29 @@ abstract class Builder /** * 生成update SQL * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return string */ public function update(Query $query): string { $options = $query->getOptions(); - $table = $this->parseTable($query, $options['table']); - $data = $this->parseData($query, $options['data']); + $data = $this->parseData($query, $options['data']); if (empty($data)) { return ''; } + $set = []; foreach ($data as $key => $val) { $set[] = $key . ' = ' . $val; } return str_replace( - ['%TABLE%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'], + ['%TABLE%', '%EXTRA%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'], [ $this->parseTable($query, $options['table']), + $this->parseExtra($query, $options['extra']), implode(' , ', $set), $this->parseJoin($query, $options['join']), $this->parseWhere($query, $options['where']), @@ -1128,7 +1244,7 @@ abstract class Builder /** * 生成delete SQL * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return string */ public function delete(Query $query): string @@ -1136,9 +1252,10 @@ abstract class Builder $options = $query->getOptions(); return str_replace( - ['%TABLE%', '%USING%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'], + ['%TABLE%', '%EXTRA%', '%USING%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'], [ $this->parseTable($query, $options['table']), + $this->parseExtra($query, $options['extra']), !empty($options['using']) ? ' USING ' . $this->parseTable($query, $options['using']) . ' ' : '', $this->parseJoin($query, $options['join']), $this->parseWhere($query, $options['where']), diff --git a/src/db/Connection.php b/src/db/Connection.php index cc42c9e..ec6b508 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -12,68 +12,124 @@ declare (strict_types = 1); namespace think\db; -use InvalidArgumentException; use PDO; use PDOStatement; +use think\Cache; use think\cache\CacheItem; +use think\Container; use think\Db; -use think\Debug; +use think\db\exception\BindParamException; use think\Exception; -use think\exception\BindParamException; use think\exception\PDOException; +use think\Log; +/** + * 数据库连接基础类 + */ abstract class Connection { const PARAM_FLOAT = 21; - protected static $instance = []; - /** @var PDOStatement PDO操作实例 */ + /** + * PDO操作实例 + * @var PDOStatement + */ protected $PDOStatement; - /** @var string 当前SQL指令 */ + /** + * 当前SQL指令 + * @var string + */ protected $queryStr = ''; - // 返回或者影响记录数 + + /** + * 返回或者影响记录数 + * @var int + */ protected $numRows = 0; - // 事务指令数 - protected $transTimes = 0; - // 错误信息 - protected $error = ''; /** - * 查询次数 - * @var integer + * 事务指令数 + * @var int */ - protected static $queryTimes = 0; + protected $transTimes = 0; - protected $queryStartTime; + /** + * 错误信息 + * @var string + */ + protected $error = ''; - /** @var PDO[] 数据库连接ID 支持多个连接 */ + /** + * 数据库连接ID 支持多个连接 + * @var PDO[] + */ protected $links = []; - /** @var PDO 当前连接ID */ + /** + * 当前连接ID + * @var PDO + */ protected $linkID; + + /** + * 当前读连接ID + * @var PDO + */ protected $linkRead; + + /** + * 当前写连接ID + * @var PDO + */ protected $linkWrite; - // 当前缓存对象 - protected $cache; - // 查询结果类型 + + /** + * 查询结果类型 + * @var int + */ protected $fetchType = PDO::FETCH_ASSOC; - // 字段属性大小写 + + /** + * 字段属性大小写 + * @var int + */ protected $attrCase = PDO::CASE_LOWER; - // 监听回调 - protected static $event = []; - // 数据表信息 - protected static $info = []; + /** + * 数据表信息 + * @var array + */ + protected $info = []; - // 数据表信息 - protected static $log = []; + /** + * 查询开始时间 + * @var float + */ + protected $queryStartTime; - // 使用Builder类 - protected $builderClassName; - // Builder对象 + /** + * Builder对象 + * @var Builder + */ protected $builder; - // 数据库连接参数配置 + + /** + * Db对象 + * @var Db + */ + protected $db; + + /** + * 是否读取主库 + * @var bool + */ + protected $readMaster = false; + + /** + * 数据库连接参数配置 + * @var array + */ protected $config = [ // 数据库类型 'type' => '', @@ -118,16 +174,17 @@ abstract class Connection // Builder类 'builder' => '', // Query类 - 'query' => '\\think\\db\\Query', + 'query' => '', // 是否需要断线重连 'break_reconnect' => false, // 断线标识字符串 'break_match_str' => [], - 'schema_path' => '', - 'class_suffix' => false, ]; - // PDO连接参数 + /** + * PDO连接参数 + * @var array + */ protected $params = [ PDO::ATTR_CASE => PDO::CASE_NATURAL, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, @@ -136,7 +193,10 @@ abstract class Connection PDO::ATTR_EMULATE_PREPARES => false, ]; - // 参数绑定类型映射 + /** + * 参数绑定类型映射 + * @var array + */ protected $bindType = [ 'string' => PDO::PARAM_STR, 'str' => PDO::PARAM_STR, @@ -147,7 +207,10 @@ abstract class Connection 'float' => self::PARAM_FLOAT, ]; - // 服务器断线标识字符 + /** + * 服务器断线标识字符 + * @var array + */ protected $breakMatchStr = [ 'server has gone away', 'no connection to the server', @@ -162,15 +225,25 @@ abstract class Connection 'failed with errno', ]; - // 绑定参数 + /** + * 绑定参数 + * @var array + */ protected $bind = []; + /** + * 缓存对象 + * @var Cache + */ + protected $cache; + /** * 架构函数 读取数据库配置信息 * @access public - * @param array $config 数据库配置数组 + * @param Cache $cache 缓存对象 + * @param array $config 数据库配置数组 */ - public function __construct(array $config = []) + public function __construct(Cache $cache, array $config = []) { if (!empty($config)) { $this->config = array_merge($this->config, $config); @@ -180,7 +253,8 @@ abstract class Connection $class = $this->getBuilderClass(); $this->builder = new $class($this); - $this->cache = Db::getCacheHandler(); + $this->cache = $cache; + // 执行初始化操作 $this->initialize(); } @@ -191,37 +265,17 @@ abstract class Connection * @return void */ protected function initialize(): void - {} + { + } /** - * 取得数据库连接类实例 + * 获取当前连接器类对应的Query类 * @access public - * @param mixed $config 连接配置 - * @param bool|string $name 连接标识 true 强制重新连接 - * @return Connection - * @throws Exception + * @return string */ - public static function instance($config = [], $name = false): Connection + public function getQueryClass(): string { - if (false === $name) { - $name = md5(serialize($config)); - } - - if (true === $name || !isset(self::$instance[$name])) { - - if (empty($config['type'])) { - throw new InvalidArgumentException('Undefined db type'); - } - - if (true === $name) { - $name = md5(serialize($config)); - } - $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\db\\connector\\' . ucwords($config['type']); - - self::$instance[$name] = new $class($config); - } - - return self::$instance[$name]; + return $this->getConfig('query') ?: Query::class; } /** @@ -231,24 +285,18 @@ abstract class Connection */ public function getBuilderClass(): string { - if (!empty($this->builderClassName)) { - return $this->builderClassName; - } - return $this->getConfig('builder') ?: '\\think\\db\\builder\\' . ucfirst($this->getConfig('type')); } /** * 设置当前的数据库Builder对象 * @access protected - * @param Builder $builder + * @param Builder $builder * @return void */ - protected function setBuilder(Builder $builder) + protected function setBuilder(Builder $builder): void { $this->builder = $builder; - - return $this; } /** @@ -261,10 +309,21 @@ abstract class Connection return $this->builder; } + /** + * 设置当前的数据库Db对象 + * @access public + * @param Db $db + * @return void + */ + public function setDb(Db $db): void + { + $this->db = $db; + } + /** * 解析pdo连接的dsn信息 * @access protected - * @param array $config 连接信息 + * @param array $config 连接信息 * @return string */ abstract protected function parseDsn(array $config); @@ -272,7 +331,7 @@ abstract class Connection /** * 取得数据表的字段信息 * @access public - * @param string $tableName + * @param string $tableName 数据表名称 * @return array */ abstract public function getFields(string $tableName); @@ -280,7 +339,7 @@ abstract class Connection /** * 取得数据库的表信息 * @access public - * @param string $dbName + * @param string $dbName 数据库名称 * @return array */ abstract public function getTables(string $dbName); @@ -288,7 +347,7 @@ abstract class Connection /** * SQL性能分析 * @access protected - * @param string $sql + * @param string $sql SQL语句 * @return array */ abstract protected function getExplain(string $sql); @@ -296,7 +355,7 @@ abstract class Connection /** * 对返数据表字段信息进行大小写转换出来 * @access public - * @param array $info 字段信息 + * @param array $info 字段信息 * @return array */ public function fieldCase(array $info): array @@ -320,7 +379,7 @@ abstract class Connection /** * 获取字段绑定类型 * @access public - * @param string $type 字段类型 + * @param string $type 字段类型 * @return integer */ public function getFieldBindType(string $type): int @@ -345,8 +404,8 @@ abstract class Connection /** * 获取数据表信息 * @access public - * @param mixed $tableName 数据表名 留空自动获取 - * @param string $fetch 获取信息类型 包括 fields type bind pk + * @param mixed $tableName 数据表名 留空自动获取 + * @param string $fetch 获取信息类型 包括 fields type bind pk * @return mixed */ public function getTableInfo($tableName, string $fetch = '') @@ -357,7 +416,7 @@ abstract class Connection if (strpos($tableName, ',')) { // 多表不获取字段信息 - return false; + return []; } // 修正子查询作为表名的问题 @@ -373,9 +432,9 @@ abstract class Connection $schema = $tableName; } - if (!isset(self::$info[$schema])) { + if (!isset($this->info[$schema])) { // 读取缓存 - $cacheFile = $this->config['schema_path'] . $schema . '.php'; + $cacheFile = Container::pull('app')->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR . $schema . '.php'; if (!$this->config['debug'] && is_file($cacheFile)) { $info = include $cacheFile; @@ -403,16 +462,16 @@ abstract class Connection $pk = null; } - self::$info[$schema] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk]; + $this->info[$schema] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk]; } - return $fetch ? self::$info[$schema][$fetch] : self::$info[$schema]; + return $fetch ? $this->info[$schema][$fetch] : $this->info[$schema]; } /** * 获取数据表的主键 * @access public - * @param string $tableName 数据表名 + * @param mixed $tableName 数据表名 * @return string|array */ public function getPk($tableName) @@ -423,7 +482,7 @@ abstract class Connection /** * 获取数据表字段信息 * @access public - * @param string $tableName 数据表名 + * @param mixed $tableName 数据表名 * @return array */ public function getTableFields($tableName): array @@ -434,8 +493,8 @@ abstract class Connection /** * 获取数据表字段类型 * @access public - * @param string $tableName 数据表名 - * @param string $field 字段名 + * @param mixed $tableName 数据表名 + * @param string $field 字段名 * @return array|string */ public function getFieldsType($tableName, string $field = null) @@ -452,7 +511,7 @@ abstract class Connection /** * 获取数据表绑定信息 * @access public - * @param string $tableName 数据表名 + * @param mixed $tableName 数据表名 * @return array */ public function getFieldsBind($tableName): array @@ -463,21 +522,24 @@ abstract class Connection /** * 获取数据库的配置参数 * @access public - * @param string $config 配置名称 + * @param string $config 配置名称 * @return mixed */ public function getConfig(string $config = '') { - return $config ? ($this->config[$config] ?? null) : $this->config; + if ('' === $config) { + return $this->config; + } + return $this->config[$config] ?? null; } /** * 设置数据库的配置参数 * @access public - * @param array $config 配置 + * @param array $config 配置 * @return void */ - public function setConfig($config): void + public function setConfig(array $config): void { $this->config = array_merge($this->config, $config); } @@ -485,9 +547,9 @@ abstract class Connection /** * 连接数据库方法 * @access public - * @param array $config 连接参数 - * @param integer $linkNum 连接序号 - * @param array|bool $autoConnection 是否自动连接主数据库(用于分布式) + * @param array $config 连接参数 + * @param integer $linkNum 连接序号 + * @param array|bool $autoConnection 是否自动连接主数据库(用于分布式) * @return PDO * @throws Exception */ @@ -497,7 +559,7 @@ abstract class Connection return $this->links[$linkNum]; } - if (!$config) { + if (empty($config)) { $config = $this->config; } else { $config = array_merge($this->config, $config); @@ -523,20 +585,18 @@ abstract class Connection } if ($config['debug']) { - $startTime = microtime(true); - } - - $this->links[$linkNum] = new PDO($config['dsn'], $config['username'], $config['password'], $params); - - if ($config['debug']) { + $startTime = microtime(true); + $this->links[$linkNum] = $this->createPdo($config['dsn'], $config['username'], $config['password'], $params); // 记录数据库连接信息 $this->log('[ DB ] CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); + } else { + $this->links[$linkNum] = $this->createPdo($config['dsn'], $config['username'], $config['password'], $params); } return $this->links[$linkNum]; } catch (\PDOException $e) { if ($autoConnection) { - $this->log($e->getMessage(), 'error'); + $this->log->error($e->getMessage()); return $this->connect($autoConnection, $linkNum); } else { throw $e; @@ -544,6 +604,19 @@ abstract class Connection } } + /** + * 创建PDO实例 + * @param $dsn + * @param $username + * @param $password + * @param $params + * @return PDO + */ + protected function createPdo($dsn, $username, $password, $params) + { + return new PDO($dsn, $username, $password, $params); + } + /** * 释放查询结果 * @access public @@ -570,11 +643,11 @@ abstract class Connection /** * 执行查询 使用生成器返回数据 * @access public - * @param Query $query 查询对象 - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param Model $model 模型对象实例 - * @param array $condition 查询条件 + * @param Query $query 查询对象 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param \think\Model $model 模型对象实例 + * @param array $condition 查询条件 * @return \Generator */ public function getCursor(Query $query, string $sql, array $bind = [], $model = null, $condition = null) @@ -584,7 +657,7 @@ abstract class Connection // 返回结果集 while ($result = $this->PDOStatement->fetch($this->fetchType)) { if ($model) { - yield $model->newInstance($result, $condition); + yield $model->newInstance($result, $condition)->setQuery($query); } else { yield $result; } @@ -594,10 +667,10 @@ abstract class Connection /** * 执行查询 返回数据集 * @access public - * @param Query $query 查询对象 - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param bool $cache 是否支持缓存 + * @param Query $query 查询对象 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param bool $cache 是否支持缓存 * @return array * @throws BindParamException * @throws \PDOException @@ -610,7 +683,7 @@ abstract class Connection $options = $query->parseOptions(); if ($cache && !empty($options['cache'])) { - $cacheItem = $options['cache']; + $cacheItem = $this->parseCache($query, $options['cache']); $resultSet = $this->cache->get($cacheItem->getKey()); if (false !== $resultSet) { @@ -637,10 +710,10 @@ abstract class Connection /** * 执行查询但只返回PDOStatement对象 * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return \PDOStatement */ - public function pdo(Query $query) + public function pdo(Query $query): PDOStatement { $bind = $query->getBind(); // 生成查询SQL @@ -652,10 +725,10 @@ abstract class Connection /** * 执行查询但只返回PDOStatement对象 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param bool $master 是否在主服务器读操作 - * @param bool $procedure 是否为存储过程调用 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param bool $master 是否在主服务器读操作 + * @param bool $procedure 是否为存储过程调用 * @return PDOStatement * @throws BindParamException * @throws \PDOException @@ -664,14 +737,14 @@ abstract class Connection */ public function getPDOStatement(string $sql, array $bind = [], bool $master = false, bool $procedure = false): PDOStatement { - $this->initConnect($master); + $this->initConnect($this->readMaster ?: $master); // 记录SQL语句 $this->queryStr = $sql; $this->bind = $bind; - self::$queryTimes++; + $this->db->updateQueryTimes(); try { // 调试开始 @@ -710,25 +783,30 @@ abstract class Connection /** * 执行语句 * @access public - * @param Query $query 查询对象 - * @param string $sql sql指令 - * @param array $bind 参数绑定 + * @param Query $query 查询对象 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param bool $origin 是否原生查询 * @return int * @throws BindParamException * @throws \PDOException * @throws \Exception * @throws \Throwable */ - public function execute(Query $query, string $sql, array $bind = []): int + public function execute(Query $query, string $sql, array $bind = [], bool $origin = false): int { $this->queryPDOStatement($query->master(true), $sql, $bind); + if (!$origin && !empty($this->config['deploy']) && !empty($this->config['read_master'])) { + $this->readMaster = true; + } + $this->numRows = $this->PDOStatement->rowCount(); return $this->numRows; } - protected function queryPDOStatement(Query $query, string $sql, array $bind = []) + protected function queryPDOStatement(Query $query, string $sql, array $bind = []): PDOStatement { $options = $query->parseOptions(); $master = !empty($options['master']) ? true : false; @@ -740,45 +818,25 @@ abstract class Connection /** * 查找单条记录 * @access public - * @param Query $query 查询对象 - * @return array|null + * @param Query $query 查询对象 + * @return array * @throws DbException * @throws ModelNotFoundException * @throws DataNotFoundException */ - public function find(Query $query) + public function find(Query $query): array { // 分析查询表达式 $options = $query->parseOptions(); - $pk = $query->getPk($options); - $data = $options['data']; - if ($this->cache && !empty($options['cache'])) { + if (!empty($options['cache'])) { // 判断查询缓存 - $cacheItem = $options['cache']; - - if (!$cacheItem->getKey()) { - $key = $this->getCacheKey($query, $data); - $cacheItem->setKey($key); - } - - $options['key'] = $cacheItem->getKey(); - } - - if (is_string($pk) && !is_array($data)) { - if (isset($key) && strpos($key, '|')) { - list($a, $val) = explode('|', $key); - $item[$pk] = $val; - } else { - $item[$pk] = $data; - } - $data = $item; + $cacheItem = $this->parseCache($query, $options['cache']); + $key = $cacheItem->getKey(); } - $query->setOption('data', $data); - - if ($this->cache && isset($options['key'])) { - $result = $this->cache->get($options['key']); + if (isset($key)) { + $result = $this->cache->get($key); if (false !== $result) { return $result; @@ -789,16 +847,16 @@ abstract class Connection $sql = $this->builder->select($query, true); // 事件回调 - $result = $query->trigger('before_find'); + $result = $this->db->trigger('before_find', $query); if (!$result) { // 执行查询 $resultSet = $this->query($query, $sql, $query->getBind()); - $result = $resultSet[0] ?? null; + $result = $resultSet[0] ?? []; } - if ($this->cache && isset($cacheItem) && $result) { + if (isset($cacheItem) && $result) { // 缓存数据 $cacheItem->set($result); $this->cacheData($cacheItem); @@ -810,7 +868,7 @@ abstract class Connection /** * 使用游标查询记录 * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return \Generator */ public function cursor(Query $query) @@ -824,13 +882,13 @@ abstract class Connection $condition = $options['where']['AND'] ?? null; // 执行查询操作 - return $this->getCursor($sql, $query->getBind(), $options['master'], $query->getModel(), $condition); + return $this->getCursor($query, $sql, $query->getBind(), $query->getModel(), $condition); } /** * 查找记录 * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return array * @throws DbException * @throws ModelNotFoundException @@ -842,7 +900,7 @@ abstract class Connection $options = $query->parseOptions(); if (!empty($options['cache'])) { - $cacheItem = $options['cache']; + $cacheItem = $this->parseCache($query, $options['cache']); $resultSet = $this->getCacheData($cacheItem); if (false !== $resultSet) { @@ -853,7 +911,7 @@ abstract class Connection // 生成查询SQL $sql = $this->builder->select($query); - $resultSet = $query->trigger('before_select'); + $resultSet = $this->db->trigger('before_select', $query); if (!$resultSet) { // 执行查询操作 @@ -872,31 +930,29 @@ abstract class Connection /** * 插入记录 * @access public - * @param Query $query 查询对象 - * @param boolean $replace 是否replace - * @param boolean $getLastInsID 返回自增主键 - * @param string $sequence 自增序列名 - * @return integer + * @param Query $query 查询对象 + * @param boolean $getLastInsID 返回自增主键 + * @return mixed */ - public function insert(Query $query, bool $replace = false, bool $getLastInsID = false, string $sequence = null) + public function insert(Query $query, bool $getLastInsID = false) { // 分析查询表达式 $options = $query->parseOptions(); // 生成SQL语句 - $sql = $this->builder->insert($query, $replace); + $sql = $this->builder->insert($query); // 执行操作 $result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind()); if ($result) { - $sequence = $sequence ?: ($options['sequence'] ?? null); + $sequence = $options['sequence'] ?? null; $lastInsId = $this->getLastInsID($sequence); $data = $options['data']; if ($lastInsId) { - $pk = $query->getPk($options); + $pk = $query->getPk(); if (is_string($pk)) { $data[$pk] = $lastInsId; } @@ -904,7 +960,7 @@ abstract class Connection $query->setOption('data', $data); - $query->trigger('after_insert'); + $this->db->trigger('after_insert', $query); if ($getLastInsID) { return $lastInsId; @@ -917,21 +973,20 @@ abstract class Connection /** * 批量插入记录 * @access public - * @param Query $query 查询对象 - * @param mixed $dataSet 数据集 - * @param bool $replace 是否replace - * @param integer $limit 每次写入数据限制 + * @param Query $query 查询对象 + * @param mixed $dataSet 数据集 + * @param integer $limit 每次写入数据限制 * @return integer * @throws \Exception * @throws \Throwable */ - public function insertAll(Query $query, array $dataSet = [], bool $replace = false, int $limit = null): int + public function insertAll(Query $query, array $dataSet = [], int $limit = 0): int { if (!is_array(reset($dataSet))) { return 0; } - $options = $query->parseOptions(); + $query->parseOptions(); if ($limit) { // 分批写入 自动启动事务支持 @@ -942,7 +997,7 @@ abstract class Connection $count = 0; foreach ($array as $item) { - $sql = $this->builder->insertAll($query, $item, $replace); + $sql = $this->builder->insertAll($query, $item); $count += $this->execute($query, $sql, $query->getBind()); } @@ -956,7 +1011,7 @@ abstract class Connection return $count; } - $sql = $this->builder->insertAll($query, $dataSet, $replace); + $sql = $this->builder->insertAll($query, $dataSet); return $this->execute($query, $sql, $query->getBind()); } @@ -964,9 +1019,9 @@ abstract class Connection /** * 通过Select方式插入记录 * @access public - * @param Query $query 查询对象 - * @param array $fields 要插入的数据表字段名 - * @param string $table 要插入的数据表名 + * @param Query $query 查询对象 + * @param array $fields 要插入的数据表字段名 + * @param string $table 要插入的数据表名 * @return integer * @throws PDOException */ @@ -981,7 +1036,7 @@ abstract class Connection /** * 更新记录 * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return integer * @throws Exception * @throws PDOException @@ -990,81 +1045,27 @@ abstract class Connection { $options = $query->parseOptions(); - if ($this->cache && isset($options['cache'])) { - $cacheItem = $options['cache']; + if (isset($options['cache'])) { + $cacheItem = $this->parseCache($query, $options['cache']); $key = $cacheItem->getKey(); } - $pk = $query->getPk($options); - $data = $options['data']; - - if (empty($options['where'])) { - // 如果存在主键数据 则自动作为更新条件 - if (is_string($pk) && isset($data[$pk])) { - $where[$pk] = [$pk, '=', $data[$pk]]; - if (!isset($key)) { - $key = $this->getCacheKey($query, $data[$pk]); - - } - unset($data[$pk]); - } elseif (is_array($pk)) { - // 增加复合主键支持 - foreach ($pk as $field) { - if (isset($data[$field])) { - $where[$field] = [$field, '=', $data[$field]]; - } else { - // 如果缺少复合主键数据则不执行 - throw new Exception('miss complex primary data'); - } - unset($data[$field]); - } - } - - if (!isset($where)) { - // 如果没有任何更新条件则不执行 - throw new Exception('miss update condition'); - } else { - $options['where']['AND'] = $where; - $query->setOption('where', ['AND' => $where]); - } - } elseif (!isset($key) && is_string($pk) && isset($options['where']['AND'])) { - foreach ($options['where']['AND'] as $val) { - if (is_array($val) && $val[0] == $pk) { - $key = $this->getCacheKey($query, $val); - } - } - } - - // 更新数据 - $query->setOption('data', $data); - // 生成UPDATE SQL语句 $sql = $this->builder->update($query); // 检测缓存 - if ($this->cache) { - if (isset($key) && $this->cache->get($key)) { - // 删除缓存 - $this->cache->delete($key); - } elseif (isset($cacheItem) && $cacheItem->getTag()) { - $this->cache->clear($cacheItem->getTag()); - } - + if (isset($key) && $this->cache->get($key)) { + // 删除缓存 + $this->cache->delete($key); + } elseif (isset($cacheItem) && $cacheItem->getTag()) { + $this->cache->tag($cacheItem->getTag())->clear(); } // 执行操作 $result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind()); if ($result) { - if (is_string($pk) && isset($options['where']['AND'][$pk])) { - $data[$pk] = $options['where']['AND'][$pk]; - } elseif (is_string($pk) && isset($key) && strpos($key, '|')) { - list($a, $val) = explode('|', $key); - $data[$pk] = $val; - } - - $query->setOption('data', $data); - $query->trigger('after_update'); + $this->db->trigger('after_update', $query); } return $result; @@ -1073,7 +1074,7 @@ abstract class Connection /** * 删除记录 * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return int * @throws Exception * @throws PDOException @@ -1082,51 +1083,28 @@ abstract class Connection { // 分析查询表达式 $options = $query->parseOptions(); - $pk = $query->getPk($options); - $data = $options['data']; if (isset($options['cache'])) { - $cacheItem = $options['cache']; + $cacheItem = $this->parseCache($query, $options['cache']); $key = $cacheItem->getKey(); - } elseif (!is_null($data) && true !== $data && !is_array($data)) { - $key = $this->getCacheKey($query, $data); - } elseif (is_string($pk) && isset($options['where']['AND'][$pk])) { - $key = $this->getCacheKey($query, $options['where']['AND'][$pk]); - } - - if (true !== $data && empty($options['where'])) { - // 如果条件为空 不进行删除操作 除非设置 1=1 - throw new Exception('delete without condition'); } // 生成删除SQL语句 $sql = $this->builder->delete($query); // 检测缓存 - - if ($this->cache) { - if (isset($key) && $this->cache->get($key)) { - // 删除缓存 - $this->cache->delete($key); - } elseif (isset($cacheItem) && $cacheItem->getTag()) { - $this->cache->clear($cacheItem->getTag()); - } - + if (isset($key) && $this->cache->get($key)) { + // 删除缓存 + $this->cache->delete($key); + } elseif (isset($cacheItem) && $cacheItem->getTag()) { + $this->cache->tag($cacheItem->getTag())->clear(); } // 执行操作 $result = $this->execute($query, $sql, $query->getBind()); if ($result) { - if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) { - list($a, $val) = explode('|', $key); - $item[$pk] = $val; - $data = $item; - } - - $options['data'] = $data; - - $query->trigger('after_delete'); + $this->db->trigger('after_delete', $query); } return $result; @@ -1135,12 +1113,13 @@ abstract class Connection /** * 得到某个字段的值 * @access public - * @param Query $query 查询对象 - * @param string $field 字段名 - * @param mixed $default 默认值 + * @param Query $query 查询对象 + * @param string $field 字段名 + * @param mixed $default 默认值 + * @param bool $one 返回一个值 * @return mixed */ - public function value(Query $query, string $field, $default = null) + public function value(Query $query, string $field, $default = null, bool $one = true) { $options = $query->parseOptions(); @@ -1148,14 +1127,10 @@ abstract class Connection $query->removeOption('field'); } - if (is_string($field)) { - $field = array_map('trim', explode(',', $field)); - } - - $query->setOption('field', $field); + $query->setOption('field', (array) $field); if (!empty($options['cache'])) { - $cacheItem = $options['cache']; + $cacheItem = $this->parseCache($query, $options['cache']); $result = $this->getCacheData($cacheItem); if (false !== $result) { @@ -1164,7 +1139,7 @@ abstract class Connection } // 生成查询SQL - $sql = $this->builder->select($query, true); + $sql = $this->builder->select($query, $one); if (isset($options['field'])) { $query->setOption('field', $options['field']); @@ -1189,10 +1164,10 @@ abstract class Connection /** * 得到某个字段的值 * @access public - * @param Query $query 查询对象 - * @param string $aggregate 聚合方法 - * @param mixed $field 字段名 - * @param bool $force 强制转为数字类型 + * @param Query $query 查询对象 + * @param string $aggregate 聚合方法 + * @param mixed $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ public function aggregate(Query $query, string $aggregate, $field, bool $force = false) @@ -1203,7 +1178,7 @@ abstract class Connection $field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS tp_' . strtolower($aggregate); - $result = $this->value($query, $field, 0); + $result = $this->value($query, $field, 0, false); return $force ? (float) $result : $result; } @@ -1211,12 +1186,12 @@ abstract class Connection /** * 得到某个列的数组 * @access public - * @param Query $query 查询对象 - * @param string $field 字段名 多个字段用逗号分隔 - * @param string $key 索引 + * @param Query $query 查询对象 + * @param string $column 字段名 多个字段用逗号分隔 + * @param string $key 索引 * @return array */ - public function column(Query $query, string $field, string $key = ''): array + public function column(Query $query, string $column, string $key = ''): array { $options = $query->parseOptions(); @@ -1224,8 +1199,10 @@ abstract class Connection $query->removeOption('field'); } - if ($key && '*' != $field) { - $field = $key . ',' . $field; + if ($key && '*' != $column) { + $field = $key . ',' . $column; + } else { + $field = $column; } $field = array_map('trim', explode(',', $field)); @@ -1234,7 +1211,7 @@ abstract class Connection if (!empty($options['cache'])) { // 判断查询缓存 - $cacheItem = $options['cache']; + $cacheItem = $this->parseCache($query, $options['cache']); $result = $this->getCacheData($cacheItem); if (false !== $result) { @@ -1254,36 +1231,21 @@ abstract class Connection // 执行查询操作 $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']); - if (1 == $pdo->columnCount()) { - $result = $pdo->fetchAll(PDO::FETCH_COLUMN); - } else { - $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC); - - if ('*' == $field && $key) { - $result = array_column($resultSet, null, $key); - } elseif ($resultSet) { - $fields = array_keys($resultSet[0]); - $count = count($fields); - $key1 = array_shift($fields); - $key2 = $fields ? array_shift($fields) : ''; - $key = $key ?: $key1; - - if (strpos($key, '.')) { - list($alias, $key) = explode('.', $key); - } + $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC); - if (2 == $count) { - $column = $key2; - } elseif (1 == $count) { - $column = $key1; - } else { - $column = null; - } + if (empty($resultSet)) { + $result = []; + } elseif (('*' == $column || strpos($column, ',')) && $key) { + $result = array_column($resultSet, null, $key); + } else { + $fields = array_keys($resultSet[0]); + $key = $key ?: array_shift($fields); - $result = array_column($resultSet, $column, $key); - } else { - $result = []; + if (strpos($key, '.')) { + list($alias, $key) = explode('.', $key); } + + $result = array_column($resultSet, $column, $key); } if (isset($cacheItem)) { @@ -1298,8 +1260,8 @@ abstract class Connection /** * 根据参数绑定组装最终的SQL语句 便于调试 * @access public - * @param string $sql 带参数绑定的sql语句 - * @param array $bind 参数绑定列表 + * @param string $sql 带参数绑定的sql语句 + * @param array $bind 参数绑定列表 * @return string */ public function getRealSql(string $sql, array $bind = []): string @@ -1330,7 +1292,7 @@ abstract class Connection * 支持 ['name'=>'value','id'=>123] 对应命名占位符 * 或者 ['value',123] 对应问号占位符 * @access public - * @param array $bind 要绑定的参数列表 + * @param array $bind 要绑定的参数列表 * @return void * @throws BindParamException */ @@ -1367,7 +1329,7 @@ abstract class Connection /** * 存储过程的输入输出参数绑定 * @access public - * @param array $bind 要绑定的参数列表 + * @param array $bind 要绑定的参数列表 * @return void * @throws BindParamException */ @@ -1399,7 +1361,7 @@ abstract class Connection /** * 获得数据集数组 * @access protected - * @param bool $procedure 是否存储过程 + * @param bool $procedure 是否存储过程 * @return array */ protected function getResult(bool $procedure = false): array @@ -1427,7 +1389,7 @@ abstract class Connection do { $result = $this->getResult(); - if ($result) { + if (!empty($result)) { $item[] = $result; } } while ($this->PDOStatement->nextRowset()); @@ -1440,7 +1402,7 @@ abstract class Connection /** * 执行数据库事务 * @access public - * @param callable $callback 数据操作方法回调 + * @param callable $callback 数据操作方法回调 * @return mixed * @throws PDOException * @throws \Exception @@ -1544,7 +1506,7 @@ abstract class Connection /** * 生成定义保存点的SQL * @access protected - * @param $name + * @param string $name 标识 * @return string */ protected function parseSavepoint(string $name): string @@ -1555,7 +1517,7 @@ abstract class Connection /** * 生成回滚到保存点的SQL * @access protected - * @param $name + * @param string $name 标识 * @return string */ protected function parseSavepointRollBack(string $name): string @@ -1567,17 +1529,13 @@ abstract class Connection * 批处理执行SQL语句 * 批处理的指令都认为是execute操作 * @access public - * @param Query $query 查询对象 - * @param array $sqlArray SQL批处理指令 - * @param array $bind 参数绑定 + * @param Query $query 查询对象 + * @param array $sqlArray SQL批处理指令 + * @param array $bind 参数绑定 * @return bool */ public function batchQuery(Query $query, array $sqlArray = [], array $bind = []): bool { - if (!is_array($sqlArray)) { - return false; - } - // 自动启动事务支持 $this->startTrans(); @@ -1595,16 +1553,6 @@ abstract class Connection return true; } - /** - * 获得查询次数 - * @access public - * @return integer - */ - public function getQueryTimes(): int - { - return self::$queryTimes; - } - /** * 关闭数据库(或者重新连接) * @access public @@ -1625,7 +1573,7 @@ abstract class Connection /** * 是否断线 * @access protected - * @param \PDOException|\Exception $e 异常对象 + * @param \PDOException|\Exception $e 异常对象 * @return bool */ protected function isBreak($e): bool @@ -1658,7 +1606,7 @@ abstract class Connection /** * 获取最近插入的ID * @access public - * @param string $sequence 自增序列名 + * @param string $sequence 自增序列名 * @return string */ public function getLastInsID(string $sequence = null): string @@ -1700,9 +1648,9 @@ abstract class Connection /** * 数据库调试 记录当前SQL及分析性能 * @access protected - * @param boolean $start 调试开始标记 true 开始 false 结束 - * @param string $sql 执行的SQL语句 留空自动获取 - * @param bool $master 主从标记 + * @param boolean $start 调试开始标记 true 开始 false 结束 + * @param string $sql 执行的SQL语句 留空自动获取 + * @param bool $master 主从标记 * @return void */ protected function debug(bool $start, string $sql = '', bool $master = false): void @@ -1728,32 +1676,22 @@ abstract class Connection } } - /** - * 监听SQL执行 - * @access public - * @param callable $callback 回调方法 - * @return void - */ - public function listen(callable $callback): void - { - self::$event[] = $callback; - } - /** * 触发SQL事件 * @access protected - * @param string $sql SQL语句 - * @param string $runtime SQL运行时间 - * @param mixed $explain SQL分析 - * @param bool $master 主从标记 + * @param string $sql SQL语句 + * @param string $runtime SQL运行时间 + * @param mixed $explain SQL分析 + * @param bool $master 主从标记 * @return void */ protected function triggerSql(string $sql, string $runtime, array $explain = [], bool $master = false): void { - if (!empty(self::$event)) { - foreach (self::$event as $callback) { + $listen = $this->db->getListen(); + if (!empty($listen)) { + foreach ($listen as $callback) { if (is_callable($callback)) { - call_user_func_array($callback, [$sql, $runtime, $explain, $master]); + $callback($sql, $runtime, $explain, $master); } } } else { @@ -1773,20 +1711,24 @@ abstract class Connection } } - public function log($log) - { - $this->config['debug'] && self::$log[] = $log; - } - - public function getSqlLog() + /** + * 记录SQL日志 + * @access protected + * @param string $log SQL日志信息 + * @param string $type 日志类型 + * @return void + */ + protected function log($log, $type = 'sql'): void { - return self::$log; + if ($this->config['debug']) { + $this->log->record($log, $type); + } } /** * 初始化数据库连接 * @access protected - * @param boolean $master 是否主服务器 + * @param boolean $master 是否主服务器 * @return void */ protected function initConnect(bool $master = true): void @@ -1815,16 +1757,16 @@ abstract class Connection /** * 连接分布式服务器 * @access protected - * @param boolean $master 主服务器 + * @param boolean $master 主服务器 * @return PDO */ protected function multiConnect(bool $master = false): PDO { - $_config = []; + $config = []; // 分布式数据库配置解析 foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { - $_config[$name] = is_string($this->config[$name]) ? explode(',', $this->config[$name]) : $this->config[$name]; + $config[$name] = is_string($this->config[$name]) ? explode(',', $this->config[$name]) : $this->config[$name]; } // 主服务器序号 @@ -1840,25 +1782,25 @@ abstract class Connection $r = $this->config['slave_no']; } else { // 读操作连接从服务器 每次随机连接的数据库 - $r = floor(mt_rand($this->config['master_num'], count($_config['hostname']) - 1)); + $r = floor(mt_rand($this->config['master_num'], count($config['hostname']) - 1)); } } else { // 读写操作不区分服务器 每次随机连接的数据库 - $r = floor(mt_rand(0, count($_config['hostname']) - 1)); + $r = floor(mt_rand(0, count($config['hostname']) - 1)); } $dbMaster = false; if ($m != $r) { $dbMaster = []; foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { - $dbMaster[$name] = $_config[$name][$m] ?? $_config[$name][0]; + $dbMaster[$name] = $config[$name][$m] ?? $config[$name][0]; } } $dbConfig = []; foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { - $dbConfig[$name] = $_config[$name][$r] ?? $_config[$name][0]; + $dbConfig[$name] = $config[$name][$r] ?? $config[$name][0]; } return $this->connect($dbConfig, $r, $r == $m ? false : $dbMaster); @@ -1880,26 +1822,21 @@ abstract class Connection /** * 缓存数据 * @access protected - * @param CacheItem $cacheItem 缓存Item + * @param CacheItem $cacheItem 缓存Item */ protected function cacheData(CacheItem $cacheItem): void { - if ($cacheItem->getTag()) { $this->cache->tag($cacheItem->getTag()); } $this->cache->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire()); - } /** * 获取缓存数据 * @access protected - * @param Query $query 查询对象 - * @param mixed $cache 缓存设置 - * @param array $data 缓存数据 - * @param string $key 缓存Key + * @param CacheItem $cacheItem * @return mixed */ protected function getCacheData(CacheItem $cacheItem) @@ -1908,32 +1845,56 @@ abstract class Connection return $this->cache->get($cacheItem->getKey()); } - /** - * 生成缓存标识 - * @access protected - * @param Query $query 查询对象 - * @param mixed $value 缓存数据 - * @return string - */ - protected function getCacheKey(Query $query, $value): string + protected function parseCache(Query $query, array $cache): CacheItem { - if (is_scalar($value)) { - $data = $value; - } elseif (is_array($value) && isset($value[1], $value[2]) && '=' == $value[1]) { - $data = $value[2]; - } + list($key, $expire, $tag) = $cache; - $prefix = 'think:' . $this->getConfig('database') . '.'; + if ($key instanceof CacheItem) { + $cacheItem = $key; + } else { + if (true === $key) { + if (!empty($query->getOptions('key'))) { + $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); + } else { + $key = md5($this->getConfig('database') . serialize($query->getOptions()) . serialize($query->getBind(false))); + } + } - if (isset($data)) { - return $prefix . $query->getTable() . '|' . $data; + $cacheItem = new CacheItem($key); + $cacheItem->expire($expire); + $cacheItem->tag($tag); } - try { - return md5($prefix . serialize($query->getOptions()) . serialize($query->getBind(false))); - } catch (\Exception $e) { - throw new Exception('closure not support cache(true)'); - } + return $cacheItem; } + /** + * 延时更新检查 返回false表示需要延时 + * 否则返回实际写入的数值 + * @access public + * @param string $type 自增或者自减 + * @param string $guid 写入标识 + * @param float $step 写入步进值 + * @param integer $lazyTime 延时时间(s) + * @return false|integer + */ + public function lazyWrite(string $type, string $guid, float $step, int $lazyTime) + { + if (!$this->cache->has($guid . '_time')) { + // 计时开始 + $this->cache->set($guid . '_time', time(), 0); + $this->cache->$type($guid, $step); + } elseif (time() > $this->cache->get($guid . '_time') + $lazyTime) { + // 删除缓存 + $value = $this->cache->$type($guid, $step); + $this->cache->delete($guid); + $this->cache->delete($guid . '_time'); + return 0 === $value ? false : $value; + } else { + // 更新缓存 + $this->cache->$type($guid, $step); + } + + return false; + } } diff --git a/src/db/Fetch.php b/src/db/Fetch.php index 387a796..38b1dd0 100644 --- a/src/db/Fetch.php +++ b/src/db/Fetch.php @@ -12,9 +12,12 @@ declare (strict_types = 1); namespace think\db; -use think\Db; use think\Exception; +use think\facade\Db; +/** + * SQL获取类 + */ class Fetch { /** @@ -56,7 +59,7 @@ class Fetch */ public function aggregate(string $aggregate, string $field): string { - $options = $this->query->parseOptions(); + $this->query->parseOptions(); $field = $aggregate . '(' . $this->builder->parseKey($this->query, $field) . ') AS tp_' . strtolower($aggregate); @@ -77,11 +80,7 @@ class Fetch $this->query->removeOption('field'); } - if (is_string($field)) { - $field = array_map('trim', explode(',', $field)); - } - - $this->query->setOption('field', $field); + $this->query->setOption('field', (array) $field); // 生成查询SQL $sql = $this->builder->select($this->query, true); @@ -133,30 +132,60 @@ class Fetch /** * 插入记录 * @access public - * @param array $data 数据 - * @param boolean $replace 是否replace + * @param array $data 数据 * @return string */ - public function insert(array $data = [], bool $replace = false): string + public function insert(array $data = []): string { $options = $this->query->parseOptions(); - $this->query->setOption('data', array_merge($options['data'], $data)); + if (!empty($data)) { + $this->query->setOption('data', $data); + } + + $replace = $options['replace'] ?? false; + $sql = $this->builder->insert($this->query, $replace); - $sql = $this->builder->insert($this->query, $replace); return $this->fetch($sql); } /** * 插入记录并获取自增ID * @access public - * @param array $data 数据 - * @param boolean $replace 是否replace + * @param array $data 数据 * @return string */ - public function insertGetId(array $data, bool $replace = false) + public function insertGetId(array $data = []): string { - return $this->insert($data, $replace); + return $this->insert($data); + } + + /** + * 保存数据 自动判断insert或者update + * @access public + * @param array $data 数据 + * @param bool $forceInsert 是否强制insert + * @return string + */ + public function save(array $data = [], bool $forceInsert = false): string + { + if (empty($data)) { + $data = $this->query->getOptions('data'); + } + + if (empty($data)) { + return ''; + } + + if ($forceInsert) { + return $this->insert($data); + } + + $isUpdate = $this->query->parseUpdateData($data); + + $this->query->setOption('data', $data); + + return $isUpdate ? $this->update() : $this->insert(); } /** @@ -180,19 +209,20 @@ class Fetch } if ($limit) { - $array = array_chunk($dataSet, $limit, true); - + $array = array_chunk($dataSet, $limit, true); + $fetchSql = []; foreach ($array as $item) { $sql = $this->builder->insertAll($this->query, $item, $replace); $bind = $this->query->getBind(); - $fetchSql[] = $this->getRealSql($sql, $bind); + $fetchSql[] = $this->connection->getRealSql($sql, $bind); } return implode(';', $fetchSql); } $sql = $this->builder->insertAll($this->query, $dataSet, $replace); + return $this->fetch($sql); } @@ -207,6 +237,7 @@ class Fetch { $this->query->parseOptions(); $sql = $this->builder->selectInsert($this->query, $fields, $table); + return $this->fetch($sql); } @@ -220,26 +251,20 @@ class Fetch { $options = $this->query->parseOptions(); - $this->query->setOption('data', array_merge($options['data'], $data)); + $data = !empty($data) ? $data : $options['data']; - if (isset($options['cache']) && is_string($options['cache']['key'])) { - $key = $options['cache']['key']; - } - - $pk = $this->query->getPk($options); - $data = $options['data']; - $options['pk'] = $pk; + $pk = $this->query->getPk(); if (empty($options['where'])) { // 如果存在主键数据 则自动作为更新条件 if (is_string($pk) && isset($data[$pk])) { - $where[$pk] = [$pk, '=', $data[$pk]]; + $this->query->where($pk, '=', $data[$pk]); unset($data[$pk]); } elseif (is_array($pk)) { // 增加复合主键支持 foreach ($pk as $field) { if (isset($data[$field])) { - $where[$field] = [$field, '=', $data[$field]]; + $this->query->where($field, '=', $data[$field]); } else { // 如果缺少复合主键数据则不执行 throw new Exception('miss complex primary data'); @@ -248,12 +273,9 @@ class Fetch } } - if (!isset($where)) { + if (empty($this->query->getOptions('where'))) { // 如果没有任何更新条件则不执行 throw new Exception('miss update condition'); - } else { - $options['where']['AND'] = $where; - $this->query->setOption('where', ['AND' => $where]); } } @@ -262,6 +284,7 @@ class Fetch // 生成UPDATE SQL语句 $sql = $this->builder->update($this->query); + return $this->fetch($sql); } @@ -292,9 +315,9 @@ class Fetch } } - $this->query->setOption('data', $data); // 生成删除SQL语句 $sql = $this->builder->delete($this->query); + return $this->fetch($sql); } @@ -313,9 +336,9 @@ class Fetch $this->query->parsePkWhere($data); } - $this->query->setOption('data', $data); // 生成查询SQL $sql = $this->builder->select($this->query); + return $this->fetch($sql); } @@ -334,14 +357,8 @@ class Fetch $this->query->parsePkWhere($data); } - $this->query->setOption('data', $data); - - $this->query->setOption('limit', '1'); - // 生成查询SQL - $sql = $this->builder->select($this->query); - - $this->query->removeOption('limit'); + $sql = $this->builder->select($this->query, true); // 获取实际执行的SQL语句 return $this->fetch($sql); @@ -350,23 +367,23 @@ class Fetch /** * 查找多条记录 如果不存在则抛出异常 * @access public - * @param array|string|Query|\Closure $data + * @param mixed $data * @return string */ public function selectOrFail($data = null): string { - return $this->failException(true)->select($data); + return $this->query->failException(true)->select($data); } /** * 查找单条记录 如果不存在则抛出异常 * @access public - * @param array|string|Query|\Closure $data + * @param mixed $data * @return string */ public function findOrFail($data = null): string { - return $this->failException(true)->find($data); + return $this->query->failException(true)->find($data); } /** @@ -413,7 +430,7 @@ class Fetch */ public function sum(string $field): string { - return $this->aggregate('SUM', $field, true); + return $this->aggregate('SUM', $field); } /** @@ -454,11 +471,11 @@ class Fetch if (strtolower(substr($method, 0, 5)) == 'getby') { // 根据某个字段获取记录 $field = Db::parseName(substr($method, 5)); - return $this->where($field, '=', $args[0])->find(); + return $this->query->where($field, '=', $args[0])->find(); } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { // 根据某个字段获取记录的某个值 $name = Db::parseName(substr($method, 10)); - return $this->where($name, '=', $args[0])->value($args[1]); + return $this->query->where($name, '=', $args[0])->value($args[1]); } $result = call_user_func_array([$this->query, $method], $args); diff --git a/src/db/Mongo.php b/src/db/Mongo.php index acffa87..d9d64d7 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -6,10 +6,9 @@ // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- - +declare (strict_types = 1); namespace think\db; -use Exception; use MongoDB\Driver\BulkWrite; use MongoDB\Driver\Command; use MongoDB\Driver\Cursor; @@ -21,95 +20,77 @@ use MongoDB\Driver\Exception\RuntimeException; use MongoDB\Driver\Query as MongoQuery; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; -use think\Db; -use think\db\connector\Mongo as MongoConnection; -use think\db\Query; +use think\Collection; +use think\db\connector\Mongo as Connection; +use think\db\Query as BaseQuery; +use think\Exception; -class Mongo extends Query +class Mongo extends BaseQuery { - /** * 架构函数 * @access public + * @param Connection $connection 数据库连接对象 */ - public function __construct(MongoConnection $connection = null) + public function __construct(Connection $connection) { - if (is_null($connection)) { - $this->connection = MongoConnection::instance(); - } else { - $this->connection = $connection; - } + $this->connection = $connection; $this->prefix = $this->connection->getConfig('prefix'); - $this->cache = Db::getCacheHandler(); } /** - * 去除某个查询条件 + * 获取当前的数据库Connection对象 * @access public - * @param string $field 查询字段 - * @param string $logic 查询逻辑 and or xor - * @return $this + * @return Connection */ - public function removeWhereField(string $field, string $logic = 'AND') + public function getConnection() { - $logic = '$' . strtoupper($logic); - - if (isset($this->options['where'][$logic])) { - foreach ($this->options['where'][$logic] as $key => $val) { - if (is_array($val) && $val[0] == $field) { - unset($this->options['where'][$logic][$key]); - } - } - } - - return $this; + return $this->connection; } /** * 执行查询 返回数据集 * @access public - * @param string $namespace - * @param MongoQuery $query 查询对象 - * @param ReadPreference $readPreference readPreference - * @param bool|string $class 指定返回的数据集对象 - * @param string|array $typeMap 指定返回的typeMap + * @param string $namespace 当前查询的collection + * @param MongoQuery $query 查询对象 + * @param ReadPreference $readPreference readPreference + * @param string|array $typeMap 指定返回的typeMap * @return mixed * @throws AuthenticationException * @throws InvalidArgumentException * @throws ConnectionException * @throws RuntimeException */ - public function mongoQuery(string $namespace, MongoQuery $query, ReadPreference $readPreference = null, $class = false, $typeMap = null) + public function mongoQuery(string $namespace, MongoQuery $query, ReadPreference $readPreference = null, $typeMap = null) { - return $this->connection->query($namespace, $query, $readPreference, $class, $typeMap); + return $this->connection->query($namespace, $query, $readPreference, $typeMap); } /** * 执行指令 返回数据集 * @access public - * @param Command $command 指令 - * @param string $dbName - * @param ReadPreference $readPreference readPreference - * @param bool|string $class 指定返回的数据集对象 - * @param string|array $typeMap 指定返回的typeMap + * @param Command $command 指令 + * @param string $dbName + * @param ReadPreference $readPreference readPreference + * @param string|array $typeMap 指定返回的typeMap * @return mixed * @throws AuthenticationException * @throws InvalidArgumentException * @throws ConnectionException * @throws RuntimeException */ - public function command(Command $command, string $dbName = '', ReadPreference $readPreference = null, $class = false, $typeMap = null) + public function command(Command $command, string $dbName = '', ReadPreference $readPreference = null, $typeMap = null) { - return $this->connection->command($command, $dbName, $readPreference, $class, $typeMap); + return $this->connection->command($command, $dbName, $readPreference, $typeMap); } /** * 执行语句 * @access public - * @param string $namespace - * @param BulkWrite $bulk - * @param WriteConcern $writeConcern + * @param string $namespace + * @param BulkWrite $bulk + * @param WriteConcern $writeConcern * @return int * @throws AuthenticationException * @throws InvalidArgumentException @@ -125,12 +106,12 @@ class Mongo extends Query /** * 执行command * @access public - * @param string|array|object $command 指令 - * @param mixed $extra 额外参数 - * @param string $db 数据库名 + * @param string|array|object $command 指令 + * @param mixed $extra 额外参数 + * @param string $db 数据库名 * @return array */ - public function cmd($command, $extra = null, $db = null) + public function cmd($command, $extra = null, string $db = ''): array { return $this->connection->cmd($this, $command, $extra, $db); } @@ -138,10 +119,10 @@ class Mongo extends Query /** * 指定distinct查询 * @access public - * @param string $field 字段名 + * @param string $field 字段名 * @return array */ - public function distinct($field) + public function getDistinct(string $field) { $result = $this->cmd('distinct', $field); return $result[0]['values']; @@ -150,17 +131,17 @@ class Mongo extends Query /** * 获取数据库的所有collection * @access public - * @param string $db 数据库名称 留空为当前数据库 + * @param string $db 数据库名称 留空为当前数据库 * @throws Exception */ - public function listCollections($db = '') + public function listCollections(string $db = '') { $cursor = $this->cmd('listCollections', null, $db); $result = []; - foreach ($cursor as $collection) { $result[] = $collection['name']; } + return $result; } @@ -181,9 +162,9 @@ class Mongo extends Query /** * 聚合查询 * @access public - * @param string $aggregate 聚合指令 - * @param string $field 字段名 - * @param bool $force 强制转为数字类型 + * @param string $aggregate 聚合指令 + * @param string $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ public function aggregate(string $aggregate, $field, bool $force = false) @@ -191,7 +172,8 @@ class Mongo extends Query $this->parseOptions(); $result = $this->cmd('aggregate', [strtolower($aggregate), $field]); - $value = $result[0]['aggregate'] ?? 0; + + $value = $result[0]['aggregate'] ?? 0; if ($force) { $value += 0; @@ -203,11 +185,11 @@ class Mongo extends Query /** * 多聚合操作 * - * @param array $aggregate 聚合指令, 可以聚合多个参数, 如 ['sum' => 'field1', 'avg' => 'field2'] - * @param array $groupBy 类似mysql里面的group字段, 可以传入多个字段, 如 ['field_a', 'field_b', 'field_c'] + * @param array $aggregate 聚合指令, 可以聚合多个参数, 如 ['sum' => 'field1', 'avg' => 'field2'] + * @param array $groupBy 类似mysql里面的group字段, 可以传入多个字段, 如 ['field_a', 'field_b', 'field_c'] * @return array 查询结果 */ - public function multiAggregate($aggregate, $groupBy) + public function multiAggregate(array $aggregate, array $groupBy): array { $this->parseOptions(); @@ -228,36 +210,53 @@ class Mongo extends Query /** * 字段值增长 * @access public - * @param string|array $field 字段名 - * @param integer $step 增长值 + * @param string $field 字段名 + * @param float $step 增长值 + * @param integer $lazyTime 延时时间(s) + * @param string $op inc/dec * @return $this */ - public function inc(string $field, int $step = 1, string $op = 'INC') + public function inc(string $field, float $step = 1, int $lazyTime = 0, string $op = 'inc') { - return parent::inc($field, $step, strtolower('$' . $op)); + if ($lazyTime > 0) { + // 延迟写入 + $condition = $this->options['where'] ?? []; + + $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); + $step = $this->connection->lazyWrite($op, $guid, $step, $lazyTime); + + if (false === $step) { + return $this; + } + } + + $this->data($field, ['$' . $op, $step]); + + return $this; } /** - * 指定当前操作的collection + * 字段值减少 * @access public - * @param string $collection + * @param string $field 字段名 + * @param float $step 减少值 + * @param integer $lazyTime 延时时间(s) * @return $this */ - public function collection(string $collection) + public function dec(string $field, float $step = 1, int $lazyTime = 0) { - return $this->table($collection); + return $this->inc($field, -1 * $step, $lazyTime); } /** - * 不主动获取数据集 + * 指定当前操作的collection * @access public - * @param bool $cursor 是否返回 Cursor 对象 + * @param string $collection * @return $this */ - public function fetchCursor(bool $cursor = true) + public function collection(string $collection) { - $this->options['fetch_cursor'] = $cursor; - return $this; + return $this->table($collection); } /** @@ -383,7 +382,7 @@ class Mongo extends Query /** * 设置返回字段 * @access public - * @param mixed $field + * @param mixed $field 字段信息 * @param boolean $except 是否排除 * @param string $tableName 数据表名 * @param string $prefix 字段前缀 @@ -392,18 +391,7 @@ class Mongo extends Query */ public function field($field, bool $except = false, string $tableName = '', string $prefix = '', string $alias = '') { - if (empty($field)) { - return $this; - } elseif ($field instanceof Expression) { - $this->options['field'][] = $field; - return $this; - } - if (is_string($field)) { - if (preg_match('/[\<\'\"\(]/', $field)) { - return $this->fieldRaw($field); - } - $field = array_map('trim', explode(',', $field)); } @@ -448,8 +436,8 @@ class Mongo extends Query /** * 指定查询数量 * @access public - * @param mixed $offset 起始位置 - * @param mixed $length 查询数量 + * @param int $offset 起始位置 + * @param int $length 查询数量 * @return $this */ public function limit(int $offset, int $length = null) @@ -468,8 +456,8 @@ class Mongo extends Query /** * 设置sort * @access public - * @param array|string|object $field - * @param string $order + * @param array|string $field + * @param string $order * @return $this */ public function order($field, string $order = '') @@ -485,7 +473,7 @@ class Mongo extends Query /** * 设置tailable * @access public - * @param bool $tailable + * @param bool $tailable * @return $this */ public function tailable(bool $tailable) @@ -497,58 +485,21 @@ class Mongo extends Query /** * 设置writeConcern对象 * @access public - * @param WriteConcern $writeConcern + * @param WriteConcern $writeConcern * @return $this */ - public function writeConcern($writeConcern) + public function writeConcern(WriteConcern $writeConcern) { $this->options['writeConcern'] = $writeConcern; return $this; } - /** - * 把主键值转换为查询条件 支持复合主键 - * @access public - * @param array|string $data 主键数据 - * @param mixed $options 表达式参数 - * @return void - * @throws Exception - */ - public function parsePkWhere($data): void - { - $pk = $this->getPk($this->options); - - if (is_string($pk)) { - // 获取数据表 - if (empty($this->options['table'])) { - $this->options['table'] = $this->getTable(); - } - - $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table']; - - if (!empty($this->options['alias'][$table])) { - $alias = $this->options['alias'][$table]; - } - - $key = isset($alias) ? $alias . '.' . $pk : $pk; - // 根据主键查询 - $where[$pk] = is_array($data) ? [$key, 'in', $data] : [$key, '=', $data]; - - if (isset($this->options['where']['$and'])) { - $this->options['where']['$and'] = array_merge($this->options['where']['$and'], $where); - } else { - $this->options['where']['$and'] = $where; - } - } - } - /** * 获取当前数据表的主键 * @access public - * @param string|array $options 数据表名或者查询参数 * @return string|array */ - public function getPk($options = '') + public function getPk() { return $this->pk ?: $this->connection->getConfig('pk'); } @@ -558,23 +509,13 @@ class Mongo extends Query * @access public * @return Cursor */ - public function getCursor() + public function getCursor(): Cursor { $this->parseOptions(); return $this->connection->getCursor($this); } - /** - * 获取模型的更新条件 - * @access protected - * @param array $options 查询参数 - */ - protected function getModelUpdateCondition(array $options) - { - return isset($options['where']['$and']) ? $options['where']['$and'] : null; - } - /** * 分析表达式(可用于查询或者写入操作) * @access public @@ -620,7 +561,7 @@ class Mongo extends Query $options['limit'] = 0; } - foreach (['master', 'fetch_cursor'] as $name) { + foreach (['master', 'fetch_sql', 'fetch_cursor'] as $name) { if (!isset($options[$name])) { $options[$name] = false; } diff --git a/src/db/Query.php b/src/db/Query.php index a9d3327..0b4cee8 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -15,14 +15,14 @@ namespace think\db; use Closure; use PDO; use PDOStatement; -use think\cache\CacheItem; +use think\App; use think\Collection; use think\Db; +use think\db\exception\BindParamException; +use think\db\exception\DataNotFoundException; +use think\db\exception\ModelNotFoundException; use think\Exception; -use think\exception\BindParamException; -use think\exception\DataNotFoundException; use think\exception\DbException; -use think\exception\ModelNotFoundException; use think\exception\PDOException; use think\Model; use think\model\Collection as ModelCollection; @@ -30,6 +30,9 @@ use think\model\Relation; use think\model\relation\OneToOne; use think\Paginator; +/** + * 数据查询类 + */ class Query { /** @@ -44,6 +47,12 @@ class Query */ protected $model; + /** + * Db对象 + * @var Db + */ + protected $db; + /** * 当前数据表名称(不含前缀) * @var string @@ -74,18 +83,6 @@ class Query */ protected $bind = []; - /** - * 事件回调 - * @var array - */ - protected static $event = []; - - /** - * 扩展查询方法 - * @var array - */ - protected static $extend = []; - /** * 日期查询表达式 * @var array @@ -101,22 +98,16 @@ class Query 'last year' => ['last year 1/1', 'this year 1/1'], ]; - /** - * 日期查询快捷定义 - * @var array - */ - protected $timeExp = ['d' => 'today', 'w' => 'week', 'm' => 'month', 'y' => 'year']; - /** * 架构函数 * @access public + * @param Connection $connection 数据库连接对象 */ - public function __construct(Connection $connection = null) + public function __construct(Connection $connection) { $this->connection = $connection; $this->prefix = $this->connection->getConfig('prefix'); - $this->cache = Db::getCacheHandler(); } /** @@ -126,72 +117,48 @@ class Query */ public function newQuery() { - return new static($this->connection); - } + $query = new static($this->connection); - /** - * 切换数据库连接 - * @access public - * @param mixed $config 连接配置 - * @param bool|string $name 连接标识 true 强制重新连接 - * @return $this|object - * @throws Exception - */ - public function connect($config = [], $name = false) - { - $this->connection = Connection::instance($config, $name); - - return $this; - } + if ($this->model) { + $query->model($this->model); + } - /** - * 数据库连接参数解析 - * @access private - * @param mixed $config - * @return array - */ - private function parseConfig($config): array - { - if (empty($config)) { - $config = $this->config; - } elseif (is_string($config) && false === strpos($config, '/')) { - // 支持读取配置参数 - $config = $this->config[$config] ?? $this->config; + if (isset($this->options['table'])) { + $query->table($this->options['table']); + } else { + $query->name($this->name); } - return is_string($config) ? $this->parseDsnConfig($config) : $config; + $query->setDb($this->db); + + return $query; } /** * 利用__call方法实现一些特殊的Model方法 * @access public - * @param string $method 方法名称 - * @param array $args 调用参数 + * @param string $method 方法名称 + * @param array $args 调用参数 * @return mixed * @throws DbException * @throws Exception */ public function __call(string $method, array $args) { - if (isset(self::$extend[strtolower($method)])) { - // 调用扩展查询方法 - array_unshift($args, $this); - - return call_user_func_array(self::$extend[strtolower($method)], $args); - } elseif (strtolower(substr($method, 0, 5)) == 'getby') { + if (strtolower(substr($method, 0, 5)) == 'getby') { // 根据某个字段获取记录 - $field = Db::parseName(substr($method, 5)); + $field = $this->db->parseName(substr($method, 5)); return $this->where($field, '=', $args[0])->find(); } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { // 根据某个字段获取记录的某个值 - $name = Db::parseName(substr($method, 10)); + $name = $this->db->parseName(substr($method, 10)); return $this->where($name, '=', $args[0])->value($args[1]); } elseif (strtolower(substr($method, 0, 7)) == 'whereor') { - $name = Db::parseName(substr($method, 7)); + $name = $this->db->parseName(substr($method, 7)); array_unshift($args, $name); return call_user_func_array([$this, 'whereOr'], $args); } elseif (strtolower(substr($method, 0, 5)) == 'where') { - $name = Db::parseName(substr($method, 5)); + $name = $this->db->parseName(substr($method, 5)); array_unshift($args, $name); return call_user_func_array([$this, 'where'], $args); } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { @@ -206,30 +173,12 @@ class Query } } - /** - * 扩展查询方法 - * @access public - * @param string|array $method 查询方法名 - * @param callable $callback - * @return void - */ - public static function extend($method, $callback = null): void - { - if (is_array($method)) { - foreach ($method as $key => $val) { - self::$extend[strtolower($key)] = $val; - } - } else { - self::$extend[strtolower($method)] = $callback; - } - } - /** * 获取当前的数据库Connection对象 * @access public * @return Connection */ - public function getConnection(): Connection + public function getConnection() { return $this->connection; } @@ -237,20 +186,43 @@ class Query /** * 设置当前的数据库Connection对象 * @access public - * @param Connection $connection + * @param Connection $connection 数据库连接对象 * @return $this */ - public function setConnection(Connection $connection) + public function setConnection($connection) { $this->connection = $connection; return $this; } + /** + * 设置Db对象 + * @access public + * @param Db $db + * @return $this + */ + public function setDb(Db $db) + { + $this->db = $db; + $this->connection->setDb($db); + return $this; + } + + /** + * 获取Db对象 + * @access public + * @return Db + */ + public function getDb() + { + return $this->db; + } + /** * 指定模型 * @access public - * @param Model $model 模型对象实例 + * @param Model $model 模型对象实例 * @return $this */ public function model(Model $model) @@ -272,7 +244,7 @@ class Query /** * 指定当前数据表名(不含前缀) * @access public - * @param string $name + * @param string $name 不含前缀的数据表名字 * @return $this */ public function name(string $name) @@ -294,7 +266,7 @@ class Query /** * 获取数据库的配置参数 * @access public - * @param string $name 参数名称 + * @param string $name 参数名称 * @return mixed */ public function getConfig(string $name = '') @@ -305,10 +277,10 @@ class Query /** * 得到当前或者指定名称的数据表 * @access public - * @param string $name - * @return string + * @param string $name 不含前缀的数据表名字 + * @return mixed */ - public function getTable(string $name = ''): string + public function getTable(string $name = '') { if (empty($name) && isset($this->options['table'])) { return $this->options['table']; @@ -316,44 +288,28 @@ class Query $name = $name ?: $this->name; - return $this->prefix . Db::parseName($name); + return $this->prefix . $this->db->parseName($name); } /** * 获取数据表字段信息 * @access public - * @param string $tableName 数据表名 + * @param string $tableName 数据表名 * @return array */ public function getTableFields($tableName = ''): array { if ('' == $tableName) { - $tableName = $this->options['table'] ?? $this->getTable(); + $tableName = $this->getTable(); } return $this->connection->getTableFields($tableName); } - /** - * 获取数据表字段类型 - * @access protected - * @param string $tableName 数据表名 - * @param string $field 字段名 - * @return array|string - */ - protected function getTableFieldsType($tableName = '', string $field = null) - { - if ('' == $tableName) { - $tableName = $this->options['table'] ?? $this->getTable(); - } - - return $this->connection->getFieldsType($tableName, $field); - } - /** * 设置字段类型信息 * @access public - * @param array $type 字段类型信息 + * @param array $type 字段类型信息 * @return $this */ public function setFieldType(array $type) @@ -365,44 +321,60 @@ class Query /** * 获取字段类型信息 * @access public - * @param string $field 字段名 - * @return string|null + * @return array */ - public function getFieldType(string $field = null) + public function getFieldsType(): array { - $fieldType = !empty($this->options['field_type']) ? $this->options['field_type'] : $this->getTableFieldsType(); - - if (is_null($field)) { - return $fieldType; + if (!empty($this->options['field_type'])) { + return $this->options['field_type']; } - return $fieldType[$field] ?? null; + return $this->connection->getFieldsType($this->getTable()); } /** * 获取字段类型信息 * @access public - * @param string $field 字段名 + * @param string $field 字段名 * @return string|null */ - public function getFieldBindType(string $field = null) + public function getFieldType(string $field) { - $fieldType = $this->getFieldType($field); + $fieldType = $this->getFieldsType(); - if (is_null($field)) { - return array_map([$this->connection, 'getFieldBindType'], $fieldType); - } + return $fieldType[$field] ?? null; + } + + /** + * 获取字段类型信息 + * @access public + * @return array + */ + public function getFieldsBindType(): array + { + $fieldType = $this->getFieldsType(); - return $this->connection->getFieldBindType($fieldType); + return array_map([$this->connection, 'getFieldBindType'], $fieldType); + } + + /** + * 获取字段类型信息 + * @access public + * @param string $field 字段名 + * @return int + */ + public function getFieldBindType(string $field): int + { + $fieldType = $this->getFieldType($field); + + return $this->connection->getFieldBindType($fieldType ?: ''); } /** * 执行查询 返回数据集 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param boolean $master 是否在主服务器读操作 - * @param bool $pdo 是否返回PDO对象 + * @param string $sql sql指令 + * @param array $bind 参数绑定 * @return array * @throws BindParamException * @throws PDOException @@ -415,32 +387,21 @@ class Query /** * 执行语句 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 + * @param string $sql sql指令 + * @param array $bind 参数绑定 * @return int * @throws BindParamException * @throws PDOException */ public function execute(string $sql, array $bind = []): int { - return $this->connection->execute($this, $sql, $bind); - } - - /** - * 监听SQL执行 - * @access public - * @param callable $callback 回调方法 - * @return void - */ - public function listen(callable $callback): void - { - $this->connection->listen($callback); + return $this->connection->execute($this, $sql, $bind, true); } /** * 获取最近插入的ID * @access public - * @param string $sequence 自增序列名 + * @param string $sequence 自增序列名 * @return string */ public function getLastInsID(string $sequence = null): string @@ -468,30 +429,10 @@ class Query return $this->connection->getLastSql(); } - /** - * 获取sql记录 - * @access public - * @return string - */ - public function getSqlLog() - { - return $this->connection->getSqlLog(); - } - - /** - * 获得查询次数 - * @access public - * @return integer - */ - public function getQueryTimes(): int - { - return $this->connection->getQueryTimes(); - } - /** * 执行数据库事务 * @access public - * @param callable $callback 数据操作方法回调 + * @param callable $callback 数据操作方法回调 * @return mixed */ public function transaction(callable $callback) @@ -535,7 +476,7 @@ class Query * 批处理执行SQL语句 * 批处理的指令都认为是execute操作 * @access public - * @param array $sql SQL批处理指令 + * @param array $sql SQL批处理指令 * @return bool */ public function batchQuery(array $sql = []): bool @@ -546,8 +487,8 @@ class Query /** * 得到某个字段的值 * @access public - * @param string $field 字段名 - * @param mixed $default 默认值 + * @param string $field 字段名 + * @param mixed $default 默认值 * @return mixed */ public function value(string $field, $default = null) @@ -558,8 +499,8 @@ class Query /** * 得到某个列的数组 * @access public - * @param string $field 字段名 多个字段用逗号分隔 - * @param string $key 索引 + * @param string $field 字段名 多个字段用逗号分隔 + * @param string $key 索引 * @return array */ public function column(string $field, string $key = ''): array @@ -570,9 +511,9 @@ class Query /** * 聚合查询 * @access protected - * @param string $aggregate 聚合方法 - * @param string|Expression $field 字段名 - * @param bool $force 强制转为数字类型 + * @param string $aggregate 聚合方法 + * @param string|Raw $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ protected function aggregate(string $aggregate, $field, bool $force = false) @@ -583,7 +524,7 @@ class Query /** * COUNT查询 * @access public - * @param string $field 字段名 + * @param string|Raw $field 字段名 * @return int */ public function count(string $field = '*'): int @@ -606,7 +547,7 @@ class Query /** * SUM查询 * @access public - * @param string $field 字段名 + * @param string|Raw $field 字段名 * @return float */ public function sum($field): float @@ -617,8 +558,8 @@ class Query /** * MIN查询 * @access public - * @param string|Expression $field 字段名 - * @param bool $force 强制转为数字类型 + * @param string|Raw $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ public function min($field, bool $force = true) @@ -629,8 +570,8 @@ class Query /** * MAX查询 * @access public - * @param string|Expression $field 字段名 - * @param bool $force 强制转为数字类型 + * @param string|Raw $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ public function max($field, bool $force = true) @@ -641,7 +582,7 @@ class Query /** * AVG查询 * @access public - * @param string|Expression $field 字段名 + * @param string|Raw $field 字段名 * @return float */ public function avg($field): float @@ -652,17 +593,17 @@ class Query /** * 查询SQL组装 join * @access public - * @param mixed $join 关联的表名 - * @param mixed $condition 条件 - * @param string $type JOIN类型 - * @param array $bind 参数绑定 + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param string $type JOIN类型 + * @param array $bind 参数绑定 * @return $this */ public function join($join, string $condition = null, string $type = 'INNER', array $bind = []) { $table = $this->getJoinTable($join); - if ($bind) { + if (!empty($bind) && $condition) { $this->bindParams($condition, $bind); } @@ -674,9 +615,9 @@ class Query /** * LEFT JOIN * @access public - * @param mixed $join 关联的表名 - * @param mixed $condition 条件 - * @param array $bind 参数绑定 + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param array $bind 参数绑定 * @return $this */ public function leftJoin($join, string $condition = null, array $bind = []) @@ -687,9 +628,9 @@ class Query /** * RIGHT JOIN * @access public - * @param mixed $join 关联的表名 - * @param mixed $condition 条件 - * @param array $bind 参数绑定 + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param array $bind 参数绑定 * @return $this */ public function rightJoin($join, string $condition = null, array $bind = []) @@ -700,9 +641,9 @@ class Query /** * FULL JOIN * @access public - * @param mixed $join 关联的表名 - * @param mixed $condition 条件 - * @param array $bind 参数绑定 + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param array $bind 参数绑定 * @return $this */ public function fullJoin($join, string $condition = null, array $bind = []) @@ -714,9 +655,9 @@ class Query * 获取Join表名及别名 支持 * ['prefix_table或者子查询'=>'alias'] 'table alias' * @access protected - * @param array|string $join - * @param string $alias - * @return string + * @param array|string|Raw $join JION表名 + * @param string $alias 别名 + * @return string|array */ protected function getJoinTable($join, &$alias = null) { @@ -724,6 +665,8 @@ class Query $table = $join; $alias = array_shift($join); return $table; + } elseif ($join instanceof Raw) { + return $join; } $join = trim($join); @@ -758,8 +701,8 @@ class Query /** * 查询SQL组装 union * @access public - * @param mixed $union - * @param boolean $all + * @param mixed $union UNION + * @param boolean $all 是否适用UNION ALL * @return $this */ public function union($union, bool $all = false) @@ -778,7 +721,7 @@ class Query /** * 查询SQL组装 union all * @access public - * @param mixed $union + * @param mixed $union UNION数据 * @return $this */ public function unionAll($union) @@ -789,18 +732,18 @@ class Query /** * 指定查询字段 支持字段排除和指定数据表 * @access public - * @param mixed $field - * @param boolean $except 是否排除 - * @param string $tableName 数据表名 - * @param string $prefix 字段前缀 - * @param string $alias 别名前缀 + * @param mixed $field 字段信息 + * @param boolean $except 是否排除 + * @param string $tableName 数据表名 + * @param string $prefix 字段前缀 + * @param string $alias 别名前缀 * @return $this */ public function field($field, bool $except = false, string $tableName = '', string $prefix = '', string $alias = '') { if (empty($field)) { return $this; - } elseif ($field instanceof Expression) { + } elseif ($field instanceof Raw) { $this->options['field'][] = $field; return $this; } @@ -848,12 +791,12 @@ class Query /** * 表达式方式指定查询字段 * @access public - * @param string $field 字段名 + * @param string $field 字段名 * @return $this */ public function fieldRaw(string $field) { - $this->options['field'][] = $this->raw($field); + $this->options['field'][] = new Raw($field); return $this; } @@ -861,7 +804,7 @@ class Query /** * 设置数据 * @access public - * @param array $data 数据 + * @param array $data 数据 * @return $this */ public function data(array $data) @@ -874,12 +817,28 @@ class Query /** * 字段值增长 * @access public - * @param string $field 字段名 - * @param integer $step 增长值 + * @param string $field 字段名 + * @param float $step 增长值 + * @param integer $lazyTime 延时时间(s) + * @param string $op INC/DEC * @return $this */ - public function inc(string $field, int $step = 1, string $op = 'INC') + public function inc(string $field, float $step = 1, int $lazyTime = 0, string $op = 'INC') { + if ($lazyTime > 0) { + // 延迟写入 + $condition = $this->options['where'] ?? []; + + $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); + $step = $this->connection->lazyWrite($op, $guid, $step, $lazyTime); + + if (false === $step) { + return $this; + } + + $op = 'INC'; + } + $this->options['data'][$field] = [$op, $step]; return $this; @@ -888,11 +847,12 @@ class Query /** * 字段值减少 * @access public - * @param string $field 字段名 - * @param integer $step 增长值 + * @param string $field 字段名 + * @param float $step 增长值 + * @param integer $lazyTime 延时时间(s) * @return $this */ - public function dec(string $field, int $step = 1) + public function dec(string $field, float $step = 1, int $lazyTime = 0) { return $this->inc($field, $step, $lazyTime, 'DEC'); } @@ -900,34 +860,24 @@ class Query /** * 使用表达式设置数据 * @access public - * @param string $field 字段名 - * @param string $value 字段值 + * @param string $field 字段名 + * @param string $value 字段值 * @return $this */ public function exp(string $field, string $value) { - $this->options['data'][$field] = $this->raw($value); + $this->options['data'][$field] = new Raw($value); return $this; } - /** - * 使用表达式设置数据 - * @access public - * @param string $value 表达式 - * @return Expression - */ - public function raw(string $value): Expression - { - return new Expression($value); - } - /** * 指定JOIN查询字段 * @access public - * @param string|array $table 数据表 - * @param string|array $field 查询字段 - * @param string|array $on JOIN条件 - * @param string $type JOIN类型 + * @param string|array $join 数据表 + * @param string|array $field 查询字段 + * @param string $on JOIN条件 + * @param string $type JOIN类型 + * @param array $bind 参数绑定 * @return $this */ public function view($join, $field = true, $on = null, string $type = 'INNER', array $bind = []) @@ -977,9 +927,9 @@ class Query /** * 指定AND查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 * @return $this */ public function where($field, $op = null, $condition = null) @@ -997,9 +947,9 @@ class Query /** * 指定OR查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 * @return $this */ public function whereOr($field, $op = null, $condition = null) @@ -1012,9 +962,9 @@ class Query /** * 指定XOR查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 * @return $this */ public function whereXor($field, $op = null, $condition = null) @@ -1027,8 +977,8 @@ class Query /** * 指定Null查询条件 * @access public - * @param mixed $field 查询字段 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNull(string $field, string $logic = 'AND') @@ -1039,8 +989,8 @@ class Query /** * 指定NotNull查询条件 * @access public - * @param mixed $field 查询字段 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotNull(string $field, string $logic = 'AND') @@ -1051,14 +1001,14 @@ class Query /** * 指定Exists查询条件 * @access public - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereExists($condition, string $logic = 'AND') { if (is_string($condition)) { - $condition = $this->raw($condition); + $condition = new Raw($condition); } $this->options['where'][strtoupper($logic)][] = ['', 'EXISTS', $condition]; @@ -1068,14 +1018,14 @@ class Query /** * 指定NotExists查询条件 * @access public - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotExists($condition, string $logic = 'AND') { if (is_string($condition)) { - $condition = $this->raw($condition); + $condition = new Raw($condition); } $this->options['where'][strtoupper($logic)][] = ['', 'NOT EXISTS', $condition]; @@ -1085,9 +1035,9 @@ class Query /** * 指定In查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereIn(string $field, $condition, string $logic = 'AND') @@ -1098,9 +1048,9 @@ class Query /** * 指定NotIn查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotIn(string $field, $condition, string $logic = 'AND') @@ -1111,9 +1061,9 @@ class Query /** * 指定Like查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereLike(string $field, $condition, string $logic = 'AND') @@ -1124,9 +1074,9 @@ class Query /** * 指定NotLike查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotLike(string $field, $condition, string $logic = 'AND') @@ -1137,9 +1087,9 @@ class Query /** * 指定Between查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereBetween(string $field, $condition, string $logic = 'AND') @@ -1150,9 +1100,9 @@ class Query /** * 指定NotBetween查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotBetween(string $field, $condition, string $logic = 'AND') @@ -1160,24 +1110,30 @@ class Query return $this->parseWhereExp($logic, $field, 'NOT BETWEEN', $condition, [], true); } + /** + * 指定FIND_IN_SET查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereFindInSet(string $field, $condition, string $logic = 'AND') + { + return $this->parseWhereExp($logic, $field, 'FIND IN SET', $condition, [], true); + } + /** * 比较两个字段 * @access public - * @param string $field1 查询字段 - * @param string $operator 比较操作符 - * @param string $field2 比较字段 - * @param string $logic 查询逻辑 and or xor + * @param string $field1 查询字段 + * @param string $operator 比较操作符 + * @param string $field2 比较字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereColumn(string $field1, string $operator, string $field2 = null, string $logic = 'AND') { - if (is_array($field1)) { - foreach ($field1 as $item) { - $this->whereColumn($item[0], $item[1], isset($item[2]) ? $item[2] : null); - } - return $this; - } - if (is_null($field2)) { $field2 = $operator; $operator = '='; @@ -1189,8 +1145,8 @@ class Query /** * 设置软删除字段及条件 * @access public - * @param string $field 查询字段 - * @param mixed $condition 查询条件 + * @param string $field 查询字段 + * @param mixed $condition 查询条件 * @return $this */ public function useSoftDelete(string $field, $condition = null) @@ -1205,38 +1161,58 @@ class Query /** * 指定Exp查询条件 * @access public - * @param mixed $field 查询字段 - * @param string $where 查询条件 - * @param array $bind 参数绑定 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereExp(string $field, $where, array $bind = [], string $logic = 'AND') + public function whereExp(string $field, string $where, array $bind = [], string $logic = 'AND') { - if ($bind) { + if (!empty($bind)) { $this->bindParams($where, $bind); } - $this->options['where'][$logic][] = [$field, 'EXP', $this->raw($where)]; + $this->options['where'][$logic][] = [$field, 'EXP', new Raw($where)]; return $this; } + /** + * 指定字段Raw查询 + * @access public + * @param string $field 查询字段表达式 + * @param mixed $op 查询表达式 + * @param string $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereFieldRaw(string $field, $op, $condition = null, string $logic = 'AND') + { + if (is_null($condition)) { + $condition = $op; + $op = '='; + } + + $this->options['where'][$logic][] = [new Raw($field), $op, $condition]; + return $this; + } + /** * 指定表达式查询条件 * @access public - * @param string $where 查询条件 - * @param array $bind 参数绑定 - * @param string $logic 查询逻辑 and or xor + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereRaw(string $where, array $bind = [], string $logic = 'AND') { - if ($bind) { + if (!empty($bind)) { $this->bindParams($where, $bind); } - $this->options['where'][$logic][] = $this->raw($where); + $this->options['where'][$logic][] = new Raw($where); return $this; } @@ -1244,8 +1220,8 @@ class Query /** * 指定表达式查询条件 OR * @access public - * @param string $where 查询条件 - * @param array $bind 参数绑定 + * @param string $where 查询条件 + * @param array $bind 参数绑定 * @return $this */ public function whereOrRaw(string $where, array $bind = []) @@ -1256,12 +1232,12 @@ class Query /** * 分析查询表达式 * @access protected - * @param string $logic 查询逻辑 and or xor - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 - * @param array $param 查询参数 - * @param bool $strict 严格模式 + * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @param array $param 查询参数 + * @param bool $strict 严格模式 * @return $this */ protected function parseWhereExp(string $logic, $field, $op, $condition, array $param = [], bool $strict = false) @@ -1272,11 +1248,15 @@ class Query $field = $this->options['via'] . '.' . $field; } - if ($field instanceof Expression) { - return $this->whereRaw($field, is_array($op) ? $op : []); + if ($field instanceof Raw) { + return $this->whereRaw($field, is_array($op) ? $op : [], $logic); } elseif ($strict) { // 使用严格模式查询 - $where = [$field, $op, $condition, $logic]; + if ('=' == $op) { + $where = $this->whereEq($field, $condition); + } else { + $where = [$field, $op, $condition, $logic]; + } } elseif (is_array($field)) { // 解析数组批量查询 return $this->parseArrayWhereItems($field, $logic); @@ -1284,9 +1264,9 @@ class Query $where = $field; } elseif (is_string($field)) { if (preg_match('/[,=\<\'\"\(\s]/', $field)) { - return $this->whereRaw($field, is_array($op) ? $op : []); + return $this->whereRaw($field, is_array($op) ? $op : [], $logic); } elseif (is_string($op) && strtolower($op) == 'exp') { - $bind = isset($param[2]) && is_array($param[2]) ? $param[2] : null; + $bind = isset($param[2]) && is_array($param[2]) ? $param[2] : []; return $this->whereExp($field, $condition, $bind, $logic); } @@ -1303,11 +1283,11 @@ class Query /** * 分析查询表达式 * @access protected - * @param string $logic 查询逻辑 and or xor - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 - * @param array $param 查询参数 + * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @param array $param 查询参数 * @return array */ protected function parseWhereItem(string $logic, $field, $op, $condition, array $param = []): array @@ -1316,24 +1296,39 @@ class Query // 同一字段多条件查询 array_unshift($param, $field); $where = $param; - } elseif (!is_string($op)) { - $where = [$field, '=', $op]; } elseif ($field && is_null($condition)) { - if (in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) { + if (is_string($op) && in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) { // null查询 $where = [$field, $op, '']; - } elseif ('=' == $op) { + } elseif ('=' === $op || is_null($op)) { $where = [$field, 'NULL', '']; - } elseif ('<>' == $op) { + } elseif ('<>' === $op) { $where = [$field, 'NOTNULL', '']; } else { // 字段相等查询 - $where = [$field, '=', $op]; + $where = $this->whereEq($field, $op); } - } elseif (in_array(strtoupper($op), ['REGEXP', 'NOT REGEXP', 'EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) { - $where = [$field, $op, is_string($condition) ? $this->raw($condition) : $condition]; + } elseif (in_array(strtoupper($op), ['EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) { + $where = [$field, $op, is_string($condition) ? new Raw($condition) : $condition]; } else { - $where = $field ? [$field, $op, $condition, isset($param[2]) ? $param[2] : null] : []; + $where = $field ? [$field, $op, $condition, $param[2] ?? null] : []; + } + + return $where; + } + + /** + * 相等查询的主键处理 + * @access protected + * @param string $field 字段名 + * @param mixed $value 字段值 + * @return array + */ + protected function whereEq(string $field, $value): array + { + $where = [$field, '=', $value]; + if ($this->getPk() == $field) { + $this->options['key'] = $value; } return $where; @@ -1342,8 +1337,8 @@ class Query /** * 数组批量查询 * @access protected - * @param array $field 批量查询 - * @param string $logic 查询逻辑 and or xor + * @param array $field 批量查询 + * @param string $logic 查询逻辑 and or xor * @return $this */ protected function parseArrayWhereItems(array $field, string $logic) @@ -1351,7 +1346,7 @@ class Query if (key($field) !== 0) { $where = []; foreach ($field as $key => $val) { - if ($val instanceof Expression) { + if ($val instanceof Raw) { $where[] = [$key, 'exp', $val]; } else { $where[] = is_null($val) ? [$key, 'NULL', ''] : [$key, is_array($val) ? 'IN' : '=', $val]; @@ -1372,8 +1367,8 @@ class Query /** * 去除某个查询条件 * @access public - * @param string $field 查询字段 - * @param string $logic 查询逻辑 and or xor + * @param string $field 查询字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function removeWhereField(string $field, string $logic = 'AND') @@ -1394,13 +1389,14 @@ class Query /** * 去除查询参数 * @access public - * @param string $option 参数名 留空去除所有参数 + * @param string $option 参数名 留空去除所有参数 * @return $this */ public function removeOption(string $option = '') { if ('' === $option) { $this->options = []; + $this->bind = []; } elseif (isset($this->options[$option])) { unset($this->options[$option]); } @@ -1411,9 +1407,9 @@ class Query /** * 条件查询 * @access public - * @param mixed $condition 满足条件(支持闭包) - * @param Closure|array $query 满足条件后执行的查询表达式(闭包或数组) - * @param Closure|array $otherwise 不满足条件后执行 + * @param mixed $condition 满足条件(支持闭包) + * @param Closure|array $query 满足条件后执行的查询表达式(闭包或数组) + * @param Closure|array $otherwise 不满足条件后执行 * @return $this */ public function when($condition, $query, $otherwise = null) @@ -1442,8 +1438,8 @@ class Query /** * 指定查询数量 * @access public - * @param int $offset 起始位置 - * @param int $length 查询数量 + * @param int $offset 起始位置 + * @param int $length 查询数量 * @return $this */ public function limit(int $offset, int $length = null) @@ -1456,8 +1452,8 @@ class Query /** * 指定分页 * @access public - * @param int $page 页数 - * @param int $listRows 每页数量 + * @param int $page 页数 + * @param int $listRows 每页数量 * @return $this */ public function page(int $page, int $listRows = null) @@ -1470,17 +1466,10 @@ class Query /** * 分页查询 * @access public - * @param int|array $listRows 每页数量 数组表示配置参数 - * @param int|bool $simple 是否简洁模式或者总记录数 - * @param array $config 配置参数 - * page:当前页, - * path:url路径, - * query:url额外参数, - * fragment:url锚点, - * var_page:分页变量, - * list_rows:每页数量 - * type:分页类名 - * @return \think\Paginator + * @param int|array $listRows 每页数量 数组表示配置参数 + * @param int|bool $simple 是否简洁模式或者总记录数 + * @param array $config 配置参数 + * @return Paginator * @throws DbException */ public function paginate($listRows = null, $simple = false, $config = []) @@ -1490,17 +1479,15 @@ class Query $simple = false; } - $paginate = Db::getConfig('paginate'); - + $defaultConfig = $this->getConfig('paginate'); if (is_array($listRows)) { - $config = array_merge($paginate, $listRows); + $config = array_merge($defaultConfig, $listRows); $listRows = intval($config['list_rows']); } else { - $config = array_merge($paginate, $config); + $config = array_merge($defaultConfig, $config); $listRows = intval($listRows ?: $config['list_rows']); } - /** @var Paginator $class */ $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\paginator\\driver\\' . ucwords($config['type']); $page = isset($config['page']) ? (int) $config['page'] : call_user_func([ $class, @@ -1535,12 +1522,12 @@ class Query /** * 表达式方式指定当前操作的数据表 * @access public - * @param mixed $table 表名 + * @param mixed $table 表名 * @return $this */ public function tableRaw(string $table) { - $this->options['table'] = $this->raw($table); + $this->options['table'] = new Raw($table); return $this; } @@ -1548,7 +1535,7 @@ class Query /** * 指定当前操作的数据表 * @access public - * @param mixed $table 表名 + * @param mixed $table 表名 * @return $this */ public function table($table) @@ -1556,6 +1543,13 @@ class Query if (is_string($table)) { if (strpos($table, ')')) { // 子查询 + } elseif (false === strpos($table, ',')) { + if (strpos($table, ' ')) { + list($item, $alias) = explode(' ', $table); + $table = []; + $this->alias([$item => $alias]); + $table[$item] = $alias; + } } else { $tables = explode(',', $table); $table = []; @@ -1571,7 +1565,7 @@ class Query } } } - } else { + } elseif (is_array($table)) { $tables = $table; $table = []; @@ -1593,7 +1587,7 @@ class Query /** * USING支持 用于多表删除 * @access public - * @param mixed $using + * @param mixed $using USING * @return $this */ public function using($using) @@ -1605,27 +1599,39 @@ class Query /** * 存储过程调用 * @access public - * @param bool $procedure + * @param bool $procedure 是否为存储过程查询 * @return $this */ - public function procedure($procedure = true) + public function procedure(bool $procedure = true) { $this->options['procedure'] = $procedure; return $this; } + /** + * 是否允许返回空数据(或空模型) + * @access public + * @param bool $allowEmpty 是否允许为空 + * @return $this + */ + public function allowEmpty(bool $allowEmpty = true) + { + $this->options['allow_empty'] = $allowEmpty; + return $this; + } + /** * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc']) * @access public - * @param string|array $field 排序字段 - * @param string $order 排序 + * @param string|array|Raw $field 排序字段 + * @param string $order 排序 * @return $this */ public function order($field, string $order = '') { if (empty($field)) { return $this; - } elseif ($field instanceof Expression) { + } elseif ($field instanceof Raw) { $this->options['order'][] = $field; return $this; } @@ -1666,17 +1672,17 @@ class Query /** * 表达式方式指定Field排序 * @access public - * @param string $field 排序字段 - * @param array $bind 参数绑定 + * @param string $field 排序字段 + * @param array $bind 参数绑定 * @return $this */ public function orderRaw(string $field, array $bind = []) { - if ($bind) { + if (!empty($bind)) { $this->bindParams($field, $bind); } - $this->options['order'][] = $this->raw($field); + $this->options['order'][] = new Raw($field); return $this; } @@ -1684,9 +1690,9 @@ class Query /** * 指定Field排序 orderField('id',[1,2,3],'desc') * @access public - * @param string $field 排序字段 - * @param array $values 排序值 - * @param string $order + * @param string $field 排序字段 + * @param array $values 排序值 + * @param string $order 排序 desc/asc * @return $this */ public function orderField(string $field, array $values, string $order = '') @@ -1714,31 +1720,23 @@ class Query /** * 查询缓存 * @access public - * @param mixed $key 缓存key - * @param integer|\DateTime $expire 缓存有效期 - * @param string $tag 缓存标签 + * @param mixed $key 缓存key + * @param integer|\DateTime $expire 缓存有效期 + * @param string $tag 缓存标签 * @return $this */ - public function cache($key = true, $expire = null, $tag = null) + public function cache($key = true, $expire = null, string $tag = null) { if (false === $key) { return $this; } - if ($key instanceof CacheItem) { - $cacheItem = $key; - } else { - if ($key instanceof \DateTimeInterface || (is_int($key) && is_null($expire))) { - $expire = $key; - $key = true; - } - - $cacheItem = new CacheItem(true === $key ? null : $key); - $cacheItem->expire($expire); - $cacheItem->tag($tag); + if ($key instanceof \DateTimeInterface || (is_int($key) && is_null($expire))) { + $expire = $key; + $key = true; } - $this->options['cache'] = $cacheItem; + $this->options['cache'] = [$key, $expire, $tag]; return $this; } @@ -1746,7 +1744,7 @@ class Query /** * 指定group查询 * @access public - * @param string|array $group GROUP + * @param string|array $group GROUP * @return $this */ public function group($group) @@ -1758,7 +1756,7 @@ class Query /** * 指定having查询 * @access public - * @param string $having having + * @param string $having having * @return $this */ public function having(string $having) @@ -1770,7 +1768,7 @@ class Query /** * 指定查询lock * @access public - * @param bool|string $lock 是否lock + * @param bool|string $lock 是否lock * @return $this */ public function lock($lock = false) @@ -1787,7 +1785,7 @@ class Query /** * 指定distinct查询 * @access public - * @param bool $distinct 是否唯一 + * @param bool $distinct 是否唯一 * @return $this */ public function distinct(bool $distinct = true) @@ -1799,7 +1797,7 @@ class Query /** * 指定数据表别名 * @access public - * @param array|string $alias 数据表别名 + * @param array|string $alias 数据表别名 * @return $this */ public function alias($alias) @@ -1818,7 +1816,7 @@ class Query /** * 指定强制索引 * @access public - * @param string $force 索引名称 + * @param string $force 索引名称 * @return $this */ public function force(string $force) @@ -1830,7 +1828,7 @@ class Query /** * 查询注释 * @access public - * @param string $comment 注释 + * @param string $comment 注释 * @return $this */ public function comment(string $comment) @@ -1842,7 +1840,7 @@ class Query /** * 获取执行的SQL语句而不进行实际的查询 * @access public - * @param bool $fetch 是否返回sql + * @param bool $fetch 是否返回sql * @return $this|Fetch */ public function fetchSql(bool $fetch = true) @@ -1859,7 +1857,7 @@ class Query /** * 设置是否返回数据集对象 * @access public - * @param bool|string $collection 是否返回数据集对象 + * @param bool|string $collection 是否返回数据集对象 * @return $this */ public function fetchCollection($collection = true) @@ -1871,21 +1869,19 @@ class Query /** * 设置是否返回数组 * @access public - * @param bool $asArray 是否返回数组 + * @param bool $asArray 是否返回数组 * @return $this */ public function fetchArray(bool $asArray = true) { - if ($asArray) { - $this->model = null; - } + $this->options['array'] = $asArray; return $this; } /** * 设置从主服务器读取数据 * @access public - * @param bool $readMaster 是否从主服务器读取 + * @param bool $readMaster 是否从主服务器读取 * @return $this */ public function master(bool $readMaster = true) @@ -1897,7 +1893,7 @@ class Query /** * 设置是否严格检查字段名 * @access public - * @param bool $strict 是否严格检查字段 + * @param bool $strict 是否严格检查字段 * @return $this */ public function strict(bool $strict = true) @@ -1909,7 +1905,7 @@ class Query /** * 设置查询数据不存在是否抛出异常 * @access public - * @param bool $fail 数据不存在是否抛出异常 + * @param bool $fail 数据不存在是否抛出异常 * @return $this */ public function failException(bool $fail = true) @@ -1921,7 +1917,7 @@ class Query /** * 设置自增序列名 * @access public - * @param string $sequence 自增序列名 + * @param string $sequence 自增序列名 * @return $this */ public function sequence(string $sequence = null) @@ -1930,10 +1926,58 @@ class Query return $this; } + /** + * 设置是否REPLACE + * @access public + * @param bool $replace 是否使用REPLACE写入数据 + * @return $this + */ + public function replace(bool $replace = true) + { + $this->options['replace'] = $replace; + return $this; + } + + /** + * 设置当前查询所在的分区 + * @access public + * @param string|array $partition 分区名称 + * @return $this + */ + public function partition($partition) + { + $this->options['partition'] = $partition; + return $this; + } + + /** + * 设置DUPLICATE + * @access public + * @param array|string|Raw $duplicate DUPLICATE信息 + * @return $this + */ + public function duplicate($duplicate) + { + $this->options['duplicate'] = $duplicate; + return $this; + } + + /** + * 设置查询的额外参数 + * @access public + * @param string $extra 额外信息 + * @return $this + */ + public function extra(string $extra) + { + $this->options['extra'] = $extra; + return $this; + } + /** * 设置需要隐藏的输出属性 * @access public - * @param array $hidden 需要隐藏的字段名 + * @param array $hidden 需要隐藏的字段名 * @return $this */ public function hidden(array $hidden) @@ -1945,7 +1989,7 @@ class Query /** * 设置需要输出的属性 * @access public - * @param array $visible 需要输出的属性 + * @param array $visible 需要输出的属性 * @return $this */ public function visible(array $visible) @@ -1957,7 +2001,7 @@ class Query /** * 设置需要追加输出的属性 * @access public - * @param array $append 需要追加的属性 + * @param array $append 需要追加的属性 * @return $this */ public function append(array $append) @@ -1969,8 +2013,8 @@ class Query /** * 设置JSON字段信息 * @access public - * @param array $json JSON字段 - * @param bool $assoc 是否取出数组 + * @param array $json JSON字段 + * @param bool $assoc 是否取出数组 * @return $this */ public function json(array $json = [], bool $assoc = false) @@ -1983,8 +2027,8 @@ class Query /** * 添加查询范围 * @access public - * @param array|string|Closure $scope 查询范围定义 - * @param array $args 参数 + * @param array|string|Closure $scope 查询范围定义 + * @param array $args 参数 * @return $this */ public function scope($scope, ...$args) @@ -2018,7 +2062,7 @@ class Query /** * 指定数据表主键 * @access public - * @param string $pk 主键 + * @param string $pk 主键 * @return $this */ public function pk(string $pk) @@ -2030,8 +2074,8 @@ class Query /** * 添加日期或者时间查询规则 * @access public - * @param string $name 时间表达式 - * @param string|array $rule 时间范围 + * @param string $name 时间表达式 + * @param string|array $rule 时间范围 * @return $this */ public function timeRule(string $name, $rule) @@ -2043,30 +2087,17 @@ class Query /** * 查询日期或者时间 * @access public - * @param string $field 日期字段名 - * @param string|array $op 比较运算符或者表达式 - * @param string|array $range 比较范围 - * @param string $logic AND OR + * @param string $field 日期字段名 + * @param string $op 比较运算符或者表达式 + * @param string|array $range 比较范围 + * @param string $logic AND OR * @return $this */ - public function whereTime(string $field, $op, $range = null, string $logic = 'AND') + public function whereTime(string $field, string $op, $range = null, string $logic = 'AND') { - if (is_null($range)) { - if (is_array($op)) { - $range = $op; - } else { - if (isset($this->timeExp[strtolower($op)])) { - $op = $this->timeExp[strtolower($op)]; - } - - if (isset($this->timeRule[strtolower($op)])) { - $range = $this->timeRule[strtolower($op)]; - } else { - $range = $op; - } - } - - $op = is_array($range) ? 'between' : '>='; + if (is_null($range) && isset($this->timeRule[$op])) { + $range = $this->timeRule[$op]; + $op = 'between'; } return $this->parseWhereExp($logic, $field, strtolower($op) . ' time', $range, [], true); @@ -2075,83 +2106,104 @@ class Query /** * 查询某个时间间隔数据 * @access protected - * @param string $field 日期字段名 - * @param string $start 开始时间 - * @param string $interval 时间间隔单位 - * @param string $logic AND OR + * @param string $field 日期字段名 + * @param string $start 开始时间 + * @param string $interval 时间间隔单位 + * @param string $logic AND OR * @return $this */ - protected function whereTimeInterval(string $field, string $start, $interval = 'day', string $logic = 'AND') + protected function whereTimeInterval(string $field, string $start, string $interval = 'day', string $logic = 'AND') { $startTime = strtotime($start); $endTime = strtotime('+1 ' . $interval, $startTime); - return $this->parseWhereExp($logic, $field, 'between time', [$startTime, $endTime], [], true); + return $this->whereTime($field, 'between', [$startTime, $endTime], $logic); } /** * 查询月数据 whereMonth('time_field', '2018-1') * @access public - * @param string $field 日期字段名 - * @param string $month 月份信息 - * @param string $logic AND OR + * @param string $field 日期字段名 + * @param string $month 月份信息 + * @param string $logic AND OR * @return $this */ - public function whereMonth(string $field, string $month, string $logic = 'AND') + public function whereMonth(string $field, string $month = 'this month', string $logic = 'AND') { + if (in_array($month, ['this month', 'last month'])) { + $month = date('Y-m', strtotime($month)); + } + return $this->whereTimeInterval($field, $month, 'month', $logic); } /** * 查询年数据 whereYear('time_field', '2018') * @access public - * @param string $field 日期字段名 - * @param string $year 年份信息 - * @param string $logic AND OR + * @param string $field 日期字段名 + * @param string $year 年份信息 + * @param string $logic AND OR * @return $this */ - public function whereYear(string $field, string $year, string $logic = 'AND') + public function whereYear(string $field, string $year = 'this year', string $logic = 'AND') { + if (in_array($year, ['this year', 'last year'])) { + $year = date('Y', strtotime($year)); + } + return $this->whereTimeInterval($field, $year . '-1-1', 'year', $logic); } /** * 查询日数据 whereDay('time_field', '2018-1-1') * @access public - * @param string $field 日期字段名 - * @param string $day 日期信息 - * @param string $logic AND OR + * @param string $field 日期字段名 + * @param string $day 日期信息 + * @param string $logic AND OR * @return $this */ - public function whereDay(string $field, string $day, string $logic = 'AND') + public function whereDay(string $field, string $day = 'today', string $logic = 'AND') { + if (in_array($day, ['today', 'yesterday'])) { + $day = date('Y-m-d', strtotime($day)); + } + return $this->whereTimeInterval($field, $day, 'day', $logic); } /** * 查询日期或者时间范围 whereBetweenTime('time_field', '2018-1-1','2018-1-15') * @access public - * @param string $field 日期字段名 - * @param string $startTime 开始时间 - * @param string $endTime 结束时间 - * @param string $logic AND OR + * @param string $field 日期字段名 + * @param string|int $startTime 开始时间 + * @param string|int $endTime 结束时间 + * @param string $logic AND OR * @return $this */ - public function whereBetweenTime(string $field, $startTime, $endTime = null, string $logic = 'AND') + public function whereBetweenTime(string $field, $startTime, $endTime, string $logic = 'AND') { - if (is_null($endTime)) { - $time = is_string($startTime) ? strtotime($startTime) : $startTime; - $endTime = strtotime('+1 day', $time); - } + return $this->whereTime($field, 'between', [$startTime, $endTime], $logic); + } - return $this->parseWhereExp($logic, $field, 'between time', [$startTime, $endTime], [], true); + /** + * 查询日期或者时间范围 whereNotBetweenTime('time_field', '2018-1-1','2018-1-15') + * @access public + * @param string $field 日期字段名 + * @param string|int $startTime 开始时间 + * @param string|int $endTime 结束时间 + * @return $this + */ + public function whereNotBetweenTime(string $field, $startTime, $endTime) + { + return $this->whereTime($field, '<', $startTime) + ->whereTime($field, '>', $endTime); } /** * 查询当前时间在两个时间字段范围 whereBetweenTimeField('start_time', 'end_time') * @access public - * @param string $startField 开始时间字段 - * @param string $endField 结束时间字段 + * @param string $startField 开始时间字段 + * @param string $endField 结束时间字段 * @return $this */ public function whereBetweenTimeField(string $startField, string $endField) @@ -2163,8 +2215,8 @@ class Query /** * 查询当前时间不在两个时间字段范围 whereNotBetweenTimeField('start_time', 'end_time') * @access public - * @param string $startField 开始时间字段 - * @param string $endField 结束时间字段 + * @param string $startField 开始时间字段 + * @param string $endField 结束时间字段 * @return $this */ public function whereNotBetweenTimeField(string $startField, string $endField) @@ -2176,46 +2228,51 @@ class Query /** * 获取当前数据表的主键 * @access public - * @param string|array $options 数据表名或者查询参数 * @return string|array */ - public function getPk($options = '') + public function getPk() { if (!empty($this->pk)) { $pk = $this->pk; } else { - $pk = $this->connection->getPk(is_array($options) && isset($options['table']) ? $options['table'] : $this->getTable()); + $this->pk = $pk = $this->connection->getPk($this->getTable()); } return $pk; } /** - * 参数绑定 + * 批量参数绑定 * @access public - * @param mixed $value 绑定变量值 - * @param integer $type 绑定类型 - * @param string $name 绑定标识 - * @return $this|string + * @param array $value 绑定变量值 + * @return $this */ - public function bind($value, int $type = PDO::PARAM_STR, string $name = null) + public function bind(array $value) { - if (is_array($value)) { - $this->bind = array_merge($this->bind, $value); - } else { - $name = $name ?: 'ThinkBind_' . (count($this->bind) + 1) . '_'; + $this->bind = array_merge($this->bind, $value); + return $this; + } - $this->bind[$name] = [$value, $type]; - return $name; - } + /** + * 单个参数绑定 + * @access public + * @param mixed $value 绑定变量值 + * @param integer $type 绑定类型 + * @param string $name 绑定标识 + * @return string + */ + public function bindValue($value, int $type = null, string $name = null) + { + $name = $name ?: 'ThinkBind_' . (count($this->bind) + 1) . '_'; - return $this; + $this->bind[$name] = [$value, $type ?: PDO::PARAM_STR]; + return $name; } /** * 检测参数是否已经绑定 * @access public - * @param string $key 参数名 + * @param string $key 参数名 * @return bool */ public function isBind($key) @@ -2226,17 +2283,17 @@ class Query /** * 参数绑定 * @access public - * @param string $sql 绑定的sql表达式 - * @param array $bind 参数绑定 + * @param string $sql 绑定的sql表达式 + * @param array $bind 参数绑定 * @return void */ protected function bindParams(string &$sql, array $bind = []): void { foreach ($bind as $key => $value) { if (is_array($value)) { - $name = $this->bind($value[0], $value[1], $value[2] ?? null); + $name = $this->bindValue($value[0], $value[1], $value[2] ?? null); } else { - $name = $this->bind($value); + $name = $this->bindValue($value); } if (is_numeric($key)) { @@ -2250,7 +2307,7 @@ class Query /** * 查询参数批量赋值 * @access protected - * @param array $options 表达式参数 + * @param array $options 表达式参数 * @return $this */ protected function options(array $options) @@ -2262,7 +2319,7 @@ class Query /** * 获取当前的查询参数 * @access public - * @param string $name 参数名 + * @param string $name 参数名 * @return mixed */ public function getOptions(string $name = '') @@ -2277,8 +2334,8 @@ class Query /** * 设置当前的查询参数 * @access public - * @param string $option 参数名 - * @param mixed $value 参数值 + * @param string $option 参数名 + * @param mixed $value 参数值 * @return $this */ public function setOption(string $option, $value) @@ -2290,12 +2347,12 @@ class Query /** * 设置关联查询 * @access public - * @param array $relation 关联名称 + * @param array $relation 关联名称 * @return $this */ public function relation(array $relation) { - if ($relation) { + if (!empty($relation)) { $this->options['relation'] = $relation; } @@ -2305,13 +2362,13 @@ class Query /** * 设置关联查询JOIN预查询 * @access public - * @param array $with 关联方法名称(数组) + * @param array|string $with 关联方法名称 * @return $this */ - public function with(array $with) + public function with($with) { - if ($with) { - $this->options['with'] = $with; + if (!empty($with)) { + $this->options['with'] = (array) $with; } return $this; @@ -2320,11 +2377,11 @@ class Query /** * 关联预载入 JOIN方式 * @access protected - * @param array $with 关联方法名 - * @param string $joinType JOIN方式 + * @param array|string $with 关联方法名 + * @param string $joinType JOIN方式 * @return $this */ - public function withJoin(array $with, string $joinType = '') + public function withJoin($with, string $joinType = '') { if (empty($with)) { return $this; @@ -2334,7 +2391,7 @@ class Query /** @var Model $class */ $class = $this->model; - foreach ($with as $key => $relation) { + foreach ((array) $with as $key => $relation) { $closure = null; $field = true; @@ -2350,7 +2407,7 @@ class Query } /** @var Relation $model */ - $relation = Db::parseName($relation, 1, false); + $relation = $this->db->parseName($relation, 1, false); $model = $class->$relation(); if ($model instanceof OneToOne) { @@ -2372,26 +2429,17 @@ class Query /** * 设置数据字段获取器 * @access public - * @param string $name 字段名 - * @param callable $callback 闭包获取器 - * @return $this - */ - public function withAttr(string $name, callable $callback) - { - $this->options['with_attr'][$name] = $callback; - - return $this; - } - - /** - * 设置数据字段获取器 - * @access public - * @param array $attrs 字段获取器 + * @param string|array $name 字段名 + * @param callable $callback 闭包获取器 * @return $this */ - public function withAttrs(array $attrs) + public function withAttr($name, callable $callback) { - $this->options['with_attr'] = $attrs; + if (is_array($name)) { + $this->options['with_attr'] = $name; + } else { + $this->options['with_attr'][$name] = $callback; + } return $this; } @@ -2399,9 +2447,9 @@ class Query /** * 使用搜索器条件搜索字段 * @access public - * @param array $fields 搜索字段 - * @param array $data 搜索数据 - * @param string $prefix 字段前缀标识 + * @param array $fields 搜索字段 + * @param array $data 搜索数据 + * @param string $prefix 字段前缀标识 * @return $this */ public function withSearch(array $fields, array $data = [], string $prefix = '') @@ -2412,7 +2460,7 @@ class Query } elseif ($this->model) { // 检测搜索器 $fieldName = is_numeric($key) ? $field : $key; - $method = 'search' . Db::parseName($fieldName, 1) . 'Attr'; + $method = 'search' . $this->db->parseName($fieldName, 1) . 'Attr'; if (method_exists($this->model, $method)) { $this->model->$method($this, $data[$field] ?? null, $data, $prefix); @@ -2426,18 +2474,14 @@ class Query /** * 关联统计 * @access protected - * @param array $relations 关联方法名 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @param bool $subQuery 是否使用子查询 + * @param array|string $relations 关联方法名 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 * @return $this */ protected function withAggregate($relations, string $aggregate = 'count', $field = '*', bool $subQuery = true) { - if (is_string($relations)) { - $relations = explode(',', $relations); - } - if (!$subQuery) { $this->options['with_count'][] = [$relations, $aggregate, $field]; } else { @@ -2445,7 +2489,7 @@ class Query $this->field('*'); } - foreach ($relations as $key => $relation) { + foreach ((array) $relations as $key => $relation) { $closure = $aggregateField = null; if ($relation instanceof Closure) { @@ -2456,12 +2500,12 @@ class Query $relation = $key; } - $relation = Db::parseName($relation, 1, false); + $relation = $this->db->parseName($relation, 1, false); $count = '(' . $this->model->$relation()->getRelationCountQuery($closure, $aggregate, $field, $aggregateField) . ')'; if (empty($aggregateField)) { - $aggregateField = Db::parseName($relation) . '_' . $aggregate; + $aggregateField = $this->db->parseName($relation) . '_' . $aggregate; } $this->field([$count => $aggregateField]); @@ -2474,8 +2518,8 @@ class Query /** * 关联统计 * @access public - * @param string|array $relation 关联方法名 - * @param bool $subQuery 是否使用子查询 + * @param string|array $relation 关联方法名 + * @param bool $subQuery 是否使用子查询 * @return $this */ public function withCount($relation, bool $subQuery = true) @@ -2486,9 +2530,9 @@ class Query /** * 关联统计Sum * @access public - * @param string|array $relation 关联方法名 - * @param string $field 字段 - * @param bool $subQuery 是否使用子查询 + * @param string|array $relation 关联方法名 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 * @return $this */ public function withSum($relation, string $field, bool $subQuery = true) @@ -2499,9 +2543,9 @@ class Query /** * 关联统计Max * @access public - * @param string|array $relation 关联方法名 - * @param string $field 字段 - * @param bool $subQuery 是否使用子查询 + * @param string|array $relation 关联方法名 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 * @return $this */ public function withMax($relation, string $field, bool $subQuery = true) @@ -2512,9 +2556,9 @@ class Query /** * 关联统计Min * @access public - * @param string|array $relation 关联方法名 - * @param string $field 字段 - * @param bool $subQuery 是否使用子查询 + * @param string|array $relation 关联方法名 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 * @return $this */ public function withMin($relation, string $field, bool $subQuery = true) @@ -2525,9 +2569,9 @@ class Query /** * 关联统计Avg * @access public - * @param string|array $relation 关联方法名 - * @param string $field 字段 - * @param bool $subQuery 是否使用子查询 + * @param string|array $relation 关联方法名 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 * @return $this */ public function withAvg($relation, string $field, bool $subQuery = true) @@ -2543,7 +2587,7 @@ class Query * }]) * * @access public - * @param string | array $field 指定获取的字段 + * @param string|array $field 指定获取的字段 * @return $this */ public function withField($field) @@ -2556,7 +2600,7 @@ class Query /** * 设置当前字段添加的表别名 * @access public - * @param string $via + * @param string $via 临时表别名 * @return $this */ public function via(string $via = '') @@ -2567,60 +2611,81 @@ class Query } /** - * 插入记录 + * 保存记录 自动判断insert或者update * @access public - * @param array $data 数据 - * @param boolean $replace 是否replace - * @param boolean $getLastInsID 返回自增主键 - * @param string $sequence 自增序列名 + * @param array $data 数据 + * @param bool $forceInsert 是否强制insert * @return integer */ - public function insert(array $data = [], bool $replace = false, bool $getLastInsID = false, string $sequence = null) + public function save(array $data = [], bool $forceInsert = false) { + if ($forceInsert) { + return $this->insert($data); + } + $this->options['data'] = array_merge($this->options['data'] ?? [], $data); - return $this->connection->insert($this, $replace, $getLastInsID, $sequence); + if (!empty($this->options['where'])) { + $isUpdate = true; + } else { + $isUpdate = $this->parseUpdateData($this->options['data']); + } + + return $isUpdate ? $this->update() : $this->insert(); + } + + /** + * 插入记录 + * @access public + * @param array $data 数据 + * @param boolean $getLastInsID 返回自增主键 + * @return integer|string + */ + public function insert(array $data = [], bool $getLastInsID = false) + { + if (!empty($data)) { + $this->options['data'] = $data; + } + + return $this->connection->insert($this, $getLastInsID); } /** * 插入记录并获取自增ID * @access public - * @param array $data 数据 - * @param boolean $replace 是否replace - * @param string $sequence 自增序列名 + * @param array $data 数据 * @return integer|string */ - public function insertGetId(array $data, bool $replace = false, string $sequence = null) + public function insertGetId(array $data) { - return $this->insert($data, $replace, true, $sequence); + return $this->insert($data, true); } /** * 批量插入记录 * @access public - * @param array $dataSet 数据集 - * @param boolean $replace 是否replace - * @param integer $limit 每次写入数据限制 + * @param array $dataSet 数据集 + * @param integer $limit 每次写入数据限制 * @return integer */ - public function insertAll(array $dataSet = [], bool $replace = false, int $limit = null): int + public function insertAll(array $dataSet = [], int $limit = 0): int { if (empty($dataSet)) { $dataSet = $this->options['data'] ?? []; } - if (empty($limit) && !empty($this->options['limit'])) { + if (empty($limit) && !empty($this->options['limit']) && is_numeric($this->options['limit'])) { $limit = (int) $this->options['limit']; } - return $this->connection->insertAll($this, $dataSet, $replace, $limit); + return $this->connection->insertAll($this, $dataSet, $limit); } /** * 通过Select方式插入记录 * @access public - * @param array $fields 要插入的数据表字段名 - * @param string $table 要插入的数据表名 + * @param array $fields 要插入的数据表字段名 + * @param string $table 要插入的数据表名 * @return integer * @throws PDOException */ @@ -2632,14 +2697,29 @@ class Query /** * 更新记录 * @access public - * @param mixed $data 数据 + * @param mixed $data 数据 * @return integer * @throws Exception * @throws PDOException */ public function update(array $data = []): int { - $this->options['data'] = array_merge($this->options['data'] ?? [], $data); + if (!empty($data)) { + $this->options['data'] = array_merge($this->options['data'] ?? [], $data); + } + + if (empty($this->options['where'])) { + $this->parseUpdateData($this->options['data']); + } + + if (empty($this->options['where']) && $this->model) { + $this->where($this->model->getWhere()); + } + + if (empty($this->options['where'])) { + // 如果没有任何更新条件则不执行 + throw new Exception('miss update condition'); + } return $this->connection->update($this); } @@ -2647,7 +2727,7 @@ class Query /** * 删除记录 * @access public - * @param mixed $data 表达式 true 表示强制删除 + * @param mixed $data 表达式 true 表示强制删除 * @return int * @throws Exception * @throws PDOException @@ -2659,6 +2739,15 @@ class Query $this->parsePkWhere($data); } + if (empty($this->options['where']) && $this->model) { + $this->where($this->model->getWhere()); + } + + if (true !== $data && empty($this->options['where'])) { + // 如果条件为空 不进行删除操作 除非设置 1=1 + throw new Exception('delete without condition'); + } + if (!empty($this->options['soft_delete'])) { // 软删除 list($field, $condition) = $this->options['soft_delete']; @@ -2688,7 +2777,7 @@ class Query /** * 使用游标查找记录 * @access public - * @param mixed $data + * @param mixed $data 数据 * @return \Generator */ public function cursor($data = null) @@ -2708,7 +2797,7 @@ class Query /** * 查找记录 * @access public - * @param mixed $data + * @param mixed $data 数据 * @return Collection|array|ModelCollection * @throws DbException * @throws ModelNotFoundException @@ -2721,17 +2810,15 @@ class Query $this->parsePkWhere($data); } - $this->options['data'] = $data; - $resultSet = $this->connection->select($this); // 返回结果处理 if (!empty($this->options['fail']) && count($resultSet) == 0) { - $this->throwNotFound($this->options); + $this->throwNotFound(); } // 数据列表读取后的处理 - if (!empty($this->model)) { + if (!empty($this->model) && empty($this->options['array'])) { // 生成模型对象 $resultSet = $this->resultSetToModelCollection($resultSet); } else { @@ -2744,7 +2831,7 @@ class Query /** * 查询数据转换为模型数据集对象 * @access protected - * @param array $resultSet 数据集 + * @param array $resultSet 数据集 * @return ModelCollection */ protected function resultSetToModelCollection(array $resultSet): ModelCollection @@ -2793,7 +2880,7 @@ class Query /** * 处理数据集 * @access public - * @param array $resultSet + * @param array $resultSet 数据集 * @return void */ protected function resultSet(array &$resultSet): void @@ -2825,8 +2912,8 @@ class Query /** * 查找单条记录 * @access public - * @param mixed $data - * @return array|null|Model + * @param mixed $data 查询数据 + * @return array|Model|null * @throws DbException * @throws ModelNotFoundException * @throws DataNotFoundException @@ -2838,8 +2925,6 @@ class Query $this->parsePkWhere($data); } - $this->options['data'] = $data; - $result = $this->connection->find($this); // 数据处理 @@ -2847,7 +2932,7 @@ class Query return $this->resultToEmpty(); } - if (!empty($this->model)) { + if (!empty($this->model) && empty($this->options['array'])) { // 返回模型对象 $this->resultToModel($result, $this->options); } else { @@ -2857,6 +2942,17 @@ class Query return $result; } + /** + * 查找单条记录 不存在返回空数据(或者空模型) + * @access public + * @param mixed $data 数据 + * @return array|Model + */ + public function findOrEmpty($data = null) + { + return $this->allowEmpty(true)->find($data); + } + /** * 处理空数据 * @access protected @@ -2868,16 +2964,18 @@ class Query protected function resultToEmpty() { if (!empty($this->options['fail'])) { - $this->throwNotFound($this->options); + $this->throwNotFound(); + } elseif (!empty($this->options['allow_empty'])) { + return !empty($this->model) && empty($this->options['array']) ? $this->model->newInstance()->setQuery($this) : []; + } elseif (!empty($this->options['array'])) { + return []; } - - return !empty($this->model) ? $this->model->newInstance([], true) : []; } /** * 获取模型的更新条件 * @access protected - * @param array $options 查询参数 + * @param array $options 查询参数 */ protected function getModelUpdateCondition(array $options) { @@ -2887,7 +2985,7 @@ class Query /** * 处理数据 * @access protected - * @param array $result 查询数据 + * @param array $result 查询数据 * @return void */ protected function result(array &$result): void @@ -2906,7 +3004,7 @@ class Query /** * 处理数据的可见和隐藏 * @access protected - * @param array $result 查询数据 + * @param array $result 查询数据 * @return void */ protected function filterResult(&$result): void @@ -2927,14 +3025,14 @@ class Query /** * 使用获取器处理数据 * @access protected - * @param array $result 查询数据 - * @param array $withAttr 字段获取器 + * @param array $result 查询数据 + * @param array $withAttr 字段获取器 * @return void */ protected function getResultAttr(array &$result, array $withAttr = []): void { foreach ($withAttr as $name => $closure) { - $name = Db::parseName($name); + $name = $this->db->parseName($name); if (strpos($name, '.')) { // 支持JSON字段 获取器定义 @@ -2952,10 +3050,10 @@ class Query /** * JSON字段数据转换 * @access protected - * @param array $result 查询数据 - * @param array $json JSON字段 - * @param bool $assoc 是否转换为数组 - * @param array $withRelationAttr 关联获取器 + * @param array $result 查询数据 + * @param array $json JSON字段 + * @param bool $assoc 是否转换为数组 + * @param array $withRelationAttr 关联获取器 * @return void */ protected function jsonResult(array &$result, array $json = [], bool $assoc = false, array $withRelationAttr = []): void @@ -2982,10 +3080,10 @@ class Query /** * 查询数据转换为模型对象 * @access protected - * @param array $result 查询数据 - * @param array $options 查询参数 - * @param bool $resultSet 是否为数据集查询 - * @param array $withRelationAttr 关联字段获取器 + * @param array $result 查询数据 + * @param array $options 查询参数 + * @param bool $resultSet 是否为数据集查询 + * @param array $withRelationAttr 关联字段获取器 * @return void */ protected function resultToModel(array &$result, array $options = [], bool $resultSet = false, array $withRelationAttr = []): void @@ -3007,7 +3105,7 @@ class Query $this->jsonResult($result, $options['json'], $options['json_assoc'], $withRelationAttr); } - $result = $this->model->newInstance($result, true, $resultSet ? null : $this->getModelUpdateCondition($options)); + $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options))->setQuery($this); // 动态获取器 if (!empty($options['with_attr'])) { @@ -3027,7 +3125,7 @@ class Query // 关联查询 if (!empty($options['relation'])) { - $result->relationQuery($options['relation'], $withRelationAttr); + $result->relationQuery($options['relation']); } // 预载入查询 @@ -3051,26 +3149,25 @@ class Query /** * 查询失败 抛出异常 * @access protected - * @param array $options 查询参数 * @return void * @throws ModelNotFoundException * @throws DataNotFoundException */ - protected function throwNotFound(array $options = []): void + protected function throwNotFound(): void { if (!empty($this->model)) { $class = get_class($this->model); - throw new ModelNotFoundException('model data Not Found:' . $class, $class, $options); + throw new ModelNotFoundException('model data Not Found:' . $class, $class, $this->options); } - $table = is_array($options['table']) ? key($options['table']) : $options['table']; - throw new DataNotFoundException('table data not Found:' . $table, $table, $options); + $table = $this->getTable(); + throw new DataNotFoundException('table data not Found:' . $table, $table, $this->options); } /** * 查找多条记录 如果不存在则抛出异常 * @access public - * @param array|string|Query|Closure $data + * @param array|string|Query|Closure $data 数据 * @return array|PDOStatement|string|Model * @throws DbException * @throws ModelNotFoundException @@ -3084,7 +3181,7 @@ class Query /** * 查找单条记录 如果不存在则抛出异常 * @access public - * @param array|string|Query|Closure $data + * @param array|string|Query|Closure $data 数据 * @return array|PDOStatement|string|Model * @throws DbException * @throws ModelNotFoundException @@ -3098,22 +3195,19 @@ class Query /** * 分批数据返回处理 * @access public - * @param integer $count 每次处理的数据数量 - * @param callable $callback 处理回调方法 - * @param string|array $column 分批处理的字段名 - * @param string $order 字段排序 + * @param integer $count 每次处理的数据数量 + * @param callable $callback 处理回调方法 + * @param string|array $column 分批处理的字段名 + * @param string $order 字段排序 * @return bool * @throws DbException */ public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool { $options = $this->getOptions(); - $column = $column ?: $this->getPk($options); + $column = $column ?: $this->getPk(); if (isset($options['order'])) { - if ($this->config['debug']) { - throw new DbException('chunk not support call order'); - } unset($options['order']); } @@ -3164,7 +3258,7 @@ class Query /** * 获取绑定的参数 并清空 * @access public - * @param bool $clear + * @param bool $clear 是否清空绑定数据 * @return array */ public function getBind(bool $clear = true): array @@ -3180,7 +3274,7 @@ class Query /** * 创建子查询SQL * @access public - * @param bool $sub + * @param bool $sub 是否添加括号 * @return string * @throws DbException */ @@ -3192,7 +3286,7 @@ class Query /** * 视图查询处理 * @access protected - * @param array $options 查询参数 + * @param array $options 查询参数 * @return void */ protected function parseView(array &$options): void @@ -3232,16 +3326,50 @@ class Query } } + /** + * 分析数据是否存在更新条件 + * @access public + * @param array $data 数据 + * @return bool + * @throws Exception + */ + public function parseUpdateData(&$data): bool + { + $pk = $this->getPk(); + $isUpdate = false; + // 如果存在主键数据 则自动作为更新条件 + if (is_string($pk) && isset($data[$pk])) { + $this->where($pk, '=', $data[$pk]); + $this->options['key'] = $data[$pk]; + unset($data[$pk]); + $isUpdate = true; + } elseif (is_array($pk)) { + // 增加复合主键支持 + foreach ($pk as $field) { + if (isset($data[$field])) { + $this->where($field, '=', $data[$field]); + $isUpdate = true; + } else { + // 如果缺少复合主键数据则不执行 + throw new Exception('miss complex primary data'); + } + unset($data[$field]); + } + } + + return $isUpdate; + } + /** * 把主键值转换为查询条件 支持复合主键 * @access public - * @param array|string $data 主键数据 + * @param array|string $data 主键数据 * @return void * @throws Exception */ public function parsePkWhere($data): void { - $pk = $this->getPk($this->options); + $pk = $this->getPk(); if (is_string($pk)) { // 获取数据表 @@ -3257,12 +3385,11 @@ class Query $key = isset($alias) ? $alias . '.' . $pk : $pk; // 根据主键查询 - $where[$pk] = is_array($data) ? [$key, 'in', $data] : [$key, '=', $data]; - - if (isset($this->options['where']['AND'])) { - $this->options['where']['AND'] = array_merge($this->options['where']['AND'], $where); + if (is_array($data)) { + $this->where($key, 'in', $data); } else { - $this->options['where']['AND'] = $where; + $this->where($key, '=', $data); + $this->options['key'] = $data; } } } @@ -3302,13 +3429,13 @@ class Query $options['strict'] = $this->connection->getConfig('fields_strict'); } - foreach (['master', 'lock', 'fetch_sql', 'distinct', 'procedure'] as $name) { + foreach (['master', 'lock', 'fetch_sql', 'array', 'distinct', 'procedure'] as $name) { if (!isset($options[$name])) { $options[$name] = false; } } - foreach (['group', 'having', 'limit', 'force', 'comment'] as $name) { + foreach (['group', 'having', 'limit', 'force', 'comment', 'partition', 'duplicate', 'extra'] as $name) { if (!isset($options[$name])) { $options[$name] = ''; } @@ -3328,33 +3455,14 @@ class Query return $options; } - /** - * 注册回调方法 - * @access public - * @param string $event 事件名 - * @param callable $callback 回调方法 - * @return void - */ - public static function event(string $event, callable $callback): void - { - self::$event[$event] = $callback; - } - - /** - * 触发事件 - * @access public - * @param string $event 事件名 - * @return bool - */ - public function trigger(string $event) + public function __debugInfo() { - $result = false; - - if (isset(self::$event[$event])) { - $result = call_user_func_array(self::$event[$event], [$this]); - } - - return $result; + return [ + 'name' => $this->name, + 'pk' => $this->pk, + 'prefix' => $this->prefix, + 'bind' => $this->bind, + 'options' => $this->options, + ]; } - } diff --git a/src/db/Expression.php b/src/db/Raw.php similarity index 97% rename from src/db/Expression.php rename to src/db/Raw.php index 3e6a56b..0091a5d 100644 --- a/src/db/Expression.php +++ b/src/db/Raw.php @@ -12,7 +12,10 @@ declare (strict_types = 1); namespace think\db; -class Expression +/** + * SQL Raw + */ +class Raw { /** * 查询表达式 diff --git a/src/db/Where.php b/src/db/Where.php index b4ed682..0880460 100644 --- a/src/db/Where.php +++ b/src/db/Where.php @@ -14,6 +14,9 @@ namespace think\db; use ArrayAccess; +/** + * 数组查询对象 + */ class Where implements ArrayAccess { /** @@ -62,7 +65,7 @@ class Where implements ArrayAccess $where = []; foreach ($this->where as $key => $val) { - if ($val instanceof Expression) { + if ($val instanceof Raw) { $where[] = [$key, 'exp', $val]; } elseif (is_null($val)) { $where[] = [$key, 'NULL', '']; @@ -86,7 +89,7 @@ class Where implements ArrayAccess protected function parseItem(string $field, array $where = []): array { $op = $where[0]; - $condition = isset($where[1]) ? $where[1] : null; + $condition = $where[1] ?? null; if (is_array($op)) { // 同一字段多条件查询 diff --git a/src/db/builder/Mongo.php b/src/db/builder/Mongo.php index 0a0d981..a6ee72e 100644 --- a/src/db/builder/Mongo.php +++ b/src/db/builder/Mongo.php @@ -6,7 +6,7 @@ // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- - +declare (strict_types = 1); namespace think\db\builder; use MongoDB\BSON\Javascript; @@ -107,10 +107,10 @@ class Mongo foreach ($data as $key => $val) { $item = $this->parseKey($key); - if ($val instanceof Expression) { - $result[$item] = $val->getValue(); - } elseif (is_object($val)) { + if (is_object($val)) { $result[$item] = $val; + } elseif (isset($val[0]) && 'exp' == $val[0]) { + $result[$item] = $val[1]; } elseif (is_null($val)) { $result[$item] = 'NULL'; } else { @@ -163,14 +163,8 @@ class Mongo } $filter = []; - foreach ($where as $logic => $val) { - $logic = strtolower($logic); - - if (0 !== strpos($logic, '$')) { - $logic = '$' . $logic; - } - + $logic = '$' . strtolower($logic); foreach ($val as $field => $value) { if (is_array($value)) { if (key($value) !== 0) { @@ -243,8 +237,8 @@ class Mongo $k = '$' . $exp; $data[$k] = $value; } - $query[$key] = $data; - return $query; + $result[$key] = $data; + return $result; } elseif (!in_array($exp, $this->exp)) { $exp = strtolower($exp); if (isset($this->exp[$exp])) { @@ -254,11 +248,6 @@ class Mongo } } - if (is_object($value) && method_exists($value, '__toString')) { - // 对象数据写入 - $value = $value->__toString(); - } - $result = []; if ('=' == $exp) { // 普通查询 @@ -341,7 +330,7 @@ class Mongo protected function parseDateTime(Query $query, $value, $key) { // 获取时间字段类型 - $type = $this->connection->getFieldsType($query->getOptions('table') ?: $query->getTable()); + $type = $this->connection->getTableInfo('', 'type'); if (isset($type[$key])) { $value = strtotime($value) ?: $value; @@ -405,6 +394,7 @@ class Mongo $bulk = new BulkWrite; $options = $query->getOptions(); + $this->insertId = []; foreach ($dataSet as $data) { // 分析并处理数据 $data = $this->parseData($query, $data); @@ -475,14 +465,20 @@ class Mongo /** * 生成Mongo查询对象 * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 + * @param bool $one 是否仅获取一个记录 * @return MongoQuery */ - public function select(Query $query) + public function select(Query $query, bool $one = false) { $options = $query->getOptions(); $where = $this->parseWhere($query, $options['where']); + + if ($one) { + $options['limit'] = 1; + } + $query = new MongoQuery($where, $options); $this->log('find', $where, $options); @@ -561,8 +557,8 @@ class Mongo /** * 多聚合查询命令, 可以对多个字段进行 group by 操作 * - * @param array $options 参数 - * @param array $extra 指令和字段 + * @param Query $query 查询对象 + * @param array $extra 指令和字段 * @return Command */ public function multiAggregate(Query $query, $extra) @@ -570,7 +566,9 @@ class Mongo $options = $query->getOptions(); list($aggregate, $groupBy) = $extra; - $groups = ['_id' => []]; + + $groups = ['_id' => []]; + foreach ($groupBy as $field) { $groups['_id'][$field] = '$' . $field; } @@ -578,10 +576,12 @@ class Mongo foreach ($aggregate as $fun => $field) { $groups[$field . '_' . $fun] = ['$' . $fun => '$' . $field]; } + $pipeline = [ ['$match' => (object) $this->parseWhere($query, $options['where'])], ['$group' => $groups], ]; + $cmd = [ 'aggregate' => $options['table'], 'allowDiskUse' => true, @@ -594,8 +594,10 @@ class Mongo $cmd[$option] = $options[$option]; } } + $command = new Command($cmd); $this->log('group', $cmd); + return $command; } diff --git a/src/db/builder/Mysql.php b/src/db/builder/Mysql.php index 9d52179..36ac102 100644 --- a/src/db/builder/Mysql.php +++ b/src/db/builder/Mysql.php @@ -13,8 +13,8 @@ declare (strict_types = 1); namespace think\db\builder; use think\db\Builder; -use think\db\Expression; use think\db\Query; +use think\db\Raw; use think\Exception; /** @@ -22,7 +22,10 @@ use think\Exception; */ class Mysql extends Builder { - // 查询表达式解析 + /** + * 查询表达式解析 + * @var array + */ protected $parser = [ 'parseCompare' => ['=', '<>', '>', '>=', '<', '<='], 'parseLike' => ['LIKE', 'NOT LIKE'], @@ -35,10 +38,107 @@ class Mysql extends Builder 'parseTime' => ['< TIME', '> TIME', '<= TIME', '>= TIME'], 'parseExists' => ['NOT EXISTS', 'EXISTS'], 'parseColumn' => ['COLUMN'], + 'parseFindInSet' => ['FIND IN SET'], ]; - protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES %DATA% %COMMENT%'; - protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + /** + * SELECT SQL表达式 + * @var string + */ + protected $selectSql = 'SELECT%DISTINCT%%EXTRA% %FIELD% FROM %TABLE%%PARTITION%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT% %LOCK%%COMMENT%'; + + /** + * INSERT SQL表达式 + * @var string + */ + protected $insertSql = '%INSERT%%EXTRA% INTO %TABLE%%PARTITION% SET %SET% %DUPLICATE%%COMMENT%'; + + /** + * INSERT ALL SQL表达式 + * @var string + */ + protected $insertAllSql = '%INSERT%%EXTRA% INTO %TABLE%%PARTITION% (%FIELD%) VALUES %DATA% %DUPLICATE%%COMMENT%'; + + /** + * UPDATE SQL表达式 + * @var string + */ + protected $updateSql = 'UPDATE%EXTRA% %TABLE%%PARTITION% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + + /** + * DELETE SQL表达式 + * @var string + */ + protected $deleteSql = 'DELETE%EXTRA% FROM %TABLE%%PARTITION%%USING%%JOIN%%WHERE%%ORDER%%LIMIT% %LOCK%%COMMENT%'; + + /** + * 生成查询SQL + * @access public + * @param Query $query 查询对象 + * @param bool $one 是否仅获取一个记录 + * @return string + */ + public function select(Query $query, bool $one = false): string + { + $options = $query->getOptions(); + + return str_replace( + ['%TABLE%', '%PARTITION%', '%DISTINCT%', '%EXTRA%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'], + [ + $this->parseTable($query, $options['table']), + $this->parsePartition($query, $options['partition']), + $this->parseDistinct($query, $options['distinct']), + $this->parseExtra($query, $options['extra']), + $this->parseField($query, $options['field']), + $this->parseJoin($query, $options['join']), + $this->parseWhere($query, $options['where']), + $this->parseGroup($query, $options['group']), + $this->parseHaving($query, $options['having']), + $this->parseOrder($query, $options['order']), + $this->parseLimit($query, $one ? '1' : $options['limit']), + $this->parseUnion($query, $options['union']), + $this->parseLock($query, $options['lock']), + $this->parseComment($query, $options['comment']), + $this->parseForce($query, $options['force']), + ], + $this->selectSql); + } + + /** + * 生成Insert SQL + * @access public + * @param Query $query 查询对象 + * @param bool $replace 是否replace + * @return string + */ + public function insert(Query $query, bool $replace = false): string + { + $options = $query->getOptions(); + + // 分析并处理数据 + $data = $this->parseData($query, $options['data']); + if (empty($data)) { + return ''; + } + + $set = []; + foreach ($data as $key => $val) { + $set[] = $key . ' = ' . $val; + } + + return str_replace( + ['%INSERT%', '%EXTRA%', '%TABLE%', '%PARTITION%', '%SET%', '%DUPLICATE%', '%COMMENT%'], + [ + $replace ? 'REPLACE' : 'INSERT', + $this->parseExtra($query, $options['extra']), + $this->parseTable($query, $options['table']), + $this->parsePartition($query, $options['partition']), + implode(' , ', $set), + $this->parseDuplicate($query, $options['duplicate']), + $this->parseComment($query, $options['comment']), + ], + $this->insertSql); + } /** * 生成insertall SQL @@ -60,10 +160,12 @@ class Mysql extends Builder } // 获取绑定信息 - $bind = $this->connection->getFieldsBind($options['table']); + $bind = $this->connection->getFieldsBind($options['table']); + $fields = []; + $values = []; foreach ($dataSet as $data) { - $data = $this->parseData($query, $data, $allowFields); + $data = $this->parseData($query, $data, $allowFields, $bind); $values[] = '( ' . implode(',', array_values($data)) . ' )'; @@ -72,36 +174,125 @@ class Mysql extends Builder } } - $fields = []; foreach ($insertFields as $field) { $fields[] = $this->parseKey($query, $field); } return str_replace( - ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], + ['%INSERT%', '%EXTRA%', '%TABLE%', '%PARTITION%', '%FIELD%', '%DATA%', '%DUPLICATE%', '%COMMENT%'], [ $replace ? 'REPLACE' : 'INSERT', + $this->parseExtra($query, $options['extra']), $this->parseTable($query, $options['table']), + $this->parsePartition($query, $options['partition']), implode(' , ', $fields), implode(' , ', $values), + $this->parseDuplicate($query, $options['duplicate']), $this->parseComment($query, $options['comment']), ], $this->insertAllSql); } + /** + * 生成update SQL + * @access public + * @param Query $query 查询对象 + * @return string + */ + public function update(Query $query): string + { + $options = $query->getOptions(); + + $data = $this->parseData($query, $options['data']); + + if (empty($data)) { + return ''; + } + $set = []; + foreach ($data as $key => $val) { + $set[] = $key . ' = ' . $val; + } + + return str_replace( + ['%TABLE%', '%PARTITION%', '%EXTRA%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'], + [ + $this->parseTable($query, $options['table']), + $this->parsePartition($query, $options['partition']), + $this->parseExtra($query, $options['extra']), + implode(' , ', $set), + $this->parseJoin($query, $options['join']), + $this->parseWhere($query, $options['where']), + $this->parseOrder($query, $options['order']), + $this->parseLimit($query, $options['limit']), + $this->parseLock($query, $options['lock']), + $this->parseComment($query, $options['comment']), + ], + $this->updateSql); + } + + /** + * 生成delete SQL + * @access public + * @param Query $query 查询对象 + * @return string + */ + public function delete(Query $query): string + { + $options = $query->getOptions(); + + return str_replace( + ['%TABLE%', '%PARTITION%', '%EXTRA%', '%USING%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'], + [ + $this->parseTable($query, $options['table']), + $this->parsePartition($query, $options['partition']), + $this->parseExtra($query, $options['extra']), + !empty($options['using']) ? ' USING ' . $this->parseTable($query, $options['using']) . ' ' : '', + $this->parseJoin($query, $options['join']), + $this->parseWhere($query, $options['where']), + $this->parseOrder($query, $options['order']), + $this->parseLimit($query, $options['limit']), + $this->parseLock($query, $options['lock']), + $this->parseComment($query, $options['comment']), + ], + $this->deleteSql); + } + /** * 正则查询 * @access protected * @param Query $query 查询对象 * @param string $key - * @param Expression $exp + * @param string $exp * @param mixed $value * @param string $field * @return string */ - protected function parseRegexp(Query $query, string $key, string $exp, Expression $value, string $field): string + protected function parseRegexp(Query $query, string $key, string $exp, $value, string $field): string { - return $key . ' ' . $exp . ' ' . $value->getValue(); + if ($value instanceof Raw) { + $value = $value->getValue(); + } + + return $key . ' ' . $exp . ' ' . $value; + } + + /** + * FIND_IN_SET 查询 + * @access protected + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @return string + */ + protected function parseFindInSet(Query $query, string $key, string $exp, $value, string $field): string + { + if ($value instanceof Raw) { + $value = $value->getValue(); + } + + return 'FIND_IN_SET(' . $value . ', ' . $key . ')'; } /** @@ -115,8 +306,8 @@ class Mysql extends Builder public function parseKey(Query $query, $key, bool $strict = false): string { if (is_int($key)) { - return $key; - } elseif ($key instanceof Expression) { + return (string) $key; + } elseif ($key instanceof Raw) { return $key->getValue(); } @@ -172,4 +363,60 @@ class Mysql extends Builder return 'rand()'; } + /** + * Partition 分析 + * @access protected + * @param Query $query 查询对象 + * @param string|array $partition 分区 + * @return string + */ + protected function parsePartition(Query $query, $partition): string + { + if ('' == $partition) { + return ''; + } + + if (is_string($partition)) { + $partition = explode(',', $partition); + } + + return ' PARTITION (' . implode(' , ', $partition) . ') '; + } + + /** + * ON DUPLICATE KEY UPDATE 分析 + * @access protected + * @param Query $query 查询对象 + * @param mixed $duplicate + * @return string + */ + protected function parseDuplicate(Query $query, $duplicate): string + { + if ('' == $duplicate) { + return ''; + } + + if ($duplicate instanceof Raw) { + return ' ON DUPLICATE KEY UPDATE ' . $duplicate->getValue() . ' '; + } + + if (is_string($duplicate)) { + $duplicate = explode(',', $duplicate); + } + + $updates = []; + foreach ($duplicate as $key => $val) { + if (is_numeric($key)) { + $val = $this->parseKey($query, $val); + $updates[] = $val . ' = VALUES(' . $val . ')'; + } elseif ($val instanceof Raw) { + $updates[] = $this->parseKey($query, $key) . " = " . $val->getValue(); + } else { + $name = $query->bindValue($val, $query->getFieldBindType($key)); + $updates[] = $this->parseKey($query, $key) . " = :" . $name; + } + } + + return ' ON DUPLICATE KEY UPDATE ' . implode(' , ', $updates) . ' '; + } } diff --git a/src/db/builder/Oracle.php b/src/db/builder/Oracle.php index 2c2bec1..1168bdc 100644 --- a/src/db/builder/Oracle.php +++ b/src/db/builder/Oracle.php @@ -17,21 +17,22 @@ use think\db\Query; */ class Oracle extends Builder { - protected $selectSql = 'SELECT * FROM (SELECT thinkphp.*, rownum AS numrow FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%) thinkphp ) %LIMIT%%COMMENT%'; /** * limit分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $limit + * @param Query $query 查询对象 + * @param mixed $limit * @return string */ - protected function parseLimit(Query $query, $limit) + protected function parseLimit(Query $query, string $limit): string { $limitStr = ''; + if (!empty($limit)) { $limit = explode(',', $limit); + if (count($limit) > 1) { $limitStr = "(numrow>" . $limit[0] . ") AND (numrow<=" . ($limit[0] + $limit[1]) . ")"; } else { @@ -46,11 +47,11 @@ class Oracle extends Builder /** * 设置锁机制 * @access protected - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @param bool|false $lock * @return string */ - protected function parseLock(Query $query, $lock = false) + protected function parseLock(Query $query, $lock = false): string { if (!$lock) { return ''; @@ -62,19 +63,13 @@ class Oracle extends Builder /** * 字段和表名处理 * @access public - * @param Query $query 查询对象 - * @param mixed $key 字段名 - * @param bool $strict 严格检测 + * @param Query $query 查询对象 + * @param string $key + * @param string $strict * @return string */ - public function parseKey(Query $query, $key, $strict = false) + public function parseKey(Query $query, $key, bool $strict = false): string { - if (is_numeric($key)) { - return $key; - } elseif ($key instanceof Expression) { - return $key->getValue(); - } - $key = trim($key); if (strpos($key, '->') && false === strpos($key, '(')) { @@ -89,12 +84,11 @@ class Oracle extends Builder /** * 随机排序 * @access protected - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return string */ - protected function parseRand(Query $query) + protected function parseRand(Query $query): string { return 'DBMS_RANDOM.value'; } - } diff --git a/src/db/builder/Pgsql.php b/src/db/builder/Pgsql.php index 399cec1..be16c92 100644 --- a/src/db/builder/Pgsql.php +++ b/src/db/builder/Pgsql.php @@ -13,15 +13,24 @@ declare (strict_types = 1); namespace think\db\builder; use think\db\Builder; -use think\db\Expression; use think\db\Query; +use think\db\Raw; /** * Pgsql数据库驱动 */ class Pgsql extends Builder { - protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; + /** + * INSERT SQL表达式 + * @var string + */ + protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; + + /** + * INSERT ALL SQL表达式 + * @var string + */ protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; /** @@ -58,8 +67,8 @@ class Pgsql extends Builder public function parseKey(Query $query, $key, bool $strict = false): string { if (is_int($key)) { - return $key; - } elseif ($key instanceof Expression) { + return (string) $key; + } elseif ($key instanceof Raw) { return $key->getValue(); } diff --git a/src/db/builder/Sqlite.php b/src/db/builder/Sqlite.php index 70c79ee..60d45a2 100644 --- a/src/db/builder/Sqlite.php +++ b/src/db/builder/Sqlite.php @@ -12,8 +12,8 @@ namespace think\db\builder; use think\db\Builder; -use think\db\Expression; use think\db\Query; +use think\db\Raw; /** * Sqlite数据库驱动 @@ -58,15 +58,15 @@ class Sqlite extends Builder * 字段和表名处理 * @access public * @param Query $query 查询对象 - * @param string $key 字段名 + * @param mixed $key 字段名 * @param bool $strict 严格检测 * @return string */ public function parseKey(Query $query, $key, bool $strict = false): string { if (is_int($key)) { - return $key; - } elseif ($key instanceof Expression) { + return (string) $key; + } elseif ($key instanceof Raw) { return $key->getValue(); } diff --git a/src/db/builder/Sqlsrv.php b/src/db/builder/Sqlsrv.php index 1351435..4cd8c9c 100644 --- a/src/db/builder/Sqlsrv.php +++ b/src/db/builder/Sqlsrv.php @@ -12,8 +12,8 @@ namespace think\db\builder; use think\db\Builder; -use think\db\Expression; use think\db\Query; +use think\db\Raw; use think\Exception; /** @@ -21,12 +21,40 @@ use think\Exception; */ class Sqlsrv extends Builder { - protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%'; + /** + * SELECT SQL表达式 + * @var string + */ + protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%'; + /** + * SELECT INSERT SQL表达式 + * @var string + */ protected $selectInsertSql = 'SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%'; - protected $updateSql = 'UPDATE %TABLE% SET %SET% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; - protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; - protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; - protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; + + /** + * UPDATE SQL表达式 + * @var string + */ + protected $updateSql = 'UPDATE %TABLE% SET %SET% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; + + /** + * DELETE SQL表达式 + * @var string + */ + protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; + + /** + * INSERT SQL表达式 + * @var string + */ + protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; + + /** + * INSERT ALL SQL表达式 + * @var string + */ + protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; /** * order分析 @@ -44,7 +72,7 @@ class Sqlsrv extends Builder $array = []; foreach ($order as $key => $val) { - if ($val instanceof Expression) { + if ($val instanceof Raw) { $array[] = $val->getValue(); } elseif ('[rand]' == $val) { $array[] = $this->parseRand($query); @@ -85,8 +113,8 @@ class Sqlsrv extends Builder public function parseKey(Query $query, $key, bool $strict = false): string { if (is_int($key)) { - return $key; - } elseif ($key instanceof Expression) { + return (string) $key; + } elseif ($key instanceof Raw) { return $key->getValue(); } diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 4de67fe..8e78dac 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -6,7 +6,7 @@ // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- - +declare (strict_types = 1); namespace think\db\connector; use MongoDB\BSON\ObjectID; @@ -22,19 +22,20 @@ use MongoDB\Driver\Manager; use MongoDB\Driver\Query as MongoQuery; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; +use think\Cache; use think\Collection; use think\Db; use think\db\builder\Mongo as Builder; use think\db\Mongo as Query; use think\Exception; +use think\Log; /** * Mongo数据库驱动 */ class Mongo { - protected static $instance = []; - protected $dbName = ''; // dbName + protected $dbName = ''; // dbName /** @var string 当前SQL指令 */ protected $queryStr = ''; // 查询数据类型 @@ -42,36 +43,20 @@ class Mongo protected $mongo; // MongoDb Object protected $cursor; // MongoCursor Object - /** - * 查询次数 - * @var integer - */ - protected static $queryTimes = 0; - - protected $queryStartTime; - - // 监听回调 - protected static $event = []; - /** @var PDO[] 数据库连接ID 支持多个连接 */ + /** @var Manager[] 数据库连接ID 支持多个连接 */ protected $links = []; - /** @var PDO 当前连接ID */ + /** @var Manger 当前连接ID */ protected $linkID; protected $linkRead; protected $linkWrite; // Builder对象 protected $builder; - // 缓存对象 - protected $cache; // 返回或者影响记录数 protected $numRows = 0; // 错误信息 protected $error = ''; // 查询参数 protected $options = []; - // 数据表信息 - protected static $info = []; - // 数据库日志 - protected static $log = []; // 数据库连接参数配置 protected $config = [ // 数据库类型 @@ -112,28 +97,44 @@ class Mongo 'slave_no' => '', // 是否严格检查字段是否存在 'fields_strict' => true, - // 数据集返回类型 - 'resultset_type' => 'array', // 自动写入时间戳字段 'auto_timestamp' => false, // 时间字段取出后的默认时间格式 'datetime_format' => 'Y-m-d H:i:s', - // 是否需要进行SQL性能分析 - 'sql_explain' => false, // 是否_id转换为id 'pk_convert_id' => false, // typeMap 'type_map' => ['root' => 'array', 'document' => 'array'], // Query对象 - 'query' => '\\think\\mongo\\Query', + 'query' => '\\think\\db\\Mongo', ]; + /** + * 缓存对象 + * @var Cache + */ + protected $cache; + + /** + * Db对象 + * @var Db + */ + protected $db; + + /** + * 日志对象 + * @var Log + */ + protected $log; + /** * 架构函数 读取数据库配置信息 * @access public + * @param Cache $cache 缓存对象 + * @param Log $log 日志对象 * @param array $config 数据库配置数组 */ - public function __construct(array $config = []) + public function __construct(Cache $cache, Log $log, array $config = []) { if (!class_exists('\MongoDB\Driver\Manager')) { throw new Exception('require mongodb > 1.0'); @@ -144,46 +145,63 @@ class Mongo } $this->builder = new Builder($this); - $this->cache = Db::getCacheHandler(); + + $this->cache = $cache; + $this->log = $log; } /** - * 取得数据库连接类实例 + * 获取当前连接器类对应的Query类 * @access public - * @param mixed $config 连接配置 - * @param bool|string $name 连接标识 true 强制重新连接 - * @return Connection - * @throws Exception + * @return string */ - public static function instance($config = [], $name = false) + public function getQueryClass(): string { - if (false === $name) { - $name = md5(serialize($config)); - } + return $this->getConfig('query') ?: Query::class; + } - if (true === $name || !isset(self::$instance[$name])) { - // 解析连接参数 支持数组和字符串 - $options = self::parseConfig($config); + /** + * 设置当前的数据库Builder对象 + * @access protected + * @param Builder $builder + * @return void + */ + protected function setBuilder(Builder $builder): void + { + $this->builder = $builder; + } - if (true === $name) { - $name = md5(serialize($config)); - } - self::$instance[$name] = new static($options); - } + /** + * 获取当前的builder实例对象 + * @access public + * @return Builder + */ + public function getBuilder(): Builder + { + return $this->builder; + } - return self::$instance[$name]; + /** + * 设置当前的数据库Db对象 + * @access public + * @param Db $db + * @return void + */ + public function setDb(Db $db): void + { + $this->db = $db; } /** * 连接数据库方法 * @access public - * @param array $config 连接参数 - * @param integer $linkNum 连接序号 + * @param array $config 连接参数 + * @param integer $linkNum 连接序号 * @return Manager * @throws InvalidArgumentException * @throws RuntimeException */ - public function connect(array $config = [], $linkNum = 0) + public function connect(array $config = [], int $linkNum = 0): Manager { if (!isset($this->links[$linkNum])) { if (empty($config)) { @@ -199,18 +217,19 @@ class Mongo $this->config['pk'] = 'id'; } - $host = 'mongodb://' . ($config['username'] ? "{$config['username']}" : '') . ($config['password'] ? ":{$config['password']}@" : '') . $config['hostname'] . ($config['hostport'] ? ":{$config['hostport']}" : '') . '/' . ($config['database'] ? "{$config['database']}" : ''); + if (empty($config['dsn'])) { + $config['dsn'] = 'mongodb://' . ($config['username'] ? "{$config['username']}" : '') . ($config['password'] ? ":{$config['password']}@" : '') . $config['hostname'] . ($config['hostport'] ? ":{$config['hostport']}" : ''); + } if ($config['debug']) { $startTime = microtime(true); } - $this->links[$linkNum] = new Manager($host, $this->config['params']); + $this->links[$linkNum] = new Manager($config['dsn'], $config['params']); + + // 记录数据库连接信息 + $this->logger('[ MongoDb ] CONNECT :[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); - if ($config['debug']) { - // 记录数据库连接信息 - $this->logger('[ MongoDb ] CONNECT :[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); - } } return $this->links[$linkNum]; @@ -219,24 +238,27 @@ class Mongo /** * 获取数据库的配置参数 * @access public - * @param string $config 配置名称 + * @param string $config 配置名称 * @return mixed */ - public function getConfig($config = '') + public function getConfig(string $config = '') { - return $config ? $this->config[$config] : $this->config; + if ('' === $config) { + return $this->config; + } + + return $this->config[$config] ?? null; } /** * 设置数据库的配置参数 * @access public - * @param string $config 配置名称 - * @param mixed $value 配置值 + * @param array $config 配置 * @return void */ - public function setConfig($config, $value) + public function setConfig(array $config): void { - $this->config[$config] = $value; + $this->config = array_merge($this->config, $config); } /** @@ -246,20 +268,16 @@ class Mongo */ public function getMongo() { - if (!$this->mongo) { - return; - } else { - return $this->mongo; - } + return $this->mongo ?: null; } /** * 设置/获取当前操作的database * @access public - * @param string $db db + * @param string $db db * @throws Exception */ - public function db($db = null) + public function db(string $db = null) { if (is_null($db)) { return $this->dbName; @@ -269,70 +287,96 @@ class Mongo } /** - * 将SQL语句中的__TABLE_NAME__字符串替换成带前缀的表名(小写) + * 执行查询但只返回Cursor对象 * @access public - * @param string $sql sql语句 - * @return string + * @param Query $query 查询对象 + * @return Cursor */ - public function parseSqlTable($sql) + public function getCursor(Query $query): Cursor { - if (false !== strpos($sql, '__')) { - $prefix = $this->getConfig('prefix'); + // 分析查询表达式 + $options = $query->parseOptions(); - $sql = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function ($match) use ($prefix) { - return $prefix . strtolower($match[1]); - }, $sql); - } + // 生成MongoQuery对象 + $mongoQuery = $this->builder->select($query); - return $sql; + // 执行查询操作 + return $this->cursor($options['table'], $mongoQuery, $options['readPreference'] ?? null); } /** - * 启动事务 + * 执行查询并返回Cursor对象 * @access public - * @return void - * @throws \PDOException - * @throws \Exception + * @param string $namespace 当前查询的collection + * @param MongoQuery $query 查询对象 + * @param ReadPreference $readPreference readPreference + * @return Cursor + * @throws AuthenticationException + * @throws InvalidArgumentException + * @throws ConnectionException + * @throws RuntimeException */ - public function startTrans() - {} + public function cursor(string $namespace, MongoQuery $query, ReadPreference $readPreference = null): Cursor + { + $this->initConnect(false); + $this->db->updateQueryTimes(); - /** - * 用于非自动提交状态下面的查询提交 - * @access public - * @return void - * @throws PDOException - */ - public function commit() - {} + if (false === strpos($namespace, '.')) { + $namespace = $this->dbName . '.' . $namespace; + } + + if ($this->config['debug'] && !empty($this->queryStr)) { + // 记录执行指令 + $this->queryStr = 'db' . strstr($namespace, '.') . '.' . $this->queryStr; + } + + $this->debug(true); + + $this->cursor = $this->mongo->executeQuery($namespace, $query, $readPreference); + + $this->debug(false); + + return $this->cursor; + } /** - * 事务回滚 + * 执行查询 * @access public - * @return void - * @throws PDOException + * @param string $namespace 当前查询的collection + * @param MongoQuery $query 查询对象 + * @param ReadPreference $readPreference readPreference + * @param string|array $typeMap 指定返回的typeMap + * @return array + * @throws AuthenticationException + * @throws InvalidArgumentException + * @throws ConnectionException + * @throws RuntimeException */ - public function rollback() - {} + public function query(string $namespace, MongoQuery $query, ReadPreference $readPreference = null, $typeMap = null): array + { + $this->cursor($namespace, $query, $readPreference); + + return $this->getResult($typeMap); + } /** - * 执行查询 + * 执行写操作 * @access public - * @param string $namespace 当前查询的collection - * @param MongoQuery $query 查询对象 - * @param ReadPreference $readPreference readPreference - * @param string|bool $class 返回的数据集类型 - * @param string|array $typeMap 指定返回的typeMap - * @return mixed + * @param string $namespace + * @param BulkWrite $bulk + * @param WriteConcern $writeConcern + * + * @return WriteResult * @throws AuthenticationException * @throws InvalidArgumentException * @throws ConnectionException * @throws RuntimeException + * @throws BulkWriteException */ - public function query($namespace, MongoQuery $query, ReadPreference $readPreference = null, $class = false, $typeMap = null) + public function execute(string $namespace, BulkWrite $bulk, WriteConcern $writeConcern = null) { - $this->initConnect(false); - self::$queryTimes++; + $this->initConnect(true); + $this->db->updateQueryTimes(); if (false === strpos($namespace, '.')) { $namespace = $this->dbName . '.' . $namespace; @@ -345,31 +389,32 @@ class Mongo $this->debug(true); - $this->cursor = $this->mongo->executeQuery($namespace, $query, $readPreference); + $writeResult = $this->mongo->executeBulkWrite($namespace, $bulk, $writeConcern); $this->debug(false); - return $this->getResult($class, $typeMap); + $this->numRows = $writeResult->getMatchedCount(); + + return $writeResult; } /** * 执行指令 * @access public - * @param Command $command 指令 - * @param string $dbName 当前数据库名 - * @param ReadPreference $readPreference readPreference - * @param string|bool $class 返回的数据集类型 - * @param string|array $typeMap 指定返回的typeMap - * @return mixed + * @param Command $command 指令 + * @param string $dbName 当前数据库名 + * @param ReadPreference $readPreference readPreference + * @param string|array $typeMap 指定返回的typeMap + * @return array * @throws AuthenticationException * @throws InvalidArgumentException * @throws ConnectionException * @throws RuntimeException */ - public function command(Command $command, $dbName = '', ReadPreference $readPreference = null, $class = false, $typeMap = null) + public function command(Command $command, string $dbName = '', ReadPreference $readPreference = null, $typeMap = null): array { $this->initConnect(false); - self::$queryTimes++; + $this->db->updateQueryTimes(); $this->debug(true); @@ -383,23 +428,17 @@ class Mongo $this->debug(false); - return $this->getResult($class, $typeMap); - + return $this->getResult($typeMap); } /** * 获得数据集 * @access protected - * @param bool|string $class true 返回Mongo cursor对象 字符串用于指定返回的类名 - * @param string|array $typeMap 指定返回的typeMap + * @param string|array $typeMap 指定返回的typeMap * @return mixed */ - protected function getResult($class = '', $typeMap = null) + protected function getResult($typeMap = null): array { - if (true === $class) { - return $this->cursor; - } - // 设置结果数据类型 if (is_null($typeMap)) { $typeMap = $this->typeMap; @@ -426,66 +465,27 @@ class Mongo /** * ObjectID处理 - * @access public - * @param array $data + * @access protected + * @param array $data 数据 * @return void */ - private function convertObjectID(&$data) + protected function convertObjectID(array &$data): void { - if (isset($data['_id'])) { + if (isset($data['_id']) && is_object($data['_id'])) { $data['id'] = $data['_id']->__toString(); unset($data['_id']); } } - /** - * 执行写操作 - * @access public - * @param string $namespace - * @param BulkWrite $bulk - * @param WriteConcern $writeConcern - * - * @return WriteResult - * @throws AuthenticationException - * @throws InvalidArgumentException - * @throws ConnectionException - * @throws RuntimeException - * @throws BulkWriteException - */ - public function execute($namespace, BulkWrite $bulk, WriteConcern $writeConcern = null) - { - $this->initConnect(true); - self::$queryTimes++; - - if (false === strpos($namespace, '.')) { - $namespace = $this->dbName . '.' . $namespace; - } - - if ($this->config['debug'] && !empty($this->queryStr)) { - // 记录执行指令 - $this->queryStr = 'db' . strstr($namespace, '.') . '.' . $this->queryStr; - } - - $this->debug(true); - - $writeResult = $this->mongo->executeBulkWrite($namespace, $bulk, $writeConcern); - - $this->debug(false); - - $this->numRows = $writeResult->getMatchedCount(); - - return $writeResult; - } - /** * 数据库日志记录(仅供参考) * @access public - * @param string $type 类型 - * @param mixed $data 数据 - * @param array $options 参数 + * @param string $type 类型 + * @param mixed $data 数据 + * @param array $options 参数 * @return void */ - public function log($type, $data, $options = []) + public function log(string $type, $data, array $options = []) { if (!$this->config['debug']) { return; @@ -493,7 +493,7 @@ class Mongo if (is_array($data)) { array_walk_recursive($data, function (&$value) { - if ($value instanceof ObjectID || $value instanceof \MongoDB\BSON\ObjectId) { + if ($value instanceof ObjectID) { $value = $value->__toString(); } }); @@ -536,52 +536,45 @@ class Mongo * @access public * @return string */ - public function getLastSql() + public function getLastSql(): string { return $this->queryStr; } - /** - * 监听SQL执行 - * @access public - * @param callable $callback 回调方法 - * @return void - */ - public function listen($callback) - { - self::$event[] = $callback; - } - /** * 触发SQL事件 * @access protected - * @param string $sql SQL语句 - * @param float $runtime SQL运行时间 - * @param mixed $options 参数 - * @return bool + * @param string $sql SQL语句 + * @param string $runtime SQL运行时间 + * @param mixed $options 参数 + * @param bool $master 主从标记 + * @return void */ - protected function triggerSql($sql, $runtime, $options = []) + protected function triggerSql(string $sql, string $runtime, array $options = [], bool $master = false): void { - if (!empty(self::$event)) { - foreach (self::$event as $callback) { + $listen = $this->db->getListen(); + + if (!empty($listen)) { + foreach ($listen as $callback) { if (is_callable($callback)) { - call_user_func_array($callback, [$sql, $runtime, $options]); + $callback($sql, $runtime, $options, $master); } } } else { // 未注册监听则记录到日志中 - $this->logger('[ SQL ] ' . $sql . ' [ RunTime:' . $runtime . 's ]'); + if ($this->config['deploy']) { + // 分布式记录当前操作的主从 + $master = $master ? 'master|' : 'slave|'; + } else { + $master = ''; + } + $this->logger('[ SQL ] ' . $sql . ' [' . $master . ' RunTime:' . $runtime . 's ]'); } } - public function logger($log, $type = 'sql') + public function logger(string $log, string $type = 'sql'): void { - $this->config['debug'] && self::$log[] = $log; - } - - public function getSqlLog() - { - return self::$log; + $this->config['debug'] && $this->log->record($log, $type); } /** @@ -589,21 +582,23 @@ class Mongo * @access protected * @param boolean $start 调试开始标记 true 开始 false 结束 * @param string $sql 执行的SQL语句 留空自动获取 + * @param bool $master 主从标记 * @return void */ - protected function debug($start, $sql = '') + protected function debug(bool $start, string $sql = '', bool $master = false) { if (!empty($this->config['debug'])) { // 开启数据库调试模式 if ($start) { $this->queryStartTime = microtime(true); } else { + // 记录操作结束时间 $runtime = number_format((microtime(true) - $this->queryStartTime), 6); $sql = $sql ?: $this->queryStr; // SQL监听 - $this->triggerSql($sql, $runtime, $this->options); + $this->triggerSql($sql, $runtime, $this->options, $master); } } } @@ -636,7 +631,7 @@ class Mongo * @param boolean $master 是否主服务器 * @return void */ - protected function initConnect($master = true) + protected function initConnect(bool $master = true): void { if (!empty($this->config['deploy'])) { // 采用分布式数据库 @@ -662,15 +657,15 @@ class Mongo /** * 连接分布式服务器 * @access protected - * @param boolean $master 主服务器 + * @param boolean $master 主服务器 * @return Manager */ - protected function multiConnect($master = false) + protected function multiConnect(bool $master = false): Manager { $config = []; // 分布式数据库配置解析 foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn'] as $name) { - $config[$name] = explode(',', $this->config[$name]); + $config[$name] = is_string($this->config[$name]) ? explode(',', $this->config[$name]) : $this->config[$name]; } // 主服务器序号 @@ -710,7 +705,7 @@ class Mongo * 创建基于复制集的连接 * @return Manager */ - public function replicaSetConnect() + public function replicaSetConnect(): Manager { $this->dbName = $this->config['database']; $this->typeMap = $this->config['type_map']; @@ -723,10 +718,8 @@ class Mongo $manager = new Manager($this->buildUrl(), $this->config['params']); - if ($this->config['debug']) { - // 记录数据库连接信息 - $this->logger('[ MongoDB ] ReplicaSet CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $this->config['dsn']); - } + // 记录数据库连接信息 + $this->logger('[ MongoDB ] ReplicaSet CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $this->config['dsn']); return $manager; } @@ -735,12 +728,12 @@ class Mongo * 根据配置信息 生成适用于连接复制集的 URL * @return string */ - private function buildUrl() + private function buildUrl(): string { $url = 'mongodb://' . ($this->config['username'] ? "{$this->config['username']}" : '') . ($this->config['password'] ? ":{$this->config['password']}@" : ''); - $hostList = explode(',', $this->config['hostname']); - $portList = explode(',', $this->config['hostport']); + $hostList = is_string($this->config['hostname']) ? explode(',', $this->config['hostname']) : $this->config['hostname']; + $portList = is_string($this->config['hostport']) ? explode(',', $this->config['hostport']) : $this->config['hostport']; for ($i = 0; $i < count($hostList); $i++) { $url = $url . $hostList[$i] . ':' . $portList[0] . ','; @@ -752,9 +745,8 @@ class Mongo /** * 插入记录 * @access public - * @param Query $query 查询对象 - * @param boolean $replace 是否replace(目前无效) - * @param boolean $getLastInsID 返回自增主键 + * @param Query $query 查询对象 + * @param boolean $getLastInsID 返回自增主键 * @return WriteResult * @throws AuthenticationException * @throws InvalidArgumentException @@ -762,7 +754,7 @@ class Mongo * @throws RuntimeException * @throws BulkWriteException */ - public function insert(Query $query, $replace = null, $getLastInsID = false) + public function insert(Query $query, bool $getLastInsID = false) { // 分析查询表达式 $options = $query->parseOptions(); @@ -772,7 +764,7 @@ class Mongo } // 生成bulk对象 - $bulk = $this->builder->insert($query, $replace); + $bulk = $this->builder->insert($query); $writeConcern = $options['writeConcern'] ?? null; $writeResult = $this->execute($options['table'], $bulk, $writeConcern); $result = $writeResult->getInsertedCount(); @@ -782,18 +774,19 @@ class Mongo $lastInsId = $this->getLastInsID(); if ($lastInsId) { - $pk = $query->getPk($options); + $pk = $query->getPk(); $data[$pk] = $lastInsId; } $query->setOption('data', $data); - $query->trigger('after_insert'); + $this->db->trigger('after_insert', $query); if ($getLastInsID) { return $lastInsId; } } + return $result; } @@ -802,17 +795,17 @@ class Mongo * @access public * @return mixed */ - public function getLastInsID($sequence = null) + public function getLastInsID(string $sequence = null) { $id = $this->builder->getLastInsID(); if (is_array($id)) { array_walk($id, function (&$item, $key) { - if ($item instanceof ObjectID || $item instanceof \MongoDB\BSON\ObjectId) { + if ($item instanceof ObjectID) { $item = $item->__toString(); } }); - } elseif ($id instanceof ObjectID || $id instanceof \MongoDB\BSON\ObjectId) { + } elseif ($id instanceof ObjectID) { $id = $id->__toString(); } @@ -822,8 +815,8 @@ class Mongo /** * 批量插入记录 * @access public - * @param Query $query 查询对象 - * @param mixed $dataSet 数据集 + * @param Query $query 查询对象 + * @param array $dataSet 数据集 * @return integer * @throws AuthenticationException * @throws InvalidArgumentException @@ -831,18 +824,18 @@ class Mongo * @throws RuntimeException * @throws BulkWriteException */ - public function insertAll(Query $query, array $dataSet) + public function insertAll(Query $query, array $dataSet = []): int { // 分析查询表达式 $options = $query->parseOptions(); if (!is_array(reset($dataSet))) { - return false; + return 0; } // 生成bulkWrite对象 $bulk = $this->builder->insertAll($query, $dataSet); - $writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : null; + $writeConcern = $options['writeConcern'] ?? null; $writeResult = $this->execute($options['table'], $bulk, $writeConcern); return $writeResult->getInsertedCount(); @@ -851,7 +844,7 @@ class Mongo /** * 更新记录 * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return int * @throws Exception * @throws AuthenticationException @@ -860,70 +853,32 @@ class Mongo * @throws RuntimeException * @throws BulkWriteException */ - public function update(Query $query) + public function update(Query $query): int { $options = $query->parseOptions(); - $data = $options['data']; - if (isset($options['cache']) && is_string($options['cache']['key'])) { - $key = $options['cache']['key']; - } - - $pk = $query->getPk($options); - - if (empty($options['where'])) { - // 如果存在主键数据 则自动作为更新条件 - if (is_string($pk) && isset($data[$pk])) { - $where[$pk] = $data[$pk]; - $key = 'mongo:' . $options['table'] . '|' . $data[$pk]; - unset($data[$pk]); - } elseif (is_array($pk)) { - // 增加复合主键支持 - foreach ($pk as $field) { - if (isset($data[$field])) { - $where[$field] = $data[$field]; - } else { - // 如果缺少复合主键数据则不执行 - throw new Exception('miss complex primary data'); - } - - unset($data[$field]); - } - } - if (!isset($where)) { - // 如果没有任何更新条件则不执行 - throw new Exception('miss update condition'); - } else { - $options['where']['$and'] = $where; - } - } elseif (!isset($key) && is_string($pk) && isset($options['where']['$and'][$pk])) { - $key = $this->getCacheKey($options['where']['$and'][$pk], $options); + if (isset($options['cache'])) { + $cacheItem = $this->parseCache($query, $options['cache']); + $key = $cacheItem->getKey(); } // 生成bulkWrite对象 $bulk = $this->builder->update($query); - $writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : null; + $writeConcern = $options['writeConcern'] ?? null; $writeResult = $this->execute($options['table'], $bulk, $writeConcern); // 检测缓存 - if ($this->cache && isset($key) && $this->cache->get($key)) { + if (isset($key) && $this->cache->get($key)) { // 删除缓存 - $this->cache->rm($key); + $this->cache->delete($key); + } elseif (isset($cacheItem) && $cacheItem->getTag()) { + $this->cache->tag($cacheItem->getTag())->clear(); } $result = $writeResult->getModifiedCount(); if ($result) { - if (isset($where[$pk])) { - $data[$pk] = $where[$pk]; - } elseif (is_string($pk) && isset($key) && strpos($key, '|')) { - list($a, $val) = explode('|', $key); - $data[$pk] = $val; - } - - $query->setOption('data', $data); - - $query->trigger('after_update'); + $this->db->trigger('after_update', $query); } return $result; @@ -932,7 +887,7 @@ class Mongo /** * 删除记录 * @access public - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return int * @throws Exception * @throws AuthenticationException @@ -941,85 +896,46 @@ class Mongo * @throws RuntimeException * @throws BulkWriteException */ - public function delete(Query $query) + public function delete(Query $query): int { // 分析查询表达式 $options = $query->parseOptions(); - $pk = $query->getPk($options); - $data = $options['data']; - - if (!is_null($data) && true !== $data) { - if (!is_array($data)) { - // 缓存标识 - $key = 'mongo:' . $options['table'] . '|' . $data; - } - - // AR模式分析主键条件 - $query->parsePkWhere($data); - } elseif (!isset($key) && is_string($pk) && isset($options['where']['$and'][$pk])) { - $key = $this->getCacheKey($options['where']['$and'][$pk], $options); - } - if (true !== $data && empty($options['where'])) { - // 如果不是强制删除且条件为空 不进行删除操作 - throw new Exception('delete without condition'); + if (isset($options['cache'])) { + $cacheItem = $this->parseCache($query, $options['cache']); + $key = $cacheItem->getKey(); } // 生成bulkWrite对象 $bulk = $this->builder->delete($query); - $writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : null; + $writeConcern = $options['writeConcern'] ?? null; // 执行操作 $writeResult = $this->execute($options['table'], $bulk, $writeConcern); // 检测缓存 - if ($this->cache && isset($key) && $this->cache->get($key)) { + if (isset($key) && $this->cache->get($key)) { // 删除缓存 - $this->cache->rm($key); + $this->cache->delete($key); + } elseif (isset($cacheItem) && $cacheItem->getTag()) { + $this->cache->tag($cacheItem->getTag())->clear(); } $result = $writeResult->getDeletedCount(); if ($result) { - if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) { - list($a, $val) = explode('|', $key); - - $item[$pk] = $val; - $data = $item; - } - - $query->setOption('data', $data); - $query->trigger('after_delete'); + $this->db->trigger('after_delete', $query); } - return $result; - } - - /** - * 执行查询但只返回Cursor对象 - * @access public - * @param Query $query 查询对象 - * @return Cursor - */ - public function getCursor(Query $query) - { - // 分析查询表达式 - $options = $query->parseOptions(); - // 生成MongoQuery对象 - $mongoQuery = $this->builder->select($query); - - // 执行查询操作 - $readPreference = isset($options['readPreference']) ? $options['readPreference'] : null; - - return $this->query($options['table'], $mongoQuery, $readPreference, true, $options['typeMap']); + return $result; } /** * 查找记录 * @access public - * @param Query $query 查询对象 - * @return Collection|false|Cursor|string + * @param Query $query 查询对象 + * @return Collection|array * @throws ModelNotFoundException * @throws DataNotFoundException * @throws AuthenticationException @@ -1029,36 +945,33 @@ class Mongo */ public function select(Query $query) { - $options = $query->parseOptions(); - $resultSet = false; - if ($this->cache && !empty($options['cache'])) { - // 判断查询缓存 - $cache = $options['cache']; - $key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options)); - $resultSet = $this->cache->get($key); + $options = $query->parseOptions(); + + if (!empty($options['cache'])) { + $cacheItem = $this->parseCache($query, $options['cache']); + $resultSet = $this->getCacheData($cacheItem); + + if (false !== $resultSet) { + return $resultSet; + } } - if (!$resultSet) { - // 生成MongoQuery对象 - $mongoQuery = $this->builder->select($query); + // 生成MongoQuery对象 + $mongoQuery = $this->builder->select($query); - if ($resultSet = $query->trigger('before_select')) { - } else { - // 执行查询操作 - $readPreference = isset($options['readPreference']) ? $options['readPreference'] : null; + $resultSet = $this->db->trigger('before_select', $query); - $resultSet = $this->query($options['table'], $mongoQuery, $readPreference, $options['fetch_cursor'], $options['typeMap']); + if (!$resultSet) { + // 执行查询操作 + $readPreference = $options['readPreference'] ?? null; - if ($resultSet instanceof Cursor) { - // 返回MongoDB\Driver\Cursor对象 - return $resultSet; - } - } + $resultSet = $this->query($options['table'], $mongoQuery, $readPreference, $options['typeMap']); + } - if (isset($cache)) { - // 缓存数据集 - $this->cacheData($key, $resultSet, $cache); - } + if (isset($cacheItem) && false !== $resultSet) { + // 缓存数据集 + $cacheItem->set($resultSet); + $this->cacheData($cacheItem); } return $resultSet; @@ -1067,8 +980,8 @@ class Mongo /** * 查找单条记录 * @access public - * @param Query $query 查询对象 - * @return array|null|Cursor|string|Model + * @param Query $query 查询对象 + * @return array * @throws ModelNotFoundException * @throws DataNotFoundException * @throws AuthenticationException @@ -1080,276 +993,208 @@ class Mongo { // 分析查询表达式 $options = $query->parseOptions(); - $pk = $query->getPk($options); - $data = $options['data']; - if ($this->cache && !empty($options['cache']) && true === $options['cache']['key'] && is_string($pk) && isset($options['where']['$and'][$pk])) { - $key = $this->getCacheKey($options['where']['$and'][$pk], $options); - } - $result = false; - if ($this->cache && !empty($options['cache'])) { + if (!empty($options['cache'])) { // 判断查询缓存 - $cache = $options['cache']; - if (true === $cache['key'] && !is_null($data) && !is_array($data)) { - $key = 'mongo:' . $options['table'] . '|' . $data; - } elseif (!isset($key)) { - $key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options)); - } - $result = $this->cache->get($key); + $cacheItem = $this->parseCache($query, $options['cache']); + $key = $cacheItem->getKey(); } - if (false === $result) { - - if (is_string($pk)) { - if (!is_array($data)) { - if (isset($key) && strpos($key, '|')) { - list($a, $val) = explode('|', $key); - $item[$pk] = $val; - } else { - $item[$pk] = $data; - } - $data = $item; - } - } + if (isset($key)) { + $result = $this->cache->get($key); - $query->setOption('data', $data); - $query->setOption('limit', 1); + if (false !== $result) { + return $result; + } + } - // 生成查询对象 - $mongoQuery = $this->builder->select($query); + // 生成查询对象 + $mongoQuery = $this->builder->select($query, true); - // 事件回调 - if ($result = $query->trigger('before_find')) { - } else { - // 执行查询 - $readPreference = isset($options['readPreference']) ? $options['readPreference'] : null; - $resultSet = $this->query($options['table'], $mongoQuery, $readPreference, $options['fetch_cursor'], $options['typeMap']); + // 事件回调 + $result = $this->db->trigger('before_find', $query); - if ($resultSet instanceof Cursor) { - // 返回MongoDB\Driver\Cursor对象 - return $resultSet; - } + if (!$result) { + // 执行查询 + $readPreference = $options['readPreference'] ?? null; + $resultSet = $this->query($options['table'], $mongoQuery, $readPreference, $options['typeMap']); - $result = isset($resultSet[0]) ? $resultSet[0] : null; - } + $result = $resultSet[0] ?? null; + } - if (isset($cache)) { - // 缓存数据 - $this->cacheData($key, $result, $cache); - } + if (isset($cache) && $result) { + // 缓存数据 + $cacheItem->set($result); + $this->cacheData($cacheItem); } return $result; } /** - * 缓存数据 - * @access public - * @param string $key 缓存标识 - * @param mixed $data 缓存数据 - * @param array $config 缓存参数 + * 获取缓存数据 + * @access protected + * @param Query $query 查询对象 + * @param mixed $cache 缓存设置 + * @param array $data 缓存数据 + * @param string $key 缓存Key + * @return mixed */ - protected function cacheData($key, $data, $config = []) + protected function getCacheData(CacheItem $cacheItem) { - $this->cache->set($key, $data, $config['expire']); + // 判断查询缓存 + return $this->cache->get($cacheItem->getKey()); } /** - * 生成缓存标识 - * @access public - * @param mixed $value 缓存数据 - * @param array $options 缓存参数 + * 缓存数据 + * @access protected + * @param CacheItem $cacheItem 缓存Item */ - protected function getCacheKey($value, $options) + protected function cacheData(CacheItem $cacheItem): void { - if (is_scalar($value)) { - $data = $value; - } elseif (is_array($value) && 'eq' == strtolower($value[0])) { - $data = $value[1]; + if ($cacheItem->getTag()) { + $this->cache->tag($cacheItem->getTag()); } - if (isset($data)) { - return 'mongo:' . $options['table'] . '|' . $data; - } else { - return md5(serialize($options)); - } + $this->cache->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire()); } - /** - * 获取数据表信息 - * @access public - * @param string $tableName 数据表名 留空自动获取 - * @param string $fetch 获取信息类型 包括 fields type pk - * @return mixed - */ - public function getTableInfo($tableName, $fetch = '') + protected function parseCache(Query $query, array $cache): CacheItem { - if (is_array($tableName)) { - $tableName = key($tableName) ?: current($tableName); - } + list($key, $expire, $tag) = $cache; - if (strpos($tableName, ',')) { - // 多表不获取字段信息 - return false; + if ($key instanceof CacheItem) { + $cacheItem = $key; } else { - $tableName = $this->parseSqlTable($tableName); - } - - $guid = md5($tableName); - if (!isset(self::$info[$guid])) { - $mongoQuery = new MongoQuery([], ['limit' => 1]); - - $cursor = $this->query($tableName, $mongoQuery, null, true, ['root' => 'array', 'document' => 'array']); - - $resultSet = $cursor->toArray(); - $result = isset($resultSet[0]) ? (array) $resultSet[0] : []; - $fields = array_keys($result); - $type = []; - - foreach ($result as $key => $val) { - // 记录字段类型 - $type[$key] = getType($val); - if ('_id' == $key) { - $pk = $key; + if (true === $key) { + if (!empty($query->getOptions('key'))) { + $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); + } else { + $key = md5($this->getConfig('database') . serialize($query->getOptions())); } } - if (!isset($pk)) { - // 设置主键 - $pk = null; - } - - $result = ['fields' => $fields, 'type' => $type, 'pk' => $pk]; - - self::$info[$guid] = $result; + $cacheItem = new CacheItem($key); + $cacheItem->expire($expire); + $cacheItem->tag($tag); } - return $fetch ? self::$info[$guid][$fetch] : self::$info[$guid]; + return $cacheItem; } /** * 得到某个字段的值 * @access public - * @param string $field 字段名 - * @param mixed $default 默认值 + * @param string $field 字段名 + * @param mixed $default 默认值 * @return mixed */ - public function value(Query $query, $field, $default = null) + public function value(Query $query, string $field, $default = null) { $options = $query->parseOptions(); - $result = null; - if ($this->cache && !empty($options['cache'])) { - // 判断查询缓存 - $cache = $options['cache']; - $key = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($options)); - $result = $this->cache->get($key); + + if (isset($options['projection'])) { + $query->removeOption('projection'); } - if (!$result) { - if (isset($options['field'])) { - $query->removeOption('field'); + $query->setOption('projection', (array) $field); + + if (!empty($options['cache'])) { + $cacheItem = $this->parseCache($query, $options['cache']); + $result = $this->getCacheData($cacheItem); + + if (false !== $result) { + return $result; } + } - $query->setOption('field', $field); - $query->setOption('limit', 1); + $mongoQuery = $this->builder->select($query, true); - $mongoQuery = $this->builder->select($query); + if (isset($options['projection'])) { + $query->setOption('projection', $options['projection']); + } else { + $query->removeOption('projection'); + } - // 执行查询操作 - $readPreference = isset($options['readPreference']) ? $options['readPreference'] : null; - $cursor = $this->query($options['table'], $mongoQuery, $readPreference, true, ['root' => 'array']); - $resultSet = $cursor->toArray(); - if (!empty($resultSet)) { - $data = (array) array_shift($resultSet); - if ($this->getConfig('pk_convert_id')) { - // 转换ObjectID 字段 - $data['id'] = $data['_id']->__toString(); - } - $result = $data[$field]; - } else { - $result = null; - } + // 执行查询操作 + $readPreference = $options['readPreference'] ?? null; + $resultSet = $this->query($options['table'], $mongoQuery, $readPreference); - if (isset($cache)) { - // 缓存数据 - $this->cacheData($key, $result, $cache); - } + if (!empty($resultSet)) { + $data = array_shift($resultSet); + + $result = $data[$field]; + } else { + $result = false; + } + + if (isset($cacheItem) && false !== $result) { + // 缓存数据 + $cacheItem->set($result); + $this->cacheData($cacheItem); } - return !is_null($result) ? $result : $default; + return false !== $result ? $result : $default; } /** * 得到某个列的数组 * @access public - * @param string $field 字段名 多个字段用逗号分隔 - * @param string $key 索引 + * @param string $field 字段名 多个字段用逗号分隔 + * @param string $key 索引 * @return array */ - public function column(Query $query, $field, $key = '') + public function column(Query $query, $field, string $key = '') { $options = $query->parseOptions(); - $result = false; - if ($this->cache && !empty($options['cache'])) { - // 判断查询缓存 - $cache = $options['cache']; - $guid = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($options)); - $result = $this->cache->get($guid); + + if (isset($options['projection'])) { + $query->removeOption('projection'); } - if (!$result) { - if (isset($options['projection'])) { - $query->removeOption('projection'); - } + if ($key && '*' != $field) { + $projection = $key . ',' . $field; + } else { + $projection = $field; + } - if ($key && '*' != $field) { - $field = $key . ',' . $field; - } + $query->setOption('projection', $projection); + + if (!empty($options['cache'])) { + // 判断查询缓存 + $cacheItem = $this->parseCache($query, $options['cache']); + $result = $this->getCacheData($cacheItem); - if (is_string($field)) { - $field = array_map('trim', explode(',', $field)); + if (false !== $result) { + return $result; } + } - $query->field($field); + $mongoQuery = $this->builder->select($query); - $mongoQuery = $this->builder->select($query); - // 执行查询操作 - $readPreference = isset($options['readPreference']) ? $options['readPreference'] : null; - $cursor = $this->query($options['table'], $mongoQuery, $readPreference, true, ['root' => 'array']); - $resultSet = $cursor->toArray(); - - if ($resultSet) { - $fields = array_keys(get_object_vars($resultSet[0])); - $count = count($fields); - $key1 = array_shift($fields); - $key2 = $fields ? array_shift($fields) : ''; - $key = $key ?: $key1; - - foreach ($resultSet as $val) { - $val = (array) $val; - if ($this->getConfig('pk_convert_id')) { - // 转换ObjectID 字段 - $val['id'] = $val['_id']->__toString(); - unset($val['_id']); - } - $name = $val[$key]; - - if (2 == $count) { - $result[$name] = $val[$key2]; - } elseif (1 == $count) { - $result[$name] = $val[$key1]; - } else { - $result[$name] = $val; - } - } - } else { - $result = []; - } + if (isset($options['projection'])) { + $query->setOption('projection', $options['projection']); + } else { + $query->removeOption('projection'); + } - if (isset($cache) && isset($guid)) { - // 缓存数据 - $this->cacheData($guid, $result, $cache); - } + // 执行查询操作 + $readPreference = $options['readPreference'] ?? null; + $resultSet = $this->query($options['table'], $mongoQuery, $readPreference); + + if (('*' == $field || strpos($field, ',')) && $key) { + $result = array_column($resultSet, null, $key); + } elseif (!empty($resultSet)) { + $result = array_column($resultSet, $field, $key); + } else { + $result = []; + } + + if (isset($cacheItem)) { + // 缓存数据 + $cacheItem->set($result); + $this->cacheData($cacheItem); } return $result; @@ -1358,13 +1203,13 @@ class Mongo /** * 执行command * @access public - * @param Query $query 查询对象 - * @param string|array|object $command 指令 - * @param mixed $extra 额外参数 - * @param string $db 数据库名 + * @param Query $query 查询对象 + * @param string|array|object $command 指令 + * @param mixed $extra 额外参数 + * @param string $db 数据库名 * @return array */ - public function cmd(Query $query, $command, $extra = null, $db = null) + public function cmd(Query $query, $command, $extra = null, string $db = ''): array { if (is_array($command) || is_object($command)) { if ($this->getConfig('debug')) { @@ -1381,84 +1226,45 @@ class Mongo return $this->command($command, $db); } - /** - * 数据库连接参数解析 - * @access private - * @param mixed $config - * @return array - */ - private static function parseConfig($config) + // 获取当前数据表字段信息 + public function getTableFields(string $tableName) { - if (empty($config)) { - $config = Db::getConfig(); - } elseif (is_string($config) && false === strpos($config, '/')) { - // 支持读取配置参数 - $config = Db::getConfig($config); - } - - if (is_string($config)) { - return self::parseDsnConfig($config); - } else { - return $config; - } + return []; } - /** - * DSN解析 - * 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1¶m2=val2#utf8 - * @access private - * @param string $dsnStr - * @return array - */ - private static function parseDsnConfig($dsnStr) + // 获取当前数据表字段类型 + public function getFieldsType(string $tableName) { - $info = parse_url($dsnStr); - - if (!$info) { - return []; - } - - $dsn = [ - 'type' => $info['scheme'], - 'username' => isset($info['user']) ? $info['user'] : '', - 'password' => isset($info['pass']) ? $info['pass'] : '', - 'hostname' => isset($info['host']) ? $info['host'] : '', - 'hostport' => isset($info['port']) ? $info['port'] : '', - 'database' => !empty($info['path']) ? ltrim($info['path'], '/') : '', - 'charset' => isset($info['fragment']) ? $info['fragment'] : 'utf8', - ]; - - if (isset($info['query'])) { - parse_str($info['query'], $dsn['params']); - } else { - $dsn['params'] = []; - } - - return $dsn; + return []; } /** - * 获取数据表的主键 + * 启动事务 * @access public - * @param string $tableName 数据表名 - * @return string|array + * @return void + * @throws \PDOException + * @throws \Exception */ - public function getPk($tableName) - { - return $this->getTableInfo($tableName, 'pk'); - } + public function startTrans() + {} - // 获取当前数据表字段信息 - public function getTableFields($tableName) - { - return $this->getTableInfo($tableName, 'fields'); - } + /** + * 用于非自动提交状态下面的查询提交 + * @access public + * @return void + * @throws PDOException + */ + public function commit() + {} - // 获取当前数据表字段类型 - public function getFieldsType($tableName) - { - return $this->getTableInfo($tableName, 'type'); - } + /** + * 事务回滚 + * @access public + * @return void + * @throws PDOException + */ + public function rollback() + {} /** * 析构方法 diff --git a/src/db/connector/Mysql.php b/src/db/connector/Mysql.php index dbdf280..7a39a53 100644 --- a/src/db/connector/Mysql.php +++ b/src/db/connector/Mysql.php @@ -14,7 +14,6 @@ namespace think\db\connector; use PDO; use think\db\Connection; -use think\db\Query; /** * mysql数据库驱动 @@ -22,30 +21,6 @@ use think\db\Query; class Mysql extends Connection { - protected $builder = '\\think\\db\\builder\\Mysql'; - - /** - * 初始化 - * @access protected - * @return void - */ - protected function initialize(): void - { - // Point类型支持 - Query::extend('point', function ($query, $field, $value = null, $fun = 'GeomFromText', $type = 'POINT') { - if (!is_null($value)) { - $query->data($field, ['point', $value, $fun, $type]); - } else { - if (is_string($field)) { - $field = explode(',', $field); - } - $query->setOption('point', $field); - } - - return $query; - }); - } - /** * 解析pdo连接的dsn信息 * @access protected @@ -92,9 +67,10 @@ class Mysql extends Connection $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; - if ($result) { + if (!empty($result)) { foreach ($result as $key => $val) { - $val = array_change_key_case($val); + $val = array_change_key_case($val); + $info[$val['field']] = [ 'name' => $val['field'], 'type' => $val['type'], diff --git a/src/db/connector/Oracle.php b/src/db/connector/Oracle.php index fb97186..7266e11 100644 --- a/src/db/connector/Oracle.php +++ b/src/db/connector/Oracle.php @@ -23,17 +23,21 @@ class Oracle extends Connection * @param array $config 连接信息 * @return string */ - protected function parseDsn($config) + protected function parseDsn(array $config): string { $dsn = 'oci:dbname='; + if (!empty($config['hostname'])) { // Oracle Instant Client $dsn .= '//' . $config['hostname'] . ($config['hostport'] ? ':' . $config['hostport'] : '') . '/'; } + $dsn .= $config['database']; + if (!empty($config['charset'])) { $dsn .= ';charset=' . $config['charset']; } + return $dsn; } @@ -43,14 +47,15 @@ class Oracle extends Connection * @param string $tableName * @return array */ - public function getFields($tableName) + public function getFields(string $tableName): array { - $this->initConnect(true); list($tableName) = explode(' ', $tableName); $sql = "select a.column_name,data_type,DECODE (nullable, 'Y', 0, 1) notnull,data_default, DECODE (A .column_name,b.column_name,1,0) pk from all_tab_columns a,(select column_name from all_constraints c, all_cons_columns col where c.constraint_name = col.constraint_name and c.constraint_type = 'P' and c.table_name = '" . strtoupper($tableName) . "' ) b where table_name = '" . strtoupper($tableName) . "' and a.column_name = b.column_name (+)"; - $pdo = $this->linkID->query($sql); - $result = $pdo->fetchAll(PDO::FETCH_ASSOC); - $info = []; + + $pdo = $this->getPDOStatement($sql); + $result = $pdo->fetchAll(PDO::FETCH_ASSOC); + $info = []; + if ($result) { foreach ($result as $key => $val) { $val = array_change_key_case($val); @@ -64,6 +69,7 @@ class Oracle extends Connection ]; } } + return $this->fieldCase($info); } @@ -73,30 +79,31 @@ class Oracle extends Connection * @param string $dbName * @return array */ - public function getTables($dbName = '') + public function getTables(string $dbName = ''): array { - $pdo = $this->linkID->query("select table_name from all_tables"); + $sql = 'select table_name from all_tables'; + $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; + foreach ($result as $key => $val) { $info[$key] = current($val); } + return $info; } /** * 获取最近插入的ID * @access public - * @param string $sequence 自增序列名 + * @param string $sequence 自增序列名 * @return string */ - public function getLastInsID($sequence = null) + public function getLastInsID(string $sequence = null): string { - if ($sequence === null) { - return ''; - } $pdo = $this->linkID->query("select {$sequence}.currval as id from dual"); $result = $pdo->fetchColumn(); + return $result; } @@ -106,12 +113,12 @@ class Oracle extends Connection * @param string $sql * @return array */ - protected function getExplain($sql) + protected function getExplain(string $sql): array { return []; } - protected function supportSavepoint() + protected function supportSavepoint(): bool { return true; } diff --git a/src/db/connector/Pgsql.php b/src/db/connector/Pgsql.php index 0d068c4..69e90a7 100644 --- a/src/db/connector/Pgsql.php +++ b/src/db/connector/Pgsql.php @@ -19,9 +19,11 @@ use think\db\Connection; */ class Pgsql extends Connection { - protected $builder = '\\think\\db\\builder\\Pgsql'; - // PDO连接参数 + /** + * 默认PDO连接参数 + * @var array + */ protected $params = [ PDO::ATTR_CASE => PDO::CASE_NATURAL, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, @@ -61,9 +63,10 @@ class Pgsql extends Connection $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; - if ($result) { + if (!empty($result)) { foreach ($result as $key => $val) { - $val = array_change_key_case($val); + $val = array_change_key_case($val); + $info[$val['field']] = [ 'name' => $val['field'], 'type' => $val['type'], diff --git a/src/db/connector/Sqlite.php b/src/db/connector/Sqlite.php index 96524b8..2ee5653 100644 --- a/src/db/connector/Sqlite.php +++ b/src/db/connector/Sqlite.php @@ -20,8 +20,6 @@ use think\db\Connection; class Sqlite extends Connection { - protected $builder = '\\think\\db\\builder\\Sqlite'; - /** * 解析pdo连接的dsn信息 * @access protected @@ -50,9 +48,10 @@ class Sqlite extends Connection $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; - if ($result) { + if (!empty($result)) { foreach ($result as $key => $val) { - $val = array_change_key_case($val); + $val = array_change_key_case($val); + $info[$val['name']] = [ 'name' => $val['name'], 'type' => $val['type'], diff --git a/src/db/connector/Sqlsrv.php b/src/db/connector/Sqlsrv.php index 17225df..bd9ea13 100644 --- a/src/db/connector/Sqlsrv.php +++ b/src/db/connector/Sqlsrv.php @@ -19,7 +19,10 @@ use think\db\Connection; */ class Sqlsrv extends Connection { - // PDO连接参数 + /** + * 默认PDO连接参数 + * @var array + */ protected $params = [ PDO::ATTR_CASE => PDO::CASE_NATURAL, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, @@ -27,8 +30,6 @@ class Sqlsrv extends Connection PDO::ATTR_STRINGIFY_FETCHES => false, ]; - protected $builder = '\\think\\db\\builder\\Sqlsrv'; - /** * 解析pdo连接的dsn信息 * @access protected @@ -68,9 +69,10 @@ class Sqlsrv extends Connection $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; - if ($result) { + if (!empty($result)) { foreach ($result as $key => $val) { - $val = array_change_key_case($val); + $val = array_change_key_case($val); + $info[$val['column_name']] = [ 'name' => $val['column_name'], 'type' => $val['data_type'], diff --git a/src/db/exception/BindParamException.php b/src/db/exception/BindParamException.php new file mode 100644 index 0000000..b76b35e --- /dev/null +++ b/src/db/exception/BindParamException.php @@ -0,0 +1,37 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db\exception; + +use think\exception\DbException; + +/** + * PDO参数绑定异常 + */ +class BindParamException extends DbException +{ + + /** + * BindParamException constructor. + * @access public + * @param string $message + * @param array $config + * @param string $sql + * @param array $bind + * @param int $code + */ + public function __construct(string $message, array $config, string $sql, array $bind, int $code = 10502) + { + $this->setData('Bind Param', $bind); + parent::__construct($message, $config, $sql, $code); + } +} diff --git a/src/db/exception/DataNotFoundException.php b/src/db/exception/DataNotFoundException.php new file mode 100644 index 0000000..8bbb10f --- /dev/null +++ b/src/db/exception/DataNotFoundException.php @@ -0,0 +1,45 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db\exception; + +use think\exception\DbException; + +class DataNotFoundException extends DbException +{ + protected $table; + + /** + * DbException constructor. + * @access public + * @param string $message + * @param string $table + * @param array $config + */ + public function __construct(string $message, string $table = '', array $config = []) + { + $this->message = $message; + $this->table = $table; + + $this->setData('Database Config', $config); + } + + /** + * 获取数据表名 + * @access public + * @return string + */ + public function getTable() + { + return $this->table; + } +} diff --git a/src/db/exception/ModelNotFoundException.php b/src/db/exception/ModelNotFoundException.php new file mode 100644 index 0000000..ec7e730 --- /dev/null +++ b/src/db/exception/ModelNotFoundException.php @@ -0,0 +1,46 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db\exception; + +use think\exception\DbException; + +class ModelNotFoundException extends DbException +{ + protected $model; + + /** + * 构造方法 + * @access public + * @param string $message + * @param string $model + * @param array $config + */ + public function __construct(string $message, string $model = '', array $config = []) + { + $this->message = $message; + $this->model = $model; + + $this->setData('Database Config', $config); + } + + /** + * 获取模型类名 + * @access public + * @return string + */ + public function getModel() + { + return $this->model; + } + +} diff --git a/src/facade/Db.php b/src/facade/Db.php new file mode 100644 index 0000000..b31a6aa --- /dev/null +++ b/src/facade/Db.php @@ -0,0 +1,74 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Db + * @mixin \think\Db + * @method object buildQuery(string $query, mixed $connection) static 创建一个新的查询对象 + * @method \think\db\Query connect(array $config =[], mixed $name = false) static 连接/切换数据库连接 + * @method \think\db\Connection getConnection() static 获取数据库连接对象 + * @method \think\db\Query master() static 从主服务器读取数据 + * @method \think\db\Query table(string $table) static 指定数据表(含前缀) + * @method \think\db\Query name(string $name) static 指定数据表(不含前缀) + * @method \think\db\Raw raw(string $value) static 使用表达式设置数据 + * @method \think\db\Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件 + * @method \think\db\Query whereRaw(string $where, array $bind = []) static 表达式查询 + * @method \think\db\Query whereExp(string $field, string $condition, array $bind = []) static 字段表达式查询 + * @method \think\db\Query when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询 + * @method \think\db\Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询 + * @method \think\db\Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询 + * @method \think\db\Query field(mixed $field, boolean $except = false) static 指定查询字段 + * @method \think\db\Query fieldRaw(string $field, array $bind = []) static 指定查询字段 + * @method \think\db\Query union(mixed $union, boolean $all = false) static UNION查询 + * @method \think\db\Query limit(mixed $offset, integer $length = null) static 查询LIMIT + * @method \think\db\Query order(mixed $field, string $order = null) static 查询ORDER + * @method \think\db\Query orderRaw(string $field, array $bind = []) static 查询ORDER + * @method \think\db\Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存 + * @method \think\db\Query withAttr(string $name, callable $callback = null) static 使用获取器获取数据 + * @method mixed value(string $field) static 获取某个字段的值 + * @method array column(string $field, string $key = '') static 获取某个列的值 + * @method mixed find(mixed $data = null) static 查询单个记录 + * @method mixed select(mixed $data = null) static 查询多个记录 + * @method integer save(boolean $forceInsert = false) static 保存记录 自动判断insert或者update + * @method integer insert(array $data, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录 + * @method integer insertGetId(array $data, string $sequence = null) static 插入一条记录并返回自增ID + * @method integer insertAll(array $dataSet) static 插入多条记录 + * @method integer update(array $data) static 更新记录 + * @method integer delete(mixed $data = null) static 删除记录 + * @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据 + * @method \Generator cursor(mixed $data = null) static 使用游标查找记录 + * @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询 + * @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行 + * @method \think\Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询 + * @method mixed transaction(callable $callback) static 执行数据库事务 + * @method void startTrans() static 启动事务 + * @method void commit() static 用于非自动提交状态下面的查询提交 + * @method void rollback() static 事务回滚 + * @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句 + * @method string getLastInsID(string $sequence = null) static 获取最近插入的ID + * @method mixed getConfig(string $name = '') static 获取数据库的配置参数 + */ +class Db extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'think\Db'; + } +} diff --git a/src/model/Collection.php b/src/model/Collection.php index a80ecf3..e920492 100644 --- a/src/model/Collection.php +++ b/src/model/Collection.php @@ -14,19 +14,23 @@ namespace think\model; use think\Collection as BaseCollection; use think\Model; +use think\Paginator; +/** + * 模型数据集类 + */ class Collection extends BaseCollection { /** * 延迟预载入关联查询 * @access public - * @param array $relation 关联 + * @param array|string $relation 关联 * @return $this */ - public function load(array $relation) + public function load($relation) { $item = current($this->items); - $item->eagerlyResultSet($this->items, $relation); + $item->eagerlyResultSet($this->items, (array) $relation); return $this; } @@ -35,13 +39,12 @@ class Collection extends BaseCollection * 设置需要隐藏的输出属性 * @access public * @param array $hidden 属性列表 - * @param bool $override 是否覆盖 * @return $this */ - public function hidden(array $hidden, bool $override = false) + public function hidden(array $hidden) { - $this->each(function (Model $model) use ($hidden, $override) { - $model->hidden($hidden, $override); + $this->each(function (Model $model) use ($hidden) { + $model->hidden($hidden); }); return $this; @@ -51,13 +54,12 @@ class Collection extends BaseCollection * 设置需要输出的属性 * @access public * @param array $visible - * @param bool $override 是否覆盖 * @return $this */ - public function visible(array $visible, bool $override = false) + public function visible(array $visible) { - $this->each(function (Model $model) use ($visible, $override) { - $model->visible($visible, $override); + $this->each(function (Model $model) use ($visible) { + $model->visible($visible); }); return $this; @@ -67,13 +69,12 @@ class Collection extends BaseCollection * 设置需要追加的输出属性 * @access public * @param array $append 属性列表 - * @param bool $override 是否覆盖 * @return $this */ - public function append(array $append, bool $override = false) + public function append(array $append) { - $this->each(function (Model $model) use ($append, $override) { - $model && $model->append($append, $override); + $this->each(function (Model $model) use ($append) { + $model->append($append); }); return $this; @@ -88,9 +89,25 @@ class Collection extends BaseCollection */ public function withAttr($name, $callback = null) { - $this->each(function ($model) use ($name, $callback) { - /** @var Model $model */ - $model && $model->withAttribute($name, $callback); + $this->each(function (Model $model) use ($name, $callback) { + $model->withAttribute($name, $callback); + }); + + return $this; + } + + /** + * 绑定(一对一)关联属性到当前模型 + * @access protected + * @param string $relation 关联名称 + * @param array $attrs 绑定属性 + * @return $this + * @throws Exception + */ + public function bindAttr($relation, $attrs = []) + { + $this->each(function (Model $model) use ($relation, $attrs) { + $model->bindAttr($relation, $attrs); }); return $this; diff --git a/src/model/Pivot.php b/src/model/Pivot.php index d5e93d3..7a7a12a 100644 --- a/src/model/Pivot.php +++ b/src/model/Pivot.php @@ -14,12 +14,22 @@ namespace think\model; use think\Model; +/** + * 多对多中间表模型类 + */ class Pivot extends Model { - /** @var Model */ + /** + * 父模型 + * @var Model + */ public $parent; + /** + * 是否时间自动写入 + * @var bool + */ protected $autoWriteTimestamp = false; /** @@ -29,7 +39,7 @@ class Pivot extends Model * @param Model $parent 上级模型 * @param string $table 中间数据表名 */ - public function __construct(array $data = [], $parent = null, string $table = '') + public function __construct(array $data = [], Model $parent = null, string $table = '') { $this->parent = $parent; diff --git a/src/model/Relation.php b/src/model/Relation.php index 5b9f247..027a524 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -17,26 +17,53 @@ use think\Exception; use think\Model; /** - * Class Relation + * 模型关联基础类 * @package think\model * * @mixin Query */ abstract class Relation { - // 父模型对象 + /** + * 父模型对象 + * @var Model + */ protected $parent; - /** @var Model 当前关联的模型类 */ + + /** + * 当前关联的模型类名 + * @var string + */ protected $model; - /** @var Query 关联模型查询对象 */ + + /** + * 关联模型查询对象 + * @var Query + */ protected $query; - // 关联表外键 + + /** + * 关联表外键 + * @var string + */ protected $foreignKey; - // 关联表主键 + + /** + * 关联表主键 + * @var string + */ protected $localKey; - // 基础查询 + + /** + * 是否执行关联基础查询 + * @var bool + */ protected $baseQuery; - // 是否为自关联 + + /** + * 是否为自关联 + * @var bool + */ protected $selfRelation; /** @@ -59,18 +86,6 @@ abstract class Relation return $this->query->getModel(); } - /** - * 设置当前关联为自关联 - * @access public - * @param bool $self 是否自关联 - * @return $this - */ - public function selfRelation(bool $self = true) - { - $this->selfRelation = $self; - return $this; - } - /** * 当前关联是否为自关联 * @access public @@ -157,8 +172,9 @@ abstract class Relation $this->baseQuery(); $result = call_user_func_array([$this->query->getModel(), $method], $args); + $class = get_class($this->query); - return $result === $this->query ? $this : $result; + return $result instanceof $class ? $this : $result; } throw new Exception('method not exists:' . __CLASS__ . '->' . $method); diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 4f9102f..4ff5e42 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -13,10 +13,13 @@ declare (strict_types = 1); namespace think\model\concern; use InvalidArgumentException; -use think\Db; -use think\Exception; +use think\db\Raw; +use think\facade\Db; use think\model\Relation; +/** + * 模型数据处理 + */ trait Attribute { /** @@ -38,7 +41,7 @@ trait Attribute protected $field = []; /** - * 数据表字段类型 + * 字段自动类型转换 * @var array */ protected $type = []; @@ -73,6 +76,12 @@ trait Attribute */ protected $json = []; + /** + * JSON数据表字段类型 + * @var array + */ + protected $jsonType = []; + /** * JSON数据取出是否需要转换为数组 * @var bool @@ -172,9 +181,9 @@ trait Attribute * 获取实际的字段名 * @access public * @param string $name 字段名 - * @return $this + * @return string */ - protected function getRealFieldName($name) + protected function getRealFieldName(string $name): string { return $this->strict ? $name : Db::parseName($name); } @@ -220,9 +229,9 @@ trait Attribute } /** - * 批量设置数据对象值 + * 批量追加数据对象值 * @access public - * @param mixed $data 数据 + * @param array $data 数据 * @param bool $set 是否需要进行数据处理 * @return $this */ @@ -283,17 +292,13 @@ trait Attribute */ public function getChangedData(): array { - if ($this->force) { - $data = $this->data; - } else { - $data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) { - if ((empty($a) || empty($b)) && $a !== $b) { - return 1; - } + $data = $this->force ? $this->data : array_udiff_assoc($this->data, $this->origin, function ($a, $b) { + if ((empty($a) || empty($b)) && $a !== $b) { + return 1; + } - return is_object($a) || $a != $b ? 1 : 0; - }); - } + return is_object($a) || $a != $b ? 1 : 0; + }); // 只读字段不允许更新 foreach ($this->readonly as $key => $field) { @@ -370,54 +375,6 @@ trait Attribute $this->data[$name] = $value; } - /** - * 是否需要自动写入时间字段 - * @access public - * @param bool $auto - * @return $this - */ - public function isAutoWriteTimestamp(bool $auto) - { - $this->autoWriteTimestamp = $auto; - - return $this; - } - - /** - * 自动写入时间戳 - * @access protected - * @param string $name 时间戳字段 - * @return mixed - */ - protected function autoWriteTimestamp(string $name) - { - $value = time(); - - if (isset($this->type[$name])) { - $type = $this->type[$name]; - - if (strpos($type, ':')) { - list($type, $param) = explode(':', $type, 2); - } - - switch ($type) { - case 'datetime': - case 'date': - case 'timestamp': - $format = !empty($param) ? $param : $this->dateFormat; - $format .= strpos($format, 'u') || false !== strpos($format, '\\') ? '' : '.u'; - $value = $this->formatDateTime($format); - break; - } - } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), - ['datetime', 'date', 'timestamp'])) { - $format = strpos($this->dateFormat, 'u') || false !== strpos($this->dateFormat, '\\') ? '' : '.u'; - $value = $this->formatDateTime($this->dateFormat . $format); - } - - return $value; - } - /** * 数据写入 类型转换 * @access protected @@ -431,6 +388,10 @@ trait Attribute return; } + if ($value instanceof Raw) { + return $value; + } + if (is_array($type)) { list($type, $param) = $type; } elseif (strpos($type, ':')) { @@ -457,9 +418,9 @@ trait Attribute } break; case 'datetime': - $format = !empty($param) ? $param : $this->dateFormat; - $value = is_numeric($value) ? $value : strtotime($value); - $value = $this->formatDateTime($format, $value); + + $value = is_numeric($value) ? $value : strtotime($value); + $value = $this->formatDateTime('Y-m-d H:i:s.u', $value); break; case 'object': if (is_object($value)) { @@ -475,6 +436,11 @@ trait Attribute case 'serialize': $value = serialize($value); break; + default: + if (is_object($value) && false !== strpos($type, '\\') && method_exists($value, '__toString')) { + // 对象类型 + $value = $value->__toString(); + } } return $value; @@ -484,33 +450,46 @@ trait Attribute * 获取器 获取数据对象的值 * @access public * @param string $name 名称 - * @param array $item 数据 * @return mixed * @throws InvalidArgumentException */ - public function getAttr(string $name, array &$item = []) + public function getAttr(string $name) { try { - $notFound = false; + $relation = false; $value = $this->getData($name); } catch (InvalidArgumentException $e) { - $notFound = true; + $relation = true; $value = null; } + return $this->getValue($name, $value, $relation); + } + + /** + * 获取经过获取器处理后的数据对象的值 + * @access protected + * @param string $name 字段名称 + * @param mixed $value 字段值 + * @param bool $relation 是否为关联属性 + * @return mixed + * @throws InvalidArgumentException + */ + protected function getValue(string $name, $value, bool $relation = false) + { // 检测属性获取器 $fieldName = $this->getRealFieldName($name); $method = 'get' . Db::parseName($name, 1) . 'Attr'; if (isset($this->withAttr[$fieldName])) { - if ($notFound) { + if ($relation) { $value = $this->getRelationValue($name); } $closure = $this->withAttr[$fieldName]; $value = $closure($value, $this->data); } elseif (method_exists($this, $method)) { - if ($notFound) { + if ($relation) { $value = $this->getRelationValue($name); } @@ -519,22 +498,20 @@ trait Attribute // 类型转换 $value = $this->readTransform($value, $this->type[$fieldName]); } elseif ($this->autoWriteTimestamp && in_array($fieldName, [$this->createTime, $this->updateTime])) { - if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ - 'datetime', - 'date', - 'timestamp', - ])) { - $value = $this->formatDateTime($this->dateFormat, $value); - } else { - $value = $this->formatDateTime($this->dateFormat, $value, true); - } - } elseif ($notFound) { - $value = $this->getRelationAttribute($name, $item); + $value = $this->getTimestampValue($value); + } elseif ($relation) { + $value = $this->getRelationAttribute($name); } return $value; } + /** + * 获取关联属性值 + * @access protected + * @param string $name 属性名 + * @return mixed + */ protected function getRelationValue(string $name) { $relation = $this->isRelationAttr($name); @@ -549,13 +526,12 @@ trait Attribute } /** - * 获取关联属性值 + * 获取并保存关联属性值 * @access protected * @param string $name 属性名 - * @param array $item 数据 * @return mixed */ - protected function getRelationAttribute(string $name, array &$item) + protected function getRelationAttribute(string $name) { $value = $this->getRelationValue($name); @@ -563,21 +539,6 @@ trait Attribute throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name); } - if ($item && method_exists($modelRelation, 'getBindAttr') && $bindAttr = $modelRelation->getBindAttr()) { - - foreach ($bindAttr as $key => $attr) { - $key = is_numeric($key) ? $attr : $key; - - if (isset($item[$key])) { - throw new Exception('bind attr has exists:' . $key); - } - - $item[$key] = $value ? $value->getAttr($attr) : null; - } - - return false; - } - // 保存关联对象值 $this->relation[$name] = $value; diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index 6afd78a..aa1b13f 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -13,10 +13,11 @@ declare (strict_types = 1); namespace think\model\concern; use think\Collection; -use think\Db; use think\Exception; +use think\facade\Db; use think\Model; use think\model\Collection as ModelCollection; +use think\model\relation\OneToOne; /** * 模型数据转换处理 @@ -51,12 +52,11 @@ trait Conversion * 设置需要附加的输出属性 * @access public * @param array $append 属性列表 - * @param bool $override 是否覆盖 * @return $this */ - public function append(array $append = [], bool $override = false) + public function append(array $append = []) { - $this->append = $override ? $append : array_merge($this->append, $append); + $this->append = $append; return $this; } @@ -97,12 +97,11 @@ trait Conversion * 设置需要隐藏的输出属性 * @access public * @param array $hidden 属性列表 - * @param bool $override 是否覆盖 * @return $this */ - public function hidden(array $hidden = [], bool $override = false) + public function hidden(array $hidden = []) { - $this->hidden = $override ? $hidden : array_merge($this->hidden, $hidden); + $this->hidden = $hidden; return $this; } @@ -111,12 +110,11 @@ trait Conversion * 设置需要输出的属性 * @access public * @param array $visible - * @param bool $override 是否覆盖 * @return $this */ - public function visible(array $visible = [], bool $override = false) + public function visible(array $visible = []) { - $this->visible = $override ? $visible : array_merge($this->visible, $visible); + $this->visible = $visible; return $this; } @@ -128,24 +126,52 @@ trait Conversion */ public function toArray(): array { - $item = []; - $visible = []; - $hidden = []; + $item = []; + $hasVisible = false; + + foreach ($this->visible as $key => $val) { + if (is_string($val)) { + if (strpos($val, '.')) { + list($relation, $name) = explode('.', $val); + $this->visible[$relation][] = $name; + } else { + $this->visible[$val] = true; + $hasVisible = true; + } + unset($this->visible[$key]); + } + } + + foreach ($this->hidden as $key => $val) { + if (is_string($val)) { + if (strpos($val, '.')) { + list($relation, $name) = explode('.', $val); + $this->hidden[$relation][] = $name; + } else { + $this->hidden[$val] = true; + } + unset($this->hidden[$key]); + } + } // 合并关联数据 $data = array_merge($this->data, $this->relation); - // 过滤属性 - if (!empty($this->visible)) { - $array = $this->parseAttr($this->visible, $visible); - $data = array_intersect_key($data, array_flip($array)); - } elseif (!empty($this->hidden)) { - $array = $this->parseAttr($this->hidden, $hidden, false); - $data = array_diff_key($data, array_flip($array)); - } - foreach ($data as $key => $val) { - $item[$key] = $this->getArrayData($key, $val, $visible, $hidden); + if ($val instanceof Model || $val instanceof ModelCollection) { + // 关联模型对象 + if (isset($this->visible[$key])) { + $val->visible($this->visible[$key]); + } elseif (isset($this->hidden[$key])) { + $val->hidden($this->hidden[$key]); + } + // 关联模型对象 + $item[$key] = $val->toArray(); + } elseif (isset($this->visible[$key])) { + $item[$key] = $this->getAttr($key); + } elseif (!isset($this->hidden[$key]) && !$hasVisible) { + $item[$key] = $this->getAttr($key); + } } // 追加属性(必须定义获取器) @@ -156,7 +182,7 @@ trait Conversion return $item; } - protected function appendAttrToArray(array &$item, $key, string $name) + protected function appendAttrToArray(array &$item, $key, $name) { if (is_array($name)) { // 追加关联对象属性 @@ -180,27 +206,39 @@ trait Conversion $item[$key] = $relation->append([$attr])->toArray(); } else { - $value = $this->getAttr($name, $item); - + $value = $this->getAttr($name); $item[$name] = $value; + + $this->getBindAttr($name, $value, $item); } } - protected function getArrayData(string $key, $val, array $visible, array $hidden) + protected function getBindAttr(string $name, $value, array &$item = []) { - if ($val instanceof Model || $val instanceof ModelCollection) { - // 关联模型对象 - if (isset($visible[$key])) { - $val->visible($visible[$key]); - } elseif (isset($hidden[$key])) { - $val->hidden($hidden[$key]); - } - // 关联模型对象 - return $val->toArray(); + $relation = $this->isRelationAttr($name); + if (!$relation) { + return false; } - // 模型属性 - return $this->getAttr($key); + $modelRelation = $this->$relation(); + + if ($modelRelation instanceof OneToOne) { + $bindAttr = $modelRelation->getBindAttr(); + + if (!empty($bindAttr)) { + unset($item[$name]); + } + + foreach ($bindAttr as $key => $attr) { + $key = is_numeric($key) ? $attr : $key; + + if (isset($item[$key])) { + throw new Exception('bind attr has exists:' . $key); + } + + $item[$key] = $value ? $value->getAttr($attr) : null; + } + } } /** @@ -214,17 +252,6 @@ trait Conversion return json_encode($this->toArray(), $options); } - /** - * 移除当前模型的关联属性 - * @access public - * @return $this - */ - public function removeRelation() - { - $this->relation = []; - return $this; - } - public function __toString() { return $this->toJson(); @@ -243,7 +270,7 @@ trait Conversion * @param string $resultSetType 数据集类 * @return Collection */ - public function toCollection(iterable $collection, string $resultSetType = null): Collection + public function toCollection(iterable $collection = [], string $resultSetType = null): Collection { $resultSetType = $resultSetType ?: $this->resultSetType; @@ -256,38 +283,4 @@ trait Conversion return $collection; } - /** - * 解析隐藏及显示属性 - * @access protected - * @param array $attrs 属性 - * @param array $result 结果集 - * @param bool $visible - * @return array - */ - protected function parseAttr(array $attrs, array &$result, bool $visible = true): array - { - $array = []; - - foreach ($attrs as $key => $val) { - if (is_array($val)) { - if ($visible) { - $array[] = $key; - } - - $result[$key] = $val; - } elseif (strpos($val, '.')) { - list($key, $name) = explode('.', $val); - - if ($visible) { - $array[] = $key; - } - - $result[$key][] = $name; - } else { - $array[] = $val; - } - } - - return $array; - } } diff --git a/src/model/concern/ModelEvent.php b/src/model/concern/ModelEvent.php index 2d2c094..79e06ca 100644 --- a/src/model/concern/ModelEvent.php +++ b/src/model/concern/ModelEvent.php @@ -12,7 +12,9 @@ declare (strict_types = 1); namespace think\model\concern; -use think\Db; +use think\Container; +use think\exception\ModelEventException; +use think\facade\Db; /** * 模型事件处理 @@ -20,22 +22,22 @@ use think\Db; trait ModelEvent { /** - * 模型回调 + * 模型事件观察 * @var array */ - private static $event = []; + protected $observe = ['AfterRead', 'BeforeWrite', 'AfterWrite', 'BeforeInsert', 'AfterInsert', 'BeforeUpdate', 'AfterUpdate', 'BeforeDelete', 'AfterDelete', 'BeforeRestore', 'AfterRestore']; /** - * 模型事件观察 - * @var array + * 模型事件观察者类名 + * @var string */ - protected static $observe = ['after_read', 'before_write', 'after_write', 'before_insert', 'after_insert', 'before_update', 'after_update', 'before_delete', 'after_delete', 'before_restore', 'after_restore']; + protected $observerClass; /** - * 绑定模型事件观察者类 + * Event * @var array */ - protected $observerClass; + protected $event = []; /** * 是否需要事件响应 @@ -43,29 +45,21 @@ trait ModelEvent */ protected $withEvent = true; - /** - * 清除回调方法 - * @access public - * @return void - */ - public static function flush(): void - { - self::$event[static::class] = []; - } - /** * 注册一个模型观察者 * - * @param string $class + * @param string $class 观察者类 * @return void */ - protected static function observe(string $class): void + protected function observe(string $class): void { - foreach (static::$observe as $event) { - $call = 'on' . Db::parseName($event, 1, false); + foreach ($this->observe as $event) { + $call = 'on' . $event; if (method_exists($class, $call)) { - self::$event[static::class][$event][] = [$class, $call]; + $instance = Container::getInstance()->invokeClass($class); + + $this->event[$event][] = [$instance, $call]; } } } @@ -85,23 +79,29 @@ trait ModelEvent /** * 触发事件 * @access protected - * @param string $event 事件名 + * @param string $event 事件名 * @return bool */ protected function trigger(string $event): bool { - $class = static::class; + if (!$this->withEvent) { + return true; + } - if ($this->withEvent && isset(self::$event[$class][$event])) { - foreach (self::$event[$class][$event] as $callback) { - $result = call_user_func_array($callback, [$this]); + $call = 'on' . Db::parseName($event, 1); + $result = true; - if (false === $result) { - return false; - } + try { + if (method_exists(static::class, $call)) { + $callback = [static::class, $call]; + } elseif ($this->observerClass && method_exists($this->observerClass, $call)) { + $callback = [$this->observerClass, $call]; } - } - return true; + $result = Container::getInstance()->invoke($callback, [$this]); + return false === $result ? false : true; + } catch (ModelEventException $e) { + return false; + } } } diff --git a/src/model/concern/OptimLock.php b/src/model/concern/OptimLock.php index 038c567..a4d72e6 100644 --- a/src/model/concern/OptimLock.php +++ b/src/model/concern/OptimLock.php @@ -63,9 +63,9 @@ trait OptimLock } } - protected function getUpdateWhere(array &$data): array + public function getWhere() { - $where = parent::getUpdateWhere($data); + $where = parent::getWhere(); $optimLock = $this->getOptimLockField(); if ($optimLock && $lockVer = $this->getOrigin($optimLock)) { diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index d41421f..1753ea0 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -13,8 +13,9 @@ declare (strict_types = 1); namespace think\model\concern; use think\Collection; -use think\Db; use think\db\Query; +use think\Exception; +use think\facade\Db; use think\Model; use think\model\Relation; use think\model\relation\BelongsTo; @@ -47,7 +48,7 @@ trait RelationShip * 关联写入定义信息 * @var array */ - private $together; + private $together = []; /** * 关联自动写入信息 @@ -202,7 +203,7 @@ trait RelationShip * @param array $resultSet 数据集 * @param string $relation 关联名 * @param array $withRelationAttr 关联获取器 - * @param bool $join 是否为JOIN方式 + * @param bool $join 是否为JOIN方式 * @return void */ public function eagerlyResultSet(array &$resultSet, array $relations, array $withRelationAttr = [], bool $join = false): void @@ -244,7 +245,7 @@ trait RelationShip * @param Model $result 数据对象 * @param array $relations 关联 * @param array $withRelationAttr 关联获取器 - * @param bool $join 是否为JOIN方式 + * @param bool $join 是否为JOIN方式 * @return void */ public function eagerlyResult(Model $result, array $relations, array $withRelationAttr = [], bool $join = false): void @@ -280,6 +281,32 @@ trait RelationShip } } + /** + * 绑定(一对一)关联属性到当前模型 + * @access protected + * @param string $relation 关联名称 + * @param array $attrs 绑定属性 + * @return $this + * @throws Exception + */ + public function bindAttr(string $relation, array $attrs = []) + { + $relation = $this->getRelation($relation); + + foreach ($attrs as $key => $attr) { + $key = is_numeric($key) ? $attr : $key; + $value = $this->getOrigin($key); + + if (!is_null($value)) { + throw new Exception('bind attr has exists:' . $key); + } else { + $this->set($key, $relation ? $relation->$attr : null); + } + } + + return $this; + } + /** * 关联统计 * @access public @@ -552,7 +579,7 @@ trait RelationShip /** * 智能获取关联模型数据 * @access protected - * @param Relation $modelRelation 模型关联对象 + * @param Relation $modelRelation 模型关联对象 * @return mixed */ protected function getRelationData(Relation $modelRelation) @@ -604,12 +631,12 @@ trait RelationShip { foreach ($this->relationWrite as $name => $val) { if ($val instanceof Model) { - $val->isUpdate()->save(); + $val->exists(true)->save(); } else { $model = $this->getRelation($name); if ($model instanceof Model) { - $model->isUpdate()->save($val); + $model->exists(true)->save($val); } } } @@ -648,4 +675,15 @@ trait RelationShip } } } + + /** + * 移除当前模型的关联属性 + * @access public + * @return $this + */ + public function removeRelation() + { + $this->relation = []; + return $this; + } } diff --git a/src/model/concern/SoftDelete.php b/src/model/concern/SoftDelete.php index 7e8fce6..1c7e432 100644 --- a/src/model/concern/SoftDelete.php +++ b/src/model/concern/SoftDelete.php @@ -46,7 +46,7 @@ trait SoftDelete * @access public * @return Query */ - public static function withTrashed() + public static function withTrashed(): Query { $model = new static(); @@ -101,7 +101,7 @@ trait SoftDelete */ public function delete(): bool { - if (!$this->isExists() || $this->isEmpty() || false === $this->trigger('before_delete', $this)) { + if (!$this->isExists() || $this->isEmpty() || false === $this->trigger('BeforeDelete')) { return false; } @@ -109,9 +109,9 @@ trait SoftDelete if ($name && !$this->isForce()) { // 软删除 - $this->data($name, $this->autoWriteTimestamp($name)); + $this->set($name, $this->autoWriteTimestamp($name)); - $result = $this->isUpdate()->withEvent(false)->save(); + $result = $this->exists()->withEvent(false)->save(); $this->withEvent(true); } else { @@ -132,7 +132,7 @@ trait SoftDelete $this->autoRelationDelete(); } - $this->trigger('after_delete', $this); + $this->trigger('AfterDelete'); $this->exists(false); @@ -180,7 +180,7 @@ trait SoftDelete { $name = $this->getDeleteTimeField(); - if (!$name || false === $this->trigger('before_restore')) { + if (!$name || false === $this->trigger('BeforeRestore')) { return false; } @@ -197,7 +197,7 @@ trait SoftDelete ->useSoftDelete($name, $this->getWithTrashedExp()) ->update([$name => $this->defaultSoftDelete]); - $this->trigger('after_restore'); + $this->trigger('AfterRestore'); return true; } @@ -239,7 +239,8 @@ trait SoftDelete $field = $this->getDeleteTimeField(true); if ($field) { - $query->useSoftDelete($field, $this->defaultSoftDelete); + $condition = is_null($this->defaultSoftDelete) ? ['null', ''] : ['=', $this->defaultSoftDelete]; + $query->useSoftDelete($field, $condition); } } } diff --git a/src/model/concern/TimeStamp.php b/src/model/concern/TimeStamp.php index c0db4d6..e0969d8 100644 --- a/src/model/concern/TimeStamp.php +++ b/src/model/concern/TimeStamp.php @@ -43,6 +43,93 @@ trait TimeStamp */ protected $dateFormat; + /** + * 是否需要自动写入时间字段 + * @access public + * @param bool|string $auto + * @return $this + */ + public function isAutoWriteTimestamp($auto) + { + $this->autoWriteTimestamp = $auto; + + return $this; + } + + /** + * 获取自动写入时间字段 + * @access public + * @return bool|string + */ + public function getAutoWriteTimestamp() + { + return $this->autoWriteTimestamp; + } + + /** + * 设置时间字段格式化 + * @access public + * @param string $format + * @return $this + */ + public function setDateFormat(string $format) + { + $this->dateFormat = $format; + + return $this; + } + + /** + * 获取自动写入时间字段 + * @access public + * @return string + */ + public function getDateFormat() + { + return $this->dateFormat; + } + + /** + * 自动写入时间戳 + * @access protected + * @param string $name 时间戳字段 + * @return mixed + */ + protected function autoWriteTimestamp(string $name) + { + $value = time(); + + if (isset($this->type[$name])) { + $type = $this->type[$name]; + + if (strpos($type, ':')) { + list($type, $param) = explode(':', $type, 2); + } + + switch ($type) { + case 'datetime': + case 'date': + case 'timestamp': + $value = $this->formatDateTime('Y-m-d H:i:s.u'); + break; + default: + if (false !== strpos($type, '\\')) { + // 对象数据写入 + $value = new $type(); + if (method_exists($value, '__toString')) { + // 对象数据写入 + $value = $value->__toString(); + } + } + } + } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), + ['datetime', 'date', 'timestamp'])) { + $value = $this->formatDateTime('Y-m-d H:i:s.u'); + } + + return $value; + } + /** * 时间日期字段格式化处理 * @access protected @@ -75,4 +162,22 @@ trait TimeStamp return $dateTime->format($format); } + /** + * 获取时间字段值 + * @access protected + * @param mixed $value + * @return mixed + */ + protected function getTimestampValue($value) + { + if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ + 'datetime', 'date', 'timestamp', + ])) { + $value = $this->formatDateTime($this->dateFormat, $value); + } else { + $value = $this->formatDateTime($this->dateFormat, $value, true); + } + + return $value; + } } diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index a8a7ef8..f9a9b23 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -13,9 +13,12 @@ declare (strict_types = 1); namespace think\model\relation; use Closure; -use think\Db; +use think\facade\Db; use think\Model; +/** + * BelongsTo关联类 + */ class BelongsTo extends OneToOne { /** @@ -81,11 +84,7 @@ class BelongsTo extends OneToOne public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', &$name = ''): string { if ($closure) { - $closure($this->query); - - if ($return && is_string($return)) { - $name = $return; - } + $closure($this->query, $name); } return $this->query @@ -115,7 +114,7 @@ class BelongsTo extends OneToOne if ($closure) { $return = $closure($this->query); - if ($resturn && is_string($return)) { + if ($return && is_string($return)) { $name = $return; } } @@ -157,7 +156,7 @@ class BelongsTo extends OneToOne * @param mixed $where 查询条件(数组或者闭包) * @param mixed $fields 字段 * @param string $joinType JOIN类型 - * @return Query + * @return \think\db\Query */ public function hasWhere($where = [], $fields = null, string $joinType = ''): Query { @@ -218,10 +217,10 @@ class BelongsTo extends OneToOne } else { $relationModel = $data[$result->$foreignKey]; $relationModel->setParent(clone $result); - $relationModel->isUpdate(true); + $relationModel->exists(true); } - if (!empty($this->bindAttr)) { + if ($relationModel && !empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($relationModel, $result); } else { @@ -258,10 +257,10 @@ class BelongsTo extends OneToOne } else { $relationModel = $data[$result->$foreignKey]; $relationModel->setParent(clone $result); - $relationModel->isUpdate(true); + $relationModel->exists(true); } - if (!empty($this->bindAttr)) { + if ($relationModel && !empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($relationModel, $result); } else { diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 04bf30f..c6164e1 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -13,22 +13,42 @@ namespace think\model\relation; use Closure; use think\Collection; -use think\Db; use think\db\Query; +use think\db\Raw; use think\Exception; +use think\facade\Db; use think\Model; use think\model\Pivot; use think\model\Relation; +use think\Paginator; +/** + * 多对多关联类 + */ class BelongsToMany extends Relation { - // 中间表表名 + /** + * 中间表表名 + * @var string + */ protected $middle; - // 中间表模型名称 + + /** + * 中间表模型名称 + * @var string + */ protected $pivotName; - // 中间表模型对象 + + /** + * 中间表模型对象 + * @var Pivot + */ protected $pivot; - // 中间表数据名称 + + /** + * 中间表数据名称 + * @var string + */ protected $pivotDataName = 'pivot'; /** @@ -76,7 +96,7 @@ class BelongsToMany extends Relation * @param string $name * @return $this */ - public function pivotDataName(string $name) + public function name(string $name) { $this->pivotDataName = $name; return $this; @@ -139,9 +159,9 @@ class BelongsToMany extends Relation // 关联查询 $pk = $this->parent->getPk(); - $condition[] = ['pivot.' . $localKey, '=', $this->parent->$pk]; + $condition = ['pivot.' . $localKey, '=', $this->parent->$pk]; - return $this->belongsToManyQuery($foreignKey, $localKey, $condition); + return $this->belongsToManyQuery($foreignKey, $localKey, [$condition]); } /** @@ -180,9 +200,9 @@ class BelongsToMany extends Relation /** * 重载paginate方法 * @access public - * @param null $listRows - * @param bool $simple - * @param array $config + * @param int|array $listRows + * @param int|bool $simple + * @param array $config * @return Paginator */ public function paginate($listRows = null, $simple = false, $config = []): Paginator @@ -203,7 +223,7 @@ class BelongsToMany extends Relation { $result = $this->buildQuery()->find($data); - if ($result) { + if (!$result->isEmpty()) { $this->hydratePivot([$result]); } @@ -218,7 +238,7 @@ class BelongsToMany extends Relation */ public function selectOrFail($data = null): Collection { - return $this->failException(true)->select($data); + return $this->buildQuery()->failException(true)->select($data); } /** @@ -229,7 +249,7 @@ class BelongsToMany extends Relation */ public function findOrFail($data = null): Model { - return $this->failException(true)->find($data); + return $this->buildQuery()->failException(true)->find($data); } /** @@ -239,9 +259,9 @@ class BelongsToMany extends Relation * @param integer $count 个数 * @param string $id 关联表的统计字段 * @param string $joinType JOIN类型 - * @return Query + * @return Model */ - public function has(string $operator = '>=', $count = 1, $id = '*', string $joinType = 'INNER'): Query + public function has(string $operator = '>=', $count = 1, $id = '*', string $joinType = 'INNER') { return $this->parent; } @@ -284,11 +304,10 @@ class BelongsToMany extends Relation */ public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void { - $localKey = $this->localKey; - $foreignKey = $this->foreignKey; + $localKey = $this->localKey; + $pk = $resultSet[0]->getPk(); + $range = []; - $pk = $resultSet[0]->getPk(); - $range = []; foreach ($resultSet as $result) { // 获取关联外键列表 if (isset($result->$pk)) { @@ -366,11 +385,7 @@ class BelongsToMany extends Relation $pk = $result->$pk; if ($closure) { - $return = $closure($this->query); - - if ($return && is_string($return)) { - $name = $return; - } + $closure($this->query, $name); } return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ @@ -399,7 +414,7 @@ class BelongsToMany extends Relation return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ [ - 'pivot.' . $this->localKey, 'exp', $this->query->raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()), + 'pivot.' . $this->localKey, 'exp', new Raw('=' . $this->parent->db(false)->getTable() . '.' . $this->parent->getPk()), ], ])->fetchSql()->$aggregate($field); } @@ -458,7 +473,7 @@ class BelongsToMany extends Relation { // 关联查询封装 $tableName = $this->query->getTable(); - $table = $this->pivot->getTable(); + $table = $this->pivot->db()->getTable(); $fields = $this->getQueryFields($tableName); $query = $this->query @@ -490,18 +505,18 @@ class BelongsToMany extends Relation /** * 批量保存当前关联数据对象 * @access public - * @param array $dataSet 数据集 - * @param array $pivot 中间表额外数据 - * @param bool $samePivot 额外数据是否相同 + * @param iterable $dataSet 数据集 + * @param array $pivot 中间表额外数据 + * @param bool $samePivot 额外数据是否相同 * @return array|false */ - public function saveAll(array $dataSet, array $pivot = [], bool $samePivot = false) + public function saveAll(iterable $dataSet, array $pivot = [], bool $samePivot = false) { $result = []; foreach ($dataSet as $key => $data) { if (!$samePivot) { - $pivotData = isset($pivot[$key]) ? $pivot[$key] : []; + $pivotData = $pivot[$key] ?? []; } else { $pivotData = $pivot; } @@ -539,7 +554,7 @@ class BelongsToMany extends Relation $id = $data->$relationFk; } - if ($id) { + if (!empty($id)) { // 保存中间表数据 $pk = $this->parent->getPk(); $pivot[$this->localKey] = $this->parent->$pk; @@ -609,6 +624,7 @@ class BelongsToMany extends Relation // 删除中间表数据 $pk = $this->parent->getPk(); + $pivot = []; $pivot[] = [$this->localKey, '=', $this->parent->$pk]; if (isset($id)) { diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index dfedb3f..ead78fa 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -14,11 +14,14 @@ namespace think\model\relation; use Closure; use think\Collection; -use think\Db; use think\db\Query; +use think\facade\Db; use think\Model; use think\model\Relation; +/** + * 一对多关联类 + */ class HasMany extends Relation { /** @@ -129,9 +132,9 @@ class HasMany extends Relation $localKey = $this->localKey; if (isset($result->$localKey)) { - $pk = $result->$localKey; - $where[] = [$this->foreignKey, '=', $pk]; - $data = $this->eagerlyOneToMany($where, $relation, $subRelation, $closure); + $pk = $result->$localKey; + $where = [$this->foreignKey, '=', $pk]; + $data = $this->eagerlyOneToMany([$where], $relation, $subRelation, $closure); // 关联数据封装 if (!isset($data[$pk])) { @@ -165,10 +168,7 @@ class HasMany extends Relation } if ($closure) { - $return = $closure($this->query); - if ($resturn && is_string($return)) { - $name = $return; - } + $closure($this->query, $name); } return $this->query @@ -188,11 +188,14 @@ class HasMany extends Relation public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $closure($this->query); + $return = $closure($this->query); + if ($return && is_string($return)) { + $name = $return; + } } - return $this->query - ->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk()) + return $this->query->alias($aggregate . '_table') + ->whereExp($aggregate . '_table.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk()) ->fetchSql() ->$aggregate($field); } @@ -232,7 +235,8 @@ class HasMany extends Relation /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 可以使用数组 关联模型对象 + * @param boolean $replace 是否自动识别更新和写入 * @return Model|false */ public function save($data, bool $replace = true) @@ -244,10 +248,10 @@ class HasMany extends Relation /** * 创建关联对象实例 - * @param array $data + * @param array|Model $data * @return Model */ - public function make(array $data = []): Model + public function make($data = []): Model { if ($data instanceof Model) { $data = $data->getData(); @@ -262,11 +266,11 @@ class HasMany extends Relation /** * 批量保存当前关联数据对象 * @access public - * @param array $dataSet 数据集 - * @param boolean $replace 是否自动识别更新和写入 + * @param iterable $dataSet 数据集 + * @param boolean $replace 是否自动识别更新和写入 * @return array|false */ - public function saveAll(array $dataSet, bool $replace = true) + public function saveAll(iterable $dataSet, bool $replace = true) { $result = []; @@ -286,7 +290,7 @@ class HasMany extends Relation * @param string $joinType JOIN类型 * @return Query */ - public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = 'INNER'): Query { $table = $this->query->getTable(); @@ -296,7 +300,7 @@ class HasMany extends Relation return $this->parent->db() ->alias($model) ->field($model . '.*') - ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType ?: $this->joinType) + ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType) ->group($relation . '.' . $this->foreignKey) ->having('count(' . $id . ')' . $operator . $count); } diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index 4d59581..33b5cf3 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -13,17 +13,27 @@ namespace think\model\relation; use Closure; use think\Collection; -use think\Db; use think\db\Query; use think\Exception; +use think\facade\Db; use think\Model; use think\model\Relation; +/** + * 远程一对多关联类 + */ class HasManyThrough extends Relation { - // 中间关联表外键 + /** + * 中间关联表外键 + * @var string + */ protected $throughKey; - // 中间表模型 + + /** + * 中间表模型 + * @var string + */ protected $through; /** diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 8741d3a..4a6ddef 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -13,10 +13,13 @@ declare (strict_types = 1); namespace think\model\relation; use Closure; -use think\Db; use think\db\Query; +use think\facade\Db; use think\Model; +/** + * HasOne 关联类 + */ class HasOne extends OneToOne { /** @@ -81,10 +84,7 @@ class HasOne extends OneToOne public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $return = $closure($this->query); - if ($resturn && is_string($return)) { - $name = $return; - } + $closure($this->query, $name); } return $this->query @@ -113,7 +113,7 @@ class HasOne extends OneToOne if ($closure) { $return = $closure($this->query); - if ($resturn && is_string($return)) { + if ($return && is_string($return)) { $name = $return; } } @@ -216,12 +216,12 @@ class HasOne extends OneToOne } else { $relationModel = $data[$result->$localKey]; $relationModel->setParent(clone $result); - $relationModel->isUpdate(true); + $relationModel->exists(true); } - if (!empty($this->bindAttr)) { + if ($relationModel && !empty($this->bindAttr)) { // 绑定关联属性 - $this->bindAttr($relationModel, $result, $this->bindAttr); + $this->bindAttr($relationModel, $result); } else { // 设置关联属性 $result->setRelation($attr, $relationModel); @@ -256,12 +256,12 @@ class HasOne extends OneToOne } else { $relationModel = $data[$result->$localKey]; $relationModel->setParent(clone $result); - $relationModel->isUpdate(true); + $relationModel->exists(true); } - if (!empty($this->bindAttr)) { + if ($relationModel && !empty($this->bindAttr)) { // 绑定关联属性 - $this->bindAttr($relationModel, $result, $this->bindAttr); + $this->bindAttr($relationModel, $result); } else { $result->setRelation(Db::parseName($relation), $relationModel); } diff --git a/src/model/relation/MorphMany.php b/src/model/relation/MorphMany.php index 6cf8a24..23cb6e9 100644 --- a/src/model/relation/MorphMany.php +++ b/src/model/relation/MorphMany.php @@ -13,18 +13,33 @@ namespace think\model\relation; use Closure; use think\Collection; -use think\Db; use think\db\Query; use think\Exception; +use think\facade\Db; use think\Model; use think\model\Relation; +/** + * 多态一对多关联 + */ class MorphMany extends Relation { - // 多态字段 + + /** + * 多态关联外键 + * @var string + */ protected $morphKey; + /** + * 多态字段名 + * @var string + */ protected $morphType; - // 多态类型 + + /** + * 多态类型 + * @var string + */ protected $type; /** @@ -139,7 +154,7 @@ class MorphMany extends Relation foreach ($data[$result->$pk] as &$relationModel) { $relationModel->setParent(clone $result); - $relationModel->isUpdate(true); + $relationModel->exists(true); } $result->setRelation($attr, $this->resultSetBuild($data[$result->$pk])); @@ -174,7 +189,7 @@ class MorphMany extends Relation foreach ($data[$key] as &$relationModel) { $relationModel->setParent(clone $result); - $relationModel->isUpdate(true); + $relationModel->exists(true); } $result->setRelation(Db::parseName($relation), $this->resultSetBuild($data[$key])); @@ -200,11 +215,7 @@ class MorphMany extends Relation } if ($closure) { - $return = $closure($this->query); - - if ($return && is_string($return)) { - $name = $return; - } + $closure($this->query, $name); } return $this->query @@ -221,9 +232,10 @@ class MorphMany extends Relation * @param Closure $closure 闭包 * @param string $aggregate 聚合查询方法 * @param string $field 字段 + * @param string $name 统计字段别名 * @return string */ - public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*'): string + public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { $return = $closure($this->query); @@ -273,19 +285,20 @@ class MorphMany extends Relation /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 可以使用数组 关联模型对象 + * @param boolean $replace 是否自动识别更新和写入 * @return Model|false */ - public function save($data) + public function save($data, bool $replace = true) { $model = $this->make(); - return $model->save($data) ? $model : false; + return $model->replace($replace)->save($data) ? $model : false; } /** * 创建关联对象实例 - * @param array $data + * @param array|Model $data * @return Model */ public function make($data = []): Model @@ -306,15 +319,16 @@ class MorphMany extends Relation /** * 批量保存当前关联数据对象 * @access public - * @param array $dataSet 数据集 + * @param iterable $dataSet 数据集 + * @param boolean $replace 是否自动识别更新和写入 * @return array|false */ - public function saveAll(array $dataSet) + public function saveAll(iterable $dataSet, bool $replace = true) { $result = []; foreach ($dataSet as $key => $data) { - $result[] = $this->save($data); + $result[] = $this->save($data, $replace); } return empty($result) ? false : $result; diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php index 6d0f3e3..7132896 100644 --- a/src/model/relation/MorphOne.php +++ b/src/model/relation/MorphOne.php @@ -12,18 +12,33 @@ namespace think\model\relation; use Closure; -use think\Db; use think\db\Query; use think\Exception; +use think\facade\Db; use think\Model; use think\model\Relation; +/** + * 多态一对一关联类 + */ class MorphOne extends Relation { - // 多态字段 + /** + * 多态关联外键 + * @var string + */ protected $morphKey; + + /** + * 多态字段 + * @var string + */ protected $morphType; - // 多态类型 + + /** + * 多态类型 + * @var string + */ protected $type; /** @@ -135,7 +150,7 @@ class MorphOne extends Relation } else { $relationModel = $data[$result->$pk]; $relationModel->setParent(clone $result); - $relationModel->isUpdate(true); + $relationModel->exists(true); } $result->setRelation($attr, $relationModel); @@ -166,7 +181,7 @@ class MorphOne extends Relation if (isset($data[$pk])) { $relationModel = $data[$pk]; $relationModel->setParent(clone $result); - $relationModel->isUpdate(true); + $relationModel->exists(true); } else { $relationModel = null; } @@ -207,18 +222,19 @@ class MorphOne extends Relation /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 可以使用数组 关联模型对象 + * @param boolean $replace 是否自动识别更新和写入 * @return Model|false */ - public function save($data) + public function save($data, bool $replace = true) { $model = $this->make(); - return $model->save($data) ? $model : false; + return $model->replace($replace)->save($data) ? $model : false; } /** * 创建关联对象实例 - * @param array $data + * @param array|Model $data * @return Model */ public function make($data = []): Model diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index 0c1008c..7d6b1d4 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -12,19 +12,38 @@ namespace think\model\relation; use Closure; -use think\Db; use think\Exception; +use think\facade\Db; use think\Model; use think\model\Relation; +/** + * 多态关联类 + */ class MorphTo extends Relation { - // 多态字段 + /** + * 多态关联外键 + * @var string + */ protected $morphKey; + + /** + * 多态字段 + * @var string + */ protected $morphType; - // 多态别名 - protected $alias; - // 关联名 + + /** + * 多态别名 + * @var array + */ + protected $alias = []; + + /** + * 关联名 + * @var string + */ protected $relation; /** @@ -92,7 +111,7 @@ class MorphTo extends Relation * @param integer $count 个数 * @param string $id 关联表的统计字段 * @param string $joinType JOIN类型 - * @return Query + * @return \think\db\Query */ public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '') { @@ -204,7 +223,7 @@ class MorphTo extends Relation } else { $relationModel = $data[$result->$morphKey]; $relationModel->setParent(clone $result); - $relationModel->isUpdate(true); + $relationModel->exists(true); } $result->setRelation($attr, $relationModel); @@ -225,8 +244,6 @@ class MorphTo extends Relation */ public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void { - $morphKey = $this->morphKey; - $morphType = $this->morphType; // 多态类型映射 $model = $this->parseModel($result->{$this->morphType}); @@ -262,7 +279,7 @@ class MorphTo extends Relation if ($data) { $data->setParent(clone $result); - $data->isUpdate(true); + $data->exists(true); } $result->setRelation(Db::parseName($relation), $data ?: null); diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index 30fa5f6..234119e 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -12,24 +12,34 @@ namespace think\model\relation; use Closure; -use think\Db; use think\db\Query; use think\Exception; +use think\facade\Db; use think\Model; use think\model\Relation; /** - * Class OneToOne + * 一对一关联基础类 * @package think\model\relation - * */ abstract class OneToOne extends Relation { - // 当前关联的JOIN类型 + /** + * JOIN类型 + * @var string + */ protected $joinType = 'INNER'; - // 要绑定的属性 + + /** + * 绑定的关联属性 + * @var array + */ protected $bindAttr = []; - // 关联名 + + /** + * 关联名 + * @var string + */ protected $relation; /** @@ -170,10 +180,11 @@ abstract class OneToOne extends Relation /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 可以使用数组 关联模型对象 + * @param boolean $replace 是否自动识别更新和写入 * @return Model|false */ - public function save($data) + public function save($data, bool $replace = true) { if ($data instanceof Model) { $data = $data->getData(); @@ -183,13 +194,13 @@ abstract class OneToOne extends Relation // 保存关联表数据 $data[$this->foreignKey] = $this->parent->{$this->localKey}; - return $model->save($data) ? $model : false; + return $model->replace($replace)->save($data) ? $model : false; } /** * 绑定关联表的属性到父模型属性 * @access public - * @param mixed $attr 要绑定的属性列表 + * @param array $attr 要绑定的属性列表 * @return $this */ public function bind(array $attr) @@ -238,11 +249,11 @@ abstract class OneToOne extends Relation } else { $relationModel = new $model($list[$relation]); $relationModel->setParent(clone $result); - $relationModel->isUpdate(true); + $relationModel->exists(true); } - if (!empty($this->bindAttr)) { - $this->bindAttr($relationModel, $result, $this->bindAttr); + if ($relationModel && !empty($this->bindAttr)) { + $this->bindAttr($relationModel, $result); } } else { $relationModel = null; @@ -262,8 +273,9 @@ abstract class OneToOne extends Relation protected function bindAttr(Model $model, Model $result): void { foreach ($this->bindAttr as $key => $attr) { - $key = is_numeric($key) ? $attr : $key; - if (isset($result->$key)) { + $key = is_numeric($key) ? $attr : $key; + $value = $result->getOrigin($key); + if (!is_null($value)) { throw new Exception('bind attr has exists:' . $key); } else { $result->setAttr($key, $model ? $model->$attr : null); diff --git a/src/paginator/driver/Bootstrap.php b/src/paginator/driver/Bootstrap.php index e490e28..6d55c39 100644 --- a/src/paginator/driver/Bootstrap.php +++ b/src/paginator/driver/Bootstrap.php @@ -13,6 +13,9 @@ namespace think\paginator\driver; use think\Paginator; +/** + * Bootstrap 分页驱动 + */ class Bootstrap extends Paginator { @@ -131,10 +134,10 @@ class Bootstrap extends Paginator * 生成一个可点击的按钮 * * @param string $url - * @param int $page + * @param string $page * @return string */ - protected function getAvailablePageWrapper(string $url, int $page): string + protected function getAvailablePageWrapper(string $url, string $page): string { return '
  • ' . $page . '
  • '; } @@ -192,10 +195,10 @@ class Bootstrap extends Paginator * 生成普通页码按钮 * * @param string $url - * @param int $page + * @param string $page * @return string */ - protected function getPageLinkWrapper(string $url, int $page): string + protected function getPageLinkWrapper(string $url, string $page): string { if ($this->currentPage() == $page) { return $this->getActivePageWrapper($page); -- Gitee From faa767d7b0c835f23abe0e552877580276536da0 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 29 Apr 2019 18:12:14 +0800 Subject: [PATCH 007/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Db.php | 2 -- src/Model.php | 4 ---- src/db/Connection.php | 23 +++++++++++++++++------ src/db/Query.php | 10 ++++++++++ src/model/concern/ModelEvent.php | 5 ++++- 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/Db.php b/src/Db.php index bdeee65..a776a4c 100644 --- a/src/Db.php +++ b/src/Db.php @@ -271,8 +271,6 @@ class Db if (isset($this->event[$event])) { return call_user_func_array($this->event[$event], [$this]); } - - return true; } /** diff --git a/src/Model.php b/src/Model.php index 30b466f..b031e4f 100644 --- a/src/Model.php +++ b/src/Model.php @@ -212,10 +212,6 @@ abstract class Model implements JsonSerializable, ArrayAccess $this->name = basename($name); } - if (static::$maker) { - call_user_func(static::$maker, $this); - } - // 执行初始化操作 $this->initialize(); } diff --git a/src/db/Connection.php b/src/db/Connection.php index ec6b508..7535806 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -16,7 +16,6 @@ use PDO; use PDOStatement; use think\Cache; use think\cache\CacheItem; -use think\Container; use think\Db; use think\db\exception\BindParamException; use think\Exception; @@ -179,6 +178,8 @@ abstract class Connection 'break_reconnect' => false, // 断线标识字符串 'break_match_str' => [], + // 字段缓存目录 + 'schema_path' => '', ]; /** @@ -237,6 +238,12 @@ abstract class Connection */ protected $cache; + /** + * 日志记录 + * @var array + */ + protected $log = []; + /** * 架构函数 读取数据库配置信息 * @access public @@ -434,7 +441,7 @@ abstract class Connection if (!isset($this->info[$schema])) { // 读取缓存 - $cacheFile = Container::pull('app')->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR . $schema . '.php'; + $cacheFile = $this->config['schema_path'] . $schema . '.php'; if (!$this->config['debug'] && is_file($cacheFile)) { $info = include $cacheFile; @@ -596,7 +603,7 @@ abstract class Connection return $this->links[$linkNum]; } catch (\PDOException $e) { if ($autoConnection) { - $this->log->error($e->getMessage()); + $this->log($e->getMessage()); return $this->connect($autoConnection, $linkNum); } else { throw $e; @@ -1715,16 +1722,20 @@ abstract class Connection * 记录SQL日志 * @access protected * @param string $log SQL日志信息 - * @param string $type 日志类型 * @return void */ - protected function log($log, $type = 'sql'): void + protected function log($log): void { if ($this->config['debug']) { - $this->log->record($log, $type); + $this->log[] = $log; } } + public function getSqlLog(): array + { + return $this->log; + } + /** * 初始化数据库连接 * @access protected diff --git a/src/db/Query.php b/src/db/Query.php index 0b4cee8..86a9472 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -429,6 +429,16 @@ class Query return $this->connection->getLastSql(); } + /** + * 获取sql记录 + * @access public + * @return string + */ + public function getSqlLog() + { + return $this->connection->getSqlLog(); + } + /** * 执行数据库事务 * @access public diff --git a/src/model/concern/ModelEvent.php b/src/model/concern/ModelEvent.php index 79e06ca..5b30d62 100644 --- a/src/model/concern/ModelEvent.php +++ b/src/model/concern/ModelEvent.php @@ -98,7 +98,10 @@ trait ModelEvent $callback = [$this->observerClass, $call]; } - $result = Container::getInstance()->invoke($callback, [$this]); + if (isset($callback)) { + $result = Container::getInstance()->invoke($callback, [$this]); + } + return false === $result ? false : true; } catch (ModelEventException $e) { return false; -- Gitee From 0536894738f6b5ccd91c746e730d0f51e2c76937 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 29 Apr 2019 18:20:57 +0800 Subject: [PATCH 008/365] =?UTF-8?q?readme=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 19 ++++++++++--------- src/db/connector/Mongo.php | 19 +++++++++++-------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index fb36df7..0639ba8 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,9 @@ - 支持Db类和查询构造器 - 支持事务 - 支持模型和关联 +- 事件支持依赖注入 +- 支持使用Db门面对象 +- 支持查询缓存 适用于不使用ThinkPHP框架的开发者。 @@ -17,7 +20,7 @@ composer require topthink/think-orm Db类用法: ~~~php -use think\Db; +use think\facade\Db; // 数据库配置信息设置(全局有效) Db::setConfig(['数据库配置参数(数组)']); // 进行CURD操作 @@ -36,20 +39,18 @@ Db::table('user') Db::table('user') ->where('id',10) ->delete(); +// 获取数据库SQL日志记录 +Db::getSqlLog(); ~~~ -Db类增加的(静态)方法包括: -- `setConfig` 设置全局配置信息 -- `getConfig` 获取数据库配置信息 -- `setCacheHandler` 设置缓存对象Handler(必须支持get、set及rm方法) -- `getSqlLog` 用于获取当前请求的SQL日志信息(包含连接信息) - -其它操作参考TP5.1的完全开发手册[数据库](https://www.kancloud.cn/manual/thinkphp5_1/353998)章节 +其它操作参考TP6.0的完全开发手册[数据库](https://www.kancloud.cn/manual/thinkphp6_0/1037530)章节 定义模型: ~~~php namespace app\index\model; + use think\Model; + class User extends Model { } @@ -203,4 +204,4 @@ $user->save(); ->where('status', 0); }); ``` -更多模型用法可以参考5.1完全开发手册的[模型](https://www.kancloud.cn/manual/thinkphp5_1/354041)章节 +更多模型用法可以参考6.0完全开发手册的[模型](https://www.kancloud.cn/manual/thinkphp6_0/1037579)章节 diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 8e78dac..48fb8f6 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -28,7 +28,6 @@ use think\Db; use think\db\builder\Mongo as Builder; use think\db\Mongo as Query; use think\Exception; -use think\Log; /** * Mongo数据库驱动 @@ -122,10 +121,10 @@ class Mongo protected $db; /** - * 日志对象 - * @var Log + * 日志记录 + * @var array */ - protected $log; + protected $log = []; /** * 架构函数 读取数据库配置信息 @@ -134,7 +133,7 @@ class Mongo * @param Log $log 日志对象 * @param array $config 数据库配置数组 */ - public function __construct(Cache $cache, Log $log, array $config = []) + public function __construct(Cache $cache, array $config = []) { if (!class_exists('\MongoDB\Driver\Manager')) { throw new Exception('require mongodb > 1.0'); @@ -147,7 +146,6 @@ class Mongo $this->builder = new Builder($this); $this->cache = $cache; - $this->log = $log; } /** @@ -572,9 +570,14 @@ class Mongo } } - public function logger(string $log, string $type = 'sql'): void + public function logger(string $log): void { - $this->config['debug'] && $this->log->record($log, $type); + $this->config['debug'] && $this->log[] = $log; + } + + public function getSqlLog(): array + { + return $this->log; } /** -- Gitee From 7429752dd05e2669d8cbb282f8ce765a0d15462a Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 29 Apr 2019 18:51:54 +0800 Subject: [PATCH 009/365] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index bf098af..69d8a1e 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": ">=7.1.0", "topthink/think-cache": "~2.0", - "topthink/think-container":"~1.0" + "topthink/think-container":"~2.0" }, "autoload": { "psr-4": { -- Gitee From a336764eb968b8bd096b05e534a4d70a2bcd69b9 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 30 Apr 2019 10:48:42 +0800 Subject: [PATCH 010/365] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Query.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/db/Query.php b/src/db/Query.php index 86a9472..e54b659 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -15,7 +15,6 @@ namespace think\db; use Closure; use PDO; use PDOStatement; -use think\App; use think\Collection; use think\Db; use think\db\exception\BindParamException; -- Gitee From cc023d007d97c612b8df7d6b59a661b3146e966b Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 12 May 2019 20:29:57 +0800 Subject: [PATCH 011/365] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=A1=86=E6=9E=B6?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 122 ++++------------------------- src/db/Connection.php | 20 +++-- src/db/Fetch.php | 61 +++++++++------ src/db/Query.php | 44 ++++++++--- src/db/connector/Mysql.php | 2 +- src/model/Relation.php | 10 +++ src/model/concern/Attribute.php | 40 ++++++++-- src/model/concern/ModelEvent.php | 44 +---------- src/model/concern/RelationShip.php | 4 +- src/model/concern/TimeStamp.php | 6 +- 10 files changed, 151 insertions(+), 202 deletions(-) diff --git a/src/Model.php b/src/Model.php index b031e4f..6aee704 100644 --- a/src/Model.php +++ b/src/Model.php @@ -22,37 +22,17 @@ use think\facade\Db; * Class Model * @package think * @mixin Query - * @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件 - * @method Query whereTime(string $field, string $op, mixed $range = null) static 查询日期和时间 - * @method Query whereBetweenTime(string $field, mixed $startTime, mixed $endTime) static 查询日期或者时间范围 - * @method Query whereBetweenTimeField(string $startField, string $endField) static 查询当前时间在两个时间字段范围 - * @method Query whereYear(string $field, string $year = 'this year') static 查询某年 - * @method Query whereMonth(string $field, string $month = 'this month') static 查询某月 - * @method Query whereDay(string $field, string $day = 'today') static 查询某日 - * @method Query whereRaw(string $where, array $bind = []) static 表达式查询 - * @method Query whereExp(string $field, string $condition, array $bind = []) static 字段表达式查询 - * @method Query when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询 - * @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询 - * @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询 - * @method Query with(mixed $with) static 关联预载入 - * @method Query count(string $field) static Count统计查询 - * @method Query min(string $field) static Min统计查询 - * @method Query max(string $field) static Max统计查询 - * @method Query sum(string $field) static SUM统计查询 - * @method Query avg(string $field) static Avg统计查询 - * @method Query field(mixed $field, boolean $except = false) static 指定查询字段 - * @method Query fieldRaw(string $field, array $bind = []) static 指定查询字段 - * @method Query union(mixed $union, boolean $all = false) static UNION查询 - * @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT - * @method Query order(mixed $field, string $order = null) static 查询ORDER - * @method Query orderRaw(string $field, array $bind = []) static 查询ORDER - * @method Query cache(mixed $key = null, integer $expire = null) static 设置查询缓存 - * @method mixed value(string $field) static 获取某个字段的值 - * @method array column(string $field, string $key = '') static 获取某个列的值 - * @method Model find(mixed $data = null) static 查询单个记录 不存在返回Null - * @method Model findOrEmpty(mixed $data = null) static 查询单个记录 不存在返回空模型 - * @method \think\model\Collection select(mixed $data = null) static 查询多个记录 - * @method Model withAttr(array $name, \Closure $closure) 动态定义获取器 + * @method void onAfterRead(Model $model) static after_read事件定义 + * @method mixed onBeforeInsert(Model $model) static before_insert事件定义 + * @method void onAfterInsert(Model $model) static after_insert事件定义 + * @method mixed onBeforeUpdate(Model $model) static before_update事件定义 + * @method void onAfterUpdate(Model $model) static after_update事件定义 + * @method mixed onBeforeWrite(Model $model) static before_write事件定义 + * @method void onAfterWrite(Model $model) static after_write事件定义 + * @method mixed onBeforeDelete(Model $model) static before_write事件定义 + * @method void onAfterDelete(Model $model) static after_delete事件定义 + * @method void onBeforeRestore(Model $model) static before_restore事件定义 + * @method void onAfterRestore(Model $model) static after_restore事件定义 */ abstract class Model implements JsonSerializable, ArrayAccess { @@ -110,24 +90,6 @@ abstract class Model implements JsonSerializable, ArrayAccess */ protected $table; - /** - * 写入自动完成定义 - * @var array - */ - protected $auto = []; - - /** - * 新增自动完成定义 - * @var array - */ - protected $insert = []; - - /** - * 更新自动完成定义 - * @var array - */ - protected $update = []; - /** * 初始化过的模型. * @var array @@ -341,10 +303,6 @@ abstract class Model implements JsonSerializable, ArrayAccess private function initialize(): void { if (!isset(static::$initialized[static::class])) { - if ($this->observerClass) { - // 注册模型观察者 - $this->observe($this->observerClass); - } static::$initialized[static::class] = true; static::init(); } @@ -358,30 +316,6 @@ abstract class Model implements JsonSerializable, ArrayAccess protected static function init() {} - /** - * 数据自动完成 - * @access protected - * @param array $auto 要自动更新的字段列表 - * @return void - */ - protected function autoCompleteData(array $auto = []): void - { - foreach ($auto as $field => $value) { - if (is_integer($field)) { - $field = $value; - $value = null; - } - - if (!isset($this->data[$field])) { - $default = null; - } else { - $default = $this->data[$field]; - } - - $this->setAttr($field, !is_null($value) ? $value : $default); - } - } - protected function checkData(): void {} @@ -529,10 +463,9 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 检查数据是否允许写入 * @access protected - * @param array $append 自动完成的字段列表 * @return array */ - protected function checkAllowFields(array $append = []): array + protected function checkAllowFields(): array { // 检测字段 if (empty($this->field)) { @@ -548,7 +481,7 @@ abstract class Model implements JsonSerializable, ArrayAccess return $this->field; } - $field = array_merge($this->field, $append); + $field = $this->field; if ($this->autoWriteTimestamp) { array_push($field, $this->createTime, $this->updateTime); @@ -569,11 +502,6 @@ abstract class Model implements JsonSerializable, ArrayAccess */ protected function updateData(): bool { - // 自动更新 - $auto = array_merge($this->auto, $this->update); - - $this->autoCompleteData($auto); - // 事件回调 if (false === $this->trigger('BeforeUpdate')) { return false; @@ -590,7 +518,7 @@ abstract class Model implements JsonSerializable, ArrayAccess $this->autoRelationUpdate(); } - return false; + return true; } if ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) { @@ -600,7 +528,7 @@ abstract class Model implements JsonSerializable, ArrayAccess } // 检查允许字段 - $allowFields = $this->checkAllowFields($auto); + $allowFields = $this->checkAllowFields(); foreach ($this->relationWrite as $name => $val) { if (!is_array($val)) { @@ -652,11 +580,6 @@ abstract class Model implements JsonSerializable, ArrayAccess */ protected function insertData(string $sequence = null): bool { - // 自动写入 - $auto = array_merge($this->auto, $this->insert); - - $this->autoCompleteData($auto); - // 时间戳自动写入 if ($this->autoWriteTimestamp) { if ($this->createTime && !isset($this->data[$this->createTime])) { @@ -675,7 +598,7 @@ abstract class Model implements JsonSerializable, ArrayAccess $this->checkData(); // 检查允许字段 - $allowFields = $this->checkAllowFields($auto); + $allowFields = $this->checkAllowFields(); $db = $this->db(); $db->startTrans(); @@ -816,19 +739,6 @@ abstract class Model implements JsonSerializable, ArrayAccess } } - /** - * 设置自动完成的字段( 规则通过修改器定义) - * @access public - * @param array $fields 需要自动完成的字段 - * @return $this - */ - public function auto(array $fields) - { - $this->auto = $fields; - - return $this; - } - /** * 写入数据 * @access public diff --git a/src/db/Connection.php b/src/db/Connection.php index 7535806..5143104 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -815,7 +815,7 @@ abstract class Connection protected function queryPDOStatement(Query $query, string $sql, array $bind = []): PDOStatement { - $options = $query->parseOptions(); + $options = $query->getOptions(); $master = !empty($options['master']) ? true : false; $procedure = !empty($options['procedure']) ? true : in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); @@ -954,7 +954,7 @@ abstract class Connection if ($result) { $sequence = $options['sequence'] ?? null; - $lastInsId = $this->getLastInsID($sequence); + $lastInsId = $query->getLastInsID($sequence); $data = $options['data']; @@ -1035,6 +1035,8 @@ abstract class Connection public function selectInsert(Query $query, array $fields, string $table): int { // 分析查询表达式 + $query->parseOptions(); + $sql = $this->builder->selectInsert($query, $fields, $table); return $this->execute($query, $sql, $query->getBind()); @@ -1055,6 +1057,7 @@ abstract class Connection if (isset($options['cache'])) { $cacheItem = $this->parseCache($query, $options['cache']); $key = $cacheItem->getKey(); + $tag = $cacheItem->getTag(); } // 生成UPDATE SQL语句 @@ -1064,8 +1067,8 @@ abstract class Connection if (isset($key) && $this->cache->get($key)) { // 删除缓存 $this->cache->delete($key); - } elseif (isset($cacheItem) && $cacheItem->getTag()) { - $this->cache->tag($cacheItem->getTag())->clear(); + } elseif (!empty($tag)) { + $this->cache->tag($tag)->clear(); } // 执行操作 @@ -1094,6 +1097,7 @@ abstract class Connection if (isset($options['cache'])) { $cacheItem = $this->parseCache($query, $options['cache']); $key = $cacheItem->getKey(); + $tag = $cacheItem->getTag(); } // 生成删除SQL语句 @@ -1103,8 +1107,8 @@ abstract class Connection if (isset($key) && $this->cache->get($key)) { // 删除缓存 $this->cache->delete($key); - } elseif (isset($cacheItem) && $cacheItem->getTag()) { - $this->cache->tag($cacheItem->getTag())->clear(); + } elseif (!empty($tag)) { + $this->cache->tag($tag)->clear(); } // 执行操作 @@ -1170,14 +1174,14 @@ abstract class Connection /** * 得到某个字段的值 - * @access public + * @access protected * @param Query $query 查询对象 * @param string $aggregate 聚合方法 * @param mixed $field 字段名 * @param bool $force 强制转为数字类型 * @return mixed */ - public function aggregate(Query $query, string $aggregate, $field, bool $force = false) + protected function aggregate(Query $query, string $aggregate, $field, bool $force = false) { if (is_string($field) && 0 === stripos($field, 'DISTINCT ')) { list($distinct, $field) = explode(' ', $field); diff --git a/src/db/Fetch.php b/src/db/Fetch.php index 38b1dd0..c852627 100644 --- a/src/db/Fetch.php +++ b/src/db/Fetch.php @@ -52,27 +52,29 @@ class Fetch /** * 聚合查询 - * @access public - * @param string $aggregate 聚合方法 - * @param string $field 字段名 + * @access protected + * @param string $aggregate 聚合方法 + * @param string $field 字段名 * @return string */ - public function aggregate(string $aggregate, string $field): string + protected function aggregate(string $aggregate, string $field): string { $this->query->parseOptions(); $field = $aggregate . '(' . $this->builder->parseKey($this->query, $field) . ') AS tp_' . strtolower($aggregate); - return $this->value($field); + return $this->value($field, 0, false); } /** * 得到某个字段的值 * @access public * @param string $field 字段名 + * @param mixed $default 默认值 + * @param bool $one 限制返回一个数据 * @return string */ - public function value(string $field): string + public function value(string $field, $default = null, bool $one = true): string { $options = $this->query->parseOptions(); @@ -83,7 +85,7 @@ class Fetch $this->query->setOption('field', (array) $field); // 生成查询SQL - $sql = $this->builder->select($this->query, true); + $sql = $this->builder->select($this->query, $one); if (isset($options['field'])) { $this->query->setOption('field', $options['field']); @@ -143,8 +145,7 @@ class Fetch $this->query->setOption('data', $data); } - $replace = $options['replace'] ?? false; - $sql = $this->builder->insert($this->query, $replace); + $sql = $this->builder->insert($this->query); return $this->fetch($sql); } @@ -169,22 +170,20 @@ class Fetch */ public function save(array $data = [], bool $forceInsert = false): string { - if (empty($data)) { - $data = $this->query->getOptions('data'); - } - - if (empty($data)) { - return ''; - } - if ($forceInsert) { return $this->insert($data); } - $isUpdate = $this->query->parseUpdateData($data); + $data = array_merge($this->query->getOptions('data') ?: [], $data); $this->query->setOption('data', $data); + if ($this->query->getOptions('where')) { + $isUpdate = true; + } else { + $isUpdate = $this->query->parseUpdateData($data); + } + return $isUpdate ? $this->update() : $this->insert(); } @@ -192,11 +191,10 @@ class Fetch * 批量插入记录 * @access public * @param array $dataSet 数据集 - * @param boolean $replace 是否replace * @param integer $limit 每次写入数据限制 * @return string */ - public function insertAll(array $dataSet = [], bool $replace = false, int $limit = null): string + public function insertAll(array $dataSet = [], int $limit = null): string { $options = $this->query->parseOptions(); @@ -212,7 +210,7 @@ class Fetch $array = array_chunk($dataSet, $limit, true); $fetchSql = []; foreach ($array as $item) { - $sql = $this->builder->insertAll($this->query, $item, $replace); + $sql = $this->builder->insertAll($this->query, $item); $bind = $this->query->getBind(); $fetchSql[] = $this->connection->getRealSql($sql, $bind); @@ -221,7 +219,7 @@ class Fetch return implode(';', $fetchSql); } - $sql = $this->builder->insertAll($this->query, $dataSet, $replace); + $sql = $this->builder->insertAll($this->query, $dataSet); return $this->fetch($sql); } @@ -372,7 +370,7 @@ class Fetch */ public function selectOrFail($data = null): string { - return $this->query->failException(true)->select($data); + return $this->select($data); } /** @@ -383,7 +381,18 @@ class Fetch */ public function findOrFail($data = null): string { - return $this->query->failException(true)->find($data); + return $this->find($data); + } + + /** + * 查找单条记录 不存在返回空数据(或者空模型) + * @access public + * @param mixed $data 数据 + * @return string + */ + public function findOrEmpty($data = null) + { + return $this->find($data); } /** @@ -471,11 +480,11 @@ class Fetch if (strtolower(substr($method, 0, 5)) == 'getby') { // 根据某个字段获取记录 $field = Db::parseName(substr($method, 5)); - return $this->query->where($field, '=', $args[0])->find(); + return $this->where($field, '=', $args[0])->find(); } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { // 根据某个字段获取记录的某个值 $name = Db::parseName(substr($method, 10)); - return $this->query->where($name, '=', $args[0])->value($args[1]); + return $this->where($name, '=', $args[0])->value($args[1]); } $result = call_user_func_array([$this->query, $method], $args); diff --git a/src/db/Query.php b/src/db/Query.php index e54b659..366deb6 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -317,6 +317,16 @@ class Query return $this; } + /** + * 获取详细字段类型信息 + * @access public + * @return array + */ + public function getFields(): array + { + return $this->connection->getFields($this->getTable()); + } + /** * 获取字段类型信息 * @access public @@ -405,7 +415,21 @@ class Query */ public function getLastInsID(string $sequence = null): string { - return $this->connection->getLastInsID($sequence); + $insertId = $this->connection->getLastInsID($sequence); + + $pk = $this->getPk(); + + if (is_string($pk)) { + $type = $this->getFieldBindType($pk); + + if (PDO::PARAM_INT == $type) { + $insertId = (int) $insertId; + } elseif (Connection::PARAM_FLOAT == $type) { + $insertId = (float) $insertId; + } + } + + return $insertId; } /** @@ -2442,7 +2466,7 @@ class Query * @param callable $callback 闭包获取器 * @return $this */ - public function withAttr($name, callable $callback) + public function withAttr($name, callable $callback = null) { if (is_array($name)) { $this->options['with_attr'] = $name; @@ -3072,16 +3096,16 @@ class Query continue; } - $result[$name] = json_decode($result[$name], $assoc); + $result[$name] = json_decode($result[$name], true); - if (!isset($withRelationAttr[$name])) { - continue; + if (isset($withRelationAttr[$name])) { + foreach ($withRelationAttr[$name] as $key => $closure) { + $result[$name][$key] = $closure($result[$name][$key] ?? null, $result[$name]); + } } - foreach ($withRelationAttr[$name] as $key => $closure) { - $data = get_object_vars($result[$name]); - - $result[$name]->$key = $closure($result[$name]->$key ?? null, $data); + if (!$assoc) { + $result[$name] = (object) $result[$name]; } } } @@ -3134,7 +3158,7 @@ class Query // 关联查询 if (!empty($options['relation'])) { - $result->relationQuery($options['relation']); + $result->relationQuery($options['relation'], $withRelationAttr); } // 预载入查询 diff --git a/src/db/connector/Mysql.php b/src/db/connector/Mysql.php index 7a39a53..82debf6 100644 --- a/src/db/connector/Mysql.php +++ b/src/db/connector/Mysql.php @@ -62,7 +62,7 @@ class Mysql extends Connection $tableName = '`' . $tableName . '`'; } - $sql = 'SHOW COLUMNS FROM ' . $tableName; + $sql = 'SHOW FULL COLUMNS FROM ' . $tableName; $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; diff --git a/src/model/Relation.php b/src/model/Relation.php index 027a524..a063a2b 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -76,6 +76,16 @@ abstract class Relation return $this->parent; } + /** + * 获取当前的关联模型类的Query实例 + * @access public + * @return Query + */ + public function getQuery() + { + return $this->query; + } + /** * 获取当前的关联模型类的实例 * @access public diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 4ff5e42..37afb42 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -486,8 +486,12 @@ trait Attribute $value = $this->getRelationValue($name); } - $closure = $this->withAttr[$fieldName]; - $value = $closure($value, $this->data); + if (in_array($fieldName, $this->json) && is_array($this->withAttr[$fieldName])) { + $value = $this->getJsonValue($fieldName, $value); + } else { + $closure = $this->withAttr[$fieldName]; + $value = $closure($value, $this->data); + } } elseif (method_exists($this, $method)) { if ($relation) { $value = $this->getRelationValue($name); @@ -506,6 +510,26 @@ trait Attribute return $value; } + /** + * 获取JSON字段属性值 + * @access protected + * @param string $name 属性名 + * @param mixed $value JSON数据 + * @return mixed + */ + protected function getJsonValue($name, $value) + { + foreach ($this->withAttr[$name] as $key => $closure) { + if ($this->jsonAssoc) { + $value[$key] = $closure($value[$key], $value); + } else { + $value->$key = $closure($value->$key, $value); + } + } + + return $value; + } + /** * 获取关联属性值 * @access protected @@ -627,14 +651,18 @@ trait Attribute { if (is_array($name)) { foreach ($name as $key => $val) { - $key = $this->getRealFieldName($key); - - $this->withAttr[$key] = $val; + $this->withAttribute($key, $val); } } else { $name = $this->getRealFieldName($name); - $this->withAttr[$name] = $callback; + if (strpos($name, '.')) { + list($name, $key) = explode('.', $name); + + $this->withAttr[$name][$key] = $callback; + } else { + $this->withAttr[$name] = $callback; + } } return $this; diff --git a/src/model/concern/ModelEvent.php b/src/model/concern/ModelEvent.php index 5b30d62..dacab99 100644 --- a/src/model/concern/ModelEvent.php +++ b/src/model/concern/ModelEvent.php @@ -21,18 +21,6 @@ use think\facade\Db; */ trait ModelEvent { - /** - * 模型事件观察 - * @var array - */ - protected $observe = ['AfterRead', 'BeforeWrite', 'AfterWrite', 'BeforeInsert', 'AfterInsert', 'BeforeUpdate', 'AfterUpdate', 'BeforeDelete', 'AfterDelete', 'BeforeRestore', 'AfterRestore']; - - /** - * 模型事件观察者类名 - * @var string - */ - protected $observerClass; - /** * Event * @var array @@ -45,25 +33,6 @@ trait ModelEvent */ protected $withEvent = true; - /** - * 注册一个模型观察者 - * - * @param string $class 观察者类 - * @return void - */ - protected function observe(string $class): void - { - foreach ($this->observe as $event) { - $call = 'on' . $event; - - if (method_exists($class, $call)) { - $instance = Container::getInstance()->invokeClass($class); - - $this->event[$event][] = [$instance, $call]; - } - } - } - /** * 当前操作的事件响应 * @access protected @@ -88,18 +57,13 @@ trait ModelEvent return true; } - $call = 'on' . Db::parseName($event, 1); - $result = true; + $call = 'on' . Db::parseName($event, 1); try { if (method_exists(static::class, $call)) { - $callback = [static::class, $call]; - } elseif ($this->observerClass && method_exists($this->observerClass, $call)) { - $callback = [$this->observerClass, $call]; - } - - if (isset($callback)) { - $result = Container::getInstance()->invoke($callback, [$this]); + $result = Container::getInstance()->invoke([static::class, $call], [$this]); + } else { + $result = true; } return false === $result ? false : true; diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 1753ea0..26a0cdd 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -232,7 +232,7 @@ trait RelationShip $relationResult = $this->$relation(); if (isset($withRelationAttr[$relationName])) { - $relationResult->withAttr($withRelationAttr[$relationName]); + $relationResult->getQuery()->withAttr($withRelationAttr[$relationName]); } $relationResult->eagerlyResultSet($resultSet, $relation, $subRelation, $closure, $join); @@ -274,7 +274,7 @@ trait RelationShip $relationResult = $this->$relation(); if (isset($withRelationAttr[$relationName])) { - $relationResult->withAttr($withRelationAttr[$relationName]); + $relationResult->getQuery()->withAttr($withRelationAttr[$relationName]); } $relationResult->eagerlyResult($result, $relation, $subRelation, $closure, $join); diff --git a/src/model/concern/TimeStamp.php b/src/model/concern/TimeStamp.php index e0969d8..cd8230f 100644 --- a/src/model/concern/TimeStamp.php +++ b/src/model/concern/TimeStamp.php @@ -69,10 +69,10 @@ trait TimeStamp /** * 设置时间字段格式化 * @access public - * @param string $format + * @param string|false $format * @return $this */ - public function setDateFormat(string $format) + public function setDateFormat($format) { $this->dateFormat = $format; @@ -82,7 +82,7 @@ trait TimeStamp /** * 获取自动写入时间字段 * @access public - * @return string + * @return string|false */ public function getDateFormat() { -- Gitee From bb8f6d4634520b5c6008433d3ea909d497c8037c Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 29 May 2019 17:33:20 +0800 Subject: [PATCH 012/365] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=A0=B8=E5=BF=83?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 21 ++-- src/db/Builder.php | 18 ++-- src/db/Query.php | 146 ++++++++++++++++++++------- src/db/builder/Mysql.php | 7 +- src/model/Collection.php | 40 +++++++- src/model/Relation.php | 18 +++- src/model/concern/Attribute.php | 4 + src/model/concern/Conversion.php | 8 +- src/model/concern/RelationShip.php | 24 +++-- src/model/relation/BelongsToMany.php | 2 +- src/model/relation/OneToOne.php | 5 +- 11 files changed, 219 insertions(+), 74 deletions(-) diff --git a/src/Model.php b/src/Model.php index 6aee704..96c5196 100644 --- a/src/Model.php +++ b/src/Model.php @@ -224,11 +224,17 @@ abstract class Model implements JsonSerializable, ArrayAccess * 设置当前模型的数据库查询对象 * @access public * @param Query $query 查询对象实例 + * @param bool $clear 是否需要清空查询条件 * @return $this */ - public function setQuery(Query $query) + public function setQuery(Query $query, bool $clear = true) { $this->queryInstance = clone $query; + + if ($clear) { + $this->queryInstance->removeOption(); + } + return $this; } @@ -264,7 +270,7 @@ abstract class Model implements JsonSerializable, ArrayAccess { /** @var Query $query */ if ($this->queryInstance) { - $query = $this->queryInstance->removeOption(); + $query = $this->queryInstance; } else { $query = Db::buildQuery($this->connection) ->name($this->name . $this->suffix) @@ -285,9 +291,8 @@ abstract class Model implements JsonSerializable, ArrayAccess } // 全局作用域 - $globalScope = is_array($scope) && !empty($scope) ? $scope : $this->globalScope; - - if (!empty($globalScope) && false !== $scope) { + if (is_array($scope)) { + $globalScope = array_diff($this->globalScope, $scope); $query->scope($globalScope); } @@ -902,12 +907,12 @@ abstract class Model implements JsonSerializable, ArrayAccess } /** - * 设置使用的全局查询范围 + * 设置不使用的全局查询范围 * @access public - * @param array|false $scope 启用的全局查询范围 + * @param array $scope 不启用的全局查询范围 * @return Query */ - public static function useGlobalScope($scope) + public static function withoutGlobalScope(array $scope = null) { $model = new static(); diff --git a/src/db/Builder.php b/src/db/Builder.php index d87cd53..c0fb8de 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -151,7 +151,7 @@ abstract class Builder if ($val instanceof Raw) { $result[$item] = $val->getValue(); continue; - } elseif (!is_scalar($val) && (in_array($key, (array) $query->getOptions('json')) || 'json' == $this->connection->getFieldsType($options['table'], $key))) { + } elseif (!is_scalar($val) && (in_array($key, (array) $query->getOptions('json')) || 'json' == $query->getFieldType($key))) { $val = json_encode($val); } @@ -799,19 +799,18 @@ abstract class Builder $table = $options['table']; } - $type = $this->connection->getTableInfo($table, 'type'); + $type = $query->getFieldType($key); - if (isset($type[$key])) { - $info = $type[$key]; + if ($type) { if (is_string($value)) { $value = strtotime($value) ?: $value; } if (is_int($value)) { - if (preg_match('/(datetime|timestamp)/is', $info)) { + if (preg_match('/(datetime|timestamp)/is', $type)) { // 日期及时间戳类型 $value = date('Y-m-d H:i:s', $value); - } elseif (preg_match('/(date)/is', $info)) { + } elseif (preg_match('/(date)/is', $type)) { // 日期及时间戳类型 $value = date('Y-m-d', $value); } @@ -1148,15 +1147,16 @@ abstract class Builder { $options = $query->getOptions(); + // 获取绑定信息 + $bind = $query->getFieldsBindType(); + // 获取合法的字段 if ('*' == $options['field']) { - $allowFields = $this->connection->getTableFields($options['table']); + $allowFields = array_keys($bind); } else { $allowFields = $options['field']; } - // 获取绑定信息 - $bind = $query->getFieldsBindType(); $fields = []; $values = []; diff --git a/src/db/Query.php b/src/db/Query.php index 366deb6..d9aede1 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -233,11 +233,12 @@ class Query /** * 获取当前的模型对象 * @access public + * @param bool $clear 是否需要清空查询条件 * @return Model|null */ - public function getModel() + public function getModel(bool $clear = true) { - return $this->model ? $this->model->setQuery($this) : null; + return $this->model ? $this->model->setQuery($this, $clear) : null; } /** @@ -763,16 +764,12 @@ class Query } /** - * 指定查询字段 支持字段排除和指定数据表 + * 指定查询字段 * @access public - * @param mixed $field 字段信息 - * @param boolean $except 是否排除 - * @param string $tableName 数据表名 - * @param string $prefix 字段前缀 - * @param string $alias 别名前缀 + * @param mixed $field 字段信息 * @return $this */ - public function field($field, bool $except = false, string $tableName = '', string $prefix = '', string $alias = '') + public function field($field) { if (empty($field)) { return $this; @@ -791,24 +788,81 @@ class Query if (true === $field) { // 获取全部字段 - $fields = $this->getTableFields($tableName); + $fields = $this->getTableFields(); $field = $fields ?: ['*']; - } elseif ($except) { - // 字段排除 + } + + if (isset($this->options['field'])) { + $field = array_merge((array) $this->options['field'], $field); + } + + $this->options['field'] = array_unique($field); + + return $this; + } + + /** + * 指定要排除的查询字段 + * @access public + * @param array|string $field 要排除的字段 + * @return $this + */ + public function withoutField($field) + { + if (empty($field)) { + return $this; + } + + if (is_string($field)) { + $field = array_map('trim', explode(',', $field)); + } + + // 字段排除 + $fields = $this->getTableFields(); + $field = $fields ? array_diff($fields, $field) : $field; + + if (isset($this->options['field'])) { + $field = array_merge((array) $this->options['field'], $field); + } + + $this->options['field'] = array_unique($field); + + return $this; + } + + /** + * 指定其它数据表的查询字段 + * @access public + * @param mixed $field 字段信息 + * @param string $tableName 数据表名 + * @param string $prefix 字段前缀 + * @param string $alias 别名前缀 + * @return $this + */ + public function tableField($field, string $tableName, string $prefix = '', string $alias = '') + { + if (empty($field)) { + return $this; + } + + if (is_string($field)) { + $field = array_map('trim', explode(',', $field)); + } + + if (true === $field) { + // 获取全部字段 $fields = $this->getTableFields($tableName); - $field = $fields ? array_diff($fields, $field) : $field; + $field = $fields ?: ['*']; } - if ($tableName) { - // 添加统一的前缀 - $prefix = $prefix ?: $tableName; - foreach ($field as $key => &$val) { - if (is_numeric($key) && $alias) { - $field[$prefix . '.' . $val] = $alias . $val; - unset($field[$key]); - } elseif (is_numeric($key)) { - $val = $prefix . '.' . $val; - } + // 添加统一的前缀 + $prefix = $prefix ?: $tableName; + foreach ($field as $key => &$val) { + if (is_numeric($key) && $alias) { + $field[$prefix . '.' . $val] = $alias . $val; + unset($field[$key]); + } elseif (is_numeric($key)) { + $val = $prefix . '.' . $val; } } @@ -969,6 +1023,7 @@ class Query { if ($field instanceof $this) { $this->options['where'] = $field->getOptions('where'); + $this->bind($field->getBind(false)); return $this; } @@ -2138,19 +2193,20 @@ class Query /** * 查询某个时间间隔数据 - * @access protected + * @access public * @param string $field 日期字段名 * @param string $start 开始时间 - * @param string $interval 时间间隔单位 + * @param string $interval 时间间隔单位 day/month/year/week/hour/minute/second + * @param int $step 间隔 * @param string $logic AND OR * @return $this */ - protected function whereTimeInterval(string $field, string $start, string $interval = 'day', string $logic = 'AND') + public function whereTimeInterval(string $field, string $start, string $interval = 'day', int $step = 1, string $logic = 'AND') { $startTime = strtotime($start); - $endTime = strtotime('+1 ' . $interval, $startTime); + $endTime = strtotime(($step > 0 ? '+' : '-') . abs($step) . ' ' . $interval . (abs($step) > 1 ? 's' : ''), $startTime); - return $this->whereTime($field, 'between', [$startTime, $endTime], $logic); + return $this->whereTime($field, 'between', $step > 0 ? [$startTime, $endTime] : [$endTime, $startTime], $logic); } /** @@ -2158,16 +2214,35 @@ class Query * @access public * @param string $field 日期字段名 * @param string $month 月份信息 + * @param int $step 间隔 * @param string $logic AND OR * @return $this */ - public function whereMonth(string $field, string $month = 'this month', string $logic = 'AND') + public function whereMonth(string $field, string $month = 'this month', int $step = 1, string $logic = 'AND') { if (in_array($month, ['this month', 'last month'])) { $month = date('Y-m', strtotime($month)); } - return $this->whereTimeInterval($field, $month, 'month', $logic); + return $this->whereTimeInterval($field, $month, 'month', $step, $logic); + } + + /** + * 查询周数据 whereWeek('time_field', '2018-1-1') 从2018-1-1开始的一周数据 + * @access public + * @param string $field 日期字段名 + * @param string $week 周信息 + * @param int $step 间隔 + * @param string $logic AND OR + * @return $this + */ + public function whereWeek(string $field, string $week = 'this week', int $step = 1, string $logic = 'AND') + { + if (in_array($week, ['this week', 'last week'])) { + $week = date('Y-m-d', strtotime($week)); + } + + return $this->whereTimeInterval($field, $week, 'week', $step, $logic); } /** @@ -2175,16 +2250,17 @@ class Query * @access public * @param string $field 日期字段名 * @param string $year 年份信息 + * @param int $step 间隔 * @param string $logic AND OR * @return $this */ - public function whereYear(string $field, string $year = 'this year', string $logic = 'AND') + public function whereYear(string $field, string $year = 'this year', int $step = 1, string $logic = 'AND') { if (in_array($year, ['this year', 'last year'])) { $year = date('Y', strtotime($year)); } - return $this->whereTimeInterval($field, $year . '-1-1', 'year', $logic); + return $this->whereTimeInterval($field, $year . '-1-1', 'year', $step, $logic); } /** @@ -2192,18 +2268,18 @@ class Query * @access public * @param string $field 日期字段名 * @param string $day 日期信息 + * @param int $step 间隔 * @param string $logic AND OR * @return $this */ - public function whereDay(string $field, string $day = 'today', string $logic = 'AND') + public function whereDay(string $field, string $day = 'today', int $step = 1, string $logic = 'AND') { if (in_array($day, ['today', 'yesterday'])) { $day = date('Y-m-d', strtotime($day)); } - return $this->whereTimeInterval($field, $day, 'day', $logic); + return $this->whereTimeInterval($field, $day, 'day', $step, $logic); } - /** * 查询日期或者时间范围 whereBetweenTime('time_field', '2018-1-1','2018-1-15') * @access public diff --git a/src/db/builder/Mysql.php b/src/db/builder/Mysql.php index 36ac102..ce055e1 100644 --- a/src/db/builder/Mysql.php +++ b/src/db/builder/Mysql.php @@ -152,15 +152,16 @@ class Mysql extends Builder { $options = $query->getOptions(); + // 获取绑定信息 + $bind = $query->getFieldsBindType(); + // 获取合法的字段 if ('*' == $options['field']) { - $allowFields = $this->connection->getTableFields($options['table']); + $allowFields = array_keys($bind); } else { $allowFields = $options['field']; } - // 获取绑定信息 - $bind = $this->connection->getFieldsBind($options['table']); $fields = []; $values = []; diff --git a/src/model/Collection.php b/src/model/Collection.php index e920492..6a07c8f 100644 --- a/src/model/Collection.php +++ b/src/model/Collection.php @@ -29,12 +29,48 @@ class Collection extends BaseCollection */ public function load($relation) { - $item = current($this->items); - $item->eagerlyResultSet($this->items, (array) $relation); + if (!$this->isEmpty()) { + $item = current($this->items); + $item->eagerlyResultSet($this->items, (array) $relation); + } return $this; } + /** + * 删除数据集的数据 + * @access public + * @return bool + */ + public function delete(): bool + { + $this->each(function (Model $model) { + $model->delete(); + }); + + return true; + } + + /** + * 更新数据 + * @access public + * @param array $data 数据数组 + * @param array $allowField 允许字段 + * @return bool + */ + public function update(array $data, array $allowField = []): bool + { + $this->each(function (Model $model) use ($data, $allowField) { + if (!empty($allowField)) { + $model->allowField($allowField); + } + + $model->save($data); + }); + + return true; + } + /** * 设置需要隐藏的输出属性 * @access public diff --git a/src/model/Relation.php b/src/model/Relation.php index a063a2b..55b1ad0 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -64,7 +64,7 @@ abstract class Relation * 是否为自关联 * @var bool */ - protected $selfRelation; + protected $selfRelation = false; /** * 获取关联的所属模型 @@ -154,6 +154,17 @@ abstract class Relation } } + /** + * 更新数据 + * @access public + * @param array $data 更新数据 + * @return integer + */ + public function update(array $data = []): int + { + return $this->query->update($data); + } + /** * 删除记录 * @access public @@ -181,10 +192,9 @@ abstract class Relation // 执行基础查询 $this->baseQuery(); - $result = call_user_func_array([$this->query->getModel(), $method], $args); - $class = get_class($this->query); + $result = call_user_func_array([$this->query->getModel(false), $method], $args); - return $result instanceof $class ? $this : $result; + return $result === $this->query ? $this : $result; } throw new Exception('method not exists:' . __CLASS__ . '->' . $method); diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 37afb42..fe2d53e 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -362,9 +362,13 @@ trait Attribute $method = 'set' . Db::parseName($name, 1) . 'Attr'; if (method_exists($this, $method)) { + $array = $this->data; $value = $this->$method($value, array_merge($this->data, $data)); $this->set[$name] = true; + if (is_null($value) && $array !== $this->data) { + return; + } } elseif (isset($this->type[$name])) { // 类型转换 $value = $this->writeTransform($value, $this->type[$name]); diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index aa1b13f..dcf53d3 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -160,13 +160,15 @@ trait Conversion foreach ($data as $key => $val) { if ($val instanceof Model || $val instanceof ModelCollection) { // 关联模型对象 - if (isset($this->visible[$key])) { + if (isset($this->visible[$key]) && is_array($this->visible[$key])) { $val->visible($this->visible[$key]); - } elseif (isset($this->hidden[$key])) { + } elseif (isset($this->hidden[$key]) && is_array($this->hidden[$key])) { $val->hidden($this->hidden[$key]); } // 关联模型对象 - $item[$key] = $val->toArray(); + if (!isset($this->hidden[$key]) || true !== $this->hidden[$key]) { + $item[$key] = $val->toArray(); + } } elseif (isset($this->visible[$key])) { $item[$key] = $this->getAttr($key); } elseif (!isset($this->hidden[$key]) && !$hasVisible) { diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 26a0cdd..734a790 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -83,9 +83,10 @@ trait RelationShip * 获取当前模型的关联模型数据 * @access public * @param string $name 关联方法名 + * @param bool $auto 不存在是否自动获取 * @return mixed */ - public function getRelation(string $name = null) + public function getRelation(string $name = null, bool $auto = false) { if (is_null($name)) { return $this->relation; @@ -93,6 +94,9 @@ trait RelationShip if (array_key_exists($name, $this->relation)) { return $this->relation[$name]; + } elseif ($auto) { + $method = Db::parseName($name, 1, false); + return $this->$method()->getRelation(); } } @@ -122,9 +126,10 @@ trait RelationShip * 查询当前模型的关联数据 * @access public * @param array $relations 关联名 + * @param array $withRelationAttr 关联获取器 * @return void */ - public function relationQuery(array $relations): void + public function relationQuery(array $relations, array $withRelationAttr = []): void { foreach ($relations as $key => $relation) { $subRelation = ''; @@ -143,9 +148,16 @@ trait RelationShip list($relation, $subRelation) = explode('.', $relation, 2); } - $method = Db::parseName($relation, 1, false); + $method = Db::parseName($relation, 1, false); + $relationName = Db::parseName($relation); + + $relationResult = $this->$method(); + + if (isset($withRelationAttr[$relationName])) { + $relationResult->getQuery()->withAttr($withRelationAttr[$relationName]); + } - $this->relation[$relation] = $this->$method()->getRelation($subRelation, $closure); + $this->relation[$relation] = $relationResult->getRelation($subRelation, $closure); } } @@ -633,7 +645,7 @@ trait RelationShip if ($val instanceof Model) { $val->exists(true)->save(); } else { - $model = $this->getRelation($name); + $model = $this->getRelation($name, true); if ($model instanceof Model) { $model->exists(true)->save($val); @@ -664,7 +676,7 @@ trait RelationShip { foreach ($this->relationWrite as $key => $name) { $name = is_numeric($key) ? $name : $key; - $result = $this->getRelation($name); + $result = $this->getRelation($name, true); if ($result instanceof Model) { $result->delete(); diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index c6164e1..2d3458d 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -478,7 +478,7 @@ class BelongsToMany extends Relation $query = $this->query ->field($fields) - ->field(true, false, $table, 'pivot', 'pivot__'); + ->tableField(true, $table, 'pivot', 'pivot__'); if (empty($this->baseQuery)) { $relationFk = $this->query->getPk(); diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index 234119e..27248ab 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -79,8 +79,7 @@ abstract class OneToOne extends Relation } else { $masterField = true; } - - $query->field($masterField, false, $table, $name); + $query->tableField($masterField, $table, $name); } // 预载入封装 @@ -108,7 +107,7 @@ abstract class OneToOne extends Relation } $query->join([$joinTable => $joinAlias], $joinOn, $joinType) - ->field($field, false, $joinTable, $joinAlias, $relation . '__'); + ->tableField($field, $joinTable, $joinAlias, $relation . '__'); } /** -- Gitee From 0f3d3745c06d592eba3d585ece221d5946a6d512 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 31 May 2019 10:20:43 +0800 Subject: [PATCH 013/365] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme=20=E5=88=A0?= =?UTF-8?q?=E9=99=A4CacheItem=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 -- src/CacheItem.php | 207 ---------------------------------------------- src/Model.php | 11 ++- 3 files changed, 7 insertions(+), 219 deletions(-) delete mode 100644 src/CacheItem.php diff --git a/README.md b/README.md index 0639ba8..04dca62 100644 --- a/README.md +++ b/README.md @@ -181,14 +181,6 @@ $user->save(); ```php User::destroy(1); ``` -* 静态调用 - - ```php - User::update([ - 'name' => 'topthink', - 'email' => 'topthink@qq.com', - ], ['id' => 1]); - ``` * destroy方法支持删除指定主键或者查询条件的数据 ```php diff --git a/src/CacheItem.php b/src/CacheItem.php deleted file mode 100644 index c6fbcdd..0000000 --- a/src/CacheItem.php +++ /dev/null @@ -1,207 +0,0 @@ - -// +---------------------------------------------------------------------- -declare (strict_types = 1); - -namespace think\cache; - -use DateInterval; -use DateTime; -use DateTimeInterface; -use InvalidArgumentException; -use Psr\Cache\CacheItemInterface; - -class CacheItem implements CacheItemInterface -{ - /** - * 缓存Key - * @var string - */ - protected $key; - - /** - * 缓存内容 - * @var mixed - */ - protected $value; - - /** - * 过期时间 - * @var int|DateTimeInterface - */ - protected $expire; - - /** - * 缓存tag - * @var string - */ - protected $tag; - - /** - * 缓存是否命中 - * @var bool - */ - protected $isHit = false; - - public function __construct(string $key = null) - { - $this->key = $key; - } - - /** - * 为此缓存项设置「键」 - * @access public - * @param string $key - * @return $this - */ - public function setKey(string $key) - { - $this->key = $key; - return $this; - } - - /** - * 返回当前缓存项的「键」 - * @access public - * @return string - */ - public function getKey() - { - return $this->key; - } - - /** - * 返回当前缓存项的有效期 - * @access public - * @return DateTimeInterface|int|null - */ - public function getExpire() - { - if ($this->expire instanceof DateTimeInterface) { - return $this->expire; - } - - return $this->expire ? $this->expire - time() : null; - } - - /** - * 获取缓存Tag - * @access public - * @return string - */ - public function getTag() - { - return $this->tag; - } - - /** - * 凭借此缓存项的「键」从缓存系统里面取出缓存项 - * @access public - * @return mixed - */ - public function get() - { - return $this->value; - } - - /** - * 确认缓存项的检查是否命中 - * @access public - * @return bool - */ - public function isHit(): bool - { - return $this->isHit; - } - - /** - * 为此缓存项设置「值」 - * @access public - * @param mixed $value - * @return $this - */ - public function set($value) - { - $this->value = $value; - $this->isHit = true; - return $this; - } - - /** - * 为此缓存项设置所属标签 - * @access public - * @param string $tag - * @return $this - */ - public function tag(string $tag = null) - { - $this->tag = $tag; - return $this; - } - - /** - * 设置缓存项的有效期 - * @access public - * @param mixed $expire - * @return $this - */ - public function expire($expire) - { - if (is_null($expire)) { - $this->expire = null; - } elseif (is_numeric($expire) || $expire instanceof DateInterval) { - $this->expiresAfter($expire); - } elseif ($expire instanceof DateTimeInterface) { - $this->expire = $expire; - } else { - throw new InvalidArgumentException('not support datetime'); - } - - return $this; - } - - /** - * 设置缓存项的准确过期时间点 - * @access public - * @param DateTimeInterface $expiration - * @return $this - */ - public function expiresAt($expiration) - { - if ($expiration instanceof DateTimeInterface) { - $this->expire = $expiration; - } else { - throw new InvalidArgumentException('not support datetime'); - } - - return $this; - } - - /** - * 设置缓存项的过期时间 - * @access public - * @param int|DateInterval $timeInterval - * @return $this - * @throws InvalidArgumentException - */ - public function expiresAfter($timeInterval) - { - if ($timeInterval instanceof DateInterval) { - $this->expire = (int) DateTime::createFromFormat('U', time())->add($timeInterval)->format('U'); - } elseif (is_numeric($timeInterval)) { - $this->expire = $timeInterval + time(); - } else { - throw new InvalidArgumentException('not support datetime'); - } - - return $this; - } - -} diff --git a/src/Model.php b/src/Model.php index 96c5196..9241418 100644 --- a/src/Model.php +++ b/src/Model.php @@ -834,10 +834,13 @@ abstract class Model implements JsonSerializable, ArrayAccess public function __debugInfo() { - return [ - 'data' => $this->data, - 'relation' => $this->relation, - ]; + $attrs = get_object_vars($this); + + foreach (['db', 'queryInstance', 'event'] as $name) { + unset($attrs[$name]); + } + + return $attrs; } /** -- Gitee From c7b144d21b856fa4c19fb9d14f169bd3e43faf4e Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 31 May 2019 11:25:15 +0800 Subject: [PATCH 014/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Mongo.php | 31 ++++++++++++++----- src/db/builder/Mongo.php | 63 ++++++++++++++++++++------------------ src/db/connector/Mongo.php | 32 ++++++++++++++++++- 3 files changed, 88 insertions(+), 38 deletions(-) diff --git a/src/db/Mongo.php b/src/db/Mongo.php index d9d64d7..0fc6c53 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -379,18 +379,35 @@ class Mongo extends BaseQuery return $this; } + /** + * 获取执行的SQL语句而不进行实际的查询 + * @access public + * @param bool $fetch 是否返回sql + * @return $this|Fetch + */ + public function fetchSql(bool $fetch = true) + { + $this->options['fetch_sql'] = $fetch; + + if ($fetch) { + throw new Exception('Mongo not support fetchSql'); + } + + return $this; + } + /** * 设置返回字段 * @access public - * @param mixed $field 字段信息 - * @param boolean $except 是否排除 - * @param string $tableName 数据表名 - * @param string $prefix 字段前缀 - * @param string $alias 别名前缀 + * @param mixed $field 字段信息 * @return $this */ - public function field($field, bool $except = false, string $tableName = '', string $prefix = '', string $alias = '') + public function field($field) { + if (empty($field) || '*' == $field) { + return $this; + } + if (is_string($field)) { $field = array_map('trim', explode(',', $field)); } @@ -549,7 +566,7 @@ class Mongo extends BaseQuery $options['modifiers'] = $modifiers; } - if (!isset($options['projection']) || '*' == $options['projection']) { + if (!isset($options['projection'])) { $options['projection'] = []; } diff --git a/src/db/builder/Mongo.php b/src/db/builder/Mongo.php index a6ee72e..9349737 100644 --- a/src/db/builder/Mongo.php +++ b/src/db/builder/Mongo.php @@ -27,7 +27,7 @@ class Mongo // 最后插入ID protected $insertId = []; // 查询表达式 - protected $exp = ['<>' => 'ne', 'neq' => 'ne', '=' => 'eq', '>' => 'gt', '>=' => 'gte', '<' => 'lt', '<=' => 'lte', 'in' => 'in', 'not in' => 'nin', 'nin' => 'nin', 'mod' => 'mod', 'exists' => 'exists', 'null' => 'null', 'notnull' => 'not null', 'not null' => 'not null', 'regex' => 'regex', 'type' => 'type', 'all' => 'all', '> time' => '> time', '< time' => '< time', 'between' => 'between', 'not between' => 'not between', 'between time' => 'between time', 'not between time' => 'not between time', 'notbetween time' => 'not between time', 'like' => 'like', 'near' => 'near', 'size' => 'size']; + protected $exp = ['<>' => 'ne', '=' => 'eq', '>' => 'gt', '>=' => 'gte', '<' => 'lt', '<=' => 'lte', 'in' => 'in', 'not in' => 'nin', 'nin' => 'nin', 'mod' => 'mod', 'exists' => 'exists', 'null' => 'null', 'notnull' => 'not null', 'not null' => 'not null', 'regex' => 'regex', 'type' => 'type', 'all' => 'all', '> time' => '> time', '< time' => '< time', 'between' => 'between', 'not between' => 'not between', 'between time' => 'between time', 'not between time' => 'not between time', 'notbetween time' => 'not between time', 'like' => 'like', 'near' => 'near', 'size' => 'size']; /** * 架构函数 @@ -55,7 +55,7 @@ class Mongo * @param string $key * @return string */ - protected function parseKey($key) + protected function parseKey(Query $query, string $key): string { if (0 === strpos($key, '__TABLE__.')) { list($collection, $key) = explode('.', $key, 2); @@ -96,7 +96,7 @@ class Mongo * @param array $data 数据 * @return array */ - protected function parseData(Query $query, $data) + protected function parseData(Query $query, array $data): array { if (empty($data)) { return []; @@ -105,7 +105,7 @@ class Mongo $result = []; foreach ($data as $key => $val) { - $item = $this->parseKey($key); + $item = $this->parseKey($query, $key); if (is_object($val)) { $result[$item] = $val; @@ -128,7 +128,7 @@ class Mongo * @param array $data 数据 * @return array */ - protected function parseSet(Query $query, $data) + protected function parseSet(Query $query, array $data): array { if (empty($data)) { return []; @@ -137,7 +137,7 @@ class Mongo $result = []; foreach ($data as $key => $val) { - $item = $this->parseKey($key); + $item = $this->parseKey($query, $key); if (is_array($val) && isset($val[0]) && is_string($val[0]) && 0 === strpos($val[0], '$')) { $result[$val[0]][$item] = $this->parseValue($query, $val[1], $key); @@ -156,7 +156,7 @@ class Mongo * @param mixed $where * @return array */ - public function parseWhere(Query $query, $where) + public function parseWhere(Query $query, array $where): array { if (empty($where)) { $where = []; @@ -213,9 +213,9 @@ class Mongo } // where子单元分析 - protected function parseWhereItem(Query $query, $field, $val) + protected function parseWhereItem(Query $query, $field, $val): array { - $key = $field ? $this->parseKey($field) : ''; + $key = $field ? $this->parseKey($query, $field) : ''; // 查询规则和条件 if (!is_array($val)) { $val = ['=', $val]; @@ -330,17 +330,21 @@ class Mongo protected function parseDateTime(Query $query, $value, $key) { // 获取时间字段类型 - $type = $this->connection->getTableInfo('', 'type'); + $type = $query->getFieldType($key); - if (isset($type[$key])) { - $value = strtotime($value) ?: $value; + if ($type) { + if (is_string($value)) { + $value = strtotime($value) ?: $value; + } - if (preg_match('/(datetime|timestamp)/is', $type[$key])) { - // 日期及时间戳类型 - $value = date('Y-m-d H:i:s', $value); - } elseif (preg_match('/(date)/is', $type[$key])) { - // 日期及时间戳类型 - $value = date('Y-m-d', $value); + if (is_int($value)) { + if (preg_match('/(datetime|timestamp)/is', $type)) { + // 日期及时间戳类型 + $value = date('Y-m-d H:i:s', $value); + } elseif (preg_match('/(date)/is', $type)) { + // 日期及时间戳类型 + $value = date('Y-m-d', $value); + } } } @@ -361,10 +365,9 @@ class Mongo * 生成insert BulkWrite对象 * @access public * @param Query $query 查询对象 - * @param array $data 数据 * @return BulkWrite */ - public function insert(Query $query, $replace = false) + public function insert(Query $query): BulkWrite { // 分析并处理数据 $options = $query->getOptions(); @@ -389,7 +392,7 @@ class Mongo * @param array $dataSet 数据集 * @return BulkWrite */ - public function insertAll(Query $query, $dataSet) + public function insertAll(Query $query, array $dataSet): BulkWrite { $bulk = new BulkWrite; $options = $query->getOptions(); @@ -414,7 +417,7 @@ class Mongo * @param Query $query 查询对象 * @return BulkWrite */ - public function update(Query $query) + public function update(Query $query): BulkWrite { $options = $query->getOptions(); @@ -442,7 +445,7 @@ class Mongo * @param Query $query 查询对象 * @return BulkWrite */ - public function delete(Query $query) + public function delete(Query $query): BulkWrite { $options = $query->getOptions(); $where = $this->parseWhere($query, $options['where']); @@ -469,7 +472,7 @@ class Mongo * @param bool $one 是否仅获取一个记录 * @return MongoQuery */ - public function select(Query $query, bool $one = false) + public function select(Query $query, bool $one = false): MongoQuery { $options = $query->getOptions(); @@ -492,7 +495,7 @@ class Mongo * @param Query $query 查询对象 * @return Command */ - public function count(Query $query) + public function count(Query $query): Command { $options = $query->getOptions(); @@ -518,7 +521,7 @@ class Mongo * @param array $extra 指令和字段 * @return Command */ - public function aggregate(Query $query, $extra) + public function aggregate(Query $query, array $extra): Command { $options = $query->getOptions(); list($fun, $field) = $extra; @@ -561,7 +564,7 @@ class Mongo * @param array $extra 指令和字段 * @return Command */ - public function multiAggregate(Query $query, $extra) + public function multiAggregate(Query $query, $extra): Command { $options = $query->getOptions(); @@ -608,7 +611,7 @@ class Mongo * @param string $field 字段名 * @return Command */ - public function distinct(Query $query, $field) + public function distinct(Query $query, $field): Command { $options = $query->getOptions(); @@ -637,7 +640,7 @@ class Mongo * @access public * @return Command */ - public function listcollections() + public function listcollections(): Command { $cmd = ['listCollections' => 1]; $command = new Command($cmd); @@ -653,7 +656,7 @@ class Mongo * @param Query $query 查询对象 * @return Command */ - public function collStats(Query $query) + public function collStats(Query $query): Command { $options = $query->getOptions(); diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 48fb8f6..a953906 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -698,7 +698,7 @@ class Mongo $dbConfig = []; foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn'] as $name) { - $dbConfig[$name] = isset($config[$name][$r]) ? $config[$name][$r] : $config[$name][0]; + $dbConfig[$name] = $config[$name][$r] ?? $config[$name][0]; } return $this->connect($dbConfig, $r); @@ -1203,6 +1203,36 @@ class Mongo return $result; } + /** + * 延时更新检查 返回false表示需要延时 + * 否则返回实际写入的数值 + * @access public + * @param string $type 自增或者自减 + * @param string $guid 写入标识 + * @param float $step 写入步进值 + * @param integer $lazyTime 延时时间(s) + * @return false|integer + */ + public function lazyWrite(string $type, string $guid, float $step, int $lazyTime) + { + if (!$this->cache->has($guid . '_time')) { + // 计时开始 + $this->cache->set($guid . '_time', time(), 0); + $this->cache->$type($guid, $step); + } elseif (time() > $this->cache->get($guid . '_time') + $lazyTime) { + // 删除缓存 + $value = $this->cache->$type($guid, $step); + $this->cache->delete($guid); + $this->cache->delete($guid . '_time'); + return 0 === $value ? false : $value; + } else { + // 更新缓存 + $this->cache->$type($guid, $step); + } + + return false; + } + /** * 执行command * @access public -- Gitee From 28369a92ddfc61ea9336310e55469f8802033128 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 31 May 2019 14:17:51 +0800 Subject: [PATCH 015/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Query.php | 4 ++-- src/db/connector/Mongo.php | 24 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/db/Query.php b/src/db/Query.php index d9aede1..5a9b677 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -412,9 +412,9 @@ class Query * 获取最近插入的ID * @access public * @param string $sequence 自增序列名 - * @return string + * @return mixed */ - public function getLastInsID(string $sequence = null): string + public function getLastInsID(string $sequence = null) { $insertId = $this->connection->getLastInsID($sequence); diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index a953906..d2e26f0 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -750,7 +750,7 @@ class Mongo * @access public * @param Query $query 查询对象 * @param boolean $getLastInsID 返回自增主键 - * @return WriteResult + * @return mixed * @throws AuthenticationException * @throws InvalidArgumentException * @throws ConnectionException @@ -1271,6 +1271,28 @@ class Mongo return []; } + /** + * 获取数据表绑定信息 + * @access public + * @param mixed $tableName 数据表名 + * @return array + */ + public function getFieldsBind($tableName): array + { + return []; + } + + /** + * 获取字段绑定类型 + * @access public + * @param string $type 字段类型 + * @return integer + */ + public function getFieldBindType(string $type): int + { + return 1; + } + /** * 启动事务 * @access public -- Gitee From 4b05aabbb5a305ad52dee79f5511ac7e45949299 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 31 May 2019 16:40:09 +0800 Subject: [PATCH 016/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=B5=AE=E7=82=B9?= =?UTF-8?q?=E5=9E=8B=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Connection.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/db/Connection.php b/src/db/Connection.php index 5143104..5ef27da 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -1281,9 +1281,7 @@ abstract class Connection $value = is_array($val) ? $val[0] : $val; $type = is_array($val) ? $val[1] : PDO::PARAM_STR; - if (self::PARAM_FLOAT == $type) { - $value = (float) $value; - } elseif (PDO::PARAM_STR == $type && is_string($value)) { + if ((self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) && is_string($value)) { $value = '\'' . addslashes($value) . '\''; } elseif (PDO::PARAM_INT == $type && '' === $value) { $value = 0; @@ -1317,7 +1315,7 @@ abstract class Connection if (PDO::PARAM_INT == $val[1] && '' === $val[0]) { $val[0] = 0; } elseif (self::PARAM_FLOAT == $val[1]) { - $val[0] = (float) $val[0]; + $val[0] = is_string($val[0]) ? (float) $val[0] : $val[0]; $val[1] = PDO::PARAM_STR; } -- Gitee From 020d5609e5b6a9df7dd442422c4820f4f73d07d2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 31 May 2019 16:44:32 +0800 Subject: [PATCH 017/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3field=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Mongo.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/db/Mongo.php b/src/db/Mongo.php index 0fc6c53..2a5df16 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -400,9 +400,10 @@ class Mongo extends BaseQuery * 设置返回字段 * @access public * @param mixed $field 字段信息 + * @param bool $except 是否排除 * @return $this */ - public function field($field) + public function field($field, bool $except = false) { if (empty($field) || '*' == $field) { return $this; -- Gitee From fe361126c37a96c395008aa32785bb331f5eda1f Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 7 Jul 2019 22:16:09 +0800 Subject: [PATCH 018/365] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 8 +- src/Collection.php | 22 + src/Db.php | 197 +- src/Model.php | 64 +- src/Paginator.php | 53 +- src/config.php | 60 - src/db/BaseQuery.php | 1680 ++++++++++++ src/db/Builder.php | 37 +- src/db/Connection.php | 1766 ++---------- src/db/Fetch.php | 12 +- src/db/PDOConnection.php | 1595 +++++++++++ src/db/Query.php | 3560 +------------------------ src/db/builder/Mysql.php | 12 +- src/db/concern/AggregateQuery.php | 107 + src/db/concern/JoinAndViewQuery.php | 283 ++ src/db/concern/ModelRelationQuery.php | 427 +++ src/db/concern/PDOQuery.php | 54 + src/db/concern/ParamsBind.php | 106 + src/db/concern/ResultOperation.php | 248 ++ src/db/concern/TableFieldInfo.php | 111 + src/db/concern/TimeFieldQuery.php | 214 ++ src/db/concern/Transaction.php | 64 + src/db/concern/WhereQuery.php | 540 ++++ src/db/connector/Mysql.php | 56 +- src/facade/Db.php | 43 - src/model/Collection.php | 37 +- src/model/Pivot.php | 6 +- src/model/Relation.php | 50 +- src/model/concern/Attribute.php | 59 +- src/model/concern/Conversion.php | 32 +- src/model/concern/ModelEvent.php | 11 +- src/model/concern/RelationShip.php | 99 +- src/model/concern/TimeStamp.php | 79 +- src/model/relation/BelongsTo.php | 85 +- src/model/relation/BelongsToMany.php | 141 +- src/model/relation/HasMany.php | 142 +- src/model/relation/HasManyThrough.php | 299 ++- src/model/relation/HasOne.php | 76 +- src/model/relation/HasOneThrough.php | 161 ++ src/model/relation/MorphMany.php | 120 +- src/model/relation/MorphOne.php | 46 +- src/model/relation/MorphTo.php | 52 +- src/model/relation/OneToOne.php | 101 +- 43 files changed, 6853 insertions(+), 6062 deletions(-) delete mode 100644 src/config.php create mode 100644 src/db/BaseQuery.php create mode 100644 src/db/PDOConnection.php create mode 100644 src/db/concern/AggregateQuery.php create mode 100644 src/db/concern/JoinAndViewQuery.php create mode 100644 src/db/concern/ModelRelationQuery.php create mode 100644 src/db/concern/PDOQuery.php create mode 100644 src/db/concern/ParamsBind.php create mode 100644 src/db/concern/ResultOperation.php create mode 100644 src/db/concern/TableFieldInfo.php create mode 100644 src/db/concern/TimeFieldQuery.php create mode 100644 src/db/concern/Transaction.php create mode 100644 src/db/concern/WhereQuery.php create mode 100644 src/model/relation/HasOneThrough.php diff --git a/composer.json b/composer.json index 69d8a1e..5fda2ad 100644 --- a/composer.json +++ b/composer.json @@ -14,15 +14,13 @@ ], "require": { "php": ">=7.1.0", - "topthink/think-cache": "~2.0", - "topthink/think-container":"~2.0" + "topthink/think-cache": "^2.0", + "topthink/think-container":"^2.0" }, "autoload": { "psr-4": { "think\\": "src" }, - "files": [ - "src/config.php" - ] + "files": [] } } \ No newline at end of file diff --git a/src/Collection.php b/src/Collection.php index f2c49a1..9997508 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -532,6 +532,28 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria return new static($items); } + /** + * 获取最后一个单元数据 + * + * @access public + * @return mixed + */ + public function first() + { + return reset($this->items); + } + + /** + * 获取第一个单元数据 + * + * @access public + * @return mixed + */ + public function last() + { + return end($this->items); + } + /** * 截取数组 * diff --git a/src/Db.php b/src/Db.php index a776a4c..45ca504 100644 --- a/src/Db.php +++ b/src/Db.php @@ -12,27 +12,19 @@ declare (strict_types = 1); namespace think; -use Exception; use InvalidArgumentException; -use think\Container; +use think\db\BaseQuery; use think\db\Connection; -use think\db\Query; use think\db\Raw; -use think\exception\DbException; /** * Class Db * @package think + * @mixin BaseQuery * @mixin Query */ -class Db +class DbManager { - /** - * 当前数据库连接对象 - * @var Connection - */ - protected $connection; - /** * 数据库连接实例 * @var array @@ -64,81 +56,64 @@ class Db protected $queryTimes = 0; /** - * 架构函数 + * 初始化 * @param array $config 连接配置 * @access public */ - public function __construct(array $config = []) + public function init(array $config = []) { $this->config = $config; } - public function setConfig(array $config): void - { - $this->config = array_merge($this->config, $config); - } - /** - * 切换数据库连接 + * 创建/切换数据库连接查询 * @access public - * @param mixed $config 连接配置 - * @param bool|string $name 连接标识 true 强制重新连接 - * @return $this + * @param string|null $name 连接配置标识 + * @param bool $force 强制重新连接 + * @return BaseQuery */ - public function connect($config = [], $name = false) + public function connect(string $name = null, bool $force = false): BaseQuery { - $this->connection = $this->instance($this->parseConfig($config), $name); - return $this; + $connection = $this->instance($name, $force); + $connection->setDb($this); + + $class = $connection->getQueryClass(); + $query = new $class($connection); + + if (!empty($this->config['time_query_rule'])) { + $query->timeRule($this->config['time_query_rule']); + } + + return $query; } /** - * 取得数据库连接类实例 - * @access public - * @param array $config 连接配置 - * @param bool|string $name 连接标识 true 强制重新连接 + * 创建数据库连接实例 + * @access protected + * @param string|null $name 连接标识 + * @param bool $force 强制重新连接 * @return Connection */ - public function instance(array $config = [], $name = false) + protected function instance(string $name = null, bool $force = false): Connection { - if (false === $name) { - $name = md5(serialize($config)); + if (empty($name)) { + $name = $this->config['default'] ?? 'mysql'; } - if (true === $name || !isset($this->instance[$name])) { - - if (empty($config['type'])) { - throw new InvalidArgumentException('Undefined db type'); + if ($force || !isset($this->instance[$name])) { + if (!isset($this->config['connections'][$name])) { + throw new InvalidArgumentException('Undefined db config:' . $name); } - if (true === $name) { - $name = md5(serialize($config)); - } + $config = $this->config['connections'][$name]; + $type = !empty($config['type']) ? $config['type'] : 'mysql'; - $this->instance[$name] = $this->factory($config['type'], '\\think\\db\\connector\\', $config); + $this->instance[$name] = Container::factory($type, '\\think\\db\\connector\\', $config); } return $this->instance[$name]; } - /** - * 创建工厂对象实例 - * @access public - * @param string $name 工厂类名 - * @param string $namespace 默认命名空间 - * @param array $args - * @return mixed - */ - public function factory(string $name, string $namespace = '', ...$args) - { - $class = false !== strpos($name, '\\') ? $name : $namespace . ucwords($name); - - if (class_exists($class)) { - return Container::getInstance()->invokeClass($class, $args); - } - - throw new Exception('class not exists:' . $class); - } - /** * 使用表达式设置数据 * @access public @@ -180,51 +155,6 @@ class Db return $this->queryTimes; } - /** - * 数据库连接参数解析 - * @access private - * @param mixed $config - * @return array - */ - private function parseConfig($config): array - { - if (empty($config)) { - $config = $this->config; - } elseif (is_string($config) && isset($this->config[$config])) { - // 支持读取配置参数 - $config = $this->config[$config]; - } - - if (!is_array($config)) { - throw new DbException('database config error:' . $config); - } - - return $config; - } - - /** - * 获取数据库的配置参数 - * @access public - * @param string $name 参数名称 - * @return mixed - */ - public function getConfig(string $name = '') - { - return $name ? ($this->config[$name] ?? null) : $this->config; - } - - /** - * 创建一个新的查询对象 - * @access public - * @param string|array $connection 连接配置信息 - * @return mixed - */ - public function buildQuery($connection = []) - { - $connection = $this->instance($this->parseConfig($connection)); - return $this->newQuery($connection); - } - /** * 监听SQL执行 * @access public @@ -273,65 +203,8 @@ class Db } } - /** - * 创建一个新的查询对象 - * @access protected - * @param Connection $connection 连接对象 - * @return mixed - */ - protected function newQuery($connection = null) - { - /** @var Query $query */ - if (is_null($connection) && !$this->connection) { - $this->connect($this->config); - } - - $connection = $connection ?: $this->connection; - $class = $connection->getQueryClass(); - $query = new $class($connection); - - $query->setDb($this); - - return $query; - } - - /** - * 字符串命名风格转换 - * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 - * @access public - * @param string $name 字符串 - * @param integer $type 转换类型 - * @param bool $ucfirst 首字母是否大写(驼峰规则) - * @return string - */ - public function parseName(string $name = null, int $type = 0, bool $ucfirst = true): string - { - if ($type) { - $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) { - return strtoupper($match[1]); - }, $name); - return $ucfirst ? ucfirst($name) : lcfirst($name); - } - - return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); - } - - /** - * 获取类名(不包含命名空间) - * @access public - * @param string|object $class - * @return string - */ - public function classBaseName($class): string - { - $class = is_object($class) ? get_class($class) : $class; - return basename(str_replace('\\', '/', $class)); - } - public function __call($method, $args) { - $query = $this->newQuery($this->connection); - - return call_user_func_array([$query, $method], $args); + return call_user_func_array([$this->connect(), $method], $args); } } diff --git a/src/Model.php b/src/Model.php index 9241418..ecc02f5 100644 --- a/src/Model.php +++ b/src/Model.php @@ -16,7 +16,6 @@ use ArrayAccess; use Closure; use JsonSerializable; use think\db\Query; -use think\facade\Db; /** * Class Model @@ -127,24 +126,31 @@ abstract class Model implements JsonSerializable, ArrayAccess protected $db; /** - * 设置Connection信息 + * 服务注入 + * @var Closure + */ + protected static $maker; + + /** + * 设置服务注入 * @access public - * @param mixed $connection 数据库配置 + * @param Closure $maker * @return void */ - public function setConnection($connection) + public static function maker(Closure $maker) { - $this->connection = $connection; + static::$maker = $maker; } /** - * 获取Connection信息 + * 设置Db对象 * @access public - * @return string|array + * @param Db $db Db对象 + * @return void */ - public function getConnection() + public function setDb(Db $db) { - return $this->connection; + $this->db = $db; } /** @@ -174,6 +180,12 @@ abstract class Model implements JsonSerializable, ArrayAccess $this->name = basename($name); } + $this->db = Container::pull('think\DbManager'); + + if (static::$maker) { + call_user_func(static::$maker, $this); + } + // 执行初始化操作 $this->initialize(); } @@ -238,6 +250,16 @@ abstract class Model implements JsonSerializable, ArrayAccess return $this; } + /** + * 获取当前模型的数据库查询对象 + * @access public + * @return Query|null + */ + public function getQuery() + { + return $this->queryInstance; + } + /** * 设置当前模型数据表的后缀 * @access public @@ -263,7 +285,7 @@ abstract class Model implements JsonSerializable, ArrayAccess /** * 获取当前模型的数据库查询对象 * @access public - * @param array|false $scope 使用的全局查询范围 + * @param array $scope 设置不使用的全局查询范围 * @return Query */ public function db($scope = []): Query @@ -272,19 +294,19 @@ abstract class Model implements JsonSerializable, ArrayAccess if ($this->queryInstance) { $query = $this->queryInstance; } else { - $query = Db::buildQuery($this->connection) + $query = $this->db->connect($this->connection) ->name($this->name . $this->suffix) ->pk($this->pk); } - $query->model($this) - ->json($this->json, $this->jsonAssoc) - ->setFieldType(array_merge($this->schema, $this->jsonType)); - if (!empty($this->table)) { $query->table($this->table . $this->suffix); } + $query->model($this) + ->json($this->json, $this->jsonAssoc) + ->setFieldType(array_merge($this->schema, $this->jsonType)); + // 软删除 if (property_exists($this, 'withTrashed') && !$this->withTrashed) { $this->withNoTrashed($query); @@ -370,7 +392,7 @@ abstract class Model implements JsonSerializable, ArrayAccess public function refresh(bool $relation = false) { if ($this->exists) { - $this->data = $this->db()->fetchArray()->find($this->getKey()); + $this->data = $this->db()->find($this->getKey())->getData(); $this->origin = $this->data; if ($relation) { @@ -618,10 +640,8 @@ abstract class Model implements JsonSerializable, ArrayAccess if ($result && $insertId = $db->getLastInsID($sequence)) { $pk = $this->getPk(); - foreach ((array) $pk as $key) { - if (!isset($this->data[$key]) || '' == $this->data[$key]) { - $this->data[$key] = $insertId; - } + if (is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) { + $this->data[$pk] = $insertId; } } @@ -689,7 +709,7 @@ abstract class Model implements JsonSerializable, ArrayAccess foreach ($dataSet as $key => $data) { if ($this->exists || (!empty($auto) && isset($data[$pk]))) { - $result[$key] = self::update($data, $this->field); + $result[$key] = self::update($data); } else { $result[$key] = self::create($data, $this->field, $this->replace); } @@ -928,7 +948,7 @@ abstract class Model implements JsonSerializable, ArrayAccess * @param string $suffix 切换的表后缀 * @return Model */ - public static function change(string $suffix) + public static function suffix(string $suffix) { $model = new static(); $model->setSuffix($suffix); diff --git a/src/Paginator.php b/src/Paginator.php index 1e0ab42..25be679 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -19,6 +19,7 @@ use Countable; use DomainException; use IteratorAggregate; use JsonSerializable; +use think\paginator\driver\Bootstrap; use Traversable; /** @@ -135,7 +136,16 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J */ public static function make($items, int $listRows, int $currentPage = 1, int $total = null, bool $simple = false, array $options = []) { - return new static($items, $listRows, $currentPage, $total, $simple, $options); + if (isset(static::$maker)) { + return call_user_func(static::$maker, $items, $listRows, $currentPage, $total, $simple, $options); + } + + return new Bootstrap($items, $listRows, $currentPage, $total, $simple, $options); + } + + public static function maker(Closure $resolver) + { + static::$maker = $resolver; } protected function setCurrentPage(int $currentPage): int @@ -189,33 +199,44 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J */ public static function getCurrentPage(string $varPage = 'page', int $default = 1): int { - $page = $_REQUEST[$varPage] ?? 1; - - if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) { - return $page; + if (isset(static::$currentPageResolver)) { + return call_user_func(static::$currentPageResolver, $varPage); } return $default; } + /** + * 设置获取当前页码闭包 + * @param Closure $resolver + */ + public static function currentPageResolver(Closure $resolver) + { + static::$currentPageResolver = $resolver; + } + /** * 自动获取当前的path * @access public + * @param string $default * @return string */ - public static function getCurrentPath(): string - { - if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { - $url = $_SERVER['HTTP_X_REWRITE_URL']; - } elseif (isset($_SERVER['REQUEST_URI'])) { - $url = $_SERVER['REQUEST_URI']; - } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { - $url = $_SERVER['ORIG_PATH_INFO'] . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ''); - } else { - $url = ''; + public static function getCurrentPath($default = '/'): string + { + if (isset(static::$currentPathResolver)) { + return call_user_func(static::$currentPathResolver); } - return strpos($url, '?') ? strstr($url, '?', true) : $url; + return $default; + } + + /** + * 设置获取当前路径闭包 + * @param Closure $resolver + */ + public static function currentPathResolver(Closure $resolver) + { + static::$currentPathResolver = $resolver; } public function total(): int diff --git a/src/config.php b/src/config.php deleted file mode 100644 index ababcf5..0000000 --- a/src/config.php +++ /dev/null @@ -1,60 +0,0 @@ - -// +---------------------------------------------------------------------- -namespace think; - -facade\Db::setConfig([ - // 数据库类型 - 'type' => '', - // 服务器地址 - 'hostname' => '', - // 数据库名 - 'database' => '', - // 用户名 - 'username' => '', - // 密码 - 'password' => '', - // 端口 - 'hostport' => '', - // 连接dsn - 'dsn' => '', - // 数据库连接参数 - 'params' => [], - // 数据库编码默认采用utf8 - 'charset' => 'utf8', - // 数据库表前缀 - 'prefix' => '', - // 数据库调试模式 - 'debug' => false, - // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, - // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, - // 读写分离后 主服务器数量 - 'master_num' => 1, - // 指定从服务器序号 - 'slave_no' => '', - // 是否严格检查字段是否存在 - 'fields_strict' => true, - // 自动写入时间戳字段 - 'auto_timestamp' => false, - // 时间字段取出后的默认时间格式 - 'datetime_format' => 'Y-m-d H:i:s', - // 是否需要进行SQL性能分析 - 'sql_explain' => false, - // 是否需要断线重连 - 'break_reconnect' => false, - // 默认分页设置 - 'paginate' => [ - 'type' => 'bootstrap', - 'var_page' => 'page', - 'list_rows' => 15, - ], -]); diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php new file mode 100644 index 0000000..27f7422 --- /dev/null +++ b/src/db/BaseQuery.php @@ -0,0 +1,1680 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db; + +use think\Collection; +use think\Container; +use think\db\exception\BindParamException; +use think\db\exception\DataNotFoundException; +use think\db\exception\ModelNotFoundException; +use think\Exception; +use think\exception\DbException; +use think\exception\PDOException; +use think\Model; +use think\Paginator; + +/** + * 数据查询类 + */ +class BaseQuery +{ + use concern\TimeFieldQuery; + use concern\AggregateQuery; + use concern\ModelRelationQuery; + use concern\ResultOperation; + use concern\Transaction; + use concern\WhereQuery; + + /** + * 当前数据库连接对象 + * @var Connection + */ + protected $connection; + + /** + * 当前数据表名称(不含前缀) + * @var string + */ + protected $name = ''; + + /** + * 当前数据表主键 + * @var string|array + */ + protected $pk; + + /** + * 当前数据表前缀 + * @var string + */ + protected $prefix = ''; + + /** + * 当前查询参数 + * @var array + */ + protected $options = []; + + /** + * 架构函数 + * @access public + * @param Connection $connection 数据库连接对象 + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + + $this->prefix = $this->connection->getConfig('prefix'); + } + + /** + * 创建一个新的查询对象 + * @access public + * @return BaseQuery + */ + public function newQuery(): BaseQuery + { + $query = new static($this->connection); + + if ($this->model) { + $query->model($this->model); + } + + if (isset($this->options['table'])) { + $query->table($this->options['table']); + } else { + $query->name($this->name); + } + + return $query; + } + + /** + * 利用__call方法实现一些特殊的Model方法 + * @access public + * @param string $method 方法名称 + * @param array $args 调用参数 + * @return mixed + * @throws DbException + * @throws Exception + */ + public function __call(string $method, array $args) + { + if (strtolower(substr($method, 0, 5)) == 'getby') { + // 根据某个字段获取记录 + $field = Container::parseName(substr($method, 5)); + return $this->where($field, '=', $args[0])->find(); + } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { + // 根据某个字段获取记录的某个值 + $name = Container::parseName(substr($method, 10)); + return $this->where($name, '=', $args[0])->value($args[1]); + } elseif (strtolower(substr($method, 0, 7)) == 'whereor') { + $name = Container::parseName(substr($method, 7)); + array_unshift($args, $name); + return call_user_func_array([$this, 'whereOr'], $args); + } elseif (strtolower(substr($method, 0, 5)) == 'where') { + $name = Container::parseName(substr($method, 5)); + array_unshift($args, $name); + return call_user_func_array([$this, 'where'], $args); + } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { + // 动态调用命名范围 + $method = 'scope' . $method; + array_unshift($args, $this); + + call_user_func_array([$this->model, $method], $args); + return $this; + } else { + throw new Exception('method not exist:' . static::class . '->' . $method); + } + } + + /** + * 获取当前的数据库Connection对象 + * @access public + * @return Connection + */ + public function getConnection(): Connection + { + return $this->connection; + } + + /** + * 设置当前的数据库Connection对象 + * @access public + * @param Connection $connection 数据库连接对象 + * @return $this + */ + public function setConnection(Connection $connection) + { + $this->connection = $connection; + + return $this; + } + + /** + * 指定当前数据表名(不含前缀) + * @access public + * @param string $name 不含前缀的数据表名字 + * @return $this + */ + public function name(string $name) + { + $this->name = $name; + return $this; + } + + /** + * 获取当前的数据表名称 + * @access public + * @return string + */ + public function getName(): string + { + return $this->name ?: $this->model->getName(); + } + + /** + * 获取数据库的配置参数 + * @access public + * @param string $name 参数名称 + * @return mixed + */ + public function getConfig(string $name = '') + { + return $this->connection->getConfig($name); + } + + /** + * 得到当前或者指定名称的数据表 + * @access public + * @param string $name 不含前缀的数据表名字 + * @return mixed + */ + public function getTable(string $name = '') + { + if (empty($name) && isset($this->options['table'])) { + return $this->options['table']; + } + + $name = $name ?: $this->name; + + return $this->prefix . Container::parseName($name); + } + + /** + * 执行查询 返回数据集 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @return array + * @throws BindParamException + * @throws PDOException + */ + public function query(string $sql, array $bind = []): array + { + return $this->connection->query($this, $sql, $bind); + } + + /** + * 执行语句 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @return int + * @throws BindParamException + * @throws PDOException + */ + public function execute(string $sql, array $bind = []): int + { + return $this->connection->execute($this, $sql, $bind, true); + } + + /** + * 获取返回或者影响的记录数 + * @access public + * @return integer + */ + public function getNumRows(): int + { + return $this->connection->getNumRows(); + } + + /** + * 获取最近一次查询的sql语句 + * @access public + * @return string + */ + public function getLastSql(): string + { + return $this->connection->getLastSql(); + } + + /** + * 获取最近插入的ID + * @access public + * @param string $sequence 自增序列名 + * @return mixed + */ + public function getLastInsID(string $sequence = null) + { + return $this->connection->getLastInsID($this, $sequence); + } + + /** + * 批处理执行SQL语句 + * 批处理的指令都认为是execute操作 + * @access public + * @param array $sql SQL批处理指令 + * @return bool + */ + public function batchQuery(array $sql = []): bool + { + return $this->connection->batchQuery($this, $sql); + } + + /** + * 得到某个字段的值 + * @access public + * @param string $field 字段名 + * @param mixed $default 默认值 + * @return mixed + */ + public function value(string $field, $default = null) + { + return $this->connection->value($this, $field, $default); + } + + /** + * 得到某个列的数组 + * @access public + * @param string $field 字段名 多个字段用逗号分隔 + * @param string $key 索引 + * @return array + */ + public function column(string $field, string $key = ''): array + { + return $this->connection->column($this, $field, $key); + } + + /** + * 查询SQL组装 union + * @access public + * @param mixed $union UNION + * @param boolean $all 是否适用UNION ALL + * @return $this + */ + public function union($union, bool $all = false) + { + $this->options['union']['type'] = $all ? 'UNION ALL' : 'UNION'; + + if (is_array($union)) { + $this->options['union'] = array_merge($this->options['union'], $union); + } else { + $this->options['union'][] = $union; + } + + return $this; + } + + /** + * 查询SQL组装 union all + * @access public + * @param mixed $union UNION数据 + * @return $this + */ + public function unionAll($union) + { + return $this->union($union, true); + } + + /** + * 指定查询字段 + * @access public + * @param mixed $field 字段信息 + * @return $this + */ + public function field($field) + { + if (empty($field)) { + return $this; + } elseif ($field instanceof Raw) { + $this->options['field'][] = $field; + return $this; + } + + if (is_string($field)) { + if (preg_match('/[\<\'\"\(]/', $field)) { + return $this->fieldRaw($field); + } + + $field = array_map('trim', explode(',', $field)); + } + + if (true === $field) { + // 获取全部字段 + $fields = $this->getTableFields(); + $field = $fields ?: ['*']; + } + + if (isset($this->options['field'])) { + $field = array_merge((array) $this->options['field'], $field); + } + + $this->options['field'] = array_unique($field); + + return $this; + } + + /** + * 指定要排除的查询字段 + * @access public + * @param array|string $field 要排除的字段 + * @return $this + */ + public function withoutField($field) + { + if (empty($field)) { + return $this; + } + + if (is_string($field)) { + $field = array_map('trim', explode(',', $field)); + } + + // 字段排除 + $fields = $this->getTableFields(); + $field = $fields ? array_diff($fields, $field) : $field; + + if (isset($this->options['field'])) { + $field = array_merge((array) $this->options['field'], $field); + } + + $this->options['field'] = array_unique($field); + + return $this; + } + + /** + * 指定其它数据表的查询字段 + * @access public + * @param mixed $field 字段信息 + * @param string $tableName 数据表名 + * @param string $prefix 字段前缀 + * @param string $alias 别名前缀 + * @return $this + */ + public function tableField($field, string $tableName, string $prefix = '', string $alias = '') + { + if (empty($field)) { + return $this; + } + + if (is_string($field)) { + $field = array_map('trim', explode(',', $field)); + } + + if (true === $field) { + // 获取全部字段 + $fields = $this->getTableFields($tableName); + $field = $fields ?: ['*']; + } + + // 添加统一的前缀 + $prefix = $prefix ?: $tableName; + foreach ($field as $key => &$val) { + if (is_numeric($key) && $alias) { + $field[$prefix . '.' . $val] = $alias . $val; + unset($field[$key]); + } elseif (is_numeric($key)) { + $val = $prefix . '.' . $val; + } + } + + if (isset($this->options['field'])) { + $field = array_merge((array) $this->options['field'], $field); + } + + $this->options['field'] = array_unique($field); + + return $this; + } + + /** + * 表达式方式指定查询字段 + * @access public + * @param string $field 字段名 + * @return $this + */ + public function fieldRaw(string $field) + { + $this->options['field'][] = new Raw($field); + + return $this; + } + + /** + * 设置数据 + * @access public + * @param array $data 数据 + * @return $this + */ + public function data(array $data) + { + $this->options['data'] = $data; + + return $this; + } + + /** + * 字段值增长 + * @access public + * @param string $field 字段名 + * @param float $step 增长值 + * @param integer $lazyTime 延时时间(s) + * @param string $op INC/DEC + * @return $this + */ + public function inc(string $field, float $step = 1, int $lazyTime = 0, string $op = 'INC') + { + if ($lazyTime > 0) { + // 延迟写入 + $condition = $this->options['where'] ?? []; + + $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); + $step = $this->connection->lazyWrite($op, $guid, $step, $lazyTime); + + if (false === $step) { + return $this; + } + + $op = 'INC'; + } + + $this->options['data'][$field] = [$op, $step]; + + return $this; + } + + /** + * 字段值减少 + * @access public + * @param string $field 字段名 + * @param float $step 增长值 + * @param integer $lazyTime 延时时间(s) + * @return $this + */ + public function dec(string $field, float $step = 1, int $lazyTime = 0) + { + return $this->inc($field, $step, $lazyTime, 'DEC'); + } + + /** + * 使用表达式设置数据 + * @access public + * @param string $field 字段名 + * @param string $value 字段值 + * @return $this + */ + public function exp(string $field, string $value) + { + $this->options['data'][$field] = new Raw($value); + return $this; + } + + /** + * 去除查询参数 + * @access public + * @param string $option 参数名 留空去除所有参数 + * @return $this + */ + public function removeOption(string $option = '') + { + if ('' === $option) { + $this->options = []; + $this->bind = []; + } elseif (isset($this->options[$option])) { + unset($this->options[$option]); + } + + return $this; + } + + /** + * 指定查询数量 + * @access public + * @param int $offset 起始位置 + * @param int $length 查询数量 + * @return $this + */ + public function limit(int $offset, int $length = null) + { + $this->options['limit'] = $offset . ($length ? ',' . $length : ''); + + return $this; + } + + /** + * 指定分页 + * @access public + * @param int $page 页数 + * @param int $listRows 每页数量 + * @return $this + */ + public function page(int $page, int $listRows = null) + { + $this->options['page'] = [$page, $listRows]; + + return $this; + } + + /** + * 分页查询 + * @access public + * @param int|array $listRows 每页数量 数组表示配置参数 + * @param int|bool $simple 是否简洁模式或者总记录数 + * @return Paginator + * @throws DbException + */ + public function paginate($listRows = null, $simple = false): Paginator + { + if (is_int($simple)) { + $total = $simple; + $simple = false; + } + + $defaultConfig = [ + 'query' => [], //url额外参数 + 'fragment' => '', //url锚点 + 'var_page' => 'page', //分页变量 + 'list_rows' => 15, //每页数量 + ]; + + if (is_array($listRows)) { + $config = array_merge($defaultConfig, $listRows); + $listRows = intval($config['list_rows']); + } else { + $config = $defaultConfig; + $listRows = intval($listRows ?: $config['list_rows']); + } + + $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']); + + $page = $page < 1 ? 1 : $page; + + $config['path'] = $config['path'] ?? Paginator::getCurrentPath(); + + if (!isset($total) && !$simple) { + $options = $this->getOptions(); + + unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']); + + $bind = $this->bind; + $total = $this->count(); + $results = $this->options($options)->bind($bind)->page($page, $listRows)->select(); + } elseif ($simple) { + $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select(); + $total = null; + } else { + $results = $this->page($page, $listRows)->select(); + } + + $this->removeOption('limit'); + $this->removeOption('page'); + + return Paginator::make($results, $listRows, $page, $total, $simple, $config); + } + + /** + * 根据数字类型字段进行分页查询(大数据) + * @access public + * @param int|array $listRows 每页数量或者分页配置 + * @param string $key 分页索引键 + * @param string $sort 索引键排序 asc|desc + * @return Paginator + * @throws DbException + */ + public function paginateX($listRows = null, string $key = null, string $sort = null): Paginator + { + $defaultConfig = [ + 'query' => [], //url额外参数 + 'fragment' => '', //url锚点 + 'var_page' => 'page', //分页变量 + 'list_rows' => 15, //每页数量 + ]; + + $config = is_array($listRows) ? array_merge($defaultConfig, $listRows) : $defaultConfig; + $listRows = is_int($listRows) ? $listRows : (int) $config['list_rows']; + $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']); + $page = $page < 1 ? 1 : $page; + + $config['path'] = $config['path'] ?? Paginator::getCurrentPath(); + + $key = $key ?: $this->getPk(); + $options = $this->getOptions(); + + if (is_null($sort)) { + $order = $options['order'] ?? ''; + if (!empty($order)) { + $sort = $order[$key] ?? 'desc'; + } else { + $this->order($key, 'desc'); + $sort = 'desc'; + } + } else { + $this->order($key, $sort); + } + + $newOption = $options; + unset($newOption['field'], $newOption['page']); + + $data = $this->newQuery() + ->options($newOption) + ->field($key) + ->where(true) + ->order($key, $sort) + ->limit(1) + ->find(); + + $result = $data[$key]; + + if (is_numeric($result)) { + $lastId = 'asc' == $sort ? ($result - 1) + ($page - 1) * $listRows : ($result + 1) - ($page - 1) * $listRows; + } else { + throw new Exception('not support type'); + } + + $results = $this->when($lastId, function ($query) use ($key, $sort, $lastId) { + $query->where($key, 'asc' == $sort ? '>' : '<', $lastId); + }) + ->limit($listRows) + ->select(); + + $this->options($options); + + return Paginator::make($results, $listRows, $page, null, true, $config); + } + + /** + * 根据最后ID查询更多N个数据 + * @access public + * @param int $limit LIMIT + * @param int|string $lastId LastId + * @param string $key 分页索引键 默认为主键 + * @param string $sort 索引键排序 asc|desc + * @return array + * @throws DbException + */ + public function more(int $limit, $lastId = null, string $key = null, string $sort = null): array + { + $key = $key ?: $this->getPk(); + + if (is_null($sort)) { + $order = $this->getOptions('order'); + if (!empty($order)) { + $sort = $order[$key] ?? 'desc'; + } else { + $this->order($key, 'desc'); + $sort = 'desc'; + } + } else { + $this->order($key, $sort); + } + + $result = $this->when($lastId, function ($query) use ($key, $sort, $lastId) { + $query->where($key, 'asc' == $sort ? '>' : '<', $lastId); + })->limit($limit)->select(); + + $last = $result->last(); + + $result->first(); + + return [ + 'data' => $result, + 'lastId' => $last[$key], + ]; + } + + /** + * 表达式方式指定当前操作的数据表 + * @access public + * @param mixed $table 表名 + * @return $this + */ + public function tableRaw(string $table) + { + $this->options['table'] = new Raw($table); + + return $this; + } + + /** + * 指定当前操作的数据表 + * @access public + * @param mixed $table 表名 + * @return $this + */ + public function table($table) + { + if (is_string($table)) { + if (strpos($table, ')')) { + // 子查询 + } elseif (false === strpos($table, ',')) { + if (strpos($table, ' ')) { + list($item, $alias) = explode(' ', $table); + $table = []; + $this->alias([$item => $alias]); + $table[$item] = $alias; + } + } else { + $tables = explode(',', $table); + $table = []; + + foreach ($tables as $item) { + $item = trim($item); + if (strpos($item, ' ')) { + list($item, $alias) = explode(' ', $item); + $this->alias([$item => $alias]); + $table[$item] = $alias; + } else { + $table[] = $item; + } + } + } + } elseif (is_array($table)) { + $tables = $table; + $table = []; + + foreach ($tables as $key => $val) { + if (is_numeric($key)) { + $table[] = $val; + } else { + $this->alias([$key => $val]); + $table[$key] = $val; + } + } + } + + $this->options['table'] = $table; + + return $this; + } + + /** + * USING支持 用于多表删除 + * @access public + * @param mixed $using USING + * @return $this + */ + public function using($using) + { + $this->options['using'] = $using; + return $this; + } + + /** + * 存储过程调用 + * @access public + * @param bool $procedure 是否为存储过程查询 + * @return $this + */ + public function procedure(bool $procedure = true) + { + $this->options['procedure'] = $procedure; + return $this; + } + + /** + * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc']) + * @access public + * @param string|array|Raw $field 排序字段 + * @param string $order 排序 + * @return $this + */ + public function order($field, string $order = '') + { + if (empty($field)) { + return $this; + } elseif ($field instanceof Raw) { + $this->options['order'][] = $field; + return $this; + } + + if (is_string($field)) { + if (!empty($this->options['via'])) { + $field = $this->options['via'] . '.' . $field; + } + if (strpos($field, ',')) { + $field = array_map('trim', explode(',', $field)); + } else { + $field = empty($order) ? $field : [$field => $order]; + } + } elseif (!empty($this->options['via'])) { + foreach ($field as $key => $val) { + if (is_numeric($key)) { + $field[$key] = $this->options['via'] . '.' . $val; + } else { + $field[$this->options['via'] . '.' . $key] = $val; + unset($field[$key]); + } + } + } + + if (!isset($this->options['order'])) { + $this->options['order'] = []; + } + + if (is_array($field)) { + $this->options['order'] = array_merge($this->options['order'], $field); + } else { + $this->options['order'][] = $field; + } + + return $this; + } + + /** + * 表达式方式指定Field排序 + * @access public + * @param string $field 排序字段 + * @param array $bind 参数绑定 + * @return $this + */ + public function orderRaw(string $field, array $bind = []) + { + if (!empty($bind)) { + $this->bindParams($field, $bind); + } + + $this->options['order'][] = new Raw($field); + + return $this; + } + + /** + * 指定Field排序 orderField('id',[1,2,3],'desc') + * @access public + * @param string $field 排序字段 + * @param array $values 排序值 + * @param string $order 排序 desc/asc + * @return $this + */ + public function orderField(string $field, array $values, string $order = '') + { + if (!empty($values)) { + $values['sort'] = $order; + + $this->options['order'][$field] = $values; + } + + return $this; + } + + /** + * 随机排序 + * @access public + * @return $this + */ + public function orderRand() + { + $this->options['order'][] = '[rand]'; + return $this; + } + + /** + * 查询缓存 + * @access public + * @param mixed $key 缓存key + * @param integer|\DateTime $expire 缓存有效期 + * @param string $tag 缓存标签 + * @return $this + */ + public function cache($key = true, $expire = null, string $tag = null) + { + if (false === $key) { + return $this; + } + + if ($key instanceof \DateTimeInterface || $key instanceof \DateInterval || (is_int($key) && is_null($expire))) { + $expire = $key; + $key = true; + } + + $this->options['cache'] = [$key, $expire, $tag]; + + return $this; + } + + /** + * 指定group查询 + * @access public + * @param string|array $group GROUP + * @return $this + */ + public function group($group) + { + $this->options['group'] = $group; + return $this; + } + + /** + * 指定having查询 + * @access public + * @param string $having having + * @return $this + */ + public function having(string $having) + { + $this->options['having'] = $having; + return $this; + } + + /** + * 指定查询lock + * @access public + * @param bool|string $lock 是否lock + * @return $this + */ + public function lock($lock = false) + { + $this->options['lock'] = $lock; + + if ($lock) { + $this->options['master'] = true; + } + + return $this; + } + + /** + * 指定distinct查询 + * @access public + * @param bool $distinct 是否唯一 + * @return $this + */ + public function distinct(bool $distinct = true) + { + $this->options['distinct'] = $distinct; + return $this; + } + + /** + * 指定数据表别名 + * @access public + * @param array|string $alias 数据表别名 + * @return $this + */ + public function alias($alias) + { + if (is_array($alias)) { + $this->options['alias'] = $alias; + } else { + $table = $this->getTable(); + + $this->options['alias'][$table] = $alias; + } + + return $this; + } + + /** + * 指定强制索引 + * @access public + * @param string $force 索引名称 + * @return $this + */ + public function force(string $force) + { + $this->options['force'] = $force; + return $this; + } + + /** + * 查询注释 + * @access public + * @param string $comment 注释 + * @return $this + */ + public function comment(string $comment) + { + $this->options['comment'] = $comment; + return $this; + } + + /** + * 获取执行的SQL语句而不进行实际的查询 + * @access public + * @param bool $fetch 是否返回sql + * @return $this|Fetch + */ + public function fetchSql(bool $fetch = true) + { + $this->options['fetch_sql'] = $fetch; + + if ($fetch) { + return new Fetch($this); + } + + return $this; + } + + /** + * 设置从主服务器读取数据 + * @access public + * @param bool $readMaster 是否从主服务器读取 + * @return $this + */ + public function master(bool $readMaster = true) + { + $this->options['master'] = $readMaster; + return $this; + } + + /** + * 设置是否严格检查字段名 + * @access public + * @param bool $strict 是否严格检查字段 + * @return $this + */ + public function strict(bool $strict = true) + { + $this->options['strict'] = $strict; + return $this; + } + + /** + * 设置自增序列名 + * @access public + * @param string $sequence 自增序列名 + * @return $this + */ + public function sequence(string $sequence = null) + { + $this->options['sequence'] = $sequence; + return $this; + } + + /** + * 设置是否REPLACE + * @access public + * @param bool $replace 是否使用REPLACE写入数据 + * @return $this + */ + public function replace(bool $replace = true) + { + $this->options['replace'] = $replace; + return $this; + } + + /** + * 设置当前查询所在的分区 + * @access public + * @param string|array $partition 分区名称 + * @return $this + */ + public function partition($partition) + { + $this->options['partition'] = $partition; + return $this; + } + + /** + * 设置DUPLICATE + * @access public + * @param array|string|Raw $duplicate DUPLICATE信息 + * @return $this + */ + public function duplicate($duplicate) + { + $this->options['duplicate'] = $duplicate; + return $this; + } + + /** + * 设置查询的额外参数 + * @access public + * @param string $extra 额外信息 + * @return $this + */ + public function extra(string $extra) + { + $this->options['extra'] = $extra; + return $this; + } + + /** + * 设置JSON字段信息 + * @access public + * @param array $json JSON字段 + * @param bool $assoc 是否取出数组 + * @return $this + */ + public function json(array $json = [], bool $assoc = false) + { + $this->options['json'] = $json; + $this->options['json_assoc'] = $assoc; + return $this; + } + + /** + * 指定数据表主键 + * @access public + * @param string $pk 主键 + * @return $this + */ + public function pk(string $pk) + { + $this->pk = $pk; + return $this; + } + + /** + * 获取当前数据表的主键 + * @access public + * @return string|array + */ + public function getPk() + { + if (empty($this->pk)) { + $this->pk = $this->connection->getPk($this->getTable()); + } + + return $this->pk; + } + + /** + * 查询参数批量赋值 + * @access protected + * @param array $options 表达式参数 + * @return $this + */ + protected function options(array $options) + { + $this->options = $options; + return $this; + } + + /** + * 获取当前的查询参数 + * @access public + * @param string $name 参数名 + * @return mixed + */ + public function getOptions(string $name = '') + { + if ('' === $name) { + return $this->options; + } + + return $this->options[$name] ?? null; + } + + /** + * 设置当前的查询参数 + * @access public + * @param string $option 参数名 + * @param mixed $value 参数值 + * @return $this + */ + public function setOption(string $option, $value) + { + $this->options[$option] = $value; + return $this; + } + + /** + * 设置当前字段添加的表别名 + * @access public + * @param string $via 临时表别名 + * @return $this + */ + public function via(string $via = '') + { + $this->options['via'] = $via; + + return $this; + } + + /** + * 保存记录 自动判断insert或者update + * @access public + * @param array $data 数据 + * @param bool $forceInsert 是否强制insert + * @return integer + */ + public function save(array $data = [], bool $forceInsert = false) + { + if ($forceInsert) { + return $this->insert($data); + } + + $this->options['data'] = array_merge($this->options['data'] ?? [], $data); + + if (!empty($this->options['where'])) { + $isUpdate = true; + } else { + $isUpdate = $this->parseUpdateData($this->options['data']); + } + + return $isUpdate ? $this->update() : $this->insert(); + } + + /** + * 插入记录 + * @access public + * @param array $data 数据 + * @param boolean $getLastInsID 返回自增主键 + * @return integer|string + */ + public function insert(array $data = [], bool $getLastInsID = false) + { + if (!empty($data)) { + $this->options['data'] = $data; + } + + return $this->connection->insert($this, $getLastInsID); + } + + /** + * 插入记录并获取自增ID + * @access public + * @param array $data 数据 + * @return integer|string + */ + public function insertGetId(array $data) + { + return $this->insert($data, true); + } + + /** + * 批量插入记录 + * @access public + * @param array $dataSet 数据集 + * @param integer $limit 每次写入数据限制 + * @return integer + */ + public function insertAll(array $dataSet = [], int $limit = 0): int + { + if (empty($dataSet)) { + $dataSet = $this->options['data'] ?? []; + } + + if (empty($limit) && !empty($this->options['limit']) && is_numeric($this->options['limit'])) { + $limit = (int) $this->options['limit']; + } + + return $this->connection->insertAll($this, $dataSet, $limit); + } + + /** + * 通过Select方式插入记录 + * @access public + * @param array $fields 要插入的数据表字段名 + * @param string $table 要插入的数据表名 + * @return integer + * @throws PDOException + */ + public function selectInsert(array $fields, string $table): int + { + return $this->connection->selectInsert($this, $fields, $table); + } + + /** + * 更新记录 + * @access public + * @param mixed $data 数据 + * @return integer + * @throws Exception + * @throws PDOException + */ + public function update(array $data = []): int + { + if (!empty($data)) { + $this->options['data'] = array_merge($this->options['data'] ?? [], $data); + } + + if (empty($this->options['where'])) { + $this->parseUpdateData($this->options['data']); + } + + if (empty($this->options['where']) && $this->model) { + $this->where($this->model->getWhere()); + } + + if (empty($this->options['where'])) { + // 如果没有任何更新条件则不执行 + throw new Exception('miss update condition'); + } + + return $this->connection->update($this); + } + + /** + * 删除记录 + * @access public + * @param mixed $data 表达式 true 表示强制删除 + * @return int + * @throws Exception + * @throws PDOException + */ + public function delete($data = null): int + { + if (!is_null($data) && true !== $data) { + // AR模式分析主键条件 + $this->parsePkWhere($data); + } + + if (empty($this->options['where']) && $this->model) { + $this->where($this->model->getWhere()); + } + + if (true !== $data && empty($this->options['where'])) { + // 如果条件为空 不进行删除操作 除非设置 1=1 + throw new Exception('delete without condition'); + } + + if (!empty($this->options['soft_delete'])) { + // 软删除 + list($field, $condition) = $this->options['soft_delete']; + if ($condition) { + unset($this->options['soft_delete']); + $this->options['data'] = [$field => $condition]; + + return $this->connection->update($this); + } + } + + $this->options['data'] = $data; + + return $this->connection->delete($this); + } + + /** + * 查找记录 + * @access public + * @param mixed $data 数据 + * @return Collection + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function select($data = null): Collection + { + if (!is_null($data)) { + // 主键条件分析 + $this->parsePkWhere($data); + } + + $resultSet = $this->connection->select($this); + + // 返回结果处理 + if (!empty($this->options['fail']) && count($resultSet) == 0) { + $this->throwNotFound(); + } + + // 数据列表读取后的处理 + if (!empty($this->model)) { + // 生成模型对象 + $resultSet = $this->resultSetToModelCollection($resultSet); + } else { + $this->resultSet($resultSet); + } + + return $resultSet; + } + + /** + * 查找单条记录 + * @access public + * @param mixed $data 查询数据 + * @return array|Model|null + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function find($data = null) + { + if (!is_null($data)) { + // AR模式分析主键条件 + $this->parsePkWhere($data); + } + + if (empty($this->options['where'])) { + $result = []; + } else { + $result = $this->connection->find($this); + } + + // 数据处理 + if (empty($result)) { + return $this->resultToEmpty(); + } + + if (!empty($this->model)) { + // 返回模型对象 + $this->resultToModel($result, $this->options); + } else { + $this->result($result); + } + + return $result; + } + + /** + * 分批数据返回处理 + * @access public + * @param integer $count 每次处理的数据数量 + * @param callable $callback 处理回调方法 + * @param string|array $column 分批处理的字段名 + * @param string $order 字段排序 + * @return bool + * @throws DbException + */ + public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool + { + $options = $this->getOptions(); + $column = $column ?: $this->getPk(); + + if (isset($options['order'])) { + unset($options['order']); + } + + $bind = $this->bind; + + if (is_array($column)) { + $times = 1; + $query = $this->options($options)->page($times, $count); + } else { + $query = $this->options($options)->limit($count); + + if (strpos($column, '.')) { + list($alias, $key) = explode('.', $column); + } else { + $key = $column; + } + } + + $resultSet = $query->order($column, $order)->select(); + + while (count($resultSet) > 0) { + if (false === call_user_func($callback, $resultSet)) { + return false; + } + + if (isset($times)) { + $times++; + $query = $this->options($options)->page($times, $count); + } else { + $end = $resultSet->pop(); + $lastId = is_array($end) ? $end[$key] : $end->getData($key); + + $query = $this->options($options) + ->limit($count) + ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId); + } + + $resultSet = $query->bind($bind)->order($column, $order)->select(); + } + + return true; + } + + /** + * 创建子查询SQL + * @access public + * @param bool $sub 是否添加括号 + * @return string + * @throws DbException + */ + public function buildSql(bool $sub = true): string + { + return $sub ? '( ' . $this->fetchSql()->select() . ' )' : $this->fetchSql()->select(); + } + + /** + * 分析表达式(可用于查询或者写入操作) + * @access public + * @return array + */ + public function parseOptions(): array + { + $options = $this->getOptions(); + + // 获取数据表 + if (empty($options['table'])) { + $options['table'] = $this->getTable(); + } + + if (!isset($options['where'])) { + $options['where'] = []; + } elseif (isset($options['view'])) { + // 视图查询条件处理 + $this->parseView($options); + } + + if (!isset($options['field'])) { + $options['field'] = '*'; + } + + foreach (['data', 'order', 'join', 'union'] as $name) { + if (!isset($options[$name])) { + $options[$name] = []; + } + } + + if (!isset($options['strict'])) { + $options['strict'] = $this->connection->getConfig('fields_strict'); + } + + foreach (['master', 'lock', 'fetch_sql', 'array', 'distinct', 'procedure'] as $name) { + if (!isset($options[$name])) { + $options[$name] = false; + } + } + + foreach (['group', 'having', 'limit', 'force', 'comment', 'partition', 'duplicate', 'extra'] as $name) { + if (!isset($options[$name])) { + $options[$name] = ''; + } + } + + if (isset($options['page'])) { + // 根据页数计算limit + list($page, $listRows) = $options['page']; + $page = $page > 0 ? $page : 1; + $listRows = $listRows ?: (is_numeric($options['limit']) ? $options['limit'] : 20); + $offset = $listRows * ($page - 1); + $options['limit'] = $offset . ',' . $listRows; + } + + $this->options = $options; + + return $options; + } + + /** + * 分析数据是否存在更新条件 + * @access public + * @param array $data 数据 + * @return bool + * @throws Exception + */ + public function parseUpdateData(&$data): bool + { + $pk = $this->getPk(); + $isUpdate = false; + // 如果存在主键数据 则自动作为更新条件 + if (is_string($pk) && isset($data[$pk])) { + $this->where($pk, '=', $data[$pk]); + $this->options['key'] = $data[$pk]; + unset($data[$pk]); + $isUpdate = true; + } elseif (is_array($pk)) { + foreach ($pk as $field) { + if (isset($data[$field])) { + $this->where($field, '=', $data[$field]); + $isUpdate = true; + } else { + // 如果缺少复合主键数据则不执行 + throw new Exception('miss complex primary data'); + } + unset($data[$field]); + } + } + + return $isUpdate; + } + + /** + * 把主键值转换为查询条件 支持复合主键 + * @access public + * @param array|string $data 主键数据 + * @return void + * @throws Exception + */ + public function parsePkWhere($data): void + { + $pk = $this->getPk(); + + if (is_string($pk)) { + // 获取数据表 + if (empty($this->options['table'])) { + $this->options['table'] = $this->getTable(); + } + + $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table']; + + if (!empty($this->options['alias'][$table])) { + $alias = $this->options['alias'][$table]; + } + + $key = isset($alias) ? $alias . '.' . $pk : $pk; + // 根据主键查询 + if (is_array($data)) { + $this->where($key, 'in', $data); + } else { + $this->where($key, '=', $data); + $this->options['key'] = $data; + } + } + } + + /** + * 获取模型的更新条件 + * @access protected + * @param array $options 查询参数 + */ + protected function getModelUpdateCondition(array $options) + { + return $options['where']['AND'] ?? null; + } +} diff --git a/src/db/Builder.php b/src/db/Builder.php index c0fb8de..b3aa1d1 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -83,7 +83,7 @@ abstract class Builder /** * 架构函数 * @access public - * @param Connection $connection 数据库连接对象实例 + * @param Connection $connection 数据库连接对象实例 */ public function __construct(Connection $connection) { @@ -359,11 +359,14 @@ abstract class Builder throw new Exception('where express error:' . var_export($value, true)); } $field = array_shift($value); - } elseif (!($value instanceof \Closure)) { + } elseif (true === $value) { + $where[] = ' ' . $logic . ' 1 '; + continue; + } elseif (!($value instanceof Closure)) { throw new Exception('where express error:' . var_export($value, true)); } - if ($value instanceof \Closure) { + if ($value instanceof Closure) { // 使用闭包查询 $where[] = $this->parseClousreWhere($query, $value, $logic); } elseif (is_array($field)) { @@ -495,7 +498,7 @@ abstract class Builder $exp = $this->exp[$exp]; } - if (is_string($field)) { + if (is_string($field) && 'LIKE' != $exp) { $bindType = $binds[$field] ?? PDO::PARAM_STR; } else { $bindType = PDO::PARAM_STR; @@ -544,7 +547,7 @@ abstract class Builder if (is_array($value)) { $array = []; foreach ($value as $item) { - $name = $query->bindValue($item, $bindType); + $name = $query->bindValue($item, PDO::PARAM_STR); $array[] = $key . ' ' . $exp . ' :' . $name; } @@ -649,7 +652,7 @@ abstract class Builder protected function parseExists(Query $query, string $key, string $exp, $value, string $field, int $bindType): string { // EXISTS 查询 - if ($value instanceof \Closure) { + if ($value instanceof Closure) { $value = $this->parseClosure($query, $value, false); } elseif ($value instanceof Raw) { $value = $value->getValue(); @@ -694,10 +697,14 @@ abstract class Builder } // 比较运算 - if ($value instanceof \Closure) { + if ($value instanceof Closure) { $value = $this->parseClosure($query, $value); } + if ('=' == $exp && is_null($value)) { + return $key . ' IS NULL'; + } + return $key . ' ' . $exp . ' ' . $value; } @@ -739,7 +746,7 @@ abstract class Builder protected function parseIn(Query $query, string $key, string $exp, $value, $field, int $bindType): string { // IN 查询 - if ($value instanceof \Closure) { + if ($value instanceof Closure) { $value = $this->parseClosure($query, $value, false); } elseif ($value instanceof Raw) { $value = $value->getValue(); @@ -752,8 +759,12 @@ abstract class Builder $array[] = ':' . $name; } - $zone = implode(',', $array); - $value = empty($zone) ? "''" : $zone; + if (count($array) == 1) { + return $key . ('IN' == $exp ? ' = ' : ' <> ') . $array[0]; + } else { + $zone = implode(',', $array); + $value = empty($zone) ? "''" : $zone; + } } return $key . ' ' . $exp . ' (' . $value . ')'; @@ -767,9 +778,9 @@ abstract class Builder * @param bool $show * @return string */ - protected function parseClosure(Query $query, \Closure $call, bool $show = true): string + protected function parseClosure(Query $query, Closure $call, bool $show = true): string { - $newQuery = $query->newQuery()->setConnection($this->connection); + $newQuery = $query->newQuery()->removeOption(); $call($newQuery); return $newQuery->buildSql($show); @@ -1022,7 +1033,7 @@ abstract class Builder unset($union['type']); foreach ($union as $u) { - if ($u instanceof \Closure) { + if ($u instanceof Closure) { $sql[] = $type . ' ' . $this->parseClosure($query, $u); } elseif (is_string($u)) { $sql[] = $type . ' ( ' . $u . ' )'; diff --git a/src/db/Connection.php b/src/db/Connection.php index 5ef27da..57844d2 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -12,13 +12,13 @@ declare (strict_types = 1); namespace think\db; -use PDO; -use PDOStatement; use think\Cache; use think\cache\CacheItem; use think\Db; -use think\db\exception\BindParamException; +use think\db\exception\DataNotFoundException; +use think\db\exception\ModelNotFoundException; use think\Exception; +use think\exception\DbException; use think\exception\PDOException; use think\Log; @@ -27,13 +27,6 @@ use think\Log; */ abstract class Connection { - const PARAM_FLOAT = 21; - - /** - * PDO操作实例 - * @var PDOStatement - */ - protected $PDOStatement; /** * 当前SQL指令 @@ -61,40 +54,28 @@ abstract class Connection /** * 数据库连接ID 支持多个连接 - * @var PDO[] + * @var array */ protected $links = []; /** * 当前连接ID - * @var PDO + * @var object */ protected $linkID; /** * 当前读连接ID - * @var PDO + * @var object */ protected $linkRead; /** * 当前写连接ID - * @var PDO + * @var object */ protected $linkWrite; - /** - * 查询结果类型 - * @var int - */ - protected $fetchType = PDO::FETCH_ASSOC; - - /** - * 字段属性大小写 - * @var int - */ - protected $attrCase = PDO::CASE_LOWER; - /** * 数据表信息 * @var array @@ -129,108 +110,7 @@ abstract class Connection * 数据库连接参数配置 * @var array */ - protected $config = [ - // 数据库类型 - 'type' => '', - // 服务器地址 - 'hostname' => '', - // 数据库名 - 'database' => '', - // 用户名 - 'username' => '', - // 密码 - 'password' => '', - // 端口 - 'hostport' => '', - // 连接dsn - 'dsn' => '', - // 数据库连接参数 - 'params' => [], - // 数据库编码默认采用utf8 - 'charset' => 'utf8', - // 数据库表前缀 - 'prefix' => '', - // 数据库调试模式 - 'debug' => false, - // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, - // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, - // 读写分离后 主服务器数量 - 'master_num' => 1, - // 指定从服务器序号 - 'slave_no' => '', - // 模型写入后自动读取主服务器 - 'read_master' => false, - // 是否严格检查字段是否存在 - 'fields_strict' => true, - // 自动写入时间戳字段 - 'auto_timestamp' => false, - // 时间字段取出后的默认时间格式 - 'datetime_format' => 'Y-m-d H:i:s', - // 是否需要进行SQL性能分析 - 'sql_explain' => false, - // Builder类 - 'builder' => '', - // Query类 - 'query' => '', - // 是否需要断线重连 - 'break_reconnect' => false, - // 断线标识字符串 - 'break_match_str' => [], - // 字段缓存目录 - 'schema_path' => '', - ]; - - /** - * PDO连接参数 - * @var array - */ - protected $params = [ - PDO::ATTR_CASE => PDO::CASE_NATURAL, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, - PDO::ATTR_STRINGIFY_FETCHES => false, - PDO::ATTR_EMULATE_PREPARES => false, - ]; - - /** - * 参数绑定类型映射 - * @var array - */ - protected $bindType = [ - 'string' => PDO::PARAM_STR, - 'str' => PDO::PARAM_STR, - 'integer' => PDO::PARAM_INT, - 'int' => PDO::PARAM_INT, - 'boolean' => PDO::PARAM_BOOL, - 'bool' => PDO::PARAM_BOOL, - 'float' => self::PARAM_FLOAT, - ]; - - /** - * 服务器断线标识字符 - * @var array - */ - protected $breakMatchStr = [ - 'server has gone away', - 'no connection to the server', - 'Lost connection', - 'is dead or not enabled', - 'Error while sending', - 'decryption failed or bad record mac', - 'server closed the connection unexpectedly', - 'SSL connection has been closed unexpectedly', - 'Error writing data to the connection', - 'Resource deadlock avoided', - 'failed with errno', - ]; - - /** - * 绑定参数 - * @var array - */ - protected $bind = []; + protected $config = []; /** * 缓存对象 @@ -239,18 +119,19 @@ abstract class Connection protected $cache; /** - * 日志记录 - * @var array + * 日志对象 + * @var Log */ - protected $log = []; + protected $log; /** * 架构函数 读取数据库配置信息 * @access public * @param Cache $cache 缓存对象 + * @param Log $log 日志对象 * @param array $config 数据库配置数组 */ - public function __construct(Cache $cache, array $config = []) + public function __construct(Cache $cache, Log $log, array $config = []) { if (!empty($config)) { $this->config = array_merge($this->config, $config); @@ -261,6 +142,7 @@ abstract class Connection $this->builder = new $class($this); $this->cache = $cache; + $this->log = $log; // 执行初始化操作 $this->initialize(); @@ -271,7 +153,7 @@ abstract class Connection * @access protected * @return void */ - protected function initialize(): void + protected function initialize() { } @@ -280,20 +162,14 @@ abstract class Connection * @access public * @return string */ - public function getQueryClass(): string - { - return $this->getConfig('query') ?: Query::class; - } + abstract public function getQueryClass(): string; /** * 获取当前连接器类对应的Builder类 * @access public * @return string */ - public function getBuilderClass(): string - { - return $this->getConfig('builder') ?: '\\think\\db\\builder\\' . ucfirst($this->getConfig('type')); - } + abstract public function getBuilderClass(): string; /** * 设置当前的数据库Builder对象 @@ -301,7 +177,7 @@ abstract class Connection * @param Builder $builder * @return void */ - protected function setBuilder(Builder $builder): void + protected function setBuilder($builder) { $this->builder = $builder; } @@ -311,7 +187,7 @@ abstract class Connection * @access public * @return Builder */ - public function getBuilder(): Builder + public function getBuilder() { return $this->builder; } @@ -322,1545 +198,248 @@ abstract class Connection * @param Db $db * @return void */ - public function setDb(Db $db): void + public function setDb(Db $db) { $this->db = $db; } /** - * 解析pdo连接的dsn信息 - * @access protected - * @param array $config 连接信息 - * @return string + * 获取数据库的配置参数 + * @access public + * @param string $config 配置名称 + * @return mixed */ - abstract protected function parseDsn(array $config); + public function getConfig(string $config = '') + { + if ('' === $config) { + return $this->config; + } + return $this->config[$config] ?? null; + } /** - * 取得数据表的字段信息 + * 设置数据库的配置参数 * @access public - * @param string $tableName 数据表名称 - * @return array + * @param array $config 配置 + * @return void */ - abstract public function getFields(string $tableName); + public function setConfig(array $config) + { + $this->config = array_merge($this->config, $config); + } /** - * 取得数据库的表信息 + * 连接数据库方法 * @access public - * @param string $dbName 数据库名称 - * @return array + * @param array $config 接参数 + * @param integer $linkNum 连接序号 + * @return mixed + * @throws Exception */ - abstract public function getTables(string $dbName); + abstract public function connect(array $config = [], $linkNum = 0); /** - * SQL性能分析 - * @access protected - * @param string $sql SQL语句 - * @return array + * 释放查询结果 + * @access public */ - abstract protected function getExplain(string $sql); + abstract public function free(); /** - * 对返数据表字段信息进行大小写转换出来 + * 查找单条记录 * @access public - * @param array $info 字段信息 + * @param BaseQuery $query 查询对象 * @return array + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException */ - public function fieldCase(array $info): array - { - // 字段大小写转换 - switch ($this->attrCase) { - case PDO::CASE_LOWER: - $info = array_change_key_case($info); - break; - case PDO::CASE_UPPER: - $info = array_change_key_case($info, CASE_UPPER); - break; - case PDO::CASE_NATURAL: - default: - // 不做转换 - } - - return $info; - } + abstract public function find(BaseQuery $query): array; /** - * 获取字段绑定类型 + * 使用游标查询记录 * @access public - * @param string $type 字段类型 - * @return integer + * @param BaseQuery $query 查询对象 + * @return \Generator */ - public function getFieldBindType(string $type): int - { - if (in_array($type, ['integer', 'string', 'float', 'boolean', 'bool', 'int', 'str'])) { - $bind = $this->bindType[$type]; - } elseif (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) { - $bind = PDO::PARAM_STR; - } elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) { - $bind = self::PARAM_FLOAT; - } elseif (preg_match('/(int|serial|bit)/is', $type)) { - $bind = PDO::PARAM_INT; - } elseif (preg_match('/bool/is', $type)) { - $bind = PDO::PARAM_BOOL; - } else { - $bind = PDO::PARAM_STR; - } + abstract public function cursor(BaseQuery $query); - return $bind; - } + /** + * 查找记录 + * @access public + * @param BaseQuery $query 查询对象 + * @return array + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + abstract public function select(BaseQuery $query): array; /** - * 获取数据表信息 + * 插入记录 * @access public - * @param mixed $tableName 数据表名 留空自动获取 - * @param string $fetch 获取信息类型 包括 fields type bind pk + * @param BaseQuery $query 查询对象 + * @param boolean $getLastInsID 返回自增主键 * @return mixed */ - public function getTableInfo($tableName, string $fetch = '') - { - if (is_array($tableName)) { - $tableName = key($tableName) ?: current($tableName); - } - - if (strpos($tableName, ',')) { - // 多表不获取字段信息 - return []; - } - - // 修正子查询作为表名的问题 - if (strpos($tableName, ')')) { - return []; - } - - list($tableName) = explode(' ', $tableName); - - if (!strpos($tableName, '.')) { - $schema = $this->getConfig('database') . '.' . $tableName; - } else { - $schema = $tableName; - } - - if (!isset($this->info[$schema])) { - // 读取缓存 - $cacheFile = $this->config['schema_path'] . $schema . '.php'; - - if (!$this->config['debug'] && is_file($cacheFile)) { - $info = include $cacheFile; - } else { - $info = $this->getFields($tableName); - } - - $fields = array_keys($info); - $bind = $type = []; - - foreach ($info as $key => $val) { - // 记录字段类型 - $type[$key] = $val['type']; - $bind[$key] = $this->getFieldBindType($val['type']); - - if (!empty($val['primary'])) { - $pk[] = $key; - } - } - - if (isset($pk)) { - // 设置主键 - $pk = count($pk) > 1 ? $pk : $pk[0]; - } else { - $pk = null; - } - - $this->info[$schema] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk]; - } - - return $fetch ? $this->info[$schema][$fetch] : $this->info[$schema]; - } + abstract public function insert(BaseQuery $query, bool $getLastInsID = false); /** - * 获取数据表的主键 + * 批量插入记录 * @access public - * @param mixed $tableName 数据表名 - * @return string|array + * @param BaseQuery $query 查询对象 + * @param mixed $dataSet 数据集 + * @return integer + * @throws \Exception + * @throws \Throwable */ - public function getPk($tableName) - { - return $this->getTableInfo($tableName, 'pk'); - } + abstract public function insertAll(BaseQuery $query, array $dataSet = []): int; /** - * 获取数据表字段信息 + * 更新记录 * @access public - * @param mixed $tableName 数据表名 - * @return array + * @param BaseQuery $query 查询对象 + * @return integer + * @throws Exception + * @throws PDOException */ - public function getTableFields($tableName): array - { - return $this->getTableInfo($tableName, 'fields'); - } + abstract public function update(BaseQuery $query): int; /** - * 获取数据表字段类型 + * 删除记录 * @access public - * @param mixed $tableName 数据表名 - * @param string $field 字段名 - * @return array|string + * @param BaseQuery $query 查询对象 + * @return int + * @throws Exception + * @throws PDOException */ - public function getFieldsType($tableName, string $field = null) - { - $result = $this->getTableInfo($tableName, 'type'); - - if ($field && isset($result[$field])) { - return $result[$field]; - } + abstract public function delete(BaseQuery $query): int; - return $result; - } + /** + * 得到某个字段的值 + * @access public + * @param BaseQuery $query 查询对象 + * @param string $field 字段名 + * @param mixed $default 默认值 + * @param bool $one 返回一个值 + * @return mixed + */ + abstract public function value(BaseQuery $query, string $field, $default = null); /** - * 获取数据表绑定信息 + * 得到某个列的数组 * @access public - * @param mixed $tableName 数据表名 + * @param BaseQuery $query 查询对象 + * @param string $column 字段名 多个字段用逗号分隔 + * @param string $key 索引 * @return array */ - public function getFieldsBind($tableName): array - { - return $this->getTableInfo($tableName, 'bind'); - } + abstract public function column(BaseQuery $query, string $column, string $key = ''): array; /** - * 获取数据库的配置参数 + * 执行数据库事务 * @access public - * @param string $config 配置名称 + * @param callable $callback 数据操作方法回调 * @return mixed + * @throws PDOException + * @throws \Exception + * @throws \Throwable */ - public function getConfig(string $config = '') - { - if ('' === $config) { - return $this->config; - } - return $this->config[$config] ?? null; - } + abstract public function transaction(callable $callback); /** - * 设置数据库的配置参数 + * 启动事务 * @access public - * @param array $config 配置 * @return void + * @throws \PDOException + * @throws \Exception */ - public function setConfig(array $config): void - { - $this->config = array_merge($this->config, $config); - } + abstract public function startTrans(); /** - * 连接数据库方法 + * 用于非自动提交状态下面的查询提交 * @access public - * @param array $config 连接参数 - * @param integer $linkNum 连接序号 - * @param array|bool $autoConnection 是否自动连接主数据库(用于分布式) - * @return PDO - * @throws Exception + * @return void + * @throws PDOException */ - public function connect(array $config = [], $linkNum = 0, $autoConnection = false): PDO - { - if (isset($this->links[$linkNum])) { - return $this->links[$linkNum]; - } - - if (empty($config)) { - $config = $this->config; - } else { - $config = array_merge($this->config, $config); - } - - // 连接参数 - if (isset($config['params']) && is_array($config['params'])) { - $params = $config['params'] + $this->params; - } else { - $params = $this->params; - } - - // 记录当前字段属性大小写设置 - $this->attrCase = $params[PDO::ATTR_CASE]; - - if (!empty($config['break_match_str'])) { - $this->breakMatchStr = array_merge($this->breakMatchStr, (array) $config['break_match_str']); - } - - try { - if (empty($config['dsn'])) { - $config['dsn'] = $this->parseDsn($config); - } - - if ($config['debug']) { - $startTime = microtime(true); - $this->links[$linkNum] = $this->createPdo($config['dsn'], $config['username'], $config['password'], $params); - // 记录数据库连接信息 - $this->log('[ DB ] CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); - } else { - $this->links[$linkNum] = $this->createPdo($config['dsn'], $config['username'], $config['password'], $params); - } - - return $this->links[$linkNum]; - } catch (\PDOException $e) { - if ($autoConnection) { - $this->log($e->getMessage()); - return $this->connect($autoConnection, $linkNum); - } else { - throw $e; - } - } - } + abstract public function commit(); /** - * 创建PDO实例 - * @param $dsn - * @param $username - * @param $password - * @param $params - * @return PDO + * 事务回滚 + * @access public + * @return void + * @throws PDOException */ - protected function createPdo($dsn, $username, $password, $params) - { - return new PDO($dsn, $username, $password, $params); - } + abstract public function rollback(); /** - * 释放查询结果 + * 关闭数据库(或者重新连接) * @access public + * @return $this */ - public function free(): void - { - $this->PDOStatement = null; - } + abstract public function close(); /** - * 获取PDO对象 + * 获取最近一次查询的sql语句 * @access public - * @return \PDO|false + * @return string */ - public function getPdo() - { - if (!$this->linkID) { - return false; - } - - return $this->linkID; - } + abstract public function getLastSql(): string; /** - * 执行查询 使用生成器返回数据 + * 获取最近插入的ID * @access public - * @param Query $query 查询对象 - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param \think\Model $model 模型对象实例 - * @param array $condition 查询条件 - * @return \Generator + * @param BaseQuery $query 查询对象 + * @param string $sequence 自增序列名 + * @return mixed */ - public function getCursor(Query $query, string $sql, array $bind = [], $model = null, $condition = null) - { - $this->queryPDOStatement($query, $sql, $bind); + abstract public function getLastInsID(BaseQuery $query, string $sequence = null); - // 返回结果集 - while ($result = $this->PDOStatement->fetch($this->fetchType)) { - if ($model) { - yield $model->newInstance($result, $condition)->setQuery($query); - } else { - yield $result; - } - } - } + /** + * 初始化数据库连接 + * @access protected + * @param boolean $master 是否主服务器 + * @return void + */ + abstract protected function initConnect(bool $master = true); /** - * 执行查询 返回数据集 - * @access public - * @param Query $query 查询对象 - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param bool $cache 是否支持缓存 - * @return array - * @throws BindParamException - * @throws \PDOException - * @throws \Exception - * @throws \Throwable + * 记录SQL日志 + * @access protected + * @param string $log SQL日志信息 + * @param string $type 日志类型 + * @return void */ - public function query(Query $query, string $sql, array $bind = [], bool $cache = false): array + protected function log($log, $type = 'sql') { - // 分析查询表达式 - $options = $query->parseOptions(); - - if ($cache && !empty($options['cache'])) { - $cacheItem = $this->parseCache($query, $options['cache']); - $resultSet = $this->cache->get($cacheItem->getKey()); - - if (false !== $resultSet) { - return $resultSet; - } - } - - $master = !empty($options['master']) ? true : false; - $procedure = !empty($options['procedure']) ? true : in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); - - $this->getPDOStatement($sql, $bind, $master, $procedure); - - $resultSet = $this->getResult($procedure); - - if (isset($cacheItem) && $resultSet) { - // 缓存数据集 - $cacheItem->set($resultSet); - $this->cacheData($cacheItem); + if ($this->config['debug']) { + $this->log->record($log, $type); } - - return $resultSet; } /** - * 执行查询但只返回PDOStatement对象 - * @access public - * @param Query $query 查询对象 - * @return \PDOStatement + * 缓存数据 + * @access protected + * @param CacheItem $cacheItem 缓存Item */ - public function pdo(Query $query): PDOStatement + protected function cacheData(CacheItem $cacheItem) { - $bind = $query->getBind(); - // 生成查询SQL - $sql = $this->builder->select($query); + if ($cacheItem->getTag()) { + $this->cache->tag($cacheItem->getTag()); + } - return $this->queryPDOStatement($query, $sql, $bind); + $this->cache->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire()); } /** - * 执行查询但只返回PDOStatement对象 - * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param bool $master 是否在主服务器读操作 - * @param bool $procedure 是否为存储过程调用 - * @return PDOStatement - * @throws BindParamException - * @throws \PDOException - * @throws \Exception - * @throws \Throwable + * 分析缓存 + * @access protected + * @param BaseQuery $query 查询对象 + * @param array $cache 缓存信息 + * @return CacheItem */ - public function getPDOStatement(string $sql, array $bind = [], bool $master = false, bool $procedure = false): PDOStatement + protected function parseCache(BaseQuery $query, array $cache): CacheItem { - $this->initConnect($this->readMaster ?: $master); - - // 记录SQL语句 - $this->queryStr = $sql; - - $this->bind = $bind; - - $this->db->updateQueryTimes(); - - try { - // 调试开始 - $this->debug(true); - - // 预处理 - $this->PDOStatement = $this->linkID->prepare($sql); - - // 参数绑定 - if ($procedure) { - $this->bindParam($bind); - } else { - $this->bindValue($bind); - } - - // 执行查询 - $this->PDOStatement->execute(); - - // 调试结束 - $this->debug(false, '', $master); - - return $this->PDOStatement; - } catch (\Throwable | \Exception $e) { - if ($this->isBreak($e)) { - return $this->close()->getPDOStatement($sql, $bind, $master, $procedure); - } - - if ($e instanceof \PDOException) { - throw new PDOException($e, $this->config, $this->getLastsql()); - } else { - throw $e; - } - } - } - - /** - * 执行语句 - * @access public - * @param Query $query 查询对象 - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param bool $origin 是否原生查询 - * @return int - * @throws BindParamException - * @throws \PDOException - * @throws \Exception - * @throws \Throwable - */ - public function execute(Query $query, string $sql, array $bind = [], bool $origin = false): int - { - $this->queryPDOStatement($query->master(true), $sql, $bind); - - if (!$origin && !empty($this->config['deploy']) && !empty($this->config['read_master'])) { - $this->readMaster = true; - } - - $this->numRows = $this->PDOStatement->rowCount(); - - return $this->numRows; - } - - protected function queryPDOStatement(Query $query, string $sql, array $bind = []): PDOStatement - { - $options = $query->getOptions(); - $master = !empty($options['master']) ? true : false; - $procedure = !empty($options['procedure']) ? true : in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); - - return $this->getPDOStatement($sql, $bind, $master, $procedure); - } - - /** - * 查找单条记录 - * @access public - * @param Query $query 查询对象 - * @return array - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException - */ - public function find(Query $query): array - { - // 分析查询表达式 - $options = $query->parseOptions(); - - if (!empty($options['cache'])) { - // 判断查询缓存 - $cacheItem = $this->parseCache($query, $options['cache']); - $key = $cacheItem->getKey(); - } - - if (isset($key)) { - $result = $this->cache->get($key); - - if (false !== $result) { - return $result; - } - } - - // 生成查询SQL - $sql = $this->builder->select($query, true); - - // 事件回调 - $result = $this->db->trigger('before_find', $query); - - if (!$result) { - // 执行查询 - $resultSet = $this->query($query, $sql, $query->getBind()); - - $result = $resultSet[0] ?? []; - } - - if (isset($cacheItem) && $result) { - // 缓存数据 - $cacheItem->set($result); - $this->cacheData($cacheItem); - } - - return $result; - } - - /** - * 使用游标查询记录 - * @access public - * @param Query $query 查询对象 - * @return \Generator - */ - public function cursor(Query $query) - { - // 分析查询表达式 - $options = $query->parseOptions(); - - // 生成查询SQL - $sql = $this->builder->select($query); - - $condition = $options['where']['AND'] ?? null; - - // 执行查询操作 - return $this->getCursor($query, $sql, $query->getBind(), $query->getModel(), $condition); - } - - /** - * 查找记录 - * @access public - * @param Query $query 查询对象 - * @return array - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException - */ - public function select(Query $query): array - { - // 分析查询表达式 - $options = $query->parseOptions(); - - if (!empty($options['cache'])) { - $cacheItem = $this->parseCache($query, $options['cache']); - $resultSet = $this->getCacheData($cacheItem); - - if (false !== $resultSet) { - return $resultSet; - } - } - - // 生成查询SQL - $sql = $this->builder->select($query); - - $resultSet = $this->db->trigger('before_select', $query); - - if (!$resultSet) { - // 执行查询操作 - $resultSet = $this->query($query, $sql, $query->getBind()); - } - - if (isset($cacheItem) && false !== $resultSet) { - // 缓存数据集 - $cacheItem->set($resultSet); - $this->cacheData($cacheItem); - } - - return $resultSet; - } - - /** - * 插入记录 - * @access public - * @param Query $query 查询对象 - * @param boolean $getLastInsID 返回自增主键 - * @return mixed - */ - public function insert(Query $query, bool $getLastInsID = false) - { - // 分析查询表达式 - $options = $query->parseOptions(); - - // 生成SQL语句 - $sql = $this->builder->insert($query); - - // 执行操作 - $result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind()); - - if ($result) { - $sequence = $options['sequence'] ?? null; - $lastInsId = $query->getLastInsID($sequence); - - $data = $options['data']; - - if ($lastInsId) { - $pk = $query->getPk(); - if (is_string($pk)) { - $data[$pk] = $lastInsId; - } - } - - $query->setOption('data', $data); - - $this->db->trigger('after_insert', $query); - - if ($getLastInsID) { - return $lastInsId; - } - } - - return $result; - } - - /** - * 批量插入记录 - * @access public - * @param Query $query 查询对象 - * @param mixed $dataSet 数据集 - * @param integer $limit 每次写入数据限制 - * @return integer - * @throws \Exception - * @throws \Throwable - */ - public function insertAll(Query $query, array $dataSet = [], int $limit = 0): int - { - if (!is_array(reset($dataSet))) { - return 0; - } - - $query->parseOptions(); - - if ($limit) { - // 分批写入 自动启动事务支持 - $this->startTrans(); - - try { - $array = array_chunk($dataSet, $limit, true); - $count = 0; - - foreach ($array as $item) { - $sql = $this->builder->insertAll($query, $item); - $count += $this->execute($query, $sql, $query->getBind()); - } - - // 提交事务 - $this->commit(); - } catch (\Exception | \Throwable $e) { - $this->rollback(); - throw $e; - } - - return $count; - } - - $sql = $this->builder->insertAll($query, $dataSet); - - return $this->execute($query, $sql, $query->getBind()); - } - - /** - * 通过Select方式插入记录 - * @access public - * @param Query $query 查询对象 - * @param array $fields 要插入的数据表字段名 - * @param string $table 要插入的数据表名 - * @return integer - * @throws PDOException - */ - public function selectInsert(Query $query, array $fields, string $table): int - { - // 分析查询表达式 - $query->parseOptions(); - - $sql = $this->builder->selectInsert($query, $fields, $table); - - return $this->execute($query, $sql, $query->getBind()); - } - - /** - * 更新记录 - * @access public - * @param Query $query 查询对象 - * @return integer - * @throws Exception - * @throws PDOException - */ - public function update(Query $query): int - { - $options = $query->parseOptions(); - - if (isset($options['cache'])) { - $cacheItem = $this->parseCache($query, $options['cache']); - $key = $cacheItem->getKey(); - $tag = $cacheItem->getTag(); - } - - // 生成UPDATE SQL语句 - $sql = $this->builder->update($query); - - // 检测缓存 - if (isset($key) && $this->cache->get($key)) { - // 删除缓存 - $this->cache->delete($key); - } elseif (!empty($tag)) { - $this->cache->tag($tag)->clear(); - } - - // 执行操作 - $result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind()); - - if ($result) { - $this->db->trigger('after_update', $query); - } - - return $result; - } - - /** - * 删除记录 - * @access public - * @param Query $query 查询对象 - * @return int - * @throws Exception - * @throws PDOException - */ - public function delete(Query $query): int - { - // 分析查询表达式 - $options = $query->parseOptions(); - - if (isset($options['cache'])) { - $cacheItem = $this->parseCache($query, $options['cache']); - $key = $cacheItem->getKey(); - $tag = $cacheItem->getTag(); - } - - // 生成删除SQL语句 - $sql = $this->builder->delete($query); - - // 检测缓存 - if (isset($key) && $this->cache->get($key)) { - // 删除缓存 - $this->cache->delete($key); - } elseif (!empty($tag)) { - $this->cache->tag($tag)->clear(); - } - - // 执行操作 - $result = $this->execute($query, $sql, $query->getBind()); - - if ($result) { - $this->db->trigger('after_delete', $query); - } - - return $result; - } - - /** - * 得到某个字段的值 - * @access public - * @param Query $query 查询对象 - * @param string $field 字段名 - * @param mixed $default 默认值 - * @param bool $one 返回一个值 - * @return mixed - */ - public function value(Query $query, string $field, $default = null, bool $one = true) - { - $options = $query->parseOptions(); - - if (isset($options['field'])) { - $query->removeOption('field'); - } - - $query->setOption('field', (array) $field); - - if (!empty($options['cache'])) { - $cacheItem = $this->parseCache($query, $options['cache']); - $result = $this->getCacheData($cacheItem); - - if (false !== $result) { - return $result; - } - } - - // 生成查询SQL - $sql = $this->builder->select($query, $one); - - if (isset($options['field'])) { - $query->setOption('field', $options['field']); - } else { - $query->removeOption('field'); - } - - // 执行查询操作 - $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']); - - $result = $pdo->fetchColumn(); - - if (isset($cacheItem) && false !== $result) { - // 缓存数据 - $cacheItem->set($result); - $this->cacheData($cacheItem); - } - - return false !== $result ? $result : $default; - } - - /** - * 得到某个字段的值 - * @access protected - * @param Query $query 查询对象 - * @param string $aggregate 聚合方法 - * @param mixed $field 字段名 - * @param bool $force 强制转为数字类型 - * @return mixed - */ - protected function aggregate(Query $query, string $aggregate, $field, bool $force = false) - { - if (is_string($field) && 0 === stripos($field, 'DISTINCT ')) { - list($distinct, $field) = explode(' ', $field); - } - - $field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS tp_' . strtolower($aggregate); - - $result = $this->value($query, $field, 0, false); - - return $force ? (float) $result : $result; - } - - /** - * 得到某个列的数组 - * @access public - * @param Query $query 查询对象 - * @param string $column 字段名 多个字段用逗号分隔 - * @param string $key 索引 - * @return array - */ - public function column(Query $query, string $column, string $key = ''): array - { - $options = $query->parseOptions(); - - if (isset($options['field'])) { - $query->removeOption('field'); - } - - if ($key && '*' != $column) { - $field = $key . ',' . $column; - } else { - $field = $column; - } - - $field = array_map('trim', explode(',', $field)); - - $query->setOption('field', $field); - - if (!empty($options['cache'])) { - // 判断查询缓存 - $cacheItem = $this->parseCache($query, $options['cache']); - $result = $this->getCacheData($cacheItem); - - if (false !== $result) { - return $result; - } - } - - // 生成查询SQL - $sql = $this->builder->select($query); - - if (isset($options['field'])) { - $query->setOption('field', $options['field']); - } else { - $query->removeOption('field'); - } - - // 执行查询操作 - $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']); - - $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC); - - if (empty($resultSet)) { - $result = []; - } elseif (('*' == $column || strpos($column, ',')) && $key) { - $result = array_column($resultSet, null, $key); - } else { - $fields = array_keys($resultSet[0]); - $key = $key ?: array_shift($fields); - - if (strpos($key, '.')) { - list($alias, $key) = explode('.', $key); - } - - $result = array_column($resultSet, $column, $key); - } - - if (isset($cacheItem)) { - // 缓存数据 - $cacheItem->set($result); - $this->cacheData($cacheItem); - } - - return $result; - } - - /** - * 根据参数绑定组装最终的SQL语句 便于调试 - * @access public - * @param string $sql 带参数绑定的sql语句 - * @param array $bind 参数绑定列表 - * @return string - */ - public function getRealSql(string $sql, array $bind = []): string - { - foreach ($bind as $key => $val) { - $value = is_array($val) ? $val[0] : $val; - $type = is_array($val) ? $val[1] : PDO::PARAM_STR; - - if ((self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) && is_string($value)) { - $value = '\'' . addslashes($value) . '\''; - } elseif (PDO::PARAM_INT == $type && '' === $value) { - $value = 0; - } - - // 判断占位符 - $sql = is_numeric($key) ? - substr_replace($sql, $value, strpos($sql, '?'), 1) : - substr_replace($sql, $value, strpos($sql, ':' . $key), strlen(':' . $key)); - } - - return rtrim($sql); - } - - /** - * 参数绑定 - * 支持 ['name'=>'value','id'=>123] 对应命名占位符 - * 或者 ['value',123] 对应问号占位符 - * @access public - * @param array $bind 要绑定的参数列表 - * @return void - * @throws BindParamException - */ - protected function bindValue(array $bind = []): void - { - foreach ($bind as $key => $val) { - // 占位符 - $param = is_numeric($key) ? $key + 1 : ':' . $key; - - if (is_array($val)) { - if (PDO::PARAM_INT == $val[1] && '' === $val[0]) { - $val[0] = 0; - } elseif (self::PARAM_FLOAT == $val[1]) { - $val[0] = is_string($val[0]) ? (float) $val[0] : $val[0]; - $val[1] = PDO::PARAM_STR; - } - - $result = $this->PDOStatement->bindValue($param, $val[0], $val[1]); - } else { - $result = $this->PDOStatement->bindValue($param, $val); - } - - if (!$result) { - throw new BindParamException( - "Error occurred when binding parameters '{$param}'", - $this->config, - $this->getLastsql(), - $bind - ); - } - } - } - - /** - * 存储过程的输入输出参数绑定 - * @access public - * @param array $bind 要绑定的参数列表 - * @return void - * @throws BindParamException - */ - protected function bindParam(array $bind): void - { - foreach ($bind as $key => $val) { - $param = is_numeric($key) ? $key + 1 : ':' . $key; - - if (is_array($val)) { - array_unshift($val, $param); - $result = call_user_func_array([$this->PDOStatement, 'bindParam'], $val); - } else { - $result = $this->PDOStatement->bindValue($param, $val); - } - - if (!$result) { - $param = array_shift($val); - - throw new BindParamException( - "Error occurred when binding parameters '{$param}'", - $this->config, - $this->getLastsql(), - $bind - ); - } - } - } - - /** - * 获得数据集数组 - * @access protected - * @param bool $procedure 是否存储过程 - * @return array - */ - protected function getResult(bool $procedure = false): array - { - if ($procedure) { - // 存储过程返回结果 - return $this->procedure(); - } - - $result = $this->PDOStatement->fetchAll($this->fetchType); - - $this->numRows = count($result); - - return $result; - } - - /** - * 获得存储过程数据集 - * @access protected - * @return array - */ - protected function procedure(): array - { - $item = []; - - do { - $result = $this->getResult(); - if (!empty($result)) { - $item[] = $result; - } - } while ($this->PDOStatement->nextRowset()); - - $this->numRows = count($item); - - return $item; - } - - /** - * 执行数据库事务 - * @access public - * @param callable $callback 数据操作方法回调 - * @return mixed - * @throws PDOException - * @throws \Exception - * @throws \Throwable - */ - public function transaction(callable $callback) - { - $this->startTrans(); - - try { - $result = null; - if (is_callable($callback)) { - $result = call_user_func_array($callback, [$this]); - } - - $this->commit(); - return $result; - } catch (\Exception | \Throwable $e) { - $this->rollback(); - throw $e; - } - } - - /** - * 启动事务 - * @access public - * @return void - * @throws \PDOException - * @throws \Exception - */ - public function startTrans(): void - { - $this->initConnect(true); - - ++$this->transTimes; - - try { - if (1 == $this->transTimes) { - $this->linkID->beginTransaction(); - } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { - $this->linkID->exec( - $this->parseSavepoint('trans' . $this->transTimes) - ); - } - } catch (\Exception $e) { - if ($this->isBreak($e)) { - --$this->transTimes; - $this->close()->startTrans(); - } - throw $e; - } - } - - /** - * 用于非自动提交状态下面的查询提交 - * @access public - * @return void - * @throws PDOException - */ - public function commit(): void - { - $this->initConnect(true); - - if (1 == $this->transTimes) { - $this->linkID->commit(); - } - - --$this->transTimes; - } - - /** - * 事务回滚 - * @access public - * @return void - * @throws PDOException - */ - public function rollback(): void - { - $this->initConnect(true); - - if (1 == $this->transTimes) { - $this->linkID->rollBack(); - } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { - $this->linkID->exec( - $this->parseSavepointRollBack('trans' . $this->transTimes) - ); - } - - $this->transTimes = max(0, $this->transTimes - 1); - } - - /** - * 是否支持事务嵌套 - * @return bool - */ - protected function supportSavepoint(): bool - { - return false; - } - - /** - * 生成定义保存点的SQL - * @access protected - * @param string $name 标识 - * @return string - */ - protected function parseSavepoint(string $name): string - { - return 'SAVEPOINT ' . $name; - } - - /** - * 生成回滚到保存点的SQL - * @access protected - * @param string $name 标识 - * @return string - */ - protected function parseSavepointRollBack(string $name): string - { - return 'ROLLBACK TO SAVEPOINT ' . $name; - } - - /** - * 批处理执行SQL语句 - * 批处理的指令都认为是execute操作 - * @access public - * @param Query $query 查询对象 - * @param array $sqlArray SQL批处理指令 - * @param array $bind 参数绑定 - * @return bool - */ - public function batchQuery(Query $query, array $sqlArray = [], array $bind = []): bool - { - // 自动启动事务支持 - $this->startTrans(); - - try { - foreach ($sqlArray as $sql) { - $this->execute($query, $sql, $bind); - } - // 提交事务 - $this->commit(); - } catch (\Exception $e) { - $this->rollback(); - throw $e; - } - - return true; - } - - /** - * 关闭数据库(或者重新连接) - * @access public - * @return $this - */ - public function close() - { - $this->linkID = null; - $this->linkWrite = null; - $this->linkRead = null; - $this->links = []; - - $this->free(); - - return $this; - } - - /** - * 是否断线 - * @access protected - * @param \PDOException|\Exception $e 异常对象 - * @return bool - */ - protected function isBreak($e): bool - { - if (!$this->config['break_reconnect']) { - return false; - } - - $error = $e->getMessage(); - - foreach ($this->breakMatchStr as $msg) { - if (false !== stripos($error, $msg)) { - return true; - } - } - - return false; - } - - /** - * 获取最近一次查询的sql语句 - * @access public - * @return string - */ - public function getLastSql(): string - { - return $this->getRealSql($this->queryStr, $this->bind); - } - - /** - * 获取最近插入的ID - * @access public - * @param string $sequence 自增序列名 - * @return string - */ - public function getLastInsID(string $sequence = null): string - { - return $this->linkID->lastInsertId($sequence); - } - - /** - * 获取返回或者影响的记录数 - * @access public - * @return integer - */ - public function getNumRows(): int - { - return $this->numRows; - } - - /** - * 获取最近的错误信息 - * @access public - * @return string - */ - public function getError(): string - { - if ($this->PDOStatement) { - $error = $this->PDOStatement->errorInfo(); - $error = $error[1] . ':' . $error[2]; - } else { - $error = ''; - } - - if ('' != $this->queryStr) { - $error .= "\n [ SQL语句 ] : " . $this->getLastsql(); - } - - return $error; - } - - /** - * 数据库调试 记录当前SQL及分析性能 - * @access protected - * @param boolean $start 调试开始标记 true 开始 false 结束 - * @param string $sql 执行的SQL语句 留空自动获取 - * @param bool $master 主从标记 - * @return void - */ - protected function debug(bool $start, string $sql = '', bool $master = false): void - { - if (!empty($this->config['debug'])) { - // 开启数据库调试模式 - if ($start) { - $this->queryStartTime = microtime(true); - } else { - // 记录操作结束时间 - $runtime = number_format((microtime(true) - $this->queryStartTime), 6); - $sql = $sql ?: $this->getLastsql(); - $result = []; - - // SQL性能分析 - if ($this->config['sql_explain'] && 0 === stripos(trim($sql), 'select')) { - $result = $this->getExplain($sql); - } - - // SQL监听 - $this->triggerSql($sql, $runtime, $result, $master); - } - } - } - - /** - * 触发SQL事件 - * @access protected - * @param string $sql SQL语句 - * @param string $runtime SQL运行时间 - * @param mixed $explain SQL分析 - * @param bool $master 主从标记 - * @return void - */ - protected function triggerSql(string $sql, string $runtime, array $explain = [], bool $master = false): void - { - $listen = $this->db->getListen(); - if (!empty($listen)) { - foreach ($listen as $callback) { - if (is_callable($callback)) { - $callback($sql, $runtime, $explain, $master); - } - } - } else { - if ($this->config['deploy']) { - // 分布式记录当前操作的主从 - $master = $master ? 'master|' : 'slave|'; - } else { - $master = ''; - } - - // 未注册监听则记录到日志中 - $this->log('[ SQL ] ' . $sql . ' [ ' . $master . 'RunTime:' . $runtime . 's ]'); - - if (!empty($explain)) { - $this->log('[ EXPLAIN : ' . var_export($explain, true) . ' ]'); - } - } - } - - /** - * 记录SQL日志 - * @access protected - * @param string $log SQL日志信息 - * @return void - */ - protected function log($log): void - { - if ($this->config['debug']) { - $this->log[] = $log; - } - } - - public function getSqlLog(): array - { - return $this->log; - } - - /** - * 初始化数据库连接 - * @access protected - * @param boolean $master 是否主服务器 - * @return void - */ - protected function initConnect(bool $master = true): void - { - if (!empty($this->config['deploy'])) { - // 采用分布式数据库 - if ($master || $this->transTimes) { - if (!$this->linkWrite) { - $this->linkWrite = $this->multiConnect(true); - } - - $this->linkID = $this->linkWrite; - } else { - if (!$this->linkRead) { - $this->linkRead = $this->multiConnect(false); - } - - $this->linkID = $this->linkRead; - } - } elseif (!$this->linkID) { - // 默认单数据库 - $this->linkID = $this->connect(); - } - } - - /** - * 连接分布式服务器 - * @access protected - * @param boolean $master 主服务器 - * @return PDO - */ - protected function multiConnect(bool $master = false): PDO - { - $config = []; - - // 分布式数据库配置解析 - foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { - $config[$name] = is_string($this->config[$name]) ? explode(',', $this->config[$name]) : $this->config[$name]; - } - - // 主服务器序号 - $m = floor(mt_rand(0, $this->config['master_num'] - 1)); - - if ($this->config['rw_separate']) { - // 主从式采用读写分离 - if ($master) // 主服务器写入 - { - $r = $m; - } elseif (is_numeric($this->config['slave_no'])) { - // 指定服务器读 - $r = $this->config['slave_no']; - } else { - // 读操作连接从服务器 每次随机连接的数据库 - $r = floor(mt_rand($this->config['master_num'], count($config['hostname']) - 1)); - } - } else { - // 读写操作不区分服务器 每次随机连接的数据库 - $r = floor(mt_rand(0, count($config['hostname']) - 1)); - } - $dbMaster = false; - - if ($m != $r) { - $dbMaster = []; - foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { - $dbMaster[$name] = $config[$name][$m] ?? $config[$name][0]; - } - } - - $dbConfig = []; - - foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { - $dbConfig[$name] = $config[$name][$r] ?? $config[$name][0]; - } - - return $this->connect($dbConfig, $r, $r == $m ? false : $dbMaster); - } - - /** - * 析构方法 - * @access public - */ - public function __destruct() - { - // 释放查询 - $this->free(); - - // 关闭连接 - $this->close(); - } - - /** - * 缓存数据 - * @access protected - * @param CacheItem $cacheItem 缓存Item - */ - protected function cacheData(CacheItem $cacheItem): void - { - if ($cacheItem->getTag()) { - $this->cache->tag($cacheItem->getTag()); - } - - $this->cache->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire()); - } - - /** - * 获取缓存数据 - * @access protected - * @param CacheItem $cacheItem - * @return mixed - */ - protected function getCacheData(CacheItem $cacheItem) - { - // 判断查询缓存 - return $this->cache->get($cacheItem->getKey()); - } - - protected function parseCache(Query $query, array $cache): CacheItem - { - list($key, $expire, $tag) = $cache; + list($key, $expire, $tag) = $cache; if ($key instanceof CacheItem) { $cacheItem = $key; @@ -1869,7 +448,7 @@ abstract class Connection if (!empty($query->getOptions('key'))) { $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); } else { - $key = md5($this->getConfig('database') . serialize($query->getOptions()) . serialize($query->getBind(false))); + $key = md5($this->getConfig('database') . serialize($query->getOptions())); } } @@ -1910,4 +489,17 @@ abstract class Connection return false; } + + /** + * 析构方法 + * @access public + */ + public function __destruct() + { + // 释放查询 + $this->free(); + + // 关闭连接 + $this->close(); + } } diff --git a/src/db/Fetch.php b/src/db/Fetch.php index c852627..93a951d 100644 --- a/src/db/Fetch.php +++ b/src/db/Fetch.php @@ -12,8 +12,8 @@ declare (strict_types = 1); namespace think\db; +use think\Container; use think\Exception; -use think\facade\Db; /** * SQL获取类 @@ -53,8 +53,8 @@ class Fetch /** * 聚合查询 * @access protected - * @param string $aggregate 聚合方法 - * @param string $field 字段名 + * @param string $aggregate 聚合方法 + * @param string $field 字段名 * @return string */ protected function aggregate(string $aggregate, string $field): string @@ -71,7 +71,6 @@ class Fetch * @access public * @param string $field 字段名 * @param mixed $default 默认值 - * @param bool $one 限制返回一个数据 * @return string */ public function value(string $field, $default = null, bool $one = true): string @@ -234,6 +233,7 @@ class Fetch public function selectInsert(array $fields, string $table): string { $this->query->parseOptions(); + $sql = $this->builder->selectInsert($this->query, $fields, $table); return $this->fetch($sql); @@ -479,11 +479,11 @@ class Fetch { if (strtolower(substr($method, 0, 5)) == 'getby') { // 根据某个字段获取记录 - $field = Db::parseName(substr($method, 5)); + $field = Container::parseName(substr($method, 5)); return $this->where($field, '=', $args[0])->find(); } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { // 根据某个字段获取记录的某个值 - $name = Db::parseName(substr($method, 10)); + $name = Container::parseName(substr($method, 10)); return $this->where($name, '=', $args[0])->value($args[1]); } diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php new file mode 100644 index 0000000..079f92d --- /dev/null +++ b/src/db/PDOConnection.php @@ -0,0 +1,1595 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db; + +use PDO; +use PDOStatement; +use think\cache\CacheItem; +use think\Container; +use think\db\exception\BindParamException; +use think\Exception; +use think\exception\PDOException; + +/** + * 数据库连接基础类 + */ +abstract class PDOConnection extends Connection +{ + const PARAM_FLOAT = 21; + + /** + * PDO操作实例 + * @var PDOStatement + */ + protected $PDOStatement; + + /** + * 当前SQL指令 + * @var string + */ + protected $queryStr = ''; + + /** + * 事务指令数 + * @var int + */ + protected $transTimes = 0; + + /** + * 查询结果类型 + * @var int + */ + protected $fetchType = PDO::FETCH_ASSOC; + + /** + * 字段属性大小写 + * @var int + */ + protected $attrCase = PDO::CASE_LOWER; + + /** + * 数据表信息 + * @var array + */ + protected $info = []; + + /** + * 查询开始时间 + * @var float + */ + protected $queryStartTime; + + /** + * PDO连接参数 + * @var array + */ + protected $params = [ + PDO::ATTR_CASE => PDO::CASE_NATURAL, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, + PDO::ATTR_STRINGIFY_FETCHES => false, + PDO::ATTR_EMULATE_PREPARES => false, + ]; + + /** + * 参数绑定类型映射 + * @var array + */ + protected $bindType = [ + 'string' => PDO::PARAM_STR, + 'str' => PDO::PARAM_STR, + 'integer' => PDO::PARAM_INT, + 'int' => PDO::PARAM_INT, + 'boolean' => PDO::PARAM_BOOL, + 'bool' => PDO::PARAM_BOOL, + 'float' => self::PARAM_FLOAT, + ]; + + /** + * 服务器断线标识字符 + * @var array + */ + protected $breakMatchStr = [ + 'server has gone away', + 'no connection to the server', + 'Lost connection', + 'is dead or not enabled', + 'Error while sending', + 'decryption failed or bad record mac', + 'server closed the connection unexpectedly', + 'SSL connection has been closed unexpectedly', + 'Error writing data to the connection', + 'Resource deadlock avoided', + 'failed with errno', + ]; + + /** + * 绑定参数 + * @var array + */ + protected $bind = []; + + /** + * 获取当前连接器类对应的Query类 + * @access public + * @return string + */ + public function getQueryClass(): string + { + return $this->getConfig('query') ?: Query::class; + } + + /** + * 获取当前连接器类对应的Builder类 + * @access public + * @return string + */ + public function getBuilderClass(): string + { + return $this->getConfig('builder') ?: '\\think\\db\\builder\\' . ucfirst($this->getConfig('type')); + } + + /** + * 解析pdo连接的dsn信息 + * @access protected + * @param array $config 连接信息 + * @return string + */ + abstract protected function parseDsn(array $config); + + /** + * 取得数据表的字段信息 + * @access public + * @param string $tableName 数据表名称 + * @return array + */ + abstract public function getFields(string $tableName); + + /** + * 取得数据库的表信息 + * @access public + * @param string $dbName 数据库名称 + * @return array + */ + abstract public function getTables(string $dbName); + + /** + * SQL性能分析 + * @access protected + * @param string $sql SQL语句 + * @return array + */ + abstract protected function getExplain(string $sql); + + /** + * 对返数据表字段信息进行大小写转换出来 + * @access public + * @param array $info 字段信息 + * @return array + */ + public function fieldCase(array $info): array + { + // 字段大小写转换 + switch ($this->attrCase) { + case PDO::CASE_LOWER: + $info = array_change_key_case($info); + break; + case PDO::CASE_UPPER: + $info = array_change_key_case($info, CASE_UPPER); + break; + case PDO::CASE_NATURAL: + default: + // 不做转换 + } + + return $info; + } + + /** + * 获取字段绑定类型 + * @access public + * @param string $type 字段类型 + * @return integer + */ + public function getFieldBindType(string $type): int + { + if (in_array($type, ['integer', 'string', 'float', 'boolean', 'bool', 'int', 'str'])) { + $bind = $this->bindType[$type]; + } elseif (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) { + $bind = PDO::PARAM_STR; + } elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) { + $bind = self::PARAM_FLOAT; + } elseif (preg_match('/(int|serial|bit)/is', $type)) { + $bind = PDO::PARAM_INT; + } elseif (preg_match('/bool/is', $type)) { + $bind = PDO::PARAM_BOOL; + } else { + $bind = PDO::PARAM_STR; + } + + return $bind; + } + + /** + * 获取数据表信息 + * @access public + * @param mixed $tableName 数据表名 留空自动获取 + * @param string $fetch 获取信息类型 包括 fields type bind pk + * @return mixed + */ + public function getTableInfo($tableName, string $fetch = '') + { + if (is_array($tableName)) { + $tableName = key($tableName) ?: current($tableName); + } + + if (strpos($tableName, ',')) { + // 多表不获取字段信息 + return []; + } + + // 修正子查询作为表名的问题 + if (strpos($tableName, ')')) { + return []; + } + + list($tableName) = explode(' ', $tableName); + + if (!strpos($tableName, '.')) { + $schema = $this->getConfig('database') . '.' . $tableName; + } else { + $schema = $tableName; + } + + if (!isset($this->info[$schema])) { + // 读取缓存 + $cacheFile = Container::pull('app')->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR . $schema . '.php'; + + if (!$this->config['debug'] && is_file($cacheFile)) { + $info = include $cacheFile; + } else { + $info = $this->getFields($tableName); + } + + $fields = array_keys($info); + $bind = $type = []; + + foreach ($info as $key => $val) { + // 记录字段类型 + $type[$key] = $val['type']; + $bind[$key] = $this->getFieldBindType($val['type']); + + if (!empty($val['primary'])) { + $pk[] = $key; + } + } + + if (isset($pk)) { + // 设置主键 + $pk = count($pk) > 1 ? $pk : $pk[0]; + } else { + $pk = null; + } + + $this->info[$schema] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk]; + } + + return $fetch ? $this->info[$schema][$fetch] : $this->info[$schema]; + } + + /** + * 获取数据表的主键 + * @access public + * @param mixed $tableName 数据表名 + * @return string|array + */ + public function getPk($tableName) + { + return $this->getTableInfo($tableName, 'pk'); + } + + /** + * 获取数据表字段信息 + * @access public + * @param mixed $tableName 数据表名 + * @return array + */ + public function getTableFields($tableName): array + { + return $this->getTableInfo($tableName, 'fields'); + } + + /** + * 获取数据表字段类型 + * @access public + * @param mixed $tableName 数据表名 + * @param string $field 字段名 + * @return array|string + */ + public function getFieldsType($tableName, string $field = null) + { + $result = $this->getTableInfo($tableName, 'type'); + + if ($field && isset($result[$field])) { + return $result[$field]; + } + + return $result; + } + + /** + * 获取数据表绑定信息 + * @access public + * @param mixed $tableName 数据表名 + * @return array + */ + public function getFieldsBind($tableName): array + { + return $this->getTableInfo($tableName, 'bind'); + } + + /** + * 连接数据库方法 + * @access public + * @param array $config 连接参数 + * @param integer $linkNum 连接序号 + * @param array|bool $autoConnection 是否自动连接主数据库(用于分布式) + * @return PDO + * @throws Exception + */ + public function connect(array $config = [], $linkNum = 0, $autoConnection = false): PDO + { + if (isset($this->links[$linkNum])) { + return $this->links[$linkNum]; + } + + if (empty($config)) { + $config = $this->config; + } else { + $config = array_merge($this->config, $config); + } + + // 连接参数 + if (isset($config['params']) && is_array($config['params'])) { + $params = $config['params'] + $this->params; + } else { + $params = $this->params; + } + + // 记录当前字段属性大小写设置 + $this->attrCase = $params[PDO::ATTR_CASE]; + + if (!empty($config['break_match_str'])) { + $this->breakMatchStr = array_merge($this->breakMatchStr, (array) $config['break_match_str']); + } + + try { + if (empty($config['dsn'])) { + $config['dsn'] = $this->parseDsn($config); + } + + $startTime = microtime(true); + $this->links[$linkNum] = $this->createPdo($config['dsn'], $config['username'], $config['password'], $params); + // 记录数据库连接信息 + $this->log('CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); + + return $this->links[$linkNum]; + } catch (\PDOException $e) { + if ($autoConnection) { + $this->log->error($e->getMessage()); + return $this->connect($autoConnection, $linkNum); + } else { + throw $e; + } + } + } + + /** + * 创建PDO实例 + * @param $dsn + * @param $username + * @param $password + * @param $params + * @return PDO + */ + protected function createPdo($dsn, $username, $password, $params) + { + return new PDO($dsn, $username, $password, $params); + } + + /** + * 释放查询结果 + * @access public + */ + public function free(): void + { + $this->PDOStatement = null; + } + + /** + * 获取PDO对象 + * @access public + * @return \PDO|false + */ + public function getPdo() + { + if (!$this->linkID) { + return false; + } + + return $this->linkID; + } + + /** + * 执行查询 使用生成器返回数据 + * @access public + * @param BaseQuery $query 查询对象 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param \think\Model $model 模型对象实例 + * @param array $condition 查询条件 + * @return \Generator + */ + public function getCursor(BaseQuery $query, string $sql, array $bind = [], $model = null, $condition = null) + { + $this->queryPDOStatement($query, $sql, $bind); + + // 返回结果集 + while ($result = $this->PDOStatement->fetch($this->fetchType)) { + if ($model) { + yield $model->newInstance($result, $condition)->setQuery($query); + } else { + yield $result; + } + } + } + + /** + * 执行查询 返回数据集 + * @access public + * @param BaseQuery $query 查询对象 + * @param mixed $sql sql指令 + * @param array $bind 参数绑定 + * @return array + * @throws BindParamException + * @throws \PDOException + * @throws \Exception + * @throws \Throwable + */ + public function query(BaseQuery $query, $sql, array $bind = []): array + { + // 分析查询表达式 + $query->parseOptions(); + + if ($query->getOptions('cache')) { + // 检查查询缓存 + $cacheItem = $this->parseCache($query, $query->getOptions('cache')); + $resultSet = $this->cache->get($cacheItem->getKey()); + + if (false !== $resultSet) { + return $resultSet; + } + } + + if ($sql instanceof \Closure) { + $sql = $sql($query); + $bind = $query->getBind(); + } + + $master = $query->getOptions('master') ? true : false; + $procedure = $query->getOptions('procedure') ? true : in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); + + $this->getPDOStatement($sql, $bind, $master, $procedure); + + $resultSet = $this->getResult($procedure); + + if (isset($cacheItem) && $resultSet) { + // 缓存数据集 + $cacheItem->set($resultSet); + $this->cacheData($cacheItem); + } + + return $resultSet; + } + + /** + * 执行查询但只返回PDOStatement对象 + * @access public + * @param BaseQuery $query 查询对象 + * @return \PDOStatement + */ + public function pdo(BaseQuery $query): PDOStatement + { + $bind = $query->getBind(); + // 生成查询SQL + $sql = $this->builder->select($query); + + return $this->queryPDOStatement($query, $sql, $bind); + } + + /** + * 执行查询但只返回PDOStatement对象 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param bool $master 是否在主服务器读操作 + * @param bool $procedure 是否为存储过程调用 + * @return PDOStatement + * @throws BindParamException + * @throws \PDOException + * @throws \Exception + * @throws \Throwable + */ + public function getPDOStatement(string $sql, array $bind = [], bool $master = false, bool $procedure = false): PDOStatement + { + $this->initConnect($this->readMaster ?: $master); + + // 记录SQL语句 + $this->queryStr = $sql; + + $this->bind = $bind; + + $this->db->updateQueryTimes(); + + try { + // 调试开始 + $this->debug(true); + + // 预处理 + $this->PDOStatement = $this->linkID->prepare($sql); + + // 参数绑定 + if ($procedure) { + $this->bindParam($bind); + } else { + $this->bindValue($bind); + } + + // 执行查询 + $this->PDOStatement->execute(); + + // 调试结束 + $this->debug(false, '', $master); + + return $this->PDOStatement; + } catch (\Throwable | \Exception $e) { + if ($this->isBreak($e)) { + return $this->close()->getPDOStatement($sql, $bind, $master, $procedure); + } + + if ($e instanceof \PDOException) { + throw new PDOException($e, $this->config, $this->getLastsql()); + } else { + throw $e; + } + } + } + + /** + * 执行语句 + * @access public + * @param BaseQuery $query 查询对象 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param bool $origin 是否原生查询 + * @return int + * @throws BindParamException + * @throws \PDOException + * @throws \Exception + * @throws \Throwable + */ + public function execute(BaseQuery $query, string $sql, array $bind = [], bool $origin = false): int + { + if ($origin) { + $query->parseOptions(); + } + + $this->queryPDOStatement($query->master(true), $sql, $bind); + + if (!$origin && !empty($this->config['deploy']) && !empty($this->config['read_master'])) { + $this->readMaster = true; + } + + $this->numRows = $this->PDOStatement->rowCount(); + + if ($query->getOptions('cache')) { + // 清理缓存数据 + $cacheItem = $this->parseCache($query, $query->getOptions('cache')); + $key = $cacheItem->getKey(); + $tag = $cacheItem->getTag(); + + if (isset($key) && $this->cache->get($key)) { + $this->cache->delete($key); + } elseif (!empty($tag)) { + $this->cache->tag($tag)->clear(); + } + } + + return $this->numRows; + } + + protected function queryPDOStatement(BaseQuery $query, string $sql, array $bind = []): PDOStatement + { + $options = $query->getOptions(); + $master = !empty($options['master']) ? true : false; + $procedure = !empty($options['procedure']) ? true : in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); + + return $this->getPDOStatement($sql, $bind, $master, $procedure); + } + + /** + * 查找单条记录 + * @access public + * @param BaseQuery $query 查询对象 + * @return array + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function find(BaseQuery $query): array + { + // 事件回调 + $result = $this->db->trigger('before_find', $query); + + if (!$result) { + // 执行查询 + $resultSet = $this->query($query, function ($query) { + return $this->builder->select($query, true); + }); + + $result = $resultSet[0] ?? []; + } + + return $result; + } + + /** + * 使用游标查询记录 + * @access public + * @param BaseQuery $query 查询对象 + * @return \Generator + */ + public function cursor(BaseQuery $query) + { + // 分析查询表达式 + $options = $query->parseOptions(); + + // 生成查询SQL + $sql = $this->builder->select($query); + + $condition = $options['where']['AND'] ?? null; + + // 执行查询操作 + return $this->getCursor($query, $sql, $query->getBind(), $query->getModel(), $condition); + } + + /** + * 查找记录 + * @access public + * @param BaseQuery $query 查询对象 + * @return array + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function select(BaseQuery $query): array + { + $resultSet = $this->db->trigger('before_select', $query); + + if (!$resultSet) { + // 执行查询操作 + $resultSet = $this->query($query, function ($query) { + return $this->builder->select($query); + }); + } + + return $resultSet; + } + + /** + * 插入记录 + * @access public + * @param BaseQuery $query 查询对象 + * @param boolean $getLastInsID 返回自增主键 + * @return mixed + */ + public function insert(BaseQuery $query, bool $getLastInsID = false) + { + // 分析查询表达式 + $options = $query->parseOptions(); + + // 生成SQL语句 + $sql = $this->builder->insert($query); + + // 执行操作 + $result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind()); + + if ($result) { + $sequence = $options['sequence'] ?? null; + $lastInsId = $this->getLastInsID($query, $sequence); + + $data = $options['data']; + + if ($lastInsId) { + $pk = $query->getPk(); + if (is_string($pk)) { + $data[$pk] = $lastInsId; + } + } + + $query->setOption('data', $data); + + $this->db->trigger('after_insert', $query); + + if ($getLastInsID) { + return $lastInsId; + } + } + + return $result; + } + + /** + * 批量插入记录 + * @access public + * @param BaseQuery $query 查询对象 + * @param mixed $dataSet 数据集 + * @param integer $limit 每次写入数据限制 + * @return integer + * @throws \Exception + * @throws \Throwable + */ + public function insertAll(BaseQuery $query, array $dataSet = [], int $limit = 0): int + { + if (!is_array(reset($dataSet))) { + return 0; + } + + $query->parseOptions(); + + if ($limit) { + // 分批写入 自动启动事务支持 + $this->startTrans(); + + try { + $array = array_chunk($dataSet, $limit, true); + $count = 0; + + foreach ($array as $item) { + $sql = $this->builder->insertAll($query, $item); + $count += $this->execute($query, $sql, $query->getBind()); + } + + // 提交事务 + $this->commit(); + } catch (\Exception | \Throwable $e) { + $this->rollback(); + throw $e; + } + + return $count; + } + + $sql = $this->builder->insertAll($query, $dataSet); + + return $this->execute($query, $sql, $query->getBind()); + } + + /** + * 通过Select方式插入记录 + * @access public + * @param BaseQuery $query 查询对象 + * @param array $fields 要插入的数据表字段名 + * @param string $table 要插入的数据表名 + * @return integer + * @throws PDOException + */ + public function selectInsert(BaseQuery $query, array $fields, string $table): int + { + // 分析查询表达式 + $query->parseOptions(); + + $sql = $this->builder->selectInsert($query, $fields, $table); + + return $this->execute($query, $sql, $query->getBind()); + } + + /** + * 更新记录 + * @access public + * @param BaseQuery $query 查询对象 + * @return integer + * @throws Exception + * @throws PDOException + */ + public function update(BaseQuery $query): int + { + $query->parseOptions(); + + // 生成UPDATE SQL语句 + $sql = $this->builder->update($query); + + // 执行操作 + $result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind()); + + if ($result) { + $this->db->trigger('after_update', $query); + } + + return $result; + } + + /** + * 删除记录 + * @access public + * @param BaseQuery $query 查询对象 + * @return int + * @throws Exception + * @throws PDOException + */ + public function delete(BaseQuery $query): int + { + // 分析查询表达式 + $query->parseOptions(); + + // 生成删除SQL语句 + $sql = $this->builder->delete($query); + + // 执行操作 + $result = $this->execute($query, $sql, $query->getBind()); + + if ($result) { + $this->db->trigger('after_delete', $query); + } + + return $result; + } + + /** + * 得到某个字段的值 + * @access public + * @param BaseQuery $query 查询对象 + * @param string $field 字段名 + * @param mixed $default 默认值 + * @param bool $one 返回一个值 + * @return mixed + */ + public function value(BaseQuery $query, string $field, $default = null, bool $one = true) + { + $options = $query->parseOptions(); + + if (isset($options['field'])) { + $query->removeOption('field'); + } + + $query->setOption('field', (array) $field); + + if (!empty($options['cache'])) { + $cacheItem = $this->parseCache($query, $options['cache']); + $result = $this->cache->get($cacheItem->getKey()); + + if (false !== $result) { + return $result; + } + } + + // 生成查询SQL + $sql = $this->builder->select($query, $one); + + if (isset($options['field'])) { + $query->setOption('field', $options['field']); + } else { + $query->removeOption('field'); + } + + // 执行查询操作 + $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']); + + $result = $pdo->fetchColumn(); + + if (isset($cacheItem) && false !== $result) { + // 缓存数据 + $cacheItem->set($result); + $this->cacheData($cacheItem); + } + + return false !== $result ? $result : $default; + } + + /** + * 得到某个字段的值 + * @access public + * @param BaseQuery $query 查询对象 + * @param string $aggregate 聚合方法 + * @param mixed $field 字段名 + * @param bool $force 强制转为数字类型 + * @return mixed + */ + public function aggregate(BaseQuery $query, string $aggregate, $field, bool $force = false) + { + if (is_string($field) && 0 === stripos($field, 'DISTINCT ')) { + list($distinct, $field) = explode(' ', $field); + } + + $field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS tp_' . strtolower($aggregate); + + $result = $this->value($query, $field, 0, false); + + return $force ? (float) $result : $result; + } + + /** + * 得到某个列的数组 + * @access public + * @param BaseQuery $query 查询对象 + * @param string $column 字段名 多个字段用逗号分隔 + * @param string $key 索引 + * @return array + */ + public function column(BaseQuery $query, string $column, string $key = ''): array + { + $options = $query->parseOptions(); + + if (isset($options['field'])) { + $query->removeOption('field'); + } + + if ($key && '*' != $column) { + $field = $key . ',' . $column; + } else { + $field = $column; + } + + $field = array_map('trim', explode(',', $field)); + + $query->setOption('field', $field); + + if (!empty($options['cache'])) { + // 判断查询缓存 + $cacheItem = $this->parseCache($query, $options['cache']); + $result = $this->cache->get($cacheItem->getKey()); + + if (false !== $result) { + return $result; + } + } + + // 生成查询SQL + $sql = $this->builder->select($query); + + if (isset($options['field'])) { + $query->setOption('field', $options['field']); + } else { + $query->removeOption('field'); + } + + // 执行查询操作 + $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']); + + $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC); + + if (empty($resultSet)) { + $result = []; + } elseif (('*' == $column || strpos($column, ',')) && $key) { + $result = array_column($resultSet, null, $key); + } else { + $fields = array_keys($resultSet[0]); + $key = $key ?: array_shift($fields); + + if (strpos($key, '.')) { + list($alias, $key) = explode('.', $key); + } + + $result = array_column($resultSet, $column, $key); + } + + if (isset($cacheItem)) { + // 缓存数据 + $cacheItem->set($result); + $this->cacheData($cacheItem); + } + + return $result; + } + + /** + * 根据参数绑定组装最终的SQL语句 便于调试 + * @access public + * @param string $sql 带参数绑定的sql语句 + * @param array $bind 参数绑定列表 + * @return string + */ + public function getRealSql(string $sql, array $bind = []): string + { + foreach ($bind as $key => $val) { + $value = is_array($val) ? $val[0] : $val; + $type = is_array($val) ? $val[1] : PDO::PARAM_STR; + + if ((self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) && is_string($value)) { + $value = '\'' . addslashes($value) . '\''; + } elseif (PDO::PARAM_INT == $type && '' === $value) { + $value = 0; + } + + // 判断占位符 + $sql = is_numeric($key) ? + substr_replace($sql, $value, strpos($sql, '?'), 1) : + substr_replace($sql, $value, strpos($sql, ':' . $key), strlen(':' . $key)); + } + + return rtrim($sql); + } + + /** + * 参数绑定 + * 支持 ['name'=>'value','id'=>123] 对应命名占位符 + * 或者 ['value',123] 对应问号占位符 + * @access public + * @param array $bind 要绑定的参数列表 + * @return void + * @throws BindParamException + */ + protected function bindValue(array $bind = []): void + { + foreach ($bind as $key => $val) { + // 占位符 + $param = is_numeric($key) ? $key + 1 : ':' . $key; + + if (is_array($val)) { + if (PDO::PARAM_INT == $val[1] && '' === $val[0]) { + $val[0] = 0; + } elseif (self::PARAM_FLOAT == $val[1]) { + $val[0] = is_string($val[0]) ? (float) $val[0] : $val[0]; + $val[1] = PDO::PARAM_STR; + } + + $result = $this->PDOStatement->bindValue($param, $val[0], $val[1]); + } else { + $result = $this->PDOStatement->bindValue($param, $val); + } + + if (!$result) { + throw new BindParamException( + "Error occurred when binding parameters '{$param}'", + $this->config, + $this->getLastsql(), + $bind + ); + } + } + } + + /** + * 存储过程的输入输出参数绑定 + * @access public + * @param array $bind 要绑定的参数列表 + * @return void + * @throws BindParamException + */ + protected function bindParam(array $bind): void + { + foreach ($bind as $key => $val) { + $param = is_numeric($key) ? $key + 1 : ':' . $key; + + if (is_array($val)) { + array_unshift($val, $param); + $result = call_user_func_array([$this->PDOStatement, 'bindParam'], $val); + } else { + $result = $this->PDOStatement->bindValue($param, $val); + } + + if (!$result) { + $param = array_shift($val); + + throw new BindParamException( + "Error occurred when binding parameters '{$param}'", + $this->config, + $this->getLastsql(), + $bind + ); + } + } + } + + /** + * 获得数据集数组 + * @access protected + * @param bool $procedure 是否存储过程 + * @return array + */ + protected function getResult(bool $procedure = false): array + { + if ($procedure) { + // 存储过程返回结果 + return $this->procedure(); + } + + $result = $this->PDOStatement->fetchAll($this->fetchType); + + $this->numRows = count($result); + + return $result; + } + + /** + * 获得存储过程数据集 + * @access protected + * @return array + */ + protected function procedure(): array + { + $item = []; + + do { + $result = $this->getResult(); + if (!empty($result)) { + $item[] = $result; + } + } while ($this->PDOStatement->nextRowset()); + + $this->numRows = count($item); + + return $item; + } + + /** + * 执行数据库事务 + * @access public + * @param callable $callback 数据操作方法回调 + * @return mixed + * @throws PDOException + * @throws \Exception + * @throws \Throwable + */ + public function transaction(callable $callback) + { + $this->startTrans(); + + try { + $result = null; + if (is_callable($callback)) { + $result = $callback($this); + } + + $this->commit(); + return $result; + } catch (\Exception | \Throwable $e) { + $this->rollback(); + throw $e; + } + } + + /** + * 启动事务 + * @access public + * @return void + * @throws \PDOException + * @throws \Exception + */ + public function startTrans(): void + { + $this->initConnect(true); + + ++$this->transTimes; + + try { + if (1 == $this->transTimes) { + $this->linkID->beginTransaction(); + } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { + $this->linkID->exec( + $this->parseSavepoint('trans' . $this->transTimes) + ); + } + } catch (\Exception $e) { + if ($this->isBreak($e)) { + --$this->transTimes; + $this->close()->startTrans(); + } + throw $e; + } + } + + /** + * 用于非自动提交状态下面的查询提交 + * @access public + * @return void + * @throws PDOException + */ + public function commit(): void + { + $this->initConnect(true); + + if (1 == $this->transTimes) { + $this->linkID->commit(); + } + + --$this->transTimes; + } + + /** + * 事务回滚 + * @access public + * @return void + * @throws PDOException + */ + public function rollback(): void + { + $this->initConnect(true); + + if (1 == $this->transTimes) { + $this->linkID->rollBack(); + } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { + $this->linkID->exec( + $this->parseSavepointRollBack('trans' . $this->transTimes) + ); + } + + $this->transTimes = max(0, $this->transTimes - 1); + } + + /** + * 是否支持事务嵌套 + * @return bool + */ + protected function supportSavepoint(): bool + { + return false; + } + + /** + * 生成定义保存点的SQL + * @access protected + * @param string $name 标识 + * @return string + */ + protected function parseSavepoint(string $name): string + { + return 'SAVEPOINT ' . $name; + } + + /** + * 生成回滚到保存点的SQL + * @access protected + * @param string $name 标识 + * @return string + */ + protected function parseSavepointRollBack(string $name): string + { + return 'ROLLBACK TO SAVEPOINT ' . $name; + } + + /** + * 批处理执行SQL语句 + * 批处理的指令都认为是execute操作 + * @access public + * @param BaseQuery $query 查询对象 + * @param array $sqlArray SQL批处理指令 + * @param array $bind 参数绑定 + * @return bool + */ + public function batchQuery(BaseQuery $query, array $sqlArray = [], array $bind = []): bool + { + // 自动启动事务支持 + $this->startTrans(); + + try { + foreach ($sqlArray as $sql) { + $this->execute($query, $sql, $bind); + } + // 提交事务 + $this->commit(); + } catch (\Exception $e) { + $this->rollback(); + throw $e; + } + + return true; + } + + /** + * 关闭数据库(或者重新连接) + * @access public + * @return $this + */ + public function close() + { + $this->linkID = null; + $this->linkWrite = null; + $this->linkRead = null; + $this->links = []; + + $this->free(); + + return $this; + } + + /** + * 是否断线 + * @access protected + * @param \PDOException|\Exception $e 异常对象 + * @return bool + */ + protected function isBreak($e): bool + { + if (!$this->config['break_reconnect']) { + return false; + } + + $error = $e->getMessage(); + + foreach ($this->breakMatchStr as $msg) { + if (false !== stripos($error, $msg)) { + return true; + } + } + + return false; + } + + /** + * 获取最近一次查询的sql语句 + * @access public + * @return string + */ + public function getLastSql(): string + { + return $this->getRealSql($this->queryStr, $this->bind); + } + + /** + * 获取最近插入的ID + * @access public + * @param BaseQuery $query 查询对象 + * @param string $sequence 自增序列名 + * @return mixed + */ + public function getLastInsID(BaseQuery $query, string $sequence = null) + { + $insertId = $this->linkID->lastInsertId($sequence); + + return $this->autoInsIDType($query, $insertId); + } + + /** + * 获取最近插入的ID + * @access public + * @param BaseQuery $query 查询对象 + * @param string $insertId 自增ID + * @return mixed + */ + protected function autoInsIDType(BaseQuery $query, string $insertId) + { + $pk = $query->getPk(); + + if (is_string($pk)) { + $type = $this->getFieldBindType($pk); + + if (PDO::PARAM_INT == $type) { + $insertId = (int) $insertId; + } elseif (self::PARAM_FLOAT == $type) { + $insertId = (float) $insertId; + } + } + + return $insertId; + } + + /** + * 获取返回或者影响的记录数 + * @access public + * @return integer + */ + public function getNumRows(): int + { + return $this->numRows; + } + + /** + * 获取最近的错误信息 + * @access public + * @return string + */ + public function getError(): string + { + if ($this->PDOStatement) { + $error = $this->PDOStatement->errorInfo(); + $error = $error[1] . ':' . $error[2]; + } else { + $error = ''; + } + + if ('' != $this->queryStr) { + $error .= "\n [ SQL语句 ] : " . $this->getLastsql(); + } + + return $error; + } + + /** + * 数据库调试 记录当前SQL及分析性能 + * @access protected + * @param boolean $start 调试开始标记 true 开始 false 结束 + * @param string $sql 执行的SQL语句 留空自动获取 + * @param bool $master 主从标记 + * @return void + */ + protected function debug(bool $start, string $sql = '', bool $master = false): void + { + if (!empty($this->config['debug'])) { + // 开启数据库调试模式 + if ($start) { + $this->queryStartTime = microtime(true); + } else { + // 记录操作结束时间 + $runtime = number_format((microtime(true) - $this->queryStartTime), 6); + $sql = $sql ?: $this->getLastsql(); + $result = []; + + // SQL性能分析 + if ($this->config['sql_explain'] && 0 === stripos(trim($sql), 'select')) { + $result = $this->getExplain($sql); + } + + // SQL监听 + $this->triggerSql($sql, $runtime, $result, $master); + } + } + } + + /** + * 触发SQL事件 + * @access protected + * @param string $sql SQL语句 + * @param string $runtime SQL运行时间 + * @param mixed $explain SQL分析 + * @param bool $master 主从标记 + * @return void + */ + protected function triggerSql(string $sql, string $runtime, array $explain = [], bool $master = false): void + { + $listen = $this->db->getListen(); + if (!empty($listen)) { + foreach ($listen as $callback) { + if (is_callable($callback)) { + $callback($sql, $runtime, $explain, $master); + } + } + } else { + if ($this->config['deploy']) { + // 分布式记录当前操作的主从 + $master = $master ? 'master|' : 'slave|'; + } else { + $master = ''; + } + + // 未注册监听则记录到日志中 + $this->log($sql . ' [ ' . $master . 'RunTime:' . $runtime . 's ]'); + + if (!empty($explain)) { + $this->log('[ EXPLAIN : ' . var_export($explain, true) . ' ]'); + } + } + } + + /** + * 初始化数据库连接 + * @access protected + * @param boolean $master 是否主服务器 + * @return void + */ + protected function initConnect(bool $master = true): void + { + if (!empty($this->config['deploy'])) { + // 采用分布式数据库 + if ($master || $this->transTimes) { + if (!$this->linkWrite) { + $this->linkWrite = $this->multiConnect(true); + } + + $this->linkID = $this->linkWrite; + } else { + if (!$this->linkRead) { + $this->linkRead = $this->multiConnect(false); + } + + $this->linkID = $this->linkRead; + } + } elseif (!$this->linkID) { + // 默认单数据库 + $this->linkID = $this->connect(); + } + } + + /** + * 连接分布式服务器 + * @access protected + * @param boolean $master 主服务器 + * @return PDO + */ + protected function multiConnect(bool $master = false): PDO + { + $config = []; + + // 分布式数据库配置解析 + foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { + $config[$name] = is_string($this->config[$name]) ? explode(',', $this->config[$name]) : $this->config[$name]; + } + + // 主服务器序号 + $m = floor(mt_rand(0, $this->config['master_num'] - 1)); + + if ($this->config['rw_separate']) { + // 主从式采用读写分离 + if ($master) // 主服务器写入 + { + $r = $m; + } elseif (is_numeric($this->config['slave_no'])) { + // 指定服务器读 + $r = $this->config['slave_no']; + } else { + // 读操作连接从服务器 每次随机连接的数据库 + $r = floor(mt_rand($this->config['master_num'], count($config['hostname']) - 1)); + } + } else { + // 读写操作不区分服务器 每次随机连接的数据库 + $r = floor(mt_rand(0, count($config['hostname']) - 1)); + } + $dbMaster = false; + + if ($m != $r) { + $dbMaster = []; + foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { + $dbMaster[$name] = $config[$name][$m] ?? $config[$name][0]; + } + } + + $dbConfig = []; + + foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { + $dbConfig[$name] = $config[$name][$r] ?? $config[$name][0]; + } + + return $this->connect($dbConfig, $r, $r == $m ? false : $dbMaster); + } + + /** + * 分析缓存 + * @access protected + * @param BaseQuery $query 查询对象 + * @param array $cache 缓存信息 + * @return CacheItem + */ + protected function parseCache(BaseQuery $query, array $cache): CacheItem + { + list($key, $expire, $tag) = $cache; + + if ($key instanceof CacheItem) { + $cacheItem = $key; + } else { + if (true === $key) { + if (!empty($query->getOptions('key'))) { + $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); + } else { + $key = md5($this->getConfig('database') . serialize($query->getOptions()) . serialize($query->getBind(false))); + } + } + + $cacheItem = new CacheItem($key); + $cacheItem->expire($expire); + $cacheItem->tag($tag); + } + + return $cacheItem; + } + +} diff --git a/src/db/Query.php b/src/db/Query.php index 5a9b677..d7bd3fa 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -12,3566 +12,10 @@ declare (strict_types = 1); namespace think\db; -use Closure; -use PDO; -use PDOStatement; -use think\Collection; -use think\Db; -use think\db\exception\BindParamException; -use think\db\exception\DataNotFoundException; -use think\db\exception\ModelNotFoundException; -use think\Exception; -use think\exception\DbException; -use think\exception\PDOException; -use think\Model; -use think\model\Collection as ModelCollection; -use think\model\Relation; -use think\model\relation\OneToOne; -use think\Paginator; - /** * 数据查询类 */ -class Query +class Query extends BaseQuery { - /** - * 当前数据库连接对象 - * @var Connection - */ - protected $connection; - - /** - * 当前模型对象 - * @var Model - */ - protected $model; - - /** - * Db对象 - * @var Db - */ - protected $db; - - /** - * 当前数据表名称(不含前缀) - * @var string - */ - protected $name = ''; - - /** - * 当前数据表主键 - * @var string|array - */ - protected $pk; - - /** - * 当前数据表前缀 - * @var string - */ - protected $prefix = ''; - - /** - * 当前查询参数 - * @var array - */ - protected $options = []; - - /** - * 当前参数绑定 - * @var array - */ - protected $bind = []; - - /** - * 日期查询表达式 - * @var array - */ - protected $timeRule = [ - 'today' => ['today', 'tomorrow'], - 'yesterday' => ['yesterday', 'today'], - 'week' => ['this week 00:00:00', 'next week 00:00:00'], - 'last week' => ['last week 00:00:00', 'this week 00:00:00'], - 'month' => ['first Day of this month 00:00:00', 'first Day of next month 00:00:00'], - 'last month' => ['first Day of last month 00:00:00', 'first Day of this month 00:00:00'], - 'year' => ['this year 1/1', 'next year 1/1'], - 'last year' => ['last year 1/1', 'this year 1/1'], - ]; - - /** - * 架构函数 - * @access public - * @param Connection $connection 数据库连接对象 - */ - public function __construct(Connection $connection) - { - $this->connection = $connection; - - $this->prefix = $this->connection->getConfig('prefix'); - } - - /** - * 创建一个新的查询对象 - * @access public - * @return Query - */ - public function newQuery() - { - $query = new static($this->connection); - - if ($this->model) { - $query->model($this->model); - } - - if (isset($this->options['table'])) { - $query->table($this->options['table']); - } else { - $query->name($this->name); - } - - $query->setDb($this->db); - - return $query; - } - - /** - * 利用__call方法实现一些特殊的Model方法 - * @access public - * @param string $method 方法名称 - * @param array $args 调用参数 - * @return mixed - * @throws DbException - * @throws Exception - */ - public function __call(string $method, array $args) - { - if (strtolower(substr($method, 0, 5)) == 'getby') { - // 根据某个字段获取记录 - $field = $this->db->parseName(substr($method, 5)); - return $this->where($field, '=', $args[0])->find(); - } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { - // 根据某个字段获取记录的某个值 - $name = $this->db->parseName(substr($method, 10)); - return $this->where($name, '=', $args[0])->value($args[1]); - } elseif (strtolower(substr($method, 0, 7)) == 'whereor') { - $name = $this->db->parseName(substr($method, 7)); - array_unshift($args, $name); - return call_user_func_array([$this, 'whereOr'], $args); - } elseif (strtolower(substr($method, 0, 5)) == 'where') { - $name = $this->db->parseName(substr($method, 5)); - array_unshift($args, $name); - return call_user_func_array([$this, 'where'], $args); - } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { - // 动态调用命名范围 - $method = 'scope' . $method; - array_unshift($args, $this); - - call_user_func_array([$this->model, $method], $args); - return $this; - } else { - throw new Exception('method not exist:' . static::class . '->' . $method); - } - } - - /** - * 获取当前的数据库Connection对象 - * @access public - * @return Connection - */ - public function getConnection() - { - return $this->connection; - } - - /** - * 设置当前的数据库Connection对象 - * @access public - * @param Connection $connection 数据库连接对象 - * @return $this - */ - public function setConnection($connection) - { - $this->connection = $connection; - - return $this; - } - - /** - * 设置Db对象 - * @access public - * @param Db $db - * @return $this - */ - public function setDb(Db $db) - { - $this->db = $db; - $this->connection->setDb($db); - return $this; - } - - /** - * 获取Db对象 - * @access public - * @return Db - */ - public function getDb() - { - return $this->db; - } - - /** - * 指定模型 - * @access public - * @param Model $model 模型对象实例 - * @return $this - */ - public function model(Model $model) - { - $this->model = $model; - return $this; - } - - /** - * 获取当前的模型对象 - * @access public - * @param bool $clear 是否需要清空查询条件 - * @return Model|null - */ - public function getModel(bool $clear = true) - { - return $this->model ? $this->model->setQuery($this, $clear) : null; - } - - /** - * 指定当前数据表名(不含前缀) - * @access public - * @param string $name 不含前缀的数据表名字 - * @return $this - */ - public function name(string $name) - { - $this->name = $name; - return $this; - } - - /** - * 获取当前的数据表名称 - * @access public - * @return string - */ - public function getName(): string - { - return $this->name ?: $this->model->getName(); - } - - /** - * 获取数据库的配置参数 - * @access public - * @param string $name 参数名称 - * @return mixed - */ - public function getConfig(string $name = '') - { - return $this->connection->getConfig($name); - } - - /** - * 得到当前或者指定名称的数据表 - * @access public - * @param string $name 不含前缀的数据表名字 - * @return mixed - */ - public function getTable(string $name = '') - { - if (empty($name) && isset($this->options['table'])) { - return $this->options['table']; - } - - $name = $name ?: $this->name; - - return $this->prefix . $this->db->parseName($name); - } - - /** - * 获取数据表字段信息 - * @access public - * @param string $tableName 数据表名 - * @return array - */ - public function getTableFields($tableName = ''): array - { - if ('' == $tableName) { - $tableName = $this->getTable(); - } - - return $this->connection->getTableFields($tableName); - } - - /** - * 设置字段类型信息 - * @access public - * @param array $type 字段类型信息 - * @return $this - */ - public function setFieldType(array $type) - { - $this->options['field_type'] = $type; - return $this; - } - - /** - * 获取详细字段类型信息 - * @access public - * @return array - */ - public function getFields(): array - { - return $this->connection->getFields($this->getTable()); - } - - /** - * 获取字段类型信息 - * @access public - * @return array - */ - public function getFieldsType(): array - { - if (!empty($this->options['field_type'])) { - return $this->options['field_type']; - } - - return $this->connection->getFieldsType($this->getTable()); - } - - /** - * 获取字段类型信息 - * @access public - * @param string $field 字段名 - * @return string|null - */ - public function getFieldType(string $field) - { - $fieldType = $this->getFieldsType(); - - return $fieldType[$field] ?? null; - } - - /** - * 获取字段类型信息 - * @access public - * @return array - */ - public function getFieldsBindType(): array - { - $fieldType = $this->getFieldsType(); - - return array_map([$this->connection, 'getFieldBindType'], $fieldType); - } - - /** - * 获取字段类型信息 - * @access public - * @param string $field 字段名 - * @return int - */ - public function getFieldBindType(string $field): int - { - $fieldType = $this->getFieldType($field); - - return $this->connection->getFieldBindType($fieldType ?: ''); - } - - /** - * 执行查询 返回数据集 - * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @return array - * @throws BindParamException - * @throws PDOException - */ - public function query(string $sql, array $bind = []): array - { - return $this->connection->query($this, $sql, $bind, true); - } - - /** - * 执行语句 - * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @return int - * @throws BindParamException - * @throws PDOException - */ - public function execute(string $sql, array $bind = []): int - { - return $this->connection->execute($this, $sql, $bind, true); - } - - /** - * 获取最近插入的ID - * @access public - * @param string $sequence 自增序列名 - * @return mixed - */ - public function getLastInsID(string $sequence = null) - { - $insertId = $this->connection->getLastInsID($sequence); - - $pk = $this->getPk(); - - if (is_string($pk)) { - $type = $this->getFieldBindType($pk); - - if (PDO::PARAM_INT == $type) { - $insertId = (int) $insertId; - } elseif (Connection::PARAM_FLOAT == $type) { - $insertId = (float) $insertId; - } - } - - return $insertId; - } - - /** - * 获取返回或者影响的记录数 - * @access public - * @return integer - */ - public function getNumRows(): int - { - return $this->connection->getNumRows(); - } - - /** - * 获取最近一次查询的sql语句 - * @access public - * @return string - */ - public function getLastSql(): string - { - return $this->connection->getLastSql(); - } - - /** - * 获取sql记录 - * @access public - * @return string - */ - public function getSqlLog() - { - return $this->connection->getSqlLog(); - } - - /** - * 执行数据库事务 - * @access public - * @param callable $callback 数据操作方法回调 - * @return mixed - */ - public function transaction(callable $callback) - { - return $this->connection->transaction($callback); - } - - /** - * 启动事务 - * @access public - * @return void - */ - public function startTrans(): void - { - $this->connection->startTrans(); - } - - /** - * 用于非自动提交状态下面的查询提交 - * @access public - * @return void - * @throws PDOException - */ - public function commit(): void - { - $this->connection->commit(); - } - - /** - * 事务回滚 - * @access public - * @return void - * @throws PDOException - */ - public function rollback(): void - { - $this->connection->rollback(); - } - - /** - * 批处理执行SQL语句 - * 批处理的指令都认为是execute操作 - * @access public - * @param array $sql SQL批处理指令 - * @return bool - */ - public function batchQuery(array $sql = []): bool - { - return $this->connection->batchQuery($this, $sql); - } - - /** - * 得到某个字段的值 - * @access public - * @param string $field 字段名 - * @param mixed $default 默认值 - * @return mixed - */ - public function value(string $field, $default = null) - { - return $this->connection->value($this, $field, $default); - } - - /** - * 得到某个列的数组 - * @access public - * @param string $field 字段名 多个字段用逗号分隔 - * @param string $key 索引 - * @return array - */ - public function column(string $field, string $key = ''): array - { - return $this->connection->column($this, $field, $key); - } - - /** - * 聚合查询 - * @access protected - * @param string $aggregate 聚合方法 - * @param string|Raw $field 字段名 - * @param bool $force 强制转为数字类型 - * @return mixed - */ - protected function aggregate(string $aggregate, $field, bool $force = false) - { - return $this->connection->aggregate($this, $aggregate, $field, $force); - } - - /** - * COUNT查询 - * @access public - * @param string|Raw $field 字段名 - * @return int - */ - public function count(string $field = '*'): int - { - if (!empty($this->options['group'])) { - // 支持GROUP - $options = $this->getOptions(); - $subSql = $this->options($options)->field('count(' . $field . ') AS think_count')->bind($this->bind)->buildSql(); - - $query = $this->newQuery()->table([$subSql => '_group_count_']); - - $count = $query->aggregate('COUNT', '*'); - } else { - $count = $this->aggregate('COUNT', $field); - } - - return (int) $count; - } - - /** - * SUM查询 - * @access public - * @param string|Raw $field 字段名 - * @return float - */ - public function sum($field): float - { - return $this->aggregate('SUM', $field, true); - } - - /** - * MIN查询 - * @access public - * @param string|Raw $field 字段名 - * @param bool $force 强制转为数字类型 - * @return mixed - */ - public function min($field, bool $force = true) - { - return $this->aggregate('MIN', $field, $force); - } - - /** - * MAX查询 - * @access public - * @param string|Raw $field 字段名 - * @param bool $force 强制转为数字类型 - * @return mixed - */ - public function max($field, bool $force = true) - { - return $this->aggregate('MAX', $field, $force); - } - - /** - * AVG查询 - * @access public - * @param string|Raw $field 字段名 - * @return float - */ - public function avg($field): float - { - return $this->aggregate('AVG', $field, true); - } - - /** - * 查询SQL组装 join - * @access public - * @param mixed $join 关联的表名 - * @param mixed $condition 条件 - * @param string $type JOIN类型 - * @param array $bind 参数绑定 - * @return $this - */ - public function join($join, string $condition = null, string $type = 'INNER', array $bind = []) - { - $table = $this->getJoinTable($join); - - if (!empty($bind) && $condition) { - $this->bindParams($condition, $bind); - } - - $this->options['join'][] = [$table, strtoupper($type), $condition]; - - return $this; - } - - /** - * LEFT JOIN - * @access public - * @param mixed $join 关联的表名 - * @param mixed $condition 条件 - * @param array $bind 参数绑定 - * @return $this - */ - public function leftJoin($join, string $condition = null, array $bind = []) - { - return $this->join($join, $condition, 'LEFT', $bind); - } - - /** - * RIGHT JOIN - * @access public - * @param mixed $join 关联的表名 - * @param mixed $condition 条件 - * @param array $bind 参数绑定 - * @return $this - */ - public function rightJoin($join, string $condition = null, array $bind = []) - { - return $this->join($join, $condition, 'RIGHT', $bind); - } - - /** - * FULL JOIN - * @access public - * @param mixed $join 关联的表名 - * @param mixed $condition 条件 - * @param array $bind 参数绑定 - * @return $this - */ - public function fullJoin($join, string $condition = null, array $bind = []) - { - return $this->join($join, $condition, 'FULL'); - } - - /** - * 获取Join表名及别名 支持 - * ['prefix_table或者子查询'=>'alias'] 'table alias' - * @access protected - * @param array|string|Raw $join JION表名 - * @param string $alias 别名 - * @return string|array - */ - protected function getJoinTable($join, &$alias = null) - { - if (is_array($join)) { - $table = $join; - $alias = array_shift($join); - return $table; - } elseif ($join instanceof Raw) { - return $join; - } - - $join = trim($join); - - if (false !== strpos($join, '(')) { - // 使用子查询 - $table = $join; - } else { - // 使用别名 - if (strpos($join, ' ')) { - // 使用别名 - list($table, $alias) = explode(' ', $join); - } else { - $table = $join; - if (false === strpos($join, '.')) { - $alias = $join; - } - } - - if ($this->prefix && false === strpos($table, '.') && 0 !== strpos($table, $this->prefix)) { - $table = $this->getTable($table); - } - } - - if (!empty($alias) && $table != $alias) { - $table = [$table => $alias]; - } - - return $table; - } - - /** - * 查询SQL组装 union - * @access public - * @param mixed $union UNION - * @param boolean $all 是否适用UNION ALL - * @return $this - */ - public function union($union, bool $all = false) - { - $this->options['union']['type'] = $all ? 'UNION ALL' : 'UNION'; - - if (is_array($union)) { - $this->options['union'] = array_merge($this->options['union'], $union); - } else { - $this->options['union'][] = $union; - } - - return $this; - } - - /** - * 查询SQL组装 union all - * @access public - * @param mixed $union UNION数据 - * @return $this - */ - public function unionAll($union) - { - return $this->union($union, true); - } - - /** - * 指定查询字段 - * @access public - * @param mixed $field 字段信息 - * @return $this - */ - public function field($field) - { - if (empty($field)) { - return $this; - } elseif ($field instanceof Raw) { - $this->options['field'][] = $field; - return $this; - } - - if (is_string($field)) { - if (preg_match('/[\<\'\"\(]/', $field)) { - return $this->fieldRaw($field); - } - - $field = array_map('trim', explode(',', $field)); - } - - if (true === $field) { - // 获取全部字段 - $fields = $this->getTableFields(); - $field = $fields ?: ['*']; - } - - if (isset($this->options['field'])) { - $field = array_merge((array) $this->options['field'], $field); - } - - $this->options['field'] = array_unique($field); - - return $this; - } - - /** - * 指定要排除的查询字段 - * @access public - * @param array|string $field 要排除的字段 - * @return $this - */ - public function withoutField($field) - { - if (empty($field)) { - return $this; - } - - if (is_string($field)) { - $field = array_map('trim', explode(',', $field)); - } - - // 字段排除 - $fields = $this->getTableFields(); - $field = $fields ? array_diff($fields, $field) : $field; - - if (isset($this->options['field'])) { - $field = array_merge((array) $this->options['field'], $field); - } - - $this->options['field'] = array_unique($field); - - return $this; - } - - /** - * 指定其它数据表的查询字段 - * @access public - * @param mixed $field 字段信息 - * @param string $tableName 数据表名 - * @param string $prefix 字段前缀 - * @param string $alias 别名前缀 - * @return $this - */ - public function tableField($field, string $tableName, string $prefix = '', string $alias = '') - { - if (empty($field)) { - return $this; - } - - if (is_string($field)) { - $field = array_map('trim', explode(',', $field)); - } - - if (true === $field) { - // 获取全部字段 - $fields = $this->getTableFields($tableName); - $field = $fields ?: ['*']; - } - - // 添加统一的前缀 - $prefix = $prefix ?: $tableName; - foreach ($field as $key => &$val) { - if (is_numeric($key) && $alias) { - $field[$prefix . '.' . $val] = $alias . $val; - unset($field[$key]); - } elseif (is_numeric($key)) { - $val = $prefix . '.' . $val; - } - } - - if (isset($this->options['field'])) { - $field = array_merge((array) $this->options['field'], $field); - } - - $this->options['field'] = array_unique($field); - - return $this; - } - - /** - * 表达式方式指定查询字段 - * @access public - * @param string $field 字段名 - * @return $this - */ - public function fieldRaw(string $field) - { - $this->options['field'][] = new Raw($field); - - return $this; - } - - /** - * 设置数据 - * @access public - * @param array $data 数据 - * @return $this - */ - public function data(array $data) - { - $this->options['data'] = $data; - - return $this; - } - - /** - * 字段值增长 - * @access public - * @param string $field 字段名 - * @param float $step 增长值 - * @param integer $lazyTime 延时时间(s) - * @param string $op INC/DEC - * @return $this - */ - public function inc(string $field, float $step = 1, int $lazyTime = 0, string $op = 'INC') - { - if ($lazyTime > 0) { - // 延迟写入 - $condition = $this->options['where'] ?? []; - - $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); - $step = $this->connection->lazyWrite($op, $guid, $step, $lazyTime); - - if (false === $step) { - return $this; - } - - $op = 'INC'; - } - - $this->options['data'][$field] = [$op, $step]; - - return $this; - } - - /** - * 字段值减少 - * @access public - * @param string $field 字段名 - * @param float $step 增长值 - * @param integer $lazyTime 延时时间(s) - * @return $this - */ - public function dec(string $field, float $step = 1, int $lazyTime = 0) - { - return $this->inc($field, $step, $lazyTime, 'DEC'); - } - - /** - * 使用表达式设置数据 - * @access public - * @param string $field 字段名 - * @param string $value 字段值 - * @return $this - */ - public function exp(string $field, string $value) - { - $this->options['data'][$field] = new Raw($value); - return $this; - } - - /** - * 指定JOIN查询字段 - * @access public - * @param string|array $join 数据表 - * @param string|array $field 查询字段 - * @param string $on JOIN条件 - * @param string $type JOIN类型 - * @param array $bind 参数绑定 - * @return $this - */ - public function view($join, $field = true, $on = null, string $type = 'INNER', array $bind = []) - { - $this->options['view'] = true; - - $fields = []; - $table = $this->getJoinTable($join, $alias); - - if (true === $field) { - $fields = $alias . '.*'; - } else { - if (is_string($field)) { - $field = explode(',', $field); - } - - foreach ($field as $key => $val) { - if (is_numeric($key)) { - $fields[] = $alias . '.' . $val; - - $this->options['map'][$val] = $alias . '.' . $val; - } else { - if (preg_match('/[,=\.\'\"\(\s]/', $key)) { - $name = $key; - } else { - $name = $alias . '.' . $key; - } - - $fields[] = $name . ' AS ' . $val; - - $this->options['map'][$val] = $name; - } - } - } - - $this->field($fields); - - if ($on) { - $this->join($table, $on, $type, $bind); - } else { - $this->table($table); - } - - return $this; - } - - /** - * 指定AND查询条件 - * @access public - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 - * @return $this - */ - public function where($field, $op = null, $condition = null) - { - if ($field instanceof $this) { - $this->options['where'] = $field->getOptions('where'); - $this->bind($field->getBind(false)); - return $this; - } - - $param = func_get_args(); - array_shift($param); - return $this->parseWhereExp('AND', $field, $op, $condition, $param); - } - - /** - * 指定OR查询条件 - * @access public - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 - * @return $this - */ - public function whereOr($field, $op = null, $condition = null) - { - $param = func_get_args(); - array_shift($param); - return $this->parseWhereExp('OR', $field, $op, $condition, $param); - } - - /** - * 指定XOR查询条件 - * @access public - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 - * @return $this - */ - public function whereXor($field, $op = null, $condition = null) - { - $param = func_get_args(); - array_shift($param); - return $this->parseWhereExp('XOR', $field, $op, $condition, $param); - } - - /** - * 指定Null查询条件 - * @access public - * @param mixed $field 查询字段 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereNull(string $field, string $logic = 'AND') - { - return $this->parseWhereExp($logic, $field, 'NULL', null, [], true); - } - - /** - * 指定NotNull查询条件 - * @access public - * @param mixed $field 查询字段 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereNotNull(string $field, string $logic = 'AND') - { - return $this->parseWhereExp($logic, $field, 'NOTNULL', null, [], true); - } - - /** - * 指定Exists查询条件 - * @access public - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereExists($condition, string $logic = 'AND') - { - if (is_string($condition)) { - $condition = new Raw($condition); - } - - $this->options['where'][strtoupper($logic)][] = ['', 'EXISTS', $condition]; - return $this; - } - - /** - * 指定NotExists查询条件 - * @access public - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereNotExists($condition, string $logic = 'AND') - { - if (is_string($condition)) { - $condition = new Raw($condition); - } - - $this->options['where'][strtoupper($logic)][] = ['', 'NOT EXISTS', $condition]; - return $this; - } - - /** - * 指定In查询条件 - * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereIn(string $field, $condition, string $logic = 'AND') - { - return $this->parseWhereExp($logic, $field, 'IN', $condition, [], true); - } - - /** - * 指定NotIn查询条件 - * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereNotIn(string $field, $condition, string $logic = 'AND') - { - return $this->parseWhereExp($logic, $field, 'NOT IN', $condition, [], true); - } - - /** - * 指定Like查询条件 - * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereLike(string $field, $condition, string $logic = 'AND') - { - return $this->parseWhereExp($logic, $field, 'LIKE', $condition, [], true); - } - - /** - * 指定NotLike查询条件 - * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereNotLike(string $field, $condition, string $logic = 'AND') - { - return $this->parseWhereExp($logic, $field, 'NOT LIKE', $condition, [], true); - } - - /** - * 指定Between查询条件 - * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereBetween(string $field, $condition, string $logic = 'AND') - { - return $this->parseWhereExp($logic, $field, 'BETWEEN', $condition, [], true); - } - - /** - * 指定NotBetween查询条件 - * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereNotBetween(string $field, $condition, string $logic = 'AND') - { - return $this->parseWhereExp($logic, $field, 'NOT BETWEEN', $condition, [], true); - } - - /** - * 指定FIND_IN_SET查询条件 - * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereFindInSet(string $field, $condition, string $logic = 'AND') - { - return $this->parseWhereExp($logic, $field, 'FIND IN SET', $condition, [], true); - } - - /** - * 比较两个字段 - * @access public - * @param string $field1 查询字段 - * @param string $operator 比较操作符 - * @param string $field2 比较字段 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereColumn(string $field1, string $operator, string $field2 = null, string $logic = 'AND') - { - if (is_null($field2)) { - $field2 = $operator; - $operator = '='; - } - - return $this->parseWhereExp($logic, $field1, 'COLUMN', [$operator, $field2], [], true); - } - - /** - * 设置软删除字段及条件 - * @access public - * @param string $field 查询字段 - * @param mixed $condition 查询条件 - * @return $this - */ - public function useSoftDelete(string $field, $condition = null) - { - if ($field) { - $this->options['soft_delete'] = [$field, $condition]; - } - - return $this; - } - - /** - * 指定Exp查询条件 - * @access public - * @param mixed $field 查询字段 - * @param string $where 查询条件 - * @param array $bind 参数绑定 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereExp(string $field, string $where, array $bind = [], string $logic = 'AND') - { - if (!empty($bind)) { - $this->bindParams($where, $bind); - } - - $this->options['where'][$logic][] = [$field, 'EXP', new Raw($where)]; - - return $this; - } - - /** - * 指定字段Raw查询 - * @access public - * @param string $field 查询字段表达式 - * @param mixed $op 查询表达式 - * @param string $condition 查询条件 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereFieldRaw(string $field, $op, $condition = null, string $logic = 'AND') - { - if (is_null($condition)) { - $condition = $op; - $op = '='; - } - - $this->options['where'][$logic][] = [new Raw($field), $op, $condition]; - return $this; - } - - /** - * 指定表达式查询条件 - * @access public - * @param string $where 查询条件 - * @param array $bind 参数绑定 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function whereRaw(string $where, array $bind = [], string $logic = 'AND') - { - if (!empty($bind)) { - $this->bindParams($where, $bind); - } - - $this->options['where'][$logic][] = new Raw($where); - - return $this; - } - - /** - * 指定表达式查询条件 OR - * @access public - * @param string $where 查询条件 - * @param array $bind 参数绑定 - * @return $this - */ - public function whereOrRaw(string $where, array $bind = []) - { - return $this->whereRaw($where, $bind, 'OR'); - } - - /** - * 分析查询表达式 - * @access protected - * @param string $logic 查询逻辑 and or xor - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 - * @param array $param 查询参数 - * @param bool $strict 严格模式 - * @return $this - */ - protected function parseWhereExp(string $logic, $field, $op, $condition, array $param = [], bool $strict = false) - { - $logic = strtoupper($logic); - - if (is_string($field) && !empty($this->options['via']) && false === strpos($field, '.')) { - $field = $this->options['via'] . '.' . $field; - } - - if ($field instanceof Raw) { - return $this->whereRaw($field, is_array($op) ? $op : [], $logic); - } elseif ($strict) { - // 使用严格模式查询 - if ('=' == $op) { - $where = $this->whereEq($field, $condition); - } else { - $where = [$field, $op, $condition, $logic]; - } - } elseif (is_array($field)) { - // 解析数组批量查询 - return $this->parseArrayWhereItems($field, $logic); - } elseif ($field instanceof Closure) { - $where = $field; - } elseif (is_string($field)) { - if (preg_match('/[,=\<\'\"\(\s]/', $field)) { - return $this->whereRaw($field, is_array($op) ? $op : [], $logic); - } elseif (is_string($op) && strtolower($op) == 'exp') { - $bind = isset($param[2]) && is_array($param[2]) ? $param[2] : []; - return $this->whereExp($field, $condition, $bind, $logic); - } - - $where = $this->parseWhereItem($logic, $field, $op, $condition, $param); - } - - if (!empty($where)) { - $this->options['where'][$logic][] = $where; - } - - return $this; - } - - /** - * 分析查询表达式 - * @access protected - * @param string $logic 查询逻辑 and or xor - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 - * @param array $param 查询参数 - * @return array - */ - protected function parseWhereItem(string $logic, $field, $op, $condition, array $param = []): array - { - if (is_array($op)) { - // 同一字段多条件查询 - array_unshift($param, $field); - $where = $param; - } elseif ($field && is_null($condition)) { - if (is_string($op) && in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) { - // null查询 - $where = [$field, $op, '']; - } elseif ('=' === $op || is_null($op)) { - $where = [$field, 'NULL', '']; - } elseif ('<>' === $op) { - $where = [$field, 'NOTNULL', '']; - } else { - // 字段相等查询 - $where = $this->whereEq($field, $op); - } - } elseif (in_array(strtoupper($op), ['EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) { - $where = [$field, $op, is_string($condition) ? new Raw($condition) : $condition]; - } else { - $where = $field ? [$field, $op, $condition, $param[2] ?? null] : []; - } - - return $where; - } - - /** - * 相等查询的主键处理 - * @access protected - * @param string $field 字段名 - * @param mixed $value 字段值 - * @return array - */ - protected function whereEq(string $field, $value): array - { - $where = [$field, '=', $value]; - if ($this->getPk() == $field) { - $this->options['key'] = $value; - } - - return $where; - } - - /** - * 数组批量查询 - * @access protected - * @param array $field 批量查询 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - protected function parseArrayWhereItems(array $field, string $logic) - { - if (key($field) !== 0) { - $where = []; - foreach ($field as $key => $val) { - if ($val instanceof Raw) { - $where[] = [$key, 'exp', $val]; - } else { - $where[] = is_null($val) ? [$key, 'NULL', ''] : [$key, is_array($val) ? 'IN' : '=', $val]; - } - } - } else { - // 数组批量查询 - $where = $field; - } - - if (!empty($where)) { - $this->options['where'][$logic] = isset($this->options['where'][$logic]) ? array_merge($this->options['where'][$logic], $where) : $where; - } - - return $this; - } - - /** - * 去除某个查询条件 - * @access public - * @param string $field 查询字段 - * @param string $logic 查询逻辑 and or xor - * @return $this - */ - public function removeWhereField(string $field, string $logic = 'AND') - { - $logic = strtoupper($logic); - - if (isset($this->options['where'][$logic])) { - foreach ($this->options['where'][$logic] as $key => $val) { - if (is_array($val) && $val[0] == $field) { - unset($this->options['where'][$logic][$key]); - } - } - } - - return $this; - } - - /** - * 去除查询参数 - * @access public - * @param string $option 参数名 留空去除所有参数 - * @return $this - */ - public function removeOption(string $option = '') - { - if ('' === $option) { - $this->options = []; - $this->bind = []; - } elseif (isset($this->options[$option])) { - unset($this->options[$option]); - } - - return $this; - } - - /** - * 条件查询 - * @access public - * @param mixed $condition 满足条件(支持闭包) - * @param Closure|array $query 满足条件后执行的查询表达式(闭包或数组) - * @param Closure|array $otherwise 不满足条件后执行 - * @return $this - */ - public function when($condition, $query, $otherwise = null) - { - if ($condition instanceof Closure) { - $condition = $condition($this); - } - - if ($condition) { - if ($query instanceof Closure) { - $query($this, $condition); - } elseif (is_array($query)) { - $this->where($query); - } - } elseif ($otherwise) { - if ($otherwise instanceof Closure) { - $otherwise($this, $condition); - } elseif (is_array($otherwise)) { - $this->where($otherwise); - } - } - - return $this; - } - - /** - * 指定查询数量 - * @access public - * @param int $offset 起始位置 - * @param int $length 查询数量 - * @return $this - */ - public function limit(int $offset, int $length = null) - { - $this->options['limit'] = $offset . ($length ? ',' . $length : ''); - - return $this; - } - - /** - * 指定分页 - * @access public - * @param int $page 页数 - * @param int $listRows 每页数量 - * @return $this - */ - public function page(int $page, int $listRows = null) - { - $this->options['page'] = [$page, $listRows]; - - return $this; - } - - /** - * 分页查询 - * @access public - * @param int|array $listRows 每页数量 数组表示配置参数 - * @param int|bool $simple 是否简洁模式或者总记录数 - * @param array $config 配置参数 - * @return Paginator - * @throws DbException - */ - public function paginate($listRows = null, $simple = false, $config = []) - { - if (is_int($simple)) { - $total = $simple; - $simple = false; - } - - $defaultConfig = $this->getConfig('paginate'); - if (is_array($listRows)) { - $config = array_merge($defaultConfig, $listRows); - $listRows = intval($config['list_rows']); - } else { - $config = array_merge($defaultConfig, $config); - $listRows = intval($listRows ?: $config['list_rows']); - } - - $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\paginator\\driver\\' . ucwords($config['type']); - $page = isset($config['page']) ? (int) $config['page'] : call_user_func([ - $class, - 'getCurrentPage', - ], $config['var_page']); - - $page = $page < 1 ? 1 : $page; - - $config['path'] = $config['path'] ?? call_user_func([$class, 'getCurrentPath']); - - if (!isset($total) && !$simple) { - $options = $this->getOptions(); - - unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']); - - $bind = $this->bind; - $total = $this->count(); - $results = $this->options($options)->bind($bind)->page($page, $listRows)->select(); - } elseif ($simple) { - $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select(); - $total = null; - } else { - $results = $this->page($page, $listRows)->select(); - } - - $this->removeOption('limit'); - $this->removeOption('page'); - - return $class::make($results, $listRows, $page, $total, $simple, $config); - } - - /** - * 表达式方式指定当前操作的数据表 - * @access public - * @param mixed $table 表名 - * @return $this - */ - public function tableRaw(string $table) - { - $this->options['table'] = new Raw($table); - - return $this; - } - - /** - * 指定当前操作的数据表 - * @access public - * @param mixed $table 表名 - * @return $this - */ - public function table($table) - { - if (is_string($table)) { - if (strpos($table, ')')) { - // 子查询 - } elseif (false === strpos($table, ',')) { - if (strpos($table, ' ')) { - list($item, $alias) = explode(' ', $table); - $table = []; - $this->alias([$item => $alias]); - $table[$item] = $alias; - } - } else { - $tables = explode(',', $table); - $table = []; - - foreach ($tables as $item) { - $item = trim($item); - if (strpos($item, ' ')) { - list($item, $alias) = explode(' ', $item); - $this->alias([$item => $alias]); - $table[$item] = $alias; - } else { - $table[] = $item; - } - } - } - } elseif (is_array($table)) { - $tables = $table; - $table = []; - - foreach ($tables as $key => $val) { - if (is_numeric($key)) { - $table[] = $val; - } else { - $this->alias([$key => $val]); - $table[$key] = $val; - } - } - } - - $this->options['table'] = $table; - - return $this; - } - - /** - * USING支持 用于多表删除 - * @access public - * @param mixed $using USING - * @return $this - */ - public function using($using) - { - $this->options['using'] = $using; - return $this; - } - - /** - * 存储过程调用 - * @access public - * @param bool $procedure 是否为存储过程查询 - * @return $this - */ - public function procedure(bool $procedure = true) - { - $this->options['procedure'] = $procedure; - return $this; - } - - /** - * 是否允许返回空数据(或空模型) - * @access public - * @param bool $allowEmpty 是否允许为空 - * @return $this - */ - public function allowEmpty(bool $allowEmpty = true) - { - $this->options['allow_empty'] = $allowEmpty; - return $this; - } - - /** - * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc']) - * @access public - * @param string|array|Raw $field 排序字段 - * @param string $order 排序 - * @return $this - */ - public function order($field, string $order = '') - { - if (empty($field)) { - return $this; - } elseif ($field instanceof Raw) { - $this->options['order'][] = $field; - return $this; - } - - if (is_string($field)) { - if (!empty($this->options['via'])) { - $field = $this->options['via'] . '.' . $field; - } - if (strpos($field, ',')) { - $field = array_map('trim', explode(',', $field)); - } else { - $field = empty($order) ? $field : [$field => $order]; - } - } elseif (!empty($this->options['via'])) { - foreach ($field as $key => $val) { - if (is_numeric($key)) { - $field[$key] = $this->options['via'] . '.' . $val; - } else { - $field[$this->options['via'] . '.' . $key] = $val; - unset($field[$key]); - } - } - } - - if (!isset($this->options['order'])) { - $this->options['order'] = []; - } - - if (is_array($field)) { - $this->options['order'] = array_merge($this->options['order'], $field); - } else { - $this->options['order'][] = $field; - } - - return $this; - } - - /** - * 表达式方式指定Field排序 - * @access public - * @param string $field 排序字段 - * @param array $bind 参数绑定 - * @return $this - */ - public function orderRaw(string $field, array $bind = []) - { - if (!empty($bind)) { - $this->bindParams($field, $bind); - } - - $this->options['order'][] = new Raw($field); - - return $this; - } - - /** - * 指定Field排序 orderField('id',[1,2,3],'desc') - * @access public - * @param string $field 排序字段 - * @param array $values 排序值 - * @param string $order 排序 desc/asc - * @return $this - */ - public function orderField(string $field, array $values, string $order = '') - { - if (!empty($values)) { - $values['sort'] = $order; - - $this->options['order'][$field] = $values; - } - - return $this; - } - - /** - * 随机排序 - * @access public - * @return $this - */ - public function orderRand() - { - $this->options['order'][] = '[rand]'; - return $this; - } - - /** - * 查询缓存 - * @access public - * @param mixed $key 缓存key - * @param integer|\DateTime $expire 缓存有效期 - * @param string $tag 缓存标签 - * @return $this - */ - public function cache($key = true, $expire = null, string $tag = null) - { - if (false === $key) { - return $this; - } - - if ($key instanceof \DateTimeInterface || (is_int($key) && is_null($expire))) { - $expire = $key; - $key = true; - } - - $this->options['cache'] = [$key, $expire, $tag]; - - return $this; - } - - /** - * 指定group查询 - * @access public - * @param string|array $group GROUP - * @return $this - */ - public function group($group) - { - $this->options['group'] = $group; - return $this; - } - - /** - * 指定having查询 - * @access public - * @param string $having having - * @return $this - */ - public function having(string $having) - { - $this->options['having'] = $having; - return $this; - } - - /** - * 指定查询lock - * @access public - * @param bool|string $lock 是否lock - * @return $this - */ - public function lock($lock = false) - { - $this->options['lock'] = $lock; - - if ($lock) { - $this->options['master'] = true; - } - - return $this; - } - - /** - * 指定distinct查询 - * @access public - * @param bool $distinct 是否唯一 - * @return $this - */ - public function distinct(bool $distinct = true) - { - $this->options['distinct'] = $distinct; - return $this; - } - - /** - * 指定数据表别名 - * @access public - * @param array|string $alias 数据表别名 - * @return $this - */ - public function alias($alias) - { - if (is_array($alias)) { - $this->options['alias'] = $alias; - } else { - $table = $this->getTable(); - - $this->options['alias'][$table] = $alias; - } - - return $this; - } - - /** - * 指定强制索引 - * @access public - * @param string $force 索引名称 - * @return $this - */ - public function force(string $force) - { - $this->options['force'] = $force; - return $this; - } - - /** - * 查询注释 - * @access public - * @param string $comment 注释 - * @return $this - */ - public function comment(string $comment) - { - $this->options['comment'] = $comment; - return $this; - } - - /** - * 获取执行的SQL语句而不进行实际的查询 - * @access public - * @param bool $fetch 是否返回sql - * @return $this|Fetch - */ - public function fetchSql(bool $fetch = true) - { - $this->options['fetch_sql'] = $fetch; - - if ($fetch) { - return new Fetch($this); - } - - return $this; - } - - /** - * 设置是否返回数据集对象 - * @access public - * @param bool|string $collection 是否返回数据集对象 - * @return $this - */ - public function fetchCollection($collection = true) - { - $this->options['collection'] = $collection; - return $this; - } - - /** - * 设置是否返回数组 - * @access public - * @param bool $asArray 是否返回数组 - * @return $this - */ - public function fetchArray(bool $asArray = true) - { - $this->options['array'] = $asArray; - return $this; - } - - /** - * 设置从主服务器读取数据 - * @access public - * @param bool $readMaster 是否从主服务器读取 - * @return $this - */ - public function master(bool $readMaster = true) - { - $this->options['master'] = $readMaster; - return $this; - } - - /** - * 设置是否严格检查字段名 - * @access public - * @param bool $strict 是否严格检查字段 - * @return $this - */ - public function strict(bool $strict = true) - { - $this->options['strict'] = $strict; - return $this; - } - - /** - * 设置查询数据不存在是否抛出异常 - * @access public - * @param bool $fail 数据不存在是否抛出异常 - * @return $this - */ - public function failException(bool $fail = true) - { - $this->options['fail'] = $fail; - return $this; - } - - /** - * 设置自增序列名 - * @access public - * @param string $sequence 自增序列名 - * @return $this - */ - public function sequence(string $sequence = null) - { - $this->options['sequence'] = $sequence; - return $this; - } - - /** - * 设置是否REPLACE - * @access public - * @param bool $replace 是否使用REPLACE写入数据 - * @return $this - */ - public function replace(bool $replace = true) - { - $this->options['replace'] = $replace; - return $this; - } - - /** - * 设置当前查询所在的分区 - * @access public - * @param string|array $partition 分区名称 - * @return $this - */ - public function partition($partition) - { - $this->options['partition'] = $partition; - return $this; - } - - /** - * 设置DUPLICATE - * @access public - * @param array|string|Raw $duplicate DUPLICATE信息 - * @return $this - */ - public function duplicate($duplicate) - { - $this->options['duplicate'] = $duplicate; - return $this; - } - - /** - * 设置查询的额外参数 - * @access public - * @param string $extra 额外信息 - * @return $this - */ - public function extra(string $extra) - { - $this->options['extra'] = $extra; - return $this; - } - - /** - * 设置需要隐藏的输出属性 - * @access public - * @param array $hidden 需要隐藏的字段名 - * @return $this - */ - public function hidden(array $hidden) - { - $this->options['hidden'] = $hidden; - return $this; - } - - /** - * 设置需要输出的属性 - * @access public - * @param array $visible 需要输出的属性 - * @return $this - */ - public function visible(array $visible) - { - $this->options['visible'] = $visible; - return $this; - } - - /** - * 设置需要追加输出的属性 - * @access public - * @param array $append 需要追加的属性 - * @return $this - */ - public function append(array $append) - { - $this->options['append'] = $append; - return $this; - } - - /** - * 设置JSON字段信息 - * @access public - * @param array $json JSON字段 - * @param bool $assoc 是否取出数组 - * @return $this - */ - public function json(array $json = [], bool $assoc = false) - { - $this->options['json'] = $json; - $this->options['json_assoc'] = $assoc; - return $this; - } - - /** - * 添加查询范围 - * @access public - * @param array|string|Closure $scope 查询范围定义 - * @param array $args 参数 - * @return $this - */ - public function scope($scope, ...$args) - { - // 查询范围的第一个参数始终是当前查询对象 - array_unshift($args, $this); - - if ($scope instanceof Closure) { - call_user_func_array($scope, $args); - return $this; - } - - if (is_string($scope)) { - $scope = explode(',', $scope); - } - - if ($this->model) { - // 检查模型类的查询范围方法 - foreach ($scope as $name) { - $method = 'scope' . trim($name); - - if (method_exists($this->model, $method)) { - call_user_func_array([$this->model, $method], $args); - } - } - } - - return $this; - } - - /** - * 指定数据表主键 - * @access public - * @param string $pk 主键 - * @return $this - */ - public function pk(string $pk) - { - $this->pk = $pk; - return $this; - } - - /** - * 添加日期或者时间查询规则 - * @access public - * @param string $name 时间表达式 - * @param string|array $rule 时间范围 - * @return $this - */ - public function timeRule(string $name, $rule) - { - $this->timeRule[$name] = $rule; - return $this; - } - - /** - * 查询日期或者时间 - * @access public - * @param string $field 日期字段名 - * @param string $op 比较运算符或者表达式 - * @param string|array $range 比较范围 - * @param string $logic AND OR - * @return $this - */ - public function whereTime(string $field, string $op, $range = null, string $logic = 'AND') - { - if (is_null($range) && isset($this->timeRule[$op])) { - $range = $this->timeRule[$op]; - $op = 'between'; - } - - return $this->parseWhereExp($logic, $field, strtolower($op) . ' time', $range, [], true); - } - - /** - * 查询某个时间间隔数据 - * @access public - * @param string $field 日期字段名 - * @param string $start 开始时间 - * @param string $interval 时间间隔单位 day/month/year/week/hour/minute/second - * @param int $step 间隔 - * @param string $logic AND OR - * @return $this - */ - public function whereTimeInterval(string $field, string $start, string $interval = 'day', int $step = 1, string $logic = 'AND') - { - $startTime = strtotime($start); - $endTime = strtotime(($step > 0 ? '+' : '-') . abs($step) . ' ' . $interval . (abs($step) > 1 ? 's' : ''), $startTime); - - return $this->whereTime($field, 'between', $step > 0 ? [$startTime, $endTime] : [$endTime, $startTime], $logic); - } - - /** - * 查询月数据 whereMonth('time_field', '2018-1') - * @access public - * @param string $field 日期字段名 - * @param string $month 月份信息 - * @param int $step 间隔 - * @param string $logic AND OR - * @return $this - */ - public function whereMonth(string $field, string $month = 'this month', int $step = 1, string $logic = 'AND') - { - if (in_array($month, ['this month', 'last month'])) { - $month = date('Y-m', strtotime($month)); - } - - return $this->whereTimeInterval($field, $month, 'month', $step, $logic); - } - - /** - * 查询周数据 whereWeek('time_field', '2018-1-1') 从2018-1-1开始的一周数据 - * @access public - * @param string $field 日期字段名 - * @param string $week 周信息 - * @param int $step 间隔 - * @param string $logic AND OR - * @return $this - */ - public function whereWeek(string $field, string $week = 'this week', int $step = 1, string $logic = 'AND') - { - if (in_array($week, ['this week', 'last week'])) { - $week = date('Y-m-d', strtotime($week)); - } - - return $this->whereTimeInterval($field, $week, 'week', $step, $logic); - } - - /** - * 查询年数据 whereYear('time_field', '2018') - * @access public - * @param string $field 日期字段名 - * @param string $year 年份信息 - * @param int $step 间隔 - * @param string $logic AND OR - * @return $this - */ - public function whereYear(string $field, string $year = 'this year', int $step = 1, string $logic = 'AND') - { - if (in_array($year, ['this year', 'last year'])) { - $year = date('Y', strtotime($year)); - } - - return $this->whereTimeInterval($field, $year . '-1-1', 'year', $step, $logic); - } - - /** - * 查询日数据 whereDay('time_field', '2018-1-1') - * @access public - * @param string $field 日期字段名 - * @param string $day 日期信息 - * @param int $step 间隔 - * @param string $logic AND OR - * @return $this - */ - public function whereDay(string $field, string $day = 'today', int $step = 1, string $logic = 'AND') - { - if (in_array($day, ['today', 'yesterday'])) { - $day = date('Y-m-d', strtotime($day)); - } - - return $this->whereTimeInterval($field, $day, 'day', $step, $logic); - } - /** - * 查询日期或者时间范围 whereBetweenTime('time_field', '2018-1-1','2018-1-15') - * @access public - * @param string $field 日期字段名 - * @param string|int $startTime 开始时间 - * @param string|int $endTime 结束时间 - * @param string $logic AND OR - * @return $this - */ - public function whereBetweenTime(string $field, $startTime, $endTime, string $logic = 'AND') - { - return $this->whereTime($field, 'between', [$startTime, $endTime], $logic); - } - - /** - * 查询日期或者时间范围 whereNotBetweenTime('time_field', '2018-1-1','2018-1-15') - * @access public - * @param string $field 日期字段名 - * @param string|int $startTime 开始时间 - * @param string|int $endTime 结束时间 - * @return $this - */ - public function whereNotBetweenTime(string $field, $startTime, $endTime) - { - return $this->whereTime($field, '<', $startTime) - ->whereTime($field, '>', $endTime); - } - - /** - * 查询当前时间在两个时间字段范围 whereBetweenTimeField('start_time', 'end_time') - * @access public - * @param string $startField 开始时间字段 - * @param string $endField 结束时间字段 - * @return $this - */ - public function whereBetweenTimeField(string $startField, string $endField) - { - return $this->whereTime($startField, '<=', time()) - ->whereTime($endField, '>=', time()); - } - - /** - * 查询当前时间不在两个时间字段范围 whereNotBetweenTimeField('start_time', 'end_time') - * @access public - * @param string $startField 开始时间字段 - * @param string $endField 结束时间字段 - * @return $this - */ - public function whereNotBetweenTimeField(string $startField, string $endField) - { - return $this->whereTime($startField, '>', time()) - ->whereTime($endField, '<', time(), 'OR'); - } - - /** - * 获取当前数据表的主键 - * @access public - * @return string|array - */ - public function getPk() - { - if (!empty($this->pk)) { - $pk = $this->pk; - } else { - $this->pk = $pk = $this->connection->getPk($this->getTable()); - } - - return $pk; - } - - /** - * 批量参数绑定 - * @access public - * @param array $value 绑定变量值 - * @return $this - */ - public function bind(array $value) - { - $this->bind = array_merge($this->bind, $value); - return $this; - } - - /** - * 单个参数绑定 - * @access public - * @param mixed $value 绑定变量值 - * @param integer $type 绑定类型 - * @param string $name 绑定标识 - * @return string - */ - public function bindValue($value, int $type = null, string $name = null) - { - $name = $name ?: 'ThinkBind_' . (count($this->bind) + 1) . '_'; - - $this->bind[$name] = [$value, $type ?: PDO::PARAM_STR]; - return $name; - } - - /** - * 检测参数是否已经绑定 - * @access public - * @param string $key 参数名 - * @return bool - */ - public function isBind($key) - { - return isset($this->bind[$key]); - } - - /** - * 参数绑定 - * @access public - * @param string $sql 绑定的sql表达式 - * @param array $bind 参数绑定 - * @return void - */ - protected function bindParams(string &$sql, array $bind = []): void - { - foreach ($bind as $key => $value) { - if (is_array($value)) { - $name = $this->bindValue($value[0], $value[1], $value[2] ?? null); - } else { - $name = $this->bindValue($value); - } - - if (is_numeric($key)) { - $sql = substr_replace($sql, ':' . $name, strpos($sql, '?'), 1); - } else { - $sql = str_replace(':' . $key, ':' . $name, $sql); - } - } - } - - /** - * 查询参数批量赋值 - * @access protected - * @param array $options 表达式参数 - * @return $this - */ - protected function options(array $options) - { - $this->options = $options; - return $this; - } - - /** - * 获取当前的查询参数 - * @access public - * @param string $name 参数名 - * @return mixed - */ - public function getOptions(string $name = '') - { - if ('' === $name) { - return $this->options; - } - - return $this->options[$name] ?? null; - } - - /** - * 设置当前的查询参数 - * @access public - * @param string $option 参数名 - * @param mixed $value 参数值 - * @return $this - */ - public function setOption(string $option, $value) - { - $this->options[$option] = $value; - return $this; - } - - /** - * 设置关联查询 - * @access public - * @param array $relation 关联名称 - * @return $this - */ - public function relation(array $relation) - { - if (!empty($relation)) { - $this->options['relation'] = $relation; - } - - return $this; - } - - /** - * 设置关联查询JOIN预查询 - * @access public - * @param array|string $with 关联方法名称 - * @return $this - */ - public function with($with) - { - if (!empty($with)) { - $this->options['with'] = (array) $with; - } - - return $this; - } - - /** - * 关联预载入 JOIN方式 - * @access protected - * @param array|string $with 关联方法名 - * @param string $joinType JOIN方式 - * @return $this - */ - public function withJoin($with, string $joinType = '') - { - if (empty($with)) { - return $this; - } - - $first = true; - - /** @var Model $class */ - $class = $this->model; - foreach ((array) $with as $key => $relation) { - $closure = null; - $field = true; - - if ($relation instanceof Closure) { - // 支持闭包查询过滤关联条件 - $closure = $relation; - $relation = $key; - } elseif (is_array($relation)) { - $field = $relation; - $relation = $key; - } elseif (is_string($relation) && strpos($relation, '.')) { - $relation = strstr($relation, '.', true); - } - - /** @var Relation $model */ - $relation = $this->db->parseName($relation, 1, false); - $model = $class->$relation(); - - if ($model instanceof OneToOne) { - $model->eagerly($this, $relation, $field, $joinType, $closure, $first); - $first = false; - } else { - // 不支持其它关联 - unset($with[$key]); - } - } - - $this->via(); - - $this->options['with_join'] = $with; - - return $this; - } - - /** - * 设置数据字段获取器 - * @access public - * @param string|array $name 字段名 - * @param callable $callback 闭包获取器 - * @return $this - */ - public function withAttr($name, callable $callback = null) - { - if (is_array($name)) { - $this->options['with_attr'] = $name; - } else { - $this->options['with_attr'][$name] = $callback; - } - - return $this; - } - - /** - * 使用搜索器条件搜索字段 - * @access public - * @param array $fields 搜索字段 - * @param array $data 搜索数据 - * @param string $prefix 字段前缀标识 - * @return $this - */ - public function withSearch(array $fields, array $data = [], string $prefix = '') - { - foreach ($fields as $key => $field) { - if ($field instanceof Closure) { - $field($this, $data[$key] ?? null, $data, $prefix); - } elseif ($this->model) { - // 检测搜索器 - $fieldName = is_numeric($key) ? $field : $key; - $method = 'search' . $this->db->parseName($fieldName, 1) . 'Attr'; - - if (method_exists($this->model, $method)) { - $this->model->$method($this, $data[$field] ?? null, $data, $prefix); - } - } - } - - return $this; - } - - /** - * 关联统计 - * @access protected - * @param array|string $relations 关联方法名 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @param bool $subQuery 是否使用子查询 - * @return $this - */ - protected function withAggregate($relations, string $aggregate = 'count', $field = '*', bool $subQuery = true) - { - if (!$subQuery) { - $this->options['with_count'][] = [$relations, $aggregate, $field]; - } else { - if (!isset($this->options['field'])) { - $this->field('*'); - } - - foreach ((array) $relations as $key => $relation) { - $closure = $aggregateField = null; - - if ($relation instanceof Closure) { - $closure = $relation; - $relation = $key; - } elseif (!is_int($key)) { - $aggregateField = $relation; - $relation = $key; - } - - $relation = $this->db->parseName($relation, 1, false); - - $count = '(' . $this->model->$relation()->getRelationCountQuery($closure, $aggregate, $field, $aggregateField) . ')'; - - if (empty($aggregateField)) { - $aggregateField = $this->db->parseName($relation) . '_' . $aggregate; - } - - $this->field([$count => $aggregateField]); - } - } - - return $this; - } - - /** - * 关联统计 - * @access public - * @param string|array $relation 关联方法名 - * @param bool $subQuery 是否使用子查询 - * @return $this - */ - public function withCount($relation, bool $subQuery = true) - { - return $this->withAggregate($relation, 'count', '*', $subQuery); - } - - /** - * 关联统计Sum - * @access public - * @param string|array $relation 关联方法名 - * @param string $field 字段 - * @param bool $subQuery 是否使用子查询 - * @return $this - */ - public function withSum($relation, string $field, bool $subQuery = true) - { - return $this->withAggregate($relation, 'sum', $field, $subQuery); - } - - /** - * 关联统计Max - * @access public - * @param string|array $relation 关联方法名 - * @param string $field 字段 - * @param bool $subQuery 是否使用子查询 - * @return $this - */ - public function withMax($relation, string $field, bool $subQuery = true) - { - return $this->withAggregate($relation, 'max', $field, $subQuery); - } - - /** - * 关联统计Min - * @access public - * @param string|array $relation 关联方法名 - * @param string $field 字段 - * @param bool $subQuery 是否使用子查询 - * @return $this - */ - public function withMin($relation, string $field, bool $subQuery = true) - { - return $this->withAggregate($relation, 'min', $field, $subQuery); - } - - /** - * 关联统计Avg - * @access public - * @param string|array $relation 关联方法名 - * @param string $field 字段 - * @param bool $subQuery 是否使用子查询 - * @return $this - */ - public function withAvg($relation, string $field, bool $subQuery = true) - { - return $this->withAggregate($relation, 'avg', $field, $subQuery); - } - - /** - * 关联预加载中 获取关联指定字段值 - * example: - * Model::with(['relation' => function($query){ - * $query->withField("id,name"); - * }]) - * - * @access public - * @param string|array $field 指定获取的字段 - * @return $this - */ - public function withField($field) - { - $this->options['with_field'] = $field; - - return $this; - } - - /** - * 设置当前字段添加的表别名 - * @access public - * @param string $via 临时表别名 - * @return $this - */ - public function via(string $via = '') - { - $this->options['via'] = $via; - - return $this; - } - - /** - * 保存记录 自动判断insert或者update - * @access public - * @param array $data 数据 - * @param bool $forceInsert 是否强制insert - * @return integer - */ - public function save(array $data = [], bool $forceInsert = false) - { - if ($forceInsert) { - return $this->insert($data); - } - - $this->options['data'] = array_merge($this->options['data'] ?? [], $data); - - if (!empty($this->options['where'])) { - $isUpdate = true; - } else { - $isUpdate = $this->parseUpdateData($this->options['data']); - } - - return $isUpdate ? $this->update() : $this->insert(); - } - - /** - * 插入记录 - * @access public - * @param array $data 数据 - * @param boolean $getLastInsID 返回自增主键 - * @return integer|string - */ - public function insert(array $data = [], bool $getLastInsID = false) - { - if (!empty($data)) { - $this->options['data'] = $data; - } - - return $this->connection->insert($this, $getLastInsID); - } - - /** - * 插入记录并获取自增ID - * @access public - * @param array $data 数据 - * @return integer|string - */ - public function insertGetId(array $data) - { - return $this->insert($data, true); - } - - /** - * 批量插入记录 - * @access public - * @param array $dataSet 数据集 - * @param integer $limit 每次写入数据限制 - * @return integer - */ - public function insertAll(array $dataSet = [], int $limit = 0): int - { - if (empty($dataSet)) { - $dataSet = $this->options['data'] ?? []; - } - - if (empty($limit) && !empty($this->options['limit']) && is_numeric($this->options['limit'])) { - $limit = (int) $this->options['limit']; - } - - return $this->connection->insertAll($this, $dataSet, $limit); - } - - /** - * 通过Select方式插入记录 - * @access public - * @param array $fields 要插入的数据表字段名 - * @param string $table 要插入的数据表名 - * @return integer - * @throws PDOException - */ - public function selectInsert(array $fields, string $table): int - { - return $this->connection->selectInsert($this, $fields, $table); - } - - /** - * 更新记录 - * @access public - * @param mixed $data 数据 - * @return integer - * @throws Exception - * @throws PDOException - */ - public function update(array $data = []): int - { - if (!empty($data)) { - $this->options['data'] = array_merge($this->options['data'] ?? [], $data); - } - - if (empty($this->options['where'])) { - $this->parseUpdateData($this->options['data']); - } - - if (empty($this->options['where']) && $this->model) { - $this->where($this->model->getWhere()); - } - - if (empty($this->options['where'])) { - // 如果没有任何更新条件则不执行 - throw new Exception('miss update condition'); - } - - return $this->connection->update($this); - } - - /** - * 删除记录 - * @access public - * @param mixed $data 表达式 true 表示强制删除 - * @return int - * @throws Exception - * @throws PDOException - */ - public function delete($data = null): int - { - if (!is_null($data) && true !== $data) { - // AR模式分析主键条件 - $this->parsePkWhere($data); - } - - if (empty($this->options['where']) && $this->model) { - $this->where($this->model->getWhere()); - } - - if (true !== $data && empty($this->options['where'])) { - // 如果条件为空 不进行删除操作 除非设置 1=1 - throw new Exception('delete without condition'); - } - - if (!empty($this->options['soft_delete'])) { - // 软删除 - list($field, $condition) = $this->options['soft_delete']; - if ($condition) { - unset($this->options['soft_delete']); - $this->options['data'] = [$field => $condition]; - - return $this->connection->update($this); - } - } - - $this->options['data'] = $data; - - return $this->connection->delete($this); - } - - /** - * 执行查询但只返回PDOStatement对象 - * @access public - * @return PDOStatement - */ - public function getPdo(): PDOStatement - { - return $this->connection->pdo($this); - } - - /** - * 使用游标查找记录 - * @access public - * @param mixed $data 数据 - * @return \Generator - */ - public function cursor($data = null) - { - if (!is_null($data)) { - // 主键条件分析 - $this->parsePkWhere($data); - } - - $this->options['data'] = $data; - - $connection = clone $this->connection; - - return $connection->cursor($this); - } - - /** - * 查找记录 - * @access public - * @param mixed $data 数据 - * @return Collection|array|ModelCollection - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException - */ - public function select($data = null) - { - if (!is_null($data)) { - // 主键条件分析 - $this->parsePkWhere($data); - } - - $resultSet = $this->connection->select($this); - - // 返回结果处理 - if (!empty($this->options['fail']) && count($resultSet) == 0) { - $this->throwNotFound(); - } - - // 数据列表读取后的处理 - if (!empty($this->model) && empty($this->options['array'])) { - // 生成模型对象 - $resultSet = $this->resultSetToModelCollection($resultSet); - } else { - $this->resultSet($resultSet); - } - - return $resultSet; - } - - /** - * 查询数据转换为模型数据集对象 - * @access protected - * @param array $resultSet 数据集 - * @return ModelCollection - */ - protected function resultSetToModelCollection(array $resultSet): ModelCollection - { - if (!empty($this->options['collection']) && is_string($this->options['collection'])) { - $collection = $this->options['collection']; - } - - if (empty($resultSet)) { - return $this->model->toCollection([], $collection ?? null); - } - - // 检查动态获取器 - if (!empty($this->options['with_attr'])) { - foreach ($this->options['with_attr'] as $name => $val) { - if (strpos($name, '.')) { - list($relation, $field) = explode('.', $name); - - $withRelationAttr[$relation][$field] = $val; - unset($this->options['with_attr'][$name]); - } - } - } - - $withRelationAttr = $withRelationAttr ?? []; - - foreach ($resultSet as $key => &$result) { - // 数据转换为模型对象 - $this->resultToModel($result, $this->options, true, $withRelationAttr); - } - - if (!empty($this->options['with'])) { - // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with'], $withRelationAttr); - } - - if (!empty($this->options['with_join'])) { - // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with_join'], $withRelationAttr, true); - } - - // 模型数据集转换 - return $this->model->toCollection($resultSet, $collection ?? null); - } - - /** - * 处理数据集 - * @access public - * @param array $resultSet 数据集 - * @return void - */ - protected function resultSet(array &$resultSet): void - { - if (!empty($this->options['json'])) { - foreach ($resultSet as &$result) { - $this->jsonResult($result, $this->options['json'], true); - } - } - - if (!empty($this->options['with_attr'])) { - foreach ($resultSet as &$result) { - $this->getResultAttr($result, $this->options['with_attr']); - } - } - - if (!empty($this->options['visible']) || !empty($this->options['hidden'])) { - foreach ($resultSet as &$result) { - $this->filterResult($result); - } - } - - if (!empty($this->options['collection'])) { - // 返回Collection对象 - $resultSet = new Collection($resultSet); - } - } - - /** - * 查找单条记录 - * @access public - * @param mixed $data 查询数据 - * @return array|Model|null - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException - */ - public function find($data = null) - { - if (!is_null($data)) { - // AR模式分析主键条件 - $this->parsePkWhere($data); - } - - $result = $this->connection->find($this); - - // 数据处理 - if (empty($result)) { - return $this->resultToEmpty(); - } - - if (!empty($this->model) && empty($this->options['array'])) { - // 返回模型对象 - $this->resultToModel($result, $this->options); - } else { - $this->result($result); - } - - return $result; - } - - /** - * 查找单条记录 不存在返回空数据(或者空模型) - * @access public - * @param mixed $data 数据 - * @return array|Model - */ - public function findOrEmpty($data = null) - { - return $this->allowEmpty(true)->find($data); - } - - /** - * 处理空数据 - * @access protected - * @return array|Model|null - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException - */ - protected function resultToEmpty() - { - if (!empty($this->options['fail'])) { - $this->throwNotFound(); - } elseif (!empty($this->options['allow_empty'])) { - return !empty($this->model) && empty($this->options['array']) ? $this->model->newInstance()->setQuery($this) : []; - } elseif (!empty($this->options['array'])) { - return []; - } - } - - /** - * 获取模型的更新条件 - * @access protected - * @param array $options 查询参数 - */ - protected function getModelUpdateCondition(array $options) - { - return $options['where']['AND'] ?? null; - } - - /** - * 处理数据 - * @access protected - * @param array $result 查询数据 - * @return void - */ - protected function result(array &$result): void - { - if (!empty($this->options['json'])) { - $this->jsonResult($result, $this->options['json'], true); - } - - if (!empty($this->options['with_attr'])) { - $this->getResultAttr($result, $this->options['with_attr']); - } - - $this->filterResult($result); - } - - /** - * 处理数据的可见和隐藏 - * @access protected - * @param array $result 查询数据 - * @return void - */ - protected function filterResult(&$result): void - { - if (!empty($this->options['visible'])) { - foreach ($this->options['visible'] as $key) { - $array[] = $key; - } - $result = array_intersect_key($result, array_flip($array)); - } elseif (!empty($this->options['hidden'])) { - foreach ($this->options['hidden'] as $key) { - $array[] = $key; - } - $result = array_diff_key($result, array_flip($array)); - } - } - - /** - * 使用获取器处理数据 - * @access protected - * @param array $result 查询数据 - * @param array $withAttr 字段获取器 - * @return void - */ - protected function getResultAttr(array &$result, array $withAttr = []): void - { - foreach ($withAttr as $name => $closure) { - $name = $this->db->parseName($name); - - if (strpos($name, '.')) { - // 支持JSON字段 获取器定义 - list($key, $field) = explode('.', $name); - - if (isset($result[$key])) { - $result[$key][$field] = $closure($result[$key][$field] ?? null, $result[$key]); - } - } else { - $result[$name] = $closure($result[$name] ?? null, $result); - } - } - } - - /** - * JSON字段数据转换 - * @access protected - * @param array $result 查询数据 - * @param array $json JSON字段 - * @param bool $assoc 是否转换为数组 - * @param array $withRelationAttr 关联获取器 - * @return void - */ - protected function jsonResult(array &$result, array $json = [], bool $assoc = false, array $withRelationAttr = []): void - { - foreach ($json as $name) { - if (!isset($result[$name])) { - continue; - } - - $result[$name] = json_decode($result[$name], true); - - if (isset($withRelationAttr[$name])) { - foreach ($withRelationAttr[$name] as $key => $closure) { - $result[$name][$key] = $closure($result[$name][$key] ?? null, $result[$name]); - } - } - - if (!$assoc) { - $result[$name] = (object) $result[$name]; - } - } - } - - /** - * 查询数据转换为模型对象 - * @access protected - * @param array $result 查询数据 - * @param array $options 查询参数 - * @param bool $resultSet 是否为数据集查询 - * @param array $withRelationAttr 关联字段获取器 - * @return void - */ - protected function resultToModel(array &$result, array $options = [], bool $resultSet = false, array $withRelationAttr = []): void - { - // 动态获取器 - if (!empty($options['with_attr']) && empty($withRelationAttr)) { - foreach ($options['with_attr'] as $name => $val) { - if (strpos($name, '.')) { - list($relation, $field) = explode('.', $name); - - $withRelationAttr[$relation][$field] = $val; - unset($options['with_attr'][$name]); - } - } - } - - // JSON 数据处理 - if (!empty($options['json'])) { - $this->jsonResult($result, $options['json'], $options['json_assoc'], $withRelationAttr); - } - - $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options))->setQuery($this); - - // 动态获取器 - if (!empty($options['with_attr'])) { - $result->withAttribute($options['with_attr']); - } - - // 输出属性控制 - if (!empty($options['visible'])) { - $result->visible($options['visible']); - } elseif (!empty($options['hidden'])) { - $result->hidden($options['hidden']); - } - - if (!empty($options['append'])) { - $result->append($options['append']); - } - - // 关联查询 - if (!empty($options['relation'])) { - $result->relationQuery($options['relation'], $withRelationAttr); - } - - // 预载入查询 - if (!$resultSet && !empty($options['with'])) { - $result->eagerlyResult($result, $options['with'], $withRelationAttr); - } - - // JOIN预载入查询 - if (!$resultSet && !empty($options['with_join'])) { - $result->eagerlyResult($result, $options['with_join'], $withRelationAttr, true); - } - - // 关联统计 - if (!empty($options['with_count'])) { - foreach ($options['with_count'] as $val) { - $result->relationCount($result, $val[0], $val[1], $val[2]); - } - } - } - - /** - * 查询失败 抛出异常 - * @access protected - * @return void - * @throws ModelNotFoundException - * @throws DataNotFoundException - */ - protected function throwNotFound(): void - { - if (!empty($this->model)) { - $class = get_class($this->model); - throw new ModelNotFoundException('model data Not Found:' . $class, $class, $this->options); - } - - $table = $this->getTable(); - throw new DataNotFoundException('table data not Found:' . $table, $table, $this->options); - } - - /** - * 查找多条记录 如果不存在则抛出异常 - * @access public - * @param array|string|Query|Closure $data 数据 - * @return array|PDOStatement|string|Model - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException - */ - public function selectOrFail($data = null) - { - return $this->failException(true)->select($data); - } - - /** - * 查找单条记录 如果不存在则抛出异常 - * @access public - * @param array|string|Query|Closure $data 数据 - * @return array|PDOStatement|string|Model - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException - */ - public function findOrFail($data = null) - { - return $this->failException(true)->find($data); - } - - /** - * 分批数据返回处理 - * @access public - * @param integer $count 每次处理的数据数量 - * @param callable $callback 处理回调方法 - * @param string|array $column 分批处理的字段名 - * @param string $order 字段排序 - * @return bool - * @throws DbException - */ - public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool - { - $options = $this->getOptions(); - $column = $column ?: $this->getPk(); - - if (isset($options['order'])) { - unset($options['order']); - } - - $bind = $this->bind; - - if (is_array($column)) { - $times = 1; - $query = $this->options($options)->page($times, $count); - } else { - $query = $this->options($options)->limit($count); - - if (strpos($column, '.')) { - list($alias, $key) = explode('.', $column); - } else { - $key = $column; - } - } - - $resultSet = $query->order($column, $order)->select(); - - while (count($resultSet) > 0) { - if ($resultSet instanceof Collection) { - $resultSet = $resultSet->all(); - } - - if (false === call_user_func($callback, $resultSet)) { - return false; - } - - if (isset($times)) { - $times++; - $query = $this->options($options)->page($times, $count); - } else { - $end = end($resultSet); - $lastId = is_array($end) ? $end[$key] : $end->getData($key); - - $query = $this->options($options) - ->limit($count) - ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId); - } - - $resultSet = $query->bind($bind)->order($column, $order)->select(); - } - - return true; - } - - /** - * 获取绑定的参数 并清空 - * @access public - * @param bool $clear 是否清空绑定数据 - * @return array - */ - public function getBind(bool $clear = true): array - { - $bind = $this->bind; - if ($clear) { - $this->bind = []; - } - - return $bind; - } - - /** - * 创建子查询SQL - * @access public - * @param bool $sub 是否添加括号 - * @return string - * @throws DbException - */ - public function buildSql(bool $sub = true): string - { - return $sub ? '( ' . $this->fetchSql()->select() . ' )' : $this->fetchSql()->select(); - } - - /** - * 视图查询处理 - * @access protected - * @param array $options 查询参数 - * @return void - */ - protected function parseView(array &$options): void - { - foreach (['AND', 'OR'] as $logic) { - if (isset($options['where'][$logic])) { - foreach ($options['where'][$logic] as $key => $val) { - if (array_key_exists($key, $options['map'])) { - array_shift($val); - array_unshift($val, $options['map'][$key]); - $options['where'][$logic][$options['map'][$key]] = $val; - unset($options['where'][$logic][$key]); - } - } - } - } - - if (isset($options['order'])) { - // 视图查询排序处理 - foreach ($options['order'] as $key => $val) { - if (is_numeric($key) && is_string($val)) { - if (strpos($val, ' ')) { - list($field, $sort) = explode(' ', $val); - if (array_key_exists($field, $options['map'])) { - $options['order'][$options['map'][$field]] = $sort; - unset($options['order'][$key]); - } - } elseif (array_key_exists($val, $options['map'])) { - $options['order'][$options['map'][$val]] = 'asc'; - unset($options['order'][$key]); - } - } elseif (array_key_exists($key, $options['map'])) { - $options['order'][$options['map'][$key]] = $val; - unset($options['order'][$key]); - } - } - } - } - - /** - * 分析数据是否存在更新条件 - * @access public - * @param array $data 数据 - * @return bool - * @throws Exception - */ - public function parseUpdateData(&$data): bool - { - $pk = $this->getPk(); - $isUpdate = false; - // 如果存在主键数据 则自动作为更新条件 - if (is_string($pk) && isset($data[$pk])) { - $this->where($pk, '=', $data[$pk]); - $this->options['key'] = $data[$pk]; - unset($data[$pk]); - $isUpdate = true; - } elseif (is_array($pk)) { - // 增加复合主键支持 - foreach ($pk as $field) { - if (isset($data[$field])) { - $this->where($field, '=', $data[$field]); - $isUpdate = true; - } else { - // 如果缺少复合主键数据则不执行 - throw new Exception('miss complex primary data'); - } - unset($data[$field]); - } - } - - return $isUpdate; - } - - /** - * 把主键值转换为查询条件 支持复合主键 - * @access public - * @param array|string $data 主键数据 - * @return void - * @throws Exception - */ - public function parsePkWhere($data): void - { - $pk = $this->getPk(); - - if (is_string($pk)) { - // 获取数据表 - if (empty($this->options['table'])) { - $this->options['table'] = $this->getTable(); - } - - $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table']; - - if (!empty($this->options['alias'][$table])) { - $alias = $this->options['alias'][$table]; - } - - $key = isset($alias) ? $alias . '.' . $pk : $pk; - // 根据主键查询 - if (is_array($data)) { - $this->where($key, 'in', $data); - } else { - $this->where($key, '=', $data); - $this->options['key'] = $data; - } - } - } - - /** - * 分析表达式(可用于查询或者写入操作) - * @access public - * @return array - */ - public function parseOptions(): array - { - $options = $this->getOptions(); - - // 获取数据表 - if (empty($options['table'])) { - $options['table'] = $this->getTable(); - } - - if (!isset($options['where'])) { - $options['where'] = []; - } elseif (isset($options['view'])) { - // 视图查询条件处理 - $this->parseView($options); - } - - if (!isset($options['field'])) { - $options['field'] = '*'; - } - - foreach (['data', 'order', 'join', 'union'] as $name) { - if (!isset($options[$name])) { - $options[$name] = []; - } - } - - if (!isset($options['strict'])) { - $options['strict'] = $this->connection->getConfig('fields_strict'); - } - - foreach (['master', 'lock', 'fetch_sql', 'array', 'distinct', 'procedure'] as $name) { - if (!isset($options[$name])) { - $options[$name] = false; - } - } - - foreach (['group', 'having', 'limit', 'force', 'comment', 'partition', 'duplicate', 'extra'] as $name) { - if (!isset($options[$name])) { - $options[$name] = ''; - } - } - - if (isset($options['page'])) { - // 根据页数计算limit - list($page, $listRows) = $options['page']; - $page = $page > 0 ? $page : 1; - $listRows = $listRows ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20); - $offset = $listRows * ($page - 1); - $options['limit'] = $offset . ',' . $listRows; - } - - $this->options = $options; - - return $options; - } - - public function __debugInfo() - { - return [ - 'name' => $this->name, - 'pk' => $this->pk, - 'prefix' => $this->prefix, - 'bind' => $this->bind, - 'options' => $this->options, - ]; - } + use concern\PDOQuery; } diff --git a/src/db/builder/Mysql.php b/src/db/builder/Mysql.php index ce055e1..c21c0c2 100644 --- a/src/db/builder/Mysql.php +++ b/src/db/builder/Mysql.php @@ -107,11 +107,10 @@ class Mysql extends Builder /** * 生成Insert SQL * @access public - * @param Query $query 查询对象 - * @param bool $replace 是否replace + * @param Query $query 查询对象 * @return string */ - public function insert(Query $query, bool $replace = false): string + public function insert(Query $query): string { $options = $query->getOptions(); @@ -129,7 +128,7 @@ class Mysql extends Builder return str_replace( ['%INSERT%', '%EXTRA%', '%TABLE%', '%PARTITION%', '%SET%', '%DUPLICATE%', '%COMMENT%'], [ - $replace ? 'REPLACE' : 'INSERT', + !empty($options['replace']) ? 'REPLACE' : 'INSERT', $this->parseExtra($query, $options['extra']), $this->parseTable($query, $options['table']), $this->parsePartition($query, $options['partition']), @@ -317,8 +316,7 @@ class Mysql extends Builder if (strpos($key, '->') && false === strpos($key, '(')) { // JSON字段支持 list($field, $name) = explode('->', $key, 2); - - return 'json_extract(' . $this->parseKey($query, $field) . ', \'$.' . str_replace('->', '.', $name) . '\')'; + return 'json_extract(' . $this->parseKey($query, $field) . ', \'$' . (strpos($name, '[') === 0 ? '' : '.') . str_replace('->', '.', $name) . '\')'; } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { list($table, $key) = explode('.', $key, 2); @@ -413,7 +411,7 @@ class Mysql extends Builder } elseif ($val instanceof Raw) { $updates[] = $this->parseKey($query, $key) . " = " . $val->getValue(); } else { - $name = $query->bindValue($val, $query->getFieldBindType($key)); + $name = $query->bindValue($val, $query->getConnection()->getFieldBindType($key)); $updates[] = $this->parseKey($query, $key) . " = :" . $name; } } diff --git a/src/db/concern/AggregateQuery.php b/src/db/concern/AggregateQuery.php new file mode 100644 index 0000000..dabfb92 --- /dev/null +++ b/src/db/concern/AggregateQuery.php @@ -0,0 +1,107 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db\concern; + +use think\db\Raw; + +/** + * 聚合查询 + */ +trait AggregateQuery +{ + /** + * 聚合查询 + * @access protected + * @param string $aggregate 聚合方法 + * @param string|Raw $field 字段名 + * @param bool $force 强制转为数字类型 + * @return mixed + */ + protected function aggregate(string $aggregate, $field, bool $force = false) + { + return $this->connection->aggregate($this, $aggregate, $field, $force); + } + + /** + * COUNT查询 + * @access public + * @param string|Raw $field 字段名 + * @return int + */ + public function count(string $field = '*'): int + { + if (!empty($this->options['group'])) { + // 支持GROUP + $options = $this->getOptions(); + $subSql = $this->options($options) + ->field('count(' . $field . ') AS think_count') + ->bind($this->bind) + ->buildSql(); + + $query = $this->newQuery()->table([$subSql => '_group_count_']); + + $count = $query->aggregate('COUNT', '*'); + } else { + $count = $this->aggregate('COUNT', $field); + } + + return (int) $count; + } + + /** + * SUM查询 + * @access public + * @param string|Raw $field 字段名 + * @return float + */ + public function sum($field): float + { + return $this->aggregate('SUM', $field, true); + } + + /** + * MIN查询 + * @access public + * @param string|Raw $field 字段名 + * @param bool $force 强制转为数字类型 + * @return mixed + */ + public function min($field, bool $force = true) + { + return $this->aggregate('MIN', $field, $force); + } + + /** + * MAX查询 + * @access public + * @param string|Raw $field 字段名 + * @param bool $force 强制转为数字类型 + * @return mixed + */ + public function max($field, bool $force = true) + { + return $this->aggregate('MAX', $field, $force); + } + + /** + * AVG查询 + * @access public + * @param string|Raw $field 字段名 + * @return float + */ + public function avg($field): float + { + return $this->aggregate('AVG', $field, true); + } + +} diff --git a/src/db/concern/JoinAndViewQuery.php b/src/db/concern/JoinAndViewQuery.php new file mode 100644 index 0000000..2e7de27 --- /dev/null +++ b/src/db/concern/JoinAndViewQuery.php @@ -0,0 +1,283 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db\concern; + +use Closure; +use think\Container; +use think\db\Raw; +use think\model\relation\OneToOne; + +/** + * JOIN和VIEW查询 + */ +trait JoinAndViewQuery +{ + + /** + * 查询SQL组装 join + * @access public + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param string $type JOIN类型 + * @param array $bind 参数绑定 + * @return $this + */ + public function join($join, string $condition = null, string $type = 'INNER', array $bind = []) + { + $table = $this->getJoinTable($join); + + if (!empty($bind) && $condition) { + $this->bindParams($condition, $bind); + } + + $this->options['join'][] = [$table, strtoupper($type), $condition]; + + return $this; + } + + /** + * LEFT JOIN + * @access public + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param array $bind 参数绑定 + * @return $this + */ + public function leftJoin($join, string $condition = null, array $bind = []) + { + return $this->join($join, $condition, 'LEFT', $bind); + } + + /** + * RIGHT JOIN + * @access public + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param array $bind 参数绑定 + * @return $this + */ + public function rightJoin($join, string $condition = null, array $bind = []) + { + return $this->join($join, $condition, 'RIGHT', $bind); + } + + /** + * FULL JOIN + * @access public + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param array $bind 参数绑定 + * @return $this + */ + public function fullJoin($join, string $condition = null, array $bind = []) + { + return $this->join($join, $condition, 'FULL'); + } + + /** + * 获取Join表名及别名 支持 + * ['prefix_table或者子查询'=>'alias'] 'table alias' + * @access protected + * @param array|string|Raw $join JION表名 + * @param string $alias 别名 + * @return string|array + */ + protected function getJoinTable($join, &$alias = null) + { + if (is_array($join)) { + $table = $join; + $alias = array_shift($join); + return $table; + } elseif ($join instanceof Raw) { + return $join; + } + + $join = trim($join); + + if (false !== strpos($join, '(')) { + // 使用子查询 + $table = $join; + } else { + // 使用别名 + if (strpos($join, ' ')) { + // 使用别名 + list($table, $alias) = explode(' ', $join); + } else { + $table = $join; + if (false === strpos($join, '.')) { + $alias = $join; + } + } + + if ($this->prefix && false === strpos($table, '.') && 0 !== strpos($table, $this->prefix)) { + $table = $this->getTable($table); + } + } + + if (!empty($alias) && $table != $alias) { + $table = [$table => $alias]; + } + + return $table; + } + + /** + * 关联预载入 JOIN方式 + * @access protected + * @param array|string $with 关联方法名 + * @param string $joinType JOIN方式 + * @return $this + */ + public function withJoin($with, string $joinType = '') + { + if (empty($with)) { + return $this; + } + + $first = true; + + /** @var Model $class */ + $class = $this->model; + foreach ((array) $with as $key => $relation) { + $closure = null; + $field = true; + + if ($relation instanceof Closure) { + // 支持闭包查询过滤关联条件 + $closure = $relation; + $relation = $key; + } elseif (is_array($relation)) { + $field = $relation; + $relation = $key; + } elseif (is_string($relation) && strpos($relation, '.')) { + $relation = strstr($relation, '.', true); + } + + /** @var Relation $model */ + $relation = Container::parseName($relation, 1, false); + $model = $class->$relation(); + + if ($model instanceof OneToOne) { + $model->eagerly($this, $relation, $field, $joinType, $closure, $first); + $first = false; + } else { + // 不支持其它关联 + unset($with[$key]); + } + } + + $this->via(); + + $this->options['with_join'] = $with; + + return $this; + } + + /** + * 指定JOIN查询字段 + * @access public + * @param string|array $join 数据表 + * @param string|array $field 查询字段 + * @param string $on JOIN条件 + * @param string $type JOIN类型 + * @param array $bind 参数绑定 + * @return $this + */ + public function view($join, $field = true, $on = null, string $type = 'INNER', array $bind = []) + { + $this->options['view'] = true; + + $fields = []; + $table = $this->getJoinTable($join, $alias); + + if (true === $field) { + $fields = $alias . '.*'; + } else { + if (is_string($field)) { + $field = explode(',', $field); + } + + foreach ($field as $key => $val) { + if (is_numeric($key)) { + $fields[] = $alias . '.' . $val; + + $this->options['map'][$val] = $alias . '.' . $val; + } else { + if (preg_match('/[,=\.\'\"\(\s]/', $key)) { + $name = $key; + } else { + $name = $alias . '.' . $key; + } + + $fields[] = $name . ' AS ' . $val; + + $this->options['map'][$val] = $name; + } + } + } + + $this->field($fields); + + if ($on) { + $this->join($table, $on, $type, $bind); + } else { + $this->table($table); + } + + return $this; + } + + /** + * 视图查询处理 + * @access protected + * @param array $options 查询参数 + * @return void + */ + protected function parseView(array &$options): void + { + foreach (['AND', 'OR'] as $logic) { + if (isset($options['where'][$logic])) { + foreach ($options['where'][$logic] as $key => $val) { + if (array_key_exists($key, $options['map'])) { + array_shift($val); + array_unshift($val, $options['map'][$key]); + $options['where'][$logic][$options['map'][$key]] = $val; + unset($options['where'][$logic][$key]); + } + } + } + } + + if (isset($options['order'])) { + // 视图查询排序处理 + foreach ($options['order'] as $key => $val) { + if (is_numeric($key) && is_string($val)) { + if (strpos($val, ' ')) { + list($field, $sort) = explode(' ', $val); + if (array_key_exists($field, $options['map'])) { + $options['order'][$options['map'][$field]] = $sort; + unset($options['order'][$key]); + } + } elseif (array_key_exists($val, $options['map'])) { + $options['order'][$options['map'][$val]] = 'asc'; + unset($options['order'][$key]); + } + } elseif (array_key_exists($key, $options['map'])) { + $options['order'][$options['map'][$key]] = $val; + unset($options['order'][$key]); + } + } + } + } + +} diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php new file mode 100644 index 0000000..6f73db6 --- /dev/null +++ b/src/db/concern/ModelRelationQuery.php @@ -0,0 +1,427 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db\concern; + +use Closure; +use think\Container; +use think\Model; +use think\model\Collection as ModelCollection; + +/** + * 模型及关联查询 + */ +trait ModelRelationQuery +{ + + /** + * 当前模型对象 + * @var Model + */ + protected $model; + + /** + * 指定模型 + * @access public + * @param Model $model 模型对象实例 + * @return $this + */ + public function model(Model $model) + { + $this->model = $model; + return $this; + } + + /** + * 获取当前的模型对象 + * @access public + * @param bool $clear 是否需要清空查询条件 + * @return Model|null + */ + public function getModel(bool $clear = true) + { + return $this->model ? $this->model->setQuery($this, $clear) : null; + } + + /** + * 设置需要隐藏的输出属性 + * @access public + * @param array $hidden 需要隐藏的字段名 + * @return $this + */ + public function hidden(array $hidden) + { + $this->options['hidden'] = $hidden; + return $this; + } + + /** + * 设置需要输出的属性 + * @access public + * @param array $visible 需要输出的属性 + * @return $this + */ + public function visible(array $visible) + { + $this->options['visible'] = $visible; + return $this; + } + + /** + * 设置需要追加输出的属性 + * @access public + * @param array $append 需要追加的属性 + * @return $this + */ + public function append(array $append) + { + $this->options['append'] = $append; + return $this; + } + + /** + * 添加查询范围 + * @access public + * @param array|string|Closure $scope 查询范围定义 + * @param array $args 参数 + * @return $this + */ + public function scope($scope, ...$args) + { + // 查询范围的第一个参数始终是当前查询对象 + array_unshift($args, $this); + + if ($scope instanceof Closure) { + call_user_func_array($scope, $args); + return $this; + } + + if (is_string($scope)) { + $scope = explode(',', $scope); + } + + if ($this->model) { + // 检查模型类的查询范围方法 + foreach ($scope as $name) { + $method = 'scope' . trim($name); + + if (method_exists($this->model, $method)) { + call_user_func_array([$this->model, $method], $args); + } + } + } + + return $this; + } + + /** + * 设置关联查询 + * @access public + * @param array $relation 关联名称 + * @return $this + */ + public function relation(array $relation) + { + if (!empty($relation)) { + $this->options['relation'] = $relation; + } + + return $this; + } + + /** + * 使用搜索器条件搜索字段 + * @access public + * @param array $fields 搜索字段 + * @param array $data 搜索数据 + * @param string $prefix 字段前缀标识 + * @return $this + */ + public function withSearch(array $fields, array $data = [], string $prefix = '') + { + foreach ($fields as $key => $field) { + if ($field instanceof Closure) { + $field($this, $data[$key] ?? null, $data, $prefix); + } elseif ($this->model) { + // 检测搜索器 + $fieldName = is_numeric($key) ? $field : $key; + $method = 'search' . Container::parseName($fieldName, 1) . 'Attr'; + + if (method_exists($this->model, $method)) { + $this->model->$method($this, $data[$field] ?? null, $data, $prefix); + } + } + } + + return $this; + } + + /** + * 设置数据字段获取器 + * @access public + * @param string|array $name 字段名 + * @param callable $callback 闭包获取器 + * @return $this + */ + public function withAttr($name, callable $callback = null) + { + if (is_array($name)) { + $this->options['with_attr'] = $name; + } else { + $this->options['with_attr'][$name] = $callback; + } + + return $this; + } + + /** + * 设置关联查询JOIN预查询 + * @access public + * @param array|string $with 关联方法名称 + * @return $this + */ + public function with($with) + { + if (!empty($with)) { + $this->options['with'] = (array) $with; + } + + return $this; + } + + /** + * 关联统计 + * @access protected + * @param array|string $relations 关联方法名 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 + * @return $this + */ + protected function withAggregate($relations, string $aggregate = 'count', $field = '*', bool $subQuery = true) + { + if (!$subQuery) { + $this->options['with_count'][] = [$relations, $aggregate, $field]; + } else { + if (!isset($this->options['field'])) { + $this->field('*'); + } + + foreach ((array) $relations as $key => $relation) { + $closure = $aggregateField = null; + + if ($relation instanceof Closure) { + $closure = $relation; + $relation = $key; + } elseif (!is_int($key)) { + $aggregateField = $relation; + $relation = $key; + } + + $relation = Container::parseName($relation, 1, false); + + $count = $this->model + ->$relation() + ->getRelationCountQuery($closure, $aggregate, $field, $aggregateField); + + if (empty($aggregateField)) { + $aggregateField = Container::parseName($relation) . '_' . $aggregate; + } + + $this->field(['(' . $count . ')' => $aggregateField]); + } + } + + return $this; + } + + /** + * 关联统计 + * @access public + * @param string|array $relation 关联方法名 + * @param bool $subQuery 是否使用子查询 + * @return $this + */ + public function withCount($relation, bool $subQuery = true) + { + return $this->withAggregate($relation, 'count', '*', $subQuery); + } + + /** + * 关联统计Sum + * @access public + * @param string|array $relation 关联方法名 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 + * @return $this + */ + public function withSum($relation, string $field, bool $subQuery = true) + { + return $this->withAggregate($relation, 'sum', $field, $subQuery); + } + + /** + * 关联统计Max + * @access public + * @param string|array $relation 关联方法名 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 + * @return $this + */ + public function withMax($relation, string $field, bool $subQuery = true) + { + return $this->withAggregate($relation, 'max', $field, $subQuery); + } + + /** + * 关联统计Min + * @access public + * @param string|array $relation 关联方法名 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 + * @return $this + */ + public function withMin($relation, string $field, bool $subQuery = true) + { + return $this->withAggregate($relation, 'min', $field, $subQuery); + } + + /** + * 关联统计Avg + * @access public + * @param string|array $relation 关联方法名 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 + * @return $this + */ + public function withAvg($relation, string $field, bool $subQuery = true) + { + return $this->withAggregate($relation, 'avg', $field, $subQuery); + } + + /** + * 查询数据转换为模型数据集对象 + * @access protected + * @param array $resultSet 数据集 + * @return ModelCollection + */ + protected function resultSetToModelCollection(array $resultSet): ModelCollection + { + if (empty($resultSet)) { + return $this->model->toCollection(); + } + + // 检查动态获取器 + if (!empty($this->options['with_attr'])) { + foreach ($this->options['with_attr'] as $name => $val) { + if (strpos($name, '.')) { + list($relation, $field) = explode('.', $name); + + $withRelationAttr[$relation][$field] = $val; + unset($this->options['with_attr'][$name]); + } + } + } + + $withRelationAttr = $withRelationAttr ?? []; + + foreach ($resultSet as $key => &$result) { + // 数据转换为模型对象 + $this->resultToModel($result, $this->options, true, $withRelationAttr); + } + + if (!empty($this->options['with'])) { + // 预载入 + $result->eagerlyResultSet($resultSet, $this->options['with'], $withRelationAttr); + } + + if (!empty($this->options['with_join'])) { + // 预载入 + $result->eagerlyResultSet($resultSet, $this->options['with_join'], $withRelationAttr, true); + } + + // 模型数据集转换 + return $this->model->toCollection($resultSet); + } + + /** + * 查询数据转换为模型对象 + * @access protected + * @param array $result 查询数据 + * @param array $options 查询参数 + * @param bool $resultSet 是否为数据集查询 + * @param array $withRelationAttr 关联字段获取器 + * @return void + */ + protected function resultToModel(array &$result, array $options = [], bool $resultSet = false, array $withRelationAttr = []): void + { + // 动态获取器 + if (!empty($options['with_attr']) && empty($withRelationAttr)) { + foreach ($options['with_attr'] as $name => $val) { + if (strpos($name, '.')) { + list($relation, $field) = explode('.', $name); + + $withRelationAttr[$relation][$field] = $val; + unset($options['with_attr'][$name]); + } + } + } + + // JSON 数据处理 + if (!empty($options['json'])) { + $this->jsonResult($result, $options['json'], $options['json_assoc'], $withRelationAttr); + } + + $result = $this->model + ->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options)) + ->setQuery($this); + + // 动态获取器 + if (!empty($options['with_attr'])) { + $result->withAttribute($options['with_attr']); + } + + // 输出属性控制 + if (!empty($options['visible'])) { + $result->visible($options['visible']); + } elseif (!empty($options['hidden'])) { + $result->hidden($options['hidden']); + } + + if (!empty($options['append'])) { + $result->append($options['append']); + } + + // 关联查询 + if (!empty($options['relation'])) { + $result->relationQuery($options['relation'], $withRelationAttr); + } + + // 预载入查询 + if (!$resultSet && !empty($options['with'])) { + $result->eagerlyResult($result, $options['with'], $withRelationAttr); + } + + // JOIN预载入查询 + if (!$resultSet && !empty($options['with_join'])) { + $result->eagerlyResult($result, $options['with_join'], $withRelationAttr, true); + } + + // 关联统计 + if (!empty($options['with_count'])) { + foreach ($options['with_count'] as $val) { + $result->relationCount($result, (array) $val[0], $val[1], $val[2]); + } + } + } + +} diff --git a/src/db/concern/PDOQuery.php b/src/db/concern/PDOQuery.php new file mode 100644 index 0000000..df2b48c --- /dev/null +++ b/src/db/concern/PDOQuery.php @@ -0,0 +1,54 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db\concern; + +use PDOStatement; + +/** + * PDO查询支持 + */ +trait PDOQuery +{ + use JoinAndViewQuery, ParamsBind, TableFieldInfo; + + /** + * 执行查询但只返回PDOStatement对象 + * @access public + * @return PDOStatement + */ + public function getPdo(): PDOStatement + { + return $this->connection->pdo($this); + } + + /** + * 使用游标查找记录 + * @access public + * @param mixed $data 数据 + * @return \Generator + */ + public function cursor($data = null) + { + if (!is_null($data)) { + // 主键条件分析 + $this->parsePkWhere($data); + } + + $this->options['data'] = $data; + + $connection = clone $this->connection; + + return $connection->cursor($this); + } + +} diff --git a/src/db/concern/ParamsBind.php b/src/db/concern/ParamsBind.php new file mode 100644 index 0000000..8267b7e --- /dev/null +++ b/src/db/concern/ParamsBind.php @@ -0,0 +1,106 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db\concern; + +use PDO; + +/** + * 参数绑定支持 + */ +trait ParamsBind +{ + /** + * 当前参数绑定 + * @var array + */ + protected $bind = []; + + /** + * 批量参数绑定 + * @access public + * @param array $value 绑定变量值 + * @return $this + */ + public function bind(array $value) + { + $this->bind = array_merge($this->bind, $value); + return $this; + } + + /** + * 单个参数绑定 + * @access public + * @param mixed $value 绑定变量值 + * @param integer $type 绑定类型 + * @param string $name 绑定标识 + * @return string + */ + public function bindValue($value, int $type = null, string $name = null) + { + $name = $name ?: 'ThinkBind_' . (count($this->bind) + 1) . '_'; + + $this->bind[$name] = [$value, $type ?: PDO::PARAM_STR]; + return $name; + } + + /** + * 检测参数是否已经绑定 + * @access public + * @param string $key 参数名 + * @return bool + */ + public function isBind($key) + { + return isset($this->bind[$key]); + } + + /** + * 参数绑定 + * @access public + * @param string $sql 绑定的sql表达式 + * @param array $bind 参数绑定 + * @return void + */ + protected function bindParams(string &$sql, array $bind = []): void + { + foreach ($bind as $key => $value) { + if (is_array($value)) { + $name = $this->bindValue($value[0], $value[1], $value[2] ?? null); + } else { + $name = $this->bindValue($value); + } + + if (is_numeric($key)) { + $sql = substr_replace($sql, ':' . $name, strpos($sql, '?'), 1); + } else { + $sql = str_replace(':' . $key, ':' . $name, $sql); + } + } + } + + /** + * 获取绑定的参数 并清空 + * @access public + * @param bool $clear 是否清空绑定数据 + * @return array + */ + public function getBind(bool $clear = true): array + { + $bind = $this->bind; + if ($clear) { + $this->bind = []; + } + + return $bind; + } +} diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php new file mode 100644 index 0000000..4cbf4f8 --- /dev/null +++ b/src/db/concern/ResultOperation.php @@ -0,0 +1,248 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db\concern; + +use think\Collection; +use think\Container; +use think\db\exception\DataNotFoundException; +use think\db\exception\ModelNotFoundException; + +/** + * 查询数据处理 + */ +trait ResultOperation +{ + /** + * 是否允许返回空数据(或空模型) + * @access public + * @param bool $allowEmpty 是否允许为空 + * @return $this + */ + public function allowEmpty(bool $allowEmpty = true) + { + $this->options['allow_empty'] = $allowEmpty; + return $this; + } + + /** + * 设置查询数据不存在是否抛出异常 + * @access public + * @param bool $fail 数据不存在是否抛出异常 + * @return $this + */ + public function failException(bool $fail = true) + { + $this->options['fail'] = $fail; + return $this; + } + + /** + * 处理数据 + * @access protected + * @param array $result 查询数据 + * @return void + */ + protected function result(array &$result): void + { + if (!empty($this->options['json'])) { + $this->jsonResult($result, $this->options['json'], true); + } + + if (!empty($this->options['with_attr'])) { + $this->getResultAttr($result, $this->options['with_attr']); + } + + $this->filterResult($result); + } + + /** + * 处理数据集 + * @access public + * @param array $resultSet 数据集 + * @return void + */ + protected function resultSet(array &$resultSet): void + { + if (!empty($this->options['json'])) { + foreach ($resultSet as &$result) { + $this->jsonResult($result, $this->options['json'], true); + } + } + + if (!empty($this->options['with_attr'])) { + foreach ($resultSet as &$result) { + $this->getResultAttr($result, $this->options['with_attr']); + } + } + + if (!empty($this->options['visible']) || !empty($this->options['hidden'])) { + foreach ($resultSet as &$result) { + $this->filterResult($result); + } + } + + // 返回Collection对象 + $resultSet = new Collection($resultSet); + } + + /** + * 处理数据的可见和隐藏 + * @access protected + * @param array $result 查询数据 + * @return void + */ + protected function filterResult(&$result): void + { + if (!empty($this->options['visible'])) { + foreach ($this->options['visible'] as $key) { + $array[] = $key; + } + $result = array_intersect_key($result, array_flip($array)); + } elseif (!empty($this->options['hidden'])) { + foreach ($this->options['hidden'] as $key) { + $array[] = $key; + } + $result = array_diff_key($result, array_flip($array)); + } + } + + /** + * 使用获取器处理数据 + * @access protected + * @param array $result 查询数据 + * @param array $withAttr 字段获取器 + * @return void + */ + protected function getResultAttr(array &$result, array $withAttr = []): void + { + foreach ($withAttr as $name => $closure) { + $name = Container::parseName($name); + + if (strpos($name, '.')) { + // 支持JSON字段 获取器定义 + list($key, $field) = explode('.', $name); + + if (isset($result[$key])) { + $result[$key][$field] = $closure($result[$key][$field] ?? null, $result[$key]); + } + } else { + $result[$name] = $closure($result[$name] ?? null, $result); + } + } + } + + /** + * 处理空数据 + * @access protected + * @return array|Model|null + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + protected function resultToEmpty() + { + if (!empty($this->options['fail'])) { + $this->throwNotFound(); + } elseif (!empty($this->options['allow_empty'])) { + return !empty($this->model) ? $this->model->newInstance()->setQuery($this) : []; + } + } + + /** + * 查找单条记录 不存在返回空数据(或者空模型) + * @access public + * @param mixed $data 数据 + * @return array|Model + */ + public function findOrEmpty($data = null) + { + return $this->allowEmpty(true)->find($data); + } + + /** + * JSON字段数据转换 + * @access protected + * @param array $result 查询数据 + * @param array $json JSON字段 + * @param bool $assoc 是否转换为数组 + * @param array $withRelationAttr 关联获取器 + * @return void + */ + protected function jsonResult(array &$result, array $json = [], bool $assoc = false, array $withRelationAttr = []): void + { + foreach ($json as $name) { + if (!isset($result[$name])) { + continue; + } + + $result[$name] = json_decode($result[$name], true); + + if (isset($withRelationAttr[$name])) { + foreach ($withRelationAttr[$name] as $key => $closure) { + $result[$name][$key] = $closure($result[$name][$key] ?? null, $result[$name]); + } + } + + if (!$assoc) { + $result[$name] = (object) $result[$name]; + } + } + } + + /** + * 查询失败 抛出异常 + * @access protected + * @return void + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + protected function throwNotFound(): void + { + if (!empty($this->model)) { + $class = get_class($this->model); + throw new ModelNotFoundException('model data Not Found:' . $class, $class, $this->options); + } + + $table = $this->getTable(); + throw new DataNotFoundException('table data not Found:' . $table, $table, $this->options); + } + + /** + * 查找多条记录 如果不存在则抛出异常 + * @access public + * @param array|string|Query|Closure $data 数据 + * @return array|Model + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function selectOrFail($data = null) + { + return $this->failException(true)->select($data); + } + + /** + * 查找单条记录 如果不存在则抛出异常 + * @access public + * @param array|string|Query|Closure $data 数据 + * @return array|Model + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function findOrFail($data = null) + { + return $this->failException(true)->find($data); + } + +} diff --git a/src/db/concern/TableFieldInfo.php b/src/db/concern/TableFieldInfo.php new file mode 100644 index 0000000..6e7da01 --- /dev/null +++ b/src/db/concern/TableFieldInfo.php @@ -0,0 +1,111 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db\concern; + +/** + * 数据字段信息 + */ +trait TableFieldInfo +{ + + /** + * 获取数据表字段信息 + * @access public + * @param string $tableName 数据表名 + * @return array + */ + public function getTableFields($tableName = ''): array + { + if ('' == $tableName) { + $tableName = $this->getTable(); + } + + return $this->connection->getTableFields($tableName); + } + + /** + * 设置字段类型信息 + * @access public + * @param array $type 字段类型信息 + * @return $this + */ + public function setFieldType(array $type) + { + $this->options['field_type'] = $type; + return $this; + } + + /** + * 获取详细字段类型信息 + * @access public + * @param string $tableName 数据表名称 + * @return array + */ + public function getFields(string $tableName = ''): array + { + return $this->connection->getFields($tableName ?: $this->getTable()); + } + + /** + * 获取字段类型信息 + * @access public + * @return array + */ + public function getFieldsType(): array + { + if (!empty($this->options['field_type'])) { + return $this->options['field_type']; + } + + return $this->connection->getFieldsType($this->getTable()); + } + + /** + * 获取字段类型信息 + * @access public + * @param string $field 字段名 + * @return string|null + */ + public function getFieldType(string $field) + { + $fieldType = $this->getFieldsType(); + + return $fieldType[$field] ?? null; + } + + /** + * 获取字段类型信息 + * @access public + * @return array + */ + public function getFieldsBindType(): array + { + $fieldType = $this->getFieldsType(); + + return array_map([$this->connection, 'getFieldBindType'], $fieldType); + } + + /** + * 获取字段类型信息 + * @access public + * @param string $field 字段名 + * @return int + */ + public function getFieldBindType(string $field): int + { + $fieldType = $this->getFieldType($field); + + return $this->connection->getFieldBindType($fieldType ?: ''); + } + +} diff --git a/src/db/concern/TimeFieldQuery.php b/src/db/concern/TimeFieldQuery.php new file mode 100644 index 0000000..59e9b37 --- /dev/null +++ b/src/db/concern/TimeFieldQuery.php @@ -0,0 +1,214 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db\concern; + +/** + * 时间查询支持 + */ +trait TimeFieldQuery +{ + /** + * 日期查询表达式 + * @var array + */ + protected $timeRule = [ + 'today' => ['today', 'tomorrow'], + 'yesterday' => ['yesterday', 'today'], + 'week' => ['this week 00:00:00', 'next week 00:00:00'], + 'last week' => ['last week 00:00:00', 'this week 00:00:00'], + 'month' => ['first Day of this month 00:00:00', 'first Day of next month 00:00:00'], + 'last month' => ['first Day of last month 00:00:00', 'first Day of this month 00:00:00'], + 'year' => ['this year 1/1', 'next year 1/1'], + 'last year' => ['last year 1/1', 'this year 1/1'], + ]; + + /** + * 添加日期或者时间查询规则 + * @access public + * @param array $rule 时间表达式 + * @return $this + */ + public function timeRule(array $rule) + { + $this->timeRule = array_merge($this->timeRule, $rule); + return $this; + } + + /** + * 查询日期或者时间 + * @access public + * @param string $field 日期字段名 + * @param string $op 比较运算符或者表达式 + * @param string|array $range 比较范围 + * @param string $logic AND OR + * @return $this + */ + public function whereTime(string $field, string $op, $range = null, string $logic = 'AND') + { + if (is_null($range)) { + if (isset($this->timeRule[$op])) { + $range = $this->timeRule[$op]; + } else { + $range = $op; + } + $op = is_array($range) ? 'between' : '>='; + } + + return $this->parseWhereExp($logic, $field, strtolower($op) . ' time', $range, [], true); + } + + /** + * 查询某个时间间隔数据 + * @access public + * @param string $field 日期字段名 + * @param string $start 开始时间 + * @param string $interval 时间间隔单位 day/month/year/week/hour/minute/second + * @param int $step 间隔 + * @param string $logic AND OR + * @return $this + */ + public function whereTimeInterval(string $field, string $start, string $interval = 'day', int $step = 1, string $logic = 'AND') + { + $startTime = strtotime($start); + $endTime = strtotime(($step > 0 ? '+' : '-') . abs($step) . ' ' . $interval . (abs($step) > 1 ? 's' : ''), $startTime); + + return $this->whereTime($field, 'between', $step > 0 ? [$startTime, $endTime] : [$endTime, $startTime], $logic); + } + + /** + * 查询月数据 whereMonth('time_field', '2018-1') + * @access public + * @param string $field 日期字段名 + * @param string $month 月份信息 + * @param int $step 间隔 + * @param string $logic AND OR + * @return $this + */ + public function whereMonth(string $field, string $month = 'this month', int $step = 1, string $logic = 'AND') + { + if (in_array($month, ['this month', 'last month'])) { + $month = date('Y-m', strtotime($month)); + } + + return $this->whereTimeInterval($field, $month, 'month', $step, $logic); + } + + /** + * 查询周数据 whereWeek('time_field', '2018-1-1') 从2018-1-1开始的一周数据 + * @access public + * @param string $field 日期字段名 + * @param string $week 周信息 + * @param int $step 间隔 + * @param string $logic AND OR + * @return $this + */ + public function whereWeek(string $field, string $week = 'this week', int $step = 1, string $logic = 'AND') + { + if (in_array($week, ['this week', 'last week'])) { + $week = date('Y-m-d', strtotime($week)); + } + + return $this->whereTimeInterval($field, $week, 'week', $step, $logic); + } + + /** + * 查询年数据 whereYear('time_field', '2018') + * @access public + * @param string $field 日期字段名 + * @param string $year 年份信息 + * @param int $step 间隔 + * @param string $logic AND OR + * @return $this + */ + public function whereYear(string $field, string $year = 'this year', int $step = 1, string $logic = 'AND') + { + if (in_array($year, ['this year', 'last year'])) { + $year = date('Y', strtotime($year)); + } + + return $this->whereTimeInterval($field, $year . '-1-1', 'year', $step, $logic); + } + + /** + * 查询日数据 whereDay('time_field', '2018-1-1') + * @access public + * @param string $field 日期字段名 + * @param string $day 日期信息 + * @param int $step 间隔 + * @param string $logic AND OR + * @return $this + */ + public function whereDay(string $field, string $day = 'today', int $step = 1, string $logic = 'AND') + { + if (in_array($day, ['today', 'yesterday'])) { + $day = date('Y-m-d', strtotime($day)); + } + + return $this->whereTimeInterval($field, $day, 'day', $step, $logic); + } + + /** + * 查询日期或者时间范围 whereBetweenTime('time_field', '2018-1-1','2018-1-15') + * @access public + * @param string $field 日期字段名 + * @param string|int $startTime 开始时间 + * @param string|int $endTime 结束时间 + * @param string $logic AND OR + * @return $this + */ + public function whereBetweenTime(string $field, $startTime, $endTime, string $logic = 'AND') + { + return $this->whereTime($field, 'between', [$startTime, $endTime], $logic); + } + + /** + * 查询日期或者时间范围 whereNotBetweenTime('time_field', '2018-1-1','2018-1-15') + * @access public + * @param string $field 日期字段名 + * @param string|int $startTime 开始时间 + * @param string|int $endTime 结束时间 + * @return $this + */ + public function whereNotBetweenTime(string $field, $startTime, $endTime) + { + return $this->whereTime($field, '<', $startTime) + ->whereTime($field, '>', $endTime); + } + + /** + * 查询当前时间在两个时间字段范围 whereBetweenTimeField('start_time', 'end_time') + * @access public + * @param string $startField 开始时间字段 + * @param string $endField 结束时间字段 + * @return $this + */ + public function whereBetweenTimeField(string $startField, string $endField) + { + return $this->whereTime($startField, '<=', time()) + ->whereTime($endField, '>=', time()); + } + + /** + * 查询当前时间不在两个时间字段范围 whereNotBetweenTimeField('start_time', 'end_time') + * @access public + * @param string $startField 开始时间字段 + * @param string $endField 结束时间字段 + * @return $this + */ + public function whereNotBetweenTimeField(string $startField, string $endField) + { + return $this->whereTime($startField, '>', time()) + ->whereTime($endField, '<', time(), 'OR'); + } + +} diff --git a/src/db/concern/Transaction.php b/src/db/concern/Transaction.php new file mode 100644 index 0000000..93a1417 --- /dev/null +++ b/src/db/concern/Transaction.php @@ -0,0 +1,64 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db\concern; + +/** + * 事务支持 + */ +trait Transaction +{ + + /** + * 执行数据库事务 + * @access public + * @param callable $callback 数据操作方法回调 + * @return mixed + */ + public function transaction(callable $callback) + { + return $this->connection->transaction($callback); + } + + /** + * 启动事务 + * @access public + * @return void + */ + public function startTrans(): void + { + $this->connection->startTrans(); + } + + /** + * 用于非自动提交状态下面的查询提交 + * @access public + * @return void + * @throws PDOException + */ + public function commit(): void + { + $this->connection->commit(); + } + + /** + * 事务回滚 + * @access public + * @return void + * @throws PDOException + */ + public function rollback(): void + { + $this->connection->rollback(); + } + +} diff --git a/src/db/concern/WhereQuery.php b/src/db/concern/WhereQuery.php new file mode 100644 index 0000000..0bb6090 --- /dev/null +++ b/src/db/concern/WhereQuery.php @@ -0,0 +1,540 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db\concern; + +use Closure; +use think\db\BaseQuery; +use think\db\Raw; + +trait WhereQuery +{ + /** + * 指定AND查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @return $this + */ + public function where($field, $op = null, $condition = null) + { + if ($field instanceof $this) { + $this->parseQueryWhere($field); + return $this; + } elseif (true === $field || 1 === $field) { + $this->options['where']['AND'][] = true; + return $this; + } + + $param = func_get_args(); + array_shift($param); + return $this->parseWhereExp('AND', $field, $op, $condition, $param); + } + + /** + * 解析Query对象查询条件 + * @access public + * @param BaseQuery $query 查询对象 + * @return void + */ + protected function parseQueryWhere(BaseQuery $query): void + { + $this->options['where'] = $query->getOptions('where'); + + if ($query->getOptions('via')) { + $via = $query->getOptions('via'); + foreach ($this->options['where'] as $logic => &$where) { + foreach ($where as $key => &$val) { + if (is_array($val) && !strpos($val[0], '.')) { + $val[0] = $via . '.' . $val[0]; + } + } + } + } + + $this->bind($query->getBind(false)); + } + + /** + * 指定OR查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @return $this + */ + public function whereOr($field, $op = null, $condition = null) + { + $param = func_get_args(); + array_shift($param); + return $this->parseWhereExp('OR', $field, $op, $condition, $param); + } + + /** + * 指定XOR查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @return $this + */ + public function whereXor($field, $op = null, $condition = null) + { + $param = func_get_args(); + array_shift($param); + return $this->parseWhereExp('XOR', $field, $op, $condition, $param); + } + + /** + * 指定Null查询条件 + * @access public + * @param mixed $field 查询字段 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereNull(string $field, string $logic = 'AND') + { + return $this->parseWhereExp($logic, $field, 'NULL', null, [], true); + } + + /** + * 指定NotNull查询条件 + * @access public + * @param mixed $field 查询字段 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereNotNull(string $field, string $logic = 'AND') + { + return $this->parseWhereExp($logic, $field, 'NOTNULL', null, [], true); + } + + /** + * 指定Exists查询条件 + * @access public + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereExists($condition, string $logic = 'AND') + { + if (is_string($condition)) { + $condition = new Raw($condition); + } + + $this->options['where'][strtoupper($logic)][] = ['', 'EXISTS', $condition]; + return $this; + } + + /** + * 指定NotExists查询条件 + * @access public + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereNotExists($condition, string $logic = 'AND') + { + if (is_string($condition)) { + $condition = new Raw($condition); + } + + $this->options['where'][strtoupper($logic)][] = ['', 'NOT EXISTS', $condition]; + return $this; + } + + /** + * 指定In查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereIn(string $field, $condition, string $logic = 'AND') + { + return $this->parseWhereExp($logic, $field, 'IN', $condition, [], true); + } + + /** + * 指定NotIn查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereNotIn(string $field, $condition, string $logic = 'AND') + { + return $this->parseWhereExp($logic, $field, 'NOT IN', $condition, [], true); + } + + /** + * 指定Like查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereLike(string $field, $condition, string $logic = 'AND') + { + return $this->parseWhereExp($logic, $field, 'LIKE', $condition, [], true); + } + + /** + * 指定NotLike查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereNotLike(string $field, $condition, string $logic = 'AND') + { + return $this->parseWhereExp($logic, $field, 'NOT LIKE', $condition, [], true); + } + + /** + * 指定Between查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereBetween(string $field, $condition, string $logic = 'AND') + { + return $this->parseWhereExp($logic, $field, 'BETWEEN', $condition, [], true); + } + + /** + * 指定NotBetween查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereNotBetween(string $field, $condition, string $logic = 'AND') + { + return $this->parseWhereExp($logic, $field, 'NOT BETWEEN', $condition, [], true); + } + + /** + * 指定FIND_IN_SET查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereFindInSet(string $field, $condition, string $logic = 'AND') + { + return $this->parseWhereExp($logic, $field, 'FIND IN SET', $condition, [], true); + } + + /** + * 比较两个字段 + * @access public + * @param string $field1 查询字段 + * @param string $operator 比较操作符 + * @param string $field2 比较字段 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereColumn(string $field1, string $operator, string $field2 = null, string $logic = 'AND') + { + if (is_null($field2)) { + $field2 = $operator; + $operator = '='; + } + + return $this->parseWhereExp($logic, $field1, 'COLUMN', [$operator, $field2], [], true); + } + + /** + * 设置软删除字段及条件 + * @access public + * @param string $field 查询字段 + * @param mixed $condition 查询条件 + * @return $this + */ + public function useSoftDelete(string $field, $condition = null) + { + if ($field) { + $this->options['soft_delete'] = [$field, $condition]; + } + + return $this; + } + + /** + * 指定Exp查询条件 + * @access public + * @param mixed $field 查询字段 + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereExp(string $field, string $where, array $bind = [], string $logic = 'AND') + { + if (!empty($bind)) { + $this->bindParams($where, $bind); + } + + $this->options['where'][$logic][] = [$field, 'EXP', new Raw($where)]; + + return $this; + } + + /** + * 指定字段Raw查询 + * @access public + * @param string $field 查询字段表达式 + * @param mixed $op 查询表达式 + * @param string $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereFieldRaw(string $field, $op, $condition = null, string $logic = 'AND') + { + if (is_null($condition)) { + $condition = $op; + $op = '='; + } + + $this->options['where'][$logic][] = [new Raw($field), $op, $condition]; + return $this; + } + + /** + * 指定表达式查询条件 + * @access public + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereRaw(string $where, array $bind = [], string $logic = 'AND') + { + if (!empty($bind)) { + $this->bindParams($where, $bind); + } + + $this->options['where'][$logic][] = new Raw($where); + + return $this; + } + + /** + * 指定表达式查询条件 OR + * @access public + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @return $this + */ + public function whereOrRaw(string $where, array $bind = []) + { + return $this->whereRaw($where, $bind, 'OR'); + } + + /** + * 分析查询表达式 + * @access protected + * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @param array $param 查询参数 + * @param bool $strict 严格模式 + * @return $this + */ + protected function parseWhereExp(string $logic, $field, $op, $condition, array $param = [], bool $strict = false) + { + $logic = strtoupper($logic); + + if (is_string($field) && !empty($this->options['via']) && false === strpos($field, '.')) { + $field = $this->options['via'] . '.' . $field; + } + + if ($field instanceof Raw) { + return $this->whereRaw($field, is_array($op) ? $op : [], $logic); + } elseif ($strict) { + // 使用严格模式查询 + if ('=' == $op) { + $where = $this->whereEq($field, $condition); + } else { + $where = [$field, $op, $condition, $logic]; + } + } elseif (is_array($field)) { + // 解析数组批量查询 + return $this->parseArrayWhereItems($field, $logic); + } elseif ($field instanceof Closure) { + $where = $field; + } elseif (is_string($field)) { + if (preg_match('/[,=\<\'\"\(\s]/', $field)) { + return $this->whereRaw($field, is_array($op) ? $op : [], $logic); + } elseif (is_string($op) && strtolower($op) == 'exp') { + $bind = isset($param[2]) && is_array($param[2]) ? $param[2] : []; + return $this->whereExp($field, $condition, $bind, $logic); + } + + $where = $this->parseWhereItem($logic, $field, $op, $condition, $param); + } + + if (!empty($where)) { + $this->options['where'][$logic][] = $where; + } + + return $this; + } + + /** + * 分析查询表达式 + * @access protected + * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @param array $param 查询参数 + * @return array + */ + protected function parseWhereItem(string $logic, $field, $op, $condition, array $param = []): array + { + if (is_array($op)) { + // 同一字段多条件查询 + array_unshift($param, $field); + $where = $param; + } elseif ($field && is_null($condition)) { + if (is_string($op) && in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) { + // null查询 + $where = [$field, $op, '']; + } elseif ('=' === $op || is_null($op)) { + $where = [$field, 'NULL', '']; + } elseif ('<>' === $op) { + $where = [$field, 'NOTNULL', '']; + } else { + // 字段相等查询 + $where = $this->whereEq($field, $op); + } + } elseif (in_array(strtoupper($op), ['EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) { + $where = [$field, $op, is_string($condition) ? new Raw($condition) : $condition]; + } else { + $where = $field ? [$field, $op, $condition, $param[2] ?? null] : []; + } + + return $where; + } + + /** + * 相等查询的主键处理 + * @access protected + * @param string $field 字段名 + * @param mixed $value 字段值 + * @return array + */ + protected function whereEq(string $field, $value): array + { + if ($this->getPk() == $field) { + $this->options['key'] = $value; + } + + return [$field, '=', $value]; + } + + /** + * 数组批量查询 + * @access protected + * @param array $field 批量查询 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + protected function parseArrayWhereItems(array $field, string $logic) + { + if (key($field) !== 0) { + $where = []; + foreach ($field as $key => $val) { + if ($val instanceof Raw) { + $where[] = [$key, 'exp', $val]; + } else { + $where[] = is_null($val) ? [$key, 'NULL', ''] : [$key, is_array($val) ? 'IN' : '=', $val]; + } + } + } else { + // 数组批量查询 + $where = $field; + } + + if (!empty($where)) { + $this->options['where'][$logic] = isset($this->options['where'][$logic]) ? + array_merge($this->options['where'][$logic], $where) : $where; + } + + return $this; + } + + /** + * 去除某个查询条件 + * @access public + * @param string $field 查询字段 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function removeWhereField(string $field, string $logic = 'AND') + { + $logic = strtoupper($logic); + + if (isset($this->options['where'][$logic])) { + foreach ($this->options['where'][$logic] as $key => $val) { + if (is_array($val) && $val[0] == $field) { + unset($this->options['where'][$logic][$key]); + } + } + } + + return $this; + } + + /** + * 条件查询 + * @access public + * @param mixed $condition 满足条件(支持闭包) + * @param Closure|array $query 满足条件后执行的查询表达式(闭包或数组) + * @param Closure|array $otherwise 不满足条件后执行 + * @return $this + */ + public function when($condition, $query, $otherwise = null) + { + if ($condition instanceof Closure) { + $condition = $condition($this); + } + + if ($condition) { + if ($query instanceof Closure) { + $query($this, $condition); + } elseif (is_array($query)) { + $this->where($query); + } + } elseif ($otherwise) { + if ($otherwise instanceof Closure) { + $otherwise($this, $condition); + } elseif (is_array($otherwise)) { + $this->where($otherwise); + } + } + + return $this; + } +} diff --git a/src/db/connector/Mysql.php b/src/db/connector/Mysql.php index 82debf6..8c7a962 100644 --- a/src/db/connector/Mysql.php +++ b/src/db/connector/Mysql.php @@ -13,14 +13,65 @@ declare (strict_types = 1); namespace think\db\connector; use PDO; -use think\db\Connection; +use think\db\PDOConnection; /** * mysql数据库驱动 */ -class Mysql extends Connection +class Mysql extends PDOConnection { + /** + * 数据库连接参数配置 + * @var array + */ + protected $config = [ + // 数据库类型 + 'type' => 'mysql', + // 服务器地址 + 'hostname' => '127.0.0.1', + // 数据库名 + 'database' => '', + // 用户名 + 'username' => '', + // 密码 + 'password' => '', + // 端口 + 'hostport' => '', + // 连接dsn + 'dsn' => '', + // 数据库连接参数 + 'params' => [], + // 数据库编码默认采用utf8 + 'charset' => 'utf8', + // 数据库表前缀 + 'prefix' => '', + // 数据库调试模式 + 'debug' => false, + // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) + 'deploy' => 0, + // 数据库读写是否分离 主从式有效 + 'rw_separate' => false, + // 读写分离后 主服务器数量 + 'master_num' => 1, + // 指定从服务器序号 + 'slave_no' => '', + // 模型写入后自动读取主服务器 + 'read_master' => false, + // 是否严格检查字段是否存在 + 'fields_strict' => true, + // 是否需要进行SQL性能分析 + 'sql_explain' => false, + // Builder类 + 'builder' => '', + // Query类 + 'query' => '', + // 是否需要断线重连 + 'break_reconnect' => false, + // 断线标识字符串 + 'break_match_str' => [], + ]; + /** * 解析pdo连接的dsn信息 * @access protected @@ -78,6 +129,7 @@ class Mysql extends Connection 'default' => $val['default'], 'primary' => (strtolower($val['key']) == 'pri'), 'autoinc' => (strtolower($val['extra']) == 'auto_increment'), + 'comment' => $val['comment'], ]; } } diff --git a/src/facade/Db.php b/src/facade/Db.php index b31a6aa..8c62107 100644 --- a/src/facade/Db.php +++ b/src/facade/Db.php @@ -16,49 +16,6 @@ use think\Facade; /** * @see \think\Db * @mixin \think\Db - * @method object buildQuery(string $query, mixed $connection) static 创建一个新的查询对象 - * @method \think\db\Query connect(array $config =[], mixed $name = false) static 连接/切换数据库连接 - * @method \think\db\Connection getConnection() static 获取数据库连接对象 - * @method \think\db\Query master() static 从主服务器读取数据 - * @method \think\db\Query table(string $table) static 指定数据表(含前缀) - * @method \think\db\Query name(string $name) static 指定数据表(不含前缀) - * @method \think\db\Raw raw(string $value) static 使用表达式设置数据 - * @method \think\db\Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件 - * @method \think\db\Query whereRaw(string $where, array $bind = []) static 表达式查询 - * @method \think\db\Query whereExp(string $field, string $condition, array $bind = []) static 字段表达式查询 - * @method \think\db\Query when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询 - * @method \think\db\Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询 - * @method \think\db\Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询 - * @method \think\db\Query field(mixed $field, boolean $except = false) static 指定查询字段 - * @method \think\db\Query fieldRaw(string $field, array $bind = []) static 指定查询字段 - * @method \think\db\Query union(mixed $union, boolean $all = false) static UNION查询 - * @method \think\db\Query limit(mixed $offset, integer $length = null) static 查询LIMIT - * @method \think\db\Query order(mixed $field, string $order = null) static 查询ORDER - * @method \think\db\Query orderRaw(string $field, array $bind = []) static 查询ORDER - * @method \think\db\Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存 - * @method \think\db\Query withAttr(string $name, callable $callback = null) static 使用获取器获取数据 - * @method mixed value(string $field) static 获取某个字段的值 - * @method array column(string $field, string $key = '') static 获取某个列的值 - * @method mixed find(mixed $data = null) static 查询单个记录 - * @method mixed select(mixed $data = null) static 查询多个记录 - * @method integer save(boolean $forceInsert = false) static 保存记录 自动判断insert或者update - * @method integer insert(array $data, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录 - * @method integer insertGetId(array $data, string $sequence = null) static 插入一条记录并返回自增ID - * @method integer insertAll(array $dataSet) static 插入多条记录 - * @method integer update(array $data) static 更新记录 - * @method integer delete(mixed $data = null) static 删除记录 - * @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据 - * @method \Generator cursor(mixed $data = null) static 使用游标查找记录 - * @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询 - * @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行 - * @method \think\Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询 - * @method mixed transaction(callable $callback) static 执行数据库事务 - * @method void startTrans() static 启动事务 - * @method void commit() static 用于非自动提交状态下面的查询提交 - * @method void rollback() static 事务回滚 - * @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句 - * @method string getLastInsID(string $sequence = null) static 获取最近插入的ID - * @method mixed getConfig(string $name = '') static 获取数据库的配置参数 */ class Db extends Facade { diff --git a/src/model/Collection.php b/src/model/Collection.php index 6a07c8f..6e1dda9 100644 --- a/src/model/Collection.php +++ b/src/model/Collection.php @@ -74,7 +74,7 @@ class Collection extends BaseCollection /** * 设置需要隐藏的输出属性 * @access public - * @param array $hidden 属性列表 + * @param array $hidden 属性列表 * @return $this */ public function hidden(array $hidden) @@ -104,7 +104,7 @@ class Collection extends BaseCollection /** * 设置需要追加的输出属性 * @access public - * @param array $append 属性列表 + * @param array $append 属性列表 * @return $this */ public function append(array $append) @@ -116,6 +116,21 @@ class Collection extends BaseCollection return $this; } + /** + * 设置父模型 + * @access public + * @param Model $parent 父模型 + * @return $this + */ + public function setParent(Model $parent) + { + $this->each(function (Model $model) use ($parent) { + $model->setParent($parent); + }); + + return $this; + } + /** * 设置数据字段获取器 * @access public @@ -135,12 +150,12 @@ class Collection extends BaseCollection /** * 绑定(一对一)关联属性到当前模型 * @access protected - * @param string $relation 关联名称 - * @param array $attrs 绑定属性 + * @param string $relation 关联名称 + * @param array $attrs 绑定属性 * @return $this * @throws Exception */ - public function bindAttr($relation, $attrs = []) + public function bindAttr(string $relation, array $attrs = []) { $this->each(function (Model $model) use ($relation, $attrs) { $model->bindAttr($relation, $attrs); @@ -153,8 +168,8 @@ class Collection extends BaseCollection * 按指定键整理数据 * * @access public - * @param mixed $items 数据 - * @param string $indexKey 键名 + * @param mixed $items 数据 + * @param string $indexKey 键名 * @return array */ public function dictionary($items = null, string &$indexKey = null) @@ -180,8 +195,8 @@ class Collection extends BaseCollection * 比较数据集,返回差集 * * @access public - * @param mixed $items 数据 - * @param string $indexKey 指定比较的键名 + * @param mixed $items 数据 + * @param string $indexKey 指定比较的键名 * @return static */ public function diff($items, string $indexKey = null) @@ -208,8 +223,8 @@ class Collection extends BaseCollection * 比较数据集,返回交集 * * @access public - * @param mixed $items 数据 - * @param string $indexKey 指定比较的键名 + * @param mixed $items 数据 + * @param string $indexKey 指定比较的键名 * @return static */ public function intersect($items, string $indexKey = null) diff --git a/src/model/Pivot.php b/src/model/Pivot.php index 7a7a12a..893c01b 100644 --- a/src/model/Pivot.php +++ b/src/model/Pivot.php @@ -35,9 +35,9 @@ class Pivot extends Model /** * 架构函数 * @access public - * @param array $data 数据 - * @param Model $parent 上级模型 - * @param string $table 中间数据表名 + * @param array $data 数据 + * @param Model $parent 上级模型 + * @param string $table 中间数据表名 */ public function __construct(array $data = [], Model $parent = null, string $table = '') { diff --git a/src/model/Relation.php b/src/model/Relation.php index 55b1ad0..aa2bd50 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -66,6 +66,18 @@ abstract class Relation */ protected $selfRelation = false; + /** + * 关联数据数量限制 + * @var int + */ + protected $withLimit; + + /** + * 关联数据字段限制 + * @var array + */ + protected $withField; + /** * 获取关联的所属模型 * @access public @@ -89,11 +101,12 @@ abstract class Relation /** * 获取当前的关联模型类的实例 * @access public + * @param bool $clear 是否需要清空查询条件 * @return Model */ - public function getModel(): Model + public function getModel(bool $clear = true): Model { - return $this->query->getModel(); + return $this->query->getModel($clear); } /** @@ -110,11 +123,12 @@ abstract class Relation * 封装关联数据集 * @access public * @param array $resultSet 数据集 + * @param Model $parent 父模型 * @return mixed */ - protected function resultSetBuild(array $resultSet) + protected function resultSetBuild(array $resultSet, Model $parent = null) { - return (new $this->model)->toCollection($resultSet); + return (new $this->model)->toCollection($resultSet)->setParent($parent); } protected function getQueryFields(string $model) @@ -178,6 +192,30 @@ abstract class Relation return $this->query->delete($data); } + /** + * 限制关联数据的数量 + * @access public + * @param int $limit 关联数量限制 + * @return $this + */ + public function withLimit(int $limit) + { + $this->withLimit = $limit; + return $this; + } + + /** + * 限制关联数据的字段 + * @access public + * @param array $field 关联字段限制 + * @return $this + */ + public function withField(array $field) + { + $this->withField = $field; + return $this; + } + /** * 执行基础查询(仅执行一次) * @access protected @@ -192,8 +230,10 @@ abstract class Relation // 执行基础查询 $this->baseQuery(); - $result = call_user_func_array([$this->query->getModel(false), $method], $args); + $model = $this->query->getModel(false); + $result = call_user_func_array([$model, $method], $args); + $this->query = $model->getQuery(); return $result === $this->query ? $this : $result; } diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index fe2d53e..3c0ec22 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -13,8 +13,8 @@ declare (strict_types = 1); namespace think\model\concern; use InvalidArgumentException; +use think\Container; use think\db\Raw; -use think\facade\Db; use think\model\Relation; /** @@ -185,7 +185,7 @@ trait Attribute */ protected function getRealFieldName(string $name): string { - return $this->strict ? $name : Db::parseName($name); + return $this->strict ? $name : Container::parseName($name); } /** @@ -356,10 +356,10 @@ trait Attribute if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { // 自动写入的时间戳字段 - $value = $this->autoWriteTimestamp($name); + $value = $this->autoWriteTimestamp(); } else { // 检测修改器 - $method = 'set' . Db::parseName($name, 1) . 'Attr'; + $method = 'set' . Container::parseName($name, 1) . 'Attr'; if (method_exists($this, $method)) { $array = $this->data; @@ -422,7 +422,6 @@ trait Attribute } break; case 'datetime': - $value = is_numeric($value) ? $value : strtotime($value); $value = $this->formatDateTime('Y-m-d H:i:s.u', $value); break; @@ -463,7 +462,7 @@ trait Attribute $relation = false; $value = $this->getData($name); } catch (InvalidArgumentException $e) { - $relation = true; + $relation = $this->isRelationAttr($name); $value = null; } @@ -473,21 +472,21 @@ trait Attribute /** * 获取经过获取器处理后的数据对象的值 * @access protected - * @param string $name 字段名称 - * @param mixed $value 字段值 - * @param bool $relation 是否为关联属性 + * @param string $name 字段名称 + * @param mixed $value 字段值 + * @param bool|string $relation 是否为关联属性或者关联名 * @return mixed * @throws InvalidArgumentException */ - protected function getValue(string $name, $value, bool $relation = false) + protected function getValue(string $name, $value, $relation = false) { // 检测属性获取器 $fieldName = $this->getRealFieldName($name); - $method = 'get' . Db::parseName($name, 1) . 'Attr'; + $method = 'get' . Container::parseName($name, 1) . 'Attr'; if (isset($this->withAttr[$fieldName])) { if ($relation) { - $value = $this->getRelationValue($name); + $value = $this->getRelationValue($relation); } if (in_array($fieldName, $this->json) && is_array($this->withAttr[$fieldName])) { @@ -498,7 +497,7 @@ trait Attribute } } elseif (method_exists($this, $method)) { if ($relation) { - $value = $this->getRelationValue($name); + $value = $this->getRelationValue($relation); } $value = $this->$method($value, $this->data); @@ -508,7 +507,9 @@ trait Attribute } elseif ($this->autoWriteTimestamp && in_array($fieldName, [$this->createTime, $this->updateTime])) { $value = $this->getTimestampValue($value); } elseif ($relation) { - $value = $this->getRelationAttribute($name); + $value = $this->getRelationValue($relation); + // 保存关联对象值 + $this->relation[$name] = $value; } return $value; @@ -537,42 +538,16 @@ trait Attribute /** * 获取关联属性值 * @access protected - * @param string $name 属性名 + * @param string $relation 关联名 * @return mixed */ - protected function getRelationValue(string $name) + protected function getRelationValue(string $relation) { - $relation = $this->isRelationAttr($name); - - if (false === $relation) { - return; - } - $modelRelation = $this->$relation(); return $modelRelation instanceof Relation ? $this->getRelationData($modelRelation) : null; } - /** - * 获取并保存关联属性值 - * @access protected - * @param string $name 属性名 - * @return mixed - */ - protected function getRelationAttribute(string $name) - { - $value = $this->getRelationValue($name); - - if (!$value) { - throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name); - } - - // 保存关联对象值 - $this->relation[$name] = $value; - - return $value; - } - /** * 数据读取 类型转换 * @access protected diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index dcf53d3..884575b 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -13,8 +13,8 @@ declare (strict_types = 1); namespace think\model\concern; use think\Collection; +use think\Container; use think\Exception; -use think\facade\Db; use think\Model; use think\model\Collection as ModelCollection; use think\model\relation\OneToOne; @@ -71,7 +71,7 @@ trait Conversion */ public function appendRelationAttr(string $attr, array $append) { - $relation = Db::parseName($attr, 1, false); + $relation = Container::parseName($attr, 1, false); if (isset($this->relation[$relation])) { $model = $this->relation[$relation]; @@ -84,9 +84,9 @@ trait Conversion $key = is_numeric($key) ? $attr : $key; if (isset($this->data[$key])) { throw new Exception('bind attr has exists:' . $key); - } else { - $this->data[$key] = $model->$attr; } + + $this->data[$key] = $model->$attr; } } @@ -167,7 +167,7 @@ trait Conversion } // 关联模型对象 if (!isset($this->hidden[$key]) || true !== $this->hidden[$key]) { - $item[$key] = $val->toArray(); + $item[$key] = $val; } } elseif (isset($this->visible[$key])) { $item[$key] = $this->getAttr($key); @@ -188,25 +188,15 @@ trait Conversion { if (is_array($name)) { // 追加关联对象属性 - $relation = $this->getRelation($key); - - if (!$relation) { - $relation = $this->getAttr($key); - $relation->visible($name); - } - - $item[$key] = $relation->append($name)->toArray(); + $relation = $this->getRelation($key, true); + $item[$key] = $relation ? $relation->append($name) + ->toArray() : []; } elseif (strpos($name, '.')) { list($key, $attr) = explode('.', $name); // 追加关联对象属性 - $relation = $this->getRelation($key); - - if (!$relation) { - $relation = $this->getAttr($key); - $relation->visible([$attr]); - } - - $item[$key] = $relation->append([$attr])->toArray(); + $relation = $this->getRelation($key, true); + $item[$key] = $relation ? $relation->append([$attr]) + ->toArray() : []; } else { $value = $this->getAttr($name); $item[$name] = $value; diff --git a/src/model/concern/ModelEvent.php b/src/model/concern/ModelEvent.php index dacab99..e58161a 100644 --- a/src/model/concern/ModelEvent.php +++ b/src/model/concern/ModelEvent.php @@ -14,18 +14,12 @@ namespace think\model\concern; use think\Container; use think\exception\ModelEventException; -use think\facade\Db; /** * 模型事件处理 */ trait ModelEvent { - /** - * Event - * @var array - */ - protected $event = []; /** * 是否需要事件响应 @@ -57,11 +51,12 @@ trait ModelEvent return true; } - $call = 'on' . Db::parseName($event, 1); + $call = 'on' . Container::parseName($event, 1); try { if (method_exists(static::class, $call)) { - $result = Container::getInstance()->invoke([static::class, $call], [$this]); + $result = Container::getInstance() + ->invoke([static::class, $call], [$this]); } else { $result = true; } diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 734a790..c7a02bc 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -12,10 +12,11 @@ declare (strict_types = 1); namespace think\model\concern; +use Closure; use think\Collection; +use think\Container; use think\db\Query; use think\Exception; -use think\facade\Db; use think\Model; use think\model\Relation; use think\model\relation\BelongsTo; @@ -23,6 +24,7 @@ use think\model\relation\BelongsToMany; use think\model\relation\HasMany; use think\model\relation\HasManyThrough; use think\model\relation\HasOne; +use think\model\relation\HasOneThrough; use think\model\relation\MorphMany; use think\model\relation\MorphOne; use think\model\relation\MorphTo; @@ -95,8 +97,8 @@ trait RelationShip if (array_key_exists($name, $this->relation)) { return $this->relation[$name]; } elseif ($auto) { - $method = Db::parseName($name, 1, false); - return $this->$method()->getRelation(); + $relation = Container::parseName($name, 1, false); + return $this->getRelationValue($relation); } } @@ -111,7 +113,7 @@ trait RelationShip public function setRelation(string $name, $value, array $data = []) { // 检测修改器 - $method = 'set' . Db::parseName($name, 1) . 'Attr'; + $method = 'set' . Container::parseName($name, 1) . 'Attr'; if (method_exists($this, $method)) { $value = $this->$method($value, array_merge($this->data, $data)); @@ -135,7 +137,7 @@ trait RelationShip $subRelation = ''; $closure = null; - if ($relation instanceof \Closure) { + if ($relation instanceof Closure) { // 支持闭包查询过滤关联条件 $closure = $relation; $relation = $key; @@ -148,8 +150,8 @@ trait RelationShip list($relation, $subRelation) = explode('.', $relation, 2); } - $method = Db::parseName($relation, 1, false); - $relationName = Db::parseName($relation); + $method = Container::parseName($relation, 1, false); + $relationName = Container::parseName($relation); $relationResult = $this->$method(); @@ -224,7 +226,7 @@ trait RelationShip $subRelation = []; $closure = null; - if ($relation instanceof \Closure) { + if ($relation instanceof Closure) { $closure = $relation; $relation = $key; } @@ -238,8 +240,8 @@ trait RelationShip $subRelation = [$subRelation]; } - $relation = Db::parseName($relation, 1, false); - $relationName = Db::parseName($relation); + $relation = Container::parseName($relation, 1, false); + $relationName = Container::parseName($relation); $relationResult = $this->$relation(); @@ -266,7 +268,7 @@ trait RelationShip $subRelation = []; $closure = null; - if ($relation instanceof \Closure) { + if ($relation instanceof Closure) { $closure = $relation; $relation = $key; } @@ -280,8 +282,8 @@ trait RelationShip $subRelation = [$subRelation]; } - $relation = Db::parseName($relation, 1, false); - $relationName = Db::parseName($relation); + $relation = Container::parseName($relation, 1, false); + $relationName = Container::parseName($relation); $relationResult = $this->$relation(); @@ -311,9 +313,9 @@ trait RelationShip if (!is_null($value)) { throw new Exception('bind attr has exists:' . $key); - } else { - $this->set($key, $relation ? $relation->$attr : null); } + + $this->set($key, $relation ? $relation->$attr : null); } return $this; @@ -333,7 +335,7 @@ trait RelationShip foreach ($relations as $key => $relation) { $closure = $name = null; - if ($relation instanceof \Closure) { + if ($relation instanceof Closure) { $closure = $relation; $relation = $key; } elseif (is_string($key)) { @@ -341,11 +343,11 @@ trait RelationShip $relation = $key; } - $relation = Db::parseName($relation, 1, false); + $relation = Container::parseName($relation, 1, false); $count = $this->$relation()->relationCount($result, $closure, $aggregate, $field, $name); if (empty($name)) { - $name = Db::parseName($relation) . '_' . $aggregate; + $name = Container::parseName($relation) . '_' . $aggregate; } $result->setAttr($name, $count); @@ -385,7 +387,7 @@ trait RelationShip $foreignKey = $foreignKey ?: $this->getForeignKey((new $model)->getName()); $localKey = $localKey ?: (new $model)->getPk(); $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $relation = Db::parseName($trace[1]['function']); + $relation = Container::parseName($trace[1]['function']); return new BelongsTo($this, $model, $foreignKey, $localKey, $relation); } @@ -416,9 +418,34 @@ trait RelationShip * @param string $foreignKey 关联外键 * @param string $throughKey 关联外键 * @param string $localKey 当前主键 + * @param string $throughPk 中间表主键 * @return HasManyThrough */ - public function hasManyThrough(string $model, string $through, string $foreignKey = '', string $throughKey = '', string $localKey = ''): HasManyThrough + public function hasManyThrough(string $model, string $through, string $foreignKey = '', string $throughKey = '', string $localKey = '', string $throughPk = ''): HasManyThrough + { + // 记录当前关联信息 + $model = $this->parseModel($model); + $through = $this->parseModel($through); + $localKey = $localKey ?: $this->getPk(); + $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); + $throughKey = $throughKey ?: $this->getForeignKey((new $through)->getName()); + $throughPk = $throughPk ?: (new $through)->getPk(); + + return new HasManyThrough($this, $model, $through, $foreignKey, $throughKey, $localKey, $throughPk); + } + + /** + * HAS ONE 远程关联定义 + * @access public + * @param string $model 模型名 + * @param string $through 中间模型名 + * @param string $foreignKey 关联外键 + * @param string $throughKey 关联外键 + * @param string $localKey 当前主键 + * @param string $throughPk 中间表主键 + * @return HasOneThrough + */ + public function hasOneThrough(string $model, string $through, string $foreignKey = '', string $throughKey = '', string $localKey = '', string $throughPk = ''): HasOneThrough { // 记录当前关联信息 $model = $this->parseModel($model); @@ -426,29 +453,30 @@ trait RelationShip $localKey = $localKey ?: $this->getPk(); $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); $throughKey = $throughKey ?: $this->getForeignKey((new $through)->getName()); + $throughPk = $throughPk ?: (new $through)->getPk(); - return new HasManyThrough($this, $model, $through, $foreignKey, $throughKey, $localKey); + return new HasOneThrough($this, $model, $through, $foreignKey, $throughKey, $localKey, $throughPk); } /** * BELONGS TO MANY 关联定义 * @access public * @param string $model 模型名 - * @param string $table 中间表名 + * @param string $middle 中间表/模型名 * @param string $foreignKey 关联外键 * @param string $localKey 当前模型关联键 * @return BelongsToMany */ - public function belongsToMany(string $model, string $table = '', string $foreignKey = '', string $localKey = ''): BelongsToMany + public function belongsToMany(string $model, string $middle = '', string $foreignKey = '', string $localKey = ''): BelongsToMany { // 记录当前关联信息 $model = $this->parseModel($model); - $name = Db::parseName(Db::classBaseName($model)); - $table = $table ?: Db::parseName($this->name) . '_' . $name; + $name = Container::parseName(Container::classBaseName($model)); + $middle = $middle ?: Container::parseName($this->name) . '_' . $name; $foreignKey = $foreignKey ?: $name . '_id'; $localKey = $localKey ?: $this->getForeignKey($this->name); - return new BelongsToMany($this, $model, $table, $foreignKey, $localKey); + return new BelongsToMany($this, $model, $middle, $foreignKey, $localKey); } /** @@ -466,7 +494,7 @@ trait RelationShip if (is_null($morph)) { $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $morph = Db::parseName($trace[1]['function']); + $morph = Container::parseName($trace[1]['function']); } if (is_array($morph)) { @@ -496,7 +524,7 @@ trait RelationShip if (is_null($morph)) { $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $morph = Db::parseName($trace[1]['function']); + $morph = Container::parseName($trace[1]['function']); } $type = $type ?: get_class($this); @@ -521,7 +549,7 @@ trait RelationShip public function morphTo($morph = null, array $alias = []): MorphTo { $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $relation = Db::parseName($trace[1]['function']); + $relation = Container::parseName($trace[1]['function']); if (is_null($morph)) { $morph = $relation; @@ -549,7 +577,7 @@ trait RelationShip if (false === strpos($model, '\\')) { $path = explode('\\', static::class); array_pop($path); - array_push($path, Db::parseName($model, 1)); + array_push($path, Container::parseName($model, 1)); $model = implode('\\', $path); } @@ -565,10 +593,10 @@ trait RelationShip protected function getForeignKey(string $name): string { if (strpos($name, '\\')) { - $name = Db::classBaseName($name); + $name = Container::classBaseName($name); } - return Db::parseName($name) . '_id'; + return Container::parseName($name) . '_id'; } /** @@ -579,7 +607,7 @@ trait RelationShip */ protected function isRelationAttr(string $attr) { - $relation = Db::parseName($attr, 1, false); + $relation = Container::parseName($attr, 1, false); if (method_exists($this, $relation) && !method_exists('think\Model', $relation)) { return $relation; @@ -596,7 +624,8 @@ trait RelationShip */ protected function getRelationData(Relation $modelRelation) { - if ($this->parent && !$modelRelation->isSelfRelation() && get_class($this->parent) == get_class($modelRelation->getModel())) { + if ($this->parent && !$modelRelation->isSelfRelation() + && get_class($this->parent) == get_class($modelRelation->getModel(false))) { return $this->parent; } @@ -662,7 +691,7 @@ trait RelationShip protected function autoRelationInsert(): void { foreach ($this->relationWrite as $name => $val) { - $method = Db::parseName($name, 1, false); + $method = Container::parseName($name, 1, false); $this->$method()->save($val); } } diff --git a/src/model/concern/TimeStamp.php b/src/model/concern/TimeStamp.php index cd8230f..e207961 100644 --- a/src/model/concern/TimeStamp.php +++ b/src/model/concern/TimeStamp.php @@ -51,11 +51,32 @@ trait TimeStamp */ public function isAutoWriteTimestamp($auto) { - $this->autoWriteTimestamp = $auto; + $this->autoWriteTimestamp = $this->checkTimeFieldType($auto); return $this; } + /** + * 检测时间字段的实际类型 + * @access public + * @param bool|string $type + * @return mixed + */ + protected function checkTimeFieldType($type) + { + if (true === $type) { + if (isset($this->type[$this->createTime])) { + $type = $this->type[$this->createTime]; + } elseif (isset($this->schema[$this->createTime]) && in_array($this->schema[$this->createTime], ['datetime', 'date', 'timestamp', 'int'])) { + $type = $this->schema[$this->createTime]; + } else { + $type = $this->getFieldType($this->createTime); + } + } + + return $type; + } + /** * 获取自动写入时间字段 * @access public @@ -92,39 +113,41 @@ trait TimeStamp /** * 自动写入时间戳 * @access protected - * @param string $name 时间戳字段 * @return mixed */ - protected function autoWriteTimestamp(string $name) + protected function autoWriteTimestamp() { - $value = time(); + // 检测时间字段类型 + $type = $this->checkTimeFieldType($this->autoWriteTimestamp); - if (isset($this->type[$name])) { - $type = $this->type[$name]; + return is_string($type) ? $this->getTimeTypeValue($type) : time(); + } - if (strpos($type, ':')) { - list($type, $param) = explode(':', $type, 2); - } + /** + * 获取指定类型的时间字段值 + * @access protected + * @param string $type 时间字段类型 + * @return mixed + */ + protected function getTimeTypeValue(string $type) + { + $value = time(); - switch ($type) { - case 'datetime': - case 'date': - case 'timestamp': - $value = $this->formatDateTime('Y-m-d H:i:s.u'); - break; - default: - if (false !== strpos($type, '\\')) { + switch ($type) { + case 'datetime': + case 'date': + case 'timestamp': + $value = $this->formatDateTime('Y-m-d H:i:s.u'); + break; + default: + if (false !== strpos($type, '\\')) { + // 对象数据写入 + $obj = new $type(); + if (method_exists($obj, '__toString')) { // 对象数据写入 - $value = new $type(); - if (method_exists($value, '__toString')) { - // 对象数据写入 - $value = $value->__toString(); - } + $value = $obj->__toString(); } - } - } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), - ['datetime', 'date', 'timestamp'])) { - $value = $this->formatDateTime('Y-m-d H:i:s.u'); + } } return $value; @@ -170,7 +193,9 @@ trait TimeStamp */ protected function getTimestampValue($value) { - if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ + $type = $this->checkTimeFieldType($this->autoWriteTimestamp); + + if (is_string($type) && in_array(strtolower($type), [ 'datetime', 'date', 'timestamp', ])) { $value = $this->formatDateTime($this->dateFormat, $value); diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index f9a9b23..5794d70 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -13,7 +13,8 @@ declare (strict_types = 1); namespace think\model\relation; use Closure; -use think\facade\Db; +use think\Container; +use think\db\BaseQuery as Query; use think\Model; /** @@ -47,14 +48,14 @@ class BelongsTo extends OneToOne /** * 延迟获取关联数据 * @access public - * @param array $subRelation 子关联名 - * @param Closure $closure 闭包查询条件 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包查询条件 * @return Model */ public function getRelation(array $subRelation = [], Closure $closure = null) { if ($closure) { - $closure($this->query); + $closure($this); } $foreignKey = $this->foreignKey; @@ -75,16 +76,16 @@ class BelongsTo extends OneToOne /** * 创建关联统计子查询 * @access public - * @param Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @param string $name 聚合字段别名 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 聚合字段别名 * @return string */ public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', &$name = ''): string { if ($closure) { - $closure($this->query, $name); + $closure($this, $name); } return $this->query @@ -96,14 +97,14 @@ class BelongsTo extends OneToOne /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @param string $name 统计字段别名 + * @param Model $result 数据对象 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return integer */ - public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*', string &$name = null) + public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null) { $foreignKey = $this->foreignKey; @@ -112,11 +113,7 @@ class BelongsTo extends OneToOne } if ($closure) { - $return = $closure($this->query); - - if ($return && is_string($return)) { - $name = $return; - } + $closure($this, $name); } return $this->query @@ -136,8 +133,8 @@ class BelongsTo extends OneToOne public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query { $table = $this->query->getTable(); - $model = Db::classBaseName($this->parent); - $relation = Db::classBaseName($this->model); + $model = Container::classBaseName($this->parent); + $relation = Container::classBaseName($this->model); $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -153,19 +150,24 @@ class BelongsTo extends OneToOne /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 - * @param string $joinType JOIN类型 - * @return \think\db\Query + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 + * @return Query */ public function hasWhere($where = [], $fields = null, string $joinType = ''): Query { $table = $this->query->getTable(); - $model = Db::classBaseName($this->parent); - $relation = Db::classBaseName($this->model); + $model = Container::classBaseName($this->parent); + $relation = Container::classBaseName($this->model); if (is_array($where)) { $this->getQueryWhere($where, $relation); + } elseif ($where instanceof Query) { + $where->via($relation); + } elseif ($where instanceof Closure) { + $where($this->query->via($relation)); + $where = $this->query; } $fields = $this->getRelationQueryFields($fields, $model); @@ -180,10 +182,10 @@ class BelongsTo extends OneToOne /** * 预载入关联查询(数据集) * @access protected - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ protected function eagerlySet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null): void @@ -207,7 +209,7 @@ class BelongsTo extends OneToOne ], $localKey, $relation, $subRelation, $closure); // 关联属性名 - $attr = Db::parseName($relation); + $attr = Container::parseName($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -234,10 +236,10 @@ class BelongsTo extends OneToOne /** * 预载入关联查询(数据) * @access protected - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ protected function eagerlyOne(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void @@ -265,22 +267,19 @@ class BelongsTo extends OneToOne $this->bindAttr($relationModel, $result); } else { // 设置关联属性 - $result->setRelation(Db::parseName($relation), $relationModel); + $result->setRelation(Container::parseName($relation), $relationModel); } } /** * 添加关联数据 * @access public - * @param Model $model 关联模型对象 + * @param Model $model关联模型对象 * @return Model */ public function associate(Model $model): Model { - $foreignKey = $this->foreignKey; - $pk = $model->getPk(); - - $this->parent->setAttr($foreignKey, $model->$pk); + $this->parent->setAttr($this->foreignKey, $model->getKey()); $this->parent->save(); return $this->parent->setRelation($this->relation, $model); diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 2d3458d..7132b65 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -13,10 +13,10 @@ namespace think\model\relation; use Closure; use think\Collection; -use think\db\Query; +use think\Container; +use think\db\BaseQuery as Query; use think\db\Raw; use think\Exception; -use think\facade\Db; use think\Model; use think\model\Pivot; use think\model\Relation; @@ -56,22 +56,22 @@ class BelongsToMany extends Relation * @access public * @param Model $parent 上级模型对象 * @param string $model 模型名 - * @param string $table 中间表名 + * @param string $middle 中间表/模型名 * @param string $foreignKey 关联模型外键 * @param string $localKey 当前模型关联键 */ - public function __construct(Model $parent, string $model, string $table, string $foreignKey, string $localKey) + public function __construct(Model $parent, string $model, string $middle, string $foreignKey, string $localKey) { $this->parent = $parent; $this->model = $model; $this->foreignKey = $foreignKey; $this->localKey = $localKey; - if (false !== strpos($table, '\\')) { - $this->pivotName = $table; - $this->middle = Db::classBaseName($table); + if (false !== strpos($middle, '\\')) { + $this->pivotName = $middle; + $this->middle = Container::classBaseName($middle); } else { - $this->middle = $table; + $this->middle = $middle; } $this->query = (new $model)->db(); @@ -111,7 +111,7 @@ class BelongsToMany extends Relation */ protected function newPivot(array $data = []): Pivot { - $class = $this->pivotName ?: '\\think\\model\\Pivot'; + $class = $this->pivotName ?: Pivot::class; $pivot = new $class($data, $this->parent, $this->middle); if ($pivot instanceof Pivot) { @@ -174,10 +174,14 @@ class BelongsToMany extends Relation public function getRelation(array $subRelation = [], Closure $closure = null): Collection { if ($closure) { - $closure($this->query); + $closure($this); } - $result = $this->buildQuery()->relation($subRelation)->select(); + $result = $this->buildQuery() + ->relation($subRelation) + ->select() + ->setParent(clone $this->parent); + $this->hydratePivot($result); return $result; @@ -200,9 +204,9 @@ class BelongsToMany extends Relation /** * 重载paginate方法 * @access public - * @param int|array $listRows - * @param int|bool $simple - * @param array $config + * @param int|array $listRows + * @param int|bool $simple + * @param array $config * @return Paginator */ public function paginate($listRows = null, $simple = false, $config = []): Paginator @@ -269,12 +273,13 @@ class BelongsToMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 * @return Query * @throws Exception */ - public function hasWhere($where = [], $fields = null) + public function hasWhere($where = [], $fields = null, string $joinType = '') { throw new Exception('relation not support: hasWhere'); } @@ -282,9 +287,9 @@ class BelongsToMany extends Relation /** * 设置中间表的查询条件 * @access public - * @param string $field - * @param string $op - * @param mixed $condition + * @param string $field + * @param string $op + * @param mixed $condition * @return $this */ public function wherePivot($field, $op = null, $condition = null) @@ -296,10 +301,10 @@ class BelongsToMany extends Relation /** * 预载入关联查询(数据集) * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param Closure $closure 闭包 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void @@ -322,7 +327,7 @@ class BelongsToMany extends Relation ], $relation, $subRelation, $closure); // 关联属性名 - $attr = Db::parseName($relation); + $attr = Container::parseName($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -330,7 +335,7 @@ class BelongsToMany extends Relation $data[$result->$pk] = []; } - $result->setRelation($attr, $this->resultSetBuild($data[$result->$pk])); + $result->setRelation($attr, $this->resultSetBuild($data[$result->$pk], clone $this->parent)); } } } @@ -338,10 +343,10 @@ class BelongsToMany extends Relation /** * 预载入关联查询(单个数据) * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param Closure $closure 闭包 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ public function eagerlyResult(Model $result, string $relation, array $subRelation, Closure $closure = null): void @@ -360,18 +365,18 @@ class BelongsToMany extends Relation $data[$pk] = []; } - $result->setRelation(Db::parseName($relation), $this->resultSetBuild($data[$pk])); + $result->setRelation(Container::parseName($relation), $this->resultSetBuild($data[$pk], clone $this->parent)); } } /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @param string $name 统计字段别名 + * @param Model $result 数据对象 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return integer */ public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): float @@ -385,7 +390,7 @@ class BelongsToMany extends Relation $pk = $result->$pk; if ($closure) { - $closure($this->query, $name); + $closure($this, $name); } return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ @@ -396,20 +401,16 @@ class BelongsToMany extends Relation /** * 获取关联统计子查询 * @access public - * @param Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @param string $name 统计字段别名 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return string */ public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $return = $closure($this->query); - - if ($return && is_string($return)) { - $name = $return; - } + $closure($this, $name); } return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ @@ -422,16 +423,16 @@ class BelongsToMany extends Relation /** * 多对多 关联模型预查询 * @access protected - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param array $subRelation 子关联 - * @param Closure $closure 闭包 + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param array $subRelation 子关联 + * @param Closure $closure 闭包 * @return array */ protected function eagerlyManyToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null): array { if ($closure) { - $closure($this->query); + $closure($this); } // 预载入关联查询 支持嵌套预载入 @@ -453,9 +454,15 @@ class BelongsToMany extends Relation } } + $key = $pivot[$this->localKey]; + + if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { + continue; + } + $set->setRelation($this->pivotDataName, $this->newPivot($pivot)); - $data[$pivot[$this->localKey]][] = $set; + $data[$key][] = $set; } return $data; @@ -464,9 +471,9 @@ class BelongsToMany extends Relation /** * BELONGS TO MANY 关联查询 * @access protected - * @param string $foreignKey 关联模型关联键 - * @param string $localKey 当前模型关联键 - * @param array $condition 关联查询条件 + * @param string $foreignKey 关联模型关联键 + * @param string $localKey 当前模型关联键 + * @param array $condition 关联查询条件 * @return Query */ protected function belongsToManyQuery(string $foreignKey, string $localKey, array $condition = []): Query @@ -476,6 +483,10 @@ class BelongsToMany extends Relation $table = $this->pivot->db()->getTable(); $fields = $this->getQueryFields($tableName); + if ($this->withLimit) { + $this->query->limit($this->withLimit); + } + $query = $this->query ->field($fields) ->tableField(true, $table, 'pivot', 'pivot__'); @@ -583,7 +594,7 @@ class BelongsToMany extends Relation /** * 判断是否存在关联数据 * @access public - * @param mixed $data 数据 可以使用关联模型对象 或者 关联对象的主键 + * @param mixed $data 数据 可以使用关联模型对象 或者 关联对象的主键 * @return Pivot|false */ public function attached($data) @@ -692,22 +703,4 @@ class BelongsToMany extends Relation return $changes; } - /** - * 执行基础查询(仅执行一次) - * @access protected - * @return void - */ - protected function baseQuery(): void - { - if (empty($this->baseQuery) && $this->parent->getData()) { - $pk = $this->parent->getPk(); - $table = $this->pivot->getTable(); - - $this->query - ->join([$table => 'pivot'], 'pivot.' . $this->foreignKey . '=' . $this->query->getTable() . '.' . $this->query->getPk()) - ->where('pivot.' . $this->localKey, $this->parent->$pk); - $this->baseQuery = true; - } - } - } diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index ead78fa..868e601 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -14,8 +14,8 @@ namespace think\model\relation; use Closure; use think\Collection; -use think\db\Query; -use think\facade\Db; +use think\Container; +use think\db\BaseQuery as Query; use think\Model; use think\model\Relation; @@ -48,37 +48,34 @@ class HasMany extends Relation /** * 延迟获取关联数据 * @access public - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 - * @return \think\Collection + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包查询条件 + * @return Collection */ public function getRelation(array $subRelation = [], Closure $closure = null): Collection { if ($closure) { - $closure($this->query); + $closure($this); } - $list = $this->query - ->where($this->foreignKey, $this->parent->{$this->localKey}) - ->relation($subRelation) - ->select(); - - $parent = clone $this->parent; - - foreach ($list as &$model) { - $model->setParent($parent); + if ($this->withLimit) { + $this->query->limit($this->withLimit); } - return $list; + return $this->query + ->where($this->foreignKey, $this->parent->{$this->localKey}) + ->relation($subRelation) + ->select() + ->setParent(clone $this->parent); } /** * 预载入关联查询 * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param Closure $closure 闭包 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void @@ -94,13 +91,12 @@ class HasMany extends Relation } if (!empty($range)) { - $where = [ + $data = $this->eagerlyOneToMany([ [$this->foreignKey, 'in', $range], - ]; - $data = $this->eagerlyOneToMany($where, $relation, $subRelation, $closure); + ], $relation, $subRelation, $closure); // 关联属性名 - $attr = Db::parseName($relation); + $attr = Container::parseName($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -109,11 +105,7 @@ class HasMany extends Relation $data[$pk] = []; } - foreach ($data[$pk] as &$relationModel) { - $relationModel->setParent(clone $result); - } - - $result->setRelation($attr, $this->resultSetBuild($data[$pk])); + $result->setRelation($attr, $this->resultSetBuild($data[$pk], clone $this->parent)); } } } @@ -121,10 +113,10 @@ class HasMany extends Relation /** * 预载入关联查询 * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param Closure $closure 闭包 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void @@ -132,34 +124,31 @@ class HasMany extends Relation $localKey = $this->localKey; if (isset($result->$localKey)) { - $pk = $result->$localKey; - $where = [$this->foreignKey, '=', $pk]; - $data = $this->eagerlyOneToMany([$where], $relation, $subRelation, $closure); + $pk = $result->$localKey; + $data = $this->eagerlyOneToMany([ + [$this->foreignKey, '=', $pk], + ], $relation, $subRelation, $closure); // 关联数据封装 if (!isset($data[$pk])) { $data[$pk] = []; } - foreach ($data[$pk] as &$relationModel) { - $relationModel->setParent(clone $result); - } - - $result->setRelation(Db::parseName($relation), $this->resultSetBuild($data[$pk])); + $result->setRelation(Container::parseName($relation), $this->resultSetBuild($data[$pk], clone $this->parent)); } } /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @param string $name 统计字段别名 + * @param Model $result 数据对象 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return integer */ - public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*', string &$name = null) + public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null) { $localKey = $this->localKey; @@ -168,7 +157,7 @@ class HasMany extends Relation } if ($closure) { - $closure($this->query, $name); + $closure($this, $name); } return $this->query @@ -179,23 +168,20 @@ class HasMany extends Relation /** * 创建关联统计子查询 * @access public - * @param Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @param string $name 统计字段别名 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return string */ public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $return = $closure($this->query); - if ($return && is_string($return)) { - $name = $return; - } + $closure($this, $name); } return $this->query->alias($aggregate . '_table') - ->whereExp($aggregate . '_table.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk()) + ->whereExp($aggregate . '_table.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey) ->fetchSql() ->$aggregate($field); } @@ -203,10 +189,10 @@ class HasMany extends Relation /** * 一对多 关联模型预查询 * @access public - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param array $subRelation 子关联 - * @param \Closure $closure + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param array $subRelation 子关联 + * @param Closure $closure * @return array */ protected function eagerlyOneToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null): array @@ -217,7 +203,8 @@ class HasMany extends Relation // 预载入关联查询 支持嵌套预载入 if ($closure) { - $closure($this->query); + $this->baseQuery = true; + $closure($this); } $list = $this->query->where($where)->with($subRelation)->select(); @@ -226,7 +213,13 @@ class HasMany extends Relation $data = []; foreach ($list as $set) { - $data[$set->$foreignKey][] = $set; + $key = $set->$foreignKey; + + if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { + continue; + } + + $data[$key][] = $set; } return $data; @@ -294,8 +287,12 @@ class HasMany extends Relation { $table = $this->query->getTable(); - $model = Db::classBaseName($this->parent); - $relation = Db::classBaseName($this->model); + $model = Container::classBaseName($this->parent); + $relation = Container::classBaseName($this->model); + + if ('*' != $id) { + $id = $relation . '.' . (new $this->model)->getPk(); + } return $this->parent->db() ->alias($model) @@ -308,19 +305,24 @@ class HasMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 - * @param string $joinType JOIN类型 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 * @return Query */ public function hasWhere($where = [], $fields = null, string $joinType = ''): Query { $table = $this->query->getTable(); - $model = Db::classBaseName($this->parent); - $relation = Db::classBaseName($this->model); + $model = Container::classBaseName($this->parent); + $relation = Container::classBaseName($this->model); if (is_array($where)) { $this->getQueryWhere($where, $relation); + } elseif ($where instanceof Query) { + $where->via($relation); + } elseif ($where instanceof Closure) { + $where($this->query->via($relation)); + $where = $this->query; } $fields = $this->getRelationQueryFields($fields, $model); diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index 33b5cf3..f01801c 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -13,9 +13,8 @@ namespace think\model\relation; use Closure; use think\Collection; -use think\db\Query; -use think\Exception; -use think\facade\Db; +use think\Container; +use think\db\BaseQuery as Query; use think\Model; use think\model\Relation; @@ -31,48 +30,62 @@ class HasManyThrough extends Relation protected $throughKey; /** - * 中间表模型 + * 中间主键 * @var string */ + protected $throughPk; + + /** + * 中间表查询对象 + * @var Query + */ protected $through; /** * 架构函数 * @access public * @param Model $parent 上级模型对象 - * @param string $model 模型名 + * @param string $model 关联模型名 * @param string $through 中间模型名 * @param string $foreignKey 关联外键 - * @param string $throughKey 关联外键 - * @param string $localKey 当前主键 + * @param string $throughKey 中间关联外键 + * @param string $localKey 当前模型主键 + * @param string $throughPk 中间模型主键 */ - public function __construct(Model $parent, string $model, string $through, string $foreignKey, string $throughKey, string $localKey) + public function __construct(Model $parent, string $model, string $through, string $foreignKey, string $throughKey, string $localKey, string $throughPk) { $this->parent = $parent; $this->model = $model; - $this->through = $through; + $this->through = (new $through)->db(); $this->foreignKey = $foreignKey; $this->throughKey = $throughKey; $this->localKey = $localKey; + $this->throughPk = $throughPk; $this->query = (new $model)->db(); } /** * 延迟获取关联数据 * @access public - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 - * @return \think\Collection + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包查询条件 + * @return Collection */ - public function getRelation(array $subRelation = [], \Closure $closure = null): Collection + public function getRelation(array $subRelation = [], Closure $closure = null) { if ($closure) { - $closure($this->query); + $closure($this); } $this->baseQuery(); - return $this->query->relation($subRelation)->select(); + if ($this->withLimit) { + $this->query->limit($this->withLimit); + } + + return $this->query->relation($subRelation) + ->select() + ->setParent(clone $this->parent); } /** @@ -84,58 +97,251 @@ class HasManyThrough extends Relation * @param string $joinType JOIN类型 * @return Query */ - public function has(string $operator = '>=', int $count = 1, string $id = '*', $joinType = '') + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query { - return $this->parent; + $model = Container::parseName(Container::classBaseName($this->parent)); + $throughTable = $this->through->getTable(); + $pk = $this->throughPk; + $throughKey = $this->throughKey; + $relation = new $this->model; + $relationTable = $relation->getTable(); + + if ('*' != $id) { + $id = $relationTable . '.' . $relation->getPk(); + } + + return $this->parent->db() + ->alias($model) + ->field($model . '.*') + ->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey) + ->join($relationTable, $relationTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk) + ->group($relationTable . '.' . $this->throughKey) + ->having('count(' . $id . ')' . $operator . $count); } /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 * @return Query */ - public function hasWhere($where = [], $fields = null, $joinType = '') + public function hasWhere($where = [], $fields = null, $joinType = ''): Query { - throw new Exception('relation not support: hasWhere'); + $model = Container::parseName(Container::classBaseName($this->parent)); + $throughTable = $this->through->getTable(); + $pk = $this->throughPk; + $throughKey = $this->throughKey; + $modelTable = (new $this->model)->getTable(); + + if (is_array($where)) { + $this->getQueryWhere($where, $modelTable); + } elseif ($where instanceof Query) { + $where->via($modelTable); + } elseif ($where instanceof Closure) { + $where($this->query->via($modelTable)); + $where = $this->query; + } + + $fields = $this->getRelationQueryFields($fields, $model); + + return $this->parent->db() + ->alias($model) + ->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey) + ->join($modelTable, $modelTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk) + ->group($modelTable . '.' . $this->throughKey) + ->where($where) + ->field($fields); } /** - * 预载入关联查询 - * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 + * 预载入关联查询(数据集) + * @access protected + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ - public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure): void - {} + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null): void + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + + $range = []; + foreach ($resultSet as $result) { + // 获取关联外键列表 + if (isset($result->$localKey)) { + $range[] = $result->$localKey; + } + } + + if (!empty($range)) { + $this->query->removeWhereField($foreignKey); + + $data = $this->eagerlyWhere([ + [$this->foreignKey, 'in', $range], + ], $foreignKey, $relation, $subRelation, $closure); + + // 关联属性名 + $attr = Container::parseName($relation); + + // 关联数据封装 + foreach ($resultSet as $result) { + $pk = $result->$localKey; + if (!isset($data[$pk])) { + $data[$pk] = []; + } + + // 设置关联属性 + $result->setRelation($attr, $this->resultSetBuild($data[$pk], clone $this->parent)); + } + } + } /** - * 预载入关联查询 返回模型对象 - * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 + * 预载入关联查询(数据) + * @access protected + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void - {} + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + $pk = $result->$localKey; + + $this->query->removeWhereField($foreignKey); + + $data = $this->eagerlyWhere([ + [$foreignKey, '=', $pk], + ], $foreignKey, $relation, $subRelation, $closure); + + // 关联数据封装 + if (!isset($data[$pk])) { + $data[$pk] = []; + } + + $result->setRelation(Container::parseName($relation), $this->resultSetBuild($data[$pk], clone $this->parent)); + } + + /** + * 关联模型预查询 + * @access public + * @param array $where 关联预查询条件 + * @param string $key 关联键名 + * @param string $relation 关联名 + * @param array $subRelation 子关联 + * @param Closure $closure + * @return array + */ + protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null): array + { + // 预载入关联查询 支持嵌套预载入 + $throughList = $this->through->where($where)->select(); + $keys = $throughList->column($this->throughPk, $this->throughPk); + + if ($closure) { + $this->baseQuery = true; + $closure($this); + } + + $list = $this->query->where($this->throughKey, 'in', $keys)->select(); + + // 组装模型数据 + $data = []; + $keys = $throughList->column($this->foreignKey, $this->throughPk); + + foreach ($list as $set) { + $key = $keys[$set->{$this->throughKey}]; + + if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { + continue; + } + + $data[$key][] = $set; + } + + return $data; + } /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @return integer + * @param Model $result 数据对象 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 + * @return mixed + */ + public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null) + { + $localKey = $this->localKey; + + if (!isset($result->$localKey)) { + return 0; + } + + if ($closure) { + $closure($this, $name); + } + + $alias = Container::parseName(Container::classBaseName($this->model)); + $throughTable = $this->through->getTable(); + $pk = $this->throughPk; + $throughKey = $this->throughKey; + $modelTable = $this->parent->getTable(); + + if (false === strpos($field, '.')) { + $field = $alias . '.' . $field; + } + + return $this->query + ->alias($alias) + ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey) + ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey) + ->where($throughTable . '.' . $this->foreignKey, $result->$localKey) + ->$aggregate($field); + } + + /** + * 创建关联统计子查询 + * @access public + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 + * @return string */ - public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*') - {} + public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string + { + if ($closure) { + $closure($this, $name); + } + + $alias = Container::parseName(Container::classBaseName($this->model)); + $throughTable = $this->through->getTable(); + $pk = $this->throughPk; + $throughKey = $this->throughKey; + $modelTable = $this->parent->getTable(); + + if (false === strpos($field, '.')) { + $field = $alias . '.' . $field; + } + + return $this->query + ->alias($alias) + ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey) + ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey) + ->whereExp($throughTable . '.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey) + ->fetchSql() + ->$aggregate($field); + } /** * 执行基础查询(仅执行一次) @@ -145,10 +351,9 @@ class HasManyThrough extends Relation protected function baseQuery(): void { if (empty($this->baseQuery) && $this->parent->getData()) { - $through = $this->through; - $alias = Db::parseName(Db::classBaseName($this->model)); - $throughTable = $through::getTable(); - $pk = (new $through)->getPk(); + $alias = Container::parseName(Container::classBaseName($this->model)); + $throughTable = $this->through->getTable(); + $pk = $this->throughPk; $throughKey = $this->throughKey; $modelTable = $this->parent->getTable(); $fields = $this->getQueryFields($alias); diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 4a6ddef..15d141a 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -13,8 +13,8 @@ declare (strict_types = 1); namespace think\model\relation; use Closure; -use think\db\Query; -use think\facade\Db; +use think\Container; +use think\db\BaseQuery as Query; use think\Model; /** @@ -46,8 +46,8 @@ class HasOne extends OneToOne /** * 延迟获取关联数据 * @access public - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包查询条件 * @return Model */ public function getRelation(array $subRelation = [], Closure $closure = null) @@ -55,7 +55,7 @@ class HasOne extends OneToOne $localKey = $this->localKey; if ($closure) { - $closure($this->query); + $closure($this); } // 判断关联类型执行查询 @@ -75,20 +75,20 @@ class HasOne extends OneToOne /** * 创建关联统计子查询 * @access public - * @param \Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @param string $name 统计字段别名 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return string */ public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $closure($this->query, $name); + $closure($this, $name); } return $this->query - ->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk()) + ->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey) ->fetchSql() ->$aggregate($field); } @@ -96,11 +96,11 @@ class HasOne extends OneToOne /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @param string $name 统计字段别名 + * @param Model $result 数据对象 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return integer */ public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null) @@ -112,10 +112,7 @@ class HasOne extends OneToOne } if ($closure) { - $return = $closure($this->query); - if ($return && is_string($return)) { - $name = $return; - } + $closure($this, $name); } return $this->query @@ -135,8 +132,8 @@ class HasOne extends OneToOne public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query { $table = $this->query->getTable(); - $model = Db::classBaseName($this->parent); - $relation = Db::classBaseName($this->model); + $model = Container::classBaseName($this->parent); + $relation = Container::classBaseName($this->model); $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -152,19 +149,24 @@ class HasOne extends OneToOne /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 - * @param string $joinType JOIN类型 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 * @return Query */ public function hasWhere($where = [], $fields = null, string $joinType = ''): Query { $table = $this->query->getTable(); - $model = Db::classBaseName($this->parent); - $relation = Db::classBaseName($this->model); + $model = Container::classBaseName($this->parent); + $relation = Container::classBaseName($this->model); if (is_array($where)) { $this->getQueryWhere($where, $relation); + } elseif ($where instanceof Query) { + $where->via($relation); + } elseif ($where instanceof Closure) { + $where($this->query->via($relation)); + $where = $this->query; } $fields = $this->getRelationQueryFields($fields, $model); @@ -179,10 +181,10 @@ class HasOne extends OneToOne /** * 预载入关联查询(数据集) * @access protected - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ protected function eagerlySet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null): void @@ -206,7 +208,7 @@ class HasOne extends OneToOne ], $foreignKey, $relation, $subRelation, $closure); // 关联属性名 - $attr = Db::parseName($relation); + $attr = Container::parseName($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -233,10 +235,10 @@ class HasOne extends OneToOne /** * 预载入关联查询(数据) * @access protected - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ protected function eagerlyOne(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void @@ -263,7 +265,7 @@ class HasOne extends OneToOne // 绑定关联属性 $this->bindAttr($relationModel, $result); } else { - $result->setRelation(Db::parseName($relation), $relationModel); + $result->setRelation(Container::parseName($relation), $relationModel); } } diff --git a/src/model/relation/HasOneThrough.php b/src/model/relation/HasOneThrough.php new file mode 100644 index 0000000..1311d67 --- /dev/null +++ b/src/model/relation/HasOneThrough.php @@ -0,0 +1,161 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\relation; + +use Closure; +use think\Container; +use think\Model; + +/** + * 远程一对一关联类 + */ +class HasOneThrough extends HasManyThrough +{ + + /** + * 延迟获取关联数据 + * @access public + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包查询条件 + * @return Model + */ + public function getRelation(array $subRelation = [], Closure $closure = null) + { + if ($closure) { + $closure($this); + } + + $this->baseQuery(); + + $relationModel = $this->query->relation($subRelation)->find(); + + if ($relationModel) { + $relationModel->setParent(clone $this->parent); + } + + return $relationModel; + } + + /** + * 预载入关联查询(数据集) + * @access protected + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 + * @return void + */ + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null): void + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + + $range = []; + foreach ($resultSet as $result) { + // 获取关联外键列表 + if (isset($result->$localKey)) { + $range[] = $result->$localKey; + } + } + + if (!empty($range)) { + $this->query->removeWhereField($foreignKey); + + $data = $this->eagerlyWhere([ + [$this->foreignKey, 'in', $range], + ], $foreignKey, $relation, $subRelation, $closure); + + // 关联属性名 + $attr = Container::parseName($relation); + + // 关联数据封装 + foreach ($resultSet as $result) { + // 关联模型 + if (!isset($data[$result->$localKey])) { + $relationModel = null; + } else { + $relationModel = $data[$result->$localKey]; + $relationModel->setParent(clone $result); + $relationModel->exists(true); + } + + // 设置关联属性 + $result->setRelation($attr, $relationModel); + } + } + } + + /** + * 预载入关联查询(数据) + * @access protected + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 + * @return void + */ + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + + $this->query->removeWhereField($foreignKey); + + $data = $this->eagerlyWhere([ + [$foreignKey, '=', $result->$localKey], + ], $foreignKey, $relation, $subRelation, $closure); + + // 关联模型 + if (!isset($data[$result->$localKey])) { + $relationModel = null; + } else { + $relationModel = $data[$result->$localKey]; + $relationModel->setParent(clone $result); + $relationModel->exists(true); + } + + $result->setRelation(Container::parseName($relation), $relationModel); + } + + /** + * 关联模型预查询 + * @access public + * @param array $where 关联预查询条件 + * @param string $key 关联键名 + * @param string $relation 关联名 + * @param array $subRelation 子关联 + * @param Closure $closure + * @return array + */ + protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null): array + { + // 预载入关联查询 支持嵌套预载入 + $keys = $this->through->where($where)->column($this->throughPk, $this->foreignKey); + + if ($closure) { + $closure($this); + } + + $list = $this->query->where($this->throughKey, 'in', $keys)->select(); + + // 组装模型数据 + $data = []; + $keys = array_flip($keys); + + foreach ($list as $set) { + $data[$keys[$set->{$this->throughKey}]] = $set; + } + + return $data; + } + +} diff --git a/src/model/relation/MorphMany.php b/src/model/relation/MorphMany.php index 23cb6e9..a1dcc33 100644 --- a/src/model/relation/MorphMany.php +++ b/src/model/relation/MorphMany.php @@ -13,9 +13,9 @@ namespace think\model\relation; use Closure; use think\Collection; -use think\db\Query; +use think\Container; +use think\db\BaseQuery as Query; use think\Exception; -use think\facade\Db; use think\Model; use think\model\Relation; @@ -64,26 +64,25 @@ class MorphMany extends Relation /** * 延迟获取关联数据 * @access public - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 - * @return \think\Collection + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包查询条件 + * @return Collection */ public function getRelation(array $subRelation = [], Closure $closure = null): Collection { if ($closure) { - $closure($this->query); + $closure($this); } $this->baseQuery(); - $list = $this->query->relation($subRelation)->select(); - $parent = clone $this->parent; - - foreach ($list as &$model) { - $model->setParent($parent); + if ($this->withLimit) { + $this->query->limit($this->withLimit); } - return $list; + return $this->query->relation($subRelation) + ->select() + ->setParent(clone $this->parent); } /** @@ -103,8 +102,9 @@ class MorphMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 * @return Query */ public function hasWhere($where = [], $fields = null, string $joinType = '') @@ -115,10 +115,10 @@ class MorphMany extends Relation /** * 预载入关联查询 * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void @@ -144,7 +144,7 @@ class MorphMany extends Relation $data = $this->eagerlyMorphToMany($where, $relation, $subRelation, $closure); // 关联属性名 - $attr = Db::parseName($relation); + $attr = Container::parseName($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -152,12 +152,7 @@ class MorphMany extends Relation $data[$result->$pk] = []; } - foreach ($data[$result->$pk] as &$relationModel) { - $relationModel->setParent(clone $result); - $relationModel->exists(true); - } - - $result->setRelation($attr, $this->resultSetBuild($data[$result->$pk])); + $result->setRelation($attr, $this->resultSetBuild($data[$result->$pk], clone $this->parent)); } } } @@ -165,10 +160,10 @@ class MorphMany extends Relation /** * 预载入关联查询 * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void @@ -176,37 +171,31 @@ class MorphMany extends Relation $pk = $result->getPk(); if (isset($result->$pk)) { - $key = $result->$pk; - $where = [ + $key = $result->$pk; + $data = $this->eagerlyMorphToMany([ [$this->morphKey, '=', $key], [$this->morphType, '=', $this->type], - ]; - $data = $this->eagerlyMorphToMany($where, $relation, $subRelation, $closure); + ], $relation, $subRelation, $closure); if (!isset($data[$key])) { $data[$key] = []; } - foreach ($data[$key] as &$relationModel) { - $relationModel->setParent(clone $result); - $relationModel->exists(true); - } - - $result->setRelation(Db::parseName($relation), $this->resultSetBuild($data[$key])); + $result->setRelation(Container::parseName($relation), $this->resultSetBuild($data[$key], clone $this->parent)); } } /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @param string $name 统计字段别名 - * @return integer + * @param Model $result 数据对象 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 + * @return mixed */ - public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*', string &$name = null) + public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null) { $pk = $result->getPk(); @@ -215,7 +204,7 @@ class MorphMany extends Relation } if ($closure) { - $closure($this->query, $name); + $closure($this, $name); } return $this->query @@ -229,20 +218,16 @@ class MorphMany extends Relation /** * 获取关联统计子查询 * @access public - * @param Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @param string $name 统计字段别名 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return string */ public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $return = $closure($this->query); - - if ($return && is_string($return)) { - $name = $return; - } + $closure($this, $name); } return $this->query @@ -255,10 +240,10 @@ class MorphMany extends Relation /** * 多态一对多 关联模型预查询 * @access protected - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param array $subRelation 子关联 - * @param \Closure $closure 闭包 + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param array $subRelation 子关联 + * @param Closure $closure 闭包 * @return array */ protected function eagerlyMorphToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null): array @@ -267,7 +252,8 @@ class MorphMany extends Relation $this->query->removeOption('where'); if ($closure) { - $closure($this->query); + $this->baseQuery = true; + $closure($this); } $list = $this->query->where($where)->with($subRelation)->select(); @@ -276,7 +262,13 @@ class MorphMany extends Relation // 组装模型数据 $data = []; foreach ($list as $set) { - $data[$set->$morphKey][] = $set; + $key = $set->$morphKey; + + if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { + continue; + } + + $data[$key][] = $set; } return $data; @@ -285,8 +277,8 @@ class MorphMany extends Relation /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 - * @param boolean $replace 是否自动识别更新和写入 + * @param mixed $data 数据 可以使用数组 关联模型对象 + * @param bool $replace 是否自动识别更新和写入 * @return Model|false */ public function save($data, bool $replace = true) diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php index 7132896..7a3de57 100644 --- a/src/model/relation/MorphOne.php +++ b/src/model/relation/MorphOne.php @@ -12,9 +12,9 @@ namespace think\model\relation; use Closure; -use think\db\Query; +use think\Container; +use think\db\BaseQuery as Query; use think\Exception; -use think\facade\Db; use think\Model; use think\model\Relation; @@ -63,14 +63,14 @@ class MorphOne extends Relation /** * 延迟获取关联数据 * @access public - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包查询条件 * @return Model */ public function getRelation(array $subRelation = [], Closure $closure = null) { if ($closure) { - $closure($this->query); + $closure($this); } $this->baseQuery(); @@ -101,8 +101,9 @@ class MorphOne extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 * @return Query */ public function hasWhere($where = [], $fields = null, string $joinType = '') @@ -113,10 +114,10 @@ class MorphOne extends Relation /** * 预载入关联查询 * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void @@ -141,7 +142,7 @@ class MorphOne extends Relation ], $relation, $subRelation, $closure); // 关联属性名 - $attr = Db::parseName($relation); + $attr = Container::parseName($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -161,10 +162,10 @@ class MorphOne extends Relation /** * 预载入关联查询 * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void @@ -186,24 +187,25 @@ class MorphOne extends Relation $relationModel = null; } - $result->setRelation(Db::parseName($relation), $relationModel); + $result->setRelation(Container::parseName($relation), $relationModel); } } /** * 多态一对一 关联模型预查询 * @access protected - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param array $subRelation 子关联 - * @param \Closure $closure 闭包 + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param array $subRelation 子关联 + * @param Closure $closure 闭包 * @return array */ protected function eagerlyMorphToOne(array $where, string $relation, array $subRelation = [], $closure = null): array { // 预载入关联查询 支持嵌套预载入 if ($closure) { - $closure($this->query); + $this->baseQuery = true; + $closure($this); } $list = $this->query->where($where)->with($subRelation)->select(); diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index 7d6b1d4..745f7b4 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -12,8 +12,8 @@ namespace think\model\relation; use Closure; +use think\Container; use think\Exception; -use think\facade\Db; use think\Model; use think\model\Relation; @@ -67,9 +67,10 @@ class MorphTo extends Relation /** * 获取当前的关联模型类的实例 * @access public + * @param bool $clear 是否需要清空查询条件 * @return Model */ - public function getModel(): Model + public function getModel(bool $clear = true): Model { $morphType = $this->morphType; $model = $this->parseModel($this->parent->$morphType); @@ -80,8 +81,8 @@ class MorphTo extends Relation /** * 延迟获取关联数据 * @access public - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包查询条件 * @return Model */ public function getRelation(array $subRelation = [], Closure $closure = null) @@ -111,7 +112,7 @@ class MorphTo extends Relation * @param integer $count 个数 * @param string $id 关联表的统计字段 * @param string $joinType JOIN类型 - * @return \think\db\Query + * @return Query */ public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '') { @@ -121,8 +122,9 @@ class MorphTo extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 * @return Query */ public function hasWhere($where = [], $fields = null, string $joinType = '') @@ -145,7 +147,7 @@ class MorphTo extends Relation if (false === strpos($model, '\\')) { $path = explode('\\', get_class($this->parent)); array_pop($path); - array_push($path, Db::parseName($model, 1)); + array_push($path, Container::parseName($model, 1)); $model = implode('\\', $path); } @@ -178,10 +180,10 @@ class MorphTo extends Relation /** * 预载入关联查询 * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void * @throws Exception */ @@ -200,7 +202,7 @@ class MorphTo extends Relation if (!empty($range)) { // 关联属性名 - $attr = Db::parseName($relation); + $attr = Container::parseName($relation); foreach ($range as $key => $val) { // 多态类型映射 @@ -236,10 +238,10 @@ class MorphTo extends Relation /** * 预载入关联查询 * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 * @return void */ public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void @@ -253,13 +255,13 @@ class MorphTo extends Relation /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 + * @param Model $result 数据对象 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 * @return integer */ - public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*') + public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*') {} /** @@ -282,14 +284,14 @@ class MorphTo extends Relation $data->exists(true); } - $result->setRelation(Db::parseName($relation), $data ?: null); + $result->setRelation(Container::parseName($relation), $data ?: null); } /** * 添加关联数据 * @access public - * @param Model $model 关联模型对象 - * @param string $type 多态类型 + * @param Model $model 关联模型对象 + * @param string $type 多态类型 * @return Model */ public function associate(Model $model, string $type = ''): Model diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index 27248ab..b5ff1a6 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -12,9 +12,9 @@ namespace think\model\relation; use Closure; -use think\db\Query; +use think\Container; +use think\db\BaseQuery as Query; use think\Exception; -use think\facade\Db; use think\Model; use think\model\Relation; @@ -57,17 +57,17 @@ abstract class OneToOne extends Relation /** * 预载入关联查询(JOIN方式) * @access public - * @param Query $query 查询对象 - * @param string $relation 关联名 - * @param mixed $field 关联字段 - * @param string $joinType JOIN方式 - * @param \Closure $closure 闭包条件 - * @param bool $first + * @param Query $query 查询对象 + * @param string $relation 关联名 + * @param mixed $field 关联字段 + * @param string $joinType JOIN方式 + * @param Closure $closure 闭包条件 + * @param bool $first * @return void */ public function eagerly(Query $query, string $relation, $field = true, string $joinType = '', Closure $closure = null, bool $first = false): void { - $name = Db::parseName(Db::classBaseName($this->parent)); + $name = Container::parseName(Container::classBaseName($this->parent)); if ($first) { $table = $query->getTable(); @@ -79,6 +79,7 @@ abstract class OneToOne extends Relation } else { $masterField = true; } + $query->tableField($masterField, $table, $name); } @@ -98,11 +99,9 @@ abstract class OneToOne extends Relation if ($closure) { // 执行闭包查询 $closure($query); - // 使用withField指定获取关联的字段,如 - // $query->where(['id'=>1])->withField('id,name'); - if ($query->getOptions('with_field')) { - $field = $query->getOptions('with_field'); - $query->removeOption('with_field'); + // 使用withField指定获取关联的字段 + if ($this->withField) { + $field = $this->withField; } } @@ -113,10 +112,10 @@ abstract class OneToOne extends Relation /** * 预载入关联查询(数据集) * @access protected - * @param array $resultSet - * @param string $relation - * @param array $subRelation - * @param \Closure $closure + * @param array $resultSet + * @param string $relation + * @param array $subRelation + * @param Closure $closure * @return mixed */ abstract protected function eagerlySet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null); @@ -124,10 +123,10 @@ abstract class OneToOne extends Relation /** * 预载入关联查询(数据) * @access protected - * @param Model $result - * @param string $relation - * @param array $subRelation - * @param \Closure $closure + * @param Model $result + * @param string $relation + * @param array $subRelation + * @param Closure $closure * @return mixed */ abstract protected function eagerlyOne(Model $result, string $relation, array $subRelation = [], Closure $closure = null); @@ -135,11 +134,11 @@ abstract class OneToOne extends Relation /** * 预载入关联查询(数据集) * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param bool $join 是否为JOIN方式 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 + * @param bool $join 是否为JOIN方式 * @return void */ public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, bool $join = false): void @@ -158,11 +157,11 @@ abstract class OneToOne extends Relation /** * 预载入关联查询(数据) * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param bool $join 是否为JOIN方式 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 + * @param bool $join 是否为JOIN方式 * @return void */ public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, bool $join = false): void @@ -258,14 +257,14 @@ abstract class OneToOne extends Relation $relationModel = null; } - $result->setRelation(Db::parseName($relation), $relationModel); + $result->setRelation(Container::parseName($relation), $relationModel); } /** * 绑定关联属性到父模型 * @access protected - * @param Model $model 关联模型对象 - * @param Model $result 父模型对象 + * @param Model $model 关联模型对象 + * @param Model $result 父模型对象 * @return void * @throws Exception */ @@ -274,33 +273,39 @@ abstract class OneToOne extends Relation foreach ($this->bindAttr as $key => $attr) { $key = is_numeric($key) ? $attr : $key; $value = $result->getOrigin($key); + if (!is_null($value)) { throw new Exception('bind attr has exists:' . $key); - } else { - $result->setAttr($key, $model ? $model->$attr : null); } + + $result->setAttr($key, $model ? $model->$attr : null); } } /** * 一对一 关联模型预查询(IN方式) * @access public - * @param array $where 关联预查询条件 - * @param string $key 关联键名 - * @param string $relation 关联名 - * @param array $subRelation 子关联 - * @param \Closure $closure + * @param array $where 关联预查询条件 + * @param string $key 关联键名 + * @param string $relation 关联名 + * @param array $subRelation 子关联 + * @param Closure $closure * @return array */ protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null) { // 预载入关联查询 支持嵌套预载入 if ($closure) { - $closure($this->query); + $this->baseQuery = true; + $closure($this); + } - if ($field = $this->query->getOptions('with_field')) { - $this->query->field($field)->removeOption('with_field'); - } + if ($this->withField) { + $this->query->field($this->withField); + } + + if ($this->query->getOptions('order')) { + $this->query->group($key); } $list = $this->query->where($where)->with($subRelation)->select(); @@ -309,7 +314,9 @@ abstract class OneToOne extends Relation $data = []; foreach ($list as $set) { - $data[$set->$key] = $set; + if (!isset($data[$set->$key])) { + $data[$set->$key] = $set; + } } return $data; -- Gitee From 85b9caece091f7808a6e209ddbeb918f7b35bf0e Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 7 Jul 2019 22:33:25 +0800 Subject: [PATCH 019/365] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/{Db.php => DbManager.php} | 0 src/facade/Db.php | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{Db.php => DbManager.php} (100%) diff --git a/src/Db.php b/src/DbManager.php similarity index 100% rename from src/Db.php rename to src/DbManager.php diff --git a/src/facade/Db.php b/src/facade/Db.php index 8c62107..130c588 100644 --- a/src/facade/Db.php +++ b/src/facade/Db.php @@ -26,6 +26,6 @@ class Db extends Facade */ protected static function getFacadeClass() { - return 'think\Db'; + return 'think\DbManager'; } } -- Gitee From b92c8c2ca77d66f448050bf00caa1374d929d5d9 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 7 Jul 2019 23:38:13 +0800 Subject: [PATCH 020/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 35 +++++++++++++++++++++++++++++++++++ src/db/Connection.php | 38 +++++++++++++++++++++----------------- src/db/PDOConnection.php | 11 +++++------ src/db/connector/Mongo.php | 10 ++++------ 4 files changed, 65 insertions(+), 29 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index 45ca504..fec00cc 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -13,6 +13,7 @@ declare (strict_types = 1); namespace think; use InvalidArgumentException; +use think\CacheManager; use think\db\BaseQuery; use think\db\Connection; use think\db\Raw; @@ -49,6 +50,12 @@ class DbManager */ protected $listen = []; + /** + * SQL日志 + * @var array + */ + protected $log = []; + /** * 查询次数 * @var int @@ -65,6 +72,11 @@ class DbManager $this->config = $config; } + public function setCacheHandler(CacheManager $cache) + { + $this->cache = $cache; + } + /** * 创建/切换数据库连接查询 * @access public @@ -76,6 +88,7 @@ class DbManager { $connection = $this->instance($name, $force); $connection->setDb($this); + $connection->setCache($this->cache); $class = $connection->getQueryClass(); $query = new $class($connection); @@ -155,6 +168,28 @@ class DbManager return $this->queryTimes; } + /** + * 记录SQL日志 + * @access protected + * @param string $log SQL日志信息 + * @param string $type 日志类型 + * @return void + */ + public function log($log, $type = 'sql') + { + $this->log[$type][] = $log; + } + + /** + * 获得查询日志 + * @access public + * @return array + */ + public function getLog(): array + { + return $this->log; + } + /** * 监听SQL执行 * @access public diff --git a/src/db/Connection.php b/src/db/Connection.php index 57844d2..913a030 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -12,15 +12,14 @@ declare (strict_types = 1); namespace think\db; -use think\Cache; +use think\CacheManager; use think\cache\CacheItem; -use think\Db; +use think\DbManager; use think\db\exception\DataNotFoundException; use think\db\exception\ModelNotFoundException; use think\Exception; use think\exception\DbException; use think\exception\PDOException; -use think\Log; /** * 数据库连接基础类 @@ -118,20 +117,12 @@ abstract class Connection */ protected $cache; - /** - * 日志对象 - * @var Log - */ - protected $log; - /** * 架构函数 读取数据库配置信息 * @access public - * @param Cache $cache 缓存对象 - * @param Log $log 日志对象 * @param array $config 数据库配置数组 */ - public function __construct(Cache $cache, Log $log, array $config = []) + public function __construct(array $config = []) { if (!empty($config)) { $this->config = array_merge($this->config, $config); @@ -141,8 +132,6 @@ abstract class Connection $class = $this->getBuilderClass(); $this->builder = new $class($this); - $this->cache = $cache; - $this->log = $log; // 执行初始化操作 $this->initialize(); @@ -195,14 +184,25 @@ abstract class Connection /** * 设置当前的数据库Db对象 * @access public - * @param Db $db + * @param DbManager $db * @return void */ - public function setDb(Db $db) + public function setDb(DbManager $db) { $this->db = $db; } + /** + * 设置当前的缓存对象 + * @access public + * @param CacheManager $cache + * @return void + */ + public function setCache(CacheManager $cache) + { + $this->cache = $cache; + } + /** * 获取数据库的配置参数 * @access public @@ -412,7 +412,7 @@ abstract class Connection protected function log($log, $type = 'sql') { if ($this->config['debug']) { - $this->log->record($log, $type); + $this->db->log($log, $type); } } @@ -472,6 +472,10 @@ abstract class Connection */ public function lazyWrite(string $type, string $guid, float $step, int $lazyTime) { + if (!$this->cache) { + return $step; + } + if (!$this->cache->has($guid . '_time')) { // 计时开始 $this->cache->set($guid . '_time', time(), 0); diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 079f92d..f124205 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -15,7 +15,6 @@ namespace think\db; use PDO; use PDOStatement; use think\cache\CacheItem; -use think\Container; use think\db\exception\BindParamException; use think\Exception; use think\exception\PDOException; @@ -253,7 +252,7 @@ abstract class PDOConnection extends Connection if (!isset($this->info[$schema])) { // 读取缓存 - $cacheFile = Container::pull('app')->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR . $schema . '.php'; + $cacheFile = $this->config['schema_path'] . 'schema' . DIRECTORY_SEPARATOR . $schema . '.php'; if (!$this->config['debug'] && is_file($cacheFile)) { $info = include $cacheFile; @@ -471,7 +470,7 @@ abstract class PDOConnection extends Connection // 分析查询表达式 $query->parseOptions(); - if ($query->getOptions('cache')) { + if ($query->getOptions('cache') && $this->cache) { // 检查查询缓存 $cacheItem = $this->parseCache($query, $query->getOptions('cache')); $resultSet = $this->cache->get($cacheItem->getKey()); @@ -602,7 +601,7 @@ abstract class PDOConnection extends Connection $this->numRows = $this->PDOStatement->rowCount(); - if ($query->getOptions('cache')) { + if ($query->getOptions('cache') && $this->cache) { // 清理缓存数据 $cacheItem = $this->parseCache($query, $query->getOptions('cache')); $key = $cacheItem->getKey(); @@ -874,7 +873,7 @@ abstract class PDOConnection extends Connection $query->setOption('field', (array) $field); - if (!empty($options['cache'])) { + if (!empty($options['cache']) && $this->cache) { $cacheItem = $this->parseCache($query, $options['cache']); $result = $this->cache->get($cacheItem->getKey()); @@ -954,7 +953,7 @@ abstract class PDOConnection extends Connection $query->setOption('field', $field); - if (!empty($options['cache'])) { + if (!empty($options['cache']) && $this->cache) { // 判断查询缓存 $cacheItem = $this->parseCache($query, $options['cache']); $result = $this->cache->get($cacheItem->getKey()); diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index d2e26f0..7387aaa 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -22,9 +22,8 @@ use MongoDB\Driver\Manager; use MongoDB\Driver\Query as MongoQuery; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; -use think\Cache; +use think\CacheManager; use think\Collection; -use think\Db; use think\db\builder\Mongo as Builder; use think\db\Mongo as Query; use think\Exception; @@ -129,11 +128,10 @@ class Mongo /** * 架构函数 读取数据库配置信息 * @access public - * @param Cache $cache 缓存对象 - * @param Log $log 日志对象 - * @param array $config 数据库配置数组 + * @param CacheManager $cache 缓存对象 + * @param array $config 数据库配置数组 */ - public function __construct(Cache $cache, array $config = []) + public function __construct(CacheManager $cache, array $config = []) { if (!class_exists('\MongoDB\Driver\Manager')) { throw new Exception('require mongodb > 1.0'); -- Gitee From 8ff3017f3f439ec2b35cb16941a1201fc87a10f2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 7 Jul 2019 23:41:52 +0800 Subject: [PATCH 021/365] =?UTF-8?q?think-cache=E4=BE=9D=E8=B5=96=E7=BA=B3?= =?UTF-8?q?=E5=85=A5suggest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5fda2ad..0933c07 100644 --- a/composer.json +++ b/composer.json @@ -14,9 +14,11 @@ ], "require": { "php": ">=7.1.0", - "topthink/think-cache": "^2.0", "topthink/think-container":"^2.0" }, + "suggest": { + "topthink/think-cache": "Required to use query cache (^2.0)." + }, "autoload": { "psr-4": { "think\\": "src" -- Gitee From d1e5f37cf1dfd3e2a61c7607dc34cded3e280510 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 8 Jul 2019 11:58:55 +0800 Subject: [PATCH 022/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 17 ++++++++++++++--- src/db/PDOConnection.php | 11 ++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index fec00cc..9222daa 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -62,17 +62,25 @@ class DbManager */ protected $queryTimes = 0; + /** + * 查询缓存对象 + * @var CacheManager + */ + protected $cache; + /** * 初始化 - * @param array $config 连接配置 * @access public + * @param array $config 连接配置 + * @return $this */ public function init(array $config = []) { $this->config = $config; + return $this; } - public function setCacheHandler(CacheManager $cache) + public function setCache(CacheManager $cache) { $this->cache = $cache; } @@ -88,7 +96,10 @@ class DbManager { $connection = $this->instance($name, $force); $connection->setDb($this); - $connection->setCache($this->cache); + + if ($this->cache) { + $connection->setCache($this->cache); + } $class = $connection->getQueryClass(); $query = new $class($connection); diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index f124205..57df69d 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -252,12 +252,17 @@ abstract class PDOConnection extends Connection if (!isset($this->info[$schema])) { // 读取缓存 - $cacheFile = $this->config['schema_path'] . 'schema' . DIRECTORY_SEPARATOR . $schema . '.php'; + if ($this->cache) { + $cacheSchema = $this->cache->get('schema:' . $schema); + } - if (!$this->config['debug'] && is_file($cacheFile)) { - $info = include $cacheFile; + if (!$this->config['debug'] && !empty($cacheSchema)) { + $info = $cacheSchema; } else { $info = $this->getFields($tableName); + if ($this->cache) { + $this->cache->set('schema:' . $schema, $info); + } } $fields = array_keys($info); -- Gitee From 285a7ba9a6ff7c69437c8d5a14599a9c8d4b0d32 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 8 Jul 2019 14:12:41 +0800 Subject: [PATCH 023/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BDb=E7=B1=BB=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0getConfig=E6=96=B9=E6=B3=95=20=E6=94=B9=E8=BF=9B?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E8=8E=B7=E5=8F=96=E9=85=8D=E7=BD=AE=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 23 ++++++++++++++++++++++- src/Model.php | 14 ++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index 9222daa..e8d7cac 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -80,11 +80,32 @@ class DbManager return $this; } + /** + * 设置缓存对象 + * @access public + * @param CacheManager $cache 缓存对象 + * @return void + */ public function setCache(CacheManager $cache) { $this->cache = $cache; } + /** + * 获取配置参数 + * @access public + * @param string $config 配置参数 + * @return mixed + */ + public function getConfig($config = '') + { + if ('' === $config) { + return $this->config; + } + + return $this->config[$config] ?? null; + } + /** * 创建/切换数据库连接查询 * @access public @@ -196,7 +217,7 @@ class DbManager * @access public * @return array */ - public function getLog(): array + public function getLog() { return $this->log; } diff --git a/src/Model.php b/src/Model.php index ecc02f5..a402cbd 100644 --- a/src/Model.php +++ b/src/Model.php @@ -180,10 +180,20 @@ abstract class Model implements JsonSerializable, ArrayAccess $this->name = basename($name); } - $this->db = Container::pull('think\DbManager'); - if (static::$maker) { call_user_func(static::$maker, $this); + } else { + $this->db = Container::pull('think\DbManager'); + + if (is_null($this->isAutoWriteTimestamp)) { + // 自动写入时间戳 + $this->isAutoWriteTimestamp($this->db->getConfig('auto_timestamp')); + } + + if (is_null($dateFormat)) { + // 设置时间戳格式 + $this->setDateFormat($this->db->getConfig('datetime_format')); + } } // 执行初始化操作 -- Gitee From 72fb958fabffe482c4482fa65b49e5a89607908d Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 8 Jul 2019 15:05:30 +0800 Subject: [PATCH 024/365] =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Model.php b/src/Model.php index a402cbd..3263e1d 100644 --- a/src/Model.php +++ b/src/Model.php @@ -185,14 +185,14 @@ abstract class Model implements JsonSerializable, ArrayAccess } else { $this->db = Container::pull('think\DbManager'); - if (is_null($this->isAutoWriteTimestamp)) { + if (is_null($this->autoWriteTimestamp)) { // 自动写入时间戳 - $this->isAutoWriteTimestamp($this->db->getConfig('auto_timestamp')); + $this->autoWriteTimestamp = $this->db->getConfig('auto_timestamp'); } - if (is_null($dateFormat)) { + if (is_null($this->dateFormat)) { // 设置时间戳格式 - $this->setDateFormat($this->db->getConfig('datetime_format')); + $this->dateFormat = $this->db->getConfig('datetime_format'); } } -- Gitee From 02ab33be5c5976f482c04ab21a0859ab5de584a1 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 8 Jul 2019 16:29:20 +0800 Subject: [PATCH 025/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 2 +- src/db/Connection.php | 11 - src/db/Mongo.php | 23 +- src/db/PDOConnection.php | 50 ++++ src/db/builder/Mongo.php | 4 +- src/db/builder/Oracle.php | 1 + src/db/builder/Pgsql.php | 6 +- src/db/builder/Sqlite.php | 1 + src/db/connector/Mongo.php | 464 +++++++++++------------------------- src/db/connector/Pgsql.php | 4 +- src/db/connector/Sqlite.php | 4 +- src/db/connector/Sqlsrv.php | 4 +- 12 files changed, 202 insertions(+), 372 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 27f7422..6e5226d 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -143,7 +143,7 @@ class BaseQuery * @access public * @return Connection */ - public function getConnection(): Connection + public function getConnection() { return $this->connection; } diff --git a/src/db/Connection.php b/src/db/Connection.php index 913a030..63cdc10 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -160,17 +160,6 @@ abstract class Connection */ abstract public function getBuilderClass(): string; - /** - * 设置当前的数据库Builder对象 - * @access protected - * @param Builder $builder - * @return void - */ - protected function setBuilder($builder) - { - $this->builder = $builder; - } - /** * 获取当前的builder实例对象 * @access public diff --git a/src/db/Mongo.php b/src/db/Mongo.php index 2a5df16..1e72cc2 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -39,32 +39,19 @@ class Mongo extends BaseQuery $this->prefix = $this->connection->getConfig('prefix'); } - /** - * 获取当前的数据库Connection对象 - * @access public - * @return Connection - */ - public function getConnection() - { - return $this->connection; - } - /** * 执行查询 返回数据集 * @access public - * @param string $namespace 当前查询的collection * @param MongoQuery $query 查询对象 - * @param ReadPreference $readPreference readPreference - * @param string|array $typeMap 指定返回的typeMap * @return mixed * @throws AuthenticationException * @throws InvalidArgumentException * @throws ConnectionException * @throws RuntimeException */ - public function mongoQuery(string $namespace, MongoQuery $query, ReadPreference $readPreference = null, $typeMap = null) + public function mongoQuery(MongoQuery $query) { - return $this->connection->query($namespace, $query, $readPreference, $typeMap); + return $this->connection->mongoQuery($this, $query); } /** @@ -88,9 +75,7 @@ class Mongo extends BaseQuery /** * 执行语句 * @access public - * @param string $namespace * @param BulkWrite $bulk - * @param WriteConcern $writeConcern * @return int * @throws AuthenticationException * @throws InvalidArgumentException @@ -98,9 +83,9 @@ class Mongo extends BaseQuery * @throws RuntimeException * @throws BulkWriteException */ - public function mongoExecute(string $namespace, BulkWrite $bulk, WriteConcern $writeConcern = null) + public function mongoExecute(BulkWrite $bulk) { - return $this->connection->execute($namespace, $bulk, $writeConcern); + return $this->connection->mongoExecute($this, $bulk); } /** diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 57df69d..0ce4b72 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -26,6 +26,56 @@ abstract class PDOConnection extends Connection { const PARAM_FLOAT = 21; + /** + * 数据库连接参数配置 + * @var array + */ + protected $config = [ + // 数据库类型 + 'type' => '', + // 服务器地址 + 'hostname' => '', + // 数据库名 + 'database' => '', + // 用户名 + 'username' => '', + // 密码 + 'password' => '', + // 端口 + 'hostport' => '', + // 连接dsn + 'dsn' => '', + // 数据库连接参数 + 'params' => [], + // 数据库编码默认采用utf8 + 'charset' => 'utf8', + // 数据库表前缀 + 'prefix' => '', + // 数据库调试模式 + 'debug' => false, + // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) + 'deploy' => 0, + // 数据库读写是否分离 主从式有效 + 'rw_separate' => false, + // 读写分离后 主服务器数量 + 'master_num' => 1, + // 指定从服务器序号 + 'slave_no' => '', + // 模型写入后自动读取主服务器 + 'read_master' => false, + // 是否严格检查字段是否存在 + 'fields_strict' => true, + // 是否需要进行SQL性能分析 + 'sql_explain' => false, + // Builder类 + 'builder' => '', + // Query类 + 'query' => '', + // 是否需要断线重连 + 'break_reconnect' => false, + // 断线标识字符串 + 'break_match_str' => [], + ]; /** * PDO操作实例 * @var PDOStatement diff --git a/src/db/builder/Mongo.php b/src/db/builder/Mongo.php index 9349737..81dd735 100644 --- a/src/db/builder/Mongo.php +++ b/src/db/builder/Mongo.php @@ -670,8 +670,6 @@ class Mongo protected function log($type, $data, $options = []) { - if ($this->connection->getConfig('debug')) { - $this->connection->log($type, $data, $options); - } + $this->connection->mongoLog($type, $data, $options); } } diff --git a/src/db/builder/Oracle.php b/src/db/builder/Oracle.php index 1168bdc..8b6e225 100644 --- a/src/db/builder/Oracle.php +++ b/src/db/builder/Oracle.php @@ -6,6 +6,7 @@ // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\db\builder; diff --git a/src/db/builder/Pgsql.php b/src/db/builder/Pgsql.php index be16c92..e1c2856 100644 --- a/src/db/builder/Pgsql.php +++ b/src/db/builder/Pgsql.php @@ -77,7 +77,7 @@ class Pgsql extends Builder if (strpos($key, '->') && false === strpos($key, '(')) { // JSON字段支持 list($field, $name) = explode('->', $key); - $key = $field . '->>\'' . $name . '\''; + $key = '"' . $field . '"' . '->>\'' . $name . '\''; } elseif (strpos($key, '.')) { list($table, $key) = explode('.', $key, 2); @@ -91,6 +91,10 @@ class Pgsql extends Builder if (isset($alias[$table])) { $table = $alias[$table]; } + + if ('*' != $key && !preg_match('/[,\"\*\(\).\s]/', $key)) { + $key = '"' . $key . '"'; + } } if (isset($table)) { diff --git a/src/db/builder/Sqlite.php b/src/db/builder/Sqlite.php index 60d45a2..bf8f129 100644 --- a/src/db/builder/Sqlite.php +++ b/src/db/builder/Sqlite.php @@ -8,6 +8,7 @@ // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- +declare (strict_types = 1); namespace think\db\builder; diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 7387aaa..637750f 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -7,8 +7,10 @@ // | Author: liu21st // +---------------------------------------------------------------------- declare (strict_types = 1); + namespace think\db\connector; +use Closure; use MongoDB\BSON\ObjectID; use MongoDB\Driver\BulkWrite; use MongoDB\Driver\Command; @@ -21,40 +23,23 @@ use MongoDB\Driver\Exception\RuntimeException; use MongoDB\Driver\Manager; use MongoDB\Driver\Query as MongoQuery; use MongoDB\Driver\ReadPreference; -use MongoDB\Driver\WriteConcern; -use think\CacheManager; -use think\Collection; +use think\db\BaseQuery; use think\db\builder\Mongo as Builder; +use think\db\Connection; use think\db\Mongo as Query; use think\Exception; /** * Mongo数据库驱动 */ -class Mongo +class Mongo extends Connection { - protected $dbName = ''; // dbName - /** @var string 当前SQL指令 */ - protected $queryStr = ''; + // 查询数据类型 protected $typeMap = 'array'; protected $mongo; // MongoDb Object protected $cursor; // MongoCursor Object - /** @var Manager[] 数据库连接ID 支持多个连接 */ - protected $links = []; - /** @var Manger 当前连接ID */ - protected $linkID; - protected $linkRead; - protected $linkWrite; - // Builder对象 - protected $builder; - // 返回或者影响记录数 - protected $numRows = 0; - // 错误信息 - protected $error = ''; - // 查询参数 - protected $options = []; // 数据库连接参数配置 protected $config = [ // 数据库类型 @@ -107,45 +92,6 @@ class Mongo 'query' => '\\think\\db\\Mongo', ]; - /** - * 缓存对象 - * @var Cache - */ - protected $cache; - - /** - * Db对象 - * @var Db - */ - protected $db; - - /** - * 日志记录 - * @var array - */ - protected $log = []; - - /** - * 架构函数 读取数据库配置信息 - * @access public - * @param CacheManager $cache 缓存对象 - * @param array $config 数据库配置数组 - */ - public function __construct(CacheManager $cache, array $config = []) - { - if (!class_exists('\MongoDB\Driver\Manager')) { - throw new Exception('require mongodb > 1.0'); - } - - if (!empty($config)) { - $this->config = array_merge($this->config, $config); - } - - $this->builder = new Builder($this); - - $this->cache = $cache; - } - /** * 获取当前连接器类对应的Query类 * @access public @@ -153,18 +99,7 @@ class Mongo */ public function getQueryClass(): string { - return $this->getConfig('query') ?: Query::class; - } - - /** - * 设置当前的数据库Builder对象 - * @access protected - * @param Builder $builder - * @return void - */ - protected function setBuilder(Builder $builder): void - { - $this->builder = $builder; + return Query::class; } /** @@ -178,14 +113,13 @@ class Mongo } /** - * 设置当前的数据库Db对象 + * 获取当前连接器类对应的Builder类 * @access public - * @param Db $db - * @return void + * @return string */ - public function setDb(Db $db): void + public function getBuilderClass(): string { - $this->db = $db; + return Builder::class; } /** @@ -197,7 +131,7 @@ class Mongo * @throws InvalidArgumentException * @throws RuntimeException */ - public function connect(array $config = [], int $linkNum = 0): Manager + public function connect(array $config = [], $linkNum = 0) { if (!isset($this->links[$linkNum])) { if (empty($config)) { @@ -224,39 +158,13 @@ class Mongo $this->links[$linkNum] = new Manager($config['dsn'], $config['params']); // 记录数据库连接信息 - $this->logger('[ MongoDb ] CONNECT :[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); + $this->log('[ MongoDb ] CONNECT :[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); } return $this->links[$linkNum]; } - /** - * 获取数据库的配置参数 - * @access public - * @param string $config 配置名称 - * @return mixed - */ - public function getConfig(string $config = '') - { - if ('' === $config) { - return $this->config; - } - - return $this->config[$config] ?? null; - } - - /** - * 设置数据库的配置参数 - * @access public - * @param array $config 配置 - * @return void - */ - public function setConfig(array $config): void - { - $this->config = array_merge($this->config, $config); - } - /** * 获取Mongo Manager对象 * @access public @@ -285,10 +193,10 @@ class Mongo /** * 执行查询但只返回Cursor对象 * @access public - * @param Query $query 查询对象 + * @param BaseQuery $query 查询对象 * @return Cursor */ - public function getCursor(Query $query): Cursor + public function cursor(BaseQuery $query) { // 分析查询表达式 $options = $query->parseOptions(); @@ -297,14 +205,14 @@ class Mongo $mongoQuery = $this->builder->select($query); // 执行查询操作 - return $this->cursor($options['table'], $mongoQuery, $options['readPreference'] ?? null); + return $this->getCursor($query, $mongoQuery); } /** * 执行查询并返回Cursor对象 * @access public - * @param string $namespace 当前查询的collection - * @param MongoQuery $query 查询对象 + * @param BaseQuery $query 查询对象 + * @param MongoQuery $mongoQuery Mongo查询对象 * @param ReadPreference $readPreference readPreference * @return Cursor * @throws AuthenticationException @@ -312,11 +220,14 @@ class Mongo * @throws ConnectionException * @throws RuntimeException */ - public function cursor(string $namespace, MongoQuery $query, ReadPreference $readPreference = null): Cursor + public function getCursor(BaseQuery $query, $mongoQuery): Cursor { $this->initConnect(false); $this->db->updateQueryTimes(); + $options = $query->getOptions(); + $namespace = $options['table']; + if (false === strpos($namespace, '.')) { $namespace = $this->dbName . '.' . $namespace; } @@ -326,9 +237,14 @@ class Mongo $this->queryStr = 'db' . strstr($namespace, '.') . '.' . $this->queryStr; } + if ($mongoQuery instanceof Closure) { + $mongoQuery = $mongoQuery($query); + } + + $readPreference = $options['readPreference'] ?? null; $this->debug(true); - $this->cursor = $this->mongo->executeQuery($namespace, $query, $readPreference); + $this->cursor = $this->mongo->executeQuery($namespace, $mongoQuery, $readPreference); $this->debug(false); @@ -338,8 +254,8 @@ class Mongo /** * 执行查询 * @access public - * @param string $namespace 当前查询的collection - * @param MongoQuery $query 查询对象 + * @param BaseQuery $query 查询对象 + * @param MongoQuery $mongoQuery Mongo查询对象 * @param ReadPreference $readPreference readPreference * @param string|array $typeMap 指定返回的typeMap * @return array @@ -348,19 +264,42 @@ class Mongo * @throws ConnectionException * @throws RuntimeException */ - public function query(string $namespace, MongoQuery $query, ReadPreference $readPreference = null, $typeMap = null): array + public function mongoQuery(BaseQuery $query, $mongoQuery): array { - $this->cursor($namespace, $query, $readPreference); + $options = $query->parseOptions(); - return $this->getResult($typeMap); + if ($query->getOptions('cache') && $this->cache) { + // 检查查询缓存 + $cacheItem = $this->parseCache($query, $query->getOptions('cache')); + $resultSet = $this->cache->get($cacheItem->getKey()); + + if (false !== $resultSet) { + return $resultSet; + } + } + + if ($mongoQuery instanceof Closure) { + $mongoQuery = $mongoQuery($query); + } + + $this->getCursor($query, $mongoQuery); + + $resultSet = $this->getResult($options['typeMap']); + + if (isset($cacheItem) && $resultSet) { + // 缓存数据集 + $cacheItem->set($resultSet); + $this->cacheData($cacheItem); + } + + return $resultSet; } /** * 执行写操作 * @access public - * @param string $namespace - * @param BulkWrite $bulk - * @param WriteConcern $writeConcern + * @param BaseQuery $query + * @param BulkWrite $bulk * * @return WriteResult * @throws AuthenticationException @@ -369,11 +308,14 @@ class Mongo * @throws RuntimeException * @throws BulkWriteException */ - public function execute(string $namespace, BulkWrite $bulk, WriteConcern $writeConcern = null) + public function mongoExecute(BaseQuery $query, BulkWrite $bulk) { $this->initConnect(true); $this->db->updateQueryTimes(); + $options = $query->getOptions(); + + $namespace = $options['table']; if (false === strpos($namespace, '.')) { $namespace = $this->dbName . '.' . $namespace; } @@ -383,6 +325,7 @@ class Mongo $this->queryStr = 'db' . strstr($namespace, '.') . '.' . $this->queryStr; } + $writeConcern = $options['writeConcern'] ?? null; $this->debug(true); $writeResult = $this->mongo->executeBulkWrite($namespace, $bulk, $writeConcern); @@ -391,6 +334,19 @@ class Mongo $this->numRows = $writeResult->getMatchedCount(); + if ($query->getOptions('cache') && $this->cache) { + // 清理缓存数据 + $cacheItem = $this->parseCache($query, $query->getOptions('cache')); + $key = $cacheItem->getKey(); + $tag = $cacheItem->getTag(); + + if (isset($key) && $this->cache->get($key)) { + $this->cache->delete($key); + } elseif (!empty($tag)) { + $this->cache->tag($tag)->clear(); + } + } + return $writeResult; } @@ -481,7 +437,7 @@ class Mongo * @param array $options 参数 * @return void */ - public function log(string $type, $data, array $options = []) + public function mongoLog(string $type, $data, array $options = []) { if (!$this->config['debug']) { return; @@ -564,20 +520,10 @@ class Mongo } else { $master = ''; } - $this->logger('[ SQL ] ' . $sql . ' [' . $master . ' RunTime:' . $runtime . 's ]'); + $this->log('[ SQL ] ' . $sql . ' [' . $master . ' RunTime:' . $runtime . 's ]'); } } - public function logger(string $log): void - { - $this->config['debug'] && $this->log[] = $log; - } - - public function getSqlLog(): array - { - return $this->log; - } - /** * 数据库调试 记录当前SQL及分析性能 * @access protected @@ -720,7 +666,7 @@ class Mongo $manager = new Manager($this->buildUrl(), $this->config['params']); // 记录数据库连接信息 - $this->logger('[ MongoDB ] ReplicaSet CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $this->config['dsn']); + $this->log('[ MongoDB ] ReplicaSet CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $this->config['dsn']); return $manager; } @@ -746,7 +692,7 @@ class Mongo /** * 插入记录 * @access public - * @param Query $query 查询对象 + * @param BaseQuery $query 查询对象 * @param boolean $getLastInsID 返回自增主键 * @return mixed * @throws AuthenticationException @@ -755,7 +701,7 @@ class Mongo * @throws RuntimeException * @throws BulkWriteException */ - public function insert(Query $query, bool $getLastInsID = false) + public function insert(BaseQuery $query, bool $getLastInsID = false) { // 分析查询表达式 $options = $query->parseOptions(); @@ -765,14 +711,14 @@ class Mongo } // 生成bulk对象 - $bulk = $this->builder->insert($query); - $writeConcern = $options['writeConcern'] ?? null; - $writeResult = $this->execute($options['table'], $bulk, $writeConcern); - $result = $writeResult->getInsertedCount(); + $bulk = $this->builder->insert($query); + + $writeResult = $this->mongoExecute($query, $bulk); + $result = $writeResult->getInsertedCount(); if ($result) { $data = $options['data']; - $lastInsId = $this->getLastInsID(); + $lastInsId = $this->getLastInsID($query); if ($lastInsId) { $pk = $query->getPk(); @@ -796,7 +742,7 @@ class Mongo * @access public * @return mixed */ - public function getLastInsID(string $sequence = null) + public function getLastInsID(BaseQuery $query, string $sequence = null) { $id = $this->builder->getLastInsID(); @@ -816,7 +762,7 @@ class Mongo /** * 批量插入记录 * @access public - * @param Query $query 查询对象 + * @param BaseQuery $query 查询对象 * @param array $dataSet 数据集 * @return integer * @throws AuthenticationException @@ -825,19 +771,19 @@ class Mongo * @throws RuntimeException * @throws BulkWriteException */ - public function insertAll(Query $query, array $dataSet = []): int + public function insertAll(BaseQuery $query, array $dataSet = []): int { // 分析查询表达式 - $options = $query->parseOptions(); + $query->parseOptions(); if (!is_array(reset($dataSet))) { return 0; } // 生成bulkWrite对象 - $bulk = $this->builder->insertAll($query, $dataSet); - $writeConcern = $options['writeConcern'] ?? null; - $writeResult = $this->execute($options['table'], $bulk, $writeConcern); + $bulk = $this->builder->insertAll($query, $dataSet); + + $writeResult = $this->mongoExecute($query, $bulk); return $writeResult->getInsertedCount(); } @@ -845,7 +791,7 @@ class Mongo /** * 更新记录 * @access public - * @param Query $query 查询对象 + * @param BaseQuery $query 查询对象 * @return int * @throws Exception * @throws AuthenticationException @@ -854,27 +800,14 @@ class Mongo * @throws RuntimeException * @throws BulkWriteException */ - public function update(Query $query): int + public function update(BaseQuery $query): int { - $options = $query->parseOptions(); - - if (isset($options['cache'])) { - $cacheItem = $this->parseCache($query, $options['cache']); - $key = $cacheItem->getKey(); - } + $query->parseOptions(); // 生成bulkWrite对象 - $bulk = $this->builder->update($query); - $writeConcern = $options['writeConcern'] ?? null; - $writeResult = $this->execute($options['table'], $bulk, $writeConcern); - - // 检测缓存 - if (isset($key) && $this->cache->get($key)) { - // 删除缓存 - $this->cache->delete($key); - } elseif (isset($cacheItem) && $cacheItem->getTag()) { - $this->cache->tag($cacheItem->getTag())->clear(); - } + $bulk = $this->builder->update($query); + + $writeResult = $this->mongoExecute($query, $bulk); $result = $writeResult->getModifiedCount(); @@ -888,7 +821,7 @@ class Mongo /** * 删除记录 * @access public - * @param Query $query 查询对象 + * @param BaseQuery $query 查询对象 * @return int * @throws Exception * @throws AuthenticationException @@ -897,31 +830,16 @@ class Mongo * @throws RuntimeException * @throws BulkWriteException */ - public function delete(Query $query): int + public function delete(BaseQuery $query): int { // 分析查询表达式 - $options = $query->parseOptions(); - - if (isset($options['cache'])) { - $cacheItem = $this->parseCache($query, $options['cache']); - $key = $cacheItem->getKey(); - } + $query->parseOptions(); // 生成bulkWrite对象 $bulk = $this->builder->delete($query); - $writeConcern = $options['writeConcern'] ?? null; - // 执行操作 - $writeResult = $this->execute($options['table'], $bulk, $writeConcern); - - // 检测缓存 - if (isset($key) && $this->cache->get($key)) { - // 删除缓存 - $this->cache->delete($key); - } elseif (isset($cacheItem) && $cacheItem->getTag()) { - $this->cache->tag($cacheItem->getTag())->clear(); - } + $writeResult = $this->mongoExecute($query, $bulk); $result = $writeResult->getDeletedCount(); @@ -935,8 +853,8 @@ class Mongo /** * 查找记录 * @access public - * @param Query $query 查询对象 - * @return Collection|array + * @param BaseQuery $query 查询对象 + * @return array * @throws ModelNotFoundException * @throws DataNotFoundException * @throws AuthenticationException @@ -944,35 +862,14 @@ class Mongo * @throws ConnectionException * @throws RuntimeException */ - public function select(Query $query) + public function select(BaseQuery $query): array { - $options = $query->parseOptions(); - - if (!empty($options['cache'])) { - $cacheItem = $this->parseCache($query, $options['cache']); - $resultSet = $this->getCacheData($cacheItem); - - if (false !== $resultSet) { - return $resultSet; - } - } - - // 生成MongoQuery对象 - $mongoQuery = $this->builder->select($query); - $resultSet = $this->db->trigger('before_select', $query); if (!$resultSet) { - // 执行查询操作 - $readPreference = $options['readPreference'] ?? null; - - $resultSet = $this->query($options['table'], $mongoQuery, $readPreference, $options['typeMap']); - } - - if (isset($cacheItem) && false !== $resultSet) { - // 缓存数据集 - $cacheItem->set($resultSet); - $this->cacheData($cacheItem); + $resultSet = $this->mongoQuery($query, function ($query) { + return $this->builder->select($query); + }); } return $resultSet; @@ -981,7 +878,7 @@ class Mongo /** * 查找单条记录 * @access public - * @param Query $query 查询对象 + * @param BaseQuery $query 查询对象 * @return array * @throws ModelNotFoundException * @throws DataNotFoundException @@ -990,100 +887,23 @@ class Mongo * @throws ConnectionException * @throws RuntimeException */ - public function find(Query $query) + public function find(BaseQuery $query): array { - // 分析查询表达式 - $options = $query->parseOptions(); - - if (!empty($options['cache'])) { - // 判断查询缓存 - $cacheItem = $this->parseCache($query, $options['cache']); - $key = $cacheItem->getKey(); - } - - if (isset($key)) { - $result = $this->cache->get($key); - - if (false !== $result) { - return $result; - } - } - - // 生成查询对象 - $mongoQuery = $this->builder->select($query, true); - // 事件回调 $result = $this->db->trigger('before_find', $query); if (!$result) { // 执行查询 - $readPreference = $options['readPreference'] ?? null; - $resultSet = $this->query($options['table'], $mongoQuery, $readPreference, $options['typeMap']); - - $result = $resultSet[0] ?? null; - } + $resultSet = $this->mongoQuery($query, function ($query) { + return $this->builder->select($query, true); + }); - if (isset($cache) && $result) { - // 缓存数据 - $cacheItem->set($result); - $this->cacheData($cacheItem); + $result = $resultSet[0] ?? []; } return $result; } - /** - * 获取缓存数据 - * @access protected - * @param Query $query 查询对象 - * @param mixed $cache 缓存设置 - * @param array $data 缓存数据 - * @param string $key 缓存Key - * @return mixed - */ - protected function getCacheData(CacheItem $cacheItem) - { - // 判断查询缓存 - return $this->cache->get($cacheItem->getKey()); - } - - /** - * 缓存数据 - * @access protected - * @param CacheItem $cacheItem 缓存Item - */ - protected function cacheData(CacheItem $cacheItem): void - { - if ($cacheItem->getTag()) { - $this->cache->tag($cacheItem->getTag()); - } - - $this->cache->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire()); - } - - protected function parseCache(Query $query, array $cache): CacheItem - { - list($key, $expire, $tag) = $cache; - - if ($key instanceof CacheItem) { - $cacheItem = $key; - } else { - if (true === $key) { - if (!empty($query->getOptions('key'))) { - $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); - } else { - $key = md5($this->getConfig('database') . serialize($query->getOptions())); - } - } - - $cacheItem = new CacheItem($key); - $cacheItem->expire($expire); - $cacheItem->tag($tag); - } - - return $cacheItem; - } - /** * 得到某个字段的值 * @access public @@ -1091,7 +911,7 @@ class Mongo * @param mixed $default 默认值 * @return mixed */ - public function value(Query $query, string $field, $default = null) + public function value(BaseQuery $query, string $field, $default = null) { $options = $query->parseOptions(); @@ -1146,7 +966,7 @@ class Mongo * @param string $key 索引 * @return array */ - public function column(Query $query, $field, string $key = '') + public function column(BaseQuery $query, string $field, string $key = ''): array { $options = $query->parseOptions(); @@ -1201,50 +1021,20 @@ class Mongo return $result; } - /** - * 延时更新检查 返回false表示需要延时 - * 否则返回实际写入的数值 - * @access public - * @param string $type 自增或者自减 - * @param string $guid 写入标识 - * @param float $step 写入步进值 - * @param integer $lazyTime 延时时间(s) - * @return false|integer - */ - public function lazyWrite(string $type, string $guid, float $step, int $lazyTime) - { - if (!$this->cache->has($guid . '_time')) { - // 计时开始 - $this->cache->set($guid . '_time', time(), 0); - $this->cache->$type($guid, $step); - } elseif (time() > $this->cache->get($guid . '_time') + $lazyTime) { - // 删除缓存 - $value = $this->cache->$type($guid, $step); - $this->cache->delete($guid); - $this->cache->delete($guid . '_time'); - return 0 === $value ? false : $value; - } else { - // 更新缓存 - $this->cache->$type($guid, $step); - } - - return false; - } - /** * 执行command * @access public - * @param Query $query 查询对象 + * @param BaseQuery $query 查询对象 * @param string|array|object $command 指令 * @param mixed $extra 额外参数 * @param string $db 数据库名 * @return array */ - public function cmd(Query $query, $command, $extra = null, string $db = ''): array + public function cmd(BaseQuery $query, $command, $extra = null, string $db = ''): array { if (is_array($command) || is_object($command)) { if ($this->getConfig('debug')) { - $this->log('cmd', 'cmd', $command); + $this->mongoLog('cmd', 'cmd', $command); } // 直接创建Command对象 @@ -1291,6 +1081,18 @@ class Mongo return 1; } + /** + * 执行数据库事务 + * @access public + * @param callable $callback 数据操作方法回调 + * @return mixed + * @throws PDOException + * @throws \Exception + * @throws \Throwable + */ + public function transaction(callable $callback) + {} + /** * 启动事务 * @access public diff --git a/src/db/connector/Pgsql.php b/src/db/connector/Pgsql.php index 69e90a7..0886d87 100644 --- a/src/db/connector/Pgsql.php +++ b/src/db/connector/Pgsql.php @@ -12,12 +12,12 @@ namespace think\db\connector; use PDO; -use think\db\Connection; +use think\db\PDOConnection; /** * Pgsql数据库驱动 */ -class Pgsql extends Connection +class Pgsql extends PDOConnection { /** diff --git a/src/db/connector/Sqlite.php b/src/db/connector/Sqlite.php index 2ee5653..513438a 100644 --- a/src/db/connector/Sqlite.php +++ b/src/db/connector/Sqlite.php @@ -12,12 +12,12 @@ namespace think\db\connector; use PDO; -use think\db\Connection; +use think\db\PDOConnection; /** * Sqlite数据库驱动 */ -class Sqlite extends Connection +class Sqlite extends PDOConnection { /** diff --git a/src/db/connector/Sqlsrv.php b/src/db/connector/Sqlsrv.php index bd9ea13..cae6dec 100644 --- a/src/db/connector/Sqlsrv.php +++ b/src/db/connector/Sqlsrv.php @@ -12,12 +12,12 @@ namespace think\db\connector; use PDO; -use think\db\Connection; +use think\db\PDOConnection; /** * Sqlsrv数据库驱动 */ -class Sqlsrv extends Connection +class Sqlsrv extends PDOConnection { /** * 默认PDO连接参数 -- Gitee From dcd65156b3db043ea4673e9dd132a27792b8146b Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 8 Jul 2019 16:33:29 +0800 Subject: [PATCH 026/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Mongo.php | 12 ------------ src/db/connector/Mongo.php | 1 + 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/db/Mongo.php b/src/db/Mongo.php index 1e72cc2..0eaf334 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -27,18 +27,6 @@ use think\Exception; class Mongo extends BaseQuery { - /** - * 架构函数 - * @access public - * @param Connection $connection 数据库连接对象 - */ - public function __construct(Connection $connection) - { - $this->connection = $connection; - - $this->prefix = $this->connection->getConfig('prefix'); - } - /** * 执行查询 返回数据集 * @access public diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 637750f..2173054 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -36,6 +36,7 @@ class Mongo extends Connection { // 查询数据类型 + protected $dbName = ''; protected $typeMap = 'array'; protected $mongo; // MongoDb Object protected $cursor; // MongoCursor Object -- Gitee From 2f632142e4e01207bdc68762f204ea88f9625eff Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 8 Jul 2019 17:00:34 +0800 Subject: [PATCH 027/365] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/exception/ClassNotFoundException.php | 32 ------------------------ 1 file changed, 32 deletions(-) delete mode 100644 src/exception/ClassNotFoundException.php diff --git a/src/exception/ClassNotFoundException.php b/src/exception/ClassNotFoundException.php deleted file mode 100644 index 34d9320..0000000 --- a/src/exception/ClassNotFoundException.php +++ /dev/null @@ -1,32 +0,0 @@ - -// +---------------------------------------------------------------------- - -namespace think\exception; - -class ClassNotFoundException extends \RuntimeException -{ - protected $class; - public function __construct(string $message, string $class = '') - { - $this->message = $message; - $this->class = $class; - } - - /** - * 获取类名 - * @access public - * @return string - */ - public function getClass() - { - return $this->class; - } -} -- Gitee From 07fd29b682c38a1ea2ca956cf2e5087ee52327c9 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 8 Jul 2019 17:19:03 +0800 Subject: [PATCH 028/365] =?UTF-8?q?readme=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 04dca62..2193776 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # think-orm -基于PHP7.1+ 的ORM实现,主要特性: +ThinkPHP6内置ORM,基于PHP7.1+ 的ORM实现,主要特性: -- 基于ThinkPHP的ORM独立封装 - 支持Mysql、Pgsql、Sqlite、SqlServer、Oracle和Mongodb - 支持Db类和查询构造器 - 支持事务 @@ -11,18 +10,59 @@ - 支持使用Db门面对象 - 支持查询缓存 -适用于不使用ThinkPHP框架的开发者。 -安装 +## 安装 ~~~ composer require topthink/think-orm ~~~ -Db类用法: +## 使用 + +Db类: ~~~php use think\facade\Db; // 数据库配置信息设置(全局有效) -Db::setConfig(['数据库配置参数(数组)']); +Db::init([ + // 默认数据连接标识 + 'default' => 'mysql', + // 数据库连接信息 + 'connections' => [ + 'mysql' => [ + // 数据库类型 + 'type' => 'mysql', + // 主机地址 + 'hostname' => '127.0.0.1', + // 用户名 + 'username' => 'root', + // 数据库名 + 'database' => 'demo', + // 数据库编码默认采用utf8 + 'charset' => 'utf8', + // 数据库表前缀 + 'prefix' => 'think_', + // 数据库调试模式 + 'debug' => true, + ], + 'mongo' => [ + // 数据库类型 + 'type' => 'mongo', + // 服务器地址 + 'hostname' => '127.0.0.1', + // 数据库名 + 'database' => 'demo', + // 用户名 + 'username' => '', + // 密码 + 'password' => '', + // 主键转换为Id + 'pk_convert_id' => true, + // 数据库调试模式 + 'debug' => true, + // 端口 + 'hostport' => '27017', + ], + ], +]); // 进行CURD操作 Db::table('user') ->data(['name'=>'thinkphp','email'=>'thinkphp@qq.com']) @@ -45,7 +85,7 @@ Db::getSqlLog(); 其它操作参考TP6.0的完全开发手册[数据库](https://www.kancloud.cn/manual/thinkphp6_0/1037530)章节 -定义模型: +模型: ~~~php namespace app\index\model; -- Gitee From 0a0513a58a65eeeb53c16d335729593bde28cb88 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 9 Jul 2019 17:17:03 +0800 Subject: [PATCH 029/365] =?UTF-8?q?=E5=BC=82=E5=B8=B8=E7=B1=BB=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 6 +-- src/db/BaseQuery.php | 22 +++++----- src/db/Builder.php | 2 +- src/db/Connection.php | 5 +-- src/db/Fetch.php | 2 +- src/db/Mongo.php | 2 +- src/db/PDOConnection.php | 9 ++-- src/db/Query.php | 2 +- src/db/builder/Mongo.php | 2 +- src/db/builder/Mysql.php | 2 +- src/db/builder/Sqlsrv.php | 2 +- src/db/connector/Mongo.php | 2 +- src/db/exception/BindParamException.php | 2 - src/db/exception/DataNotFoundException.php | 2 - .../exception/DbException.php} | 35 +++++++++++++-- src/db/exception/ModelNotFoundException.php | 2 - src/{ => db}/exception/PDOException.php | 3 +- src/exception/BindParamException.php | 35 --------------- src/exception/DataNotFoundException.php | 43 ------------------ src/exception/DbException.php | 44 ------------------- src/exception/ModelNotFoundException.php | 44 ------------------- src/facade/Db.php | 4 +- src/model/Relation.php | 3 +- src/model/concern/Conversion.php | 2 +- src/model/concern/ModelEvent.php | 2 +- src/model/concern/OptimLock.php | 2 +- src/model/concern/RelationShip.php | 2 +- src/model/relation/BelongsToMany.php | 2 +- src/model/relation/MorphMany.php | 2 +- src/model/relation/MorphOne.php | 2 +- src/model/relation/MorphTo.php | 2 +- src/model/relation/OneToOne.php | 2 +- 32 files changed, 72 insertions(+), 221 deletions(-) rename src/{Exception.php => db/exception/DbException.php} (62%) rename src/{ => db}/exception/PDOException.php (96%) delete mode 100644 src/exception/BindParamException.php delete mode 100644 src/exception/DataNotFoundException.php delete mode 100644 src/exception/DbException.php delete mode 100644 src/exception/ModelNotFoundException.php diff --git a/src/DbManager.php b/src/DbManager.php index e8d7cac..be98202 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -19,10 +19,10 @@ use think\db\Connection; use think\db\Raw; /** - * Class Db + * Class DbManager * @package think - * @mixin BaseQuery - * @mixin Query + * @mixin think\db\BaseQuery + * @mixin think\db\Query */ class DbManager { diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 6e5226d..0f9aa34 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -16,15 +16,14 @@ use think\Collection; use think\Container; use think\db\exception\BindParamException; use think\db\exception\DataNotFoundException; +use think\db\exception\DbException as Exception; use think\db\exception\ModelNotFoundException; -use think\Exception; -use think\exception\DbException; -use think\exception\PDOException; +use think\db\exception\PDOException; use think\Model; use think\Paginator; /** - * 数据查询类 + * 数据查询基础类 */ class BaseQuery { @@ -105,7 +104,6 @@ class BaseQuery * @param string $method 方法名称 * @param array $args 调用参数 * @return mixed - * @throws DbException * @throws Exception */ public function __call(string $method, array $args) @@ -583,7 +581,7 @@ class BaseQuery * @param int|array $listRows 每页数量 数组表示配置参数 * @param int|bool $simple 是否简洁模式或者总记录数 * @return Paginator - * @throws DbException + * @throws Exception */ public function paginate($listRows = null, $simple = false): Paginator { @@ -641,7 +639,7 @@ class BaseQuery * @param string $key 分页索引键 * @param string $sort 索引键排序 asc|desc * @return Paginator - * @throws DbException + * @throws Exception */ public function paginateX($listRows = null, string $key = null, string $sort = null): Paginator { @@ -712,7 +710,7 @@ class BaseQuery * @param string $key 分页索引键 默认为主键 * @param string $sort 索引键排序 asc|desc * @return array - * @throws DbException + * @throws Exception */ public function more(int $limit, $lastId = null, string $key = null, string $sort = null): array { @@ -1403,7 +1401,7 @@ class BaseQuery * @access public * @param mixed $data 数据 * @return Collection - * @throws DbException + * @throws Exception * @throws ModelNotFoundException * @throws DataNotFoundException */ @@ -1437,7 +1435,7 @@ class BaseQuery * @access public * @param mixed $data 查询数据 * @return array|Model|null - * @throws DbException + * @throws Exception * @throws ModelNotFoundException * @throws DataNotFoundException */ @@ -1477,7 +1475,7 @@ class BaseQuery * @param string|array $column 分批处理的字段名 * @param string $order 字段排序 * @return bool - * @throws DbException + * @throws Exception */ public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool { @@ -1533,7 +1531,7 @@ class BaseQuery * @access public * @param bool $sub 是否添加括号 * @return string - * @throws DbException + * @throws Exception */ public function buildSql(bool $sub = true): string { diff --git a/src/db/Builder.php b/src/db/Builder.php index b3aa1d1..def81f5 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -14,7 +14,7 @@ namespace think\db; use Closure; use PDO; -use think\Exception; +use think\db\exception\DbException as Exception; /** * Db Builder diff --git a/src/db/Connection.php b/src/db/Connection.php index 63cdc10..e3764d8 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -16,10 +16,9 @@ use think\CacheManager; use think\cache\CacheItem; use think\DbManager; use think\db\exception\DataNotFoundException; +use think\db\exception\DbException as Exception; use think\db\exception\ModelNotFoundException; -use think\Exception; -use think\exception\DbException; -use think\exception\PDOException; +use think\db\exception\PDOException; /** * 数据库连接基础类 diff --git a/src/db/Fetch.php b/src/db/Fetch.php index 93a951d..3be92e2 100644 --- a/src/db/Fetch.php +++ b/src/db/Fetch.php @@ -13,7 +13,7 @@ declare (strict_types = 1); namespace think\db; use think\Container; -use think\Exception; +use think\db\exception\DbException as Exception; /** * SQL获取类 diff --git a/src/db/Mongo.php b/src/db/Mongo.php index 0eaf334..c04b0d4 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -22,8 +22,8 @@ use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; use think\Collection; use think\db\connector\Mongo as Connection; +use think\db\exception\DbException as Exception; use think\db\Query as BaseQuery; -use think\Exception; class Mongo extends BaseQuery { diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 0ce4b72..518cf45 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -16,8 +16,9 @@ use PDO; use PDOStatement; use think\cache\CacheItem; use think\db\exception\BindParamException; -use think\Exception; -use think\exception\PDOException; +use think\db\exception\DataNotFoundException; +use think\db\exception\ModelNotFoundException; +use think\db\exception\PDOException; /** * 数据库连接基础类 @@ -399,7 +400,7 @@ abstract class PDOConnection extends Connection * @param integer $linkNum 连接序号 * @param array|bool $autoConnection 是否自动连接主数据库(用于分布式) * @return PDO - * @throws Exception + * @throws PDOException */ public function connect(array $config = [], $linkNum = 0, $autoConnection = false): PDO { @@ -863,7 +864,6 @@ abstract class PDOConnection extends Connection * @access public * @param BaseQuery $query 查询对象 * @return integer - * @throws Exception * @throws PDOException */ public function update(BaseQuery $query): int @@ -888,7 +888,6 @@ abstract class PDOConnection extends Connection * @access public * @param BaseQuery $query 查询对象 * @return int - * @throws Exception * @throws PDOException */ public function delete(BaseQuery $query): int diff --git a/src/db/Query.php b/src/db/Query.php index d7bd3fa..bb749c2 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -13,7 +13,7 @@ declare (strict_types = 1); namespace think\db; /** - * 数据查询类 + * PDO数据查询类 */ class Query extends BaseQuery { diff --git a/src/db/builder/Mongo.php b/src/db/builder/Mongo.php index 81dd735..4afe169 100644 --- a/src/db/builder/Mongo.php +++ b/src/db/builder/Mongo.php @@ -17,8 +17,8 @@ use MongoDB\Driver\Command; use MongoDB\Driver\Exception\InvalidArgumentException; use MongoDB\Driver\Query as MongoQuery; use think\db\connector\Mongo as Connection; +use think\db\exception\DbException as Exception; use think\db\Mongo as Query; -use think\Exception; class Mongo { diff --git a/src/db/builder/Mysql.php b/src/db/builder/Mysql.php index c21c0c2..33aec85 100644 --- a/src/db/builder/Mysql.php +++ b/src/db/builder/Mysql.php @@ -13,9 +13,9 @@ declare (strict_types = 1); namespace think\db\builder; use think\db\Builder; +use think\db\exception\DbException as Exception; use think\db\Query; use think\db\Raw; -use think\Exception; /** * mysql数据库驱动 diff --git a/src/db/builder/Sqlsrv.php b/src/db/builder/Sqlsrv.php index 4cd8c9c..cd06c34 100644 --- a/src/db/builder/Sqlsrv.php +++ b/src/db/builder/Sqlsrv.php @@ -12,9 +12,9 @@ namespace think\db\builder; use think\db\Builder; +use think\db\exception\DbException as Exception; use think\db\Query; use think\db\Raw; -use think\Exception; /** * Sqlsrv数据库驱动 diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 2173054..4826e33 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -26,8 +26,8 @@ use MongoDB\Driver\ReadPreference; use think\db\BaseQuery; use think\db\builder\Mongo as Builder; use think\db\Connection; +use think\db\exception\DbException as Exception; use think\db\Mongo as Query; -use think\Exception; /** * Mongo数据库驱动 diff --git a/src/db/exception/BindParamException.php b/src/db/exception/BindParamException.php index b76b35e..08bb388 100644 --- a/src/db/exception/BindParamException.php +++ b/src/db/exception/BindParamException.php @@ -12,8 +12,6 @@ declare (strict_types = 1); namespace think\db\exception; -use think\exception\DbException; - /** * PDO参数绑定异常 */ diff --git a/src/db/exception/DataNotFoundException.php b/src/db/exception/DataNotFoundException.php index 8bbb10f..d10dd43 100644 --- a/src/db/exception/DataNotFoundException.php +++ b/src/db/exception/DataNotFoundException.php @@ -12,8 +12,6 @@ declare (strict_types = 1); namespace think\db\exception; -use think\exception\DbException; - class DataNotFoundException extends DbException { protected $table; diff --git a/src/Exception.php b/src/db/exception/DbException.php similarity index 62% rename from src/Exception.php rename to src/db/exception/DbException.php index 034c85b..d5bfc3f 100644 --- a/src/Exception.php +++ b/src/db/exception/DbException.php @@ -2,17 +2,45 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: 麦当苗儿 // +---------------------------------------------------------------------- +declare (strict_types = 1); -namespace think; +namespace think\db\exception; -class Exception extends \Exception +use Exception; + +/** + * Database相关异常处理类 + */ +class DbException extends Exception { + /** + * DbException constructor. + * @access public + * @param string $message + * @param array $config + * @param string $sql + * @param int $code + */ + public function __construct(string $message, array $config = [], string $sql = '', int $code = 10500) + { + $this->message = $message; + $this->code = $code; + + $this->setData('Database Status', [ + 'Error Code' => $code, + 'Error Message' => $message, + 'Error SQL' => $sql, + ]); + + unset($config['username'], $config['password']); + $this->setData('Database Config', $config); + } /** * 保存异常页面显示的额外Debug数据 @@ -50,5 +78,4 @@ class Exception extends \Exception { return $this->data; } - } diff --git a/src/db/exception/ModelNotFoundException.php b/src/db/exception/ModelNotFoundException.php index ec7e730..84a1525 100644 --- a/src/db/exception/ModelNotFoundException.php +++ b/src/db/exception/ModelNotFoundException.php @@ -12,8 +12,6 @@ declare (strict_types = 1); namespace think\db\exception; -use think\exception\DbException; - class ModelNotFoundException extends DbException { protected $model; diff --git a/src/exception/PDOException.php b/src/db/exception/PDOException.php similarity index 96% rename from src/exception/PDOException.php rename to src/db/exception/PDOException.php index 314b521..41c0d73 100644 --- a/src/exception/PDOException.php +++ b/src/db/exception/PDOException.php @@ -8,8 +8,9 @@ // +---------------------------------------------------------------------- // | Author: 麦当苗儿 // +---------------------------------------------------------------------- +declare (strict_types = 1); -namespace think\exception; +namespace think\db\exception; /** * PDO异常处理类 diff --git a/src/exception/BindParamException.php b/src/exception/BindParamException.php deleted file mode 100644 index b657a7a..0000000 --- a/src/exception/BindParamException.php +++ /dev/null @@ -1,35 +0,0 @@ - -// +---------------------------------------------------------------------- -declare (strict_types = 1); - -namespace think\exception; - -/** - * PDO参数绑定异常 - */ -class BindParamException extends DbException -{ - - /** - * BindParamException constructor. - * @access public - * @param string $message - * @param array $config - * @param string $sql - * @param array $bind - * @param int $code - */ - public function __construct(string $message, array $config, string $sql, array $bind, int $code = 10502) - { - $this->setData('Bind Param', $bind); - parent::__construct($message, $config, $sql, $code); - } -} diff --git a/src/exception/DataNotFoundException.php b/src/exception/DataNotFoundException.php deleted file mode 100644 index 89b1c09..0000000 --- a/src/exception/DataNotFoundException.php +++ /dev/null @@ -1,43 +0,0 @@ - -// +---------------------------------------------------------------------- -declare (strict_types = 1); - -namespace think\exception; - -class DataNotFoundException extends DbException -{ - protected $table; - - /** - * DbException constructor. - * @access public - * @param string $message - * @param string $table - * @param array $config - */ - public function __construct(string $message, string $table = '', array $config = []) - { - $this->message = $message; - $this->table = $table; - - $this->setData('Database Config', $config); - } - - /** - * 获取数据表名 - * @access public - * @return string - */ - public function getTable() - { - return $this->table; - } -} diff --git a/src/exception/DbException.php b/src/exception/DbException.php deleted file mode 100644 index 93200a6..0000000 --- a/src/exception/DbException.php +++ /dev/null @@ -1,44 +0,0 @@ - -// +---------------------------------------------------------------------- - -namespace think\exception; - -use think\Exception; - -/** - * Database相关异常处理类 - */ -class DbException extends Exception -{ - /** - * DbException constructor. - * @access public - * @param string $message - * @param array $config - * @param string $sql - * @param int $code - */ - public function __construct(string $message, array $config = [], string $sql = '', int $code = 10500) - { - $this->message = $message; - $this->code = $code; - - $this->setData('Database Status', [ - 'Error Code' => $code, - 'Error Message' => $message, - 'Error SQL' => $sql, - ]); - - unset($config['username'], $config['password']); - $this->setData('Database Config', $config); - } - -} diff --git a/src/exception/ModelNotFoundException.php b/src/exception/ModelNotFoundException.php deleted file mode 100644 index cb54b42..0000000 --- a/src/exception/ModelNotFoundException.php +++ /dev/null @@ -1,44 +0,0 @@ - -// +---------------------------------------------------------------------- -declare (strict_types = 1); - -namespace think\exception; - -class ModelNotFoundException extends DbException -{ - protected $model; - - /** - * 构造方法 - * @access public - * @param string $message - * @param string $model - * @param array $config - */ - public function __construct(string $message, string $model = '', array $config = []) - { - $this->message = $message; - $this->model = $model; - - $this->setData('Database Config', $config); - } - - /** - * 获取模型类名 - * @access public - * @return string - */ - public function getModel() - { - return $this->model; - } - -} diff --git a/src/facade/Db.php b/src/facade/Db.php index 130c588..b0296c6 100644 --- a/src/facade/Db.php +++ b/src/facade/Db.php @@ -14,8 +14,8 @@ namespace think\facade; use think\Facade; /** - * @see \think\Db - * @mixin \think\Db + * @see \think\DbManager + * @mixin \think\DbManager */ class Db extends Facade { diff --git a/src/model/Relation.php b/src/model/Relation.php index aa2bd50..403dbbc 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -12,14 +12,13 @@ declare (strict_types = 1); namespace think\model; +use think\db\exception\DbException as Exception; use think\db\Query; -use think\Exception; use think\Model; /** * 模型关联基础类 * @package think\model - * * @mixin Query */ abstract class Relation diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index 884575b..dc96399 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -14,7 +14,7 @@ namespace think\model\concern; use think\Collection; use think\Container; -use think\Exception; +use think\db\exception\DbException as Exception; use think\Model; use think\model\Collection as ModelCollection; use think\model\relation\OneToOne; diff --git a/src/model/concern/ModelEvent.php b/src/model/concern/ModelEvent.php index e58161a..a5b4897 100644 --- a/src/model/concern/ModelEvent.php +++ b/src/model/concern/ModelEvent.php @@ -13,7 +13,7 @@ declare (strict_types = 1); namespace think\model\concern; use think\Container; -use think\exception\ModelEventException; +use think\db\exception\ModelEventException; /** * 模型事件处理 diff --git a/src/model/concern/OptimLock.php b/src/model/concern/OptimLock.php index a4d72e6..5e61318 100644 --- a/src/model/concern/OptimLock.php +++ b/src/model/concern/OptimLock.php @@ -12,7 +12,7 @@ declare (strict_types = 1); namespace think\model\concern; -use think\Exception; +use think\db\exception\DbException as Exception; /** * 乐观锁 diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index c7a02bc..68457ff 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -15,8 +15,8 @@ namespace think\model\concern; use Closure; use think\Collection; use think\Container; +use think\db\exception\DbException as Exception; use think\db\Query; -use think\Exception; use think\Model; use think\model\Relation; use think\model\relation\BelongsTo; diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 7132b65..c83aabc 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -15,8 +15,8 @@ use Closure; use think\Collection; use think\Container; use think\db\BaseQuery as Query; +use think\db\exception\DbException as Exception; use think\db\Raw; -use think\Exception; use think\Model; use think\model\Pivot; use think\model\Relation; diff --git a/src/model/relation/MorphMany.php b/src/model/relation/MorphMany.php index a1dcc33..5d0e0f7 100644 --- a/src/model/relation/MorphMany.php +++ b/src/model/relation/MorphMany.php @@ -15,7 +15,7 @@ use Closure; use think\Collection; use think\Container; use think\db\BaseQuery as Query; -use think\Exception; +use think\db\exception\DbException as Exception; use think\Model; use think\model\Relation; diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php index 7a3de57..54c53f5 100644 --- a/src/model/relation/MorphOne.php +++ b/src/model/relation/MorphOne.php @@ -14,7 +14,7 @@ namespace think\model\relation; use Closure; use think\Container; use think\db\BaseQuery as Query; -use think\Exception; +use think\db\exception\DbException as Exception; use think\Model; use think\model\Relation; diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index 745f7b4..ed6b18c 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -13,7 +13,7 @@ namespace think\model\relation; use Closure; use think\Container; -use think\Exception; +use think\db\exception\DbException as Exception; use think\Model; use think\model\Relation; diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index b5ff1a6..f3bbb1a 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -14,7 +14,7 @@ namespace think\model\relation; use Closure; use think\Container; use think\db\BaseQuery as Query; -use think\Exception; +use think\db\exception\DbException as Exception; use think\Model; use think\model\Relation; -- Gitee From 8c088a18b178f034bd303b3d37f15ee882a98363 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Wed, 10 Jul 2019 14:42:34 +0800 Subject: [PATCH 030/365] =?UTF-8?q?=E7=A7=BB=E9=99=A4think\Collection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + composer.json | 4 +- src/Collection.php | 645 --------------------------------------------- src/DbManager.php | 5 +- src/Model.php | 13 +- 5 files changed, 16 insertions(+), 653 deletions(-) delete mode 100644 src/Collection.php diff --git a/.gitignore b/.gitignore index 485dee6..82cfc4e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .idea +composer.lock +vendor diff --git a/composer.json b/composer.json index 0933c07..391e71a 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ ], "require": { "php": ">=7.1.0", - "topthink/think-container":"^2.0" + "topthink/think-container":"^2.0.2" }, "suggest": { "topthink/think-cache": "Required to use query cache (^2.0)." @@ -25,4 +25,4 @@ }, "files": [] } -} \ No newline at end of file +} diff --git a/src/Collection.php b/src/Collection.php deleted file mode 100644 index 9997508..0000000 --- a/src/Collection.php +++ /dev/null @@ -1,645 +0,0 @@ - -// +---------------------------------------------------------------------- -declare (strict_types = 1); - -namespace think; - -use ArrayAccess; -use ArrayIterator; -use Countable; -use IteratorAggregate; -use JsonSerializable; - -/** - * 数据集管理类 - */ -class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable -{ - /** - * 数据集数据 - * @var array - */ - protected $items = []; - - public function __construct($items = []) - { - $this->items = $this->convertToArray($items); - } - - public static function make($items = []) - { - return new static($items); - } - - /** - * 是否为空 - * @access public - * @return bool - */ - public function isEmpty(): bool - { - return empty($this->items); - } - - public function toArray(): array - { - return array_map(function ($value) { - return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value; - }, $this->items); - } - - public function all(): array - { - return $this->items; - } - - /** - * 合并数组 - * - * @access public - * @param mixed $items 数据 - * @return static - */ - public function merge($items) - { - return new static(array_merge($this->items, $this->convertToArray($items))); - } - - /** - * 按指定键整理数据 - * - * @access public - * @param mixed $items 数据 - * @param string $indexKey 键名 - * @return array - */ - public function dictionary($items = null, string &$indexKey = null) - { - if ($items instanceof self || $items instanceof Paginator) { - $items = $items->all(); - } - - $items = is_null($items) ? $this->items : $items; - - if ($items && empty($indexKey)) { - $indexKey = is_array($items[0]) ? 'id' : $items[0]->getPk(); - } - - if (isset($indexKey) && is_string($indexKey)) { - return array_column($items, null, $indexKey); - } - - return $items; - } - - /** - * 比较数组,返回差集 - * - * @access public - * @param mixed $items 数据 - * @param string $indexKey 指定比较的键名 - * @return static - */ - public function diff($items, string $indexKey = null) - { - if ($this->isEmpty() || is_scalar($this->items[0])) { - return new static(array_diff($this->items, $this->convertToArray($items))); - } - - $diff = []; - $dictionary = $this->dictionary($items, $indexKey); - - if (is_string($indexKey)) { - foreach ($this->items as $item) { - if (!isset($dictionary[$item[$indexKey]])) { - $diff[] = $item; - } - } - } - - return new static($diff); - } - - /** - * 比较数组,返回交集 - * - * @access public - * @param mixed $items 数据 - * @param string $indexKey 指定比较的键名 - * @return static - */ - public function intersect($items, string $indexKey = null) - { - if ($this->isEmpty() || is_scalar($this->items[0])) { - return new static(array_diff($this->items, $this->convertToArray($items))); - } - - $intersect = []; - $dictionary = $this->dictionary($items, $indexKey); - - if (is_string($indexKey)) { - foreach ($this->items as $item) { - if (isset($dictionary[$item[$indexKey]])) { - $intersect[] = $item; - } - } - } - - return new static($intersect); - } - - /** - * 交换数组中的键和值 - * - * @access public - * @return static - */ - public function flip() - { - return new static(array_flip($this->items)); - } - - /** - * 返回数组中所有的键名 - * - * @access public - * @return static - */ - public function keys() - { - return new static(array_keys($this->items)); - } - - /** - * 返回数组中所有的值组成的新 Collection 实例 - * @access public - * @return static - */ - public function values() - { - return new static(array_values($this->items)); - } - - /** - * 删除数组的最后一个元素(出栈) - * - * @access public - * @return mixed - */ - public function pop() - { - return array_pop($this->items); - } - - /** - * 通过使用用户自定义函数,以字符串返回数组 - * - * @access public - * @param callable $callback 调用方法 - * @param mixed $initial - * @return mixed - */ - public function reduce(callable $callback, $initial = null) - { - return array_reduce($this->items, $callback, $initial); - } - - /** - * 以相反的顺序返回数组。 - * - * @access public - * @return static - */ - public function reverse() - { - return new static(array_reverse($this->items)); - } - - /** - * 删除数组中首个元素,并返回被删除元素的值 - * - * @access public - * @return mixed - */ - public function shift() - { - return array_shift($this->items); - } - - /** - * 在数组结尾插入一个元素 - * @access public - * @param mixed $value 元素 - * @param string $key KEY - * @return void - */ - public function push($value, string $key = null): void - { - if (is_null($key)) { - $this->items[] = $value; - } else { - $this->items[$key] = $value; - } - } - - /** - * 把一个数组分割为新的数组块. - * - * @access public - * @param int $size 块大小 - * @param bool $preserveKeys - * @return static - */ - public function chunk(int $size, bool $preserveKeys = false) - { - $chunks = []; - - foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) { - $chunks[] = new static($chunk); - } - - return new static($chunks); - } - - /** - * 在数组开头插入一个元素 - * @access public - * @param mixed $value 元素 - * @param string $key KEY - * @return void - */ - public function unshift($value, string $key = null): void - { - if (is_null($key)) { - array_unshift($this->items, $value); - } else { - $this->items = [$key => $value] + $this->items; - } - } - - /** - * 给每个元素执行个回调 - * - * @access public - * @param callable $callback 回调 - * @return $this - */ - public function each(callable $callback) - { - foreach ($this->items as $key => $item) { - $result = $callback($item, $key); - - if (false === $result) { - break; - } elseif (!is_object($item)) { - $this->items[$key] = $result; - } - } - - return $this; - } - - /** - * 用回调函数处理数组中的元素 - * @access public - * @param callable|null $callback 回调 - * @return static - */ - public function map(callable $callback) - { - return new static(array_map($callback, $this->items)); - } - - /** - * 用回调函数过滤数组中的元素 - * @access public - * @param callable|null $callback 回调 - * @return static - */ - public function filter(callable $callback = null) - { - if ($callback) { - return new static(array_filter($this->items, $callback)); - } - - return new static(array_filter($this->items)); - } - - /** - * 根据字段条件过滤数组中的元素 - * @access public - * @param string $field 字段名 - * @param mixed $operator 操作符 - * @param mixed $value 数据 - * @return static - */ - public function where(string $field, $operator, $value = null) - { - if (is_null($value)) { - $value = $operator; - $operator = '='; - } - - return $this->filter(function ($data) use ($field, $operator, $value) { - if (strpos($field, '.')) { - list($field, $relation) = explode('.', $field); - - $result = $data[$field][$relation] ?? null; - } else { - $result = $data[$field] ?? null; - } - - switch ($operator) { - case '===': - return $result === $value; - case '!==': - return $result !== $value; - case '!=': - case '<>': - return $result != $value; - case '>': - return $result > $value; - case '>=': - return $result >= $value; - case '<': - return $result < $value; - case '<=': - return $result <= $value; - case 'like': - return is_string($result) && false !== strpos($result, $value); - case 'not like': - return is_string($result) && false === strpos($result, $value); - case 'in': - return is_scalar($result) && in_array($result, $value, true); - case 'not in': - return is_scalar($result) && !in_array($result, $value, true); - case 'between': - list($min, $max) = is_string($value) ? explode(',', $value) : $value; - return is_scalar($result) && $result >= $min && $result <= $max; - case 'not between': - list($min, $max) = is_string($value) ? explode(',', $value) : $value; - return is_scalar($result) && $result > $max || $result < $min; - case '==': - case '=': - default: - return $result == $value; - } - }); - } - - /** - * LIKE过滤 - * @access public - * @param string $field 字段名 - * @param string $value 数据 - * @return static - */ - public function whereLike(string $field, string $value) - { - return $this->where($field, 'like', $value); - } - - /** - * NOT LIKE过滤 - * @access public - * @param string $field 字段名 - * @param string $value 数据 - * @return static - */ - public function whereNotLike(string $field, string $value) - { - return $this->where($field, 'not like', $value); - } - - /** - * IN过滤 - * @access public - * @param string $field 字段名 - * @param array $value 数据 - * @return static - */ - public function whereIn(string $field, array $value) - { - return $this->where($field, 'in', $value); - } - - /** - * NOT IN过滤 - * @access public - * @param string $field 字段名 - * @param array $value 数据 - * @return static - */ - public function whereNotIn(string $field, array $value) - { - return $this->where($field, 'not in', $value); - } - - /** - * BETWEEN 过滤 - * @access public - * @param string $field 字段名 - * @param mixed $value 数据 - * @return static - */ - public function whereBetween(string $field, $value) - { - return $this->where($field, 'between', $value); - } - - /** - * NOT BETWEEN 过滤 - * @access public - * @param string $field 字段名 - * @param mixed $value 数据 - * @return static - */ - public function whereNotBetween(string $field, $value) - { - return $this->where($field, 'not between', $value); - } - - /** - * 返回数据中指定的一列 - * @access public - * @param string $columnKey 键名 - * @param string $indexKey 作为索引值的列 - * @return array - */ - public function column(string $columnKey, string $indexKey = null) - { - return array_column($this->items, $columnKey, $indexKey); - } - - /** - * 对数组排序 - * - * @access public - * @param callable|null $callback 回调 - * @return static - */ - public function sort(callable $callback = null) - { - $items = $this->items; - - $callback = $callback ?: function ($a, $b) { - return $a == $b ? 0 : (($a < $b) ? -1 : 1); - - }; - - uasort($items, $callback); - - return new static($items); - } - - /** - * 指定字段排序 - * @access public - * @param string $field 排序字段 - * @param string $order 排序 - * @return $this - */ - public function order(string $field, string $order = null) - { - return $this->sort(function ($a, $b) use ($field, $order) { - $fieldA = $a[$field] ?? null; - $fieldB = $b[$field] ?? null; - - return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB); - }); - } - - /** - * 将数组打乱 - * - * @access public - * @return static - */ - public function shuffle() - { - $items = $this->items; - - shuffle($items); - - return new static($items); - } - - /** - * 获取最后一个单元数据 - * - * @access public - * @return mixed - */ - public function first() - { - return reset($this->items); - } - - /** - * 获取第一个单元数据 - * - * @access public - * @return mixed - */ - public function last() - { - return end($this->items); - } - - /** - * 截取数组 - * - * @access public - * @param int $offset 起始位置 - * @param int $length 截取长度 - * @param bool $preserveKeys preserveKeys - * @return static - */ - public function slice(int $offset, int $length = null, bool $preserveKeys = false) - { - return new static(array_slice($this->items, $offset, $length, $preserveKeys)); - } - - // ArrayAccess - public function offsetExists($offset) - { - return array_key_exists($offset, $this->items); - } - - public function offsetGet($offset) - { - return $this->items[$offset]; - } - - public function offsetSet($offset, $value) - { - if (is_null($offset)) { - $this->items[] = $value; - } else { - $this->items[$offset] = $value; - } - } - - public function offsetUnset($offset) - { - unset($this->items[$offset]); - } - - //Countable - public function count() - { - return count($this->items); - } - - //IteratorAggregate - public function getIterator() - { - return new ArrayIterator($this->items); - } - - //JsonSerializable - public function jsonSerialize() - { - return $this->toArray(); - } - - /** - * 转换当前数据集为JSON字符串 - * @access public - * @param integer $options json参数 - * @return string - */ - public function toJson(int $options = JSON_UNESCAPED_UNICODE) - { - return json_encode($this->toArray(), $options); - } - - public function __toString() - { - return $this->toJson(); - } - - /** - * 转换成数组 - * - * @access public - * @param mixed $items 数据 - * @return array - */ - protected function convertToArray($items): array - { - if ($items instanceof self) { - return $items->all(); - } - - return (array) $items; - } -} diff --git a/src/DbManager.php b/src/DbManager.php index be98202..74486b7 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -16,13 +16,14 @@ use InvalidArgumentException; use think\CacheManager; use think\db\BaseQuery; use think\db\Connection; +use think\db\Query; use think\db\Raw; /** * Class DbManager * @package think - * @mixin think\db\BaseQuery - * @mixin think\db\Query + * @mixin BaseQuery + * @mixin Query */ class DbManager { diff --git a/src/Model.php b/src/Model.php index 3263e1d..3d3453a 100644 --- a/src/Model.php +++ b/src/Model.php @@ -15,6 +15,8 @@ namespace think; use ArrayAccess; use Closure; use JsonSerializable; +use think\contract\Arrayable; +use think\contract\Jsonable; use think\db\Query; /** @@ -33,7 +35,7 @@ use think\db\Query; * @method void onBeforeRestore(Model $model) static before_restore事件定义 * @method void onAfterRestore(Model $model) static after_restore事件定义 */ -abstract class Model implements JsonSerializable, ArrayAccess +abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonable { use model\concern\Attribute; use model\concern\RelationShip; @@ -351,13 +353,16 @@ abstract class Model implements JsonSerializable, ArrayAccess * @return void */ protected static function init() - {} + { + } protected function checkData(): void - {} + { + } protected function checkResult($result): void - {} + { + } /** * 更新是否强制写入数据 而不做比较 -- Gitee From fd7e5b5c8b8f20ce03d3b63f3c7fb80069aa0f4f Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 11 Jul 2019 12:34:33 +0800 Subject: [PATCH 031/365] =?UTF-8?q?=E8=A7=A3=E9=99=A4=E5=AF=B9think-contai?= =?UTF-8?q?ner=E7=9A=84=E4=BE=9D=E8=B5=96=20=E5=A2=9E=E5=8A=A0think-helper?= =?UTF-8?q?=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 2 +- src/DbManager.php | 43 +++++++++++++++++++---- src/Model.php | 12 ------- src/db/BaseQuery.php | 12 +++---- src/db/Fetch.php | 6 ++-- src/db/concern/JoinAndViewQuery.php | 4 +-- src/db/concern/ModelRelationQuery.php | 8 ++--- src/db/concern/ResultOperation.php | 4 +-- src/db/exception/ModelEventException.php | 19 ++++++++++ src/model/concern/Attribute.php | 8 ++--- src/model/concern/Conversion.php | 4 +-- src/model/concern/ModelEvent.php | 7 ++-- src/model/concern/RelationShip.php | 44 ++++++++++++------------ src/model/relation/BelongsTo.php | 14 ++++---- src/model/relation/BelongsToMany.php | 8 ++--- src/model/relation/HasMany.php | 14 ++++---- src/model/relation/HasManyThrough.php | 16 ++++----- src/model/relation/HasOne.php | 14 ++++---- src/model/relation/HasOneThrough.php | 6 ++-- src/model/relation/MorphMany.php | 6 ++-- src/model/relation/MorphOne.php | 6 ++-- src/model/relation/MorphTo.php | 8 ++--- src/model/relation/OneToOne.php | 6 ++-- 23 files changed, 154 insertions(+), 117 deletions(-) create mode 100644 src/db/exception/ModelEventException.php diff --git a/composer.json b/composer.json index 391e71a..2247119 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ ], "require": { "php": ">=7.1.0", - "topthink/think-container":"^2.0.2" + "topthink/think-helper":"^3.1" }, "suggest": { "topthink/think-cache": "Required to use query cache (^2.0)." diff --git a/src/DbManager.php b/src/DbManager.php index 74486b7..1d62e30 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -70,15 +70,40 @@ class DbManager protected $cache; /** - * 初始化 + * 架构函数 + * @access public + */ + public function __construct() + { + // 模型注入Db对象 + Model::maker(function (Model $model) { + $model->setDb($this); + + $isAutoWriteTimestamp = $model->getAutoWriteTimestamp(); + + if (is_null($isAutoWriteTimestamp)) { + // 自动写入时间戳 + $model->isAutoWriteTimestamp($this->config['auto_timestamp'] ?? true); + } + + $dateFormat = $model->getDateFormat(); + + if (is_null($dateFormat)) { + // 设置时间戳格式 + $model->setDateFormat($this->config['datetime_format'] ?? 'Y-m-d H:i:s'); + } + }); + } + + /** + * 初始化配置参数 * @access public * @param array $config 连接配置 - * @return $this + * @return void */ - public function init(array $config = []) + public function setConfig(array $config = []): void { $this->config = $config; - return $this; } /** @@ -87,7 +112,7 @@ class DbManager * @param CacheManager $cache 缓存对象 * @return void */ - public function setCache(CacheManager $cache) + public function setCache(CacheManager $cache): void { $this->cache = $cache; } @@ -154,7 +179,13 @@ class DbManager $config = $this->config['connections'][$name]; $type = !empty($config['type']) ? $config['type'] : 'mysql'; - $this->instance[$name] = Container::factory($type, '\\think\\db\\connector\\', $config); + if (strpos($type, '\\')) { + $class = $type; + } else { + $class = '\\think\\db\\connector\\' . ucfirst($type); + } + + $this->instance[$name] = new $class($config); } return $this->instance[$name]; diff --git a/src/Model.php b/src/Model.php index 3d3453a..42df736 100644 --- a/src/Model.php +++ b/src/Model.php @@ -184,18 +184,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab if (static::$maker) { call_user_func(static::$maker, $this); - } else { - $this->db = Container::pull('think\DbManager'); - - if (is_null($this->autoWriteTimestamp)) { - // 自动写入时间戳 - $this->autoWriteTimestamp = $this->db->getConfig('auto_timestamp'); - } - - if (is_null($this->dateFormat)) { - // 设置时间戳格式 - $this->dateFormat = $this->db->getConfig('datetime_format'); - } } // 执行初始化操作 diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 0f9aa34..6c075c9 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -13,12 +13,12 @@ declare (strict_types = 1); namespace think\db; use think\Collection; -use think\Container; use think\db\exception\BindParamException; use think\db\exception\DataNotFoundException; use think\db\exception\DbException as Exception; use think\db\exception\ModelNotFoundException; use think\db\exception\PDOException; +use think\helper\Str; use think\Model; use think\Paginator; @@ -110,18 +110,18 @@ class BaseQuery { if (strtolower(substr($method, 0, 5)) == 'getby') { // 根据某个字段获取记录 - $field = Container::parseName(substr($method, 5)); + $field = Str::snake(substr($method, 5)); return $this->where($field, '=', $args[0])->find(); } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { // 根据某个字段获取记录的某个值 - $name = Container::parseName(substr($method, 10)); + $name = Str::snake(substr($method, 10)); return $this->where($name, '=', $args[0])->value($args[1]); } elseif (strtolower(substr($method, 0, 7)) == 'whereor') { - $name = Container::parseName(substr($method, 7)); + $name = Str::snake(substr($method, 7)); array_unshift($args, $name); return call_user_func_array([$this, 'whereOr'], $args); } elseif (strtolower(substr($method, 0, 5)) == 'where') { - $name = Container::parseName(substr($method, 5)); + $name = Str::snake(substr($method, 5)); array_unshift($args, $name); return call_user_func_array([$this, 'where'], $args); } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { @@ -206,7 +206,7 @@ class BaseQuery $name = $name ?: $this->name; - return $this->prefix . Container::parseName($name); + return $this->prefix . Str::snake($name); } /** diff --git a/src/db/Fetch.php b/src/db/Fetch.php index 3be92e2..3d5bf76 100644 --- a/src/db/Fetch.php +++ b/src/db/Fetch.php @@ -12,8 +12,8 @@ declare (strict_types = 1); namespace think\db; -use think\Container; use think\db\exception\DbException as Exception; +use think\helper\Str; /** * SQL获取类 @@ -479,11 +479,11 @@ class Fetch { if (strtolower(substr($method, 0, 5)) == 'getby') { // 根据某个字段获取记录 - $field = Container::parseName(substr($method, 5)); + $field = Str::snake(substr($method, 5)); return $this->where($field, '=', $args[0])->find(); } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { // 根据某个字段获取记录的某个值 - $name = Container::parseName(substr($method, 10)); + $name = Str::snake(substr($method, 10)); return $this->where($name, '=', $args[0])->value($args[1]); } diff --git a/src/db/concern/JoinAndViewQuery.php b/src/db/concern/JoinAndViewQuery.php index 2e7de27..ad46d08 100644 --- a/src/db/concern/JoinAndViewQuery.php +++ b/src/db/concern/JoinAndViewQuery.php @@ -13,8 +13,8 @@ declare (strict_types = 1); namespace think\db\concern; use Closure; -use think\Container; use think\db\Raw; +use think\helper\Str; use think\model\relation\OneToOne; /** @@ -164,7 +164,7 @@ trait JoinAndViewQuery } /** @var Relation $model */ - $relation = Container::parseName($relation, 1, false); + $relation = Str::camel($relation); $model = $class->$relation(); if ($model instanceof OneToOne) { diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 6f73db6..4dab48f 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -13,7 +13,7 @@ declare (strict_types = 1); namespace think\db\concern; use Closure; -use think\Container; +use think\helper\Str; use think\Model; use think\model\Collection as ModelCollection; @@ -154,7 +154,7 @@ trait ModelRelationQuery } elseif ($this->model) { // 检测搜索器 $fieldName = is_numeric($key) ? $field : $key; - $method = 'search' . Container::parseName($fieldName, 1) . 'Attr'; + $method = 'search' . Str::studly($fieldName) . 'Attr'; if (method_exists($this->model, $method)) { $this->model->$method($this, $data[$field] ?? null, $data, $prefix); @@ -227,14 +227,14 @@ trait ModelRelationQuery $relation = $key; } - $relation = Container::parseName($relation, 1, false); + $relation = Str::camel($relation); $count = $this->model ->$relation() ->getRelationCountQuery($closure, $aggregate, $field, $aggregateField); if (empty($aggregateField)) { - $aggregateField = Container::parseName($relation) . '_' . $aggregate; + $aggregateField = Str::snake($relation) . '_' . $aggregate; } $this->field(['(' . $count . ')' => $aggregateField]); diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index 4cbf4f8..01dbf1d 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -13,9 +13,9 @@ declare (strict_types = 1); namespace think\db\concern; use think\Collection; -use think\Container; use think\db\exception\DataNotFoundException; use think\db\exception\ModelNotFoundException; +use think\helper\Str; /** * 查询数据处理 @@ -126,7 +126,7 @@ trait ResultOperation protected function getResultAttr(array &$result, array $withAttr = []): void { foreach ($withAttr as $name => $closure) { - $name = Container::parseName($name); + $name = Str::snake($name); if (strpos($name, '.')) { // 支持JSON字段 获取器定义 diff --git a/src/db/exception/ModelEventException.php b/src/db/exception/ModelEventException.php new file mode 100644 index 0000000..767bc1a --- /dev/null +++ b/src/db/exception/ModelEventException.php @@ -0,0 +1,19 @@ + +// +---------------------------------------------------------------------- + +namespace think\db\exception; + +/** + * 模型事件异常 + */ +class ModelEventException extends DbException +{ +} diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 3c0ec22..3ce402f 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -13,8 +13,8 @@ declare (strict_types = 1); namespace think\model\concern; use InvalidArgumentException; -use think\Container; use think\db\Raw; +use think\helper\Str; use think\model\Relation; /** @@ -185,7 +185,7 @@ trait Attribute */ protected function getRealFieldName(string $name): string { - return $this->strict ? $name : Container::parseName($name); + return $this->strict ? $name : Str::snake($name); } /** @@ -359,7 +359,7 @@ trait Attribute $value = $this->autoWriteTimestamp(); } else { // 检测修改器 - $method = 'set' . Container::parseName($name, 1) . 'Attr'; + $method = 'set' . Str::studly($name) . 'Attr'; if (method_exists($this, $method)) { $array = $this->data; @@ -482,7 +482,7 @@ trait Attribute { // 检测属性获取器 $fieldName = $this->getRealFieldName($name); - $method = 'get' . Container::parseName($name, 1) . 'Attr'; + $method = 'get' . Str::studly($name) . 'Attr'; if (isset($this->withAttr[$fieldName])) { if ($relation) { diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index dc96399..781825a 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -13,8 +13,8 @@ declare (strict_types = 1); namespace think\model\concern; use think\Collection; -use think\Container; use think\db\exception\DbException as Exception; +use think\helper\Str; use think\Model; use think\model\Collection as ModelCollection; use think\model\relation\OneToOne; @@ -71,7 +71,7 @@ trait Conversion */ public function appendRelationAttr(string $attr, array $append) { - $relation = Container::parseName($attr, 1, false); + $relation = Str::camel($attr); if (isset($this->relation[$relation])) { $model = $this->relation[$relation]; diff --git a/src/model/concern/ModelEvent.php b/src/model/concern/ModelEvent.php index a5b4897..0f89b76 100644 --- a/src/model/concern/ModelEvent.php +++ b/src/model/concern/ModelEvent.php @@ -12,8 +12,8 @@ declare (strict_types = 1); namespace think\model\concern; -use think\Container; use think\db\exception\ModelEventException; +use think\helper\Str; /** * 模型事件处理 @@ -51,12 +51,11 @@ trait ModelEvent return true; } - $call = 'on' . Container::parseName($event, 1); + $call = 'on' . Str::studly($event); try { if (method_exists(static::class, $call)) { - $result = Container::getInstance() - ->invoke([static::class, $call], [$this]); + $result = call_user_func([static::class, $call], $this); } else { $result = true; } diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 68457ff..8e18b68 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -14,9 +14,9 @@ namespace think\model\concern; use Closure; use think\Collection; -use think\Container; use think\db\exception\DbException as Exception; use think\db\Query; +use think\helper\Str; use think\Model; use think\model\Relation; use think\model\relation\BelongsTo; @@ -97,7 +97,7 @@ trait RelationShip if (array_key_exists($name, $this->relation)) { return $this->relation[$name]; } elseif ($auto) { - $relation = Container::parseName($name, 1, false); + $relation = Str::camel($name); return $this->getRelationValue($relation); } } @@ -113,7 +113,7 @@ trait RelationShip public function setRelation(string $name, $value, array $data = []) { // 检测修改器 - $method = 'set' . Container::parseName($name, 1) . 'Attr'; + $method = 'set' . Str::studly($name) . 'Attr'; if (method_exists($this, $method)) { $value = $this->$method($value, array_merge($this->data, $data)); @@ -150,8 +150,8 @@ trait RelationShip list($relation, $subRelation) = explode('.', $relation, 2); } - $method = Container::parseName($relation, 1, false); - $relationName = Container::parseName($relation); + $method = Str::camel($relation); + $relationName = Str::snake($relation); $relationResult = $this->$method(); @@ -240,8 +240,8 @@ trait RelationShip $subRelation = [$subRelation]; } - $relation = Container::parseName($relation, 1, false); - $relationName = Container::parseName($relation); + $relation = Str::camel($relation); + $relationName = Str::snake($relation); $relationResult = $this->$relation(); @@ -282,8 +282,8 @@ trait RelationShip $subRelation = [$subRelation]; } - $relation = Container::parseName($relation, 1, false); - $relationName = Container::parseName($relation); + $relation = Str::camel($relation); + $relationName = Str::snake($relation); $relationResult = $this->$relation(); @@ -343,11 +343,11 @@ trait RelationShip $relation = $key; } - $relation = Container::parseName($relation, 1, false); + $relation = Str::camel($relation); $count = $this->$relation()->relationCount($result, $closure, $aggregate, $field, $name); if (empty($name)) { - $name = Container::parseName($relation) . '_' . $aggregate; + $name = Str::snake($relation) . '_' . $aggregate; } $result->setAttr($name, $count); @@ -387,7 +387,7 @@ trait RelationShip $foreignKey = $foreignKey ?: $this->getForeignKey((new $model)->getName()); $localKey = $localKey ?: (new $model)->getPk(); $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $relation = Container::parseName($trace[1]['function']); + $relation = Str::snake($trace[1]['function']); return new BelongsTo($this, $model, $foreignKey, $localKey, $relation); } @@ -471,8 +471,8 @@ trait RelationShip { // 记录当前关联信息 $model = $this->parseModel($model); - $name = Container::parseName(Container::classBaseName($model)); - $middle = $middle ?: Container::parseName($this->name) . '_' . $name; + $name = Str::snake(class_basename($model)); + $middle = $middle ?: Str::snake($this->name) . '_' . $name; $foreignKey = $foreignKey ?: $name . '_id'; $localKey = $localKey ?: $this->getForeignKey($this->name); @@ -494,7 +494,7 @@ trait RelationShip if (is_null($morph)) { $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $morph = Container::parseName($trace[1]['function']); + $morph = Str::snake($trace[1]['function']); } if (is_array($morph)) { @@ -524,7 +524,7 @@ trait RelationShip if (is_null($morph)) { $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $morph = Container::parseName($trace[1]['function']); + $morph = Str::snake($trace[1]['function']); } $type = $type ?: get_class($this); @@ -549,7 +549,7 @@ trait RelationShip public function morphTo($morph = null, array $alias = []): MorphTo { $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $relation = Container::parseName($trace[1]['function']); + $relation = Str::snake($trace[1]['function']); if (is_null($morph)) { $morph = $relation; @@ -577,7 +577,7 @@ trait RelationShip if (false === strpos($model, '\\')) { $path = explode('\\', static::class); array_pop($path); - array_push($path, Container::parseName($model, 1)); + array_push($path, Str::studly($model)); $model = implode('\\', $path); } @@ -593,10 +593,10 @@ trait RelationShip protected function getForeignKey(string $name): string { if (strpos($name, '\\')) { - $name = Container::classBaseName($name); + $name = class_basename($name); } - return Container::parseName($name) . '_id'; + return Str::snake($name) . '_id'; } /** @@ -607,7 +607,7 @@ trait RelationShip */ protected function isRelationAttr(string $attr) { - $relation = Container::parseName($attr, 1, false); + $relation = Str::camel($attr); if (method_exists($this, $relation) && !method_exists('think\Model', $relation)) { return $relation; @@ -691,7 +691,7 @@ trait RelationShip protected function autoRelationInsert(): void { foreach ($this->relationWrite as $name => $val) { - $method = Container::parseName($name, 1, false); + $method = Str::camel($name); $this->$method()->save($val); } } diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 5794d70..57414e8 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -13,8 +13,8 @@ declare (strict_types = 1); namespace think\model\relation; use Closure; -use think\Container; use think\db\BaseQuery as Query; +use think\helper\Str; use think\Model; /** @@ -133,8 +133,8 @@ class BelongsTo extends OneToOne public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query { $table = $this->query->getTable(); - $model = Container::classBaseName($this->parent); - $relation = Container::classBaseName($this->model); + $model = class_basename($this->parent); + $relation = class_basename($this->model); $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -158,8 +158,8 @@ class BelongsTo extends OneToOne public function hasWhere($where = [], $fields = null, string $joinType = ''): Query { $table = $this->query->getTable(); - $model = Container::classBaseName($this->parent); - $relation = Container::classBaseName($this->model); + $model = class_basename($this->parent); + $relation = class_basename($this->model); if (is_array($where)) { $this->getQueryWhere($where, $relation); @@ -209,7 +209,7 @@ class BelongsTo extends OneToOne ], $localKey, $relation, $subRelation, $closure); // 关联属性名 - $attr = Container::parseName($relation); + $attr = Str::snake($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -267,7 +267,7 @@ class BelongsTo extends OneToOne $this->bindAttr($relationModel, $result); } else { // 设置关联属性 - $result->setRelation(Container::parseName($relation), $relationModel); + $result->setRelation(Str::snake($relation), $relationModel); } } diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index c83aabc..4f15d32 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -13,10 +13,10 @@ namespace think\model\relation; use Closure; use think\Collection; -use think\Container; use think\db\BaseQuery as Query; use think\db\exception\DbException as Exception; use think\db\Raw; +use think\helper\Str; use think\Model; use think\model\Pivot; use think\model\Relation; @@ -69,7 +69,7 @@ class BelongsToMany extends Relation if (false !== strpos($middle, '\\')) { $this->pivotName = $middle; - $this->middle = Container::classBaseName($middle); + $this->middle = class_basename($middle); } else { $this->middle = $middle; } @@ -327,7 +327,7 @@ class BelongsToMany extends Relation ], $relation, $subRelation, $closure); // 关联属性名 - $attr = Container::parseName($relation); + $attr = Str::snake($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -365,7 +365,7 @@ class BelongsToMany extends Relation $data[$pk] = []; } - $result->setRelation(Container::parseName($relation), $this->resultSetBuild($data[$pk], clone $this->parent)); + $result->setRelation(Str::snake($relation), $this->resultSetBuild($data[$pk], clone $this->parent)); } } diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index 868e601..5aad65f 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -14,8 +14,8 @@ namespace think\model\relation; use Closure; use think\Collection; -use think\Container; use think\db\BaseQuery as Query; +use think\helper\Str; use think\Model; use think\model\Relation; @@ -96,7 +96,7 @@ class HasMany extends Relation ], $relation, $subRelation, $closure); // 关联属性名 - $attr = Container::parseName($relation); + $attr = Str::snake($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -134,7 +134,7 @@ class HasMany extends Relation $data[$pk] = []; } - $result->setRelation(Container::parseName($relation), $this->resultSetBuild($data[$pk], clone $this->parent)); + $result->setRelation(Str::snake($relation), $this->resultSetBuild($data[$pk], clone $this->parent)); } } @@ -287,8 +287,8 @@ class HasMany extends Relation { $table = $this->query->getTable(); - $model = Container::classBaseName($this->parent); - $relation = Container::classBaseName($this->model); + $model = class_basename($this->parent); + $relation = class_basename($this->model); if ('*' != $id) { $id = $relation . '.' . (new $this->model)->getPk(); @@ -313,8 +313,8 @@ class HasMany extends Relation public function hasWhere($where = [], $fields = null, string $joinType = ''): Query { $table = $this->query->getTable(); - $model = Container::classBaseName($this->parent); - $relation = Container::classBaseName($this->model); + $model = class_basename($this->parent); + $relation = class_basename($this->model); if (is_array($where)) { $this->getQueryWhere($where, $relation); diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index f01801c..b02dfcf 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -13,8 +13,8 @@ namespace think\model\relation; use Closure; use think\Collection; -use think\Container; use think\db\BaseQuery as Query; +use think\helper\Str; use think\Model; use think\model\Relation; @@ -99,7 +99,7 @@ class HasManyThrough extends Relation */ public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query { - $model = Container::parseName(Container::classBaseName($this->parent)); + $model = Str::snake(class_basename($this->parent)); $throughTable = $this->through->getTable(); $pk = $this->throughPk; $throughKey = $this->throughKey; @@ -129,7 +129,7 @@ class HasManyThrough extends Relation */ public function hasWhere($where = [], $fields = null, $joinType = ''): Query { - $model = Container::parseName(Container::classBaseName($this->parent)); + $model = Str::snake(class_basename($this->parent)); $throughTable = $this->through->getTable(); $pk = $this->throughPk; $throughKey = $this->throughKey; @@ -185,7 +185,7 @@ class HasManyThrough extends Relation ], $foreignKey, $relation, $subRelation, $closure); // 关联属性名 - $attr = Container::parseName($relation); + $attr = Str::snake($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -226,7 +226,7 @@ class HasManyThrough extends Relation $data[$pk] = []; } - $result->setRelation(Container::parseName($relation), $this->resultSetBuild($data[$pk], clone $this->parent)); + $result->setRelation(Str::snake($relation), $this->resultSetBuild($data[$pk], clone $this->parent)); } /** @@ -291,7 +291,7 @@ class HasManyThrough extends Relation $closure($this, $name); } - $alias = Container::parseName(Container::classBaseName($this->model)); + $alias = Str::snake(class_basename($this->model)); $throughTable = $this->through->getTable(); $pk = $this->throughPk; $throughKey = $this->throughKey; @@ -324,7 +324,7 @@ class HasManyThrough extends Relation $closure($this, $name); } - $alias = Container::parseName(Container::classBaseName($this->model)); + $alias = Str::snake(class_basename($this->model)); $throughTable = $this->through->getTable(); $pk = $this->throughPk; $throughKey = $this->throughKey; @@ -351,7 +351,7 @@ class HasManyThrough extends Relation protected function baseQuery(): void { if (empty($this->baseQuery) && $this->parent->getData()) { - $alias = Container::parseName(Container::classBaseName($this->model)); + $alias = Str::snake(class_basename($this->model)); $throughTable = $this->through->getTable(); $pk = $this->throughPk; $throughKey = $this->throughKey; diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 15d141a..3897ba3 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -13,8 +13,8 @@ declare (strict_types = 1); namespace think\model\relation; use Closure; -use think\Container; use think\db\BaseQuery as Query; +use think\helper\Str; use think\Model; /** @@ -132,8 +132,8 @@ class HasOne extends OneToOne public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query { $table = $this->query->getTable(); - $model = Container::classBaseName($this->parent); - $relation = Container::classBaseName($this->model); + $model = class_basename($this->parent); + $relation = class_basename($this->model); $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -157,8 +157,8 @@ class HasOne extends OneToOne public function hasWhere($where = [], $fields = null, string $joinType = ''): Query { $table = $this->query->getTable(); - $model = Container::classBaseName($this->parent); - $relation = Container::classBaseName($this->model); + $model = class_basename($this->parent); + $relation = class_basename($this->model); if (is_array($where)) { $this->getQueryWhere($where, $relation); @@ -208,7 +208,7 @@ class HasOne extends OneToOne ], $foreignKey, $relation, $subRelation, $closure); // 关联属性名 - $attr = Container::parseName($relation); + $attr = Str::snake($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -265,7 +265,7 @@ class HasOne extends OneToOne // 绑定关联属性 $this->bindAttr($relationModel, $result); } else { - $result->setRelation(Container::parseName($relation), $relationModel); + $result->setRelation(Str::snake($relation), $relationModel); } } diff --git a/src/model/relation/HasOneThrough.php b/src/model/relation/HasOneThrough.php index 1311d67..fb4e587 100644 --- a/src/model/relation/HasOneThrough.php +++ b/src/model/relation/HasOneThrough.php @@ -12,7 +12,7 @@ namespace think\model\relation; use Closure; -use think\Container; +use think\helper\Str; use think\Model; /** @@ -75,7 +75,7 @@ class HasOneThrough extends HasManyThrough ], $foreignKey, $relation, $subRelation, $closure); // 关联属性名 - $attr = Container::parseName($relation); + $attr = Str::snake($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -123,7 +123,7 @@ class HasOneThrough extends HasManyThrough $relationModel->exists(true); } - $result->setRelation(Container::parseName($relation), $relationModel); + $result->setRelation(Str::snake($relation), $relationModel); } /** diff --git a/src/model/relation/MorphMany.php b/src/model/relation/MorphMany.php index 5d0e0f7..64f3a57 100644 --- a/src/model/relation/MorphMany.php +++ b/src/model/relation/MorphMany.php @@ -13,9 +13,9 @@ namespace think\model\relation; use Closure; use think\Collection; -use think\Container; use think\db\BaseQuery as Query; use think\db\exception\DbException as Exception; +use think\helper\Str; use think\Model; use think\model\Relation; @@ -144,7 +144,7 @@ class MorphMany extends Relation $data = $this->eagerlyMorphToMany($where, $relation, $subRelation, $closure); // 关联属性名 - $attr = Container::parseName($relation); + $attr = Str::snake($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -181,7 +181,7 @@ class MorphMany extends Relation $data[$key] = []; } - $result->setRelation(Container::parseName($relation), $this->resultSetBuild($data[$key], clone $this->parent)); + $result->setRelation(Str::snake($relation), $this->resultSetBuild($data[$key], clone $this->parent)); } } diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php index 54c53f5..fbc0b25 100644 --- a/src/model/relation/MorphOne.php +++ b/src/model/relation/MorphOne.php @@ -12,9 +12,9 @@ namespace think\model\relation; use Closure; -use think\Container; use think\db\BaseQuery as Query; use think\db\exception\DbException as Exception; +use think\helper\Str; use think\Model; use think\model\Relation; @@ -142,7 +142,7 @@ class MorphOne extends Relation ], $relation, $subRelation, $closure); // 关联属性名 - $attr = Container::parseName($relation); + $attr = Str::snake($relation); // 关联数据封装 foreach ($resultSet as $result) { @@ -187,7 +187,7 @@ class MorphOne extends Relation $relationModel = null; } - $result->setRelation(Container::parseName($relation), $relationModel); + $result->setRelation(Str::snake($relation), $relationModel); } } diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index ed6b18c..91824c1 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -12,8 +12,8 @@ namespace think\model\relation; use Closure; -use think\Container; use think\db\exception\DbException as Exception; +use think\helper\Str; use think\Model; use think\model\Relation; @@ -147,7 +147,7 @@ class MorphTo extends Relation if (false === strpos($model, '\\')) { $path = explode('\\', get_class($this->parent)); array_pop($path); - array_push($path, Container::parseName($model, 1)); + array_push($path, Str::studly($model)); $model = implode('\\', $path); } @@ -202,7 +202,7 @@ class MorphTo extends Relation if (!empty($range)) { // 关联属性名 - $attr = Container::parseName($relation); + $attr = Str::snake($relation); foreach ($range as $key => $val) { // 多态类型映射 @@ -284,7 +284,7 @@ class MorphTo extends Relation $data->exists(true); } - $result->setRelation(Container::parseName($relation), $data ?: null); + $result->setRelation(Str::snake($relation), $data ?: null); } /** diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index f3bbb1a..6990843 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -12,9 +12,9 @@ namespace think\model\relation; use Closure; -use think\Container; use think\db\BaseQuery as Query; use think\db\exception\DbException as Exception; +use think\helper\Str; use think\Model; use think\model\Relation; @@ -67,7 +67,7 @@ abstract class OneToOne extends Relation */ public function eagerly(Query $query, string $relation, $field = true, string $joinType = '', Closure $closure = null, bool $first = false): void { - $name = Container::parseName(Container::classBaseName($this->parent)); + $name = Str::snake(class_basename($this->parent)); if ($first) { $table = $query->getTable(); @@ -257,7 +257,7 @@ abstract class OneToOne extends Relation $relationModel = null; } - $result->setRelation(Container::parseName($relation), $relationModel); + $result->setRelation(Str::snake($relation), $relationModel); } /** -- Gitee From 4e019ee4ce58a937a4ebde59ef4df53fc7a5d3ec Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 11 Jul 2019 14:37:33 +0800 Subject: [PATCH 032/365] =?UTF-8?q?=E8=B0=83=E6=95=B4DbManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/DbManager.php b/src/DbManager.php index 1d62e30..558e419 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -75,7 +75,16 @@ class DbManager */ public function __construct() { - // 模型注入Db对象 + $this->modelMaker(); + } + + /** + * 注入模型对象 + * @access public + * @return void + */ + protected function modelMaker() + { Model::maker(function (Model $model) { $model->setDb($this); -- Gitee From 4c0f03671df019568ca5ae37b1f4d26969b23f12 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 11 Jul 2019 16:59:24 +0800 Subject: [PATCH 033/365] =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E4=BD=BF=E7=94=A8PSR-6=E5=92=8CPSR-16=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 5 +- src/DbManager.php | 6 +- src/db/BaseQuery.php | 2 +- src/db/CacheItem.php | 210 ++++++++++++++++++ src/db/Connection.php | 31 ++- src/db/PDOConnection.php | 22 +- src/db/exception/InvalidArgumentException.php | 22 ++ 7 files changed, 271 insertions(+), 27 deletions(-) create mode 100644 src/db/CacheItem.php create mode 100644 src/db/exception/InvalidArgumentException.php diff --git a/composer.json b/composer.json index 2247119..63585c3 100644 --- a/composer.json +++ b/composer.json @@ -14,11 +14,10 @@ ], "require": { "php": ">=7.1.0", + "psr/cache": "~1.0", + "psr/simple-cache": "^1.0", "topthink/think-helper":"^3.1" }, - "suggest": { - "topthink/think-cache": "Required to use query cache (^2.0)." - }, "autoload": { "psr-4": { "think\\": "src" diff --git a/src/DbManager.php b/src/DbManager.php index 558e419..2f90885 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -13,7 +13,7 @@ declare (strict_types = 1); namespace think; use InvalidArgumentException; -use think\CacheManager; +use Psr\SimpleCache\CacheInterface; use think\db\BaseQuery; use think\db\Connection; use think\db\Query; @@ -118,10 +118,10 @@ class DbManager /** * 设置缓存对象 * @access public - * @param CacheManager $cache 缓存对象 + * @param CacheInterface $cache 缓存对象 * @return void */ - public function setCache(CacheManager $cache): void + public function setCache(CacheInterface $cache): void { $this->cache = $cache; } diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 6c075c9..5515743 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -938,7 +938,7 @@ class BaseQuery */ public function cache($key = true, $expire = null, string $tag = null) { - if (false === $key) { + if (false === $key || !$this->getConnection()->getCache()) { return $this; } diff --git a/src/db/CacheItem.php b/src/db/CacheItem.php new file mode 100644 index 0000000..4479891 --- /dev/null +++ b/src/db/CacheItem.php @@ -0,0 +1,210 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db; + +use DateInterval; +use DateTime; +use DateTimeInterface; +use Psr\Cache\CacheItemInterface; +use think\db\exception\InvalidArgumentException; + +/** + * CacheItem实现类 + */ +class CacheItem implements CacheItemInterface +{ + /** + * 缓存Key + * @var string + */ + protected $key; + + /** + * 缓存内容 + * @var mixed + */ + protected $value; + + /** + * 过期时间 + * @var int|DateTimeInterface + */ + protected $expire; + + /** + * 缓存tag + * @var string + */ + protected $tag; + + /** + * 缓存是否命中 + * @var bool + */ + protected $isHit = false; + + public function __construct(string $key = null) + { + $this->key = $key; + } + + /** + * 为此缓存项设置「键」 + * @access public + * @param string $key + * @return $this + */ + public function setKey(string $key) + { + $this->key = $key; + return $this; + } + + /** + * 返回当前缓存项的「键」 + * @access public + * @return string + */ + public function getKey() + { + return $this->key; + } + + /** + * 返回当前缓存项的有效期 + * @access public + * @return DateTimeInterface|int|null + */ + public function getExpire() + { + if ($this->expire instanceof DateTimeInterface) { + return $this->expire; + } + + return $this->expire ? $this->expire - time() : null; + } + + /** + * 获取缓存Tag + * @access public + * @return string + */ + public function getTag() + { + return $this->tag; + } + + /** + * 凭借此缓存项的「键」从缓存系统里面取出缓存项 + * @access public + * @return mixed + */ + public function get() + { + return $this->value; + } + + /** + * 确认缓存项的检查是否命中 + * @access public + * @return bool + */ + public function isHit(): bool + { + return $this->isHit; + } + + /** + * 为此缓存项设置「值」 + * @access public + * @param mixed $value + * @return $this + */ + public function set($value) + { + $this->value = $value; + $this->isHit = true; + return $this; + } + + /** + * 为此缓存项设置所属标签 + * @access public + * @param string $tag + * @return $this + */ + public function tag(string $tag = null) + { + $this->tag = $tag; + return $this; + } + + /** + * 设置缓存项的有效期 + * @access public + * @param mixed $expire + * @return $this + */ + public function expire($expire) + { + if (is_null($expire)) { + $this->expire = null; + } elseif (is_numeric($expire) || $expire instanceof DateInterval) { + $this->expiresAfter($expire); + } elseif ($expire instanceof DateTimeInterface) { + $this->expire = $expire; + } else { + throw new InvalidArgumentException('not support datetime'); + } + + return $this; + } + + /** + * 设置缓存项的准确过期时间点 + * @access public + * @param DateTimeInterface $expiration + * @return $this + */ + public function expiresAt($expiration) + { + if ($expiration instanceof DateTimeInterface) { + $this->expire = $expiration; + } else { + throw new InvalidArgumentException('not support datetime'); + } + + return $this; + } + + /** + * 设置缓存项的过期时间 + * @access public + * @param int|DateInterval $timeInterval + * @return $this + * @throws InvalidArgumentException + */ + public function expiresAfter($timeInterval) + { + if ($timeInterval instanceof DateInterval) { + $this->expire = (int) DateTime::createFromFormat('U', (string) time())->add($timeInterval)->format('U'); + } elseif (is_numeric($timeInterval)) { + $this->expire = $timeInterval + time(); + } else { + throw new InvalidArgumentException('not support datetime'); + } + + return $this; + } + +} diff --git a/src/db/Connection.php b/src/db/Connection.php index e3764d8..d5ac804 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -12,9 +12,10 @@ declare (strict_types = 1); namespace think\db; -use think\CacheManager; -use think\cache\CacheItem; +use Psr\Cache\CacheItemInterface; +use Psr\SimpleCache\CacheInterface; use think\DbManager; +use think\db\CacheItem; use think\db\exception\DataNotFoundException; use think\db\exception\DbException as Exception; use think\db\exception\ModelNotFoundException; @@ -183,14 +184,24 @@ abstract class Connection /** * 设置当前的缓存对象 * @access public - * @param CacheManager $cache + * @param CacheInterface $cache * @return void */ - public function setCache(CacheManager $cache) + public function setCache(CacheInterface $cache) { $this->cache = $cache; } + /** + * 获取当前的缓存对象 + * @access public + * @return CacheInterface|null + */ + public function getCache() + { + return $this->cache; + } + /** * 获取数据库的配置参数 * @access public @@ -407,11 +418,11 @@ abstract class Connection /** * 缓存数据 * @access protected - * @param CacheItem $cacheItem 缓存Item + * @param CacheItemInterface $cacheItem 缓存Item */ - protected function cacheData(CacheItem $cacheItem) + protected function cacheData(CacheItemInterface $cacheItem) { - if ($cacheItem->getTag()) { + if (method_exists($cacheItem, 'getTag') && $cacheItem->getTag()) { $this->cache->tag($cacheItem->getTag()); } @@ -423,13 +434,13 @@ abstract class Connection * @access protected * @param BaseQuery $query 查询对象 * @param array $cache 缓存信息 - * @return CacheItem + * @return CacheItemInterface */ - protected function parseCache(BaseQuery $query, array $cache): CacheItem + protected function parseCache(BaseQuery $query, array $cache): CacheItemInterface { list($key, $expire, $tag) = $cache; - if ($key instanceof CacheItem) { + if ($key instanceof CacheItemInterface) { $cacheItem = $key; } else { if (true === $key) { diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 518cf45..90a8e3b 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -12,9 +12,11 @@ declare (strict_types = 1); namespace think\db; +use Closure; use PDO; use PDOStatement; -use think\cache\CacheItem; +use Psr\Cache\CacheItemInterface; +use think\db\CacheItem; use think\db\exception\BindParamException; use think\db\exception\DataNotFoundException; use think\db\exception\ModelNotFoundException; @@ -526,7 +528,7 @@ abstract class PDOConnection extends Connection // 分析查询表达式 $query->parseOptions(); - if ($query->getOptions('cache') && $this->cache) { + if ($query->getOptions('cache')) { // 检查查询缓存 $cacheItem = $this->parseCache($query, $query->getOptions('cache')); $resultSet = $this->cache->get($cacheItem->getKey()); @@ -536,7 +538,7 @@ abstract class PDOConnection extends Connection } } - if ($sql instanceof \Closure) { + if ($sql instanceof Closure) { $sql = $sql($query); $bind = $query->getBind(); } @@ -657,7 +659,7 @@ abstract class PDOConnection extends Connection $this->numRows = $this->PDOStatement->rowCount(); - if ($query->getOptions('cache') && $this->cache) { + if ($query->getOptions('cache')) { // 清理缓存数据 $cacheItem = $this->parseCache($query, $query->getOptions('cache')); $key = $cacheItem->getKey(); @@ -665,7 +667,7 @@ abstract class PDOConnection extends Connection if (isset($key) && $this->cache->get($key)) { $this->cache->delete($key); - } elseif (!empty($tag)) { + } elseif (!empty($tag) && method_exists($this->cache, 'tag')) { $this->cache->tag($tag)->clear(); } } @@ -927,7 +929,7 @@ abstract class PDOConnection extends Connection $query->setOption('field', (array) $field); - if (!empty($options['cache']) && $this->cache) { + if (!empty($options['cache'])) { $cacheItem = $this->parseCache($query, $options['cache']); $result = $this->cache->get($cacheItem->getKey()); @@ -1007,7 +1009,7 @@ abstract class PDOConnection extends Connection $query->setOption('field', $field); - if (!empty($options['cache']) && $this->cache) { + if (!empty($options['cache'])) { // 判断查询缓存 $cacheItem = $this->parseCache($query, $options['cache']); $result = $this->cache->get($cacheItem->getKey()); @@ -1620,13 +1622,13 @@ abstract class PDOConnection extends Connection * @access protected * @param BaseQuery $query 查询对象 * @param array $cache 缓存信息 - * @return CacheItem + * @return CacheItemInterface */ - protected function parseCache(BaseQuery $query, array $cache): CacheItem + protected function parseCache(BaseQuery $query, array $cache): CacheItemInterface { list($key, $expire, $tag) = $cache; - if ($key instanceof CacheItem) { + if ($key instanceof CacheItemInterface) { $cacheItem = $key; } else { if (true === $key) { diff --git a/src/db/exception/InvalidArgumentException.php b/src/db/exception/InvalidArgumentException.php new file mode 100644 index 0000000..9fcf2d3 --- /dev/null +++ b/src/db/exception/InvalidArgumentException.php @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); +namespace think\db\exception; + +use Psr\Cache\InvalidArgumentException as Psr6CacheInvalidArgumentInterface; +use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInvalidArgumentInterface; + +/** + * 非法数据异常 + */ +class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInvalidArgumentInterface, SimpleCacheInvalidArgumentInterface +{ +} -- Gitee From 6e05639fcfafab826d65b49e749e7e9ea4db1aac Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 11 Jul 2019 17:42:29 +0800 Subject: [PATCH 034/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E9=97=A8=E9=9D=A2?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 4 ++-- src/facade/Db.php | 57 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/Model.php b/src/Model.php index 42df736..fda11ea 100644 --- a/src/Model.php +++ b/src/Model.php @@ -147,10 +147,10 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab /** * 设置Db对象 * @access public - * @param Db $db Db对象 + * @param DbManager $db Db对象 * @return void */ - public function setDb(Db $db) + public function setDb(DbManager $db) { $this->db = $db; } diff --git a/src/facade/Db.php b/src/facade/Db.php index b0296c6..174a4f0 100644 --- a/src/facade/Db.php +++ b/src/facade/Db.php @@ -11,7 +11,62 @@ namespace think\facade; -use think\Facade; +if (class_exists('think\Facade')) { + class Facade extends \think\Facade + {} +} else { + class Facade + { + /** + * 始终创建新的对象实例 + * @var bool + */ + protected static $alwaysNewInstance; + + protected static $instance; + + /** + * 获取当前Facade对应类名 + * @access protected + * @return string + */ + protected static function getFacadeClass() + {} + + /** + * 创建Facade实例 + * @static + * @access protected + * @param bool $newInstance 是否每次创建新的实例 + * @return object + */ + protected static function createFacade(bool $newInstance = false) + { + $class = static::getFacadeClass() ?: 'think\DbManager'; + + if (static::$alwaysNewInstance) { + $newInstance = true; + } + + if ($newInstance) { + return new $class(); + } + + if (!self::$instance) { + self::$instance = new $class(); + } + + return self::$instance; + + } + + // 调用实际类的方法 + public static function __callStatic($method, $params) + { + return call_user_func_array([static::createFacade(), $method], $params); + } + } +} /** * @see \think\DbManager -- Gitee From 6e223698b29f1360c4e3fd2fec9a32b4b7694934 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 12 Jul 2019 08:10:52 +0800 Subject: [PATCH 035/365] =?UTF-8?q?readme=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2193776..3df901e 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Db类: ~~~php use think\facade\Db; // 数据库配置信息设置(全局有效) -Db::init([ +Db::setConfig([ // 默认数据连接标识 'default' => 'mysql', // 数据库连接信息 -- Gitee From 514c911d3d586690c829db212e6fed37ab6b6ffd Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 12 Jul 2019 08:41:54 +0800 Subject: [PATCH 036/365] =?UTF-8?q?=E5=8F=96=E6=B6=88PSR-6=E8=A7=84?= =?UTF-8?q?=E8=8C=83=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 1 - src/db/Connection.php | 15 +++++++-------- src/db/PDOConnection.php | 7 +++---- src/db/connector/Mongo.php | 6 +++--- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/composer.json b/composer.json index 63585c3..82ef85d 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,6 @@ ], "require": { "php": ">=7.1.0", - "psr/cache": "~1.0", "psr/simple-cache": "^1.0", "topthink/think-helper":"^3.1" }, diff --git a/src/db/Connection.php b/src/db/Connection.php index d5ac804..4aa384e 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -12,7 +12,6 @@ declare (strict_types = 1); namespace think\db; -use Psr\Cache\CacheItemInterface; use Psr\SimpleCache\CacheInterface; use think\DbManager; use think\db\CacheItem; @@ -418,11 +417,11 @@ abstract class Connection /** * 缓存数据 * @access protected - * @param CacheItemInterface $cacheItem 缓存Item + * @param CacheItem $cacheItem 缓存Item */ - protected function cacheData(CacheItemInterface $cacheItem) + protected function cacheData(CacheItem $cacheItem) { - if (method_exists($cacheItem, 'getTag') && $cacheItem->getTag()) { + if ($cacheItem->getTag() && method_exists($this->cache, 'tag')) { $this->cache->tag($cacheItem->getTag()); } @@ -434,13 +433,13 @@ abstract class Connection * @access protected * @param BaseQuery $query 查询对象 * @param array $cache 缓存信息 - * @return CacheItemInterface + * @return CacheItem */ - protected function parseCache(BaseQuery $query, array $cache): CacheItemInterface + protected function parseCache(BaseQuery $query, array $cache): CacheItem { list($key, $expire, $tag) = $cache; - if ($key instanceof CacheItemInterface) { + if ($key instanceof CacheItem) { $cacheItem = $key; } else { if (true === $key) { @@ -471,7 +470,7 @@ abstract class Connection */ public function lazyWrite(string $type, string $guid, float $step, int $lazyTime) { - if (!$this->cache) { + if (!$this->cache || !method_exists($this->cache, $type)) { return $step; } diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 90a8e3b..1ede8ae 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -15,7 +15,6 @@ namespace think\db; use Closure; use PDO; use PDOStatement; -use Psr\Cache\CacheItemInterface; use think\db\CacheItem; use think\db\exception\BindParamException; use think\db\exception\DataNotFoundException; @@ -1622,13 +1621,13 @@ abstract class PDOConnection extends Connection * @access protected * @param BaseQuery $query 查询对象 * @param array $cache 缓存信息 - * @return CacheItemInterface + * @return CacheItem */ - protected function parseCache(BaseQuery $query, array $cache): CacheItemInterface + protected function parseCache(BaseQuery $query, array $cache): CacheItem { list($key, $expire, $tag) = $cache; - if ($key instanceof CacheItemInterface) { + if ($key instanceof CacheItem) { $cacheItem = $key; } else { if (true === $key) { diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 4826e33..e92d010 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -269,7 +269,7 @@ class Mongo extends Connection { $options = $query->parseOptions(); - if ($query->getOptions('cache') && $this->cache) { + if ($query->getOptions('cache')) { // 检查查询缓存 $cacheItem = $this->parseCache($query, $query->getOptions('cache')); $resultSet = $this->cache->get($cacheItem->getKey()); @@ -335,7 +335,7 @@ class Mongo extends Connection $this->numRows = $writeResult->getMatchedCount(); - if ($query->getOptions('cache') && $this->cache) { + if ($query->getOptions('cache')) { // 清理缓存数据 $cacheItem = $this->parseCache($query, $query->getOptions('cache')); $key = $cacheItem->getKey(); @@ -343,7 +343,7 @@ class Mongo extends Connection if (isset($key) && $this->cache->get($key)) { $this->cache->delete($key); - } elseif (!empty($tag)) { + } elseif (!empty($tag) && method_exists($this->cache, 'tag')) { $this->cache->tag($tag)->clear(); } } -- Gitee From fab302a977ef04162488ad2fbba49057c136c38b Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 12 Jul 2019 11:34:10 +0800 Subject: [PATCH 037/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=97=A5=E5=BF=97PSR?= =?UTF-8?q?-3=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 3 +- src/DbManager.php | 70 ++++++++++++++++++++---------- src/Model.php | 2 +- src/db/CacheItem.php | 3 +- src/db/Connection.php | 23 +++++++--- src/db/PDOConnection.php | 28 +++--------- src/model/Relation.php | 2 +- src/model/concern/RelationShip.php | 2 +- src/model/concern/SoftDelete.php | 2 +- 9 files changed, 78 insertions(+), 57 deletions(-) diff --git a/composer.json b/composer.json index 82ef85d..5a5115a 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ ], "require": { "php": ">=7.1.0", - "psr/simple-cache": "^1.0", + "psr/simple-cache": "^1.0", + "psr/log": "~1.0", "topthink/think-helper":"^3.1" }, "autoload": { diff --git a/src/DbManager.php b/src/DbManager.php index 2f90885..1d43c66 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -13,6 +13,7 @@ declare (strict_types = 1); namespace think; use InvalidArgumentException; +use Psr\Log\LoggerInterface; use Psr\SimpleCache\CacheInterface; use think\db\BaseQuery; use think\db\Connection; @@ -55,7 +56,7 @@ class DbManager * SQL日志 * @var array */ - protected $log = []; + protected $dbLog = []; /** * 查询次数 @@ -65,10 +66,16 @@ class DbManager /** * 查询缓存对象 - * @var CacheManager + * @var CacheInterface */ protected $cache; + /** + * 查询日志对象 + * @var LoggerInterface + */ + protected $log; + /** * 架构函数 * @access public @@ -126,6 +133,43 @@ class DbManager $this->cache = $cache; } + /** + * 设置日志对象 + * @access public + * @param LoggerInterface $log 日志对象 + * @return void + */ + public function setLog(LoggerInterface $log): void + { + $this->log = $log; + } + + /** + * 记录SQL日志 + * @access protected + * @param string $log SQL日志信息 + * @param string $type 日志类型 + * @return void + */ + public function log($log, $type = 'sql') + { + if ($this->log) { + $this->log->log($type, $log); + } else { + $this->dbLog[$type][] = $log; + } + } + + /** + * 获得查询日志(没有设置日志对象使用) + * @access public + * @return array + */ + public function getDbLog(): array + { + return $this->dbLog; + } + /** * 获取配置参数 * @access public @@ -241,28 +285,6 @@ class DbManager return $this->queryTimes; } - /** - * 记录SQL日志 - * @access protected - * @param string $log SQL日志信息 - * @param string $type 日志类型 - * @return void - */ - public function log($log, $type = 'sql') - { - $this->log[$type][] = $log; - } - - /** - * 获得查询日志 - * @access public - * @return array - */ - public function getLog() - { - return $this->log; - } - /** * 监听SQL执行 * @access public diff --git a/src/Model.php b/src/Model.php index fda11ea..0c6d136 100644 --- a/src/Model.php +++ b/src/Model.php @@ -17,7 +17,7 @@ use Closure; use JsonSerializable; use think\contract\Arrayable; use think\contract\Jsonable; -use think\db\Query; +use think\db\BaseQuery as Query; /** * Class Model diff --git a/src/db/CacheItem.php b/src/db/CacheItem.php index 4479891..6e82523 100644 --- a/src/db/CacheItem.php +++ b/src/db/CacheItem.php @@ -15,13 +15,12 @@ namespace think\db; use DateInterval; use DateTime; use DateTimeInterface; -use Psr\Cache\CacheItemInterface; use think\db\exception\InvalidArgumentException; /** * CacheItem实现类 */ -class CacheItem implements CacheItemInterface +class CacheItem { /** * 缓存Key diff --git a/src/db/Connection.php b/src/db/Connection.php index 4aa384e..5075811 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -428,6 +428,23 @@ abstract class Connection $this->cache->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire()); } + /** + * 分析缓存Key + * @access protected + * @param BaseQuery $query 查询对象 + * @return string + */ + protected function getCacheKey(BaseQuery $query): string + { + if (!empty($query->getOptions('key'))) { + $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); + } else { + $key = md5($this->getConfig('database') . serialize($query->getOptions())); + } + + return $key; + } + /** * 分析缓存 * @access protected @@ -443,11 +460,7 @@ abstract class Connection $cacheItem = $key; } else { if (true === $key) { - if (!empty($query->getOptions('key'))) { - $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); - } else { - $key = md5($this->getConfig('database') . serialize($query->getOptions())); - } + $key = $this->getCacheKey($query); } $cacheItem = new CacheItem($key); diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 1ede8ae..34d647d 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -15,7 +15,6 @@ namespace think\db; use Closure; use PDO; use PDOStatement; -use think\db\CacheItem; use think\db\exception\BindParamException; use think\db\exception\DataNotFoundException; use think\db\exception\ModelNotFoundException; @@ -1617,33 +1616,20 @@ abstract class PDOConnection extends Connection } /** - * 分析缓存 + * 分析缓存Key * @access protected * @param BaseQuery $query 查询对象 - * @param array $cache 缓存信息 - * @return CacheItem + * @return string */ - protected function parseCache(BaseQuery $query, array $cache): CacheItem + protected function getCacheKey(BaseQuery $query): string { - list($key, $expire, $tag) = $cache; - - if ($key instanceof CacheItem) { - $cacheItem = $key; + if (!empty($query->getOptions('key'))) { + $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); } else { - if (true === $key) { - if (!empty($query->getOptions('key'))) { - $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); - } else { - $key = md5($this->getConfig('database') . serialize($query->getOptions()) . serialize($query->getBind(false))); - } - } - - $cacheItem = new CacheItem($key); - $cacheItem->expire($expire); - $cacheItem->tag($tag); + $key = md5($this->getConfig('database') . serialize($query->getOptions()) . serialize($query->getBind(false))); } - return $cacheItem; + return $key; } } diff --git a/src/model/Relation.php b/src/model/Relation.php index 403dbbc..fd813ad 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -12,8 +12,8 @@ declare (strict_types = 1); namespace think\model; +use think\db\BaseQuery as Query; use think\db\exception\DbException as Exception; -use think\db\Query; use think\Model; /** diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 8e18b68..3072864 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -14,8 +14,8 @@ namespace think\model\concern; use Closure; use think\Collection; +use think\db\BaseQuery as Query; use think\db\exception\DbException as Exception; -use think\db\Query; use think\helper\Str; use think\Model; use think\model\Relation; diff --git a/src/model/concern/SoftDelete.php b/src/model/concern/SoftDelete.php index 1c7e432..7357bc5 100644 --- a/src/model/concern/SoftDelete.php +++ b/src/model/concern/SoftDelete.php @@ -12,7 +12,7 @@ declare (strict_types = 1); namespace think\model\concern; -use think\db\Query; +use think\db\BaseQuery as Query; /** * 数据软删除 -- Gitee From d7880e2b36bb0069bbdbdbbd61d95f06cb725c07 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 12 Jul 2019 16:03:15 +0800 Subject: [PATCH 038/365] =?UTF-8?q?=E5=BA=9F=E9=99=A4explain=E5=88=86?= =?UTF-8?q?=E6=9E=90=20=E6=94=B9=E8=BF=9B=E6=9F=A5=E8=AF=A2=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E5=88=A4=E6=96=AD=20=E4=BF=AE=E6=AD=A3MongoDb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 39 +++++++++--------------- src/db/connector/Mongo.php | 61 ++++++++++++++++--------------------- src/db/connector/Mysql.php | 21 ------------- src/db/connector/Oracle.php | 11 ------- src/db/connector/Pgsql.php | 11 ------- src/db/connector/Sqlite.php | 11 ------- src/db/connector/Sqlsrv.php | 10 ------ 7 files changed, 40 insertions(+), 124 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 34d647d..eb2a0fe 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -529,10 +529,10 @@ abstract class PDOConnection extends Connection if ($query->getOptions('cache')) { // 检查查询缓存 $cacheItem = $this->parseCache($query, $query->getOptions('cache')); - $resultSet = $this->cache->get($cacheItem->getKey()); + $key = $cacheItem->getKey(); - if (false !== $resultSet) { - return $resultSet; + if ($this->cache->has($key)) { + return $this->cache->get($key); } } @@ -663,7 +663,7 @@ abstract class PDOConnection extends Connection $key = $cacheItem->getKey(); $tag = $cacheItem->getTag(); - if (isset($key) && $this->cache->get($key)) { + if (isset($key) && $this->cache->has($key)) { $this->cache->delete($key); } elseif (!empty($tag) && method_exists($this->cache, 'tag')) { $this->cache->tag($tag)->clear(); @@ -929,10 +929,10 @@ abstract class PDOConnection extends Connection if (!empty($options['cache'])) { $cacheItem = $this->parseCache($query, $options['cache']); - $result = $this->cache->get($cacheItem->getKey()); + $key = $cacheItem->getKey(); - if (false !== $result) { - return $result; + if ($this->cache->has($key)) { + return $this->cache->get($key); } } @@ -950,7 +950,7 @@ abstract class PDOConnection extends Connection $result = $pdo->fetchColumn(); - if (isset($cacheItem) && false !== $result) { + if (isset($cacheItem)) { // 缓存数据 $cacheItem->set($result); $this->cacheData($cacheItem); @@ -1010,10 +1010,10 @@ abstract class PDOConnection extends Connection if (!empty($options['cache'])) { // 判断查询缓存 $cacheItem = $this->parseCache($query, $options['cache']); - $result = $this->cache->get($cacheItem->getKey()); + $key = $cacheItem->getKey(); - if (false !== $result) { - return $result; + if ($this->cache->has($key)) { + return $this->cache->get($key); } } @@ -1486,15 +1486,9 @@ abstract class PDOConnection extends Connection // 记录操作结束时间 $runtime = number_format((microtime(true) - $this->queryStartTime), 6); $sql = $sql ?: $this->getLastsql(); - $result = []; - - // SQL性能分析 - if ($this->config['sql_explain'] && 0 === stripos(trim($sql), 'select')) { - $result = $this->getExplain($sql); - } // SQL监听 - $this->triggerSql($sql, $runtime, $result, $master); + $this->triggerSql($sql, $runtime, $master); } } } @@ -1504,17 +1498,16 @@ abstract class PDOConnection extends Connection * @access protected * @param string $sql SQL语句 * @param string $runtime SQL运行时间 - * @param mixed $explain SQL分析 * @param bool $master 主从标记 * @return void */ - protected function triggerSql(string $sql, string $runtime, array $explain = [], bool $master = false): void + protected function triggerSql(string $sql, string $runtime, bool $master = false): void { $listen = $this->db->getListen(); if (!empty($listen)) { foreach ($listen as $callback) { if (is_callable($callback)) { - $callback($sql, $runtime, $explain, $master); + $callback($sql, $runtime, $master); } } } else { @@ -1527,10 +1520,6 @@ abstract class PDOConnection extends Connection // 未注册监听则记录到日志中 $this->log($sql . ' [ ' . $master . 'RunTime:' . $runtime . 's ]'); - - if (!empty($explain)) { - $this->log('[ EXPLAIN : ' . var_export($explain, true) . ' ]'); - } } } diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index e92d010..969b72a 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -89,8 +89,6 @@ class Mongo extends Connection 'pk_convert_id' => false, // typeMap 'type_map' => ['root' => 'array', 'document' => 'array'], - // Query对象 - 'query' => '\\think\\db\\Mongo', ]; /** @@ -213,8 +211,7 @@ class Mongo extends Connection * 执行查询并返回Cursor对象 * @access public * @param BaseQuery $query 查询对象 - * @param MongoQuery $mongoQuery Mongo查询对象 - * @param ReadPreference $readPreference readPreference + * @param MongoQuery|Closure $mongoQuery Mongo查询对象 * @return Cursor * @throws AuthenticationException * @throws InvalidArgumentException @@ -256,9 +253,7 @@ class Mongo extends Connection * 执行查询 * @access public * @param BaseQuery $query 查询对象 - * @param MongoQuery $mongoQuery Mongo查询对象 - * @param ReadPreference $readPreference readPreference - * @param string|array $typeMap 指定返回的typeMap + * @param MongoQuery|Closure $mongoQuery Mongo查询对象 * @return array * @throws AuthenticationException * @throws InvalidArgumentException @@ -272,10 +267,10 @@ class Mongo extends Connection if ($query->getOptions('cache')) { // 检查查询缓存 $cacheItem = $this->parseCache($query, $query->getOptions('cache')); - $resultSet = $this->cache->get($cacheItem->getKey()); + $key = $cacheItem->getKey(); - if (false !== $resultSet) { - return $resultSet; + if ($this->cache->has($key)) { + return $this->cache->get($key); } } @@ -299,8 +294,8 @@ class Mongo extends Connection /** * 执行写操作 * @access public - * @param BaseQuery $query - * @param BulkWrite $bulk + * @param BaseQuery $query + * @param BulkWrite $bulk * * @return WriteResult * @throws AuthenticationException @@ -341,7 +336,7 @@ class Mongo extends Connection $key = $cacheItem->getKey(); $tag = $cacheItem->getTag(); - if (isset($key) && $this->cache->get($key)) { + if (isset($key) && $this->cache->has($key)) { $this->cache->delete($key); } elseif (!empty($tag) && method_exists($this->cache, 'tag')) { $this->cache->tag($tag)->clear(); @@ -499,18 +494,17 @@ class Mongo extends Connection * @access protected * @param string $sql SQL语句 * @param string $runtime SQL运行时间 - * @param mixed $options 参数 * @param bool $master 主从标记 * @return void */ - protected function triggerSql(string $sql, string $runtime, array $options = [], bool $master = false): void + protected function triggerSql(string $sql, string $runtime, bool $master = false): void { $listen = $this->db->getListen(); if (!empty($listen)) { foreach ($listen as $callback) { if (is_callable($callback)) { - $callback($sql, $runtime, $options, $master); + $callback($sql, $runtime, $master); } } } else { @@ -546,7 +540,7 @@ class Mongo extends Connection $sql = $sql ?: $this->queryStr; // SQL监听 - $this->triggerSql($sql, $runtime, $this->options, $master); + $this->triggerSql($sql, $runtime, $master); } } } @@ -693,7 +687,7 @@ class Mongo extends Connection /** * 插入记录 * @access public - * @param BaseQuery $query 查询对象 + * @param BaseQuery $query 查询对象 * @param boolean $getLastInsID 返回自增主键 * @return mixed * @throws AuthenticationException @@ -764,7 +758,7 @@ class Mongo extends Connection * 批量插入记录 * @access public * @param BaseQuery $query 查询对象 - * @param array $dataSet 数据集 + * @param array $dataSet 数据集 * @return integer * @throws AuthenticationException * @throws InvalidArgumentException @@ -792,7 +786,7 @@ class Mongo extends Connection /** * 更新记录 * @access public - * @param BaseQuery $query 查询对象 + * @param BaseQuery $query 查询对象 * @return int * @throws Exception * @throws AuthenticationException @@ -822,7 +816,7 @@ class Mongo extends Connection /** * 删除记录 * @access public - * @param BaseQuery $query 查询对象 + * @param BaseQuery $query 查询对象 * @return int * @throws Exception * @throws AuthenticationException @@ -924,10 +918,10 @@ class Mongo extends Connection if (!empty($options['cache'])) { $cacheItem = $this->parseCache($query, $options['cache']); - $result = $this->getCacheData($cacheItem); + $key = $cacheItem->getKey(); - if (false !== $result) { - return $result; + if ($this->cache->has($key)) { + return $this->cache->get($key); } } @@ -940,12 +934,10 @@ class Mongo extends Connection } // 执行查询操作 - $readPreference = $options['readPreference'] ?? null; - $resultSet = $this->query($options['table'], $mongoQuery, $readPreference); + $resultSet = $this->mongoQuery($query, $mongoQuery); if (!empty($resultSet)) { - $data = array_shift($resultSet); - + $data = array_shift($resultSet); $result = $data[$field]; } else { $result = false; @@ -981,15 +973,15 @@ class Mongo extends Connection $projection = $field; } - $query->setOption('projection', $projection); + $query->field($projection); if (!empty($options['cache'])) { // 判断查询缓存 $cacheItem = $this->parseCache($query, $options['cache']); - $result = $this->getCacheData($cacheItem); + $key = $cacheItem->getKey(); - if (false !== $result) { - return $result; + if ($this->cache->has($key)) { + return $this->cache->get($key); } } @@ -1002,8 +994,7 @@ class Mongo extends Connection } // 执行查询操作 - $readPreference = $options['readPreference'] ?? null; - $resultSet = $this->query($options['table'], $mongoQuery, $readPreference); + $resultSet = $this->mongoQuery($query, $mongoQuery); if (('*' == $field || strpos($field, ',')) && $key) { $result = array_column($resultSet, null, $key); @@ -1025,7 +1016,7 @@ class Mongo extends Connection /** * 执行command * @access public - * @param BaseQuery $query 查询对象 + * @param BaseQuery $query 查询对象 * @param string|array|object $command 指令 * @param mixed $extra 额外参数 * @param string $db 数据库名 diff --git a/src/db/connector/Mysql.php b/src/db/connector/Mysql.php index 8c7a962..daf670b 100644 --- a/src/db/connector/Mysql.php +++ b/src/db/connector/Mysql.php @@ -157,27 +157,6 @@ class Mysql extends PDOConnection return $info; } - /** - * SQL性能分析 - * @access protected - * @param string $sql - * @return array - */ - protected function getExplain(string $sql): array - { - $pdo = $this->linkID->query("EXPLAIN " . $sql); - $result = $pdo->fetch(PDO::FETCH_ASSOC); - $result = array_change_key_case($result); - - if (isset($result['extra'])) { - if (strpos($result['extra'], 'filesort') || strpos($result['extra'], 'temporary')) { - $this->log('SQL:' . $this->queryStr . '[' . $result['extra'] . ']', 'warn'); - } - } - - return $result; - } - protected function supportSavepoint(): bool { return true; diff --git a/src/db/connector/Oracle.php b/src/db/connector/Oracle.php index 7266e11..a89f3ed 100644 --- a/src/db/connector/Oracle.php +++ b/src/db/connector/Oracle.php @@ -107,17 +107,6 @@ class Oracle extends Connection return $result; } - /** - * SQL性能分析 - * @access protected - * @param string $sql - * @return array - */ - protected function getExplain(string $sql): array - { - return []; - } - protected function supportSavepoint(): bool { return true; diff --git a/src/db/connector/Pgsql.php b/src/db/connector/Pgsql.php index 0886d87..1310b97 100644 --- a/src/db/connector/Pgsql.php +++ b/src/db/connector/Pgsql.php @@ -101,17 +101,6 @@ class Pgsql extends PDOConnection return $info; } - /** - * SQL性能分析 - * @access protected - * @param string $sql - * @return array - */ - protected function getExplain(string $sql): array - { - return []; - } - protected function supportSavepoint(): bool { return true; diff --git a/src/db/connector/Sqlite.php b/src/db/connector/Sqlite.php index 513438a..12a0517 100644 --- a/src/db/connector/Sqlite.php +++ b/src/db/connector/Sqlite.php @@ -89,17 +89,6 @@ class Sqlite extends PDOConnection return $info; } - /** - * SQL性能分析 - * @access protected - * @param string $sql - * @return array - */ - protected function getExplain(string $sql): array - { - return []; - } - protected function supportSavepoint(): bool { return true; diff --git a/src/db/connector/Sqlsrv.php b/src/db/connector/Sqlsrv.php index cae6dec..d220bcd 100644 --- a/src/db/connector/Sqlsrv.php +++ b/src/db/connector/Sqlsrv.php @@ -127,14 +127,4 @@ class Sqlsrv extends PDOConnection return $info; } - /** - * SQL性能分析 - * @access protected - * @param string $sql - * @return array - */ - protected function getExplain(string $sql): array - { - return []; - } } -- Gitee From ed89fe2ed5db6975481a3fdce53dd5c39aeaee5b Mon Sep 17 00:00:00 2001 From: 4352570 <51403503+4352570@users.noreply.github.com> Date: Fri, 12 Jul 2019 16:35:07 +0800 Subject: [PATCH 039/365] =?UTF-8?q?=E6=8A=A5=E9=94=99=E4=BA=86=EF=BC=8C?= =?UTF-8?q?=E8=BF=99=E9=87=8C=E6=B2=A1=E6=9C=89=E5=88=A0=E9=99=A4getExplai?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 这里没有删除getExplain --- src/db/PDOConnection.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index eb2a0fe..c4f9a6e 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -213,14 +213,6 @@ abstract class PDOConnection extends Connection */ abstract public function getTables(string $dbName); - /** - * SQL性能分析 - * @access protected - * @param string $sql SQL语句 - * @return array - */ - abstract protected function getExplain(string $sql); - /** * 对返数据表字段信息进行大小写转换出来 * @access public -- Gitee From a27dc4794fe681bf74e425b407ae951d8d7ae28b Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 14 Jul 2019 10:35:15 +0800 Subject: [PATCH 040/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index 1d43c66..2d02788 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -99,14 +99,14 @@ class DbManager if (is_null($isAutoWriteTimestamp)) { // 自动写入时间戳 - $model->isAutoWriteTimestamp($this->config['auto_timestamp'] ?? true); + $model->isAutoWriteTimestamp($this->getConfig('auto_timestamp', true)); } $dateFormat = $model->getDateFormat(); if (is_null($dateFormat)) { // 设置时间戳格式 - $model->setDateFormat($this->config['datetime_format'] ?? 'Y-m-d H:i:s'); + $model->setDateFormat($this->getConfig('datetime_format', 'Y-m-d H:i:s')); } }); } @@ -117,7 +117,7 @@ class DbManager * @param array $config 连接配置 * @return void */ - public function setConfig(array $config = []): void + public function setConfig($config = []): void { $this->config = $config; } @@ -174,15 +174,16 @@ class DbManager * 获取配置参数 * @access public * @param string $config 配置参数 + * @param mixed $default 默认值 * @return mixed */ - public function getConfig($config = '') + public function getConfig(string $name = '', $default = null) { if ('' === $config) { return $this->config; } - return $this->config[$config] ?? null; + return $this->config[$name] ?? $default; } /** @@ -204,8 +205,9 @@ class DbManager $class = $connection->getQueryClass(); $query = new $class($connection); - if (!empty($this->config['time_query_rule'])) { - $query->timeRule($this->config['time_query_rule']); + $timeRule = $this->getConfig('time_query_rule'); + if (!empty($timeRule)) { + $query->timeRule($timeRule); } return $query; @@ -221,15 +223,16 @@ class DbManager protected function instance(string $name = null, bool $force = false): Connection { if (empty($name)) { - $name = $this->config['default'] ?? 'mysql'; + $name = $this->getConfig('default', 'mysql'); } if ($force || !isset($this->instance[$name])) { - if (!isset($this->config['connections'][$name])) { + $connections = $this->getConfig('connections'); + if (!isset($connections[$name])) { throw new InvalidArgumentException('Undefined db config:' . $name); } - $config = $this->config['connections'][$name]; + $config = $connections[$name]; $type = !empty($config['type']) ? $config['type'] : 'mysql'; if (strpos($type, '\\')) { -- Gitee From 9afeffe4dab6e389d8b2b1d2d3fdc3a5404242a8 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 15 Jul 2019 15:33:49 +0800 Subject: [PATCH 041/365] =?UTF-8?q?=E5=8E=BB=E9=99=A4PSR-6=E7=9A=84?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 2 +- src/db/exception/InvalidArgumentException.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index 2d02788..dff67c2 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -117,7 +117,7 @@ class DbManager * @param array $config 连接配置 * @return void */ - public function setConfig($config = []): void + public function setConfig($config): void { $this->config = $config; } diff --git a/src/db/exception/InvalidArgumentException.php b/src/db/exception/InvalidArgumentException.php index 9fcf2d3..047e45e 100644 --- a/src/db/exception/InvalidArgumentException.php +++ b/src/db/exception/InvalidArgumentException.php @@ -11,12 +11,11 @@ declare (strict_types = 1); namespace think\db\exception; -use Psr\Cache\InvalidArgumentException as Psr6CacheInvalidArgumentInterface; use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInvalidArgumentInterface; /** * 非法数据异常 */ -class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInvalidArgumentInterface, SimpleCacheInvalidArgumentInterface +class InvalidArgumentException extends \InvalidArgumentException implements SimpleCacheInvalidArgumentInterface { } -- Gitee From 5f06deac9ca4b870e7db1ba30ea64993060f5270 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 15 Jul 2019 16:49:04 +0800 Subject: [PATCH 042/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/ModelEvent.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/model/concern/ModelEvent.php b/src/model/concern/ModelEvent.php index 0f89b76..eea002e 100644 --- a/src/model/concern/ModelEvent.php +++ b/src/model/concern/ModelEvent.php @@ -21,12 +21,29 @@ use think\helper\Str; trait ModelEvent { + /** + * Event对象 + * @var object + */ + protected $event; + /** * 是否需要事件响应 * @var bool */ protected $withEvent = true; + /** + * 设置Event对象 + * @access public + * @param object $event Event对象 + * @return void + */ + public function setEvent($event) + { + $this->event = $event; + } + /** * 当前操作的事件响应 * @access protected @@ -56,6 +73,9 @@ trait ModelEvent try { if (method_exists(static::class, $call)) { $result = call_user_func([static::class, $call], $this); + } elseif (is_object($this->event) && method_exists($this->event, 'trigger')) { + $result = $this->event->trigger(static::class . '.' . $event, $this); + $result = empty($result) ? true : end($result); } else { $result = true; } -- Gitee From 6cf12a91554fa30d24e9e6e2d0978c8bf7095242 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 15 Jul 2019 16:54:26 +0800 Subject: [PATCH 043/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BDbManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/DbManager.php b/src/DbManager.php index dff67c2..04f0538 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -95,6 +95,10 @@ class DbManager Model::maker(function (Model $model) { $model->setDb($this); + if (is_object($this->event)) { + $model->setEvent($this->event); + } + $isAutoWriteTimestamp = $model->getAutoWriteTimestamp(); if (is_null($isAutoWriteTimestamp)) { -- Gitee From 01c226d32c86391a348a40b78000280a3e71dead Mon Sep 17 00:00:00 2001 From: 4352570 <51403503+4352570@users.noreply.github.com> Date: Tue, 16 Jul 2019 11:05:53 +0800 Subject: [PATCH 044/365] =?UTF-8?q?getConfig=20=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DbManager.php b/src/DbManager.php index 04f0538..d7a5826 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -183,7 +183,7 @@ class DbManager */ public function getConfig(string $name = '', $default = null) { - if ('' === $config) { + if ('' === $name) { return $this->config; } -- Gitee From 4ff6a161f46fde7ecd78343731bb08099541cb69 Mon Sep 17 00:00:00 2001 From: 4352570 <51403503+4352570@users.noreply.github.com> Date: Tue, 16 Jul 2019 11:09:52 +0800 Subject: [PATCH 045/365] Update DbManager.php --- src/DbManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DbManager.php b/src/DbManager.php index d7a5826..cd375a4 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -177,7 +177,7 @@ class DbManager /** * 获取配置参数 * @access public - * @param string $config 配置参数 + * @param string $name 配置参数 * @param mixed $default 默认值 * @return mixed */ -- Gitee From 268f24635aabecadde6dda0f9ca3ea7229988019 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 16 Jul 2019 11:18:06 +0800 Subject: [PATCH 046/365] =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index cd375a4..22aba38 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -41,10 +41,10 @@ class DbManager protected $config = []; /** - * Event - * @var array + * Event对象或者数组 + * @var array|object */ - protected $event = []; + protected $event; /** * SQL监听 -- Gitee From 6ade3e1b941386a5f798cd197d33a10d293f2ec2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 16 Jul 2019 11:36:46 +0800 Subject: [PATCH 047/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E7=9A=84=E7=B2=BE=E5=87=86=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/TimeFieldQuery.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/db/concern/TimeFieldQuery.php b/src/db/concern/TimeFieldQuery.php index 59e9b37..1267e54 100644 --- a/src/db/concern/TimeFieldQuery.php +++ b/src/db/concern/TimeFieldQuery.php @@ -22,14 +22,14 @@ trait TimeFieldQuery * @var array */ protected $timeRule = [ - 'today' => ['today', 'tomorrow'], - 'yesterday' => ['yesterday', 'today'], - 'week' => ['this week 00:00:00', 'next week 00:00:00'], - 'last week' => ['last week 00:00:00', 'this week 00:00:00'], - 'month' => ['first Day of this month 00:00:00', 'first Day of next month 00:00:00'], - 'last month' => ['first Day of last month 00:00:00', 'first Day of this month 00:00:00'], - 'year' => ['this year 1/1', 'next year 1/1'], - 'last year' => ['last year 1/1', 'this year 1/1'], + 'today' => ['today', 'tomorrow -1second'], + 'yesterday' => ['yesterday', 'today -1second'], + 'week' => ['this week 00:00:00', 'next week 00:00:00 -1second'], + 'last week' => ['last week 00:00:00', 'this week 00:00:00 -1second'], + 'month' => ['first Day of this month 00:00:00', 'first Day of next month 00:00:00 -1second'], + 'last month' => ['first Day of last month 00:00:00', 'first Day of this month 00:00:00 -1second'], + 'year' => ['this year 1/1', 'next year 1/1 -1second'], + 'last year' => ['last year 1/1', 'this year 1/1 -1second'], ]; /** @@ -82,7 +82,7 @@ trait TimeFieldQuery $startTime = strtotime($start); $endTime = strtotime(($step > 0 ? '+' : '-') . abs($step) . ' ' . $interval . (abs($step) > 1 ? 's' : ''), $startTime); - return $this->whereTime($field, 'between', $step > 0 ? [$startTime, $endTime] : [$endTime, $startTime], $logic); + return $this->whereTime($field, 'between', $step > 0 ? [$startTime, $endTime - 1] : [$endTime, $startTime - 1], $logic); } /** -- Gitee From fe225d16e3276af5f527e77c9c78fcadb1bbecc2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 17 Jul 2019 11:14:46 +0800 Subject: [PATCH 048/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0schema=5Fcache=5Fpath?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 58 +++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index c4f9a6e..40f5b49 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -33,50 +33,53 @@ abstract class PDOConnection extends Connection */ protected $config = [ // 数据库类型 - 'type' => '', + 'type' => '', // 服务器地址 - 'hostname' => '', + 'hostname' => '', // 数据库名 - 'database' => '', + 'database' => '', // 用户名 - 'username' => '', + 'username' => '', // 密码 - 'password' => '', + 'password' => '', // 端口 - 'hostport' => '', + 'hostport' => '', // 连接dsn - 'dsn' => '', + 'dsn' => '', // 数据库连接参数 - 'params' => [], + 'params' => [], // 数据库编码默认采用utf8 - 'charset' => 'utf8', + 'charset' => 'utf8', // 数据库表前缀 - 'prefix' => '', + 'prefix' => '', // 数据库调试模式 - 'debug' => false, + 'debug' => false, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, + 'deploy' => 0, // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, + 'rw_separate' => false, // 读写分离后 主服务器数量 - 'master_num' => 1, + 'master_num' => 1, // 指定从服务器序号 - 'slave_no' => '', + 'slave_no' => '', // 模型写入后自动读取主服务器 - 'read_master' => false, + 'read_master' => false, // 是否严格检查字段是否存在 - 'fields_strict' => true, + 'fields_strict' => true, // 是否需要进行SQL性能分析 - 'sql_explain' => false, + 'sql_explain' => false, // Builder类 - 'builder' => '', + 'builder' => '', // Query类 - 'query' => '', + 'query' => '', // 是否需要断线重连 - 'break_reconnect' => false, + 'break_reconnect' => false, // 断线标识字符串 - 'break_match_str' => [], + 'break_match_str' => [], + // 字段缓存路径 + 'schema_cache_path' => '', ]; + /** * PDO操作实例 * @var PDOStatement @@ -295,17 +298,12 @@ abstract class PDOConnection extends Connection if (!isset($this->info[$schema])) { // 读取缓存 - if ($this->cache) { - $cacheSchema = $this->cache->get('schema:' . $schema); - } + $cacheFile = $this->config['schema_cache_path'] . $schema . '.php'; - if (!$this->config['debug'] && !empty($cacheSchema)) { - $info = $cacheSchema; + if (!$this->config['debug'] && is_file($cacheFile)) { + $info = include $cacheFile; } else { $info = $this->getFields($tableName); - if ($this->cache) { - $this->cache->set('schema:' . $schema, $info); - } } $fields = array_keys($info); -- Gitee From a473bdcd0cdc4e7f80d2b79de384e422dba28f0b Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 17 Jul 2019 13:56:39 +0800 Subject: [PATCH 049/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0withCache=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E7=94=A8=E4=BA=8E=E7=BC=93=E5=AD=98=E5=85=B3=E8=81=94?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 41 +++++++++++++++++++++++++-- src/model/concern/RelationShip.php | 30 ++++++++++++++------ src/model/relation/BelongsTo.php | 10 ++++--- src/model/relation/BelongsToMany.php | 12 +++++--- src/model/relation/HasMany.php | 19 +++++++++---- src/model/relation/HasManyThrough.php | 18 ++++++++---- src/model/relation/HasOne.php | 10 ++++--- src/model/relation/HasOneThrough.php | 18 ++++++++---- src/model/relation/MorphMany.php | 19 +++++++++---- src/model/relation/MorphOne.php | 19 +++++++++---- src/model/relation/MorphTo.php | 21 +++++++++----- src/model/relation/OneToOne.php | 19 +++++++++---- 12 files changed, 171 insertions(+), 65 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 4dab48f..9bd7435 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -244,6 +244,43 @@ trait ModelRelationQuery return $this; } + /** + * 关联缓存 + * @access public + * @param string|array|bool $relation 关联方法名 + * @param mixed $key 缓存key + * @param integer|\DateTime $expire 缓存有效期 + * @param string $tag 缓存标签 + * @return $this + */ + public function withCache($relation = true, $key = true, $expire = null, string $tag = null) + { + if (false === $relation || false === $key || !$this->getConnection()->getCache()) { + return $this; + } + + if ($key instanceof \DateTimeInterface || $key instanceof \DateInterval || (is_int($key) && is_null($expire))) { + $expire = $key; + $key = true; + } + + if (true === $relation || is_numeric($relation)) { + $this->options['with_cache'] = $relation; + return $this; + } + + $relations = (array) $relation; + foreach ($relations as $name => $relation) { + if (!is_numeric($name)) { + $this->options['with_cache'][Str::snake($name)] = is_array($relation) ? $relation : [$key, $relation, $tag]; + } else { + $this->options['with_cache'][Str::snake($relation)] = [$key, $expire, $tag]; + } + } + + return $this; + } + /** * 关联统计 * @access public @@ -408,12 +445,12 @@ trait ModelRelationQuery // 预载入查询 if (!$resultSet && !empty($options['with'])) { - $result->eagerlyResult($result, $options['with'], $withRelationAttr); + $result->eagerlyResult($result, $options['with'], $withRelationAttr, false, $options['with_cache'] ?? false); } // JOIN预载入查询 if (!$resultSet && !empty($options['with_join'])) { - $result->eagerlyResult($result, $options['with_join'], $withRelationAttr, true); + $result->eagerlyResult($result, $options['with_join'], $withRelationAttr, true, $options['with_cache'] ?? false); } // 关联统计 diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 3072864..5fbb78b 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -218,9 +218,10 @@ trait RelationShip * @param string $relation 关联名 * @param array $withRelationAttr 关联获取器 * @param bool $join 是否为JOIN方式 + * @param mixed $cache 关联缓存 * @return void */ - public function eagerlyResultSet(array &$resultSet, array $relations, array $withRelationAttr = [], bool $join = false): void + public function eagerlyResultSet(array &$resultSet, array $relations, array $withRelationAttr = [], bool $join = false, $cache = false): void { foreach ($relations as $key => $relation) { $subRelation = []; @@ -249,20 +250,27 @@ trait RelationShip $relationResult->getQuery()->withAttr($withRelationAttr[$relationName]); } - $relationResult->eagerlyResultSet($resultSet, $relation, $subRelation, $closure, $join); + if (is_scalar($cache)) { + $relationCache = [$cache]; + } else { + $relationCache = $cache[$relationName] ?? []; + } + + $relationResult->eagerlyResultSet($resultSet, $relation, $subRelation, $closure, $relationCache, $join); } } /** * 预载入关联查询 返回模型对象 * @access public - * @param Model $result 数据对象 - * @param array $relations 关联 - * @param array $withRelationAttr 关联获取器 - * @param bool $join 是否为JOIN方式 + * @param Model $result 数据对象 + * @param array $relations 关联 + * @param array $withRelationAttr 关联获取器 + * @param bool $join 是否为JOIN方式 + * @param mixed $cache 关联缓存 * @return void */ - public function eagerlyResult(Model $result, array $relations, array $withRelationAttr = [], bool $join = false): void + public function eagerlyResult(Model $result, array $relations, array $withRelationAttr = [], bool $join = false, $cache = false): void { foreach ($relations as $key => $relation) { $subRelation = []; @@ -291,7 +299,13 @@ trait RelationShip $relationResult->getQuery()->withAttr($withRelationAttr[$relationName]); } - $relationResult->eagerlyResult($result, $relation, $subRelation, $closure, $join); + if (is_scalar($cache)) { + $relationCache = [$cache]; + } else { + $relationCache = $cache[$relationName] ?? []; + } + + $relationResult->eagerlyResult($result, $relation, $subRelation, $closure, $relationCache, $join); } } diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 57414e8..cf38956 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -186,9 +186,10 @@ class BelongsTo extends OneToOne * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - protected function eagerlySet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null): void + protected function eagerlySet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -206,7 +207,7 @@ class BelongsTo extends OneToOne $data = $this->eagerlyWhere([ [$localKey, 'in', $range], - ], $localKey, $relation, $subRelation, $closure); + ], $localKey, $relation, $subRelation, $closure, $cache); // 关联属性名 $attr = Str::snake($relation); @@ -240,9 +241,10 @@ class BelongsTo extends OneToOne * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - protected function eagerlyOne(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void + protected function eagerlyOne(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -251,7 +253,7 @@ class BelongsTo extends OneToOne $data = $this->eagerlyWhere([ [$localKey, '=', $result->$foreignKey], - ], $localKey, $relation, $subRelation, $closure); + ], $localKey, $relation, $subRelation, $closure, $cache); // 关联模型 if (!isset($data[$result->$foreignKey])) { diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 4f15d32..d82756f 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -305,6 +305,7 @@ class BelongsToMany extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void @@ -324,7 +325,7 @@ class BelongsToMany extends Relation // 查询关联数据 $data = $this->eagerlyManyToMany([ ['pivot.' . $localKey, 'in', $range], - ], $relation, $subRelation, $closure); + ], $relation, $subRelation, $closure, $cache); // 关联属性名 $attr = Str::snake($relation); @@ -347,9 +348,10 @@ class BelongsToMany extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - public function eagerlyResult(Model $result, string $relation, array $subRelation, Closure $closure = null): void + public function eagerlyResult(Model $result, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void { $pk = $result->getPk(); @@ -358,7 +360,7 @@ class BelongsToMany extends Relation // 查询管理数据 $data = $this->eagerlyManyToMany([ ['pivot.' . $this->localKey, '=', $pk], - ], $relation, $subRelation, $closure); + ], $relation, $subRelation, $closure, $cache); // 关联数据封装 if (!isset($data[$pk])) { @@ -427,9 +429,10 @@ class BelongsToMany extends Relation * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return array */ - protected function eagerlyManyToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null): array + protected function eagerlyManyToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): array { if ($closure) { $closure($this); @@ -438,6 +441,7 @@ class BelongsToMany extends Relation // 预载入关联查询 支持嵌套预载入 $list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where) ->with($subRelation) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) ->select(); // 组装模型数据 diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index 5aad65f..2623811 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -76,9 +76,10 @@ class HasMany extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void { $localKey = $this->localKey; $range = []; @@ -93,7 +94,7 @@ class HasMany extends Relation if (!empty($range)) { $data = $this->eagerlyOneToMany([ [$this->foreignKey, 'in', $range], - ], $relation, $subRelation, $closure); + ], $relation, $subRelation, $closure, $cache); // 关联属性名 $attr = Str::snake($relation); @@ -117,9 +118,10 @@ class HasMany extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void { $localKey = $this->localKey; @@ -127,7 +129,7 @@ class HasMany extends Relation $pk = $result->$localKey; $data = $this->eagerlyOneToMany([ [$this->foreignKey, '=', $pk], - ], $relation, $subRelation, $closure); + ], $relation, $subRelation, $closure, $cache); // 关联数据封装 if (!isset($data[$pk])) { @@ -193,9 +195,10 @@ class HasMany extends Relation * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure + * @param array $cache 关联缓存 * @return array */ - protected function eagerlyOneToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null): array + protected function eagerlyOneToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): array { $foreignKey = $this->foreignKey; @@ -207,7 +210,11 @@ class HasMany extends Relation $closure($this); } - $list = $this->query->where($where)->with($subRelation)->select(); + $list = $this->query + ->where($where) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) + ->with($subRelation) + ->select(); // 组装模型数据 $data = []; diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index b02dfcf..9c0d0dd 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -162,9 +162,10 @@ class HasManyThrough extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null): void + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -182,7 +183,7 @@ class HasManyThrough extends Relation $data = $this->eagerlyWhere([ [$this->foreignKey, 'in', $range], - ], $foreignKey, $relation, $subRelation, $closure); + ], $foreignKey, $relation, $subRelation, $closure, $cache); // 关联属性名 $attr = Str::snake($relation); @@ -207,9 +208,10 @@ class HasManyThrough extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -219,7 +221,7 @@ class HasManyThrough extends Relation $data = $this->eagerlyWhere([ [$foreignKey, '=', $pk], - ], $foreignKey, $relation, $subRelation, $closure); + ], $foreignKey, $relation, $subRelation, $closure, $cache); // 关联数据封装 if (!isset($data[$pk])) { @@ -237,9 +239,10 @@ class HasManyThrough extends Relation * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure + * @param array $cache 关联缓存 * @return array */ - protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null): array + protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): array { // 预载入关联查询 支持嵌套预载入 $throughList = $this->through->where($where)->select(); @@ -250,7 +253,10 @@ class HasManyThrough extends Relation $closure($this); } - $list = $this->query->where($this->throughKey, 'in', $keys)->select(); + $list = $this->query + ->where($this->throughKey, 'in', $keys) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) + ->select(); // 组装模型数据 $data = []; diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 3897ba3..d05489c 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -185,9 +185,10 @@ class HasOne extends OneToOne * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - protected function eagerlySet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null): void + protected function eagerlySet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -205,7 +206,7 @@ class HasOne extends OneToOne $data = $this->eagerlyWhere([ [$foreignKey, 'in', $range], - ], $foreignKey, $relation, $subRelation, $closure); + ], $foreignKey, $relation, $subRelation, $closure, $cache); // 关联属性名 $attr = Str::snake($relation); @@ -239,9 +240,10 @@ class HasOne extends OneToOne * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - protected function eagerlyOne(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void + protected function eagerlyOne(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -250,7 +252,7 @@ class HasOne extends OneToOne $data = $this->eagerlyWhere([ [$foreignKey, '=', $result->$localKey], - ], $foreignKey, $relation, $subRelation, $closure); + ], $foreignKey, $relation, $subRelation, $closure, $cache); // 关联模型 if (!isset($data[$result->$localKey])) { diff --git a/src/model/relation/HasOneThrough.php b/src/model/relation/HasOneThrough.php index fb4e587..1a3c231 100644 --- a/src/model/relation/HasOneThrough.php +++ b/src/model/relation/HasOneThrough.php @@ -52,9 +52,10 @@ class HasOneThrough extends HasManyThrough * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null): void + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -72,7 +73,7 @@ class HasOneThrough extends HasManyThrough $data = $this->eagerlyWhere([ [$this->foreignKey, 'in', $range], - ], $foreignKey, $relation, $subRelation, $closure); + ], $foreignKey, $relation, $subRelation, $closure, $cache); // 关联属性名 $attr = Str::snake($relation); @@ -101,9 +102,10 @@ class HasOneThrough extends HasManyThrough * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void { $localKey = $this->localKey; $foreignKey = $this->foreignKey; @@ -112,7 +114,7 @@ class HasOneThrough extends HasManyThrough $data = $this->eagerlyWhere([ [$foreignKey, '=', $result->$localKey], - ], $foreignKey, $relation, $subRelation, $closure); + ], $foreignKey, $relation, $subRelation, $closure, $cache); // 关联模型 if (!isset($data[$result->$localKey])) { @@ -134,9 +136,10 @@ class HasOneThrough extends HasManyThrough * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure + * @param array $cache 关联缓存 * @return array */ - protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null): array + protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): array { // 预载入关联查询 支持嵌套预载入 $keys = $this->through->where($where)->column($this->throughPk, $this->foreignKey); @@ -145,7 +148,10 @@ class HasOneThrough extends HasManyThrough $closure($this); } - $list = $this->query->where($this->throughKey, 'in', $keys)->select(); + $list = $this->query + ->where($this->throughKey, 'in', $keys) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) + ->select(); // 组装模型数据 $data = []; diff --git a/src/model/relation/MorphMany.php b/src/model/relation/MorphMany.php index 64f3a57..edfb0a9 100644 --- a/src/model/relation/MorphMany.php +++ b/src/model/relation/MorphMany.php @@ -119,9 +119,10 @@ class MorphMany extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void { $morphType = $this->morphType; $morphKey = $this->morphKey; @@ -141,7 +142,7 @@ class MorphMany extends Relation [$morphKey, 'in', $range], [$morphType, '=', $type], ]; - $data = $this->eagerlyMorphToMany($where, $relation, $subRelation, $closure); + $data = $this->eagerlyMorphToMany($where, $relation, $subRelation, $closure, $cache); // 关联属性名 $attr = Str::snake($relation); @@ -164,9 +165,10 @@ class MorphMany extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void { $pk = $result->getPk(); @@ -175,7 +177,7 @@ class MorphMany extends Relation $data = $this->eagerlyMorphToMany([ [$this->morphKey, '=', $key], [$this->morphType, '=', $this->type], - ], $relation, $subRelation, $closure); + ], $relation, $subRelation, $closure, $cache); if (!isset($data[$key])) { $data[$key] = []; @@ -244,9 +246,10 @@ class MorphMany extends Relation * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return array */ - protected function eagerlyMorphToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null): array + protected function eagerlyMorphToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): array { // 预载入关联查询 支持嵌套预载入 $this->query->removeOption('where'); @@ -256,7 +259,11 @@ class MorphMany extends Relation $closure($this); } - $list = $this->query->where($where)->with($subRelation)->select(); + $list = $this->query + ->where($where) + ->with($subRelation) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) + ->select(); $morphKey = $this->morphKey; // 组装模型数据 diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php index fbc0b25..566e109 100644 --- a/src/model/relation/MorphOne.php +++ b/src/model/relation/MorphOne.php @@ -118,9 +118,10 @@ class MorphOne extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void { $morphType = $this->morphType; $morphKey = $this->morphKey; @@ -139,7 +140,7 @@ class MorphOne extends Relation $data = $this->eagerlyMorphToOne([ [$morphKey, 'in', $range], [$morphType, '=', $type], - ], $relation, $subRelation, $closure); + ], $relation, $subRelation, $closure, $cache); // 关联属性名 $attr = Str::snake($relation); @@ -166,9 +167,10 @@ class MorphOne extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void { $pk = $result->getPk(); @@ -177,7 +179,7 @@ class MorphOne extends Relation $data = $this->eagerlyMorphToOne([ [$this->morphKey, '=', $pk], [$this->morphType, '=', $this->type], - ], $relation, $subRelation, $closure); + ], $relation, $subRelation, $closure, $cache); if (isset($data[$pk])) { $relationModel = $data[$pk]; @@ -198,9 +200,10 @@ class MorphOne extends Relation * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return array */ - protected function eagerlyMorphToOne(array $where, string $relation, array $subRelation = [], $closure = null): array + protected function eagerlyMorphToOne(array $where, string $relation, array $subRelation = [], $closure = null, array $cache = []): array { // 预载入关联查询 支持嵌套预载入 if ($closure) { @@ -208,7 +211,11 @@ class MorphOne extends Relation $closure($this); } - $list = $this->query->where($where)->with($subRelation)->select(); + $list = $this->query + ->where($where) + ->with($subRelation) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) + ->select(); $morphKey = $this->morphKey; // 组装模型数据 diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index 91824c1..e8655d1 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -184,10 +184,11 @@ class MorphTo extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void * @throws Exception */ - public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void { $morphKey = $this->morphKey; $morphType = $this->morphType; @@ -209,8 +210,10 @@ class MorphTo extends Relation $model = $this->parseModel($key); $obj = new $model; $pk = $obj->getPk(); - $list = $obj->all($val, $subRelation); - $data = []; + $list = $obj->with($subRelation) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) + ->select($val); + $data = []; foreach ($list as $k => $vo) { $data[$vo->$pk] = $vo; @@ -242,14 +245,15 @@ class MorphTo extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ - public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void { // 多态类型映射 $model = $this->parseModel($result->{$this->morphType}); - $this->eagerlyMorphToOne($model, $relation, $result, $subRelation); + $this->eagerlyMorphToOne($model, $relation, $result, $subRelation, $cache); } /** @@ -271,13 +275,16 @@ class MorphTo extends Relation * @param string $relation 关联名 * @param Model $result * @param array $subRelation 子关联 + * @param array $cache 关联缓存 * @return void */ - protected function eagerlyMorphToOne(string $model, string $relation, Model $result, array $subRelation = []): void + protected function eagerlyMorphToOne(string $model, string $relation, Model $result, array $subRelation = [], array $cache = []): void { // 预载入关联查询 支持嵌套预载入 $pk = $this->parent->{$this->morphKey}; - $data = (new $model)->with($subRelation)->find($pk); + $data = (new $model)->with($subRelation) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) + ->find($pk); if ($data) { $data->setParent(clone $result); diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index 6990843..e907e28 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -138,10 +138,11 @@ abstract class OneToOne extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @param bool $join 是否为JOIN方式 * @return void */ - public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, bool $join = false): void + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, array $cache = [], bool $join = false): void { if ($join) { // 模型JOIN关联组装 @@ -150,7 +151,7 @@ abstract class OneToOne extends Relation } } else { // IN查询 - $this->eagerlySet($resultSet, $relation, $subRelation, $closure); + $this->eagerlySet($resultSet, $relation, $subRelation, $closure, $cache); } } @@ -161,17 +162,18 @@ abstract class OneToOne extends Relation * @param string $relation 当前关联名 * @param array $subRelation 子关联名 * @param Closure $closure 闭包 + * @param array $cache 关联缓存 * @param bool $join 是否为JOIN方式 * @return void */ - public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, bool $join = false): void + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = [], bool $join = false): void { if ($join) { // 模型JOIN关联组装 $this->match($this->model, $relation, $result); } else { // IN查询 - $this->eagerlyOne($result, $relation, $subRelation, $closure); + $this->eagerlyOne($result, $relation, $subRelation, $closure, $cache); } } @@ -290,9 +292,10 @@ abstract class OneToOne extends Relation * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure + * @param array $cache 关联缓存 * @return array */ - protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null) + protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []) { // 预载入关联查询 支持嵌套预载入 if ($closure) { @@ -308,7 +311,11 @@ abstract class OneToOne extends Relation $this->query->group($key); } - $list = $this->query->where($where)->with($subRelation)->select(); + $list = $this->query + ->where($where) + ->with($subRelation) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) + ->select(); // 组装模型数据 $data = []; -- Gitee From b2e913325c5efaa07457f12098bd3ca373fe96cd Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 17 Jul 2019 14:06:26 +0800 Subject: [PATCH 050/365] =?UTF-8?q?=E5=BB=B6=E8=BF=9F=E9=A2=84=E8=BD=BD?= =?UTF-8?q?=E5=85=A5=E4=B9=9F=E6=94=AF=E6=8C=81=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Collection.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/model/Collection.php b/src/model/Collection.php index 6e1dda9..fd54272 100644 --- a/src/model/Collection.php +++ b/src/model/Collection.php @@ -25,13 +25,14 @@ class Collection extends BaseCollection * 延迟预载入关联查询 * @access public * @param array|string $relation 关联 + * @param mixed $cache 关联缓存 * @return $this */ - public function load($relation) + public function load($relation, $cache = false) { if (!$this->isEmpty()) { $item = current($this->items); - $item->eagerlyResultSet($this->items, (array) $relation); + $item->eagerlyResultSet($this->items, (array) $relation, [], false, $cache); } return $this; -- Gitee From 063547f3809c2a19178e90f12780be7fe6f1b356 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 18 Jul 2019 12:07:17 +0800 Subject: [PATCH 051/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0mysql=E7=9A=84XA?= =?UTF-8?q?=E4=BA=8B=E5=8A=A1=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 2 +- src/db/Connection.php | 36 +++++++++++++++++++++++ src/db/concern/Transaction.php | 53 ++++++++++++++++++++++++++++++++++ src/db/connector/Mysql.php | 48 ++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 1 deletion(-) diff --git a/src/Model.php b/src/Model.php index 0c6d136..e5e92ed 100644 --- a/src/Model.php +++ b/src/Model.php @@ -353,7 +353,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } /** - * 更新是否强制写入数据 而不做比较 + * 更新是否强制写入数据 而不做比较(亦可用于软删除的强制删除) * @access public * @param bool $force * @return $this diff --git a/src/db/Connection.php b/src/db/Connection.php index 5075811..af7161a 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -505,6 +505,42 @@ abstract class Connection return false; } + /** + * 启动XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function startTransXa(string $xid) + {} + + /** + * 预编译XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function prepareXa(string $xid) + {} + + /** + * 提交XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function commitXa(string $xid) + {} + + /** + * 回滚XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function rollbackXa(string $xid) + {} + /** * 析构方法 * @access public diff --git a/src/db/concern/Transaction.php b/src/db/concern/Transaction.php index 93a1417..f804ae2 100644 --- a/src/db/concern/Transaction.php +++ b/src/db/concern/Transaction.php @@ -12,12 +12,65 @@ declare (strict_types = 1); namespace think\db\concern; +use think\db\BaseQuery; + /** * 事务支持 */ trait Transaction { + /** + * 执行数据库Xa事务 + * @access public + * @param callable $callback 数据操作方法回调 + * @param array $dbs 多个查询对象或者连接对象 + * @return mixed + * @throws PDOException + * @throws \Exception + * @throws \Throwable + */ + public function transactionXa($callback, array $dbs = []) + { + $xid = uniqid('xa'); + + if (empty($dbs)) { + $dbs[] = $this->getConnection(); + } + + foreach ($dbs as $key => $db) { + if ($db instanceof BaseQuery) { + $db = $db->getConnection(); + + $dbs[$key] = $db; + } + + $db->startTransXa($xid); + } + + try { + $result = null; + if (is_callable($callback)) { + $result = call_user_func_array($callback, [$this]); + } + + foreach ($dbs as $db) { + $db->prepareXa($xid); + } + + foreach ($dbs as $db) { + $db->commitXa($xid); + } + + return $result; + } catch (\Exception | \Throwable $e) { + foreach ($dbs as $db) { + $db->rollbackXa($xid); + } + throw $e; + } + } + /** * 执行数据库事务 * @access public diff --git a/src/db/connector/Mysql.php b/src/db/connector/Mysql.php index daf670b..62549fa 100644 --- a/src/db/connector/Mysql.php +++ b/src/db/connector/Mysql.php @@ -162,4 +162,52 @@ class Mysql extends PDOConnection return true; } + /** + * 启动XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function startTransXa(string $xid) + { + $this->initConnect(true); + $this->linkID->execute("XA START '$xid'"); + } + + /** + * 预编译XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function prepareXa(string $xid) + { + $this->initConnect(true); + $this->linkID->execute("XA END '$xid'"); + $this->linkID->execute("XA PREPARE '$xid'"); + } + + /** + * 提交XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function commitXa(string $xid) + { + $this->initConnect(true); + $this->linkID->execute("XA COMMIT '$xid'"); + } + + /** + * 回滚XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function rollbackXa(string $xid) + { + $this->initConnect(true); + $this->linkID->execute("XA ROLLBACK '$xid'"); + } } -- Gitee From a75df64f099e30fc04f18a07d614936d23350189 Mon Sep 17 00:00:00 2001 From: Alex-Yuen Date: Mon, 22 Jul 2019 14:04:00 +0800 Subject: [PATCH 052/365] Update PDOConnection.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修正日志输出 --- src/db/PDOConnection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 40f5b49..a4c8f01 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -431,7 +431,7 @@ abstract class PDOConnection extends Connection return $this->links[$linkNum]; } catch (\PDOException $e) { if ($autoConnection) { - $this->log->error($e->getMessage()); + $this->log($e->getMessage(), 'error'); return $this->connect($autoConnection, $linkNum); } else { throw $e; -- Gitee From d0649a61896ddd060e7a038b79b3481d8730ceff Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 23 Jul 2019 16:25:29 +0800 Subject: [PATCH 053/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E6=A0=87=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 2 +- src/db/Connection.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index 22aba38..41e1ba1 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -239,7 +239,7 @@ class DbManager $config = $connections[$name]; $type = !empty($config['type']) ? $config['type'] : 'mysql'; - if (strpos($type, '\\')) { + if (false !== strpos($type, '\\')) { $class = $type; } else { $class = '\\think\\db\\connector\\' . ucfirst($type); diff --git a/src/db/Connection.php b/src/db/Connection.php index af7161a..882c297 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -422,10 +422,10 @@ abstract class Connection protected function cacheData(CacheItem $cacheItem) { if ($cacheItem->getTag() && method_exists($this->cache, 'tag')) { - $this->cache->tag($cacheItem->getTag()); + $this->cache->tag($cacheItem->getTag())->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire()); + } else { + $this->cache->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire()); } - - $this->cache->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire()); } /** -- Gitee From c2d0e578c1694bdb66ded74d4244315243b70ab3 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 24 Jul 2019 22:49:57 +0800 Subject: [PATCH 054/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3mysql=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/connector/Mysql.php | 46 ++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/db/connector/Mysql.php b/src/db/connector/Mysql.php index 62549fa..0ff0aa4 100644 --- a/src/db/connector/Mysql.php +++ b/src/db/connector/Mysql.php @@ -27,49 +27,51 @@ class Mysql extends PDOConnection */ protected $config = [ // 数据库类型 - 'type' => 'mysql', + 'type' => 'mysql', // 服务器地址 - 'hostname' => '127.0.0.1', + 'hostname' => '127.0.0.1', // 数据库名 - 'database' => '', + 'database' => '', // 用户名 - 'username' => '', + 'username' => '', // 密码 - 'password' => '', + 'password' => '', // 端口 - 'hostport' => '', + 'hostport' => '', // 连接dsn - 'dsn' => '', + 'dsn' => '', // 数据库连接参数 - 'params' => [], + 'params' => [], // 数据库编码默认采用utf8 - 'charset' => 'utf8', + 'charset' => 'utf8', // 数据库表前缀 - 'prefix' => '', + 'prefix' => '', // 数据库调试模式 - 'debug' => false, + 'debug' => false, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, + 'deploy' => 0, // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, + 'rw_separate' => false, // 读写分离后 主服务器数量 - 'master_num' => 1, + 'master_num' => 1, // 指定从服务器序号 - 'slave_no' => '', + 'slave_no' => '', // 模型写入后自动读取主服务器 - 'read_master' => false, + 'read_master' => false, // 是否严格检查字段是否存在 - 'fields_strict' => true, + 'fields_strict' => true, // 是否需要进行SQL性能分析 - 'sql_explain' => false, + 'sql_explain' => false, // Builder类 - 'builder' => '', + 'builder' => '', // Query类 - 'query' => '', + 'query' => '', // 是否需要断线重连 - 'break_reconnect' => false, + 'break_reconnect' => false, // 断线标识字符串 - 'break_match_str' => [], + 'break_match_str' => [], + // 字段缓存路径 + 'schema_cache_path' => '', ]; /** -- Gitee From bb3c6eca07f9980374a3948fdad4f2e8b71e99b6 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 24 Jul 2019 22:50:56 +0800 Subject: [PATCH 055/365] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 2 -- src/db/connector/Mysql.php | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index a4c8f01..adfbea1 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -66,8 +66,6 @@ abstract class PDOConnection extends Connection 'read_master' => false, // 是否严格检查字段是否存在 'fields_strict' => true, - // 是否需要进行SQL性能分析 - 'sql_explain' => false, // Builder类 'builder' => '', // Query类 diff --git a/src/db/connector/Mysql.php b/src/db/connector/Mysql.php index 0ff0aa4..70b1d47 100644 --- a/src/db/connector/Mysql.php +++ b/src/db/connector/Mysql.php @@ -60,8 +60,6 @@ class Mysql extends PDOConnection 'read_master' => false, // 是否严格检查字段是否存在 'fields_strict' => true, - // 是否需要进行SQL性能分析 - 'sql_explain' => false, // Builder类 'builder' => '', // Query类 -- Gitee From bf5bc40814fe690c153648d06e37932e8f9765c5 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 25 Jul 2019 14:30:59 +0800 Subject: [PATCH 056/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bcolumn=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index a4c8f01..f0ae758 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1033,7 +1033,7 @@ abstract class PDOConnection extends Connection list($alias, $key) = explode('.', $key); } - $result = array_column($resultSet, $column, $key); + $result = array_column($resultSet, strpos($column, ',') ? null : $column, $key); } if (isset($cacheItem)) { -- Gitee From 3fcbf5cf5abb7edf374e5ba34ee60e88a87c612e Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 25 Jul 2019 16:36:29 +0800 Subject: [PATCH 057/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3BelongsToMany?= =?UTF-8?q?=E5=85=B3=E8=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsToMany.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index d82756f..7712be4 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -308,7 +308,7 @@ class BelongsToMany extends Relation * @param array $cache 关联缓存 * @return void */ - public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null): void + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void { $localKey = $this->localKey; $pk = $resultSet[0]->getPk(); -- Gitee From 50452763b860dc6bc12476af1f52358beac5b392 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 26 Jul 2019 16:09:27 +0800 Subject: [PATCH 058/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=85=B3=E8=81=94=E7=9A=84has/hasWhere=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E8=BD=AF=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsTo.php | 14 +++++++++++--- src/model/relation/HasMany.php | 11 ++++++++++- src/model/relation/HasManyThrough.php | 10 +++++++++- src/model/relation/HasOne.php | 14 +++++++++++--- 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index cf38956..3e19307 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -137,13 +137,17 @@ class BelongsTo extends OneToOne $relation = class_basename($this->model); $localKey = $this->localKey; $foreignKey = $this->foreignKey; + $softDelete = $this->query->getOptions('soft_delete'); return $this->parent->db() ->alias($model) - ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) { + ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey, $softDelete) { $query->table([$table => $relation]) ->field($relation . '.' . $localKey) - ->whereExp($model . '.' . $foreignKey, '=' . $relation . '.' . $localKey); + ->whereExp($model . '.' . $foreignKey, '=' . $relation . '.' . $localKey) + ->when($softDelete, function ($query) use ($softDelete, $relation) { + $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); + }); }); } @@ -170,12 +174,16 @@ class BelongsTo extends OneToOne $where = $this->query; } - $fields = $this->getRelationQueryFields($fields, $model); + $fields = $this->getRelationQueryFields($fields, $model); + $softDelete = $this->query->getOptions('soft_delete'); return $this->parent->db() ->alias($model) ->field($fields) ->join([$table => $relation], $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $joinType ?: $this->joinType) + ->when($softDelete, function ($query) use ($softDelete, $relation) { + $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); + }) ->where($where); } diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index 2623811..af065e8 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -301,10 +301,15 @@ class HasMany extends Relation $id = $relation . '.' . (new $this->model)->getPk(); } + $softDelete = $this->query->getOptions('soft_delete'); + return $this->parent->db() ->alias($model) ->field($model . '.*') ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType) + ->when($softDelete, function ($query) use ($softDelete, $relation) { + $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); + }) ->group($relation . '.' . $this->foreignKey) ->having('count(' . $id . ')' . $operator . $count); } @@ -332,13 +337,17 @@ class HasMany extends Relation $where = $this->query; } - $fields = $this->getRelationQueryFields($fields, $model); + $fields = $this->getRelationQueryFields($fields, $model); + $softDelete = $this->query->getOptions('soft_delete'); return $this->parent->db() ->alias($model) ->group($model . '.' . $this->localKey) ->field($fields) ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey) + ->when($softDelete, function ($query) use ($softDelete, $relation) { + $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); + }) ->where($where); } diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index 9c0d0dd..f24e9a1 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -105,6 +105,7 @@ class HasManyThrough extends Relation $throughKey = $this->throughKey; $relation = new $this->model; $relationTable = $relation->getTable(); + $softDelete = $this->query->getOptions('soft_delete'); if ('*' != $id) { $id = $relationTable . '.' . $relation->getPk(); @@ -115,6 +116,9 @@ class HasManyThrough extends Relation ->field($model . '.*') ->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey) ->join($relationTable, $relationTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk) + ->when($softDelete, function ($query) use ($softDelete, $relationTable) { + $query->where($relationTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); + }) ->group($relationTable . '.' . $this->throughKey) ->having('count(' . $id . ')' . $operator . $count); } @@ -144,12 +148,16 @@ class HasManyThrough extends Relation $where = $this->query; } - $fields = $this->getRelationQueryFields($fields, $model); + $fields = $this->getRelationQueryFields($fields, $model); + $softDelete = $this->query->getOptions('soft_delete'); return $this->parent->db() ->alias($model) ->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey) ->join($modelTable, $modelTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk) + ->when($softDelete, function ($query) use ($softDelete, $relationTable) { + $query->where($relationTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); + }) ->group($modelTable . '.' . $this->throughKey) ->where($where) ->field($fields); diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index d05489c..7b6c009 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -136,13 +136,17 @@ class HasOne extends OneToOne $relation = class_basename($this->model); $localKey = $this->localKey; $foreignKey = $this->foreignKey; + $softDelete = $this->query->getOptions('soft_delete'); return $this->parent->db() ->alias($model) - ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) { + ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey, $softDelete) { $query->table([$table => $relation]) ->field($relation . '.' . $foreignKey) - ->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey); + ->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey) + ->when($softDelete, function ($query) use ($softDelete, $relation) { + $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); + }); }); } @@ -169,12 +173,16 @@ class HasOne extends OneToOne $where = $this->query; } - $fields = $this->getRelationQueryFields($fields, $model); + $fields = $this->getRelationQueryFields($fields, $model); + $softDelete = $this->query->getOptions('soft_delete'); return $this->parent->db() ->alias($model) ->field($fields) ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType ?: $this->joinType) + ->when($softDelete, function ($query) use ($softDelete, $relation) { + $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); + }) ->where($where); } -- Gitee From b30fa45240fcce6f8d206be13e6c212a97efd72a Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 1 Aug 2019 12:16:58 +0800 Subject: [PATCH 059/365] =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E7=9A=84=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=99=A8=E3=80=81=E4=BF=AE=E6=94=B9=E5=99=A8=E5=92=8C?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E5=99=A8=E6=96=B9=E6=B3=95=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 34 +++++++++++++++++++++++++++ src/db/concern/ModelRelationQuery.php | 2 +- src/model/concern/Attribute.php | 5 ++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/Model.php b/src/Model.php index e5e92ed..850ec02 100644 --- a/src/Model.php +++ b/src/Model.php @@ -127,6 +127,12 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab */ protected $db; + /** + * 容器对象的依赖注入方法 + * @var callable + */ + protected $invoker; + /** * 服务注入 * @var Closure @@ -155,6 +161,34 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $this->db = $db; } + /** + * 设置容器对象及依赖注入方法 + * @access public + * @param callable $callable 依赖注入方法 + * @return void + */ + public function setInvoker(callable $callable): void + { + $this->invoker = $callable; + } + + /** + * 调用反射执行模型方法 支持参数绑定 + * @access public + * @param string $method + * @param array $vars 参数 + * @return mixed + */ + public function invoke(string $method, array $vars = []) + { + if ($this->invoker) { + $call = $this->invoker; + return $call([$this, $method], $vars); + } else { + return call_user_func_array([$this, $method], $vars); + } + } + /** * 架构函数 * @access public diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 9bd7435..d3a9b59 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -157,7 +157,7 @@ trait ModelRelationQuery $method = 'search' . Str::studly($fieldName) . 'Attr'; if (method_exists($this->model, $method)) { - $this->model->$method($this, $data[$field] ?? null, $data, $prefix); + $this->model->invoke($method, [$this, $data[$field] ?? null, $data, $prefix]); } } } diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 3ce402f..8e53aa5 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -363,7 +363,8 @@ trait Attribute if (method_exists($this, $method)) { $array = $this->data; - $value = $this->$method($value, array_merge($this->data, $data)); + + $value = $this->invoke($method, [$value, array_merge($this->data, $data)]); $this->set[$name] = true; if (is_null($value) && $array !== $this->data) { @@ -500,7 +501,7 @@ trait Attribute $value = $this->getRelationValue($relation); } - $value = $this->$method($value, $this->data); + $value = $this->invoke($method, [$value, $this->data]); } elseif (isset($this->type[$fieldName])) { // 类型转换 $value = $this->readTransform($value, $this->type[$fieldName]); -- Gitee From f49db1016443a090cb0e75c71a50c83851e04e25 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 1 Aug 2019 15:00:19 +0800 Subject: [PATCH 060/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E7=B1=BB=E6=94=AF=E6=8C=81=E5=BA=8F=E5=88=97=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 73 +++++---------------------- src/db/PDOConnection.php | 2 +- src/db/concern/ModelRelationQuery.php | 5 +- src/db/concern/ResultOperation.php | 2 +- src/model/concern/ModelEvent.php | 10 ++-- 5 files changed, 21 insertions(+), 71 deletions(-) diff --git a/src/Model.php b/src/Model.php index 850ec02..a877a14 100644 --- a/src/Model.php +++ b/src/Model.php @@ -97,12 +97,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab */ protected static $initialized = []; - /** - * 查询对象实例 - * @var Query - */ - protected $queryInstance; - /** * 软删除字段默认值 * @var mixed @@ -123,15 +117,15 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab /** * Db对象 - * @var Db + * @var DbManager */ - protected $db; + protected static $db; /** * 容器对象的依赖注入方法 * @var callable */ - protected $invoker; + protected static $invoker; /** * 服务注入 @@ -156,9 +150,9 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab * @param DbManager $db Db对象 * @return void */ - public function setDb(DbManager $db) + public static function setDb(DbManager $db) { - $this->db = $db; + self::$db = $db; } /** @@ -167,9 +161,9 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab * @param callable $callable 依赖注入方法 * @return void */ - public function setInvoker(callable $callable): void + public static function setInvoker(callable $callable): void { - $this->invoker = $callable; + self::$invoker = $callable; } /** @@ -181,8 +175,8 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab */ public function invoke(string $method, array $vars = []) { - if ($this->invoker) { - $call = $this->invoker; + if (self::$invoker) { + $call = self::$invoker; return $call([$this, $method], $vars); } else { return call_user_func_array([$this, $method], $vars); @@ -266,34 +260,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $this->updateWhere = $where; } - /** - * 设置当前模型的数据库查询对象 - * @access public - * @param Query $query 查询对象实例 - * @param bool $clear 是否需要清空查询条件 - * @return $this - */ - public function setQuery(Query $query, bool $clear = true) - { - $this->queryInstance = clone $query; - - if ($clear) { - $this->queryInstance->removeOption(); - } - - return $this; - } - - /** - * 获取当前模型的数据库查询对象 - * @access public - * @return Query|null - */ - public function getQuery() - { - return $this->queryInstance; - } - /** * 设置当前模型数据表的后缀 * @access public @@ -325,13 +291,9 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab public function db($scope = []): Query { /** @var Query $query */ - if ($this->queryInstance) { - $query = $this->queryInstance; - } else { - $query = $this->db->connect($this->connection) - ->name($this->name . $this->suffix) - ->pk($this->pk); - } + $query = self::$db->connect($this->connection) + ->name($this->name . $this->suffix) + ->pk($this->pk); if (!empty($this->table)) { $query->table($this->table . $this->suffix); @@ -889,17 +851,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $this->initialize(); } - public function __debugInfo() - { - $attrs = get_object_vars($this); - - foreach (['db', 'queryInstance', 'event'] as $name) { - unset($attrs[$name]); - } - - return $attrs; - } - /** * 修改器 设置数据对象的值 * @access public diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 921bc0c..66f4ec3 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -490,7 +490,7 @@ abstract class PDOConnection extends Connection // 返回结果集 while ($result = $this->PDOStatement->fetch($this->fetchType)) { if ($model) { - yield $model->newInstance($result, $condition)->setQuery($query); + yield $model->newInstance($result, $condition); } else { yield $result; } diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index d3a9b59..a63a739 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -49,7 +49,7 @@ trait ModelRelationQuery */ public function getModel(bool $clear = true) { - return $this->model ? $this->model->setQuery($this, $clear) : null; + return $this->model ?: null; } /** @@ -419,8 +419,7 @@ trait ModelRelationQuery } $result = $this->model - ->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options)) - ->setQuery($this); + ->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options)); // 动态获取器 if (!empty($options['with_attr'])) { diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index 01dbf1d..563f989 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -154,7 +154,7 @@ trait ResultOperation if (!empty($this->options['fail'])) { $this->throwNotFound(); } elseif (!empty($this->options['allow_empty'])) { - return !empty($this->model) ? $this->model->newInstance()->setQuery($this) : []; + return !empty($this->model) ? $this->model->newInstance() : []; } } diff --git a/src/model/concern/ModelEvent.php b/src/model/concern/ModelEvent.php index eea002e..f560379 100644 --- a/src/model/concern/ModelEvent.php +++ b/src/model/concern/ModelEvent.php @@ -25,7 +25,7 @@ trait ModelEvent * Event对象 * @var object */ - protected $event; + protected static $event; /** * 是否需要事件响应 @@ -39,9 +39,9 @@ trait ModelEvent * @param object $event Event对象 * @return void */ - public function setEvent($event) + public static function setEvent($event) { - $this->event = $event; + self::$event = $event; } /** @@ -73,8 +73,8 @@ trait ModelEvent try { if (method_exists(static::class, $call)) { $result = call_user_func([static::class, $call], $this); - } elseif (is_object($this->event) && method_exists($this->event, 'trigger')) { - $result = $this->event->trigger(static::class . '.' . $event, $this); + } elseif (is_object(self::$event) && method_exists(self::$event, 'trigger')) { + $result = self::$event->trigger(static::class . '.' . $event, $this); $result = empty($result) ? true : end($result); } else { $result = true; -- Gitee From 973821e5248c3f2ab6a345c48e1a669824c92d3c Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 1 Aug 2019 18:57:43 +0800 Subject: [PATCH 061/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 9 +++++---- src/db/concern/ModelRelationQuery.php | 2 +- src/model/concern/Attribute.php | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Model.php b/src/Model.php index a877a14..67d8b57 100644 --- a/src/Model.php +++ b/src/Model.php @@ -169,15 +169,16 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab /** * 调用反射执行模型方法 支持参数绑定 * @access public - * @param string $method - * @param array $vars 参数 + * @param mixed $method + * @param array $vars 参数 + * @param bool $accessible 设置是否可访问 * @return mixed */ - public function invoke(string $method, array $vars = []) + public function invoke($method, array $vars = [], bool $accessible = false) { if (self::$invoker) { $call = self::$invoker; - return $call([$this, $method], $vars); + return $call($method instanceof Closure ? $method : [$this, $method], $vars, $accessible); } else { return call_user_func_array([$this, $method], $vars); } diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index a63a739..6e786f6 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -157,7 +157,7 @@ trait ModelRelationQuery $method = 'search' . Str::studly($fieldName) . 'Attr'; if (method_exists($this->model, $method)) { - $this->model->invoke($method, [$this, $data[$field] ?? null, $data, $prefix]); + $this->model->$method($this, $data[$field] ?? null, $data, $prefix); } } } diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 8e53aa5..f62c3d5 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -364,7 +364,7 @@ trait Attribute if (method_exists($this, $method)) { $array = $this->data; - $value = $this->invoke($method, [$value, array_merge($this->data, $data)]); + $value = $this->$method($value, array_merge($this->data, $data)); $this->set[$name] = true; if (is_null($value) && $array !== $this->data) { @@ -501,7 +501,7 @@ trait Attribute $value = $this->getRelationValue($relation); } - $value = $this->invoke($method, [$value, $this->data]); + $value = $this->$method($value, $this->data); } elseif (isset($this->type[$fieldName])) { // 类型转换 $value = $this->readTransform($value, $this->type[$fieldName]); -- Gitee From 1063d26e47840b4b7638d6cf18aaf0cdcf9754d2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 1 Aug 2019 19:08:36 +0800 Subject: [PATCH 062/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3Mongo=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E7=B1=BBinc=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Mongo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/Mongo.php b/src/db/Mongo.php index c04b0d4..79cada8 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -203,7 +203,7 @@ class Mongo extends BaseQuery } } - $this->data($field, ['$' . $op, $step]); + $this->options['data'][$field] = ['$' . $op, $step]; return $this; } -- Gitee From 4aa7976c8d3497db65c32ed78b30afb1d1b1e5f3 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 1 Aug 2019 19:12:44 +0800 Subject: [PATCH 063/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Model.php b/src/Model.php index 67d8b57..d84ac28 100644 --- a/src/Model.php +++ b/src/Model.php @@ -171,14 +171,13 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab * @access public * @param mixed $method * @param array $vars 参数 - * @param bool $accessible 设置是否可访问 * @return mixed */ - public function invoke($method, array $vars = [], bool $accessible = false) + public function invoke($method, array $vars = []) { if (self::$invoker) { $call = self::$invoker; - return $call($method instanceof Closure ? $method : [$this, $method], $vars, $accessible); + return $call($method instanceof Closure ? $method : Closure::fromCallable([$this, $method]), $vars); } else { return call_user_func_array([$this, $method], $vars); } -- Gitee From f47c3d4f2c38f6957d55fab50905f7f9fd74d7cb Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 1 Aug 2019 19:27:17 +0800 Subject: [PATCH 064/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Model.php b/src/Model.php index d84ac28..d3bb101 100644 --- a/src/Model.php +++ b/src/Model.php @@ -178,9 +178,9 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab if (self::$invoker) { $call = self::$invoker; return $call($method instanceof Closure ? $method : Closure::fromCallable([$this, $method]), $vars); - } else { - return call_user_func_array([$this, $method], $vars); } + + return call_user_func_array($method instanceof Closure ? $method : [$this, $method], $vars); } /** -- Gitee From ea0240aa04d1085576064e7164ac563838f0f79c Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 2 Aug 2019 11:54:20 +0800 Subject: [PATCH 065/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Model.php b/src/Model.php index d3bb101..5412e16 100644 --- a/src/Model.php +++ b/src/Model.php @@ -129,9 +129,9 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab /** * 服务注入 - * @var Closure + * @var Closure[] */ - protected static $maker; + protected static $maker = []; /** * 设置服务注入 @@ -141,7 +141,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab */ public static function maker(Closure $maker) { - static::$maker = $maker; + static::$maker[] = $maker; } /** @@ -210,8 +210,10 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $this->name = basename($name); } - if (static::$maker) { - call_user_func(static::$maker, $this); + if (!empty(static::$maker)) { + foreach (static::$maker as $maker) { + call_user_func($maker, $this); + } } // 执行初始化操作 -- Gitee From fbe4b45d16284dfc9b805e2cbd245883c45460d2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 5 Aug 2019 13:56:43 +0800 Subject: [PATCH 066/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=85=B3=E8=81=94=E7=9A=84=E9=97=AD=E5=8C=85=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 2 +- src/db/concern/ModelRelationQuery.php | 3 +-- src/model/Relation.php | 4 +--- src/model/concern/RelationShip.php | 6 +++--- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Model.php b/src/Model.php index 5412e16..a0c2672 100644 --- a/src/Model.php +++ b/src/Model.php @@ -156,7 +156,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } /** - * 设置容器对象及依赖注入方法 + * 设置容器对象的依赖注入方法 * @access public * @param callable $callable 依赖注入方法 * @return void diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 6e786f6..dfff21f 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -44,10 +44,9 @@ trait ModelRelationQuery /** * 获取当前的模型对象 * @access public - * @param bool $clear 是否需要清空查询条件 * @return Model|null */ - public function getModel(bool $clear = true) + public function getModel() { return $this->model ?: null; } diff --git a/src/model/Relation.php b/src/model/Relation.php index fd813ad..640f227 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -229,10 +229,8 @@ abstract class Relation // 执行基础查询 $this->baseQuery(); - $model = $this->query->getModel(false); - $result = call_user_func_array([$model, $method], $args); + $result = call_user_func_array([$this->query, $method], $args); - $this->query = $model->getQuery(); return $result === $this->query ? $this : $result; } diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 5fbb78b..44b2afc 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -156,7 +156,7 @@ trait RelationShip $relationResult = $this->$method(); if (isset($withRelationAttr[$relationName])) { - $relationResult->getQuery()->withAttr($withRelationAttr[$relationName]); + $relationResult->withAttr($withRelationAttr[$relationName]); } $this->relation[$relation] = $relationResult->getRelation($subRelation, $closure); @@ -247,7 +247,7 @@ trait RelationShip $relationResult = $this->$relation(); if (isset($withRelationAttr[$relationName])) { - $relationResult->getQuery()->withAttr($withRelationAttr[$relationName]); + $relationResult->withAttr($withRelationAttr[$relationName]); } if (is_scalar($cache)) { @@ -296,7 +296,7 @@ trait RelationShip $relationResult = $this->$relation(); if (isset($withRelationAttr[$relationName])) { - $relationResult->getQuery()->withAttr($withRelationAttr[$relationName]); + $relationResult->withAttr($withRelationAttr[$relationName]); } if (is_scalar($cache)) { -- Gitee From 8528ccaf5cfd9d4d32c6e0d29e91d624fe5910fb Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 5 Aug 2019 14:47:26 +0800 Subject: [PATCH 067/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=85=B3=E8=81=94?= =?UTF-8?q?=E7=9A=84=E9=97=AD=E5=8C=85=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 2 +- src/model/Relation.php | 17 +++++++++++++++++ src/model/relation/BelongsTo.php | 2 +- src/model/relation/BelongsToMany.php | 8 ++++---- src/model/relation/HasMany.php | 8 ++++---- src/model/relation/HasManyThrough.php | 8 ++++---- src/model/relation/HasOne.php | 6 +++--- src/model/relation/HasOneThrough.php | 4 ++-- src/model/relation/MorphMany.php | 8 ++++---- src/model/relation/MorphOne.php | 4 ++-- src/model/relation/OneToOne.php | 2 +- 11 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index dfff21f..06de4e6 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -48,7 +48,7 @@ trait ModelRelationQuery */ public function getModel() { - return $this->model ?: null; + return $this->model; } /** diff --git a/src/model/Relation.php b/src/model/Relation.php index 640f227..679f5f3 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -12,6 +12,8 @@ declare (strict_types = 1); namespace think\model; +use Closure; +use ReflectionFunction; use think\db\BaseQuery as Query; use think\db\exception\DbException as Exception; use think\Model; @@ -215,6 +217,21 @@ abstract class Relation return $this; } + /** + * 执行关联的闭包查询 + * @access protected + * @return void + */ + protected function callClosure(Closure $closure, ...$param) + { + $reflect = new ReflectionFunction($closure); + $params = $reflect->getParameters(); + $type = $params[0]->getType(); + + array_unshift($param, Relation::class == $type ? $this : $this->query); + $reflect->invokeArgs($param); + } + /** * 执行基础查询(仅执行一次) * @access protected diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 3e19307..83027fc 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -85,7 +85,7 @@ class BelongsTo extends OneToOne public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', &$name = ''): string { if ($closure) { - $closure($this, $name); + $this->callClosure($closure, $name); } return $this->query diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 7712be4..f195d8a 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -174,7 +174,7 @@ class BelongsToMany extends Relation public function getRelation(array $subRelation = [], Closure $closure = null): Collection { if ($closure) { - $closure($this); + $this->callClosure($closure); } $result = $this->buildQuery() @@ -392,7 +392,7 @@ class BelongsToMany extends Relation $pk = $result->$pk; if ($closure) { - $closure($this, $name); + $this->callClosure($closure, $name); } return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ @@ -412,7 +412,7 @@ class BelongsToMany extends Relation public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $closure($this, $name); + $this->callClosure($closure, $name); } return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ @@ -435,7 +435,7 @@ class BelongsToMany extends Relation protected function eagerlyManyToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): array { if ($closure) { - $closure($this); + $this->callClosure($closure); } // 预载入关联查询 支持嵌套预载入 diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index af065e8..c251bf3 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -55,7 +55,7 @@ class HasMany extends Relation public function getRelation(array $subRelation = [], Closure $closure = null): Collection { if ($closure) { - $closure($this); + $this->callClosure($closure); } if ($this->withLimit) { @@ -159,7 +159,7 @@ class HasMany extends Relation } if ($closure) { - $closure($this, $name); + $this->callClosure($closure, $name); } return $this->query @@ -179,7 +179,7 @@ class HasMany extends Relation public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $closure($this, $name); + $this->callClosure($closure, $name); } return $this->query->alias($aggregate . '_table') @@ -207,7 +207,7 @@ class HasMany extends Relation // 预载入关联查询 支持嵌套预载入 if ($closure) { $this->baseQuery = true; - $closure($this); + $this->callClosure($closure); } $list = $this->query diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index f24e9a1..c9a1baa 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -74,7 +74,7 @@ class HasManyThrough extends Relation public function getRelation(array $subRelation = [], Closure $closure = null) { if ($closure) { - $closure($this); + $this->callClosure($closure); } $this->baseQuery(); @@ -258,7 +258,7 @@ class HasManyThrough extends Relation if ($closure) { $this->baseQuery = true; - $closure($this); + $this->callClosure($closure); } $list = $this->query @@ -302,7 +302,7 @@ class HasManyThrough extends Relation } if ($closure) { - $closure($this, $name); + $this->callClosure($closure, $name); } $alias = Str::snake(class_basename($this->model)); @@ -335,7 +335,7 @@ class HasManyThrough extends Relation public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $closure($this, $name); + $this->callClosure($closure, $name); } $alias = Str::snake(class_basename($this->model)); diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 7b6c009..4f8e76c 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -55,7 +55,7 @@ class HasOne extends OneToOne $localKey = $this->localKey; if ($closure) { - $closure($this); + $this->callClosure($closure); } // 判断关联类型执行查询 @@ -84,7 +84,7 @@ class HasOne extends OneToOne public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $closure($this, $name); + $this->callClosure($closure, $name); } return $this->query @@ -112,7 +112,7 @@ class HasOne extends OneToOne } if ($closure) { - $closure($this, $name); + $this->callClosure($closure, $name); } return $this->query diff --git a/src/model/relation/HasOneThrough.php b/src/model/relation/HasOneThrough.php index 1a3c231..86aef67 100644 --- a/src/model/relation/HasOneThrough.php +++ b/src/model/relation/HasOneThrough.php @@ -31,7 +31,7 @@ class HasOneThrough extends HasManyThrough public function getRelation(array $subRelation = [], Closure $closure = null) { if ($closure) { - $closure($this); + $this->callClosure($closure); } $this->baseQuery(); @@ -145,7 +145,7 @@ class HasOneThrough extends HasManyThrough $keys = $this->through->where($where)->column($this->throughPk, $this->foreignKey); if ($closure) { - $closure($this); + $this->callClosure($closure); } $list = $this->query diff --git a/src/model/relation/MorphMany.php b/src/model/relation/MorphMany.php index edfb0a9..1c61f6c 100644 --- a/src/model/relation/MorphMany.php +++ b/src/model/relation/MorphMany.php @@ -71,7 +71,7 @@ class MorphMany extends Relation public function getRelation(array $subRelation = [], Closure $closure = null): Collection { if ($closure) { - $closure($this); + $this->callClosure($closure); } $this->baseQuery(); @@ -206,7 +206,7 @@ class MorphMany extends Relation } if ($closure) { - $closure($this, $name); + $this->callClosure($closure, $name); } return $this->query @@ -229,7 +229,7 @@ class MorphMany extends Relation public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $closure($this, $name); + $this->callClosure($closure, $name); } return $this->query @@ -256,7 +256,7 @@ class MorphMany extends Relation if ($closure) { $this->baseQuery = true; - $closure($this); + $this->callClosure($closure); } $list = $this->query diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php index 566e109..0623c8d 100644 --- a/src/model/relation/MorphOne.php +++ b/src/model/relation/MorphOne.php @@ -70,7 +70,7 @@ class MorphOne extends Relation public function getRelation(array $subRelation = [], Closure $closure = null) { if ($closure) { - $closure($this); + $this->callClosure($closure); } $this->baseQuery(); @@ -208,7 +208,7 @@ class MorphOne extends Relation // 预载入关联查询 支持嵌套预载入 if ($closure) { $this->baseQuery = true; - $closure($this); + $this->callClosure($closure); } $list = $this->query diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index e907e28..3b800f0 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -300,7 +300,7 @@ abstract class OneToOne extends Relation // 预载入关联查询 支持嵌套预载入 if ($closure) { $this->baseQuery = true; - $closure($this); + $this->callClosure($closure); } if ($this->withField) { -- Gitee From f5d097ffd853dce68032a021c959be19ed1d2bc1 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 5 Aug 2019 16:52:36 +0800 Subject: [PATCH 068/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsTo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 83027fc..407c248 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -55,7 +55,7 @@ class BelongsTo extends OneToOne public function getRelation(array $subRelation = [], Closure $closure = null) { if ($closure) { - $closure($this); + $this->callClosure($closure); } $foreignKey = $this->foreignKey; @@ -113,7 +113,7 @@ class BelongsTo extends OneToOne } if ($closure) { - $closure($this, $name); + $this->callClosure($closure, $name); } return $this->query -- Gitee From 9f6e43018799a66bf97e93413c5d788b011db096 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 5 Aug 2019 16:59:40 +0800 Subject: [PATCH 069/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=85=B3=E8=81=94?= =?UTF-8?q?=E7=9A=84callClosure=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Relation.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/model/Relation.php b/src/model/Relation.php index 679f5f3..a0a5471 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -220,16 +220,19 @@ abstract class Relation /** * 执行关联的闭包查询 * @access protected - * @return void + * @return mixed */ protected function callClosure(Closure $closure, ...$param) { $reflect = new ReflectionFunction($closure); $params = $reflect->getParameters(); - $type = $params[0]->getType(); - array_unshift($param, Relation::class == $type ? $this : $this->query); - $reflect->invokeArgs($param); + if (!empty($params)) { + $type = $params[0]->getType(); + array_unshift($param, Relation::class == $type ? $this : $this->query); + } + + return $reflect->invokeArgs($param); } /** -- Gitee From 5f52623b7d1239e73821d67bd76c1ecb256ca2c0 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 5 Aug 2019 17:06:00 +0800 Subject: [PATCH 070/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 10 +++++----- src/model/Relation.php | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index 41e1ba1..6a81353 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -92,13 +92,13 @@ class DbManager */ protected function modelMaker() { - Model::maker(function (Model $model) { - $model->setDb($this); + Model::setDb($this); - if (is_object($this->event)) { - $model->setEvent($this->event); - } + if (is_object($this->event)) { + Model::setEvent($this->event); + } + Model::maker(function (Model $model) { $isAutoWriteTimestamp = $model->getAutoWriteTimestamp(); if (is_null($isAutoWriteTimestamp)) { diff --git a/src/model/Relation.php b/src/model/Relation.php index a0a5471..0a8342d 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -222,17 +222,17 @@ abstract class Relation * @access protected * @return mixed */ - protected function callClosure(Closure $closure, ...$param) + protected function callClosure(Closure $closure, ...$vars) { $reflect = new ReflectionFunction($closure); $params = $reflect->getParameters(); if (!empty($params)) { $type = $params[0]->getType(); - array_unshift($param, Relation::class == $type ? $this : $this->query); + array_unshift($vars, Relation::class == $type ? $this : $this->query); } - return $reflect->invokeArgs($param); + return $reflect->invokeArgs($vars); } /** -- Gitee From a81fe59d094f32650747e874388d31d6f81259b4 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 6 Aug 2019 11:58:01 +0800 Subject: [PATCH 071/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Conversion.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index 781825a..178ad0f 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -167,7 +167,7 @@ trait Conversion } // 关联模型对象 if (!isset($this->hidden[$key]) || true !== $this->hidden[$key]) { - $item[$key] = $val; + $item[$key] = $val->toArray(); } } elseif (isset($this->visible[$key])) { $item[$key] = $this->getAttr($key); -- Gitee From 4043d149e5cdb4385b711d27933ab5df9c6afdaa Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 7 Aug 2019 11:56:23 +0800 Subject: [PATCH 072/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E9=97=AD=E5=8C=85?= =?UTF-8?q?=E7=9A=84=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Relation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/Relation.php b/src/model/Relation.php index 0a8342d..64fb1ef 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -229,7 +229,7 @@ abstract class Relation if (!empty($params)) { $type = $params[0]->getType(); - array_unshift($vars, Relation::class == $type ? $this : $this->query); + array_unshift($vars, Relation::class == $type || is_null($type) ? $this : $this->query); } return $reflect->invokeArgs($vars); -- Gitee From c9579f1ef3c99a6e3a372c63548eb8b60d915df2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 7 Aug 2019 14:44:34 +0800 Subject: [PATCH 073/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=85=B3=E8=81=94?= =?UTF-8?q?=E7=B1=BBgetModel=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Relation.php | 5 ++--- src/model/concern/RelationShip.php | 2 +- src/model/relation/MorphTo.php | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/model/Relation.php b/src/model/Relation.php index 64fb1ef..e5bba56 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -102,12 +102,11 @@ abstract class Relation /** * 获取当前的关联模型类的实例 * @access public - * @param bool $clear 是否需要清空查询条件 * @return Model */ - public function getModel(bool $clear = true): Model + public function getModel(): Model { - return $this->query->getModel($clear); + return $this->query->getModel(); } /** diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 44b2afc..e15aa28 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -639,7 +639,7 @@ trait RelationShip protected function getRelationData(Relation $modelRelation) { if ($this->parent && !$modelRelation->isSelfRelation() - && get_class($this->parent) == get_class($modelRelation->getModel(false))) { + && get_class($this->parent) == get_class($modelRelation->getModel())) { return $this->parent; } diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index e8655d1..c1a1161 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -67,10 +67,9 @@ class MorphTo extends Relation /** * 获取当前的关联模型类的实例 * @access public - * @param bool $clear 是否需要清空查询条件 * @return Model */ - public function getModel(bool $clear = true): Model + public function getModel(): Model { $morphType = $this->morphType; $model = $this->parseModel($this->parent->$morphType); -- Gitee From 8c1d08c06e84a3f04951b4e69290ac33dd255bd2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 7 Aug 2019 14:49:37 +0800 Subject: [PATCH 074/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BgetLastInsID=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 66f4ec3..0152a15 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -774,7 +774,7 @@ abstract class PDOConnection extends Connection $this->db->trigger('after_insert', $query); - if ($getLastInsID) { + if ($getLastInsID && $lastInsId) { return $lastInsId; } } @@ -1396,7 +1396,11 @@ abstract class PDOConnection extends Connection */ public function getLastInsID(BaseQuery $query, string $sequence = null) { - $insertId = $this->linkID->lastInsertId($sequence); + try { + $insertId = $this->linkID->lastInsertId($sequence); + } catch (\Exception $e) { + $insertId = ''; + } return $this->autoInsIDType($query, $insertId); } -- Gitee From 52c9edff36426cc5be5db0ff828c0b6963fb770a Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 8 Aug 2019 17:12:07 +0800 Subject: [PATCH 075/365] =?UTF-8?q?=E6=A8=A1=E5=9E=8Bhas/hasWhere=E5=85=B3?= =?UTF-8?q?=E8=81=94=E6=9F=A5=E8=AF=A2=E6=94=AF=E6=8C=81=E5=A4=9A=E6=AC=A1?= =?UTF-8?q?=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 29 +++++++++++++++++++++++++++ src/model/relation/HasManyThrough.php | 4 ++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 06de4e6..2dda441 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -344,6 +344,35 @@ trait ModelRelationQuery return $this->withAggregate($relation, 'avg', $field, $subQuery); } + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $relation 关联方法名 + * @param mixed $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 + * @return $this + */ + public function has(string $relation, string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '') + { + return $this->model->has($relation, $operator, $count, $id, $joinType); + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $relation 关联方法名 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 + * @return $this + */ + public function hasWhere(string $relation, $where = [], string $fields = '*', string $joinType = '') + { + return $this->model->hasWhere($relation, $where, $fields, $joinType); + } + /** * 查询数据转换为模型数据集对象 * @access protected diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index c9a1baa..f2f51fb 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -155,8 +155,8 @@ class HasManyThrough extends Relation ->alias($model) ->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey) ->join($modelTable, $modelTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk) - ->when($softDelete, function ($query) use ($softDelete, $relationTable) { - $query->where($relationTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); + ->when($softDelete, function ($query) use ($softDelete, $modelTable) { + $query->where($modelTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); }) ->group($modelTable . '.' . $this->throughKey) ->where($where) -- Gitee From 687149d3092939eaa913ec2225b8729be32f62cd Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 8 Aug 2019 17:53:18 +0800 Subject: [PATCH 076/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8Bhas?= =?UTF-8?q?/hasWhere=E5=A4=9A=E6=AC=A1=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 4 ++-- src/model/concern/RelationShip.php | 10 +++++---- src/model/relation/BelongsTo.php | 32 +++++++++++++-------------- src/model/relation/BelongsToMany.php | 6 +++-- src/model/relation/HasMany.php | 16 +++++++------- src/model/relation/HasManyThrough.php | 16 +++++++------- src/model/relation/HasOne.php | 32 +++++++++++++-------------- src/model/relation/MorphMany.php | 6 +++-- src/model/relation/MorphOne.php | 6 +++-- src/model/relation/MorphTo.php | 6 +++-- 10 files changed, 72 insertions(+), 62 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 2dda441..6ea3dea 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -356,7 +356,7 @@ trait ModelRelationQuery */ public function has(string $relation, string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '') { - return $this->model->has($relation, $operator, $count, $id, $joinType); + return $this->model->has($relation, $operator, $count, $id, $joinType, $this); } /** @@ -370,7 +370,7 @@ trait ModelRelationQuery */ public function hasWhere(string $relation, $where = [], string $fields = '*', string $joinType = '') { - return $this->model->hasWhere($relation, $where, $fields, $joinType); + return $this->model->hasWhere($relation, $where, $fields, $joinType, $this); } /** diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index e15aa28..10c9328 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -186,13 +186,14 @@ trait RelationShip * @param integer $count 个数 * @param string $id 关联表的统计字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public static function has(string $relation, string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query + public static function has(string $relation, string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null): Query { return (new static()) ->$relation() - ->has($operator, $count, $id, $joinType); + ->has($operator, $count, $id, $joinType, $query); } /** @@ -202,13 +203,14 @@ trait RelationShip * @param mixed $where 查询条件(数组或者闭包) * @param mixed $fields 字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public static function hasWhere(string $relation, $where = [], string $fields = '*', string $joinType = ''): Query + public static function hasWhere(string $relation, $where = [], string $fields = '*', string $joinType = '', Query $query = null): Query { return (new static()) ->$relation() - ->hasWhere($where, $fields, $joinType); + ->hasWhere($where, $fields, $joinType, $query); } /** diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 407c248..6f7622a 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -128,9 +128,10 @@ class BelongsTo extends OneToOne * @param integer $count 个数 * @param string $id 关联表的统计字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null): Query { $table = $this->query->getTable(); $model = class_basename($this->parent); @@ -138,17 +139,16 @@ class BelongsTo extends OneToOne $localKey = $this->localKey; $foreignKey = $this->foreignKey; $softDelete = $this->query->getOptions('soft_delete'); - - return $this->parent->db() - ->alias($model) - ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey, $softDelete) { - $query->table([$table => $relation]) - ->field($relation . '.' . $localKey) - ->whereExp($model . '.' . $foreignKey, '=' . $relation . '.' . $localKey) - ->when($softDelete, function ($query) use ($softDelete, $relation) { - $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); - }); - }); + $query = $query ?: $this->parent->db()->alias($model); + + return $query->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey, $softDelete) { + $query->table([$table => $relation]) + ->field($relation . '.' . $localKey) + ->whereExp($model . '.' . $foreignKey, '=' . $relation . '.' . $localKey) + ->when($softDelete, function ($query) use ($softDelete, $relation) { + $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); + }); + }); } /** @@ -157,9 +157,10 @@ class BelongsTo extends OneToOne * @param mixed $where 查询条件(数组或者闭包) * @param mixed $fields 字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function hasWhere($where = [], $fields = null, string $joinType = ''): Query + public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null): Query { $table = $this->query->getTable(); $model = class_basename($this->parent); @@ -176,10 +177,9 @@ class BelongsTo extends OneToOne $fields = $this->getRelationQueryFields($fields, $model); $softDelete = $this->query->getOptions('soft_delete'); + $query = $query ?: $this->parent->db()->alias($model); - return $this->parent->db() - ->alias($model) - ->field($fields) + return $query->field($fields) ->join([$table => $relation], $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $joinType ?: $this->joinType) ->when($softDelete, function ($query) use ($softDelete, $relation) { $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index f195d8a..bcc8569 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -263,9 +263,10 @@ class BelongsToMany extends Relation * @param integer $count 个数 * @param string $id 关联表的统计字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Model */ - public function has(string $operator = '>=', $count = 1, $id = '*', string $joinType = 'INNER') + public function has(string $operator = '>=', $count = 1, $id = '*', string $joinType = 'INNER', Query $query = null) { return $this->parent; } @@ -276,10 +277,11 @@ class BelongsToMany extends Relation * @param mixed $where 查询条件(数组或者闭包) * @param mixed $fields 字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query * @throws Exception */ - public function hasWhere($where = [], $fields = null, string $joinType = '') + public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null) { throw new Exception('relation not support: hasWhere'); } diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index c251bf3..25c919e 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -288,9 +288,10 @@ class HasMany extends Relation * @param integer $count 个数 * @param string $id 关联表的统计字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = 'INNER'): Query + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = 'INNER', Query $query = null): Query { $table = $this->query->getTable(); @@ -302,10 +303,9 @@ class HasMany extends Relation } $softDelete = $this->query->getOptions('soft_delete'); + $query = $query ?: $this->parent->db()->alias($model); - return $this->parent->db() - ->alias($model) - ->field($model . '.*') + return $query->field($model . '.*') ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType) ->when($softDelete, function ($query) use ($softDelete, $relation) { $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); @@ -320,9 +320,10 @@ class HasMany extends Relation * @param mixed $where 查询条件(数组或者闭包) * @param mixed $fields 字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function hasWhere($where = [], $fields = null, string $joinType = ''): Query + public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null): Query { $table = $this->query->getTable(); $model = class_basename($this->parent); @@ -339,10 +340,9 @@ class HasMany extends Relation $fields = $this->getRelationQueryFields($fields, $model); $softDelete = $this->query->getOptions('soft_delete'); + $query = $query ?: $this->parent->db()->alias($model); - return $this->parent->db() - ->alias($model) - ->group($model . '.' . $this->localKey) + return $query->group($model . '.' . $this->localKey) ->field($fields) ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey) ->when($softDelete, function ($query) use ($softDelete, $relation) { diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index f2f51fb..9121cc0 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -95,9 +95,10 @@ class HasManyThrough extends Relation * @param integer $count 个数 * @param string $id 关联表的统计字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null): Query { $model = Str::snake(class_basename($this->parent)); $throughTable = $this->through->getTable(); @@ -110,10 +111,9 @@ class HasManyThrough extends Relation if ('*' != $id) { $id = $relationTable . '.' . $relation->getPk(); } + $query = $query ?: $this->parent->db()->alias($model); - return $this->parent->db() - ->alias($model) - ->field($model . '.*') + return $query->field($model . '.*') ->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey) ->join($relationTable, $relationTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk) ->when($softDelete, function ($query) use ($softDelete, $relationTable) { @@ -129,9 +129,10 @@ class HasManyThrough extends Relation * @param mixed $where 查询条件(数组或者闭包) * @param mixed $fields 字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function hasWhere($where = [], $fields = null, $joinType = ''): Query + public function hasWhere($where = [], $fields = null, $joinType = '', Query $query = null): Query { $model = Str::snake(class_basename($this->parent)); $throughTable = $this->through->getTable(); @@ -150,10 +151,9 @@ class HasManyThrough extends Relation $fields = $this->getRelationQueryFields($fields, $model); $softDelete = $this->query->getOptions('soft_delete'); + $query = $query ?: $this->parent->db()->alias($model); - return $this->parent->db() - ->alias($model) - ->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey) + return $query->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey) ->join($modelTable, $modelTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk) ->when($softDelete, function ($query) use ($softDelete, $modelTable) { $query->where($modelTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 4f8e76c..32cfab6 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -127,9 +127,10 @@ class HasOne extends OneToOne * @param integer $count 个数 * @param string $id 关联表的统计字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = ''): Query + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null): Query { $table = $this->query->getTable(); $model = class_basename($this->parent); @@ -137,17 +138,16 @@ class HasOne extends OneToOne $localKey = $this->localKey; $foreignKey = $this->foreignKey; $softDelete = $this->query->getOptions('soft_delete'); - - return $this->parent->db() - ->alias($model) - ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey, $softDelete) { - $query->table([$table => $relation]) - ->field($relation . '.' . $foreignKey) - ->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey) - ->when($softDelete, function ($query) use ($softDelete, $relation) { - $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); - }); - }); + $query = $query ?: $this->parent->db()->alias($model); + + return $query->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey, $softDelete) { + $query->table([$table => $relation]) + ->field($relation . '.' . $foreignKey) + ->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey) + ->when($softDelete, function ($query) use ($softDelete, $relation) { + $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); + }); + }); } /** @@ -156,9 +156,10 @@ class HasOne extends OneToOne * @param mixed $where 查询条件(数组或者闭包) * @param mixed $fields 字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function hasWhere($where = [], $fields = null, string $joinType = ''): Query + public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null): Query { $table = $this->query->getTable(); $model = class_basename($this->parent); @@ -175,10 +176,9 @@ class HasOne extends OneToOne $fields = $this->getRelationQueryFields($fields, $model); $softDelete = $this->query->getOptions('soft_delete'); + $query = $query ?: $this->parent->db()->alias($model); - return $this->parent->db() - ->alias($model) - ->field($fields) + return $query->field($fields) ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType ?: $this->joinType) ->when($softDelete, function ($query) use ($softDelete, $relation) { $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); diff --git a/src/model/relation/MorphMany.php b/src/model/relation/MorphMany.php index 1c61f6c..e58f13f 100644 --- a/src/model/relation/MorphMany.php +++ b/src/model/relation/MorphMany.php @@ -92,9 +92,10 @@ class MorphMany extends Relation * @param integer $count 个数 * @param string $id 关联表的统计字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '') + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null) { throw new Exception('relation not support: has'); } @@ -105,9 +106,10 @@ class MorphMany extends Relation * @param mixed $where 查询条件(数组或者闭包) * @param mixed $fields 字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function hasWhere($where = [], $fields = null, string $joinType = '') + public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null) { throw new Exception('relation not support: hasWhere'); } diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php index 0623c8d..995fc11 100644 --- a/src/model/relation/MorphOne.php +++ b/src/model/relation/MorphOne.php @@ -91,9 +91,10 @@ class MorphOne extends Relation * @param integer $count 个数 * @param string $id 关联表的统计字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '') + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null) { return $this->parent; } @@ -104,9 +105,10 @@ class MorphOne extends Relation * @param mixed $where 查询条件(数组或者闭包) * @param mixed $fields 字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function hasWhere($where = [], $fields = null, string $joinType = '') + public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null) { throw new Exception('relation not support: hasWhere'); } diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index c1a1161..b6a9cf6 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -111,9 +111,10 @@ class MorphTo extends Relation * @param integer $count 个数 * @param string $id 关联表的统计字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '') + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null) { return $this->parent; } @@ -124,9 +125,10 @@ class MorphTo extends Relation * @param mixed $where 查询条件(数组或者闭包) * @param mixed $fields 字段 * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ - public function hasWhere($where = [], $fields = null, string $joinType = '') + public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null) { throw new Exception('relation not support: hasWhere'); } -- Gitee From 6d8846be82c1a4cd09763f6b973355accb7f799b Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 15 Aug 2019 21:53:39 +0800 Subject: [PATCH 077/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E8=81=9A=E5=90=88?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=20=E6=94=B9=E8=BF=9Bcolumn=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E5=AF=B9=E5=A4=9A=E8=A1=A8=E6=9F=A5=E8=AF=A2=E7=9A=84=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 0152a15..c946ec5 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -913,6 +913,10 @@ abstract class PDOConnection extends Connection $query->removeOption('field'); } + if (isset($options['group'])) { + $query->group(''); + } + $query->setOption('field', (array) $field); if (!empty($options['cache'])) { @@ -933,6 +937,10 @@ abstract class PDOConnection extends Connection $query->removeOption('field'); } + if (isset($options['group'])) { + $query->setOption('group', $options['group']); + } + // 执行查询操作 $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']); @@ -1027,11 +1035,17 @@ abstract class PDOConnection extends Connection $fields = array_keys($resultSet[0]); $key = $key ?: array_shift($fields); + if (strpos($column, ',')) { + $column = null; + } elseif (strpos($column, '.')) { + list($alias, $column) = explode('.', $column); + } + if (strpos($key, '.')) { list($alias, $key) = explode('.', $key); } - $result = array_column($resultSet, strpos($column, ',') ? null : $column, $key); + $result = array_column($resultSet, $column, $key); } if (isset($cacheItem)) { -- Gitee From e8987c69c7cd9b4bd8358edf58004f94abc736d3 Mon Sep 17 00:00:00 2001 From: 9007967 <33853639+9007967@users.noreply.github.com> Date: Fri, 23 Aug 2019 16:14:08 +0800 Subject: [PATCH 078/365] =?UTF-8?q?=E4=BF=AE=E5=A4=8D$startTime=E6=9C=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复未开启debug 时候 $startTime未定义 --- src/db/connector/Mongo.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 969b72a..6aa37e2 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -157,7 +157,9 @@ class Mongo extends Connection $this->links[$linkNum] = new Manager($config['dsn'], $config['params']); // 记录数据库连接信息 - $this->log('[ MongoDb ] CONNECT :[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); + if ($config['debug']) { + $this->log('[ MongoDb ] CONNECT :[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); + } } @@ -661,8 +663,9 @@ class Mongo extends Connection $manager = new Manager($this->buildUrl(), $this->config['params']); // 记录数据库连接信息 - $this->log('[ MongoDB ] ReplicaSet CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $this->config['dsn']); - + if ($this->config['debug']) { + $this->log('[ MongoDB ] ReplicaSet CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $this->config['dsn']); + } return $manager; } -- Gitee From 6ae099480a7649540b7f71b71a30395c8635d4aa Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 25 Aug 2019 14:36:57 +0800 Subject: [PATCH 079/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bfind=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E6=94=AF=E6=8C=81=E4=B8=8D=E5=B8=A6=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E4=BD=BF=E7=94=A8order=E6=96=B9=E6=B3=95=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 5515743..ec10fac 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -1446,7 +1446,7 @@ class BaseQuery $this->parsePkWhere($data); } - if (empty($this->options['where'])) { + if (empty($this->options['where']) && empty($this->options['order'])) { $result = []; } else { $result = $this->connection->find($this); -- Gitee From 224ad6302865b708ebcd03d90dc55fbd8937f5b1 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 27 Aug 2019 10:13:28 +0800 Subject: [PATCH 080/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=85=B3=E8=81=94?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E9=97=AD=E5=8C=85=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Relation.php | 8 ++++---- src/model/relation/BelongsTo.php | 6 +++--- src/model/relation/BelongsToMany.php | 8 ++++---- src/model/relation/HasMany.php | 8 ++++---- src/model/relation/HasManyThrough.php | 8 ++++---- src/model/relation/HasOne.php | 6 +++--- src/model/relation/HasOneThrough.php | 4 ++-- src/model/relation/MorphMany.php | 8 ++++---- src/model/relation/MorphOne.php | 4 ++-- src/model/relation/OneToOne.php | 2 +- 10 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/model/Relation.php b/src/model/Relation.php index e5bba56..12ab8a8 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -217,21 +217,21 @@ abstract class Relation } /** - * 执行关联的闭包查询 + * 判断闭包的参数类型 * @access protected * @return mixed */ - protected function callClosure(Closure $closure, ...$vars) + protected function getClosureType(Closure $closure) { $reflect = new ReflectionFunction($closure); $params = $reflect->getParameters(); if (!empty($params)) { $type = $params[0]->getType(); - array_unshift($vars, Relation::class == $type || is_null($type) ? $this : $this->query); + return Relation::class == $type || is_null($type) ? $this : $this->query; } - return $reflect->invokeArgs($vars); + return $this; } /** diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 6f7622a..25b9c11 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -55,7 +55,7 @@ class BelongsTo extends OneToOne public function getRelation(array $subRelation = [], Closure $closure = null) { if ($closure) { - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } $foreignKey = $this->foreignKey; @@ -85,7 +85,7 @@ class BelongsTo extends OneToOne public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', &$name = ''): string { if ($closure) { - $this->callClosure($closure, $name); + $closure($this->getClosureType($closure), $name); } return $this->query @@ -113,7 +113,7 @@ class BelongsTo extends OneToOne } if ($closure) { - $this->callClosure($closure, $name); + $closure($this->getClosureType($closure), $name); } return $this->query diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index bcc8569..68a771e 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -174,7 +174,7 @@ class BelongsToMany extends Relation public function getRelation(array $subRelation = [], Closure $closure = null): Collection { if ($closure) { - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } $result = $this->buildQuery() @@ -394,7 +394,7 @@ class BelongsToMany extends Relation $pk = $result->$pk; if ($closure) { - $this->callClosure($closure, $name); + $closure($this->getClosureType($closure), $name); } return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ @@ -414,7 +414,7 @@ class BelongsToMany extends Relation public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $this->callClosure($closure, $name); + $closure($this->getClosureType($closure), $name); } return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ @@ -437,7 +437,7 @@ class BelongsToMany extends Relation protected function eagerlyManyToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): array { if ($closure) { - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } // 预载入关联查询 支持嵌套预载入 diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index 25c919e..5a63d03 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -55,7 +55,7 @@ class HasMany extends Relation public function getRelation(array $subRelation = [], Closure $closure = null): Collection { if ($closure) { - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } if ($this->withLimit) { @@ -159,7 +159,7 @@ class HasMany extends Relation } if ($closure) { - $this->callClosure($closure, $name); + $closure($this->getClosureType($closure), $name); } return $this->query @@ -179,7 +179,7 @@ class HasMany extends Relation public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $this->callClosure($closure, $name); + $closure($this->getClosureType($closure), $name); } return $this->query->alias($aggregate . '_table') @@ -207,7 +207,7 @@ class HasMany extends Relation // 预载入关联查询 支持嵌套预载入 if ($closure) { $this->baseQuery = true; - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } $list = $this->query diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index 9121cc0..7909043 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -74,7 +74,7 @@ class HasManyThrough extends Relation public function getRelation(array $subRelation = [], Closure $closure = null) { if ($closure) { - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } $this->baseQuery(); @@ -258,7 +258,7 @@ class HasManyThrough extends Relation if ($closure) { $this->baseQuery = true; - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } $list = $this->query @@ -302,7 +302,7 @@ class HasManyThrough extends Relation } if ($closure) { - $this->callClosure($closure, $name); + $closure($this->getClosureType($closure), $name); } $alias = Str::snake(class_basename($this->model)); @@ -335,7 +335,7 @@ class HasManyThrough extends Relation public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $this->callClosure($closure, $name); + $closure($this->getClosureType($closure), $name); } $alias = Str::snake(class_basename($this->model)); diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 32cfab6..fef18ed 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -55,7 +55,7 @@ class HasOne extends OneToOne $localKey = $this->localKey; if ($closure) { - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } // 判断关联类型执行查询 @@ -84,7 +84,7 @@ class HasOne extends OneToOne public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $this->callClosure($closure, $name); + $closure($this->getClosureType($closure), $name); } return $this->query @@ -112,7 +112,7 @@ class HasOne extends OneToOne } if ($closure) { - $this->callClosure($closure, $name); + $closure($this->getClosureType($closure), $name); } return $this->query diff --git a/src/model/relation/HasOneThrough.php b/src/model/relation/HasOneThrough.php index 86aef67..2b4d57f 100644 --- a/src/model/relation/HasOneThrough.php +++ b/src/model/relation/HasOneThrough.php @@ -31,7 +31,7 @@ class HasOneThrough extends HasManyThrough public function getRelation(array $subRelation = [], Closure $closure = null) { if ($closure) { - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } $this->baseQuery(); @@ -145,7 +145,7 @@ class HasOneThrough extends HasManyThrough $keys = $this->through->where($where)->column($this->throughPk, $this->foreignKey); if ($closure) { - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } $list = $this->query diff --git a/src/model/relation/MorphMany.php b/src/model/relation/MorphMany.php index e58f13f..731560b 100644 --- a/src/model/relation/MorphMany.php +++ b/src/model/relation/MorphMany.php @@ -71,7 +71,7 @@ class MorphMany extends Relation public function getRelation(array $subRelation = [], Closure $closure = null): Collection { if ($closure) { - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } $this->baseQuery(); @@ -208,7 +208,7 @@ class MorphMany extends Relation } if ($closure) { - $this->callClosure($closure, $name); + $closure($this->getClosureType($closure), $name); } return $this->query @@ -231,7 +231,7 @@ class MorphMany extends Relation public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string { if ($closure) { - $this->callClosure($closure, $name); + $closure($this->getClosureType($closure), $name); } return $this->query @@ -258,7 +258,7 @@ class MorphMany extends Relation if ($closure) { $this->baseQuery = true; - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } $list = $this->query diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php index 995fc11..916a2ed 100644 --- a/src/model/relation/MorphOne.php +++ b/src/model/relation/MorphOne.php @@ -70,7 +70,7 @@ class MorphOne extends Relation public function getRelation(array $subRelation = [], Closure $closure = null) { if ($closure) { - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } $this->baseQuery(); @@ -210,7 +210,7 @@ class MorphOne extends Relation // 预载入关联查询 支持嵌套预载入 if ($closure) { $this->baseQuery = true; - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } $list = $this->query diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index 3b800f0..a30df2b 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -300,7 +300,7 @@ abstract class OneToOne extends Relation // 预载入关联查询 支持嵌套预载入 if ($closure) { $this->baseQuery = true; - $this->callClosure($closure); + $closure($this->getClosureType($closure)); } if ($this->withField) { -- Gitee From f3e2c11eb176bd89bb3c699b9b72b31b45bc9dc2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 29 Aug 2019 21:32:54 +0800 Subject: [PATCH 081/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95=20=E5=8F=96?= =?UTF-8?q?=E6=B6=88debug=E9=85=8D=E7=BD=AE=E5=8F=82=E6=95=B0=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9Etrigger=5Fsql=20=E5=92=8C=20fields=5Fcache=20=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 -- src/DbManager.php | 25 +++++++- src/db/Connection.php | 26 ++++++-- src/db/PDOConnection.php | 90 ++++++++------------------- src/db/connector/Mongo.php | 118 ++++++++++-------------------------- src/db/connector/Mysql.php | 51 ---------------- src/db/connector/Oracle.php | 7 ++- src/db/connector/Sqlsrv.php | 6 +- 8 files changed, 107 insertions(+), 220 deletions(-) diff --git a/README.md b/README.md index 3df901e..fb2020c 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,6 @@ Db::setConfig([ 'charset' => 'utf8', // 数据库表前缀 'prefix' => 'think_', - // 数据库调试模式 - 'debug' => true, ], 'mongo' => [ // 数据库类型 @@ -56,8 +54,6 @@ Db::setConfig([ 'password' => '', // 主键转换为Id 'pk_convert_id' => true, - // 数据库调试模式 - 'debug' => true, // 端口 'hostport' => '27017', ], diff --git a/src/DbManager.php b/src/DbManager.php index 6a81353..5b4835c 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -92,6 +92,8 @@ class DbManager */ protected function modelMaker() { + $this->triggerSql(); + Model::setDb($this); if (is_object($this->event)) { @@ -115,6 +117,27 @@ class DbManager }); } + /** + * 监听SQL + * @access protected + * @return void + */ + protected function triggerSql() + { + // 监听SQL + $this->listen(function ($sql, $time, $master) { + // 记录SQL + if (is_bool($master)) { + // 分布式记录当前操作的主从 + $master = $master ? 'master|' : 'slave|'; + } else { + $master = ''; + } + + $this->log($sql . ' [ ' . $master . 'RunTime:' . $time . 's ]'); + }); + } + /** * 初始化配置参数 * @access public @@ -155,7 +178,7 @@ class DbManager * @param string $type 日志类型 * @return void */ - public function log($log, $type = 'sql') + public function log(string $log, string $type = 'sql') { if ($this->log) { $this->log->log($type, $log); diff --git a/src/db/Connection.php b/src/db/Connection.php index 882c297..7b64bc9 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -212,6 +212,7 @@ abstract class Connection if ('' === $config) { return $this->config; } + return $this->config[$config] ?? null; } @@ -401,16 +402,29 @@ abstract class Connection abstract protected function initConnect(bool $master = true); /** - * 记录SQL日志 + * 数据库SQL监控 * @access protected - * @param string $log SQL日志信息 - * @param string $type 日志类型 + * @param string $sql 执行的SQL语句 留空自动获取 + * @param bool $master 主从标记 * @return void */ - protected function log($log, $type = 'sql') + protected function trigger(string $sql = '', bool $master = false): void { - if ($this->config['debug']) { - $this->db->log($log, $type); + $listen = $this->db->getListen(); + + if (!empty($listen)) { + $runtime = number_format((microtime(true) - $this->queryStartTime), 6); + $sql = $sql ?: $this->getLastsql(); + + if (empty($this->config['deploy'])) { + $master = null; + } + + foreach ($listen as $callback) { + if (is_callable($callback)) { + $callback($sql, $runtime, $master); + } + } } } diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index c946ec5..39fefca 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -52,8 +52,6 @@ abstract class PDOConnection extends Connection 'charset' => 'utf8', // 数据库表前缀 'prefix' => '', - // 数据库调试模式 - 'debug' => false, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) 'deploy' => 0, // 数据库读写是否分离 主从式有效 @@ -66,6 +64,10 @@ abstract class PDOConnection extends Connection 'read_master' => false, // 是否严格检查字段是否存在 'fields_strict' => true, + // 开启字段缓存 + 'fields_cache' => false, + // 监听SQL + 'trigger_sql' => true, // Builder类 'builder' => '', // Query类 @@ -295,13 +297,22 @@ abstract class PDOConnection extends Connection } if (!isset($this->info[$schema])) { - // 读取缓存 + // 读取字段缓存 $cacheFile = $this->config['schema_cache_path'] . $schema . '.php'; - if (!$this->config['debug'] && is_file($cacheFile)) { + if ($this->config['fields_cache'] && is_file($cacheFile)) { $info = include $cacheFile; } else { $info = $this->getFields($tableName); + + if ($this->config['fields_cache']) { + if (!is_dir($this->config['schema_cache_path'])) { + mkdir($this->config['schema_cache_path'], 0755, true); + } + + $content = 'parseDsn($config); } - $startTime = microtime(true); + $startTime = microtime(true); + $this->links[$linkNum] = $this->createPdo($config['dsn'], $config['username'], $config['password'], $params); // 记录数据库连接信息 - $this->log('CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); + $this->db->log('CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); return $this->links[$linkNum]; } catch (\PDOException $e) { if ($autoConnection) { - $this->log($e->getMessage(), 'error'); + $this->db->log($e->getMessage(), 'error'); return $this->connect($autoConnection, $linkNum); } else { throw $e; @@ -585,8 +597,7 @@ abstract class PDOConnection extends Connection $this->db->updateQueryTimes(); try { - // 调试开始 - $this->debug(true); + $this->queryStartTime = microtime(true); // 预处理 $this->PDOStatement = $this->linkID->prepare($sql); @@ -601,8 +612,10 @@ abstract class PDOConnection extends Connection // 执行查询 $this->PDOStatement->execute(); - // 调试结束 - $this->debug(false, '', $master); + // SQL监控 + if (!empty($this->config['trigger_sql'])) { + $this->trigger('', $master); + } return $this->PDOStatement; } catch (\Throwable | \Exception $e) { @@ -1474,61 +1487,6 @@ abstract class PDOConnection extends Connection return $error; } - /** - * 数据库调试 记录当前SQL及分析性能 - * @access protected - * @param boolean $start 调试开始标记 true 开始 false 结束 - * @param string $sql 执行的SQL语句 留空自动获取 - * @param bool $master 主从标记 - * @return void - */ - protected function debug(bool $start, string $sql = '', bool $master = false): void - { - if (!empty($this->config['debug'])) { - // 开启数据库调试模式 - if ($start) { - $this->queryStartTime = microtime(true); - } else { - // 记录操作结束时间 - $runtime = number_format((microtime(true) - $this->queryStartTime), 6); - $sql = $sql ?: $this->getLastsql(); - - // SQL监听 - $this->triggerSql($sql, $runtime, $master); - } - } - } - - /** - * 触发SQL事件 - * @access protected - * @param string $sql SQL语句 - * @param string $runtime SQL运行时间 - * @param bool $master 主从标记 - * @return void - */ - protected function triggerSql(string $sql, string $runtime, bool $master = false): void - { - $listen = $this->db->getListen(); - if (!empty($listen)) { - foreach ($listen as $callback) { - if (is_callable($callback)) { - $callback($sql, $runtime, $master); - } - } - } else { - if ($this->config['deploy']) { - // 分布式记录当前操作的主从 - $master = $master ? 'master|' : 'slave|'; - } else { - $master = ''; - } - - // 未注册监听则记录到日志中 - $this->log($sql . ' [ ' . $master . 'RunTime:' . $runtime . 's ]'); - } - } - /** * 初始化数据库连接 * @access protected diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 6aa37e2..da3bfd2 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -69,8 +69,6 @@ class Mongo extends Connection 'pk_type' => 'ObjectID', // 数据库表前缀 'prefix' => '', - // 数据库调试模式 - 'debug' => false, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) 'deploy' => 0, // 数据库读写是否分离 主从式有效 @@ -81,6 +79,10 @@ class Mongo extends Connection 'slave_no' => '', // 是否严格检查字段是否存在 'fields_strict' => true, + // 开启字段缓存 + 'fields_cache' => false, + // 监听SQL + 'trigger_sql' => true, // 自动写入时间戳字段 'auto_timestamp' => false, // 时间字段取出后的默认时间格式 @@ -150,16 +152,12 @@ class Mongo extends Connection $config['dsn'] = 'mongodb://' . ($config['username'] ? "{$config['username']}" : '') . ($config['password'] ? ":{$config['password']}@" : '') . $config['hostname'] . ($config['hostport'] ? ":{$config['hostport']}" : ''); } - if ($config['debug']) { - $startTime = microtime(true); - } + $startTime = microtime(true); $this->links[$linkNum] = new Manager($config['dsn'], $config['params']); // 记录数据库连接信息 - if ($config['debug']) { - $this->log('[ MongoDb ] CONNECT :[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); - } + $this->db->log('[ MongoDb ] CONNECT :[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); } @@ -232,7 +230,7 @@ class Mongo extends Connection $namespace = $this->dbName . '.' . $namespace; } - if ($this->config['debug'] && !empty($this->queryStr)) { + if (!empty($this->queryStr)) { // 记录执行指令 $this->queryStr = 'db' . strstr($namespace, '.') . '.' . $this->queryStr; } @@ -241,12 +239,15 @@ class Mongo extends Connection $mongoQuery = $mongoQuery($query); } - $readPreference = $options['readPreference'] ?? null; - $this->debug(true); + $readPreference = $options['readPreference'] ?? null; + $this->queryStartTime = microtime(true); $this->cursor = $this->mongo->executeQuery($namespace, $mongoQuery, $readPreference); - $this->debug(false); + // SQL监控 + if (!empty($this->config['trigger_sql'])) { + $this->trigger('', $master); + } return $this->cursor; } @@ -318,17 +319,20 @@ class Mongo extends Connection $namespace = $this->dbName . '.' . $namespace; } - if ($this->config['debug'] && !empty($this->queryStr)) { + if (!empty($this->queryStr)) { // 记录执行指令 $this->queryStr = 'db' . strstr($namespace, '.') . '.' . $this->queryStr; } - $writeConcern = $options['writeConcern'] ?? null; - $this->debug(true); + $writeConcern = $options['writeConcern'] ?? null; + $this->queryStartTime = microtime(true); $writeResult = $this->mongo->executeBulkWrite($namespace, $bulk, $writeConcern); - $this->debug(false); + // SQL监控 + if (!empty($this->config['trigger_sql'])) { + $this->trigger(); + } $this->numRows = $writeResult->getMatchedCount(); @@ -366,17 +370,20 @@ class Mongo extends Connection $this->initConnect(false); $this->db->updateQueryTimes(); - $this->debug(true); + $this->queryStartTime = microtime(true); $dbName = $dbName ?: $this->dbName; - if ($this->config['debug'] && !empty($this->queryStr)) { + if (!empty($this->queryStr)) { $this->queryStr = 'db.' . $this->queryStr; } $this->cursor = $this->mongo->executeCommand($dbName, $command, $readPreference); - $this->debug(false); + // SQL监控 + if (!empty($this->config['trigger_sql'])) { + $this->trigger('', $master); + } return $this->getResult($typeMap); } @@ -437,7 +444,7 @@ class Mongo extends Connection */ public function mongoLog(string $type, $data, array $options = []) { - if (!$this->config['debug']) { + if (!$this->config['trigger_sql']) { return; } @@ -491,62 +498,6 @@ class Mongo extends Connection return $this->queryStr; } - /** - * 触发SQL事件 - * @access protected - * @param string $sql SQL语句 - * @param string $runtime SQL运行时间 - * @param bool $master 主从标记 - * @return void - */ - protected function triggerSql(string $sql, string $runtime, bool $master = false): void - { - $listen = $this->db->getListen(); - - if (!empty($listen)) { - foreach ($listen as $callback) { - if (is_callable($callback)) { - $callback($sql, $runtime, $master); - } - } - } else { - // 未注册监听则记录到日志中 - if ($this->config['deploy']) { - // 分布式记录当前操作的主从 - $master = $master ? 'master|' : 'slave|'; - } else { - $master = ''; - } - $this->log('[ SQL ] ' . $sql . ' [' . $master . ' RunTime:' . $runtime . 's ]'); - } - } - - /** - * 数据库调试 记录当前SQL及分析性能 - * @access protected - * @param boolean $start 调试开始标记 true 开始 false 结束 - * @param string $sql 执行的SQL语句 留空自动获取 - * @param bool $master 主从标记 - * @return void - */ - protected function debug(bool $start, string $sql = '', bool $master = false) - { - if (!empty($this->config['debug'])) { - // 开启数据库调试模式 - if ($start) { - $this->queryStartTime = microtime(true); - } else { - // 记录操作结束时间 - $runtime = number_format((microtime(true) - $this->queryStartTime), 6); - - $sql = $sql ?: $this->queryStr; - - // SQL监听 - $this->triggerSql($sql, $runtime, $master); - } - } - } - /** * 释放查询结果 * @access public @@ -654,18 +605,16 @@ class Mongo extends Connection $this->dbName = $this->config['database']; $this->typeMap = $this->config['type_map']; - if ($this->config['debug']) { - $startTime = microtime(true); - } + $startTime = microtime(true); $this->config['params']['replicaSet'] = $this->config['database']; $manager = new Manager($this->buildUrl(), $this->config['params']); // 记录数据库连接信息 - if ($this->config['debug']) { - $this->log('[ MongoDB ] ReplicaSet CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $this->config['dsn']); - } + + $this->db->log('[ MongoDB ] ReplicaSet CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $this->config['dsn']); + return $manager; } @@ -1028,9 +977,8 @@ class Mongo extends Connection public function cmd(BaseQuery $query, $command, $extra = null, string $db = ''): array { if (is_array($command) || is_object($command)) { - if ($this->getConfig('debug')) { - $this->mongoLog('cmd', 'cmd', $command); - } + + $this->mongoLog('cmd', 'cmd', $command); // 直接创建Command对象 $command = new Command($command); diff --git a/src/db/connector/Mysql.php b/src/db/connector/Mysql.php index 70b1d47..e82f4f0 100644 --- a/src/db/connector/Mysql.php +++ b/src/db/connector/Mysql.php @@ -21,57 +21,6 @@ use think\db\PDOConnection; class Mysql extends PDOConnection { - /** - * 数据库连接参数配置 - * @var array - */ - protected $config = [ - // 数据库类型 - 'type' => 'mysql', - // 服务器地址 - 'hostname' => '127.0.0.1', - // 数据库名 - 'database' => '', - // 用户名 - 'username' => '', - // 密码 - 'password' => '', - // 端口 - 'hostport' => '', - // 连接dsn - 'dsn' => '', - // 数据库连接参数 - 'params' => [], - // 数据库编码默认采用utf8 - 'charset' => 'utf8', - // 数据库表前缀 - 'prefix' => '', - // 数据库调试模式 - 'debug' => false, - // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, - // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, - // 读写分离后 主服务器数量 - 'master_num' => 1, - // 指定从服务器序号 - 'slave_no' => '', - // 模型写入后自动读取主服务器 - 'read_master' => false, - // 是否严格检查字段是否存在 - 'fields_strict' => true, - // Builder类 - 'builder' => '', - // Query类 - 'query' => '', - // 是否需要断线重连 - 'break_reconnect' => false, - // 断线标识字符串 - 'break_match_str' => [], - // 字段缓存路径 - 'schema_cache_path' => '', - ]; - /** * 解析pdo连接的dsn信息 * @access protected diff --git a/src/db/connector/Oracle.php b/src/db/connector/Oracle.php index a89f3ed..8a4ef11 100644 --- a/src/db/connector/Oracle.php +++ b/src/db/connector/Oracle.php @@ -10,12 +10,12 @@ namespace think\db\connector; use PDO; -use think\db\Connection; +use think\db\PDOConnection; /** * Oracle数据库驱动 */ -class Oracle extends Connection +class Oracle extends PDOConnection { /** * 解析pdo连接的dsn信息 @@ -58,7 +58,8 @@ class Oracle extends Connection if ($result) { foreach ($result as $key => $val) { - $val = array_change_key_case($val); + $val = array_change_key_case($val); + $info[$val['column_name']] = [ 'name' => $val['column_name'], 'type' => $val['data_type'], diff --git a/src/db/connector/Sqlsrv.php b/src/db/connector/Sqlsrv.php index d220bcd..6e8c5c8 100644 --- a/src/db/connector/Sqlsrv.php +++ b/src/db/connector/Sqlsrv.php @@ -86,13 +86,11 @@ class Sqlsrv extends PDOConnection $sql = "SELECT column_name FROM information_schema.key_column_usage WHERE table_name='$tableName'"; - // 调试开始 - $this->debug(true); + $this->queryStartTime = microtime(true); $pdo = $this->linkID->query($sql); - // 调试结束 - $this->debug(false, $sql); + $this->trigger($sql); $result = $pdo->fetch(PDO::FETCH_ASSOC); -- Gitee From 49c525b43b2c91e7a8e4abe8a7f3eaae01c03897 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 30 Aug 2019 22:32:08 +0800 Subject: [PATCH 082/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 32 +++++++++ src/db/PDOConnection.php | 136 +++++++++++++++++++++++++++++---------- 2 files changed, 133 insertions(+), 35 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index ec10fac..8391930 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -52,6 +52,12 @@ class BaseQuery */ protected $pk; + /** + * 当前数据表自增主键 + * @var string + */ + protected $autoinc; + /** * 当前数据表前缀 * @var string @@ -1175,6 +1181,18 @@ class BaseQuery return $this; } + /** + * 指定数据表自增主键 + * @access public + * @param string $autoinc 自增键 + * @return $this + */ + public function autoinc(string $autoinc) + { + $this->autoinc = $autoinc; + return $this; + } + /** * 获取当前数据表的主键 * @access public @@ -1189,6 +1207,20 @@ class BaseQuery return $this->pk; } + /** + * 获取当前数据表的自增主键 + * @access public + * @return string + */ + public function getAutoInc() + { + if (empty($this->autoinc)) { + $this->autoinc = $this->connection->getAutoInc($this->getTable()); + } + + return $this->autoinc; + } + /** * 查询参数批量赋值 * @access protected diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 39fefca..aa71c7e 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -139,13 +139,15 @@ abstract class PDOConnection extends Connection * @var array */ protected $bindType = [ - 'string' => PDO::PARAM_STR, - 'str' => PDO::PARAM_STR, - 'integer' => PDO::PARAM_INT, - 'int' => PDO::PARAM_INT, - 'boolean' => PDO::PARAM_BOOL, - 'bool' => PDO::PARAM_BOOL, - 'float' => self::PARAM_FLOAT, + 'string' => PDO::PARAM_STR, + 'str' => PDO::PARAM_STR, + 'integer' => PDO::PARAM_INT, + 'int' => PDO::PARAM_INT, + 'boolean' => PDO::PARAM_BOOL, + 'bool' => PDO::PARAM_BOOL, + 'float' => self::PARAM_FLOAT, + 'datetime' => PDO::PARAM_STR, + 'timestamp' => PDO::PARAM_STR, ]; /** @@ -240,6 +242,33 @@ abstract class PDOConnection extends Connection return $info; } + /** + * 获取字段类型 + * @access protected + * @param string $type 字段类型 + * @return string + */ + protected function getFieldType(string $type): string + { + if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) { + $result = 'string'; + } elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) { + $result = 'float'; + } elseif (preg_match('/(int|serial|bit)/is', $type)) { + $result = 'int'; + } elseif (preg_match('/bool/is', $type)) { + $result = 'bool'; + } elseif (0 === strpos($type, 'timestamp')) { + $result = 'timestamp'; + } elseif (0 === strpos($type, 'datetime')) { + $result = 'datetime'; + } else { + $result = 'string'; + } + + return $result; + } + /** * 获取字段绑定类型 * @access public @@ -278,16 +307,11 @@ abstract class PDOConnection extends Connection $tableName = key($tableName) ?: current($tableName); } - if (strpos($tableName, ',')) { + if (strpos($tableName, ',') || strpos($tableName, ')')) { // 多表不获取字段信息 return []; } - // 修正子查询作为表名的问题 - if (strpos($tableName, ')')) { - return []; - } - list($tableName) = explode(' ', $tableName); if (!strpos($tableName, '.')) { @@ -303,8 +327,7 @@ abstract class PDOConnection extends Connection if ($this->config['fields_cache'] && is_file($cacheFile)) { $info = include $cacheFile; } else { - $info = $this->getFields($tableName); - + $info = $this->getTableFieldsInfo($tableName); if ($this->config['fields_cache']) { if (!is_dir($this->config['schema_cache_path'])) { mkdir($this->config['schema_cache_path'], 0755, true); @@ -315,30 +338,62 @@ abstract class PDOConnection extends Connection } } - $fields = array_keys($info); - $bind = $type = []; + $pk = $info['_pk'] ?? null; + $autoinc = $info['_autoinc'] ?? null; + unset($info['_pk'], $info['_autoinc']); - foreach ($info as $key => $val) { - // 记录字段类型 - $type[$key] = $val['type']; - $bind[$key] = $this->getFieldBindType($val['type']); + $bind = []; + foreach ($info as $name => $val) { + $bind[$name] = $this->getFieldBindType($val); + } - if (!empty($val['primary'])) { - $pk[] = $key; - } + $this->info[$schema] = [ + 'fields' => array_keys($info), + 'type' => $info, + 'bind' => $bind, + 'pk' => $pk, + 'autoinc' => $autoinc, + ]; + } + + return $fetch ? $this->info[$schema][$fetch] : $this->info[$schema]; + } + + /** + * 获取数据表的字段信息 + * @access public + * @param string $tableName 数据表名 + * @return array + */ + public function getTableFieldsInfo(string $tableName): array + { + $fields = $this->getFields($tableName); + $info = []; + + foreach ($fields as $key => $val) { + // 记录字段类型 + $info[$key] = $this->getFieldType($val['type']); + + if (!empty($val['primary'])) { + $pk[] = $key; } - if (isset($pk)) { - // 设置主键 - $pk = count($pk) > 1 ? $pk : $pk[0]; - } else { - $pk = null; + if (!empty($val['autoinc'])) { + $autoinc = $key; } + } - $this->info[$schema] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk]; + if (isset($pk)) { + // 设置主键 + $pk = count($pk) > 1 ? $pk : $pk[0]; + $info['_pk'] = $pk; } - return $fetch ? $this->info[$schema][$fetch] : $this->info[$schema]; + if (isset($autoinc)) { + $info['_autoinc'] = $autoinc; + } + + return $info; } /** @@ -352,6 +407,17 @@ abstract class PDOConnection extends Connection return $this->getTableInfo($tableName, 'pk'); } + /** + * 获取数据表的自增主键 + * @access public + * @param mixed $tableName 数据表名 + * @return string + */ + public function getAutoInc($tableName) + { + return $this->getTableInfo($tableName, 'autoinc'); + } + /** * 获取数据表字段信息 * @access public @@ -777,8 +843,8 @@ abstract class PDOConnection extends Connection $data = $options['data']; if ($lastInsId) { - $pk = $query->getPk(); - if (is_string($pk)) { + $pk = $query->getAutoInc(); + if ($pk) { $data[$pk] = $lastInsId; } } @@ -1441,9 +1507,9 @@ abstract class PDOConnection extends Connection */ protected function autoInsIDType(BaseQuery $query, string $insertId) { - $pk = $query->getPk(); + $pk = $query->getAutoInc(); - if (is_string($pk)) { + if ($pk) { $type = $this->getFieldBindType($pk); if (PDO::PARAM_INT == $type) { -- Gitee From d4253e6db5fd173ef7a5bafd28a6b1e6302aa713 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 1 Sep 2019 10:25:42 +0800 Subject: [PATCH 083/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 19 ++++++++----- src/db/Fetch.php | 2 +- src/db/PDOConnection.php | 58 ++++++++++++++++++++-------------------- 3 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index 5b4835c..4cc791c 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -190,11 +190,17 @@ class DbManager /** * 获得查询日志(没有设置日志对象使用) * @access public + * @param bool $clear 是否清空 * @return array */ - public function getDbLog(): array + public function getDbLog(bool $clear = false): array { - return $this->dbLog; + $logs = $this->dbLog; + if ($clear) { + $this->dbLog = []; + } + + return $logs; } /** @@ -345,7 +351,7 @@ class DbManager */ public function event(string $event, callable $callback): void { - $this->event[$event] = $callback; + $this->event[$event][] = $callback; } /** @@ -353,13 +359,14 @@ class DbManager * @access public * @param string $event 事件名 * @param mixed $params 传入参数 - * @param bool $once * @return mixed */ - public function trigger(string $event, $params = null, bool $once = false) + public function trigger(string $event, $params = null) { if (isset($this->event[$event])) { - return call_user_func_array($this->event[$event], [$this]); + foreach ($this->event[$event] as $callback) { + call_user_func_array($callback, [$this]); + } } } diff --git a/src/db/Fetch.php b/src/db/Fetch.php index 3d5bf76..38f797c 100644 --- a/src/db/Fetch.php +++ b/src/db/Fetch.php @@ -61,7 +61,7 @@ class Fetch { $this->query->parseOptions(); - $field = $aggregate . '(' . $this->builder->parseKey($this->query, $field) . ') AS tp_' . strtolower($aggregate); + $field = $aggregate . '(' . $this->builder->parseKey($this->query, $field) . ') AS think_' . strtolower($aggregate); return $this->value($field, 0, false); } diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index aa71c7e..c765d56 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -578,9 +578,9 @@ abstract class PDOConnection extends Connection /** * 执行查询 返回数据集 * @access public - * @param BaseQuery $query 查询对象 - * @param mixed $sql sql指令 - * @param array $bind 参数绑定 + * @param BaseQuery $query 查询对象 + * @param mixed $sql sql指令 + * @param array $bind 参数绑定 * @return array * @throws BindParamException * @throws \PDOException @@ -700,10 +700,10 @@ abstract class PDOConnection extends Connection /** * 执行语句 * @access public - * @param BaseQuery $query 查询对象 - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param bool $origin 是否原生查询 + * @param BaseQuery $query 查询对象 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param bool $origin 是否原生查询 * @return int * @throws BindParamException * @throws \PDOException @@ -821,8 +821,8 @@ abstract class PDOConnection extends Connection /** * 插入记录 * @access public - * @param BaseQuery $query 查询对象 - * @param boolean $getLastInsID 返回自增主键 + * @param BaseQuery $query 查询对象 + * @param boolean $getLastInsID 返回自增主键 * @return mixed */ public function insert(BaseQuery $query, bool $getLastInsID = false) @@ -864,9 +864,9 @@ abstract class PDOConnection extends Connection /** * 批量插入记录 * @access public - * @param BaseQuery $query 查询对象 - * @param mixed $dataSet 数据集 - * @param integer $limit 每次写入数据限制 + * @param BaseQuery $query 查询对象 + * @param mixed $dataSet 数据集 + * @param integer $limit 每次写入数据限制 * @return integer * @throws \Exception * @throws \Throwable @@ -910,9 +910,9 @@ abstract class PDOConnection extends Connection /** * 通过Select方式插入记录 * @access public - * @param BaseQuery $query 查询对象 - * @param array $fields 要插入的数据表字段名 - * @param string $table 要插入的数据表名 + * @param BaseQuery $query 查询对象 + * @param array $fields 要插入的数据表字段名 + * @param string $table 要插入的数据表名 * @return integer * @throws PDOException */ @@ -978,10 +978,10 @@ abstract class PDOConnection extends Connection /** * 得到某个字段的值 * @access public - * @param BaseQuery $query 查询对象 - * @param string $field 字段名 - * @param mixed $default 默认值 - * @param bool $one 返回一个值 + * @param BaseQuery $query 查询对象 + * @param string $field 字段名 + * @param mixed $default 默认值 + * @param bool $one 返回一个值 * @return mixed */ public function value(BaseQuery $query, string $field, $default = null, bool $one = true) @@ -1037,10 +1037,10 @@ abstract class PDOConnection extends Connection /** * 得到某个字段的值 * @access public - * @param BaseQuery $query 查询对象 - * @param string $aggregate 聚合方法 - * @param mixed $field 字段名 - * @param bool $force 强制转为数字类型 + * @param BaseQuery $query 查询对象 + * @param string $aggregate 聚合方法 + * @param mixed $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ public function aggregate(BaseQuery $query, string $aggregate, $field, bool $force = false) @@ -1049,7 +1049,7 @@ abstract class PDOConnection extends Connection list($distinct, $field) = explode(' ', $field); } - $field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS tp_' . strtolower($aggregate); + $field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS think_' . strtolower($aggregate); $result = $this->value($query, $field, 0, false); @@ -1059,9 +1059,9 @@ abstract class PDOConnection extends Connection /** * 得到某个列的数组 * @access public - * @param BaseQuery $query 查询对象 - * @param string $column 字段名 多个字段用逗号分隔 - * @param string $key 索引 + * @param BaseQuery $query 查询对象 + * @param string $column 字段名 多个字段用逗号分隔 + * @param string $key 索引 * @return array */ public function column(BaseQuery $query, string $column, string $key = ''): array @@ -1407,8 +1407,8 @@ abstract class PDOConnection extends Connection * 批处理的指令都认为是execute操作 * @access public * @param BaseQuery $query 查询对象 - * @param array $sqlArray SQL批处理指令 - * @param array $bind 参数绑定 + * @param array $sqlArray SQL批处理指令 + * @param array $bind 参数绑定 * @return bool */ public function batchQuery(BaseQuery $query, array $sqlArray = [], array $bind = []): bool -- Gitee From 26d04b93dc7aef655ff7eb4f743a27af0ea31ef4 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 1 Sep 2019 20:13:13 +0800 Subject: [PATCH 084/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=85=B3=E8=81=94?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 4 ++-- src/model/concern/RelationShip.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 6ea3dea..da9d367 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -406,12 +406,12 @@ trait ModelRelationQuery if (!empty($this->options['with'])) { // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with'], $withRelationAttr); + $result->eagerlyResultSet($resultSet, $this->options['with'], $withRelationAttr, false, $this->options['with_cache'] ?? false); } if (!empty($this->options['with_join'])) { // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with_join'], $withRelationAttr, true); + $result->eagerlyResultSet($resultSet, $this->options['with_join'], $withRelationAttr, true, $this->options['with_cache'] ?? false); } // 模型数据集转换 diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 10c9328..22a612b 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -255,7 +255,7 @@ trait RelationShip if (is_scalar($cache)) { $relationCache = [$cache]; } else { - $relationCache = $cache[$relationName] ?? []; + $relationCache = $cache[$relationName] ?? $cache; } $relationResult->eagerlyResultSet($resultSet, $relation, $subRelation, $closure, $relationCache, $join); -- Gitee From ff2f296207eec09375d782756c52b4b4fa82b61d Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 1 Sep 2019 21:20:04 +0800 Subject: [PATCH 085/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BwithJoin=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/JoinAndViewQuery.php | 54 --------------------------- src/db/concern/ModelRelationQuery.php | 49 +++++++++++++++++++++++- src/model/concern/RelationShip.php | 25 +++++++++++++ src/model/relation/OneToOne.php | 3 +- 4 files changed, 75 insertions(+), 56 deletions(-) diff --git a/src/db/concern/JoinAndViewQuery.php b/src/db/concern/JoinAndViewQuery.php index ad46d08..cd48ab2 100644 --- a/src/db/concern/JoinAndViewQuery.php +++ b/src/db/concern/JoinAndViewQuery.php @@ -12,10 +12,8 @@ declare (strict_types = 1); namespace think\db\concern; -use Closure; use think\db\Raw; use think\helper\Str; -use think\model\relation\OneToOne; /** * JOIN和VIEW查询 @@ -131,58 +129,6 @@ trait JoinAndViewQuery return $table; } - /** - * 关联预载入 JOIN方式 - * @access protected - * @param array|string $with 关联方法名 - * @param string $joinType JOIN方式 - * @return $this - */ - public function withJoin($with, string $joinType = '') - { - if (empty($with)) { - return $this; - } - - $first = true; - - /** @var Model $class */ - $class = $this->model; - foreach ((array) $with as $key => $relation) { - $closure = null; - $field = true; - - if ($relation instanceof Closure) { - // 支持闭包查询过滤关联条件 - $closure = $relation; - $relation = $key; - } elseif (is_array($relation)) { - $field = $relation; - $relation = $key; - } elseif (is_string($relation) && strpos($relation, '.')) { - $relation = strstr($relation, '.', true); - } - - /** @var Relation $model */ - $relation = Str::camel($relation); - $model = $class->$relation(); - - if ($model instanceof OneToOne) { - $model->eagerly($this, $relation, $field, $joinType, $closure, $first); - $first = false; - } else { - // 不支持其它关联 - unset($with[$key]); - } - } - - $this->via(); - - $this->options['with_join'] = $with; - - return $this; - } - /** * 指定JOIN查询字段 * @access public diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index da9d367..aea9368 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -183,7 +183,7 @@ trait ModelRelationQuery } /** - * 设置关联查询JOIN预查询 + * 关联预载入 In方式 * @access public * @param array|string $with 关联方法名称 * @return $this @@ -197,6 +197,53 @@ trait ModelRelationQuery return $this; } + /** + * 关联预载入 JOIN方式 + * @access protected + * @param array|string $with 关联方法名 + * @param string $joinType JOIN方式 + * @return $this + */ + public function withJoin($with, string $joinType = '') + { + if (empty($with)) { + return $this; + } + + $with = (array) $with; + $first = true; + + foreach ($with as $key => $relation) { + $closure = null; + $field = true; + + if ($relation instanceof Closure) { + // 支持闭包查询过滤关联条件 + $closure = $relation; + $relation = $key; + } elseif (is_array($relation)) { + $field = $relation; + $relation = $key; + } elseif (is_string($relation) && strpos($relation, '.')) { + $relation = strstr($relation, '.', true); + } + + $result = $this->model->eagerly($this, $relation, $field, $joinType, $closure, $first); + + if (!$result) { + unset($with[$key]); + } else { + $first = false; + } + } + + $this->via(); + + $this->options['with_join'] = $with; + + return $this; + } + /** * 关联统计 * @access protected diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 22a612b..63af9f5 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -28,6 +28,7 @@ use think\model\relation\HasOneThrough; use think\model\relation\MorphMany; use think\model\relation\MorphOne; use think\model\relation\MorphTo; +use think\model\relation\OneToOne; /** * 模型关联处理 @@ -213,6 +214,30 @@ trait RelationShip ->hasWhere($where, $fields, $joinType, $query); } + /** + * 预载入关联查询 JOIN方式 + * @access public + * @param Query $query Query对象 + * @param string $relation 关联方法名 + * @param mixed $field 字段 + * @param string $joinType JOIN类型 + * @param Closure $closure 闭包 + * @param bool $first + * @return bool + */ + public function eagerly(Query $query, string $relation, $field, string $joinType = '', Closure $closure = null, bool $first = false): bool + { + $relation = Str::camel($relation); + $class = $this->$relation(); + + if ($class instanceof OneToOne) { + $class->eagerly($query, $relation, $field, $joinType, $closure, $first); + return true; + } else { + return false; + } + } + /** * 预载入关联查询 返回数据集 * @access public diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index a30df2b..a22aba0 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -98,7 +98,8 @@ abstract class OneToOne extends Relation if ($closure) { // 执行闭包查询 - $closure($query); + $closure($this->getClosureType($closure)); + // 使用withField指定获取关联的字段 if ($this->withField) { $field = $this->withField; -- Gitee From 5cc9a89b7aa8c0fce3586ee4b400e2b05806b93d Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 1 Sep 2019 22:11:39 +0800 Subject: [PATCH 086/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BwithCount=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 26 ++------------------------ src/model/concern/RelationShip.php | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index aea9368..20d2bde 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -262,29 +262,7 @@ trait ModelRelationQuery $this->field('*'); } - foreach ((array) $relations as $key => $relation) { - $closure = $aggregateField = null; - - if ($relation instanceof Closure) { - $closure = $relation; - $relation = $key; - } elseif (!is_int($key)) { - $aggregateField = $relation; - $relation = $key; - } - - $relation = Str::camel($relation); - - $count = $this->model - ->$relation() - ->getRelationCountQuery($closure, $aggregate, $field, $aggregateField); - - if (empty($aggregateField)) { - $aggregateField = Str::snake($relation) . '_' . $aggregate; - } - - $this->field(['(' . $count . ')' => $aggregateField]); - } + $this->model->relationCount($this, (array) $relations, $aggregate, $field, true); } return $this; @@ -530,7 +508,7 @@ trait ModelRelationQuery // 关联统计 if (!empty($options['with_count'])) { foreach ($options['with_count'] as $val) { - $result->relationCount($result, (array) $val[0], $val[1], $val[2]); + $result->relationCount($this, (array) $val[0], $val[1], $val[2], false); } } } diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 63af9f5..1a79a73 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -365,13 +365,13 @@ trait RelationShip /** * 关联统计 * @access public - * @param Model $result 数据对象 + * @param Query $query 查询对象 * @param array $relations 关联名 * @param string $aggregate 聚合查询方法 * @param string $field 字段 * @return void */ - public function relationCount(Model $result, array $relations, string $aggregate = 'sum', string $field = '*'): void + public function relationCount(Query $query, array $relations, string $aggregate = 'sum', string $field = '*', bool $useSubQuery = true): void { foreach ($relations as $key => $relation) { $closure = $name = null; @@ -385,13 +385,22 @@ trait RelationShip } $relation = Str::camel($relation); - $count = $this->$relation()->relationCount($result, $closure, $aggregate, $field, $name); + + if ($useSubQuery) { + $count = $this->$relation()->getRelationCountQuery($closure, $aggregate, $field, $name); + } else { + $count = $this->$relation()->relationCount($this, $closure, $aggregate, $field, $name); + } if (empty($name)) { $name = Str::snake($relation) . '_' . $aggregate; } - $result->setAttr($name, $count); + if ($useSubQuery) { + $query->field(['(' . $count . ')' => $name]); + } else { + $this->setAttr($name, $count); + } } } -- Gitee From 49c18c9ba68403451031f6afac9c6091cb071115 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 2 Sep 2019 14:01:06 +0800 Subject: [PATCH 087/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E5=AF=B9=E9=97=AD=E5=8C=85=E7=9A=84=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 2 +- src/db/BaseQuery.php | 13 ++++++++++++- src/db/Connection.php | 2 +- src/db/Mongo.php | 2 +- src/db/PDOConnection.php | 17 ----------------- src/model/concern/RelationShip.php | 9 +++++---- 6 files changed, 20 insertions(+), 25 deletions(-) diff --git a/composer.json b/composer.json index 5a5115a..82be76a 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": ">=7.1.0", "psr/simple-cache": "^1.0", - "psr/log": "~1.0", + "psr/log": "~1.0", "topthink/think-helper":"^3.1" }, "autoload": { diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 8391930..ba16451 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -494,7 +494,7 @@ class BaseQuery // 延迟写入 $condition = $this->options['where'] ?? []; - $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); + $guid = md5($this->getTable() . '_' . $field . '_' . $this->getQueryGuid($condition)); $step = $this->connection->lazyWrite($op, $guid, $step, $lazyTime); if (false === $step) { @@ -1248,6 +1248,17 @@ class BaseQuery return $this->options[$name] ?? null; } + /** + * 获取当前的查询标识 + * @access public + * @param mixed $data 要序列化的数据 + * @return string + */ + public function getQueryGuid($data = null): string + { + return md5($this->getConfig('database') . serialize(var_export($data ?: $this->options, true)) . serialize($this->getBind(false))); + } + /** * 设置当前的查询参数 * @access public diff --git a/src/db/Connection.php b/src/db/Connection.php index 7b64bc9..e47cec2 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -453,7 +453,7 @@ abstract class Connection if (!empty($query->getOptions('key'))) { $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); } else { - $key = md5($this->getConfig('database') . serialize($query->getOptions())); + $key = $query->getQueryGuid(); } return $key; diff --git a/src/db/Mongo.php b/src/db/Mongo.php index 79cada8..94f919b 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -195,7 +195,7 @@ class Mongo extends BaseQuery // 延迟写入 $condition = $this->options['where'] ?? []; - $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); + $guid = md5($this->getTable() . '_' . $field . '_' . $this->getQueryGuid($condition)); $step = $this->connection->lazyWrite($op, $guid, $step, $lazyTime); if (false === $step) { diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index c765d56..430c4bf 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1634,21 +1634,4 @@ abstract class PDOConnection extends Connection return $this->connect($dbConfig, $r, $r == $m ? false : $dbMaster); } - /** - * 分析缓存Key - * @access protected - * @param BaseQuery $query 查询对象 - * @return string - */ - protected function getCacheKey(BaseQuery $query): string - { - if (!empty($query->getOptions('key'))) { - $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); - } else { - $key = md5($this->getConfig('database') . serialize($query->getOptions()) . serialize($query->getBind(false))); - } - - return $key; - } - } diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 1a79a73..ad9be80 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -365,10 +365,11 @@ trait RelationShip /** * 关联统计 * @access public - * @param Query $query 查询对象 - * @param array $relations 关联名 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 + * @param Query $query 查询对象 + * @param array $relations 关联名 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param bool $useSubQuery 子查询 * @return void */ public function relationCount(Query $query, array $relations, string $aggregate = 'sum', string $field = '*', bool $useSubQuery = true): void -- Gitee From ea63da7e0bb8c7892f7e5a867fb074873a4d3acd Mon Sep 17 00:00:00 2001 From: 9007967 <33853639+9007967@users.noreply.github.com> Date: Thu, 5 Sep 2019 10:07:50 +0800 Subject: [PATCH 088/365] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BC=BA=E5=B0=91=20?= =?UTF-8?q?$master?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复缺少 $master --- src/db/connector/Mongo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index da3bfd2..39c64ef 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -218,7 +218,7 @@ class Mongo extends Connection * @throws ConnectionException * @throws RuntimeException */ - public function getCursor(BaseQuery $query, $mongoQuery): Cursor + public function getCursor(BaseQuery $query, $mongoQuery, bool $master = false): Cursor { $this->initConnect(false); $this->db->updateQueryTimes(); @@ -365,7 +365,7 @@ class Mongo extends Connection * @throws ConnectionException * @throws RuntimeException */ - public function command(Command $command, string $dbName = '', ReadPreference $readPreference = null, $typeMap = null): array + public function command(Command $command, string $dbName = '', ReadPreference $readPreference = null, $typeMap = null, bool $master = false): array { $this->initConnect(false); $this->db->updateQueryTimes(); -- Gitee From e3746277f9f8972f21c3adfad2f2014b75335495 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 10 Sep 2019 07:40:43 +0800 Subject: [PATCH 089/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=B8=BB=E4=BB=8E?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/connector/Mongo.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 39c64ef..338cd8a 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -203,8 +203,10 @@ class Mongo extends Connection // 生成MongoQuery对象 $mongoQuery = $this->builder->select($query); + $master = $query->getOptions('master') ? true : false; + // 执行查询操作 - return $this->getCursor($query, $mongoQuery); + return $this->getCursor($query, $mongoQuery, $master); } /** @@ -212,6 +214,7 @@ class Mongo extends Connection * @access public * @param BaseQuery $query 查询对象 * @param MongoQuery|Closure $mongoQuery Mongo查询对象 + * @param bool $master 是否主库操作 * @return Cursor * @throws AuthenticationException * @throws InvalidArgumentException @@ -220,7 +223,7 @@ class Mongo extends Connection */ public function getCursor(BaseQuery $query, $mongoQuery, bool $master = false): Cursor { - $this->initConnect(false); + $this->initConnect($master); $this->db->updateQueryTimes(); $options = $query->getOptions(); @@ -281,7 +284,8 @@ class Mongo extends Connection $mongoQuery = $mongoQuery($query); } - $this->getCursor($query, $mongoQuery); + $master = $query->getOptions('master') ? true : false; + $this->getCursor($query, $mongoQuery, $master); $resultSet = $this->getResult($options['typeMap']); @@ -359,6 +363,7 @@ class Mongo extends Connection * @param string $dbName 当前数据库名 * @param ReadPreference $readPreference readPreference * @param string|array $typeMap 指定返回的typeMap + * @param bool $master 是否主库操作 * @return array * @throws AuthenticationException * @throws InvalidArgumentException @@ -367,7 +372,7 @@ class Mongo extends Connection */ public function command(Command $command, string $dbName = '', ReadPreference $readPreference = null, $typeMap = null, bool $master = false): array { - $this->initConnect(false); + $this->initConnect($master); $this->db->updateQueryTimes(); $this->queryStartTime = microtime(true); -- Gitee From e33dbc0fb9f2b2ecb8050ee485b17b8085a54916 Mon Sep 17 00:00:00 2001 From: auooru Date: Tue, 10 Sep 2019 21:41:35 +0800 Subject: [PATCH 090/365] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E7=B1=BB=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 5 +++-- src/Paginator.php | 11 ++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 82be76a..1a5b9e4 100644 --- a/composer.json +++ b/composer.json @@ -14,8 +14,9 @@ ], "require": { "php": ">=7.1.0", - "psr/simple-cache": "^1.0", - "psr/log": "~1.0", + "ext-json": "*", + "psr/simple-cache": "^1.0", + "psr/log": "~1.0", "topthink/think-helper":"^3.1" }, "autoload": { diff --git a/src/Paginator.php b/src/Paginator.php index 25be679..2ae2aae 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -42,13 +42,13 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 当前页 - * @var integer + * @var int */ protected $currentPage; /** * 最后一页 - * @var integer + * @var int */ protected $lastPage; @@ -60,7 +60,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 每页数量 - * @var integer + * @var int */ protected $listRows; @@ -351,6 +351,11 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J return $this->items->all(); } + /** + * 获取数据集 + * + * @return Collection|\think\model\Collection + */ public function getCollection() { return $this->items; -- Gitee From f59495e1dcce6ab53a27c20e0c7583c09c0ce294 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 12 Sep 2019 16:19:29 +0800 Subject: [PATCH 091/365] =?UTF-8?q?readme=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 242 ++++-------------------------------------------------- 1 file changed, 17 insertions(+), 225 deletions(-) diff --git a/README.md b/README.md index fb2020c..c65afe9 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,20 @@ -# think-orm +# ThinkORM -ThinkPHP6内置ORM,基于PHP7.1+ 的ORM实现,主要特性: +基于PHP7.1+ 和PDO实现的ORM,支持多数据库,2.0版本主要特性包括: -- 支持Mysql、Pgsql、Sqlite、SqlServer、Oracle和Mongodb -- 支持Db类和查询构造器 -- 支持事务 -- 支持模型和关联 -- 事件支持依赖注入 -- 支持使用Db门面对象 -- 支持查询缓存 +* 基于PDO和PHP强类型实现 +* 支持原生查询和查询构造器 +* 自动参数绑定和预查询 +* 简洁易用的查询功能 +* 强大灵活的模型用法 +* 支持预载入关联查询和延迟关联查询 +* 支持多数据库及动态切换 +* 支持`MongoDb` +* 支持分布式及事务 +* 支持断点重连 +* 支持`JSON`查询 +* 支持数据库日志 +* 支持`PSR-16`缓存及`PSR-3`日志规范 ## 安装 @@ -16,220 +22,6 @@ ThinkPHP6内置ORM,基于PHP7.1+ 的ORM实现,主要特性: composer require topthink/think-orm ~~~ -## 使用 +## 文档 -Db类: -~~~php -use think\facade\Db; -// 数据库配置信息设置(全局有效) -Db::setConfig([ - // 默认数据连接标识 - 'default' => 'mysql', - // 数据库连接信息 - 'connections' => [ - 'mysql' => [ - // 数据库类型 - 'type' => 'mysql', - // 主机地址 - 'hostname' => '127.0.0.1', - // 用户名 - 'username' => 'root', - // 数据库名 - 'database' => 'demo', - // 数据库编码默认采用utf8 - 'charset' => 'utf8', - // 数据库表前缀 - 'prefix' => 'think_', - ], - 'mongo' => [ - // 数据库类型 - 'type' => 'mongo', - // 服务器地址 - 'hostname' => '127.0.0.1', - // 数据库名 - 'database' => 'demo', - // 用户名 - 'username' => '', - // 密码 - 'password' => '', - // 主键转换为Id - 'pk_convert_id' => true, - // 端口 - 'hostport' => '27017', - ], - ], -]); -// 进行CURD操作 -Db::table('user') - ->data(['name'=>'thinkphp','email'=>'thinkphp@qq.com']) - ->insert(); -Db::table('user')->find(); -Db::table('user') - ->where('id','>',10) - ->order('id','desc') - ->limit(10) - ->select(); -Db::table('user') - ->where('id',10) - ->update(['name'=>'test']); -Db::table('user') - ->where('id',10) - ->delete(); -// 获取数据库SQL日志记录 -Db::getSqlLog(); -~~~ - -其它操作参考TP6.0的完全开发手册[数据库](https://www.kancloud.cn/manual/thinkphp6_0/1037530)章节 - -模型: -~~~php -namespace app\index\model; - -use think\Model; - -class User extends Model -{ -} -~~~ - -代码调用: - -~~~php -use app\index\model\User; - -$user = User::find(1); -$user->name = 'thinkphp'; -$user->save(); -~~~ - -## Db类和模型对比使用 -#### :white_check_mark: 创建Create -* Db用法 - - ```php - Db::table('user') - ->insert([ - 'name' => 'thinkphp', - 'email' => 'thinkphp@qq.com', - ]); - ``` -* 模型用法 - - ```php - $user = new User; - $user->name = 'thinkphp'; - $user->email = 'thinkphp@qq.com'; - $user->save(); - ``` -* 或者批量设置 - - ```php - $user = new User; - $user->save([ - 'name' => 'thinkphp', - 'email' => 'thinkphp@qq.com', - ]); - ``` -#### :white_check_mark: 读取Read -* Db用法 - - ```php - $user = Db::table('user') - ->where('id', 1) - ->find(); - // 或者 - $user = Db::table('user') - ->find(1); - echo $user['id']; - echo $user['name']; - ``` -* 模型用法 - - ```php - $user = User::find(1); - echo $user->id; - echo $user->name; - ``` -* 模型实现读取多个记录 - - ```php - // 查询用户数据集 - $users = User::where('id', '>', 1) - ->limit(5) - ->select(); - - // 遍历读取用户数据 - foreach ($users as $user) { - echo $user->id; - echo $user->name; - } - ``` -#### :white_check_mark: 更新Update -* Db用法 - - ```php - Db::table('user') - ->where('id', 1) - ->update([ - 'name' => 'topthink', - 'email' => 'topthink@qq.com', - ]); - ``` -* 模型用法 - - ```php - $user = User::find(1); - $user->name = 'topthink'; - $user->email = 'topthink@qq.com'; - $user->save(); - ``` -* 或者使用 - - ```php - $user = User::find(1); - $user->save([ - 'name' => 'topthink', - 'email' => 'topthink@qq.com', - ]); - ``` -* 静态调用 - - ```php - User::update([ - 'name' => 'topthink', - 'email' => 'topthink@qq.com', - ], ['id' => 1]); - ``` -#### :white_check_mark: 删除Delete -* Db用法 - - ```php - Db::table('user')->delete(1); - ``` -* 模型用法 - - ```php - $user = User::find(1); - $user->delete(); - ``` -* 或者静态实现 - - ```php - User::destroy(1); - ``` -* destroy方法支持删除指定主键或者查询条件的数据 - - ```php - // 根据主键删除多个数据 - User::destroy([1, 2, 3]); - // 指定条件删除数据 - User::destroy([ - 'status' => 0, - ]); - // 使用闭包条件 - User::destroy(function ($query) { - $query->where('id', '>', 0) - ->where('status', 0); - }); - ``` -更多模型用法可以参考6.0完全开发手册的[模型](https://www.kancloud.cn/manual/thinkphp6_0/1037579)章节 +详细参考 [ThinkORM开发指南](https://www.kancloud.cn/manual/think-orm/content) -- Gitee From cebecbd75630b0f94e7317200b5bc07521e20080 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 15 Sep 2019 14:52:45 +0800 Subject: [PATCH 092/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=97=AD=E5=8C=85?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=9D=A1=E4=BB=B6=E7=9A=84=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E7=BB=91=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Builder.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/db/Builder.php b/src/db/Builder.php index def81f5..a6b4e6d 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -368,7 +368,7 @@ abstract class Builder if ($value instanceof Closure) { // 使用闭包查询 - $where[] = $this->parseClousreWhere($query, $value, $logic); + $where[] = $this->parseClosureWhere($query, $value, $logic); } elseif (is_array($field)) { $where[] = $this->parseMultiWhereField($query, $value, $field, $logic, $binds); } elseif ($field instanceof Raw) { @@ -437,14 +437,15 @@ abstract class Builder * @param string $logic Logic * @return string */ - protected function parseClousreWhere(Query $query, Closure $value, string $logic): string + protected function parseClosureWhere(Query $query, Closure $value, string $logic): string { $newQuery = $query->newQuery()->setConnection($this->connection); $value($newQuery); - $whereClause = $this->buildWhere($query, $newQuery->getOptions('where') ?: []); + $whereClosure = $this->buildWhere($newQuery, $newQuery->getOptions('where') ?: []); - if (!empty($whereClause)) { - $where = ' ' . $logic . ' ( ' . $whereClause . ' )'; + if (!empty($whereClosure)) { + $query->bind($newQuery->getBind(false)); + $where = ' ' . $logic . ' ( ' . $whereClosure . ' )'; } return $where ?? ''; -- Gitee From 7488c77265aa4a5fe948e1e71bc8c74988e8381d Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 15 Sep 2019 15:08:19 +0800 Subject: [PATCH 093/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E7=B1=BBgetWhere=E6=96=B9=E6=B3=95=E5=AF=B9=E5=A4=8D=E5=90=88?= =?UTF-8?q?=E4=B8=BB=E9=94=AE=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Model.php b/src/Model.php index a0c2672..d1a9710 100644 --- a/src/Model.php +++ b/src/Model.php @@ -677,10 +677,16 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab if (is_string($pk) && isset($this->data[$pk])) { $where = [[$pk, '=', $this->data[$pk]]]; - } elseif (!empty($this->updateWhere)) { - $where = $this->updateWhere; - } else { - $where = null; + } elseif (is_array($pk)) { + foreach ($pk as $field) { + if (isset($this->data[$field])) { + $where[] = [$field, '=', $this->data[$field]]; + } + } + } + + if (empty($where)) { + $where = empty($this->updateWhere) ? null : $this->updateWhere; } return $where; -- Gitee From 759cc5848dbd3d5b9662040e135011198e882146 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 16 Sep 2019 09:27:31 +0800 Subject: [PATCH 094/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bpk=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E4=BC=A0=E5=85=A5=E6=95=B0=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index ba16451..6ea04a8 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -1172,10 +1172,10 @@ class BaseQuery /** * 指定数据表主键 * @access public - * @param string $pk 主键 + * @param string|array $pk 主键 * @return $this */ - public function pk(string $pk) + public function pk($pk) { $this->pk = $pk; return $this; -- Gitee From 045c669eb7468a618b10ccee9bf88e6b74918b94 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 17 Sep 2019 09:05:11 +0800 Subject: [PATCH 095/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bsqlsrv=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/connector/Sqlsrv.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/db/connector/Sqlsrv.php b/src/db/connector/Sqlsrv.php index 6e8c5c8..1a5fffe 100644 --- a/src/db/connector/Sqlsrv.php +++ b/src/db/connector/Sqlsrv.php @@ -84,14 +84,8 @@ class Sqlsrv extends PDOConnection } } - $sql = "SELECT column_name FROM information_schema.key_column_usage WHERE table_name='$tableName'"; - - $this->queryStartTime = microtime(true); - - $pdo = $this->linkID->query($sql); - - $this->trigger($sql); - + $sql = "SELECT column_name FROM information_schema.key_column_usage WHERE table_name='$tableName'"; + $pdo = $this->linkID->query($sql); $result = $pdo->fetch(PDO::FETCH_ASSOC); if ($result) { -- Gitee From fc80730c6b617ce5c3c58bef44bf36ddf19955f5 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 17 Sep 2019 09:26:27 +0800 Subject: [PATCH 096/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bsql=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 7 ++++++- src/db/PDOConnection.php | 7 +++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index 4cc791c..4ee1bdf 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -122,10 +122,15 @@ class DbManager * @access protected * @return void */ - protected function triggerSql() + protected function triggerSql(): void { // 监听SQL $this->listen(function ($sql, $time, $master) { + if (0 === strpos($sql, 'CONNECT:')) { + $this->log($sql); + return; + } + // 记录SQL if (is_bool($master)) { // 分布式记录当前操作的主从 diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 430c4bf..29cbabe 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -501,8 +501,11 @@ abstract class PDOConnection extends Connection $startTime = microtime(true); $this->links[$linkNum] = $this->createPdo($config['dsn'], $config['username'], $config['password'], $params); - // 记录数据库连接信息 - $this->db->log('CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); + + // SQL监控 + if (!empty($config['trigger_sql'])) { + $this->trigger('CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); + } return $this->links[$linkNum]; } catch (\PDOException $e) { -- Gitee From f88de9e99b418ddb23b316268c46652ff83bdb4f Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 23 Sep 2019 19:34:18 +0800 Subject: [PATCH 097/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0ConnectionInterface?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 13 +- src/db/BaseQuery.php | 275 -------------------------------- src/db/Connection.php | 259 +------------------------------ src/db/ConnectionInterface.php | 216 ++++++++++++++++++++++++++ src/db/Mongo.php | 28 ++-- src/db/PDOConnection.php | 64 ++++++-- src/db/concern/PDOQuery.php | 276 ++++++++++++++++++++++++++++++++- src/db/concern/ParamsBind.php | 2 +- src/db/connector/Mongo.php | 68 +++----- 9 files changed, 589 insertions(+), 612 deletions(-) create mode 100644 src/db/ConnectionInterface.php diff --git a/src/DbManager.php b/src/DbManager.php index 4ee1bdf..e80f637 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -234,11 +234,6 @@ class DbManager public function connect(string $name = null, bool $force = false): BaseQuery { $connection = $this->instance($name, $force); - $connection->setDb($this); - - if ($this->cache) { - $connection->setCache($this->cache); - } $class = $connection->getQueryClass(); $query = new $class($connection); @@ -279,7 +274,13 @@ class DbManager $class = '\\think\\db\\connector\\' . ucfirst($type); } - $this->instance[$name] = new $class($config); + $connection = new $class($config); + $connection->setDb($this); + + if ($this->cache) { + $connection->setCache($this->cache); + } + $this->instance[$name] = $connection; } return $this->instance[$name]; diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 6ea04a8..9c79f52 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -13,7 +13,6 @@ declare (strict_types = 1); namespace think\db; use think\Collection; -use think\db\exception\BindParamException; use think\db\exception\DataNotFoundException; use think\db\exception\DbException as Exception; use think\db\exception\ModelNotFoundException; @@ -215,44 +214,6 @@ class BaseQuery return $this->prefix . Str::snake($name); } - /** - * 执行查询 返回数据集 - * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @return array - * @throws BindParamException - * @throws PDOException - */ - public function query(string $sql, array $bind = []): array - { - return $this->connection->query($this, $sql, $bind); - } - - /** - * 执行语句 - * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @return int - * @throws BindParamException - * @throws PDOException - */ - public function execute(string $sql, array $bind = []): int - { - return $this->connection->execute($this, $sql, $bind, true); - } - - /** - * 获取返回或者影响的记录数 - * @access public - * @return integer - */ - public function getNumRows(): int - { - return $this->connection->getNumRows(); - } - /** * 获取最近一次查询的sql语句 * @access public @@ -274,18 +235,6 @@ class BaseQuery return $this->connection->getLastInsID($this, $sequence); } - /** - * 批处理执行SQL语句 - * 批处理的指令都认为是execute操作 - * @access public - * @param array $sql SQL批处理指令 - * @return bool - */ - public function batchQuery(array $sql = []): bool - { - return $this->connection->batchQuery($this, $sql); - } - /** * 得到某个字段的值 * @access public @@ -813,30 +762,6 @@ class BaseQuery return $this; } - /** - * USING支持 用于多表删除 - * @access public - * @param mixed $using USING - * @return $this - */ - public function using($using) - { - $this->options['using'] = $using; - return $this; - } - - /** - * 存储过程调用 - * @access public - * @param bool $procedure 是否为存储过程查询 - * @return $this - */ - public function procedure(bool $procedure = true) - { - $this->options['procedure'] = $procedure; - return $this; - } - /** * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc']) * @access public @@ -958,30 +883,6 @@ class BaseQuery return $this; } - /** - * 指定group查询 - * @access public - * @param string|array $group GROUP - * @return $this - */ - public function group($group) - { - $this->options['group'] = $group; - return $this; - } - - /** - * 指定having查询 - * @access public - * @param string $having having - * @return $this - */ - public function having(string $having) - { - $this->options['having'] = $having; - return $this; - } - /** * 指定查询lock * @access public @@ -999,18 +900,6 @@ class BaseQuery return $this; } - /** - * 指定distinct查询 - * @access public - * @param bool $distinct 是否唯一 - * @return $this - */ - public function distinct(bool $distinct = true) - { - $this->options['distinct'] = $distinct; - return $this; - } - /** * 指定数据表别名 * @access public @@ -1030,47 +919,6 @@ class BaseQuery return $this; } - /** - * 指定强制索引 - * @access public - * @param string $force 索引名称 - * @return $this - */ - public function force(string $force) - { - $this->options['force'] = $force; - return $this; - } - - /** - * 查询注释 - * @access public - * @param string $comment 注释 - * @return $this - */ - public function comment(string $comment) - { - $this->options['comment'] = $comment; - return $this; - } - - /** - * 获取执行的SQL语句而不进行实际的查询 - * @access public - * @param bool $fetch 是否返回sql - * @return $this|Fetch - */ - public function fetchSql(bool $fetch = true) - { - $this->options['fetch_sql'] = $fetch; - - if ($fetch) { - return new Fetch($this); - } - - return $this; - } - /** * 设置从主服务器读取数据 * @access public @@ -1095,66 +943,6 @@ class BaseQuery return $this; } - /** - * 设置自增序列名 - * @access public - * @param string $sequence 自增序列名 - * @return $this - */ - public function sequence(string $sequence = null) - { - $this->options['sequence'] = $sequence; - return $this; - } - - /** - * 设置是否REPLACE - * @access public - * @param bool $replace 是否使用REPLACE写入数据 - * @return $this - */ - public function replace(bool $replace = true) - { - $this->options['replace'] = $replace; - return $this; - } - - /** - * 设置当前查询所在的分区 - * @access public - * @param string|array $partition 分区名称 - * @return $this - */ - public function partition($partition) - { - $this->options['partition'] = $partition; - return $this; - } - - /** - * 设置DUPLICATE - * @access public - * @param array|string|Raw $duplicate DUPLICATE信息 - * @return $this - */ - public function duplicate($duplicate) - { - $this->options['duplicate'] = $duplicate; - return $this; - } - - /** - * 设置查询的额外参数 - * @access public - * @param string $extra 额外信息 - * @return $this - */ - public function extra(string $extra) - { - $this->options['extra'] = $extra; - return $this; - } - /** * 设置JSON字段信息 * @access public @@ -1181,46 +969,6 @@ class BaseQuery return $this; } - /** - * 指定数据表自增主键 - * @access public - * @param string $autoinc 自增键 - * @return $this - */ - public function autoinc(string $autoinc) - { - $this->autoinc = $autoinc; - return $this; - } - - /** - * 获取当前数据表的主键 - * @access public - * @return string|array - */ - public function getPk() - { - if (empty($this->pk)) { - $this->pk = $this->connection->getPk($this->getTable()); - } - - return $this->pk; - } - - /** - * 获取当前数据表的自增主键 - * @access public - * @return string - */ - public function getAutoInc() - { - if (empty($this->autoinc)) { - $this->autoinc = $this->connection->getAutoInc($this->getTable()); - } - - return $this->autoinc; - } - /** * 查询参数批量赋值 * @access protected @@ -1248,17 +996,6 @@ class BaseQuery return $this->options[$name] ?? null; } - /** - * 获取当前的查询标识 - * @access public - * @param mixed $data 要序列化的数据 - * @return string - */ - public function getQueryGuid($data = null): string - { - return md5($this->getConfig('database') . serialize(var_export($data ?: $this->options, true)) . serialize($this->getBind(false))); - } - /** * 设置当前的查询参数 * @access public @@ -1569,18 +1306,6 @@ class BaseQuery return true; } - /** - * 创建子查询SQL - * @access public - * @param bool $sub 是否添加括号 - * @return string - * @throws Exception - */ - public function buildSql(bool $sub = true): string - { - return $sub ? '( ' . $this->fetchSql()->select() . ' )' : $this->fetchSql()->select(); - } - /** * 分析表达式(可用于查询或者写入操作) * @access public diff --git a/src/db/Connection.php b/src/db/Connection.php index e47cec2..08ef943 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -15,10 +15,6 @@ namespace think\db; use Psr\SimpleCache\CacheInterface; use think\DbManager; use think\db\CacheItem; -use think\db\exception\DataNotFoundException; -use think\db\exception\DbException as Exception; -use think\db\exception\ModelNotFoundException; -use think\db\exception\PDOException; /** * 数据库连接基础类 @@ -116,49 +112,6 @@ abstract class Connection */ protected $cache; - /** - * 架构函数 读取数据库配置信息 - * @access public - * @param array $config 数据库配置数组 - */ - public function __construct(array $config = []) - { - if (!empty($config)) { - $this->config = array_merge($this->config, $config); - } - - // 创建Builder对象 - $class = $this->getBuilderClass(); - - $this->builder = new $class($this); - - // 执行初始化操作 - $this->initialize(); - } - - /** - * 初始化 - * @access protected - * @return void - */ - protected function initialize() - { - } - - /** - * 获取当前连接器类对应的Query类 - * @access public - * @return string - */ - abstract public function getQueryClass(): string; - - /** - * 获取当前连接器类对应的Builder类 - * @access public - * @return string - */ - abstract public function getBuilderClass(): string; - /** * 获取当前的builder实例对象 * @access public @@ -227,180 +180,6 @@ abstract class Connection $this->config = array_merge($this->config, $config); } - /** - * 连接数据库方法 - * @access public - * @param array $config 接参数 - * @param integer $linkNum 连接序号 - * @return mixed - * @throws Exception - */ - abstract public function connect(array $config = [], $linkNum = 0); - - /** - * 释放查询结果 - * @access public - */ - abstract public function free(); - - /** - * 查找单条记录 - * @access public - * @param BaseQuery $query 查询对象 - * @return array - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException - */ - abstract public function find(BaseQuery $query): array; - - /** - * 使用游标查询记录 - * @access public - * @param BaseQuery $query 查询对象 - * @return \Generator - */ - abstract public function cursor(BaseQuery $query); - - /** - * 查找记录 - * @access public - * @param BaseQuery $query 查询对象 - * @return array - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException - */ - abstract public function select(BaseQuery $query): array; - - /** - * 插入记录 - * @access public - * @param BaseQuery $query 查询对象 - * @param boolean $getLastInsID 返回自增主键 - * @return mixed - */ - abstract public function insert(BaseQuery $query, bool $getLastInsID = false); - - /** - * 批量插入记录 - * @access public - * @param BaseQuery $query 查询对象 - * @param mixed $dataSet 数据集 - * @return integer - * @throws \Exception - * @throws \Throwable - */ - abstract public function insertAll(BaseQuery $query, array $dataSet = []): int; - - /** - * 更新记录 - * @access public - * @param BaseQuery $query 查询对象 - * @return integer - * @throws Exception - * @throws PDOException - */ - abstract public function update(BaseQuery $query): int; - - /** - * 删除记录 - * @access public - * @param BaseQuery $query 查询对象 - * @return int - * @throws Exception - * @throws PDOException - */ - abstract public function delete(BaseQuery $query): int; - - /** - * 得到某个字段的值 - * @access public - * @param BaseQuery $query 查询对象 - * @param string $field 字段名 - * @param mixed $default 默认值 - * @param bool $one 返回一个值 - * @return mixed - */ - abstract public function value(BaseQuery $query, string $field, $default = null); - - /** - * 得到某个列的数组 - * @access public - * @param BaseQuery $query 查询对象 - * @param string $column 字段名 多个字段用逗号分隔 - * @param string $key 索引 - * @return array - */ - abstract public function column(BaseQuery $query, string $column, string $key = ''): array; - - /** - * 执行数据库事务 - * @access public - * @param callable $callback 数据操作方法回调 - * @return mixed - * @throws PDOException - * @throws \Exception - * @throws \Throwable - */ - abstract public function transaction(callable $callback); - - /** - * 启动事务 - * @access public - * @return void - * @throws \PDOException - * @throws \Exception - */ - abstract public function startTrans(); - - /** - * 用于非自动提交状态下面的查询提交 - * @access public - * @return void - * @throws PDOException - */ - abstract public function commit(); - - /** - * 事务回滚 - * @access public - * @return void - * @throws PDOException - */ - abstract public function rollback(); - - /** - * 关闭数据库(或者重新连接) - * @access public - * @return $this - */ - abstract public function close(); - - /** - * 获取最近一次查询的sql语句 - * @access public - * @return string - */ - abstract public function getLastSql(): string; - - /** - * 获取最近插入的ID - * @access public - * @param BaseQuery $query 查询对象 - * @param string $sequence 自增序列名 - * @return mixed - */ - abstract public function getLastInsID(BaseQuery $query, string $sequence = null); - - /** - * 初始化数据库连接 - * @access protected - * @param boolean $master 是否主服务器 - * @return void - */ - abstract protected function initConnect(bool $master = true); - /** * 数据库SQL监控 * @access protected @@ -520,40 +299,14 @@ abstract class Connection } /** - * 启动XA事务 - * @access public - * @param string $xid XA事务id - * @return void - */ - public function startTransXa(string $xid) - {} - - /** - * 预编译XA事务 + * 获取返回或者影响的记录数 * @access public - * @param string $xid XA事务id - * @return void - */ - public function prepareXa(string $xid) - {} - - /** - * 提交XA事务 - * @access public - * @param string $xid XA事务id - * @return void - */ - public function commitXa(string $xid) - {} - - /** - * 回滚XA事务 - * @access public - * @param string $xid XA事务id - * @return void + * @return integer */ - public function rollbackXa(string $xid) - {} + public function getNumRows(): int + { + return $this->numRows; + } /** * 析构方法 diff --git a/src/db/ConnectionInterface.php b/src/db/ConnectionInterface.php new file mode 100644 index 0000000..c983c34 --- /dev/null +++ b/src/db/ConnectionInterface.php @@ -0,0 +1,216 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db; + +use Psr\SimpleCache\CacheInterface; +use think\DbManager; + +/** + * Connection interface + */ +interface ConnectionInterface +{ + /** + * 获取当前连接器类对应的Query类 + * @access public + * @return string + */ + public function getQueryClass(): string; + + /** + * 连接数据库方法 + * @access public + * @param array $config 接参数 + * @param integer $linkNum 连接序号 + * @return mixed + */ + public function connect(array $config = [], $linkNum = 0); + + /** + * 设置当前的数据库Db对象 + * @access public + * @param DbManager $db + * @return void + */ + public function setDb(DbManager $db); + + /** + * 设置当前的缓存对象 + * @access public + * @param CacheInterface $cache + * @return void + */ + public function setCache(CacheInterface $cache); + + /** + * 获取当前的缓存对象 + * @access public + * @return CacheInterface|null + */ + public function getCache(); + + /** + * 获取数据库的配置参数 + * @access public + * @param string $config 配置名称 + * @return mixed + */ + public function getConfig(string $config = ''); + + /** + * 释放查询结果 + * @access public + */ + public function free(); + + /** + * 关闭数据库(或者重新连接) + * @access public + * @return $this + */ + public function close(); + + /** + * 查找单条记录 + * @access public + * @param BaseQuery $query 查询对象 + * @return array + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function find(BaseQuery $query): array; + + /** + * 使用游标查询记录 + * @access public + * @param BaseQuery $query 查询对象 + */ + public function cursor(BaseQuery $query); + + /** + * 查找记录 + * @access public + * @param BaseQuery $query 查询对象 + * @return array + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function select(BaseQuery $query): array; + + /** + * 插入记录 + * @access public + * @param BaseQuery $query 查询对象 + * @param boolean $getLastInsID 返回自增主键 + * @return mixed + */ + public function insert(BaseQuery $query, bool $getLastInsID = false); + + /** + * 批量插入记录 + * @access public + * @param BaseQuery $query 查询对象 + * @param mixed $dataSet 数据集 + * @return integer + * @throws \Exception + * @throws \Throwable + */ + public function insertAll(BaseQuery $query, array $dataSet = []): int; + + /** + * 更新记录 + * @access public + * @param BaseQuery $query 查询对象 + * @return integer + * @throws Exception + * @throws PDOException + */ + public function update(BaseQuery $query): int; + + /** + * 删除记录 + * @access public + * @param BaseQuery $query 查询对象 + * @return int + * @throws Exception + * @throws PDOException + */ + public function delete(BaseQuery $query): int; + + /** + * 得到某个字段的值 + * @access public + * @param BaseQuery $query 查询对象 + * @param string $field 字段名 + * @param mixed $default 默认值 + * @param bool $one 返回一个值 + * @return mixed + */ + public function value(BaseQuery $query, string $field, $default = null); + + /** + * 得到某个列的数组 + * @access public + * @param BaseQuery $query 查询对象 + * @param string $column 字段名 多个字段用逗号分隔 + * @param string $key 索引 + * @return array + */ + public function column(BaseQuery $query, string $column, string $key = ''): array; + + /** + * 执行数据库事务 + * @access public + * @param callable $callback 数据操作方法回调 + * @return mixed + * @throws PDOException + * @throws \Exception + * @throws \Throwable + */ + public function transaction(callable $callback); + + /** + * 启动事务 + * @access public + * @return void + * @throws \PDOException + * @throws \Exception + */ + public function startTrans(); + + /** + * 用于非自动提交状态下面的查询提交 + * @access public + * @return void + * @throws PDOException + */ + public function commit(); + + /** + * 事务回滚 + * @access public + * @return void + * @throws PDOException + */ + public function rollback(); + + /** + * 获取最近一次查询的sql语句 + * @access public + * @return string + */ + public function getLastSql(): string; + +} diff --git a/src/db/Mongo.php b/src/db/Mongo.php index 94f919b..c015835 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -352,23 +352,6 @@ class Mongo extends BaseQuery return $this; } - /** - * 获取执行的SQL语句而不进行实际的查询 - * @access public - * @param bool $fetch 是否返回sql - * @return $this|Fetch - */ - public function fetchSql(bool $fetch = true) - { - $this->options['fetch_sql'] = $fetch; - - if ($fetch) { - throw new Exception('Mongo not support fetchSql'); - } - - return $this; - } - /** * 设置返回字段 * @access public @@ -507,6 +490,17 @@ class Mongo extends BaseQuery return $this->connection->getCursor($this); } + /** + * 获取当前的查询标识 + * @access public + * @param mixed $data 要序列化的数据 + * @return string + */ + public function getQueryGuid($data = null): string + { + return md5($this->getConfig('database') . serialize(var_export($data ?: $this->options, true))); + } + /** * 分析表达式(可用于查询或者写入操作) * @access public diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 29cbabe..20f1034 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -23,7 +23,7 @@ use think\db\exception\PDOException; /** * 数据库连接基础类 */ -abstract class PDOConnection extends Connection +abstract class PDOConnection extends Connection implements ConnectionInterface { const PARAM_FLOAT = 21; @@ -174,6 +174,23 @@ abstract class PDOConnection extends Connection */ protected $bind = []; + /** + * 架构函数 读取数据库配置信息 + * @access public + * @param array $config 数据库配置数组 + */ + public function __construct(array $config = []) + { + if (!empty($config)) { + $this->config = array_merge($this->config, $config); + } + + // 创建Builder对象 + $class = $this->getBuilderClass(); + + $this->builder = new $class($this); + } + /** * 获取当前连接器类对应的Query类 * @access public @@ -1525,16 +1542,6 @@ abstract class PDOConnection extends Connection return $insertId; } - /** - * 获取返回或者影响的记录数 - * @access public - * @return integer - */ - public function getNumRows(): int - { - return $this->numRows; - } - /** * 获取最近的错误信息 * @access public @@ -1637,4 +1644,39 @@ abstract class PDOConnection extends Connection return $this->connect($dbConfig, $r, $r == $m ? false : $dbMaster); } + /** + * 启动XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function startTransXa(string $xid) + {} + + /** + * 预编译XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function prepareXa(string $xid) + {} + + /** + * 提交XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function commitXa(string $xid) + {} + + /** + * 回滚XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function rollbackXa(string $xid) + {} } diff --git a/src/db/concern/PDOQuery.php b/src/db/concern/PDOQuery.php index df2b48c..a56a47c 100644 --- a/src/db/concern/PDOQuery.php +++ b/src/db/concern/PDOQuery.php @@ -13,7 +13,7 @@ declare (strict_types = 1); namespace think\db\concern; use PDOStatement; - +use think\db\Fetch; /** * PDO查询支持 */ @@ -21,6 +21,280 @@ trait PDOQuery { use JoinAndViewQuery, ParamsBind, TableFieldInfo; + /** + * 执行查询 返回数据集 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @return array + * @throws BindParamException + * @throws PDOException + */ + public function query(string $sql, array $bind = []): array + { + return $this->connection->query($this, $sql, $bind); + } + + /** + * 执行语句 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @return int + * @throws BindParamException + * @throws PDOException + */ + public function execute(string $sql, array $bind = []): int + { + return $this->connection->execute($this, $sql, $bind, true); + } + + /** + * 获取返回或者影响的记录数 + * @access public + * @return integer + */ + public function getNumRows(): int + { + return $this->connection->getNumRows(); + } + + /** + * 获取执行的SQL语句而不进行实际的查询 + * @access public + * @param bool $fetch 是否返回sql + * @return $this|Fetch + */ + public function fetchSql(bool $fetch = true) + { + $this->options['fetch_sql'] = $fetch; + + if ($fetch) { + return new Fetch($this); + } + + return $this; + } + + /** + * 批处理执行SQL语句 + * 批处理的指令都认为是execute操作 + * @access public + * @param array $sql SQL批处理指令 + * @return bool + */ + public function batchQuery(array $sql = []): bool + { + return $this->connection->batchQuery($this, $sql); + } + + /** + * USING支持 用于多表删除 + * @access public + * @param mixed $using USING + * @return $this + */ + public function using($using) + { + $this->options['using'] = $using; + return $this; + } + + /** + * 存储过程调用 + * @access public + * @param bool $procedure 是否为存储过程查询 + * @return $this + */ + public function procedure(bool $procedure = true) + { + $this->options['procedure'] = $procedure; + return $this; + } + + /** + * 指定group查询 + * @access public + * @param string|array $group GROUP + * @return $this + */ + public function group($group) + { + $this->options['group'] = $group; + return $this; + } + + /** + * 指定having查询 + * @access public + * @param string $having having + * @return $this + */ + public function having(string $having) + { + $this->options['having'] = $having; + return $this; + } + + /** + * 指定distinct查询 + * @access public + * @param bool $distinct 是否唯一 + * @return $this + */ + public function distinct(bool $distinct = true) + { + $this->options['distinct'] = $distinct; + return $this; + } + + /** + * 设置自增序列名 + * @access public + * @param string $sequence 自增序列名 + * @return $this + */ + public function sequence(string $sequence = null) + { + $this->options['sequence'] = $sequence; + return $this; + } + + /** + * 指定强制索引 + * @access public + * @param string $force 索引名称 + * @return $this + */ + public function force(string $force) + { + $this->options['force'] = $force; + return $this; + } + + /** + * 查询注释 + * @access public + * @param string $comment 注释 + * @return $this + */ + public function comment(string $comment) + { + $this->options['comment'] = $comment; + return $this; + } + + /** + * 设置是否REPLACE + * @access public + * @param bool $replace 是否使用REPLACE写入数据 + * @return $this + */ + public function replace(bool $replace = true) + { + $this->options['replace'] = $replace; + return $this; + } + + /** + * 设置当前查询所在的分区 + * @access public + * @param string|array $partition 分区名称 + * @return $this + */ + public function partition($partition) + { + $this->options['partition'] = $partition; + return $this; + } + + /** + * 设置DUPLICATE + * @access public + * @param array|string|Raw $duplicate DUPLICATE信息 + * @return $this + */ + public function duplicate($duplicate) + { + $this->options['duplicate'] = $duplicate; + return $this; + } + + /** + * 设置查询的额外参数 + * @access public + * @param string $extra 额外信息 + * @return $this + */ + public function extra(string $extra) + { + $this->options['extra'] = $extra; + return $this; + } + + /** + * 创建子查询SQL + * @access public + * @param bool $sub 是否添加括号 + * @return string + * @throws Exception + */ + public function buildSql(bool $sub = true): string + { + return $sub ? '( ' . $this->fetchSql()->select() . ' )' : $this->fetchSql()->select(); + } + + /** + * 获取当前数据表的主键 + * @access public + * @return string|array + */ + public function getPk() + { + if (empty($this->pk)) { + $this->pk = $this->connection->getPk($this->getTable()); + } + + return $this->pk; + } + + /** + * 指定数据表自增主键 + * @access public + * @param string $autoinc 自增键 + * @return $this + */ + public function autoinc(string $autoinc) + { + $this->autoinc = $autoinc; + return $this; + } + + /** + * 获取当前数据表的自增主键 + * @access public + * @return string + */ + public function getAutoInc() + { + if (empty($this->autoinc)) { + $this->autoinc = $this->connection->getAutoInc($this->getTable()); + } + + return $this->autoinc; + } + + /** + * 获取当前的查询标识 + * @access public + * @param mixed $data 要序列化的数据 + * @return string + */ + public function getQueryGuid($data = null): string + { + return md5($this->getConfig('database') . serialize(var_export($data ?: $this->options, true)) . serialize($this->getBind(false))); + } + /** * 执行查询但只返回PDOStatement对象 * @access public diff --git a/src/db/concern/ParamsBind.php b/src/db/concern/ParamsBind.php index 8267b7e..2ae858c 100644 --- a/src/db/concern/ParamsBind.php +++ b/src/db/concern/ParamsBind.php @@ -47,7 +47,7 @@ trait ParamsBind */ public function bindValue($value, int $type = null, string $name = null) { - $name = $name ?: 'ThinkBind_' . (count($this->bind) + 1) . '_'; + $name = $name ?: 'ThinkBind_' . (count($this->bind) + 1) . '_' . mt_rand() . '_'; $this->bind[$name] = [$value, $type ?: PDO::PARAM_STR]; return $name; diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 338cd8a..0443928 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -32,7 +32,7 @@ use think\db\Mongo as Query; /** * Mongo数据库驱动 */ -class Mongo extends Connection +class Mongo extends Connection implements ConnectionInterface { // 查询数据类型 @@ -93,6 +93,23 @@ class Mongo extends Connection 'type_map' => ['root' => 'array', 'document' => 'array'], ]; + /** + * 架构函数 读取数据库配置信息 + * @access public + * @param array $config 数据库配置数组 + */ + public function __construct(array $config = []) + { + if (!empty($config)) { + $this->config = array_merge($this->config, $config); + } + + // 创建Builder对象 + $class = $this->getBuilderClass(); + + $this->builder = new $class($this); + } + /** * 获取当前连接器类对应的Query类 * @access public @@ -692,9 +709,10 @@ class Mongo extends Connection /** * 获取最近插入的ID * @access public + * @param BaseQuery $query 查询对象 * @return mixed */ - public function getLastInsID(BaseQuery $query, string $sequence = null) + public function getLastInsID(BaseQuery $query) { $id = $this->builder->getLastInsID(); @@ -995,40 +1013,6 @@ class Mongo extends Connection return $this->command($command, $db); } - // 获取当前数据表字段信息 - public function getTableFields(string $tableName) - { - return []; - } - - // 获取当前数据表字段类型 - public function getFieldsType(string $tableName) - { - return []; - } - - /** - * 获取数据表绑定信息 - * @access public - * @param mixed $tableName 数据表名 - * @return array - */ - public function getFieldsBind($tableName): array - { - return []; - } - - /** - * 获取字段绑定类型 - * @access public - * @param string $type 字段类型 - * @return integer - */ - public function getFieldBindType(string $type): int - { - return 1; - } - /** * 执行数据库事务 * @access public @@ -1069,16 +1053,4 @@ class Mongo extends Connection public function rollback() {} - /** - * 析构方法 - * @access public - */ - public function __destruct() - { - // 释放查询 - $this->free(); - - // 关闭连接 - $this->close(); - } } -- Gitee From b66c5484cb0fb29db661d4632ecfc928061b5482 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 23 Sep 2019 19:42:43 +0800 Subject: [PATCH 098/365] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/ConnectionInterface.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/db/ConnectionInterface.php b/src/db/ConnectionInterface.php index c983c34..b976ec5 100644 --- a/src/db/ConnectionInterface.php +++ b/src/db/ConnectionInterface.php @@ -52,13 +52,6 @@ interface ConnectionInterface */ public function setCache(CacheInterface $cache); - /** - * 获取当前的缓存对象 - * @access public - * @return CacheInterface|null - */ - public function getCache(); - /** * 获取数据库的配置参数 * @access public -- Gitee From 80ec39a38cc7f1818c2a9339f9dce52b055e7635 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Mon, 23 Sep 2019 20:12:15 +0800 Subject: [PATCH 099/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 64 ++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index e80f637..0aa324b 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -16,7 +16,7 @@ use InvalidArgumentException; use Psr\Log\LoggerInterface; use Psr\SimpleCache\CacheInterface; use think\db\BaseQuery; -use think\db\Connection; +use think\db\ConnectionInterface; use think\db\Query; use think\db\Raw; @@ -157,7 +157,7 @@ class DbManager /** * 设置缓存对象 * @access public - * @param CacheInterface $cache 缓存对象 + * @param CacheInterface $cache 缓存对象 * @return void */ public function setCache(CacheInterface $cache): void @@ -168,7 +168,7 @@ class DbManager /** * 设置日志对象 * @access public - * @param LoggerInterface $log 日志对象 + * @param LoggerInterface $log 日志对象 * @return void */ public function setLog(LoggerInterface $log): void @@ -211,8 +211,8 @@ class DbManager /** * 获取配置参数 * @access public - * @param string $name 配置参数 - * @param mixed $default 默认值 + * @param string $name 配置参数 + * @param mixed $default 默认值 * @return mixed */ public function getConfig(string $name = '', $default = null) @@ -227,7 +227,7 @@ class DbManager /** * 创建/切换数据库连接查询 * @access public - * @param string|null $name 连接配置标识 + * @param string|null $name 连接配置标识 * @param bool $force 强制重新连接 * @return BaseQuery */ @@ -251,39 +251,51 @@ class DbManager * @access protected * @param string|null $name 连接标识 * @param bool $force 强制重新连接 - * @return Connection + * @return ConnectionInterface */ - protected function instance(string $name = null, bool $force = false): Connection + protected function instance(string $name = null, bool $force = false): ConnectionInterface { if (empty($name)) { $name = $this->getConfig('default', 'mysql'); } if ($force || !isset($this->instance[$name])) { - $connections = $this->getConfig('connections'); - if (!isset($connections[$name])) { - throw new InvalidArgumentException('Undefined db config:' . $name); - } + $this->instance[$name] = $this->createConnection($name); + } - $config = $connections[$name]; - $type = !empty($config['type']) ? $config['type'] : 'mysql'; + return $this->instance[$name]; + } - if (false !== strpos($type, '\\')) { - $class = $type; - } else { - $class = '\\think\\db\\connector\\' . ucfirst($type); - } + /** + * 创建连接 + * @param $name + * @return ConnectionInterface + */ + protected function createConnection($name) + { + $connections = $this->getConfig('connections'); + if (!isset($connections[$name])) { + throw new InvalidArgumentException('Undefined db config:' . $name); + } - $connection = new $class($config); - $connection->setDb($this); + $config = $connections[$name]; + $type = !empty($config['type']) ? $config['type'] : 'mysql'; - if ($this->cache) { - $connection->setCache($this->cache); - } - $this->instance[$name] = $connection; + if (false !== strpos($type, '\\')) { + $class = $type; + } else { + $class = '\\think\\db\\connector\\' . ucfirst($type); } - return $this->instance[$name]; + /** @var ConnectionInterface $connection */ + $connection = new $class($config); + $connection->setDb($this); + + if ($this->cache) { + $connection->setCache($this->cache); + } + + return $connection; } /** -- Gitee From fb6377d1f258dc69bac0b81a26a3d0a8ac119ed7 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 23 Sep 2019 20:21:14 +0800 Subject: [PATCH 100/365] =?UTF-8?q?=E5=8F=96=E6=B6=88=E5=BB=B6=E6=97=B6?= =?UTF-8?q?=E5=86=99=E5=85=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 30 +++++++---------------------- src/db/Connection.php | 45 ------------------------------------------- src/db/Mongo.php | 23 ++++------------------ 3 files changed, 11 insertions(+), 87 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 9c79f52..64b7d70 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -72,9 +72,9 @@ class BaseQuery /** * 架构函数 * @access public - * @param Connection $connection 数据库连接对象 + * @param ConnectionInterface $connection 数据库连接对象 */ - public function __construct(Connection $connection) + public function __construct(ConnectionInterface $connection) { $this->connection = $connection; @@ -433,27 +433,11 @@ class BaseQuery * @access public * @param string $field 字段名 * @param float $step 增长值 - * @param integer $lazyTime 延时时间(s) - * @param string $op INC/DEC * @return $this */ - public function inc(string $field, float $step = 1, int $lazyTime = 0, string $op = 'INC') + public function inc(string $field, float $step = 1) { - if ($lazyTime > 0) { - // 延迟写入 - $condition = $this->options['where'] ?? []; - - $guid = md5($this->getTable() . '_' . $field . '_' . $this->getQueryGuid($condition)); - $step = $this->connection->lazyWrite($op, $guid, $step, $lazyTime); - - if (false === $step) { - return $this; - } - - $op = 'INC'; - } - - $this->options['data'][$field] = [$op, $step]; + $this->options['data'][$field] = ['INC', $step]; return $this; } @@ -463,12 +447,12 @@ class BaseQuery * @access public * @param string $field 字段名 * @param float $step 增长值 - * @param integer $lazyTime 延时时间(s) * @return $this */ - public function dec(string $field, float $step = 1, int $lazyTime = 0) + public function dec(string $field, float $step = 1) { - return $this->inc($field, $step, $lazyTime, 'DEC'); + $this->options['data'][$field] = ['DEC', $step]; + return $this; } /** diff --git a/src/db/Connection.php b/src/db/Connection.php index 08ef943..c477111 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -169,17 +169,6 @@ abstract class Connection return $this->config[$config] ?? null; } - /** - * 设置数据库的配置参数 - * @access public - * @param array $config 配置 - * @return void - */ - public function setConfig(array $config) - { - $this->config = array_merge($this->config, $config); - } - /** * 数据库SQL监控 * @access protected @@ -264,40 +253,6 @@ abstract class Connection return $cacheItem; } - /** - * 延时更新检查 返回false表示需要延时 - * 否则返回实际写入的数值 - * @access public - * @param string $type 自增或者自减 - * @param string $guid 写入标识 - * @param float $step 写入步进值 - * @param integer $lazyTime 延时时间(s) - * @return false|integer - */ - public function lazyWrite(string $type, string $guid, float $step, int $lazyTime) - { - if (!$this->cache || !method_exists($this->cache, $type)) { - return $step; - } - - if (!$this->cache->has($guid . '_time')) { - // 计时开始 - $this->cache->set($guid . '_time', time(), 0); - $this->cache->$type($guid, $step); - } elseif (time() > $this->cache->get($guid . '_time') + $lazyTime) { - // 删除缓存 - $value = $this->cache->$type($guid, $step); - $this->cache->delete($guid); - $this->cache->delete($guid . '_time'); - return 0 === $value ? false : $value; - } else { - // 更新缓存 - $this->cache->$type($guid, $step); - } - - return false; - } - /** * 获取返回或者影响的记录数 * @access public diff --git a/src/db/Mongo.php b/src/db/Mongo.php index c015835..f4176b1 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -185,25 +185,11 @@ class Mongo extends BaseQuery * @access public * @param string $field 字段名 * @param float $step 增长值 - * @param integer $lazyTime 延时时间(s) - * @param string $op inc/dec * @return $this */ - public function inc(string $field, float $step = 1, int $lazyTime = 0, string $op = 'inc') + public function inc(string $field, float $step = 1) { - if ($lazyTime > 0) { - // 延迟写入 - $condition = $this->options['where'] ?? []; - - $guid = md5($this->getTable() . '_' . $field . '_' . $this->getQueryGuid($condition)); - $step = $this->connection->lazyWrite($op, $guid, $step, $lazyTime); - - if (false === $step) { - return $this; - } - } - - $this->options['data'][$field] = ['$' . $op, $step]; + $this->options['data'][$field] = ['$inc', $step]; return $this; } @@ -213,12 +199,11 @@ class Mongo extends BaseQuery * @access public * @param string $field 字段名 * @param float $step 减少值 - * @param integer $lazyTime 延时时间(s) * @return $this */ - public function dec(string $field, float $step = 1, int $lazyTime = 0) + public function dec(string $field, float $step = 1) { - return $this->inc($field, -1 * $step, $lazyTime); + return $this->inc($field, -1 * $step); } /** -- Gitee From d23f55e386e3e4a646f8310fdb5690c4f81e129d Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 23 Sep 2019 20:26:55 +0800 Subject: [PATCH 101/365] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/ConnectionInterface.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/db/ConnectionInterface.php b/src/db/ConnectionInterface.php index b976ec5..d69e590 100644 --- a/src/db/ConnectionInterface.php +++ b/src/db/ConnectionInterface.php @@ -84,13 +84,6 @@ interface ConnectionInterface */ public function find(BaseQuery $query): array; - /** - * 使用游标查询记录 - * @access public - * @param BaseQuery $query 查询对象 - */ - public function cursor(BaseQuery $query); - /** * 查找记录 * @access public -- Gitee From 70e364bd3ab9f50d36d90f24ec85bca3d2ad7e87 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 23 Sep 2019 20:37:22 +0800 Subject: [PATCH 102/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Connection.php | 3 --- src/db/ConnectionInterface.php | 6 ------ 2 files changed, 9 deletions(-) diff --git a/src/db/Connection.php b/src/db/Connection.php index c477111..ba51920 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -269,9 +269,6 @@ abstract class Connection */ public function __destruct() { - // 释放查询 - $this->free(); - // 关闭连接 $this->close(); } diff --git a/src/db/ConnectionInterface.php b/src/db/ConnectionInterface.php index d69e590..f6b1e08 100644 --- a/src/db/ConnectionInterface.php +++ b/src/db/ConnectionInterface.php @@ -60,12 +60,6 @@ interface ConnectionInterface */ public function getConfig(string $config = ''); - /** - * 释放查询结果 - * @access public - */ - public function free(); - /** * 关闭数据库(或者重新连接) * @access public -- Gitee From 5aa17803e903c4b64eec9f55603ff148c2ee56f6 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Mon, 23 Sep 2019 21:29:34 +0800 Subject: [PATCH 103/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index 0aa324b..ceb508f 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -267,19 +267,30 @@ class DbManager } /** - * 创建连接 - * @param $name - * @return ConnectionInterface + * 获取连接配置 + * @param string $name + * @return array */ - protected function createConnection($name) + protected function getConnectionConfig(string $name): array { $connections = $this->getConfig('connections'); if (!isset($connections[$name])) { throw new InvalidArgumentException('Undefined db config:' . $name); } - $config = $connections[$name]; - $type = !empty($config['type']) ? $config['type'] : 'mysql'; + return $connections[$name]; + } + + /** + * 创建连接 + * @param $name + * @return ConnectionInterface + */ + protected function createConnection(string $name): ConnectionInterface + { + $config = $this->getConnectionConfig($name); + + $type = !empty($config['type']) ? $config['type'] : 'mysql'; if (false !== strpos($type, '\\')) { $class = $type; -- Gitee From 60d14f32035a3a71fd77a65daf8c027937f7fae0 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 24 Sep 2019 09:36:29 +0800 Subject: [PATCH 104/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3mongo=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/connector/Mongo.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 0443928..e552a77 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -26,6 +26,7 @@ use MongoDB\Driver\ReadPreference; use think\db\BaseQuery; use think\db\builder\Mongo as Builder; use think\db\Connection; +use think\db\ConnectionInterface; use think\db\exception\DbException as Exception; use think\db\Mongo as Query; -- Gitee From 75d65d9fc11b53ce395acfabfe945ff17533cb3d Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 24 Sep 2019 18:45:24 +0800 Subject: [PATCH 105/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BMongo=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 446 ++++++++---------------------- src/db/Builder.php | 36 +-- src/db/Mongo.php | 138 ++++++++- src/db/builder/Mongo.php | 10 +- src/db/concern/PDOQuery.php | 213 ++++++++++++++ src/db/concern/TableFieldInfo.php | 12 - src/db/connector/Mongo.php | 37 +-- 7 files changed, 489 insertions(+), 403 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 64b7d70..71e7dd1 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -16,7 +16,6 @@ use think\Collection; use think\db\exception\DataNotFoundException; use think\db\exception\DbException as Exception; use think\db\exception\ModelNotFoundException; -use think\db\exception\PDOException; use think\helper\Str; use think\Model; use think\Paginator; @@ -24,7 +23,7 @@ use think\Paginator; /** * 数据查询基础类 */ -class BaseQuery +abstract class BaseQuery { use concern\TimeFieldQuery; use concern\AggregateQuery; @@ -103,67 +102,16 @@ class BaseQuery return $query; } - /** - * 利用__call方法实现一些特殊的Model方法 - * @access public - * @param string $method 方法名称 - * @param array $args 调用参数 - * @return mixed - * @throws Exception - */ - public function __call(string $method, array $args) - { - if (strtolower(substr($method, 0, 5)) == 'getby') { - // 根据某个字段获取记录 - $field = Str::snake(substr($method, 5)); - return $this->where($field, '=', $args[0])->find(); - } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { - // 根据某个字段获取记录的某个值 - $name = Str::snake(substr($method, 10)); - return $this->where($name, '=', $args[0])->value($args[1]); - } elseif (strtolower(substr($method, 0, 7)) == 'whereor') { - $name = Str::snake(substr($method, 7)); - array_unshift($args, $name); - return call_user_func_array([$this, 'whereOr'], $args); - } elseif (strtolower(substr($method, 0, 5)) == 'where') { - $name = Str::snake(substr($method, 5)); - array_unshift($args, $name); - return call_user_func_array([$this, 'where'], $args); - } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { - // 动态调用命名范围 - $method = 'scope' . $method; - array_unshift($args, $this); - - call_user_func_array([$this->model, $method], $args); - return $this; - } else { - throw new Exception('method not exist:' . static::class . '->' . $method); - } - } - /** * 获取当前的数据库Connection对象 * @access public - * @return Connection + * @return ConnectionInterface */ public function getConnection() { return $this->connection; } - /** - * 设置当前的数据库Connection对象 - * @access public - * @param Connection $connection 数据库连接对象 - * @return $this - */ - public function setConnection(Connection $connection) - { - $this->connection = $connection; - - return $this; - } - /** * 指定当前数据表名(不含前缀) * @access public @@ -214,6 +162,18 @@ class BaseQuery return $this->prefix . Str::snake($name); } + /** + * 设置字段类型信息 + * @access public + * @param array $type 字段类型信息 + * @return $this + */ + public function setFieldType(array $type) + { + $this->options['field_type'] = $type; + return $this; + } + /** * 获取最近一次查询的sql语句 * @access public @@ -402,19 +362,6 @@ class BaseQuery return $this; } - /** - * 表达式方式指定查询字段 - * @access public - * @param string $field 字段名 - * @return $this - */ - public function fieldRaw(string $field) - { - $this->options['field'][] = new Raw($field); - - return $this; - } - /** * 设置数据 * @access public @@ -428,46 +375,6 @@ class BaseQuery return $this; } - /** - * 字段值增长 - * @access public - * @param string $field 字段名 - * @param float $step 增长值 - * @return $this - */ - public function inc(string $field, float $step = 1) - { - $this->options['data'][$field] = ['INC', $step]; - - return $this; - } - - /** - * 字段值减少 - * @access public - * @param string $field 字段名 - * @param float $step 增长值 - * @return $this - */ - public function dec(string $field, float $step = 1) - { - $this->options['data'][$field] = ['DEC', $step]; - return $this; - } - - /** - * 使用表达式设置数据 - * @access public - * @param string $field 字段名 - * @param string $value 字段值 - * @return $this - */ - public function exp(string $field, string $value) - { - $this->options['data'][$field] = new Raw($value); - return $this; - } - /** * 去除查询参数 * @access public @@ -514,6 +421,107 @@ class BaseQuery return $this; } + /** + * 指定当前操作的数据表 + * @access public + * @param mixed $table 表名 + * @return $this + */ + public function table($table) + { + if (is_string($table)) { + if (strpos($table, ')')) { + // 子查询 + } elseif (false === strpos($table, ',')) { + if (strpos($table, ' ')) { + list($item, $alias) = explode(' ', $table); + $table = []; + $this->alias([$item => $alias]); + $table[$item] = $alias; + } + } else { + $tables = explode(',', $table); + $table = []; + + foreach ($tables as $item) { + $item = trim($item); + if (strpos($item, ' ')) { + list($item, $alias) = explode(' ', $item); + $this->alias([$item => $alias]); + $table[$item] = $alias; + } else { + $table[] = $item; + } + } + } + } elseif (is_array($table)) { + $tables = $table; + $table = []; + + foreach ($tables as $key => $val) { + if (is_numeric($key)) { + $table[] = $val; + } else { + $this->alias([$key => $val]); + $table[$key] = $val; + } + } + } + + $this->options['table'] = $table; + + return $this; + } + + /** + * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc']) + * @access public + * @param string|array|Raw $field 排序字段 + * @param string $order 排序 + * @return $this + */ + public function order($field, string $order = '') + { + if (empty($field)) { + return $this; + } elseif ($field instanceof Raw) { + $this->options['order'][] = $field; + return $this; + } + + if (is_string($field)) { + if (!empty($this->options['via'])) { + $field = $this->options['via'] . '.' . $field; + } + if (strpos($field, ',')) { + $field = array_map('trim', explode(',', $field)); + } else { + $field = empty($order) ? $field : [$field => $order]; + } + } elseif (!empty($this->options['via'])) { + foreach ($field as $key => $val) { + if (is_numeric($key)) { + $field[$key] = $this->options['via'] . '.' . $val; + } else { + $field[$this->options['via'] . '.' . $key] = $val; + unset($field[$key]); + } + } + } + + if (!isset($this->options['order'])) { + $this->options['order'] = []; + } + + if (is_array($field)) { + $this->options['order'] = array_merge($this->options['order'], $field); + } else { + $this->options['order'][] = $field; + } + + return $this; + } + /** * 分页查询 * @access public @@ -681,168 +689,6 @@ class BaseQuery ]; } - /** - * 表达式方式指定当前操作的数据表 - * @access public - * @param mixed $table 表名 - * @return $this - */ - public function tableRaw(string $table) - { - $this->options['table'] = new Raw($table); - - return $this; - } - - /** - * 指定当前操作的数据表 - * @access public - * @param mixed $table 表名 - * @return $this - */ - public function table($table) - { - if (is_string($table)) { - if (strpos($table, ')')) { - // 子查询 - } elseif (false === strpos($table, ',')) { - if (strpos($table, ' ')) { - list($item, $alias) = explode(' ', $table); - $table = []; - $this->alias([$item => $alias]); - $table[$item] = $alias; - } - } else { - $tables = explode(',', $table); - $table = []; - - foreach ($tables as $item) { - $item = trim($item); - if (strpos($item, ' ')) { - list($item, $alias) = explode(' ', $item); - $this->alias([$item => $alias]); - $table[$item] = $alias; - } else { - $table[] = $item; - } - } - } - } elseif (is_array($table)) { - $tables = $table; - $table = []; - - foreach ($tables as $key => $val) { - if (is_numeric($key)) { - $table[] = $val; - } else { - $this->alias([$key => $val]); - $table[$key] = $val; - } - } - } - - $this->options['table'] = $table; - - return $this; - } - - /** - * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc']) - * @access public - * @param string|array|Raw $field 排序字段 - * @param string $order 排序 - * @return $this - */ - public function order($field, string $order = '') - { - if (empty($field)) { - return $this; - } elseif ($field instanceof Raw) { - $this->options['order'][] = $field; - return $this; - } - - if (is_string($field)) { - if (!empty($this->options['via'])) { - $field = $this->options['via'] . '.' . $field; - } - if (strpos($field, ',')) { - $field = array_map('trim', explode(',', $field)); - } else { - $field = empty($order) ? $field : [$field => $order]; - } - } elseif (!empty($this->options['via'])) { - foreach ($field as $key => $val) { - if (is_numeric($key)) { - $field[$key] = $this->options['via'] . '.' . $val; - } else { - $field[$this->options['via'] . '.' . $key] = $val; - unset($field[$key]); - } - } - } - - if (!isset($this->options['order'])) { - $this->options['order'] = []; - } - - if (is_array($field)) { - $this->options['order'] = array_merge($this->options['order'], $field); - } else { - $this->options['order'][] = $field; - } - - return $this; - } - - /** - * 表达式方式指定Field排序 - * @access public - * @param string $field 排序字段 - * @param array $bind 参数绑定 - * @return $this - */ - public function orderRaw(string $field, array $bind = []) - { - if (!empty($bind)) { - $this->bindParams($field, $bind); - } - - $this->options['order'][] = new Raw($field); - - return $this; - } - - /** - * 指定Field排序 orderField('id',[1,2,3],'desc') - * @access public - * @param string $field 排序字段 - * @param array $values 排序值 - * @param string $order 排序 desc/asc - * @return $this - */ - public function orderField(string $field, array $values, string $order = '') - { - if (!empty($values)) { - $values['sort'] = $order; - - $this->options['order'][$field] = $values; - } - - return $this; - } - - /** - * 随机排序 - * @access public - * @return $this - */ - public function orderRand() - { - $this->options['order'][] = '[rand]'; - return $this; - } - /** * 查询缓存 * @access public @@ -1083,7 +929,6 @@ class BaseQuery * @param array $fields 要插入的数据表字段名 * @param string $table 要插入的数据表名 * @return integer - * @throws PDOException */ public function selectInsert(array $fields, string $table): int { @@ -1096,7 +941,6 @@ class BaseQuery * @param mixed $data 数据 * @return integer * @throws Exception - * @throws PDOException */ public function update(array $data = []): int { @@ -1126,7 +970,6 @@ class BaseQuery * @param mixed $data 表达式 true 表示强制删除 * @return int * @throws Exception - * @throws PDOException */ public function delete($data = null): int { @@ -1231,65 +1074,6 @@ class BaseQuery return $result; } - /** - * 分批数据返回处理 - * @access public - * @param integer $count 每次处理的数据数量 - * @param callable $callback 处理回调方法 - * @param string|array $column 分批处理的字段名 - * @param string $order 字段排序 - * @return bool - * @throws Exception - */ - public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool - { - $options = $this->getOptions(); - $column = $column ?: $this->getPk(); - - if (isset($options['order'])) { - unset($options['order']); - } - - $bind = $this->bind; - - if (is_array($column)) { - $times = 1; - $query = $this->options($options)->page($times, $count); - } else { - $query = $this->options($options)->limit($count); - - if (strpos($column, '.')) { - list($alias, $key) = explode('.', $column); - } else { - $key = $column; - } - } - - $resultSet = $query->order($column, $order)->select(); - - while (count($resultSet) > 0) { - if (false === call_user_func($callback, $resultSet)) { - return false; - } - - if (isset($times)) { - $times++; - $query = $this->options($options)->page($times, $count); - } else { - $end = $resultSet->pop(); - $lastId = is_array($end) ? $end[$key] : $end->getData($key); - - $query = $this->options($options) - ->limit($count) - ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId); - } - - $resultSet = $query->bind($bind)->order($column, $order)->select(); - } - - return true; - } - /** * 分析表达式(可用于查询或者写入操作) * @access public diff --git a/src/db/Builder.php b/src/db/Builder.php index a6b4e6d..c2a86d3 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -23,7 +23,7 @@ abstract class Builder { /** * Connection对象 - * @var Connection + * @var ConnectionInterface */ protected $connection; @@ -83,9 +83,9 @@ abstract class Builder /** * 架构函数 * @access public - * @param Connection $connection 数据库连接对象实例 + * @param ConnectionInterface $connection 数据库连接对象实例 */ - public function __construct(Connection $connection) + public function __construct(ConnectionInterface $connection) { $this->connection = $connection; } @@ -93,9 +93,9 @@ abstract class Builder /** * 获取当前的连接对象实例 * @access public - * @return Connection + * @return ConnectionInterface */ - public function getConnection(): Connection + public function getConnection(): ConnectionInterface { return $this->connection; } @@ -103,8 +103,8 @@ abstract class Builder /** * 注册查询表达式解析 * @access public - * @param string $name 解析方法 - * @param array $parser 匹配表达式数据 + * @param string $name 解析方法 + * @param array $parser 匹配表达式数据 * @return $this */ public function bindParser(string $name, array $parser) @@ -116,10 +116,10 @@ abstract class Builder /** * 数据分析 * @access protected - * @param Query $query 查询对象 - * @param array $data 数据 - * @param array $fields 字段信息 - * @param array $bind 参数绑定 + * @param Query $query 查询对象 + * @param array $data 数据 + * @param array $fields 字段信息 + * @param array $bind 参数绑定 * @return array */ protected function parseData(Query $query, array $data = [], array $fields = [], array $bind = []): array @@ -186,10 +186,10 @@ abstract class Builder /** * 数据绑定处理 * @access protected - * @param Query $query 查询对象 - * @param string $key 字段名 - * @param mixed $data 数据 - * @param array $bind 绑定数据 + * @param Query $query 查询对象 + * @param string $key 字段名 + * @param mixed $data 数据 + * @param array $bind 绑定数据 * @return string */ protected function parseDataBind(Query $query, string $key, $data, array $bind = []): string @@ -289,8 +289,8 @@ abstract class Builder /** * where分析 * @access protected - * @param Query $query 查询对象 - * @param mixed $where 查询条件 + * @param Query $query 查询对象 + * @param mixed $where 查询条件 * @return string */ protected function parseWhere(Query $query, array $where): string @@ -439,7 +439,7 @@ abstract class Builder */ protected function parseClosureWhere(Query $query, Closure $value, string $logic): string { - $newQuery = $query->newQuery()->setConnection($this->connection); + $newQuery = $query->newQuery(); $value($newQuery); $whereClosure = $this->buildWhere($newQuery, $newQuery->getOptions('where') ?: []); diff --git a/src/db/Mongo.php b/src/db/Mongo.php index f4176b1..b590976 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -23,23 +23,23 @@ use MongoDB\Driver\WriteConcern; use think\Collection; use think\db\connector\Mongo as Connection; use think\db\exception\DbException as Exception; -use think\db\Query as BaseQuery; +use think\Paginator; class Mongo extends BaseQuery { /** * 执行查询 返回数据集 * @access public - * @param MongoQuery $query 查询对象 + * @param MongoQuery $query 查询对象 * @return mixed * @throws AuthenticationException * @throws InvalidArgumentException * @throws ConnectionException * @throws RuntimeException */ - public function mongoQuery(MongoQuery $query) + public function query(MongoQuery $query) { - return $this->connection->mongoQuery($this, $query); + return $this->connection->query($this, $query); } /** @@ -63,7 +63,7 @@ class Mongo extends BaseQuery /** * 执行语句 * @access public - * @param BulkWrite $bulk + * @param BulkWrite $bulk * @return int * @throws AuthenticationException * @throws InvalidArgumentException @@ -71,9 +71,9 @@ class Mongo extends BaseQuery * @throws RuntimeException * @throws BulkWriteException */ - public function mongoExecute(BulkWrite $bulk) + public function execute(BulkWrite $bulk) { - return $this->connection->mongoExecute($this, $bulk); + return $this->connection->execute($this, $bulk); } /** @@ -86,6 +86,7 @@ class Mongo extends BaseQuery */ public function cmd($command, $extra = null, string $db = ''): array { + $this->parseOptions(); return $this->connection->cmd($this, $command, $extra, $db); } @@ -121,12 +122,11 @@ class Mongo extends BaseQuery /** * COUNT查询 * @access public + * @param string $field 字段名 * @return integer */ public function count(string $field = null): int { - $this->parseOptions(); - $result = $this->cmd('count'); return $result[0]['n']; @@ -142,11 +142,8 @@ class Mongo extends BaseQuery */ public function aggregate(string $aggregate, $field, bool $force = false) { - $this->parseOptions(); - $result = $this->cmd('aggregate', [strtolower($aggregate), $field]); - - $value = $result[0]['aggregate'] ?? 0; + $value = $result[0]['aggregate'] ?? 0; if ($force) { $value += 0; @@ -164,8 +161,6 @@ class Mongo extends BaseQuery */ public function multiAggregate(array $aggregate, array $groupBy): array { - $this->parseOptions(); - $result = $this->cmd('multiAggregate', [$aggregate, $groupBy]); foreach ($result as &$row) { @@ -486,6 +481,119 @@ class Mongo extends BaseQuery return md5($this->getConfig('database') . serialize(var_export($data ?: $this->options, true))); } + /** + * 分页查询 + * @access public + * @param int|array $listRows 每页数量 数组表示配置参数 + * @param int|bool $simple 是否简洁模式或者总记录数 + * @return Paginator + * @throws Exception + */ + public function paginate($listRows = null, $simple = false): Paginator + { + if (is_int($simple)) { + $total = $simple; + $simple = false; + } + + $defaultConfig = [ + 'query' => [], //url额外参数 + 'fragment' => '', //url锚点 + 'var_page' => 'page', //分页变量 + 'list_rows' => 15, //每页数量 + ]; + + if (is_array($listRows)) { + $config = array_merge($defaultConfig, $listRows); + $listRows = intval($config['list_rows']); + } else { + $config = $defaultConfig; + $listRows = intval($listRows ?: $config['list_rows']); + } + + $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']); + + $page = $page < 1 ? 1 : $page; + + $config['path'] = $config['path'] ?? Paginator::getCurrentPath(); + + if (!isset($total) && !$simple) { + $options = $this->getOptions(); + + unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']); + + $total = $this->count(); + $results = $this->options($options)->page($page, $listRows)->select(); + } elseif ($simple) { + $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select(); + $total = null; + } else { + $results = $this->page($page, $listRows)->select(); + } + + $this->removeOption('limit'); + $this->removeOption('page'); + + return Paginator::make($results, $listRows, $page, $total, $simple, $config); + } + + /** + * 分批数据返回处理 + * @access public + * @param integer $count 每次处理的数据数量 + * @param callable $callback 处理回调方法 + * @param string|array $column 分批处理的字段名 + * @param string $order 字段排序 + * @return bool + * @throws Exception + */ + public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool + { + $options = $this->getOptions(); + $column = $column ?: $this->getPk(); + + if (isset($options['order'])) { + unset($options['order']); + } + + if (is_array($column)) { + $times = 1; + $query = $this->options($options)->page($times, $count); + } else { + $query = $this->options($options)->limit($count); + + if (strpos($column, '.')) { + list($alias, $key) = explode('.', $column); + } else { + $key = $column; + } + } + + $resultSet = $query->order($column, $order)->select(); + + while (count($resultSet) > 0) { + if (false === call_user_func($callback, $resultSet)) { + return false; + } + + if (isset($times)) { + $times++; + $query = $this->options($options)->page($times, $count); + } else { + $end = $resultSet->pop(); + $lastId = is_array($end) ? $end[$key] : $end->getData($key); + + $query = $this->options($options) + ->limit($count) + ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId); + } + + $resultSet = $query->order($column, $order)->select(); + } + + return true; + } + /** * 分析表达式(可用于查询或者写入操作) * @access public diff --git a/src/db/builder/Mongo.php b/src/db/builder/Mongo.php index 4afe169..85b0600 100644 --- a/src/db/builder/Mongo.php +++ b/src/db/builder/Mongo.php @@ -32,7 +32,7 @@ class Mongo /** * 架构函数 * @access public - * @param Connection $connection 数据库连接对象实例 + * @param Connection $connection 数据库连接对象实例 */ public function __construct(Connection $connection) { @@ -42,9 +42,9 @@ class Mongo /** * 获取当前的连接对象实例 * @access public - * @return void + * @return Connection */ - public function getConnection() + public function getConnection(): Connection { return $this->connection; } @@ -500,7 +500,7 @@ class Mongo $options = $query->getOptions(); $cmd['count'] = $options['table']; - $cmd['query'] = $this->parseWhere($query, $options['where']); + $cmd['query'] = (object) $this->parseWhere($query, $options['where']); foreach (['hint', 'limit', 'maxTimeMS', 'skip'] as $option) { if (isset($options[$option])) { @@ -621,7 +621,7 @@ class Mongo ]; if (!empty($options['where'])) { - $cmd['query'] = $this->parseWhere($query, $options['where']); + $cmd['query'] = (object) $this->parseWhere($query, $options['where']); } if (isset($options['maxTimeMS'])) { diff --git a/src/db/concern/PDOQuery.php b/src/db/concern/PDOQuery.php index a56a47c..23c948f 100644 --- a/src/db/concern/PDOQuery.php +++ b/src/db/concern/PDOQuery.php @@ -14,6 +14,9 @@ namespace think\db\concern; use PDOStatement; use think\db\Fetch; +use think\db\Raw; +use think\helper\Str; + /** * PDO查询支持 */ @@ -21,6 +24,131 @@ trait PDOQuery { use JoinAndViewQuery, ParamsBind, TableFieldInfo; + /** + * 利用__call方法实现一些特殊的Model方法 + * @access public + * @param string $method 方法名称 + * @param array $args 调用参数 + * @return mixed + * @throws Exception + */ + public function __call(string $method, array $args) + { + if (strtolower(substr($method, 0, 5)) == 'getby') { + // 根据某个字段获取记录 + $field = Str::snake(substr($method, 5)); + return $this->where($field, '=', $args[0])->find(); + } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { + // 根据某个字段获取记录的某个值 + $name = Str::snake(substr($method, 10)); + return $this->where($name, '=', $args[0])->value($args[1]); + } elseif (strtolower(substr($method, 0, 7)) == 'whereor') { + $name = Str::snake(substr($method, 7)); + array_unshift($args, $name); + return call_user_func_array([$this, 'whereOr'], $args); + } elseif (strtolower(substr($method, 0, 5)) == 'where') { + $name = Str::snake(substr($method, 5)); + array_unshift($args, $name); + return call_user_func_array([$this, 'where'], $args); + } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { + // 动态调用命名范围 + $method = 'scope' . $method; + array_unshift($args, $this); + + call_user_func_array([$this->model, $method], $args); + return $this; + } else { + throw new Exception('method not exist:' . static::class . '->' . $method); + } + } + + /** + * 表达式方式指定Field排序 + * @access public + * @param string $field 排序字段 + * @param array $bind 参数绑定 + * @return $this + */ + public function orderRaw(string $field, array $bind = []) + { + if (!empty($bind)) { + $this->bindParams($field, $bind); + } + + $this->options['order'][] = new Raw($field); + + return $this; + } + + /** + * 表达式方式指定查询字段 + * @access public + * @param string $field 字段名 + * @return $this + */ + public function fieldRaw(string $field) + { + $this->options['field'][] = new Raw($field); + + return $this; + } + + /** + * 指定Field排序 orderField('id',[1,2,3],'desc') + * @access public + * @param string $field 排序字段 + * @param array $values 排序值 + * @param string $order 排序 desc/asc + * @return $this + */ + public function orderField(string $field, array $values, string $order = '') + { + if (!empty($values)) { + $values['sort'] = $order; + + $this->options['order'][$field] = $values; + } + + return $this; + } + + /** + * 随机排序 + * @access public + * @return $this + */ + public function orderRand() + { + $this->options['order'][] = '[rand]'; + return $this; + } + + /** + * 使用表达式设置数据 + * @access public + * @param string $field 字段名 + * @param string $value 字段值 + * @return $this + */ + public function exp(string $field, string $value) + { + $this->options['data'][$field] = new Raw($value); + return $this; + } + + /** + * 表达式方式指定当前操作的数据表 + * @access public + * @param mixed $table 表名 + * @return $this + */ + public function tableRaw(string $table) + { + $this->options['table'] = new Raw($table); + + return $this; + } + /** * 执行查询 返回数据集 * @access public @@ -284,6 +412,33 @@ trait PDOQuery return $this->autoinc; } + /** + * 字段值增长 + * @access public + * @param string $field 字段名 + * @param float $step 增长值 + * @return $this + */ + public function inc(string $field, float $step = 1) + { + $this->options['data'][$field] = ['INC', $step]; + + return $this; + } + + /** + * 字段值减少 + * @access public + * @param string $field 字段名 + * @param float $step 增长值 + * @return $this + */ + public function dec(string $field, float $step = 1) + { + $this->options['data'][$field] = ['DEC', $step]; + return $this; + } + /** * 获取当前的查询标识 * @access public @@ -325,4 +480,62 @@ trait PDOQuery return $connection->cursor($this); } + /** + * 分批数据返回处理 + * @access public + * @param integer $count 每次处理的数据数量 + * @param callable $callback 处理回调方法 + * @param string|array $column 分批处理的字段名 + * @param string $order 字段排序 + * @return bool + * @throws Exception + */ + public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool + { + $options = $this->getOptions(); + $column = $column ?: $this->getPk(); + + if (isset($options['order'])) { + unset($options['order']); + } + + $bind = $this->bind; + + if (is_array($column)) { + $times = 1; + $query = $this->options($options)->page($times, $count); + } else { + $query = $this->options($options)->limit($count); + + if (strpos($column, '.')) { + list($alias, $key) = explode('.', $column); + } else { + $key = $column; + } + } + + $resultSet = $query->order($column, $order)->select(); + + while (count($resultSet) > 0) { + if (false === call_user_func($callback, $resultSet)) { + return false; + } + + if (isset($times)) { + $times++; + $query = $this->options($options)->page($times, $count); + } else { + $end = $resultSet->pop(); + $lastId = is_array($end) ? $end[$key] : $end->getData($key); + + $query = $this->options($options) + ->limit($count) + ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId); + } + + $resultSet = $query->bind($bind)->order($column, $order)->select(); + } + + return true; + } } diff --git a/src/db/concern/TableFieldInfo.php b/src/db/concern/TableFieldInfo.php index 6e7da01..9070bef 100644 --- a/src/db/concern/TableFieldInfo.php +++ b/src/db/concern/TableFieldInfo.php @@ -33,18 +33,6 @@ trait TableFieldInfo return $this->connection->getTableFields($tableName); } - /** - * 设置字段类型信息 - * @access public - * @param array $type 字段类型信息 - * @return $this - */ - public function setFieldType(array $type) - { - $this->options['field_type'] = $type; - return $this; - } - /** * 获取详细字段类型信息 * @access public diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index e552a77..dad0fbc 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -174,8 +174,10 @@ class Mongo extends Connection implements ConnectionInterface $this->links[$linkNum] = new Manager($config['dsn'], $config['params']); - // 记录数据库连接信息 - $this->db->log('[ MongoDb ] CONNECT :[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); + if (!empty($config['trigger_sql'])) { + // 记录数据库连接信息 + $this->trigger('CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); + } } @@ -284,7 +286,7 @@ class Mongo extends Connection implements ConnectionInterface * @throws ConnectionException * @throws RuntimeException */ - public function mongoQuery(BaseQuery $query, $mongoQuery): array + public function query(BaseQuery $query, $mongoQuery): array { $options = $query->parseOptions(); @@ -329,7 +331,7 @@ class Mongo extends Connection implements ConnectionInterface * @throws RuntimeException * @throws BulkWriteException */ - public function mongoExecute(BaseQuery $query, BulkWrite $bulk) + public function execute(BaseQuery $query, BulkWrite $bulk) { $this->initConnect(true); $this->db->updateQueryTimes(); @@ -414,7 +416,7 @@ class Mongo extends Connection implements ConnectionInterface /** * 获得数据集 * @access protected - * @param string|array $typeMap 指定返回的typeMap + * @param string|array $typeMap 指定返回的typeMap * @return mixed */ protected function getResult($typeMap = null): array @@ -521,15 +523,6 @@ class Mongo extends Connection implements ConnectionInterface return $this->queryStr; } - /** - * 释放查询结果 - * @access public - */ - public function free() - { - $this->cursor = null; - } - /** * 关闭数据库 * @access public @@ -683,7 +676,7 @@ class Mongo extends Connection implements ConnectionInterface // 生成bulk对象 $bulk = $this->builder->insert($query); - $writeResult = $this->mongoExecute($query, $bulk); + $writeResult = $this->execute($query, $bulk); $result = $writeResult->getInsertedCount(); if ($result) { @@ -754,7 +747,7 @@ class Mongo extends Connection implements ConnectionInterface // 生成bulkWrite对象 $bulk = $this->builder->insertAll($query, $dataSet); - $writeResult = $this->mongoExecute($query, $bulk); + $writeResult = $this->execute($query, $bulk); return $writeResult->getInsertedCount(); } @@ -778,7 +771,7 @@ class Mongo extends Connection implements ConnectionInterface // 生成bulkWrite对象 $bulk = $this->builder->update($query); - $writeResult = $this->mongoExecute($query, $bulk); + $writeResult = $this->execute($query, $bulk); $result = $writeResult->getModifiedCount(); @@ -810,7 +803,7 @@ class Mongo extends Connection implements ConnectionInterface $bulk = $this->builder->delete($query); // 执行操作 - $writeResult = $this->mongoExecute($query, $bulk); + $writeResult = $this->execute($query, $bulk); $result = $writeResult->getDeletedCount(); @@ -838,7 +831,7 @@ class Mongo extends Connection implements ConnectionInterface $resultSet = $this->db->trigger('before_select', $query); if (!$resultSet) { - $resultSet = $this->mongoQuery($query, function ($query) { + $resultSet = $this->query($query, function ($query) { return $this->builder->select($query); }); } @@ -865,7 +858,7 @@ class Mongo extends Connection implements ConnectionInterface if (!$result) { // 执行查询 - $resultSet = $this->mongoQuery($query, function ($query) { + $resultSet = $this->query($query, function ($query) { return $this->builder->select($query, true); }); @@ -910,7 +903,7 @@ class Mongo extends Connection implements ConnectionInterface } // 执行查询操作 - $resultSet = $this->mongoQuery($query, $mongoQuery); + $resultSet = $this->query($query, $mongoQuery); if (!empty($resultSet)) { $data = array_shift($resultSet); @@ -970,7 +963,7 @@ class Mongo extends Connection implements ConnectionInterface } // 执行查询操作 - $resultSet = $this->mongoQuery($query, $mongoQuery); + $resultSet = $this->query($query, $mongoQuery); if (('*' == $field || strpos($field, ',')) && $key) { $result = array_column($resultSet, null, $key); -- Gitee From a7e62ad73f51fd8ee02ecbfe30065c5fc96d327b Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 24 Sep 2019 19:21:02 +0800 Subject: [PATCH 106/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BMongo=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8=E7=9A=84table/field/withoutfield=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Mongo.php | 49 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/db/Mongo.php b/src/db/Mongo.php index b590976..637359b 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -202,7 +202,20 @@ class Mongo extends BaseQuery } /** - * 指定当前操作的collection + * 指定当前操作的Collection + * @access public + * @param string $table 表名 + * @return $this + */ + public function table($table) + { + $this->options['table'] = $table; + + return $this; + } + + /** + * table方法的别名 * @access public * @param string $collection * @return $this @@ -336,10 +349,9 @@ class Mongo extends BaseQuery * 设置返回字段 * @access public * @param mixed $field 字段信息 - * @param bool $except 是否排除 * @return $this */ - public function field($field, bool $except = false) + public function field($field) { if (empty($field) || '*' == $field) { return $this; @@ -352,7 +364,7 @@ class Mongo extends BaseQuery $projection = []; foreach ($field as $key => $val) { if (is_numeric($key)) { - $projection[$val] = $except ? 0 : 1; + $projection[$val] = 1; } else { $projection[$key] = $val; } @@ -363,6 +375,35 @@ class Mongo extends BaseQuery return $this; } + /** + * 指定要排除的查询字段 + * @access public + * @param array|string $field 要排除的字段 + * @return $this + */ + public function withoutField($field) + { + if (empty($field) || '*' == $field) { + return $this; + } + + if (is_string($field)) { + $field = array_map('trim', explode(',', $field)); + } + + $projection = []; + foreach ($field as $key => $val) { + if (is_numeric($key)) { + $projection[$val] = 0; + } else { + $projection[$key] = $val; + } + } + + $this->options['projection'] = $projection; + return $this; + } + /** * 设置skip * @access public -- Gitee From 91858ffdf286cd87f8308125e9d758f5aea2e1f2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 24 Sep 2019 19:29:34 +0800 Subject: [PATCH 107/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3mongo=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Mongo.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/db/Mongo.php b/src/db/Mongo.php index 637359b..6f94f3c 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -345,6 +345,17 @@ class Mongo extends BaseQuery return $this; } + /** + * 设置是否REPLACE + * @access public + * @param bool $replace 是否使用REPLACE写入数据 + * @return $this + */ + public function replace(bool $replace = true) + { + return $this; + } + /** * 设置返回字段 * @access public -- Gitee From 908897bcf899f78c55d39eff2713a3acba19b6a3 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 24 Sep 2019 19:59:58 +0800 Subject: [PATCH 108/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/connector/Mongo.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index dad0fbc..647c286 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -492,6 +492,10 @@ class Mongo extends Connection implements ConnectionInterface $this->queryStr .= '.sort(' . json_encode($options['sort']) . ')'; } + if (isset($options['skip'])) { + $this->queryStr .= '.skip(' . $options['skip'] . ')'; + } + if (isset($options['limit'])) { $this->queryStr .= '.limit(' . $options['limit'] . ')'; } @@ -628,8 +632,9 @@ class Mongo extends Connection implements ConnectionInterface $manager = new Manager($this->buildUrl(), $this->config['params']); // 记录数据库连接信息 - - $this->db->log('[ MongoDB ] ReplicaSet CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $this->config['dsn']); + if (!empty($config['trigger_sql'])) { + $this->trigger('CONNECT:ReplicaSet[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $this->config['dsn']); + } return $manager; } -- Gitee From 8d419f482f76050123e7981138b87af99062669f Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 25 Sep 2019 17:29:46 +0800 Subject: [PATCH 109/365] =?UTF-8?q?=E5=8F=96=E6=B6=88PDOQuery?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 48 ++++ src/db/Query.php | 474 ++++++++++++++++++++++++++++++- src/db/concern/PDOQuery.php | 541 ------------------------------------ 3 files changed, 521 insertions(+), 542 deletions(-) delete mode 100644 src/db/concern/PDOQuery.php diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 71e7dd1..3f0f07f 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -80,6 +80,44 @@ abstract class BaseQuery $this->prefix = $this->connection->getConfig('prefix'); } + /** + * 利用__call方法实现一些特殊的Model方法 + * @access public + * @param string $method 方法名称 + * @param array $args 调用参数 + * @return mixed + * @throws Exception + */ + public function __call(string $method, array $args) + { + if (strtolower(substr($method, 0, 5)) == 'getby') { + // 根据某个字段获取记录 + $field = Str::snake(substr($method, 5)); + return $this->where($field, '=', $args[0])->find(); + } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { + // 根据某个字段获取记录的某个值 + $name = Str::snake(substr($method, 10)); + return $this->where($name, '=', $args[0])->value($args[1]); + } elseif (strtolower(substr($method, 0, 7)) == 'whereor') { + $name = Str::snake(substr($method, 7)); + array_unshift($args, $name); + return call_user_func_array([$this, 'whereOr'], $args); + } elseif (strtolower(substr($method, 0, 5)) == 'where') { + $name = Str::snake(substr($method, 5)); + array_unshift($args, $name); + return call_user_func_array([$this, 'where'], $args); + } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { + // 动态调用命名范围 + $method = 'scope' . $method; + array_unshift($args, $this); + + call_user_func_array([$this->model, $method], $args); + return $this; + } else { + throw new Exception('method not exist:' . static::class . '->' . $method); + } + } + /** * 创建一个新的查询对象 * @access public @@ -184,6 +222,16 @@ abstract class BaseQuery return $this->connection->getLastSql(); } + /** + * 获取返回或者影响的记录数 + * @access public + * @return integer + */ + public function getNumRows(): int + { + return $this->connection->getNumRows(); + } + /** * 获取最近插入的ID * @access public diff --git a/src/db/Query.php b/src/db/Query.php index bb749c2..ba0e5e5 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -12,10 +12,482 @@ declare (strict_types = 1); namespace think\db; +use PDOStatement; +use think\helper\Str; + /** * PDO数据查询类 */ class Query extends BaseQuery { - use concern\PDOQuery; + use concern\JoinAndViewQuery; + use concern\ParamsBind; + use concern\TableFieldInfo; + + /** + * 表达式方式指定Field排序 + * @access public + * @param string $field 排序字段 + * @param array $bind 参数绑定 + * @return $this + */ + public function orderRaw(string $field, array $bind = []) + { + if (!empty($bind)) { + $this->bindParams($field, $bind); + } + + $this->options['order'][] = new Raw($field); + + return $this; + } + + /** + * 表达式方式指定查询字段 + * @access public + * @param string $field 字段名 + * @return $this + */ + public function fieldRaw(string $field) + { + $this->options['field'][] = new Raw($field); + + return $this; + } + + /** + * 指定Field排序 orderField('id',[1,2,3],'desc') + * @access public + * @param string $field 排序字段 + * @param array $values 排序值 + * @param string $order 排序 desc/asc + * @return $this + */ + public function orderField(string $field, array $values, string $order = '') + { + if (!empty($values)) { + $values['sort'] = $order; + + $this->options['order'][$field] = $values; + } + + return $this; + } + + /** + * 随机排序 + * @access public + * @return $this + */ + public function orderRand() + { + $this->options['order'][] = '[rand]'; + return $this; + } + + /** + * 使用表达式设置数据 + * @access public + * @param string $field 字段名 + * @param string $value 字段值 + * @return $this + */ + public function exp(string $field, string $value) + { + $this->options['data'][$field] = new Raw($value); + return $this; + } + + /** + * 表达式方式指定当前操作的数据表 + * @access public + * @param mixed $table 表名 + * @return $this + */ + public function tableRaw(string $table) + { + $this->options['table'] = new Raw($table); + + return $this; + } + + /** + * 执行查询 返回数据集 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @return array + * @throws BindParamException + * @throws PDOException + */ + public function query(string $sql, array $bind = []): array + { + return $this->connection->query($this, $sql, $bind); + } + + /** + * 执行语句 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @return int + * @throws BindParamException + * @throws PDOException + */ + public function execute(string $sql, array $bind = []): int + { + return $this->connection->execute($this, $sql, $bind, true); + } + + /** + * 获取执行的SQL语句而不进行实际的查询 + * @access public + * @param bool $fetch 是否返回sql + * @return $this|Fetch + */ + public function fetchSql(bool $fetch = true) + { + $this->options['fetch_sql'] = $fetch; + + if ($fetch) { + return new Fetch($this); + } + + return $this; + } + + /** + * 批处理执行SQL语句 + * 批处理的指令都认为是execute操作 + * @access public + * @param array $sql SQL批处理指令 + * @return bool + */ + public function batchQuery(array $sql = []): bool + { + return $this->connection->batchQuery($this, $sql); + } + + /** + * USING支持 用于多表删除 + * @access public + * @param mixed $using USING + * @return $this + */ + public function using($using) + { + $this->options['using'] = $using; + return $this; + } + + /** + * 存储过程调用 + * @access public + * @param bool $procedure 是否为存储过程查询 + * @return $this + */ + public function procedure(bool $procedure = true) + { + $this->options['procedure'] = $procedure; + return $this; + } + + /** + * 指定group查询 + * @access public + * @param string|array $group GROUP + * @return $this + */ + public function group($group) + { + $this->options['group'] = $group; + return $this; + } + + /** + * 指定having查询 + * @access public + * @param string $having having + * @return $this + */ + public function having(string $having) + { + $this->options['having'] = $having; + return $this; + } + + /** + * 指定distinct查询 + * @access public + * @param bool $distinct 是否唯一 + * @return $this + */ + public function distinct(bool $distinct = true) + { + $this->options['distinct'] = $distinct; + return $this; + } + + /** + * 设置自增序列名 + * @access public + * @param string $sequence 自增序列名 + * @return $this + */ + public function sequence(string $sequence = null) + { + $this->options['sequence'] = $sequence; + return $this; + } + + /** + * 指定强制索引 + * @access public + * @param string $force 索引名称 + * @return $this + */ + public function force(string $force) + { + $this->options['force'] = $force; + return $this; + } + + /** + * 查询注释 + * @access public + * @param string $comment 注释 + * @return $this + */ + public function comment(string $comment) + { + $this->options['comment'] = $comment; + return $this; + } + + /** + * 设置是否REPLACE + * @access public + * @param bool $replace 是否使用REPLACE写入数据 + * @return $this + */ + public function replace(bool $replace = true) + { + $this->options['replace'] = $replace; + return $this; + } + + /** + * 设置当前查询所在的分区 + * @access public + * @param string|array $partition 分区名称 + * @return $this + */ + public function partition($partition) + { + $this->options['partition'] = $partition; + return $this; + } + + /** + * 设置DUPLICATE + * @access public + * @param array|string|Raw $duplicate DUPLICATE信息 + * @return $this + */ + public function duplicate($duplicate) + { + $this->options['duplicate'] = $duplicate; + return $this; + } + + /** + * 设置查询的额外参数 + * @access public + * @param string $extra 额外信息 + * @return $this + */ + public function extra(string $extra) + { + $this->options['extra'] = $extra; + return $this; + } + + /** + * 创建子查询SQL + * @access public + * @param bool $sub 是否添加括号 + * @return string + * @throws Exception + */ + public function buildSql(bool $sub = true): string + { + return $sub ? '( ' . $this->fetchSql()->select() . ' )' : $this->fetchSql()->select(); + } + + /** + * 获取当前数据表的主键 + * @access public + * @return string|array + */ + public function getPk() + { + if (empty($this->pk)) { + $this->pk = $this->connection->getPk($this->getTable()); + } + + return $this->pk; + } + + /** + * 指定数据表自增主键 + * @access public + * @param string $autoinc 自增键 + * @return $this + */ + public function autoinc(string $autoinc) + { + $this->autoinc = $autoinc; + return $this; + } + + /** + * 获取当前数据表的自增主键 + * @access public + * @return string + */ + public function getAutoInc() + { + if (empty($this->autoinc)) { + $this->autoinc = $this->connection->getAutoInc($this->getTable()); + } + + return $this->autoinc; + } + + /** + * 字段值增长 + * @access public + * @param string $field 字段名 + * @param float $step 增长值 + * @return $this + */ + public function inc(string $field, float $step = 1) + { + $this->options['data'][$field] = ['INC', $step]; + + return $this; + } + + /** + * 字段值减少 + * @access public + * @param string $field 字段名 + * @param float $step 增长值 + * @return $this + */ + public function dec(string $field, float $step = 1) + { + $this->options['data'][$field] = ['DEC', $step]; + return $this; + } + + /** + * 获取当前的查询标识 + * @access public + * @param mixed $data 要序列化的数据 + * @return string + */ + public function getQueryGuid($data = null): string + { + return md5($this->getConfig('database') . serialize(var_export($data ?: $this->options, true)) . serialize($this->getBind(false))); + } + + /** + * 执行查询但只返回PDOStatement对象 + * @access public + * @return PDOStatement + */ + public function getPdo(): PDOStatement + { + return $this->connection->pdo($this); + } + + /** + * 使用游标查找记录 + * @access public + * @param mixed $data 数据 + * @return \Generator + */ + public function cursor($data = null) + { + if (!is_null($data)) { + // 主键条件分析 + $this->parsePkWhere($data); + } + + $this->options['data'] = $data; + + $connection = clone $this->connection; + + return $connection->cursor($this); + } + + /** + * 分批数据返回处理 + * @access public + * @param integer $count 每次处理的数据数量 + * @param callable $callback 处理回调方法 + * @param string|array $column 分批处理的字段名 + * @param string $order 字段排序 + * @return bool + * @throws Exception + */ + public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool + { + $options = $this->getOptions(); + $column = $column ?: $this->getPk(); + + if (isset($options['order'])) { + unset($options['order']); + } + + $bind = $this->bind; + + if (is_array($column)) { + $times = 1; + $query = $this->options($options)->page($times, $count); + } else { + $query = $this->options($options)->limit($count); + + if (strpos($column, '.')) { + list($alias, $key) = explode('.', $column); + } else { + $key = $column; + } + } + + $resultSet = $query->order($column, $order)->select(); + + while (count($resultSet) > 0) { + if (false === call_user_func($callback, $resultSet)) { + return false; + } + + if (isset($times)) { + $times++; + $query = $this->options($options)->page($times, $count); + } else { + $end = $resultSet->pop(); + $lastId = is_array($end) ? $end[$key] : $end->getData($key); + + $query = $this->options($options) + ->limit($count) + ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId); + } + + $resultSet = $query->bind($bind)->order($column, $order)->select(); + } + + return true; + } } diff --git a/src/db/concern/PDOQuery.php b/src/db/concern/PDOQuery.php deleted file mode 100644 index 23c948f..0000000 --- a/src/db/concern/PDOQuery.php +++ /dev/null @@ -1,541 +0,0 @@ - -// +---------------------------------------------------------------------- -declare (strict_types = 1); - -namespace think\db\concern; - -use PDOStatement; -use think\db\Fetch; -use think\db\Raw; -use think\helper\Str; - -/** - * PDO查询支持 - */ -trait PDOQuery -{ - use JoinAndViewQuery, ParamsBind, TableFieldInfo; - - /** - * 利用__call方法实现一些特殊的Model方法 - * @access public - * @param string $method 方法名称 - * @param array $args 调用参数 - * @return mixed - * @throws Exception - */ - public function __call(string $method, array $args) - { - if (strtolower(substr($method, 0, 5)) == 'getby') { - // 根据某个字段获取记录 - $field = Str::snake(substr($method, 5)); - return $this->where($field, '=', $args[0])->find(); - } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { - // 根据某个字段获取记录的某个值 - $name = Str::snake(substr($method, 10)); - return $this->where($name, '=', $args[0])->value($args[1]); - } elseif (strtolower(substr($method, 0, 7)) == 'whereor') { - $name = Str::snake(substr($method, 7)); - array_unshift($args, $name); - return call_user_func_array([$this, 'whereOr'], $args); - } elseif (strtolower(substr($method, 0, 5)) == 'where') { - $name = Str::snake(substr($method, 5)); - array_unshift($args, $name); - return call_user_func_array([$this, 'where'], $args); - } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { - // 动态调用命名范围 - $method = 'scope' . $method; - array_unshift($args, $this); - - call_user_func_array([$this->model, $method], $args); - return $this; - } else { - throw new Exception('method not exist:' . static::class . '->' . $method); - } - } - - /** - * 表达式方式指定Field排序 - * @access public - * @param string $field 排序字段 - * @param array $bind 参数绑定 - * @return $this - */ - public function orderRaw(string $field, array $bind = []) - { - if (!empty($bind)) { - $this->bindParams($field, $bind); - } - - $this->options['order'][] = new Raw($field); - - return $this; - } - - /** - * 表达式方式指定查询字段 - * @access public - * @param string $field 字段名 - * @return $this - */ - public function fieldRaw(string $field) - { - $this->options['field'][] = new Raw($field); - - return $this; - } - - /** - * 指定Field排序 orderField('id',[1,2,3],'desc') - * @access public - * @param string $field 排序字段 - * @param array $values 排序值 - * @param string $order 排序 desc/asc - * @return $this - */ - public function orderField(string $field, array $values, string $order = '') - { - if (!empty($values)) { - $values['sort'] = $order; - - $this->options['order'][$field] = $values; - } - - return $this; - } - - /** - * 随机排序 - * @access public - * @return $this - */ - public function orderRand() - { - $this->options['order'][] = '[rand]'; - return $this; - } - - /** - * 使用表达式设置数据 - * @access public - * @param string $field 字段名 - * @param string $value 字段值 - * @return $this - */ - public function exp(string $field, string $value) - { - $this->options['data'][$field] = new Raw($value); - return $this; - } - - /** - * 表达式方式指定当前操作的数据表 - * @access public - * @param mixed $table 表名 - * @return $this - */ - public function tableRaw(string $table) - { - $this->options['table'] = new Raw($table); - - return $this; - } - - /** - * 执行查询 返回数据集 - * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @return array - * @throws BindParamException - * @throws PDOException - */ - public function query(string $sql, array $bind = []): array - { - return $this->connection->query($this, $sql, $bind); - } - - /** - * 执行语句 - * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @return int - * @throws BindParamException - * @throws PDOException - */ - public function execute(string $sql, array $bind = []): int - { - return $this->connection->execute($this, $sql, $bind, true); - } - - /** - * 获取返回或者影响的记录数 - * @access public - * @return integer - */ - public function getNumRows(): int - { - return $this->connection->getNumRows(); - } - - /** - * 获取执行的SQL语句而不进行实际的查询 - * @access public - * @param bool $fetch 是否返回sql - * @return $this|Fetch - */ - public function fetchSql(bool $fetch = true) - { - $this->options['fetch_sql'] = $fetch; - - if ($fetch) { - return new Fetch($this); - } - - return $this; - } - - /** - * 批处理执行SQL语句 - * 批处理的指令都认为是execute操作 - * @access public - * @param array $sql SQL批处理指令 - * @return bool - */ - public function batchQuery(array $sql = []): bool - { - return $this->connection->batchQuery($this, $sql); - } - - /** - * USING支持 用于多表删除 - * @access public - * @param mixed $using USING - * @return $this - */ - public function using($using) - { - $this->options['using'] = $using; - return $this; - } - - /** - * 存储过程调用 - * @access public - * @param bool $procedure 是否为存储过程查询 - * @return $this - */ - public function procedure(bool $procedure = true) - { - $this->options['procedure'] = $procedure; - return $this; - } - - /** - * 指定group查询 - * @access public - * @param string|array $group GROUP - * @return $this - */ - public function group($group) - { - $this->options['group'] = $group; - return $this; - } - - /** - * 指定having查询 - * @access public - * @param string $having having - * @return $this - */ - public function having(string $having) - { - $this->options['having'] = $having; - return $this; - } - - /** - * 指定distinct查询 - * @access public - * @param bool $distinct 是否唯一 - * @return $this - */ - public function distinct(bool $distinct = true) - { - $this->options['distinct'] = $distinct; - return $this; - } - - /** - * 设置自增序列名 - * @access public - * @param string $sequence 自增序列名 - * @return $this - */ - public function sequence(string $sequence = null) - { - $this->options['sequence'] = $sequence; - return $this; - } - - /** - * 指定强制索引 - * @access public - * @param string $force 索引名称 - * @return $this - */ - public function force(string $force) - { - $this->options['force'] = $force; - return $this; - } - - /** - * 查询注释 - * @access public - * @param string $comment 注释 - * @return $this - */ - public function comment(string $comment) - { - $this->options['comment'] = $comment; - return $this; - } - - /** - * 设置是否REPLACE - * @access public - * @param bool $replace 是否使用REPLACE写入数据 - * @return $this - */ - public function replace(bool $replace = true) - { - $this->options['replace'] = $replace; - return $this; - } - - /** - * 设置当前查询所在的分区 - * @access public - * @param string|array $partition 分区名称 - * @return $this - */ - public function partition($partition) - { - $this->options['partition'] = $partition; - return $this; - } - - /** - * 设置DUPLICATE - * @access public - * @param array|string|Raw $duplicate DUPLICATE信息 - * @return $this - */ - public function duplicate($duplicate) - { - $this->options['duplicate'] = $duplicate; - return $this; - } - - /** - * 设置查询的额外参数 - * @access public - * @param string $extra 额外信息 - * @return $this - */ - public function extra(string $extra) - { - $this->options['extra'] = $extra; - return $this; - } - - /** - * 创建子查询SQL - * @access public - * @param bool $sub 是否添加括号 - * @return string - * @throws Exception - */ - public function buildSql(bool $sub = true): string - { - return $sub ? '( ' . $this->fetchSql()->select() . ' )' : $this->fetchSql()->select(); - } - - /** - * 获取当前数据表的主键 - * @access public - * @return string|array - */ - public function getPk() - { - if (empty($this->pk)) { - $this->pk = $this->connection->getPk($this->getTable()); - } - - return $this->pk; - } - - /** - * 指定数据表自增主键 - * @access public - * @param string $autoinc 自增键 - * @return $this - */ - public function autoinc(string $autoinc) - { - $this->autoinc = $autoinc; - return $this; - } - - /** - * 获取当前数据表的自增主键 - * @access public - * @return string - */ - public function getAutoInc() - { - if (empty($this->autoinc)) { - $this->autoinc = $this->connection->getAutoInc($this->getTable()); - } - - return $this->autoinc; - } - - /** - * 字段值增长 - * @access public - * @param string $field 字段名 - * @param float $step 增长值 - * @return $this - */ - public function inc(string $field, float $step = 1) - { - $this->options['data'][$field] = ['INC', $step]; - - return $this; - } - - /** - * 字段值减少 - * @access public - * @param string $field 字段名 - * @param float $step 增长值 - * @return $this - */ - public function dec(string $field, float $step = 1) - { - $this->options['data'][$field] = ['DEC', $step]; - return $this; - } - - /** - * 获取当前的查询标识 - * @access public - * @param mixed $data 要序列化的数据 - * @return string - */ - public function getQueryGuid($data = null): string - { - return md5($this->getConfig('database') . serialize(var_export($data ?: $this->options, true)) . serialize($this->getBind(false))); - } - - /** - * 执行查询但只返回PDOStatement对象 - * @access public - * @return PDOStatement - */ - public function getPdo(): PDOStatement - { - return $this->connection->pdo($this); - } - - /** - * 使用游标查找记录 - * @access public - * @param mixed $data 数据 - * @return \Generator - */ - public function cursor($data = null) - { - if (!is_null($data)) { - // 主键条件分析 - $this->parsePkWhere($data); - } - - $this->options['data'] = $data; - - $connection = clone $this->connection; - - return $connection->cursor($this); - } - - /** - * 分批数据返回处理 - * @access public - * @param integer $count 每次处理的数据数量 - * @param callable $callback 处理回调方法 - * @param string|array $column 分批处理的字段名 - * @param string $order 字段排序 - * @return bool - * @throws Exception - */ - public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool - { - $options = $this->getOptions(); - $column = $column ?: $this->getPk(); - - if (isset($options['order'])) { - unset($options['order']); - } - - $bind = $this->bind; - - if (is_array($column)) { - $times = 1; - $query = $this->options($options)->page($times, $count); - } else { - $query = $this->options($options)->limit($count); - - if (strpos($column, '.')) { - list($alias, $key) = explode('.', $column); - } else { - $key = $column; - } - } - - $resultSet = $query->order($column, $order)->select(); - - while (count($resultSet) > 0) { - if (false === call_user_func($callback, $resultSet)) { - return false; - } - - if (isset($times)) { - $times++; - $query = $this->options($options)->page($times, $count); - } else { - $end = $resultSet->pop(); - $lastId = is_array($end) ? $end[$key] : $end->getData($key); - - $query = $this->options($options) - ->limit($count) - ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId); - } - - $resultSet = $query->bind($bind)->order($column, $order)->select(); - } - - return true; - } -} -- Gitee From e1d2896909fd49b1e558e9393dae7213d0bbdcdb Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 25 Sep 2019 19:50:29 +0800 Subject: [PATCH 110/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E4=B8=80=E5=AF=B9?= =?UTF-8?q?=E4=B8=80=E5=85=B3=E8=81=94=E7=9A=84=E5=B1=9E=E6=80=A7=E7=BB=91?= =?UTF-8?q?=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsTo.php | 5 +++++ src/model/relation/HasOne.php | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 25b9c11..3372d14 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -67,6 +67,11 @@ class BelongsTo extends OneToOne ->find(); if ($relationModel) { + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($relationModel, $this->parent); + } + $relationModel->setParent(clone $this->parent); } diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index fef18ed..2d8437c 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -66,6 +66,11 @@ class HasOne extends OneToOne ->find(); if ($relationModel) { + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($relationModel, $this->parent); + } + $relationModel->setParent(clone $this->parent); } -- Gitee From eb6bd49ac86cecd58e679a3700017406e619e1e7 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 26 Sep 2019 08:27:41 +0800 Subject: [PATCH 111/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E9=97=AD=E5=8C=85?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 3f0f07f..83362a2 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -137,6 +137,14 @@ abstract class BaseQuery $query->name($this->name); } + if (isset($this->options['json'])) { + $query->json($this->options['json'], $this->options['json_assoc']); + } + + if (isset($this->options['field_type'])) { + $query->setFieldType($this->options['field_type']); + } + return $query; } -- Gitee From 01f530a70a31b461557937ea75a7713862418b32 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 27 Sep 2019 20:49:28 +0800 Subject: [PATCH 112/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3oracle=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/connector/Oracle.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/db/connector/Oracle.php b/src/db/connector/Oracle.php index 8a4ef11..1c80323 100644 --- a/src/db/connector/Oracle.php +++ b/src/db/connector/Oracle.php @@ -10,6 +10,7 @@ namespace think\db\connector; use PDO; +use think\db\BaseQuery; use think\db\PDOConnection; /** @@ -97,10 +98,11 @@ class Oracle extends PDOConnection /** * 获取最近插入的ID * @access public - * @param string $sequence 自增序列名 - * @return string + * @param BaseQuery $query 查询对象 + * @param string $sequence 自增序列名 + * @return mixed */ - public function getLastInsID(string $sequence = null): string + public function getLastInsID(BaseQuery $query, string $sequence = null) { $pdo = $this->linkID->query("select {$sequence}.currval as id from dual"); $result = $pdo->fetchColumn(); -- Gitee From 8ef883759fc94512bf5c62468c8dc0d8dc176afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E9=B8=BF=E8=95=8A?= Date: Thu, 10 Oct 2019 09:02:15 +0800 Subject: [PATCH 113/365] =?UTF-8?q?strtoupper=E5=89=8D=E5=A2=9E=E5=8A=A0is?= =?UTF-8?q?=5Fstring=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/WhereQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/concern/WhereQuery.php b/src/db/concern/WhereQuery.php index 0bb6090..33b0763 100644 --- a/src/db/concern/WhereQuery.php +++ b/src/db/concern/WhereQuery.php @@ -429,7 +429,7 @@ trait WhereQuery // 字段相等查询 $where = $this->whereEq($field, $op); } - } elseif (in_array(strtoupper($op), ['EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) { + } elseif (is_string($op) && in_array(strtoupper($op), ['EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) { $where = [$field, $op, is_string($condition) ? new Raw($condition) : $condition]; } else { $where = $field ? [$field, $op, $condition, $param[2] ?? null] : []; -- Gitee From 1752dfab0dabdda06b1ce49ceb16374875541726 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 18 Oct 2019 22:43:21 +0800 Subject: [PATCH 114/365] =?UTF-8?q?insertAll=E6=96=B9=E6=B3=95=E5=A4=A7?= =?UTF-8?q?=E9=87=8F=E6=95=B0=E6=8D=AE=E5=86=99=E5=85=A5=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 20f1034..b15d42b 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -899,6 +899,10 @@ abstract class PDOConnection extends Connection implements ConnectionInterface $query->parseOptions(); + if (0 === $limit && count($dataSet) >= 5000) { + $limit = 1000; + } + if ($limit) { // 分批写入 自动启动事务支持 $this->startTrans(); -- Gitee From dc09c77e433d8eb781120a6df19d3e42cc1eb97f Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Tue, 22 Oct 2019 01:06:07 +0800 Subject: [PATCH 115/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E4=B8=8A=E4=BD=BF=E7=94=A8=E9=9B=86=E5=90=88?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E7=9A=84=E8=BF=94=E5=9B=9E=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Paginator.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Paginator.php b/src/Paginator.php index 2ae2aae..06999a1 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -24,7 +24,7 @@ use Traversable; /** * 分页基础类 - * @method array all() + * @mixin Collection */ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable { @@ -484,11 +484,10 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J public function __call($name, $arguments) { - $collection = $this->getCollection(); + $result = call_user_func_array([$this->items, $name], $arguments); - $result = call_user_func_array([$collection, $name], $arguments); - - if ($result === $collection) { + if ($result instanceof Collection) { + $this->items = $result; return $this; } -- Gitee From 09bc0206370090376b92b306e3677ced69257781 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 22 Oct 2019 23:19:51 +0800 Subject: [PATCH 116/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=85=B3=E8=81=94?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E7=9A=84=E4=B8=A5=E6=A0=BC=E5=A4=A7=E5=B0=8F?= =?UTF-8?q?=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 4 ++-- src/model/concern/Attribute.php | 2 +- src/model/concern/RelationShip.php | 10 +++++----- src/model/relation/BelongsTo.php | 11 ++++------- src/model/relation/BelongsToMany.php | 14 +++++--------- src/model/relation/HasMany.php | 14 +++++--------- src/model/relation/HasManyThrough.php | 14 +++++--------- src/model/relation/HasOne.php | 11 ++++------- src/model/relation/HasOneThrough.php | 14 +++++--------- src/model/relation/MorphMany.php | 14 +++++--------- src/model/relation/MorphOne.php | 14 +++++--------- src/model/relation/MorphTo.php | 6 ++---- src/model/relation/OneToOne.php | 5 ++--- 13 files changed, 50 insertions(+), 83 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 20d2bde..9a21137 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -296,9 +296,9 @@ trait ModelRelationQuery $relations = (array) $relation; foreach ($relations as $name => $relation) { if (!is_numeric($name)) { - $this->options['with_cache'][Str::snake($name)] = is_array($relation) ? $relation : [$key, $relation, $tag]; + $this->options['with_cache'][$name] = is_array($relation) ? $relation : [$key, $relation, $tag]; } else { - $this->options['with_cache'][Str::snake($relation)] = [$key, $expire, $tag]; + $this->options['with_cache'][$relation] = [$key, $expire, $tag]; } } diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index f62c3d5..8ea0d67 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -179,7 +179,7 @@ trait Attribute /** * 获取实际的字段名 - * @access public + * @access protected * @param string $name 字段名 * @return string */ diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index ad9be80..dd75b8e 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -120,7 +120,7 @@ trait RelationShip $value = $this->$method($value, array_merge($this->data, $data)); } - $this->relation[$name] = $value; + $this->relation[$this->getRealFieldName($name)] = $value; return $this; } @@ -268,8 +268,8 @@ trait RelationShip $subRelation = [$subRelation]; } + $relationName = $relation; $relation = Str::camel($relation); - $relationName = Str::snake($relation); $relationResult = $this->$relation(); @@ -283,7 +283,7 @@ trait RelationShip $relationCache = $cache[$relationName] ?? $cache; } - $relationResult->eagerlyResultSet($resultSet, $relation, $subRelation, $closure, $relationCache, $join); + $relationResult->eagerlyResultSet($resultSet, $relationName, $subRelation, $closure, $relationCache, $join); } } @@ -317,8 +317,8 @@ trait RelationShip $subRelation = [$subRelation]; } + $relationName = $relation; $relation = Str::camel($relation); - $relationName = Str::snake($relation); $relationResult = $this->$relation(); @@ -332,7 +332,7 @@ trait RelationShip $relationCache = $cache[$relationName] ?? []; } - $relationResult->eagerlyResult($result, $relation, $subRelation, $closure, $relationCache, $join); + $relationResult->eagerlyResult($result, $relationName, $subRelation, $closure, $relationCache, $join); } } diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 3372d14..76c7019 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -220,10 +220,7 @@ class BelongsTo extends OneToOne $data = $this->eagerlyWhere([ [$localKey, 'in', $range], - ], $localKey, $relation, $subRelation, $closure, $cache); - - // 关联属性名 - $attr = Str::snake($relation); + ], $localKey, $subRelation, $closure, $cache); // 关联数据封装 foreach ($resultSet as $result) { @@ -241,7 +238,7 @@ class BelongsTo extends OneToOne $this->bindAttr($relationModel, $result); } else { // 设置关联属性 - $result->setRelation($attr, $relationModel); + $result->setRelation($relation, $relationModel); } } } @@ -266,7 +263,7 @@ class BelongsTo extends OneToOne $data = $this->eagerlyWhere([ [$localKey, '=', $result->$foreignKey], - ], $localKey, $relation, $subRelation, $closure, $cache); + ], $localKey, $subRelation, $closure, $cache); // 关联模型 if (!isset($data[$result->$foreignKey])) { @@ -282,7 +279,7 @@ class BelongsTo extends OneToOne $this->bindAttr($relationModel, $result); } else { // 设置关联属性 - $result->setRelation(Str::snake($relation), $relationModel); + $result->setRelation($relation, $relationModel); } } diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 68a771e..16f4bf5 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -327,10 +327,7 @@ class BelongsToMany extends Relation // 查询关联数据 $data = $this->eagerlyManyToMany([ ['pivot.' . $localKey, 'in', $range], - ], $relation, $subRelation, $closure, $cache); - - // 关联属性名 - $attr = Str::snake($relation); + ], $subRelation, $closure, $cache); // 关联数据封装 foreach ($resultSet as $result) { @@ -338,7 +335,7 @@ class BelongsToMany extends Relation $data[$result->$pk] = []; } - $result->setRelation($attr, $this->resultSetBuild($data[$result->$pk], clone $this->parent)); + $result->setRelation($relation, $this->resultSetBuild($data[$result->$pk], clone $this->parent)); } } } @@ -362,14 +359,14 @@ class BelongsToMany extends Relation // 查询管理数据 $data = $this->eagerlyManyToMany([ ['pivot.' . $this->localKey, '=', $pk], - ], $relation, $subRelation, $closure, $cache); + ], $subRelation, $closure, $cache); // 关联数据封装 if (!isset($data[$pk])) { $data[$pk] = []; } - $result->setRelation(Str::snake($relation), $this->resultSetBuild($data[$pk], clone $this->parent)); + $result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent)); } } @@ -428,13 +425,12 @@ class BelongsToMany extends Relation * 多对多 关联模型预查询 * @access protected * @param array $where 关联预查询条件 - * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure 闭包 * @param array $cache 关联缓存 * @return array */ - protected function eagerlyManyToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): array + protected function eagerlyManyToMany(array $where, array $subRelation = [], Closure $closure = null, array $cache = []): array { if ($closure) { $closure($this->getClosureType($closure)); diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index 5a63d03..aa46a88 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -94,10 +94,7 @@ class HasMany extends Relation if (!empty($range)) { $data = $this->eagerlyOneToMany([ [$this->foreignKey, 'in', $range], - ], $relation, $subRelation, $closure, $cache); - - // 关联属性名 - $attr = Str::snake($relation); + ], $subRelation, $closure, $cache); // 关联数据封装 foreach ($resultSet as $result) { @@ -106,7 +103,7 @@ class HasMany extends Relation $data[$pk] = []; } - $result->setRelation($attr, $this->resultSetBuild($data[$pk], clone $this->parent)); + $result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent)); } } } @@ -129,14 +126,14 @@ class HasMany extends Relation $pk = $result->$localKey; $data = $this->eagerlyOneToMany([ [$this->foreignKey, '=', $pk], - ], $relation, $subRelation, $closure, $cache); + ], $subRelation, $closure, $cache); // 关联数据封装 if (!isset($data[$pk])) { $data[$pk] = []; } - $result->setRelation(Str::snake($relation), $this->resultSetBuild($data[$pk], clone $this->parent)); + $result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent)); } } @@ -192,13 +189,12 @@ class HasMany extends Relation * 一对多 关联模型预查询 * @access public * @param array $where 关联预查询条件 - * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure * @param array $cache 关联缓存 * @return array */ - protected function eagerlyOneToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): array + protected function eagerlyOneToMany(array $where, array $subRelation = [], Closure $closure = null, array $cache = []): array { $foreignKey = $this->foreignKey; diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index 7909043..23367f3 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -191,10 +191,7 @@ class HasManyThrough extends Relation $data = $this->eagerlyWhere([ [$this->foreignKey, 'in', $range], - ], $foreignKey, $relation, $subRelation, $closure, $cache); - - // 关联属性名 - $attr = Str::snake($relation); + ], $foreignKey, $subRelation, $closure, $cache); // 关联数据封装 foreach ($resultSet as $result) { @@ -204,7 +201,7 @@ class HasManyThrough extends Relation } // 设置关联属性 - $result->setRelation($attr, $this->resultSetBuild($data[$pk], clone $this->parent)); + $result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent)); } } } @@ -229,14 +226,14 @@ class HasManyThrough extends Relation $data = $this->eagerlyWhere([ [$foreignKey, '=', $pk], - ], $foreignKey, $relation, $subRelation, $closure, $cache); + ], $foreignKey, $subRelation, $closure, $cache); // 关联数据封装 if (!isset($data[$pk])) { $data[$pk] = []; } - $result->setRelation(Str::snake($relation), $this->resultSetBuild($data[$pk], clone $this->parent)); + $result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent)); } /** @@ -244,13 +241,12 @@ class HasManyThrough extends Relation * @access public * @param array $where 关联预查询条件 * @param string $key 关联键名 - * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure * @param array $cache 关联缓存 * @return array */ - protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): array + protected function eagerlyWhere(array $where, string $key, array $subRelation = [], Closure $closure = null, array $cache = []): array { // 预载入关联查询 支持嵌套预载入 $throughList = $this->through->where($where)->select(); diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 2d8437c..98bdf89 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -219,10 +219,7 @@ class HasOne extends OneToOne $data = $this->eagerlyWhere([ [$foreignKey, 'in', $range], - ], $foreignKey, $relation, $subRelation, $closure, $cache); - - // 关联属性名 - $attr = Str::snake($relation); + ], $foreignKey, $subRelation, $closure, $cache); // 关联数据封装 foreach ($resultSet as $result) { @@ -240,7 +237,7 @@ class HasOne extends OneToOne $this->bindAttr($relationModel, $result); } else { // 设置关联属性 - $result->setRelation($attr, $relationModel); + $result->setRelation($relation, $relationModel); } } } @@ -265,7 +262,7 @@ class HasOne extends OneToOne $data = $this->eagerlyWhere([ [$foreignKey, '=', $result->$localKey], - ], $foreignKey, $relation, $subRelation, $closure, $cache); + ], $foreignKey, $subRelation, $closure, $cache); // 关联模型 if (!isset($data[$result->$localKey])) { @@ -280,7 +277,7 @@ class HasOne extends OneToOne // 绑定关联属性 $this->bindAttr($relationModel, $result); } else { - $result->setRelation(Str::snake($relation), $relationModel); + $result->setRelation($relation, $relationModel); } } diff --git a/src/model/relation/HasOneThrough.php b/src/model/relation/HasOneThrough.php index 2b4d57f..8ec42df 100644 --- a/src/model/relation/HasOneThrough.php +++ b/src/model/relation/HasOneThrough.php @@ -73,10 +73,7 @@ class HasOneThrough extends HasManyThrough $data = $this->eagerlyWhere([ [$this->foreignKey, 'in', $range], - ], $foreignKey, $relation, $subRelation, $closure, $cache); - - // 关联属性名 - $attr = Str::snake($relation); + ], $foreignKey, $subRelation, $closure, $cache); // 关联数据封装 foreach ($resultSet as $result) { @@ -90,7 +87,7 @@ class HasOneThrough extends HasManyThrough } // 设置关联属性 - $result->setRelation($attr, $relationModel); + $result->setRelation($relation, $relationModel); } } } @@ -114,7 +111,7 @@ class HasOneThrough extends HasManyThrough $data = $this->eagerlyWhere([ [$foreignKey, '=', $result->$localKey], - ], $foreignKey, $relation, $subRelation, $closure, $cache); + ], $foreignKey, $subRelation, $closure, $cache); // 关联模型 if (!isset($data[$result->$localKey])) { @@ -125,7 +122,7 @@ class HasOneThrough extends HasManyThrough $relationModel->exists(true); } - $result->setRelation(Str::snake($relation), $relationModel); + $result->setRelation($relation, $relationModel); } /** @@ -133,13 +130,12 @@ class HasOneThrough extends HasManyThrough * @access public * @param array $where 关联预查询条件 * @param string $key 关联键名 - * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure * @param array $cache 关联缓存 * @return array */ - protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): array + protected function eagerlyWhere(array $where, string $key, array $subRelation = [], Closure $closure = null, array $cache = []): array { // 预载入关联查询 支持嵌套预载入 $keys = $this->through->where($where)->column($this->throughPk, $this->foreignKey); diff --git a/src/model/relation/MorphMany.php b/src/model/relation/MorphMany.php index 731560b..82910cb 100644 --- a/src/model/relation/MorphMany.php +++ b/src/model/relation/MorphMany.php @@ -144,10 +144,7 @@ class MorphMany extends Relation [$morphKey, 'in', $range], [$morphType, '=', $type], ]; - $data = $this->eagerlyMorphToMany($where, $relation, $subRelation, $closure, $cache); - - // 关联属性名 - $attr = Str::snake($relation); + $data = $this->eagerlyMorphToMany($where, $subRelation, $closure, $cache); // 关联数据封装 foreach ($resultSet as $result) { @@ -155,7 +152,7 @@ class MorphMany extends Relation $data[$result->$pk] = []; } - $result->setRelation($attr, $this->resultSetBuild($data[$result->$pk], clone $this->parent)); + $result->setRelation($relation, $this->resultSetBuild($data[$result->$pk], clone $this->parent)); } } } @@ -179,13 +176,13 @@ class MorphMany extends Relation $data = $this->eagerlyMorphToMany([ [$this->morphKey, '=', $key], [$this->morphType, '=', $this->type], - ], $relation, $subRelation, $closure, $cache); + ], $subRelation, $closure, $cache); if (!isset($data[$key])) { $data[$key] = []; } - $result->setRelation(Str::snake($relation), $this->resultSetBuild($data[$key], clone $this->parent)); + $result->setRelation($relation, $this->resultSetBuild($data[$key], clone $this->parent)); } } @@ -245,13 +242,12 @@ class MorphMany extends Relation * 多态一对多 关联模型预查询 * @access protected * @param array $where 关联预查询条件 - * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure 闭包 * @param array $cache 关联缓存 * @return array */ - protected function eagerlyMorphToMany(array $where, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): array + protected function eagerlyMorphToMany(array $where, array $subRelation = [], Closure $closure = null, array $cache = []): array { // 预载入关联查询 支持嵌套预载入 $this->query->removeOption('where'); diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php index 916a2ed..6789c76 100644 --- a/src/model/relation/MorphOne.php +++ b/src/model/relation/MorphOne.php @@ -142,10 +142,7 @@ class MorphOne extends Relation $data = $this->eagerlyMorphToOne([ [$morphKey, 'in', $range], [$morphType, '=', $type], - ], $relation, $subRelation, $closure, $cache); - - // 关联属性名 - $attr = Str::snake($relation); + ], $subRelation, $closure, $cache); // 关联数据封装 foreach ($resultSet as $result) { @@ -157,7 +154,7 @@ class MorphOne extends Relation $relationModel->exists(true); } - $result->setRelation($attr, $relationModel); + $result->setRelation($relation, $relationModel); } } } @@ -181,7 +178,7 @@ class MorphOne extends Relation $data = $this->eagerlyMorphToOne([ [$this->morphKey, '=', $pk], [$this->morphType, '=', $this->type], - ], $relation, $subRelation, $closure, $cache); + ], $subRelation, $closure, $cache); if (isset($data[$pk])) { $relationModel = $data[$pk]; @@ -191,7 +188,7 @@ class MorphOne extends Relation $relationModel = null; } - $result->setRelation(Str::snake($relation), $relationModel); + $result->setRelation($relation, $relationModel); } } @@ -199,13 +196,12 @@ class MorphOne extends Relation * 多态一对一 关联模型预查询 * @access protected * @param array $where 关联预查询条件 - * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure 闭包 * @param array $cache 关联缓存 * @return array */ - protected function eagerlyMorphToOne(array $where, string $relation, array $subRelation = [], $closure = null, array $cache = []): array + protected function eagerlyMorphToOne(array $where, array $subRelation = [], $closure = null, array $cache = []): array { // 预载入关联查询 支持嵌套预载入 if ($closure) { diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index b6a9cf6..c939c1d 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -203,8 +203,6 @@ class MorphTo extends Relation } if (!empty($range)) { - // 关联属性名 - $attr = Str::snake($relation); foreach ($range as $key => $val) { // 多态类型映射 @@ -232,7 +230,7 @@ class MorphTo extends Relation $relationModel->exists(true); } - $result->setRelation($attr, $relationModel); + $result->setRelation($relation, $relationModel); } } } @@ -292,7 +290,7 @@ class MorphTo extends Relation $data->exists(true); } - $result->setRelation(Str::snake($relation), $data ?: null); + $result->setRelation($relation, $data ?: null); } /** diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index a22aba0..e3eb48a 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -260,7 +260,7 @@ abstract class OneToOne extends Relation $relationModel = null; } - $result->setRelation(Str::snake($relation), $relationModel); + $result->setRelation($relation, $relationModel); } /** @@ -290,13 +290,12 @@ abstract class OneToOne extends Relation * @access public * @param array $where 关联预查询条件 * @param string $key 关联键名 - * @param string $relation 关联名 * @param array $subRelation 子关联 * @param Closure $closure * @param array $cache 关联缓存 * @return array */ - protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []) + protected function eagerlyWhere(array $where, string $key, array $subRelation = [], Closure $closure = null, array $cache = []) { // 预载入关联查询 支持嵌套预载入 if ($closure) { -- Gitee From 02affaaccade2cdd8bbb2d2f5d15e46113e6eb50 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 23 Oct 2019 10:16:50 +0800 Subject: [PATCH 117/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BgetData=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 8ea0d67..a89b0b0 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -278,8 +278,8 @@ trait Attribute if (array_key_exists($fieldName, $this->data)) { return $this->data[$fieldName]; - } elseif (array_key_exists($name, $this->relation)) { - return $this->relation[$name]; + } elseif (array_key_exists($fieldName, $this->relation)) { + return $this->relation[$fieldName]; } throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name); -- Gitee From 40a59f5e28dd5ff1825732cb2868a451bc330662 Mon Sep 17 00:00:00 2001 From: huangbinghe Date: Fri, 25 Oct 2019 14:08:34 +0800 Subject: [PATCH 118/365] =?UTF-8?q?Model=E7=B1=BBmongo=E9=A9=B1=E5=8A=A8ch?= =?UTF-8?q?eckAllowFields=E6=8A=A5=E9=94=99=20issue=20#93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/connector/Mongo.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 647c286..4eb631b 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -1011,6 +1011,17 @@ class Mongo extends Connection implements ConnectionInterface return $this->command($command, $db); } + + /** + * 获取数据库字段 + * @access public + * @param mixed $tableName 数据表名 + * @return array + */ + public function getTableFields($tableName): array + { + return []; + } /** * 执行数据库事务 -- Gitee From aac60421e1a8ac81aea6251e971250fb1aed683c Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 25 Oct 2019 21:33:48 +0800 Subject: [PATCH 119/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=A4=9A=E5=AF=B9?= =?UTF-8?q?=E5=A4=9A=E7=9A=84=E5=88=86=E9=A1=B5=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsToMany.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 16f4bf5..36f1504 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -206,12 +206,11 @@ class BelongsToMany extends Relation * @access public * @param int|array $listRows * @param int|bool $simple - * @param array $config * @return Paginator */ - public function paginate($listRows = null, $simple = false, $config = []): Paginator + public function paginate($listRows = null, $simple = false): Paginator { - $result = $this->buildQuery()->paginate($listRows, $simple, $config); + $result = $this->buildQuery()->paginate($listRows, $simple); $this->hydratePivot($result); return $result; -- Gitee From 38e0a22ca4f390bee4edb5c17ed2c355c40808ca Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 31 Oct 2019 13:11:24 +0800 Subject: [PATCH 120/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bmongo=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Mongo.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/db/Mongo.php b/src/db/Mongo.php index 6f94f3c..3fe4c0a 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -712,4 +712,30 @@ class Mongo extends BaseQuery return $options; } + /** + * 获取字段类型信息 + * @access public + * @return array + */ + public function getFieldsType(): array + { + if (!empty($this->options['field_type'])) { + return $this->options['field_type']; + } + + return []; + } + + /** + * 获取字段类型信息 + * @access public + * @param string $field 字段名 + * @return string|null + */ + public function getFieldType(string $field) + { + $fieldType = $this->getFieldsType(); + + return $fieldType[$field] ?? null; + } } -- Gitee From ef1f3f5b41f59f95ee592c1c4064498e59cc7f62 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 6 Nov 2019 16:55:55 +0800 Subject: [PATCH 121/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E6=9F=A5=E8=AF=A2=E8=87=AA=E5=8A=A8=E8=AF=86=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/WhereQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/concern/WhereQuery.php b/src/db/concern/WhereQuery.php index 33b0763..92e0545 100644 --- a/src/db/concern/WhereQuery.php +++ b/src/db/concern/WhereQuery.php @@ -384,7 +384,7 @@ trait WhereQuery } elseif ($field instanceof Closure) { $where = $field; } elseif (is_string($field)) { - if (preg_match('/[,=\<\'\"\(\s]/', $field)) { + if (preg_match('/[,=\<\>\'\"\(\s]/', $field)) { return $this->whereRaw($field, is_array($op) ? $op : [], $logic); } elseif (is_string($op) && strtolower($op) == 'exp') { $bind = isset($param[2]) && is_array($param[2]) ? $param[2] : []; -- Gitee From bfdb5417c2c158a7118e5465910a83761193c488 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 6 Nov 2019 18:08:57 +0800 Subject: [PATCH 122/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=AD=E7=BA=BF?= =?UTF-8?q?=E9=87=8D=E8=BF=9E=E6=AC=A1=E6=95=B0=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index b15d42b..a19815a 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -98,6 +98,12 @@ abstract class PDOConnection extends Connection implements ConnectionInterface */ protected $transTimes = 0; + /** + * 重连次数 + * @var int + */ + protected $reConnectTimes = 0; + /** * 查询结果类型 * @var int @@ -703,9 +709,12 @@ abstract class PDOConnection extends Connection implements ConnectionInterface $this->trigger('', $master); } + $this->reConnectTimes = 0; + return $this->PDOStatement; } catch (\Throwable | \Exception $e) { - if ($this->isBreak($e)) { + if ($this->reConnectTimes < 4 && $this->isBreak($e)) { + ++$this->reConnectTimes; return $this->close()->getPDOStatement($sql, $bind, $master, $procedure); } @@ -1348,9 +1357,11 @@ abstract class PDOConnection extends Connection implements ConnectionInterface $this->parseSavepoint('trans' . $this->transTimes) ); } + $this->reConnectTimes = 0; } catch (\Exception $e) { - if ($this->isBreak($e)) { + if ($this->reConnectTimes < 4 && $this->isBreak($e)) { --$this->transTimes; + ++$this->reConnectTimes; $this->close()->startTrans(); } throw $e; -- Gitee From 0fad893bdaea82193c3dd49a03574dce3e117bc8 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 8 Nov 2019 09:13:36 +0800 Subject: [PATCH 123/365] =?UTF-8?q?=E6=9B=BF=E6=8D=A2list=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 16 ++++++++-------- src/db/Builder.php | 16 ++++++++-------- src/db/Connection.php | 2 +- src/db/Fetch.php | 2 +- src/db/Mongo.php | 2 +- src/db/PDOConnection.php | 8 ++++---- src/db/Query.php | 2 +- src/db/builder/Mongo.php | 14 +++++++------- src/db/builder/Mysql.php | 4 ++-- src/db/builder/Oracle.php | 4 ++-- src/db/builder/Pgsql.php | 6 +++--- src/db/builder/Sqlite.php | 2 +- src/db/builder/Sqlsrv.php | 4 ++-- src/db/concern/JoinAndViewQuery.php | 4 ++-- src/db/concern/ModelRelationQuery.php | 4 ++-- src/db/concern/ResultOperation.php | 2 +- src/db/connector/Mysql.php | 2 +- src/db/connector/Oracle.php | 4 ++-- src/db/connector/Pgsql.php | 4 ++-- src/db/connector/Sqlite.php | 4 ++-- src/db/connector/Sqlsrv.php | 2 +- src/model/concern/Attribute.php | 10 +++++----- src/model/concern/Conversion.php | 6 +++--- src/model/concern/RelationShip.php | 12 ++++++------ src/model/relation/BelongsToMany.php | 4 ++-- src/model/relation/OneToOne.php | 2 +- 26 files changed, 71 insertions(+), 71 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 83362a2..df207a8 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -490,8 +490,8 @@ abstract class BaseQuery // 子查询 } elseif (false === strpos($table, ',')) { if (strpos($table, ' ')) { - list($item, $alias) = explode(' ', $table); - $table = []; + [$item, $alias] = explode(' ', $table); + $table = []; $this->alias([$item => $alias]); $table[$item] = $alias; } @@ -502,7 +502,7 @@ abstract class BaseQuery foreach ($tables as $item) { $item = trim($item); if (strpos($item, ' ')) { - list($item, $alias) = explode(' ', $item); + [$item, $alias] = explode(' ', $item); $this->alias([$item => $alias]); $table[$item] = $alias; } else { @@ -1179,11 +1179,11 @@ abstract class BaseQuery if (isset($options['page'])) { // 根据页数计算limit - list($page, $listRows) = $options['page']; - $page = $page > 0 ? $page : 1; - $listRows = $listRows ?: (is_numeric($options['limit']) ? $options['limit'] : 20); - $offset = $listRows * ($page - 1); - $options['limit'] = $offset . ',' . $listRows; + [$page, $listRows] = $options['page']; + $page = $page > 0 ? $page : 1; + $listRows = $listRows ?: (is_numeric($options['limit']) ? $options['limit'] : 20); + $offset = $listRows * ($page - 1); + $options['limit'] = $offset . ',' . $listRows; } $this->options = $options; diff --git a/src/db/Builder.php b/src/db/Builder.php index c2a86d3..1b01c98 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -156,9 +156,9 @@ abstract class Builder } if (false !== strpos($key, '->')) { - list($key, $name) = explode('->', $key, 2); - $item = $this->parseKey($query, $key); - $result[$item] = 'json_set(' . $item . ', \'$.' . $name . '\', ' . $this->parseDataBind($query, $key . '->' . $name, $val, $bind) . ')'; + [$key, $name] = explode('->', $key, 2); + $item = $this->parseKey($query, $key); + $result[$item] = 'json_set(' . $item . ', \'$.' . $name . '\', ' . $this->parseDataBind($query, $key . '->' . $name, $val, $bind) . ')'; } elseif (false === strpos($key, '.') && !in_array($key, $fields, true)) { if ($options['strict']) { throw new Exception('fields not exists:[' . $key . ']'); @@ -300,7 +300,7 @@ abstract class Builder if (!empty($options['soft_delete'])) { // 附加软删除条件 - list($field, $condition) = $options['soft_delete']; + [$field, $condition] = $options['soft_delete']; $binds = $query->getFieldsBindType(); $whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : ''; @@ -591,7 +591,7 @@ abstract class Builder protected function parseColumn(Query $query, string $key, $exp, array $value, string $field, int $bindType): string { // 字段比较查询 - list($op, $field) = $value; + [$op, $field] = $value; if (!in_array(trim($op), ['=', '<>', '>', '>=', '<', '<='])) { throw new Exception('where express error:' . var_export($value, true)); @@ -802,7 +802,7 @@ abstract class Builder // 获取时间字段类型 if (strpos($key, '.')) { - list($table, $key) = explode('.', $key); + [$table, $key] = explode('.', $key); if (isset($options['alias']) && $pos = array_search($table, $options['alias'])) { $table = $pos; @@ -858,10 +858,10 @@ abstract class Builder $joinStr = ''; foreach ($join as $item) { - list($table, $type, $on) = $item; + [$table, $type, $on] = $item; if (strpos($on, '=')) { - list($val1, $val2) = explode('=', $on, 2); + [$val1, $val2] = explode('=', $on, 2); $condition = $this->parseKey($query, $val1) . '=' . $this->parseKey($query, $val2); } else { diff --git a/src/db/Connection.php b/src/db/Connection.php index ba51920..fb9f74b 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -236,7 +236,7 @@ abstract class Connection */ protected function parseCache(BaseQuery $query, array $cache): CacheItem { - list($key, $expire, $tag) = $cache; + [$key, $expire, $tag] = $cache; if ($key instanceof CacheItem) { $cacheItem = $key; diff --git a/src/db/Fetch.php b/src/db/Fetch.php index 38f797c..76658e6 100644 --- a/src/db/Fetch.php +++ b/src/db/Fetch.php @@ -303,7 +303,7 @@ class Fetch if (!empty($options['soft_delete'])) { // 软删除 - list($field, $condition) = $options['soft_delete']; + [$field, $condition] = $options['soft_delete']; if ($condition) { $this->query->setOption('soft_delete', null); $this->query->setOption('data', [$field => $condition]); diff --git a/src/db/Mongo.php b/src/db/Mongo.php index 3fe4c0a..7d2e469 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -615,7 +615,7 @@ class Mongo extends BaseQuery $query = $this->options($options)->limit($count); if (strpos($column, '.')) { - list($alias, $key) = explode('.', $column); + [$alias, $key] = explode('.', $column); } else { $key = $column; } diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index a19815a..5cce2e1 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -335,7 +335,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface return []; } - list($tableName) = explode(' ', $tableName); + [$tableName] = explode(' ', $tableName); if (!strpos($tableName, '.')) { $schema = $this->getConfig('database') . '.' . $tableName; @@ -1079,7 +1079,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface public function aggregate(BaseQuery $query, string $aggregate, $field, bool $force = false) { if (is_string($field) && 0 === stripos($field, 'DISTINCT ')) { - list($distinct, $field) = explode(' ', $field); + [$distinct, $field] = explode(' ', $field); } $field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS think_' . strtolower($aggregate); @@ -1150,11 +1150,11 @@ abstract class PDOConnection extends Connection implements ConnectionInterface if (strpos($column, ',')) { $column = null; } elseif (strpos($column, '.')) { - list($alias, $column) = explode('.', $column); + [$alias, $column] = explode('.', $column); } if (strpos($key, '.')) { - list($alias, $key) = explode('.', $key); + [$alias, $key] = explode('.', $key); } $result = array_column($resultSet, $column, $key); diff --git a/src/db/Query.php b/src/db/Query.php index ba0e5e5..f039384 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -460,7 +460,7 @@ class Query extends BaseQuery $query = $this->options($options)->limit($count); if (strpos($column, '.')) { - list($alias, $key) = explode('.', $column); + [$alias, $key] = explode('.', $column); } else { $key = $column; } diff --git a/src/db/builder/Mongo.php b/src/db/builder/Mongo.php index 85b0600..823156b 100644 --- a/src/db/builder/Mongo.php +++ b/src/db/builder/Mongo.php @@ -58,7 +58,7 @@ class Mongo protected function parseKey(Query $query, string $key): string { if (0 === strpos($key, '__TABLE__.')) { - list($collection, $key) = explode('.', $key, 2); + [$collection, $key] = explode('.', $key, 2); } if ('id' == $key && $this->connection->getConfig('pk_convert_id')) { @@ -205,8 +205,8 @@ class Mongo $options = $query->getOptions(); if (!empty($options['soft_delete'])) { // 附加软删除条件 - list($field, $condition) = $options['soft_delete']; - $filter['$and'][] = $this->parseWhereItem($query, $field, $condition); + [$field, $condition] = $options['soft_delete']; + $filter['$and'][] = $this->parseWhereItem($query, $field, $condition); } return $filter; @@ -220,7 +220,7 @@ class Mongo if (!is_array($val)) { $val = ['=', $val]; } - list($exp, $value) = $val; + [$exp, $value] = $val; // 对一个字段使用多个查询条件 if (is_array($exp)) { @@ -523,8 +523,8 @@ class Mongo */ public function aggregate(Query $query, array $extra): Command { - $options = $query->getOptions(); - list($fun, $field) = $extra; + $options = $query->getOptions(); + [$fun, $field] = $extra; if ('id' == $field && $this->connection->getConfig('pk_convert_id')) { $field = '_id'; @@ -568,7 +568,7 @@ class Mongo { $options = $query->getOptions(); - list($aggregate, $groupBy) = $extra; + [$aggregate, $groupBy] = $extra; $groups = ['_id' => []]; diff --git a/src/db/builder/Mysql.php b/src/db/builder/Mysql.php index 33aec85..977ff9b 100644 --- a/src/db/builder/Mysql.php +++ b/src/db/builder/Mysql.php @@ -315,10 +315,10 @@ class Mysql extends Builder if (strpos($key, '->') && false === strpos($key, '(')) { // JSON字段支持 - list($field, $name) = explode('->', $key, 2); + [$field, $name] = explode('->', $key, 2); return 'json_extract(' . $this->parseKey($query, $field) . ', \'$' . (strpos($name, '[') === 0 ? '' : '.') . str_replace('->', '.', $name) . '\')'; } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { - list($table, $key) = explode('.', $key, 2); + [$table, $key] = explode('.', $key, 2); $alias = $query->getOptions('alias'); diff --git a/src/db/builder/Oracle.php b/src/db/builder/Oracle.php index 8b6e225..77ad3c8 100644 --- a/src/db/builder/Oracle.php +++ b/src/db/builder/Oracle.php @@ -75,8 +75,8 @@ class Oracle extends Builder if (strpos($key, '->') && false === strpos($key, '(')) { // JSON字段支持 - list($field, $name) = explode($key, '->'); - $key = $field . '."' . $name . '"'; + [$field, $name] = explode($key, '->'); + $key = $field . '."' . $name . '"'; } return $key; diff --git a/src/db/builder/Pgsql.php b/src/db/builder/Pgsql.php index e1c2856..4b581df 100644 --- a/src/db/builder/Pgsql.php +++ b/src/db/builder/Pgsql.php @@ -76,10 +76,10 @@ class Pgsql extends Builder if (strpos($key, '->') && false === strpos($key, '(')) { // JSON字段支持 - list($field, $name) = explode('->', $key); - $key = '"' . $field . '"' . '->>\'' . $name . '\''; + [$field, $name] = explode('->', $key); + $key = '"' . $field . '"' . '->>\'' . $name . '\''; } elseif (strpos($key, '.')) { - list($table, $key) = explode('.', $key, 2); + [$table, $key] = explode('.', $key, 2); $alias = $query->getOptions('alias'); diff --git a/src/db/builder/Sqlite.php b/src/db/builder/Sqlite.php index bf8f129..87a84eb 100644 --- a/src/db/builder/Sqlite.php +++ b/src/db/builder/Sqlite.php @@ -74,7 +74,7 @@ class Sqlite extends Builder $key = trim($key); if (strpos($key, '.')) { - list($table, $key) = explode('.', $key, 2); + [$table, $key] = explode('.', $key, 2); $alias = $query->getOptions('alias'); diff --git a/src/db/builder/Sqlsrv.php b/src/db/builder/Sqlsrv.php index cd06c34..80cb166 100644 --- a/src/db/builder/Sqlsrv.php +++ b/src/db/builder/Sqlsrv.php @@ -78,7 +78,7 @@ class Sqlsrv extends Builder $array[] = $this->parseRand($query); } else { if (is_numeric($key)) { - list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' '); + [$key, $sort] = explode(' ', strpos($val, ' ') ? $val : $val . ' '); } else { $sort = $val; } @@ -121,7 +121,7 @@ class Sqlsrv extends Builder $key = trim($key); if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) { - list($table, $key) = explode('.', $key, 2); + [$table, $key] = explode('.', $key, 2); $alias = $query->getOptions('alias'); diff --git a/src/db/concern/JoinAndViewQuery.php b/src/db/concern/JoinAndViewQuery.php index cd48ab2..c33d1ed 100644 --- a/src/db/concern/JoinAndViewQuery.php +++ b/src/db/concern/JoinAndViewQuery.php @@ -109,7 +109,7 @@ trait JoinAndViewQuery // 使用别名 if (strpos($join, ' ')) { // 使用别名 - list($table, $alias) = explode(' ', $join); + [$table, $alias] = explode(' ', $join); } else { $table = $join; if (false === strpos($join, '.')) { @@ -209,7 +209,7 @@ trait JoinAndViewQuery foreach ($options['order'] as $key => $val) { if (is_numeric($key) && is_string($val)) { if (strpos($val, ' ')) { - list($field, $sort) = explode(' ', $val); + [$field, $sort] = explode(' ', $val); if (array_key_exists($field, $options['map'])) { $options['order'][$options['map'][$field]] = $sort; unset($options['order'][$key]); diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 9a21137..92e4417 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -414,7 +414,7 @@ trait ModelRelationQuery if (!empty($this->options['with_attr'])) { foreach ($this->options['with_attr'] as $name => $val) { if (strpos($name, '.')) { - list($relation, $field) = explode('.', $name); + [$relation, $field] = explode('.', $name); $withRelationAttr[$relation][$field] = $val; unset($this->options['with_attr'][$name]); @@ -458,7 +458,7 @@ trait ModelRelationQuery if (!empty($options['with_attr']) && empty($withRelationAttr)) { foreach ($options['with_attr'] as $name => $val) { if (strpos($name, '.')) { - list($relation, $field) = explode('.', $name); + [$relation, $field] = explode('.', $name); $withRelationAttr[$relation][$field] = $val; unset($options['with_attr'][$name]); diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index 563f989..f761d1e 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -130,7 +130,7 @@ trait ResultOperation if (strpos($name, '.')) { // 支持JSON字段 获取器定义 - list($key, $field) = explode('.', $name); + [$key, $field] = explode('.', $name); if (isset($result[$key])) { $result[$key][$field] = $closure($result[$key][$field] ?? null, $result[$key]); diff --git a/src/db/connector/Mysql.php b/src/db/connector/Mysql.php index e82f4f0..1db3152 100644 --- a/src/db/connector/Mysql.php +++ b/src/db/connector/Mysql.php @@ -53,7 +53,7 @@ class Mysql extends PDOConnection */ public function getFields(string $tableName): array { - list($tableName) = explode(' ', $tableName); + [$tableName] = explode(' ', $tableName); if (false === strpos($tableName, '`')) { if (strpos($tableName, '.')) { diff --git a/src/db/connector/Oracle.php b/src/db/connector/Oracle.php index 1c80323..236d8bf 100644 --- a/src/db/connector/Oracle.php +++ b/src/db/connector/Oracle.php @@ -50,8 +50,8 @@ class Oracle extends PDOConnection */ public function getFields(string $tableName): array { - list($tableName) = explode(' ', $tableName); - $sql = "select a.column_name,data_type,DECODE (nullable, 'Y', 0, 1) notnull,data_default, DECODE (A .column_name,b.column_name,1,0) pk from all_tab_columns a,(select column_name from all_constraints c, all_cons_columns col where c.constraint_name = col.constraint_name and c.constraint_type = 'P' and c.table_name = '" . strtoupper($tableName) . "' ) b where table_name = '" . strtoupper($tableName) . "' and a.column_name = b.column_name (+)"; + [$tableName] = explode(' ', $tableName); + $sql = "select a.column_name,data_type,DECODE (nullable, 'Y', 0, 1) notnull,data_default, DECODE (A .column_name,b.column_name,1,0) pk from all_tab_columns a,(select column_name from all_constraints c, all_cons_columns col where c.constraint_name = col.constraint_name and c.constraint_type = 'P' and c.table_name = '" . strtoupper($tableName) . "' ) b where table_name = '" . strtoupper($tableName) . "' and a.column_name = b.column_name (+)"; $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); diff --git a/src/db/connector/Pgsql.php b/src/db/connector/Pgsql.php index 1310b97..fec8f84 100644 --- a/src/db/connector/Pgsql.php +++ b/src/db/connector/Pgsql.php @@ -56,8 +56,8 @@ class Pgsql extends PDOConnection */ public function getFields(string $tableName): array { - list($tableName) = explode(' ', $tableName); - $sql = 'select fields_name as "field",fields_type as "type",fields_not_null as "null",fields_key_name as "key",fields_default as "default",fields_default as "extra" from table_msg(\'' . $tableName . '\');'; + [$tableName] = explode(' ', $tableName); + $sql = 'select fields_name as "field",fields_type as "type",fields_not_null as "null",fields_key_name as "key",fields_default as "default",fields_default as "extra" from table_msg(\'' . $tableName . '\');'; $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); diff --git a/src/db/connector/Sqlite.php b/src/db/connector/Sqlite.php index 12a0517..c664f20 100644 --- a/src/db/connector/Sqlite.php +++ b/src/db/connector/Sqlite.php @@ -41,8 +41,8 @@ class Sqlite extends PDOConnection */ public function getFields(string $tableName): array { - list($tableName) = explode(' ', $tableName); - $sql = 'PRAGMA table_info( ' . $tableName . ' )'; + [$tableName] = explode(' ', $tableName); + $sql = 'PRAGMA table_info( ' . $tableName . ' )'; $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); diff --git a/src/db/connector/Sqlsrv.php b/src/db/connector/Sqlsrv.php index 1a5fffe..63b2df0 100644 --- a/src/db/connector/Sqlsrv.php +++ b/src/db/connector/Sqlsrv.php @@ -55,7 +55,7 @@ class Sqlsrv extends PDOConnection */ public function getFields(string $tableName): array { - list($tableName) = explode(' ', $tableName); + [$tableName] = explode(' ', $tableName); $sql = "SELECT column_name, data_type, column_default, is_nullable FROM information_schema.tables AS t diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index a89b0b0..5c0eea7 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -398,9 +398,9 @@ trait Attribute } if (is_array($type)) { - list($type, $param) = $type; + [$type, $param] = $type; } elseif (strpos($type, ':')) { - list($type, $param) = explode(':', $type, 2); + [$type, $param] = explode(':', $type, 2); } switch ($type) { @@ -563,9 +563,9 @@ trait Attribute } if (is_array($type)) { - list($type, $param) = $type; + [$type, $param] = $type; } elseif (strpos($type, ':')) { - list($type, $param) = explode(':', $type, 2); + [$type, $param] = explode(':', $type, 2); } switch ($type) { @@ -637,7 +637,7 @@ trait Attribute $name = $this->getRealFieldName($name); if (strpos($name, '.')) { - list($name, $key) = explode('.', $name); + [$name, $key] = explode('.', $name); $this->withAttr[$name][$key] = $callback; } else { diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index 178ad0f..e12d0af 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -132,7 +132,7 @@ trait Conversion foreach ($this->visible as $key => $val) { if (is_string($val)) { if (strpos($val, '.')) { - list($relation, $name) = explode('.', $val); + [$relation, $name] = explode('.', $val); $this->visible[$relation][] = $name; } else { $this->visible[$val] = true; @@ -145,7 +145,7 @@ trait Conversion foreach ($this->hidden as $key => $val) { if (is_string($val)) { if (strpos($val, '.')) { - list($relation, $name) = explode('.', $val); + [$relation, $name] = explode('.', $val); $this->hidden[$relation][] = $name; } else { $this->hidden[$val] = true; @@ -192,7 +192,7 @@ trait Conversion $item[$key] = $relation ? $relation->append($name) ->toArray() : []; } elseif (strpos($name, '.')) { - list($key, $attr) = explode('.', $name); + [$key, $attr] = explode('.', $name); // 追加关联对象属性 $relation = $this->getRelation($key, true); $item[$key] = $relation ? $relation->append([$attr]) diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index dd75b8e..7b0d4a7 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -148,7 +148,7 @@ trait RelationShip $subRelation = $relation; $relation = $key; } elseif (strpos($relation, '.')) { - list($relation, $subRelation) = explode('.', $relation, 2); + [$relation, $subRelation] = explode('.', $relation, 2); } $method = Str::camel($relation); @@ -263,7 +263,7 @@ trait RelationShip $subRelation = $relation; $relation = $key; } elseif (strpos($relation, '.')) { - list($relation, $subRelation) = explode('.', $relation, 2); + [$relation, $subRelation] = explode('.', $relation, 2); $subRelation = [$subRelation]; } @@ -312,7 +312,7 @@ trait RelationShip $subRelation = $relation; $relation = $key; } elseif (strpos($relation, '.')) { - list($relation, $subRelation) = explode('.', $relation, 2); + [$relation, $subRelation] = explode('.', $relation, 2); $subRelation = [$subRelation]; } @@ -549,7 +549,7 @@ trait RelationShip } if (is_array($morph)) { - list($morphType, $foreignKey) = $morph; + [$morphType, $foreignKey] = $morph; } else { $morphType = $morph . '_type'; $foreignKey = $morph . '_id'; @@ -581,7 +581,7 @@ trait RelationShip $type = $type ?: get_class($this); if (is_array($morph)) { - list($morphType, $foreignKey) = $morph; + [$morphType, $foreignKey] = $morph; } else { $morphType = $morph . '_type'; $foreignKey = $morph . '_id'; @@ -608,7 +608,7 @@ trait RelationShip // 记录当前关联信息 if (is_array($morph)) { - list($morphType, $foreignKey) = $morph; + [$morphType, $foreignKey] = $morph; } else { $morphType = $morph . '_type'; $foreignKey = $morph . '_id'; diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 36f1504..eccbfe9 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -133,7 +133,7 @@ class BelongsToMany extends Relation foreach ($model->getData() as $key => $val) { if (strpos($key, '__')) { - list($name, $attr) = explode('__', $key, 2); + [$name, $attr] = explode('__', $key, 2); if ('pivot' == $name) { $pivot[$attr] = $val; @@ -447,7 +447,7 @@ class BelongsToMany extends Relation $pivot = []; foreach ($set->getData() as $key => $val) { if (strpos($key, '__')) { - list($name, $attr) = explode('__', $key, 2); + [$name, $attr] = explode('__', $key, 2); if ('pivot' == $name) { $pivot[$attr] = $val; unset($set->$key); diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index e3eb48a..540fcc2 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -234,7 +234,7 @@ abstract class OneToOne extends Relation // 重新组装模型数据 foreach ($result->getData() as $key => $val) { if (strpos($key, '__')) { - list($name, $attr) = explode('__', $key, 2); + [$name, $attr] = explode('__', $key, 2); if ($name == $relation) { $list[$name][$attr] = $val; unset($result->$key); -- Gitee From 50c2c4932e9323002793d2babf13acb547e43723 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 8 Nov 2019 15:44:23 +0800 Subject: [PATCH 124/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/WhereQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/concern/WhereQuery.php b/src/db/concern/WhereQuery.php index 92e0545..33b0763 100644 --- a/src/db/concern/WhereQuery.php +++ b/src/db/concern/WhereQuery.php @@ -384,7 +384,7 @@ trait WhereQuery } elseif ($field instanceof Closure) { $where = $field; } elseif (is_string($field)) { - if (preg_match('/[,=\<\>\'\"\(\s]/', $field)) { + if (preg_match('/[,=\<\'\"\(\s]/', $field)) { return $this->whereRaw($field, is_array($op) ? $op : [], $logic); } elseif (is_string($op) && strtolower($op) == 'exp') { $bind = isset($param[2]) && is_array($param[2]) ? $param[2] : []; -- Gitee From 1b29dea44c8714ca215e148e7746393a573560bd Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 12 Nov 2019 22:07:45 +0800 Subject: [PATCH 125/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=90=8E=E8=87=AA=E5=8A=A8=E6=B8=85=E7=90=86?= =?UTF-8?q?=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Model.php b/src/Model.php index d1a9710..c3bcd7e 100644 --- a/src/Model.php +++ b/src/Model.php @@ -85,6 +85,12 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab */ protected $name; + /** + * 主键值 + * @var string + */ + protected $key; + /** * 数据表名称 * @var string @@ -575,9 +581,13 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $db->startTrans(); try { - $where = $this->getWhere(); + $this->key = null; + $where = $this->getWhere(); + $result = $db->where($where) ->strict(false) + ->cache(true) + ->setOption('key', $this->key) ->field($allowFields) ->update($data); @@ -676,7 +686,8 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $pk = $this->getPk(); if (is_string($pk) && isset($this->data[$pk])) { - $where = [[$pk, '=', $this->data[$pk]]]; + $where = [[$pk, '=', $this->data[$pk]]]; + $this->key = $this->data[$pk]; } elseif (is_array($pk)) { foreach ($pk as $field) { if (isset($this->data[$field])) { -- Gitee From 3fb17fa37fc56f0eb0c20a8bd08667b1065bad37 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 13 Nov 2019 10:48:08 +0800 Subject: [PATCH 126/365] =?UTF-8?q?cache=E6=96=B9=E6=B3=95tag=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E6=94=AF=E6=8C=81=E6=95=B0=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 4 ++-- src/db/CacheItem.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index df207a8..1f97e63 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -750,10 +750,10 @@ abstract class BaseQuery * @access public * @param mixed $key 缓存key * @param integer|\DateTime $expire 缓存有效期 - * @param string $tag 缓存标签 + * @param string|array $tag 缓存标签 * @return $this */ - public function cache($key = true, $expire = null, string $tag = null) + public function cache($key = true, $expire = null, $tag = null) { if (false === $key || !$this->getConnection()->getCache()) { return $this; diff --git a/src/db/CacheItem.php b/src/db/CacheItem.php index 6e82523..839f384 100644 --- a/src/db/CacheItem.php +++ b/src/db/CacheItem.php @@ -96,7 +96,7 @@ class CacheItem /** * 获取缓存Tag * @access public - * @return string + * @return string|array */ public function getTag() { @@ -139,10 +139,10 @@ class CacheItem /** * 为此缓存项设置所属标签 * @access public - * @param string $tag + * @param string|array $tag * @return $this */ - public function tag(string $tag = null) + public function tag($tag = null) { $this->tag = $tag; return $this; -- Gitee From ff5f112f5559497222f2716385b5a709ab82741b Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 17 Nov 2019 15:53:45 +0800 Subject: [PATCH 127/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=9A=84=E4=B8=BB=E9=94=AE=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Model.php b/src/Model.php index c3bcd7e..9609478 100644 --- a/src/Model.php +++ b/src/Model.php @@ -645,14 +645,15 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $result = $db->strict(false) ->field($allowFields) ->replace($this->replace) - ->insert($this->data, false, $sequence); + ->sequence($sequence) + ->insert($this->data, true); // 获取自动增长主键 - if ($result && $insertId = $db->getLastInsID($sequence)) { + if ($result) { $pk = $this->getPk(); if (is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) { - $this->data[$pk] = $insertId; + $this->data[$pk] = $result; } } -- Gitee From e5b93c21a6364b21f89b4d71825ca37370580d69 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 27 Nov 2019 20:55:56 +0800 Subject: [PATCH 128/365] =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E8=AE=BE=E7=BD=AE=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/Model.php b/src/Model.php index 9609478..979d952 100644 --- a/src/Model.php +++ b/src/Model.php @@ -268,6 +268,28 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $this->updateWhere = $where; } + /** + * 设置当前模型的数据库连接 + * @access public + * @param string $connection 数据表连接标识 + * @return $this + */ + public function setConnection(string $connection) + { + $this->connection = $connection; + return $this; + } + + /** + * 获取当前模型的数据库连接标识 + * @access public + * @return string + */ + public function getConnection(): string + { + return $this->connection ?: ''; + } + /** * 设置当前模型数据表的后缀 * @access public @@ -964,6 +986,20 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab return $model; } + /** + * 切换后缀进行查询 + * @access public + * @param string $suffix 切换的表后缀 + * @return Model + */ + public static function connect(string $connection) + { + $model = new static(); + $model->setConnection($connection); + + return $model; + } + public function __call($method, $args) { if ('withattr' == strtolower($method)) { -- Gitee From ac7a571e3f84286d05e2dfdc6b76a450806931b1 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 28 Nov 2019 10:03:57 +0800 Subject: [PATCH 129/365] =?UTF-8?q?=E6=B3=A8=E9=87=8A=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Model.php b/src/Model.php index 979d952..e855326 100644 --- a/src/Model.php +++ b/src/Model.php @@ -987,9 +987,9 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } /** - * 切换后缀进行查询 + * 切换数据库连接进行查询 * @access public - * @param string $suffix 切换的表后缀 + * @param string $connection 数据库连接标识 * @return Model */ public static function connect(string $connection) -- Gitee From 097d5ae4b195232801e8b6906b4be151c1dde6af Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 28 Nov 2019 18:51:56 +0800 Subject: [PATCH 130/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3column=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E7=9A=84=E6=9F=A5=E8=AF=A2=E7=BC=93=E5=AD=98=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 5cce2e1..171b4af 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1118,10 +1118,10 @@ abstract class PDOConnection extends Connection implements ConnectionInterface if (!empty($options['cache'])) { // 判断查询缓存 $cacheItem = $this->parseCache($query, $options['cache']); - $key = $cacheItem->getKey(); + $name = $cacheItem->getKey(); - if ($this->cache->has($key)) { - return $this->cache->get($key); + if ($this->cache->has($name)) { + return $this->cache->get($name); } } -- Gitee From 465b0dafc99f0a2d17034895bae398c4e0048241 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 30 Nov 2019 22:14:07 +0800 Subject: [PATCH 131/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BQuery=E7=B1=BB?= =?UTF-8?q?=E7=9A=84getAutoInc=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Query.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/db/Query.php b/src/db/Query.php index f039384..4fc9a99 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -353,12 +353,14 @@ class Query extends BaseQuery /** * 获取当前数据表的自增主键 * @access public - * @return string + * @return string|null */ public function getAutoInc() { - if (empty($this->autoinc)) { - $this->autoinc = $this->connection->getAutoInc($this->getTable()); + $tableName = $this->getTable(); + + if (empty($this->autoinc) && $tableName) { + $this->autoinc = $this->connection->getAutoInc($tableName); } return $this->autoinc; -- Gitee From 4b3c6c213a4e02ffdaae974daf69fc2c581d5bb6 Mon Sep 17 00:00:00 2001 From: TLingC <5617150+TLingC@users.noreply.github.com> Date: Wed, 4 Dec 2019 20:56:22 +0800 Subject: [PATCH 132/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=9D=A1=E4=BB=B6=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Model.php b/src/Model.php index e855326..42b324b 100644 --- a/src/Model.php +++ b/src/Model.php @@ -708,13 +708,13 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab { $pk = $this->getPk(); - if (is_string($pk) && isset($this->data[$pk])) { - $where = [[$pk, '=', $this->data[$pk]]]; - $this->key = $this->data[$pk]; + if (is_string($pk) && isset($this->origin[$pk])) { + $where = [[$pk, '=', $this->origin[$pk]]]; + $this->key = $this->origin[$pk]; } elseif (is_array($pk)) { foreach ($pk as $field) { - if (isset($this->data[$field])) { - $where[] = [$field, '=', $this->data[$field]]; + if (isset($this->origin[$field])) { + $where[] = [$field, '=', $this->origin[$field]]; } } } -- Gitee From 79cff6640f9bf9995b1cecd8b84fa58487c15eb2 Mon Sep 17 00:00:00 2001 From: hejiaqiang1980 Date: Sat, 7 Dec 2019 10:35:55 +0800 Subject: [PATCH 133/365] Update Model.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修正使用模型对象更改数据时忽略自定义的suffix 和 connection参数 --- src/Model.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Model.php b/src/Model.php index 42b324b..be6df4a 100644 --- a/src/Model.php +++ b/src/Model.php @@ -245,11 +245,22 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab */ public function newInstance(array $data = [], $where = null): Model { + $model = new static($data); + + if ($this->connection) { + $model->setConnection($this->connection); + } + + if ($this->suffix) { + $model->setSuffix($this->suffix); + } + if (empty($data)) { - return new static(); + return $model; } - $model = (new static($data))->exists(true); + $model->exists(true); + $model->setUpdateWhere($where); $model->trigger('AfterRead'); -- Gitee From 54a66ed6eaf177b9e636ec0f30bc7feca7b2811f Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 17 Dec 2019 17:33:54 +0800 Subject: [PATCH 134/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3XA=E4=BA=8B=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/connector/Mysql.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/db/connector/Mysql.php b/src/db/connector/Mysql.php index 1db3152..fd0d544 100644 --- a/src/db/connector/Mysql.php +++ b/src/db/connector/Mysql.php @@ -120,7 +120,7 @@ class Mysql extends PDOConnection public function startTransXa(string $xid) { $this->initConnect(true); - $this->linkID->execute("XA START '$xid'"); + $this->linkID->exec("XA START '$xid'"); } /** @@ -132,8 +132,8 @@ class Mysql extends PDOConnection public function prepareXa(string $xid) { $this->initConnect(true); - $this->linkID->execute("XA END '$xid'"); - $this->linkID->execute("XA PREPARE '$xid'"); + $this->linkID->exec("XA END '$xid'"); + $this->linkID->exec("XA PREPARE '$xid'"); } /** @@ -145,7 +145,7 @@ class Mysql extends PDOConnection public function commitXa(string $xid) { $this->initConnect(true); - $this->linkID->execute("XA COMMIT '$xid'"); + $this->linkID->exec("XA COMMIT '$xid'"); } /** @@ -157,6 +157,6 @@ class Mysql extends PDOConnection public function rollbackXa(string $xid) { $this->initConnect(true); - $this->linkID->execute("XA ROLLBACK '$xid'"); + $this->linkID->exec("XA ROLLBACK '$xid'"); } } -- Gitee From 37cde31af3725cf5460ad8dc18032326b88e6310 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 23 Dec 2019 21:50:01 +0800 Subject: [PATCH 135/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bcolumn=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 171b4af..482e759 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1144,8 +1144,9 @@ abstract class PDOConnection extends Connection implements ConnectionInterface } elseif (('*' == $column || strpos($column, ',')) && $key) { $result = array_column($resultSet, null, $key); } else { - $fields = array_keys($resultSet[0]); - $key = $key ?: array_shift($fields); + if (empty($key)) { + $key = null; + } if (strpos($column, ',')) { $column = null; @@ -1153,7 +1154,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface [$alias, $column] = explode('.', $column); } - if (strpos($key, '.')) { + if (is_string($key) && strpos($key, '.')) { [$alias, $key] = explode('.', $key); } -- Gitee From 33891e2291269e90f67e73594d52650fa3618523 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 27 Dec 2019 10:57:22 +0800 Subject: [PATCH 136/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=B5=AE=E7=82=B9=E7=B1=BB=E5=9E=8B=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 5c0eea7..a02dc3f 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -411,7 +411,7 @@ trait Attribute if (empty($param)) { $value = (float) $value; } else { - $value = (float) number_format($value, $param, '.', ''); + $value = (float) number_format($value, (int) $param, '.', ''); } break; case 'boolean': @@ -576,7 +576,7 @@ trait Attribute if (empty($param)) { $value = (float) $value; } else { - $value = (float) number_format($value, $param, '.', ''); + $value = (float) number_format($value, (int) $param, '.', ''); } break; case 'boolean': -- Gitee From 7d41788f574fa9d6af4b17d4a48ca5e0dd4001ef Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 28 Dec 2019 12:10:11 +0800 Subject: [PATCH 137/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=9A=E6=80=81?= =?UTF-8?q?=E5=A4=9A=E5=AF=B9=E5=A4=9A=E5=85=B3=E8=81=94=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/RelationShip.php | 60 +++++ src/model/relation/MorphToMany.php | 398 +++++++++++++++++++++++++++++ 2 files changed, 458 insertions(+) create mode 100644 src/model/relation/MorphToMany.php diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 7b0d4a7..43b76e1 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -28,6 +28,7 @@ use think\model\relation\HasOneThrough; use think\model\relation\MorphMany; use think\model\relation\MorphOne; use think\model\relation\MorphTo; +use think\model\relation\MorphToMany; use think\model\relation\OneToOne; /** @@ -617,6 +618,65 @@ trait RelationShip return new MorphTo($this, $morphType, $foreignKey, $alias, $relation); } + /** + * MORPH TO MANY关联定义 + * @access public + * @param string $model 模型名 + * @param string $middle 中间表名/模型名 + * @param string|array $morph 多态字段信息 + * @param string $localKey 当前模型关联键 + * @return MorphToMany + */ + public function morphToMany(string $model, string $middle, $morph = null, string $localKey = null): MorphToMany + { + if (is_null($morph)) { + $morph = $middle; + } + + // 记录当前关联信息 + if (is_array($morph)) { + [$morphType, $morphKey] = $morph; + } else { + $morphType = $morph . '_type'; + $morphKey = $morph . '_id'; + } + + $model = $this->parseModel($model); + $localKey = $localKey ?: $this->getForeignKey($this->name); + + return new MorphToMany($this, $model, $middle, $morphType, $morphKey, $localKey); + } + + /** + * MORPH BY MANY关联定义 + * @access public + * @param string $model 模型名 + * @param string $middle 中间表名/模型名 + * @param string|array $morph 多态字段信息 + * @param string $foreignKey 关联外键 + * @param array $alias 多态别名定义 + * @return MorphToMany + */ + public function morphByMany(string $model, string $middle, $morph = null, string $foreignKey = null): MorphToMany + { + if (is_null($morph)) { + $morph = $middle; + } + + // 记录当前关联信息 + if (is_array($morph)) { + [$morphType, $morphKey] = $morph; + } else { + $morphType = $morph . '_type'; + $morphKey = $morph . '_id'; + } + + $model = $this->parseModel($model); + $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); + + return new MorphToMany($this, $model, $middle, $morphType, $morphKey, $foreignKey, true); + } + /** * 解析模型的完整命名空间 * @access protected diff --git a/src/model/relation/MorphToMany.php b/src/model/relation/MorphToMany.php new file mode 100644 index 0000000..c89e44c --- /dev/null +++ b/src/model/relation/MorphToMany.php @@ -0,0 +1,398 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\relation; + +use Closure; +use think\db\BaseQuery as Query; +use think\helper\Str; +use think\Model; +use think\model\Relation; + +/** + * 多态多对多关联 + */ +class MorphToMany extends BelongsToMany +{ + + /** + * 多态关联外键 + * @var string + */ + protected $morphKey; + + /** + * 多态字段名 + * @var string + */ + protected $morphType; + + /** + * 多态模型名 + * @var string + */ + protected $morphClass; + + /** + * 架构函数 + * @access public + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $middle 中间表名/模型名 + * @param string $morphKey 关联外键 + * @param string $morphType 多态字段名 + * @param string $localKey 当前模型关联键 + * @param bool $inverse 反向关联 + */ + public function __construct(Model $parent, string $model, string $middle, string $morphType, string $morphKey, string $localKey, bool $inverse = false) + { + $this->morphKey = $morphKey; + $this->morphType = $morphType; + $this->morphClass = $inverse ? $model : get_class($parent); + + parent::__construct($parent, $model, $middle, $morphKey, $localKey); + } + + /** + * 预载入关联查询(数据集) + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 + * @param array $cache 关联缓存 + * @return void + */ + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void + { + $pk = $resultSet[0]->getPk(); + $range = []; + + foreach ($resultSet as $result) { + // 获取关联外键列表 + if (isset($result->$pk)) { + $range[] = $result->$pk; + } + } + + if (!empty($range)) { + // 查询关联数据 + $data = $this->eagerlyManyToMany([ + ['pivot.' . $this->morphKey, 'in', $range], + ['pivot.' . $this->morphType, '=', $this->morphClass], + ], $subRelation, $closure, $cache); + + // 关联数据封装 + foreach ($resultSet as $result) { + if (!isset($data[$result->$pk])) { + $data[$result->$pk] = []; + } + + $result->setRelation($relation, $this->resultSetBuild($data[$result->$pk], clone $this->parent)); + } + } + } + + /** + * 预载入关联查询(单个数据) + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 + * @param array $cache 关联缓存 + * @return void + */ + public function eagerlyResult(Model $result, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void + { + $pk = $result->getPk(); + + if (isset($result->$pk)) { + $pk = $result->$pk; + // 查询管理数据 + $data = $this->eagerlyManyToMany([ + ['pivot.' . $this->morphKey, '=', $pk], + ['pivot.' . $this->morphType, '=', $this->morphClass], + ], $subRelation, $closure, $cache); + + // 关联数据封装 + if (!isset($data[$pk])) { + $data[$pk] = []; + } + + $result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent)); + } + } + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 + * @return mixed + */ + public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): float + { + $pk = $result->getPk(); + + if (!isset($result->$pk)) { + return 0; + } + + if ($closure) { + $closure($this->getClosureType($closure), $name); + } + + return $this->query + ->where([ + [$this->morphKey, '=', $result->$pk], + [$this->morphType, '=', $this->morphClass], + ]) + ->$aggregate($field); + } + + /** + * 获取关联统计子查询 + * @access public + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 + * @return string + */ + public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string + { + if ($closure) { + $closure($this->getClosureType($closure), $name); + } + + return $this->query + ->whereExp($this->morphKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk()) + ->where($this->morphType, '=', $this->morphClass) + ->fetchSql() + ->$aggregate($field); + } + + /** + * 附加关联的一个中间表数据 + * @access public + * @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键 + * @param array $pivot 中间表额外数据 + * @return array|Pivot + * @throws Exception + */ + public function attach($data, array $pivot = []) + { + if (is_array($data)) { + if (key($data) === 0) { + $id = $data; + } else { + // 保存关联表数据 + $model = new $this->model; + $id = $model->insertGetId($data); + } + } elseif (is_numeric($data) || is_string($data)) { + // 根据关联表主键直接写入中间表 + $id = $data; + } elseif ($data instanceof Model) { + // 根据关联表主键直接写入中间表 + $relationFk = $data->getPk(); + $id = $data->$relationFk; + } + + if (!empty($id)) { + // 保存中间表数据 + $pk = $this->parent->getPk(); + $pivot[$this->morphKey] = $this->parent->$pk; + $pivot[$this->morphType] = $this->morphClass; + $ids = (array) $id; + + foreach ($ids as $id) { + $pivot[$this->foreignKey] = $id; + $this->pivot->replace() + ->exists(false) + ->data([]) + ->save($pivot); + $result[] = $this->newPivot($pivot); + } + + if (count($result) == 1) { + // 返回中间表模型对象 + $result = $result[0]; + } + + return $result; + } else { + throw new Exception('miss relation data'); + } + } + + /** + * 判断是否存在关联数据 + * @access public + * @param mixed $data 数据 可以使用关联模型对象 或者 关联对象的主键 + * @return Pivot|false + */ + public function attached($data) + { + if ($data instanceof Model) { + $id = $data->getKey(); + } else { + $id = $data; + } + + $pivot = $this->pivot + ->where($this->morphKey, $this->parent->getKey()) + ->where($this->morphType, $this->morphClass) + ->where($this->foreignKey, $id) + ->find(); + + return $pivot ?: false; + } + + /** + * 解除关联的一个中间表数据 + * @access public + * @param integer|array $data 数据 可以使用关联对象的主键 + * @param bool $relationDel 是否同时删除关联表数据 + * @return integer + */ + public function detach($data = null, bool $relationDel = false): int + { + 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->morphKey, '=', $this->parent->$pk], + [$this->morphType, '=', $this->morphClass], + ]; + + if (isset($id)) { + $pivot[] = [$this->foreignKey, is_array($id) ? 'in' : '=', $id]; + } + + $result = $this->pivot->where($pivot)->delete(); + + // 删除关联表数据 + if (isset($id) && $relationDel) { + $model = $this->model; + $model::destroy($id); + } + + return $result; + } + + /** + * 数据同步 + * @access public + * @param array $ids + * @param bool $detaching + * @return array + */ + public function sync(array $ids, bool $detaching = true): array + { + $changes = [ + 'attached' => [], + 'detached' => [], + 'updated' => [], + ]; + + $pk = $this->parent->getPk(); + + $current = $this->pivot + ->where($this->morphKey, $this->parent->$pk) + ->where($this->morphType, $this->morphClass) + ->column($this->foreignKey); + + $records = []; + + foreach ($ids as $key => $value) { + if (!is_array($value)) { + $records[$value] = []; + } else { + $records[$key] = $value; + } + } + + $detach = array_diff($current, array_keys($records)); + + if ($detaching && count($detach) > 0) { + $this->detach($detach); + $changes['detached'] = $detach; + } + + foreach ($records as $id => $attributes) { + if (!in_array($id, $current)) { + $this->attach($id, $attributes); + $changes['attached'][] = $id; + } elseif (count($attributes) > 0 && $this->attach($id, $attributes)) { + $changes['updated'][] = $id; + } + } + + return $changes; + } + + /** + * 创建关联查询Query对象 + * @access protected + * @return Query + */ + protected function buildQuery(): Query + { + $foreignKey = $this->foreignKey; + $localKey = $this->localKey; + + // 关联查询 + $pk = $this->parent->getPk(); + + $condition = [ + ['pivot.' . $this->morphKey, '=', $this->parent->$pk], + ['pivot.' . $this->morphType, '=', $this->morphClass], + ]; + + return $this->belongsToManyQuery($foreignKey, $localKey, $condition); + } + + /** + * 执行基础查询(仅执行一次) + * @access protected + * @return void + */ + protected function baseQuery(): void + { + if (empty($this->baseQuery) && $this->parent->getData()) { + $pk = $this->parent->getPk(); + + $this->query->where([ + [$this->morphKey, '=', $this->parent->$pk], + [$this->morphType, '=', $this->morphClass], + ]); + + $this->baseQuery = true; + } + } + +} -- Gitee From eb277091925bf8f82c5d0fd4f3d8aa94d85208d1 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 28 Dec 2019 12:12:19 +0800 Subject: [PATCH 138/365] =?UTF-8?q?=E6=B3=A8=E9=87=8A=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/RelationShip.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 43b76e1..b46916e 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -654,7 +654,6 @@ trait RelationShip * @param string $middle 中间表名/模型名 * @param string|array $morph 多态字段信息 * @param string $foreignKey 关联外键 - * @param array $alias 多态别名定义 * @return MorphToMany */ public function morphByMany(string $model, string $middle, $morph = null, string $foreignKey = null): MorphToMany -- Gitee From d685b9fb8644fa06bd12dc6e7f40bf4aa9e4831f Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 28 Dec 2019 12:26:02 +0800 Subject: [PATCH 139/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/MorphToMany.php | 49 ++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/model/relation/MorphToMany.php b/src/model/relation/MorphToMany.php index c89e44c..cc97c63 100644 --- a/src/model/relation/MorphToMany.php +++ b/src/model/relation/MorphToMany.php @@ -355,6 +355,55 @@ class MorphToMany extends BelongsToMany return $changes; } + /** + * 多对多 关联模型预查询 + * @access protected + * @param array $where 关联预查询条件 + * @param array $subRelation 子关联 + * @param Closure $closure 闭包 + * @param array $cache 关联缓存 + * @return array + */ + protected function eagerlyManyToMany(array $where, array $subRelation = [], Closure $closure = null, array $cache = []): array + { + if ($closure) { + $closure($this->getClosureType($closure)); + } + + // 预载入关联查询 支持嵌套预载入 + $list = $this->belongsToManyQuery($this->morphKey, $this->localKey, $where) + ->with($subRelation) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) + ->select(); + + // 组装模型数据 + $data = []; + foreach ($list as $set) { + $pivot = []; + foreach ($set->getData() as $key => $val) { + if (strpos($key, '__')) { + [$name, $attr] = explode('__', $key, 2); + if ('pivot' == $name) { + $pivot[$attr] = $val; + unset($set->$key); + } + } + } + + $key = $pivot[$this->morphKey]; + + if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { + continue; + } + + $set->setRelation($this->pivotDataName, $this->newPivot($pivot)); + + $data[$key][] = $set; + } + + return $data; + } + /** * 创建关联查询Query对象 * @access protected -- Gitee From b2a26c54d5f4ef2f861e672d70f00b404aede787 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 28 Dec 2019 12:52:16 +0800 Subject: [PATCH 140/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=A4=9A=E5=AF=B9?= =?UTF-8?q?=E5=A4=9A=E5=85=B3=E8=81=94=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/MorphToMany.php | 161 ++++++++++++++++++----------- 1 file changed, 100 insertions(+), 61 deletions(-) diff --git a/src/model/relation/MorphToMany.php b/src/model/relation/MorphToMany.php index cc97c63..d64d73c 100644 --- a/src/model/relation/MorphToMany.php +++ b/src/model/relation/MorphToMany.php @@ -13,6 +13,7 @@ namespace think\model\relation; use Closure; use think\db\BaseQuery as Query; +use think\db\Raw; use think\helper\Str; use think\Model; use think\model\Relation; @@ -41,6 +42,12 @@ class MorphToMany extends BelongsToMany */ protected $morphClass; + /** + * 是否反向关联 + * @var bool + */ + protected $inverse; + /** * 架构函数 * @access public @@ -56,6 +63,7 @@ class MorphToMany extends BelongsToMany { $this->morphKey = $morphKey; $this->morphType = $morphType; + $this->inverse = $inverse; $this->morphClass = $inverse ? $model : get_class($parent); parent::__construct($parent, $model, $middle, $morphKey, $localKey); @@ -140,7 +148,7 @@ class MorphToMany extends BelongsToMany * @param string $aggregate 聚合查询方法 * @param string $field 字段 * @param string $name 统计字段别名 - * @return mixed + * @return integer */ public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): float { @@ -150,16 +158,16 @@ class MorphToMany extends BelongsToMany return 0; } + $pk = $result->$pk; + if ($closure) { $closure($this->getClosureType($closure), $name); } - return $this->query - ->where([ - [$this->morphKey, '=', $result->$pk], - [$this->morphType, '=', $this->morphClass], - ]) - ->$aggregate($field); + return $this->belongsToManyQuery($this->morphKey, $this->localKey, [ + ['pivot.' . ($this->inverse ? $this->localKey : $this->morphKey), '=', $pk], + ['pivot.' . $this->morphType, '=', $this->morphClass], + ])->$aggregate($field); } /** @@ -177,11 +185,91 @@ class MorphToMany extends BelongsToMany $closure($this->getClosureType($closure), $name); } - return $this->query - ->whereExp($this->morphKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk()) - ->where($this->morphType, '=', $this->morphClass) - ->fetchSql() - ->$aggregate($field); + return $this->belongsToManyQuery($this->morphKey, $this->localKey, [ + ['pivot.' . ($this->inverse ? $this->localKey : $this->morphKey), 'exp', new Raw('=' . $this->parent->db(false)->getTable() . '.' . $this->parent->getPk())], + ['pivot.' . $this->morphType, '=', $this->morphClass], + ])->fetchSql()->$aggregate($field); + } + + /** + * BELONGS TO MANY 关联查询 + * @access protected + * @param string $foreignKey 关联模型关联键 + * @param string $localKey 当前模型关联键 + * @param array $condition 关联查询条件 + * @return Query + */ + protected function belongsToManyQuery(string $foreignKey, string $localKey, array $condition = []): Query + { + // 关联查询封装 + $tableName = $this->query->getTable(); + $table = $this->pivot->db()->getTable(); + $fields = $this->getQueryFields($tableName); + + if ($this->withLimit) { + $this->query->limit($this->withLimit); + } + + $query = $this->query + ->field($fields) + ->tableField(true, $table, 'pivot', 'pivot__'); + + if (empty($this->baseQuery)) { + $relationFk = $this->query->getPk(); + $query->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk) + ->where($condition); + } + + return $query; + } + + /** + * 多对多 关联模型预查询 + * @access protected + * @param array $where 关联预查询条件 + * @param array $subRelation 子关联 + * @param Closure $closure 闭包 + * @param array $cache 关联缓存 + * @return array + */ + protected function eagerlyManyToMany(array $where, array $subRelation = [], Closure $closure = null, array $cache = []): array + { + if ($closure) { + $closure($this->getClosureType($closure)); + } + + // 预载入关联查询 支持嵌套预载入 + $list = $this->belongsToManyQuery($this->morphKey, $this->localKey, $where) + ->with($subRelation) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) + ->select(); + + // 组装模型数据 + $data = []; + foreach ($list as $set) { + $pivot = []; + foreach ($set->getData() as $key => $val) { + if (strpos($key, '__')) { + [$name, $attr] = explode('__', $key, 2); + if ('pivot' == $name) { + $pivot[$attr] = $val; + unset($set->$key); + } + } + } + + $key = $pivot[$this->morphKey]; + + if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { + continue; + } + + $set->setRelation($this->pivotDataName, $this->newPivot($pivot)); + + $data[$key][] = $set; + } + + return $data; } /** @@ -355,55 +443,6 @@ class MorphToMany extends BelongsToMany return $changes; } - /** - * 多对多 关联模型预查询 - * @access protected - * @param array $where 关联预查询条件 - * @param array $subRelation 子关联 - * @param Closure $closure 闭包 - * @param array $cache 关联缓存 - * @return array - */ - protected function eagerlyManyToMany(array $where, array $subRelation = [], Closure $closure = null, array $cache = []): array - { - if ($closure) { - $closure($this->getClosureType($closure)); - } - - // 预载入关联查询 支持嵌套预载入 - $list = $this->belongsToManyQuery($this->morphKey, $this->localKey, $where) - ->with($subRelation) - ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) - ->select(); - - // 组装模型数据 - $data = []; - foreach ($list as $set) { - $pivot = []; - foreach ($set->getData() as $key => $val) { - if (strpos($key, '__')) { - [$name, $attr] = explode('__', $key, 2); - if ('pivot' == $name) { - $pivot[$attr] = $val; - unset($set->$key); - } - } - } - - $key = $pivot[$this->morphKey]; - - if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { - continue; - } - - $set->setRelation($this->pivotDataName, $this->newPivot($pivot)); - - $data[$key][] = $set; - } - - return $data; - } - /** * 创建关联查询Query对象 * @access protected -- Gitee From 444277e2acff186a8066a8a42912ebea01d948c5 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 28 Dec 2019 21:25:27 +0800 Subject: [PATCH 141/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=A4=9A=E6=80=81?= =?UTF-8?q?=E5=A4=9A=E5=AF=B9=E5=A4=9A=E5=85=B3=E8=81=94attach=E7=AD=89?= =?UTF-8?q?=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/RelationShip.php | 3 +- src/model/relation/BelongsToMany.php | 23 +++++---------- src/model/relation/MorphToMany.php | 42 ++++++++++------------------ 3 files changed, 24 insertions(+), 44 deletions(-) diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index b46916e..4839228 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -642,7 +642,8 @@ trait RelationShip } $model = $this->parseModel($model); - $localKey = $localKey ?: $this->getForeignKey($this->name); + $name = Str::snake(class_basename($model)); + $localKey = $localKey ?: $this->getForeignKey($name); return new MorphToMany($this, $model, $middle, $morphType, $morphKey, $localKey); } diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index eccbfe9..69888c9 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -157,9 +157,7 @@ class BelongsToMany extends Relation $localKey = $this->localKey; // 关联查询 - $pk = $this->parent->getPk(); - - $condition = ['pivot.' . $localKey, '=', $this->parent->$pk]; + $condition = ['pivot.' . $localKey, '=', $this->parent->getKey()]; return $this->belongsToManyQuery($foreignKey, $localKey, [$condition]); } @@ -454,7 +452,6 @@ class BelongsToMany extends Relation } } } - $key = $pivot[$this->localKey]; if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { @@ -562,16 +559,14 @@ class BelongsToMany extends Relation $id = $data; } elseif ($data instanceof Model) { // 根据关联表主键直接写入中间表 - $relationFk = $data->getPk(); - $id = $data->$relationFk; + $id = $data->getKey(); } if (!empty($id)) { // 保存中间表数据 - $pk = $this->parent->getPk(); - $pivot[$this->localKey] = $this->parent->$pk; - $ids = (array) $id; + $pivot[$this->localKey] = $this->parent->getKey(); + $ids = (array) $id; foreach ($ids as $id) { $pivot[$this->foreignKey] = $id; $this->pivot->replace() @@ -630,14 +625,12 @@ class BelongsToMany extends Relation $id = $data; } elseif ($data instanceof Model) { // 根据关联表主键直接写入中间表 - $relationFk = $data->getPk(); - $id = $data->$relationFk; + $id = $data->getKey(); } // 删除中间表数据 - $pk = $this->parent->getPk(); $pivot = []; - $pivot[] = [$this->localKey, '=', $this->parent->$pk]; + $pivot[] = [$this->localKey, '=', $this->parent->getKey()]; if (isset($id)) { $pivot[] = [$this->foreignKey, is_array($id) ? 'in' : '=', $id]; @@ -669,10 +662,8 @@ class BelongsToMany extends Relation 'updated' => [], ]; - $pk = $this->parent->getPk(); - $current = $this->pivot - ->where($this->localKey, $this->parent->$pk) + ->where($this->localKey, $this->parent->getKey()) ->column($this->foreignKey); $records = []; diff --git a/src/model/relation/MorphToMany.php b/src/model/relation/MorphToMany.php index d64d73c..7b8438b 100644 --- a/src/model/relation/MorphToMany.php +++ b/src/model/relation/MorphToMany.php @@ -295,19 +295,19 @@ class MorphToMany extends BelongsToMany $id = $data; } elseif ($data instanceof Model) { // 根据关联表主键直接写入中间表 - $relationFk = $data->getPk(); - $id = $data->$relationFk; + $id = $data->getKey(); } if (!empty($id)) { // 保存中间表数据 - $pk = $this->parent->getPk(); - $pivot[$this->morphKey] = $this->parent->$pk; + $pivot[$this->inverse ? $this->localKey : $this->morphKey] = $this->parent->getKey(); + $pivot[$this->morphType] = $this->morphClass; $ids = (array) $id; foreach ($ids as $id) { - $pivot[$this->foreignKey] = $id; + $pivot[$this->inverse ? $this->morphKey : $this->localKey] = $id; + $this->pivot->replace() ->exists(false) ->data([]) @@ -341,9 +341,9 @@ class MorphToMany extends BelongsToMany } $pivot = $this->pivot - ->where($this->morphKey, $this->parent->getKey()) + ->where($this->inverse ? $this->localKey : $this->morphKey, $this->parent->getKey()) ->where($this->morphType, $this->morphClass) - ->where($this->foreignKey, $id) + ->where($this->inverse ? $this->morphKey : $this->localKey, $id) ->find(); return $pivot ?: false; @@ -365,20 +365,17 @@ class MorphToMany extends BelongsToMany $id = $data; } elseif ($data instanceof Model) { // 根据关联表主键直接写入中间表 - $relationFk = $data->getPk(); - $id = $data->$relationFk; + $id = $data->getKey(); } // 删除中间表数据 - $pk = $this->parent->getPk(); - $pivot = [ - [$this->morphKey, '=', $this->parent->$pk], + [$this->inverse ? $this->localKey : $this->morphKey, '=', $this->parent->getKey()], [$this->morphType, '=', $this->morphClass], ]; if (isset($id)) { - $pivot[] = [$this->foreignKey, is_array($id) ? 'in' : '=', $id]; + $pivot[] = [$this->inverse ? $this->morphKey : $this->localKey, is_array($id) ? 'in' : '=', $id]; } $result = $this->pivot->where($pivot)->delete(); @@ -407,12 +404,10 @@ class MorphToMany extends BelongsToMany 'updated' => [], ]; - $pk = $this->parent->getPk(); - $current = $this->pivot - ->where($this->morphKey, $this->parent->$pk) + ->where($this->inverse ? $this->localKey : $this->morphKey, $this->parent->getKey()) ->where($this->morphType, $this->morphClass) - ->column($this->foreignKey); + ->column($this->inverse ? $this->morphKey : $this->localKey); $records = []; @@ -450,18 +445,13 @@ class MorphToMany extends BelongsToMany */ protected function buildQuery(): Query { - $foreignKey = $this->foreignKey; - $localKey = $this->localKey; - // 关联查询 - $pk = $this->parent->getPk(); - $condition = [ - ['pivot.' . $this->morphKey, '=', $this->parent->$pk], + ['pivot.' . $this->morphKey, '=', $this->parent->getKey()], ['pivot.' . $this->morphType, '=', $this->morphClass], ]; - return $this->belongsToManyQuery($foreignKey, $localKey, $condition); + return $this->belongsToManyQuery($this->morphKey, $this->localKey, $condition); } /** @@ -472,10 +462,8 @@ class MorphToMany extends BelongsToMany protected function baseQuery(): void { if (empty($this->baseQuery) && $this->parent->getData()) { - $pk = $this->parent->getPk(); - $this->query->where([ - [$this->morphKey, '=', $this->parent->$pk], + [$this->morphKey, '=', $this->parent->getKey()], [$this->morphType, '=', $this->morphClass], ]); -- Gitee From a343dbb39fc51a09e78a68e7968dc8e8060c4a66 Mon Sep 17 00:00:00 2001 From: klinson Date: Tue, 31 Dec 2019 18:31:55 +0800 Subject: [PATCH 142/365] =?UTF-8?q?mongodb=E5=8A=A0=E5=85=A5=E4=BA=8B?= =?UTF-8?q?=E5=8A=A1=E6=94=AF=E6=8C=81=EF=BC=8C=E9=9C=80=E8=A6=81mongodb?= =?UTF-8?q?=E7=89=88=E6=9C=ACV4.0=E4=BB=A5=E4=B8=8A=E4=B8=94=E5=BC=80?= =?UTF-8?q?=E5=90=AF=E5=A4=8D=E5=88=B6=E9=9B=86=E6=88=96=E5=88=86=E5=B8=83?= =?UTF-8?q?=E5=BC=8F=20=E4=BA=8B=E5=8A=A1=E5=86=85=E6=94=AF=E6=8C=81insert?= =?UTF-8?q?=E3=80=81update=E3=80=81delete=E3=80=81cmd=E7=AD=89=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=96=B9=E5=BC=8F=20=E6=94=AF=E6=8C=81=E6=97=A0?= =?UTF-8?q?=E9=99=90=E7=BA=A7=E5=B5=8C=E5=A5=97=E4=BA=8B=E5=8A=A1=EF=BC=8C?= =?UTF-8?q?=E4=BD=86=E6=98=AF=E5=A4=96=E5=9B=B4=E4=BA=8B=E5=8A=A1=E5=9B=9E?= =?UTF-8?q?=E6=BB=9A=E4=B8=8D=E5=BD=B1=E5=93=8D=E5=86=85=E9=83=A8=E4=BA=8B?= =?UTF-8?q?=E5=8A=A1=E7=9A=84commit=20```=20Db::transaction(function=20()?= =?UTF-8?q?=20{=20=20=20=20=20db()->table('users')->insert(['username'=20?= =?UTF-8?q?=3D>=20'test80']);=20=20=20=20=20Db::startTrans();=20=20=20=20?= =?UTF-8?q?=20db()->table('users')->where(['username'=20=3D>=20'test84'])-?= =?UTF-8?q?>update(['sex'=20=3D>=204]);=20=20=20=20=20Db::commit();=20});?= =?UTF-8?q?=20```?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/connector/Mongo.php | 99 +++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 7 deletions(-) diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 4eb631b..809246f 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -23,6 +23,7 @@ use MongoDB\Driver\Exception\RuntimeException; use MongoDB\Driver\Manager; use MongoDB\Driver\Query as MongoQuery; use MongoDB\Driver\ReadPreference; +use MongoDB\Driver\WriteConcern; use think\db\BaseQuery; use think\db\builder\Mongo as Builder; use think\db\Connection; @@ -41,6 +42,8 @@ class Mongo extends Connection implements ConnectionInterface protected $typeMap = 'array'; protected $mongo; // MongoDb Object protected $cursor; // MongoCursor Object + protected $session_uuid; // sessions会话列表当前会话数组key 随机生成 + protected $sessions = []; // 会话列表 // 数据库连接参数配置 protected $config = [ @@ -265,7 +268,14 @@ class Mongo extends Connection implements ConnectionInterface $readPreference = $options['readPreference'] ?? null; $this->queryStartTime = microtime(true); - $this->cursor = $this->mongo->executeQuery($namespace, $mongoQuery, $readPreference); + if ($session = $this->getSession()) { + $this->cursor = $this->mongo->executeQuery($namespace, $query, [ + 'readPreference' => is_null($readPreference) ? new ReadPreference(ReadPreference::RP_PRIMARY) : $readPreference, + 'session' => $session + ]); + } else { + $this->cursor = $this->mongo->executeQuery($namespace, $mongoQuery, $readPreference); + } // SQL监控 if (!empty($this->config['trigger_sql'])) { @@ -351,7 +361,14 @@ class Mongo extends Connection implements ConnectionInterface $writeConcern = $options['writeConcern'] ?? null; $this->queryStartTime = microtime(true); - $writeResult = $this->mongo->executeBulkWrite($namespace, $bulk, $writeConcern); + if ($session = $this->getSession()) { + $writeResult = $this->mongo->executeBulkWrite($namespace, $bulk, [ + 'session' => $session, + 'writeConcern' => is_null($writeConcern) ? new WriteConcern(1) : $writeConcern + ]); + } else { + $writeResult = $this->mongo->executeBulkWrite($namespace, $bulk, $writeConcern); + } // SQL监控 if (!empty($this->config['trigger_sql'])) { @@ -403,7 +420,14 @@ class Mongo extends Connection implements ConnectionInterface $this->queryStr = 'db.' . $this->queryStr; } - $this->cursor = $this->mongo->executeCommand($dbName, $command, $readPreference); + if ($session = $this->getSession()) { + $this->cursor = $this->mongo->executeCommand($dbName, $command, [ + 'readPreference' => is_null($readPreference) ? new ReadPreference(ReadPreference::RP_PRIMARY) : $readPreference, + 'session' => $session + ]); + } else { + $this->cursor = $this->mongo->executeCommand($dbName, $command, $readPreference); + } // SQL监控 if (!empty($this->config['trigger_sql'])) { @@ -1033,7 +1057,23 @@ class Mongo extends Connection implements ConnectionInterface * @throws \Throwable */ public function transaction(callable $callback) - {} + { + $this->startTrans(); + try { + $result = null; + if (is_callable($callback)) { + $result = call_user_func_array($callback, [$this]); + } + $this->commit(); + return $result; + } catch (\Exception $e) { + $this->rollback(); + throw $e; + } catch (\Throwable $e) { + $this->rollback(); + throw $e; + } + } /** * 启动事务 @@ -1043,7 +1083,13 @@ class Mongo extends Connection implements ConnectionInterface * @throws \Exception */ public function startTrans() - {} + { + $this->initConnect(true); + $this->session_uuid = uniqid(); + $this->sessions[$this->session_uuid] = $this->getMongo()->startSession(); + + $this->sessions[$this->session_uuid]->startTransaction([]); + } /** * 用于非自动提交状态下面的查询提交 @@ -1052,7 +1098,12 @@ class Mongo extends Connection implements ConnectionInterface * @throws PDOException */ public function commit() - {} + { + if ($session = $this->getSession()) { + $session->commitTransaction(); + $this->setLastSession(); + } + } /** * 事务回滚 @@ -1061,6 +1112,40 @@ class Mongo extends Connection implements ConnectionInterface * @throws PDOException */ public function rollback() - {} + { + if ($session = $this->getSession()) { + $session->abortTransaction(); + $this->setLastSession(); + } + } + + /** + * 结束当前会话,设置上一个会话为当前会话 + * @author klinson + */ + protected function setLastSession() + { + if ($session = $this->getSession()) { + $session->endSession(); + unset($this->sessions[$this->session_uuid]); + if (empty($this->sessions)) { + $this->session_uuid = null; + } else { + end($this->sessions); + $this->session_uuid = key($this->sessions); + } + } + } + /** + * 获取当前会话 + * @return \MongoDB\Driver\Session|null + * @author klinson + */ + public function getSession() + { + return ($this->session_uuid && isset($this->sessions[$this->session_uuid])) + ? $this->sessions[$this->session_uuid] + : null; + } } -- Gitee From e90ef8c33ce770932dec397857d39f4af8d4d499 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 2 Jan 2020 17:09:21 +0800 Subject: [PATCH 143/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BinsertAll=E5=AF=B9rep?= =?UTF-8?q?lace=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 482e759..0d23b83 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -906,7 +906,8 @@ abstract class PDOConnection extends Connection implements ConnectionInterface return 0; } - $query->parseOptions(); + $options = $query->parseOptions(); + $replace = !empty($options['replace']); if (0 === $limit && count($dataSet) >= 5000) { $limit = 1000; @@ -921,7 +922,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface $count = 0; foreach ($array as $item) { - $sql = $this->builder->insertAll($query, $item); + $sql = $this->builder->insertAll($query, $item, $replace); $count += $this->execute($query, $sql, $query->getBind()); } @@ -935,7 +936,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface return $count; } - $sql = $this->builder->insertAll($query, $dataSet); + $sql = $this->builder->insertAll($query, $dataSet, $replace); return $this->execute($query, $sql, $query->getBind()); } -- Gitee From 9caecd97608c937059fc09550b41af2557471d35 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 4 Jan 2020 12:00:06 +0800 Subject: [PATCH 144/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bdatetime=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=86=99=E5=85=A5=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index a02dc3f..9e4367b 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -424,7 +424,7 @@ trait Attribute break; case 'datetime': $value = is_numeric($value) ? $value : strtotime($value); - $value = $this->formatDateTime('Y-m-d H:i:s.u', $value); + $value = $this->formatDateTime('Y-m-d H:i:s.u', $value, true); break; case 'object': if (is_object($value)) { -- Gitee From d6965dfae21f150e29cc899ab6f9b1bd2c0f2ee4 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 7 Jan 2020 18:05:10 +0800 Subject: [PATCH 145/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3mongo=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 2552 +++++++++++++++++++++--------------------- src/db/Query.php | 978 ++++++++-------- 2 files changed, 1765 insertions(+), 1765 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 1f97e63..447b749 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -1,1270 +1,1282 @@ - -// +---------------------------------------------------------------------- -declare (strict_types = 1); - -namespace think\db; - -use think\Collection; -use think\db\exception\DataNotFoundException; -use think\db\exception\DbException as Exception; -use think\db\exception\ModelNotFoundException; -use think\helper\Str; -use think\Model; -use think\Paginator; - -/** - * 数据查询基础类 - */ -abstract class BaseQuery -{ - use concern\TimeFieldQuery; - use concern\AggregateQuery; - use concern\ModelRelationQuery; - use concern\ResultOperation; - use concern\Transaction; - use concern\WhereQuery; - - /** - * 当前数据库连接对象 - * @var Connection - */ - protected $connection; - - /** - * 当前数据表名称(不含前缀) - * @var string - */ - protected $name = ''; - - /** - * 当前数据表主键 - * @var string|array - */ - protected $pk; - - /** - * 当前数据表自增主键 - * @var string - */ - protected $autoinc; - - /** - * 当前数据表前缀 - * @var string - */ - protected $prefix = ''; - - /** - * 当前查询参数 - * @var array - */ - protected $options = []; - - /** - * 架构函数 - * @access public - * @param ConnectionInterface $connection 数据库连接对象 - */ - public function __construct(ConnectionInterface $connection) - { - $this->connection = $connection; - - $this->prefix = $this->connection->getConfig('prefix'); - } - - /** - * 利用__call方法实现一些特殊的Model方法 - * @access public - * @param string $method 方法名称 - * @param array $args 调用参数 - * @return mixed - * @throws Exception - */ - public function __call(string $method, array $args) - { - if (strtolower(substr($method, 0, 5)) == 'getby') { - // 根据某个字段获取记录 - $field = Str::snake(substr($method, 5)); - return $this->where($field, '=', $args[0])->find(); - } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { - // 根据某个字段获取记录的某个值 - $name = Str::snake(substr($method, 10)); - return $this->where($name, '=', $args[0])->value($args[1]); - } elseif (strtolower(substr($method, 0, 7)) == 'whereor') { - $name = Str::snake(substr($method, 7)); - array_unshift($args, $name); - return call_user_func_array([$this, 'whereOr'], $args); - } elseif (strtolower(substr($method, 0, 5)) == 'where') { - $name = Str::snake(substr($method, 5)); - array_unshift($args, $name); - return call_user_func_array([$this, 'where'], $args); - } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { - // 动态调用命名范围 - $method = 'scope' . $method; - array_unshift($args, $this); - - call_user_func_array([$this->model, $method], $args); - return $this; - } else { - throw new Exception('method not exist:' . static::class . '->' . $method); - } - } - - /** - * 创建一个新的查询对象 - * @access public - * @return BaseQuery - */ - public function newQuery(): BaseQuery - { - $query = new static($this->connection); - - if ($this->model) { - $query->model($this->model); - } - - if (isset($this->options['table'])) { - $query->table($this->options['table']); - } else { - $query->name($this->name); - } - - if (isset($this->options['json'])) { - $query->json($this->options['json'], $this->options['json_assoc']); - } - - if (isset($this->options['field_type'])) { - $query->setFieldType($this->options['field_type']); - } - - return $query; - } - - /** - * 获取当前的数据库Connection对象 - * @access public - * @return ConnectionInterface - */ - public function getConnection() - { - return $this->connection; - } - - /** - * 指定当前数据表名(不含前缀) - * @access public - * @param string $name 不含前缀的数据表名字 - * @return $this - */ - public function name(string $name) - { - $this->name = $name; - return $this; - } - - /** - * 获取当前的数据表名称 - * @access public - * @return string - */ - public function getName(): string - { - return $this->name ?: $this->model->getName(); - } - - /** - * 获取数据库的配置参数 - * @access public - * @param string $name 参数名称 - * @return mixed - */ - public function getConfig(string $name = '') - { - return $this->connection->getConfig($name); - } - - /** - * 得到当前或者指定名称的数据表 - * @access public - * @param string $name 不含前缀的数据表名字 - * @return mixed - */ - public function getTable(string $name = '') - { - if (empty($name) && isset($this->options['table'])) { - return $this->options['table']; - } - - $name = $name ?: $this->name; - - return $this->prefix . Str::snake($name); - } - - /** - * 设置字段类型信息 - * @access public - * @param array $type 字段类型信息 - * @return $this - */ - public function setFieldType(array $type) - { - $this->options['field_type'] = $type; - return $this; - } - - /** - * 获取最近一次查询的sql语句 - * @access public - * @return string - */ - public function getLastSql(): string - { - return $this->connection->getLastSql(); - } - - /** - * 获取返回或者影响的记录数 - * @access public - * @return integer - */ - public function getNumRows(): int - { - return $this->connection->getNumRows(); - } - - /** - * 获取最近插入的ID - * @access public - * @param string $sequence 自增序列名 - * @return mixed - */ - public function getLastInsID(string $sequence = null) - { - return $this->connection->getLastInsID($this, $sequence); - } - - /** - * 得到某个字段的值 - * @access public - * @param string $field 字段名 - * @param mixed $default 默认值 - * @return mixed - */ - public function value(string $field, $default = null) - { - return $this->connection->value($this, $field, $default); - } - - /** - * 得到某个列的数组 - * @access public - * @param string $field 字段名 多个字段用逗号分隔 - * @param string $key 索引 - * @return array - */ - public function column(string $field, string $key = ''): array - { - return $this->connection->column($this, $field, $key); - } - - /** - * 查询SQL组装 union - * @access public - * @param mixed $union UNION - * @param boolean $all 是否适用UNION ALL - * @return $this - */ - public function union($union, bool $all = false) - { - $this->options['union']['type'] = $all ? 'UNION ALL' : 'UNION'; - - if (is_array($union)) { - $this->options['union'] = array_merge($this->options['union'], $union); - } else { - $this->options['union'][] = $union; - } - - return $this; - } - - /** - * 查询SQL组装 union all - * @access public - * @param mixed $union UNION数据 - * @return $this - */ - public function unionAll($union) - { - return $this->union($union, true); - } - - /** - * 指定查询字段 - * @access public - * @param mixed $field 字段信息 - * @return $this - */ - public function field($field) - { - if (empty($field)) { - return $this; - } elseif ($field instanceof Raw) { - $this->options['field'][] = $field; - return $this; - } - - if (is_string($field)) { - if (preg_match('/[\<\'\"\(]/', $field)) { - return $this->fieldRaw($field); - } - - $field = array_map('trim', explode(',', $field)); - } - - if (true === $field) { - // 获取全部字段 - $fields = $this->getTableFields(); - $field = $fields ?: ['*']; - } - - if (isset($this->options['field'])) { - $field = array_merge((array) $this->options['field'], $field); - } - - $this->options['field'] = array_unique($field); - - return $this; - } - - /** - * 指定要排除的查询字段 - * @access public - * @param array|string $field 要排除的字段 - * @return $this - */ - public function withoutField($field) - { - if (empty($field)) { - return $this; - } - - if (is_string($field)) { - $field = array_map('trim', explode(',', $field)); - } - - // 字段排除 - $fields = $this->getTableFields(); - $field = $fields ? array_diff($fields, $field) : $field; - - if (isset($this->options['field'])) { - $field = array_merge((array) $this->options['field'], $field); - } - - $this->options['field'] = array_unique($field); - - return $this; - } - - /** - * 指定其它数据表的查询字段 - * @access public - * @param mixed $field 字段信息 - * @param string $tableName 数据表名 - * @param string $prefix 字段前缀 - * @param string $alias 别名前缀 - * @return $this - */ - public function tableField($field, string $tableName, string $prefix = '', string $alias = '') - { - if (empty($field)) { - return $this; - } - - if (is_string($field)) { - $field = array_map('trim', explode(',', $field)); - } - - if (true === $field) { - // 获取全部字段 - $fields = $this->getTableFields($tableName); - $field = $fields ?: ['*']; - } - - // 添加统一的前缀 - $prefix = $prefix ?: $tableName; - foreach ($field as $key => &$val) { - if (is_numeric($key) && $alias) { - $field[$prefix . '.' . $val] = $alias . $val; - unset($field[$key]); - } elseif (is_numeric($key)) { - $val = $prefix . '.' . $val; - } - } - - if (isset($this->options['field'])) { - $field = array_merge((array) $this->options['field'], $field); - } - - $this->options['field'] = array_unique($field); - - return $this; - } - - /** - * 设置数据 - * @access public - * @param array $data 数据 - * @return $this - */ - public function data(array $data) - { - $this->options['data'] = $data; - - return $this; - } - - /** - * 去除查询参数 - * @access public - * @param string $option 参数名 留空去除所有参数 - * @return $this - */ - public function removeOption(string $option = '') - { - if ('' === $option) { - $this->options = []; - $this->bind = []; - } elseif (isset($this->options[$option])) { - unset($this->options[$option]); - } - - return $this; - } - - /** - * 指定查询数量 - * @access public - * @param int $offset 起始位置 - * @param int $length 查询数量 - * @return $this - */ - public function limit(int $offset, int $length = null) - { - $this->options['limit'] = $offset . ($length ? ',' . $length : ''); - - return $this; - } - - /** - * 指定分页 - * @access public - * @param int $page 页数 - * @param int $listRows 每页数量 - * @return $this - */ - public function page(int $page, int $listRows = null) - { - $this->options['page'] = [$page, $listRows]; - - return $this; - } - - /** - * 指定当前操作的数据表 - * @access public - * @param mixed $table 表名 - * @return $this - */ - public function table($table) - { - if (is_string($table)) { - if (strpos($table, ')')) { - // 子查询 - } elseif (false === strpos($table, ',')) { - if (strpos($table, ' ')) { - [$item, $alias] = explode(' ', $table); - $table = []; - $this->alias([$item => $alias]); - $table[$item] = $alias; - } - } else { - $tables = explode(',', $table); - $table = []; - - foreach ($tables as $item) { - $item = trim($item); - if (strpos($item, ' ')) { - [$item, $alias] = explode(' ', $item); - $this->alias([$item => $alias]); - $table[$item] = $alias; - } else { - $table[] = $item; - } - } - } - } elseif (is_array($table)) { - $tables = $table; - $table = []; - - foreach ($tables as $key => $val) { - if (is_numeric($key)) { - $table[] = $val; - } else { - $this->alias([$key => $val]); - $table[$key] = $val; - } - } - } - - $this->options['table'] = $table; - - return $this; - } - - /** - * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc']) - * @access public - * @param string|array|Raw $field 排序字段 - * @param string $order 排序 - * @return $this - */ - public function order($field, string $order = '') - { - if (empty($field)) { - return $this; - } elseif ($field instanceof Raw) { - $this->options['order'][] = $field; - return $this; - } - - if (is_string($field)) { - if (!empty($this->options['via'])) { - $field = $this->options['via'] . '.' . $field; - } - if (strpos($field, ',')) { - $field = array_map('trim', explode(',', $field)); - } else { - $field = empty($order) ? $field : [$field => $order]; - } - } elseif (!empty($this->options['via'])) { - foreach ($field as $key => $val) { - if (is_numeric($key)) { - $field[$key] = $this->options['via'] . '.' . $val; - } else { - $field[$this->options['via'] . '.' . $key] = $val; - unset($field[$key]); - } - } - } - - if (!isset($this->options['order'])) { - $this->options['order'] = []; - } - - if (is_array($field)) { - $this->options['order'] = array_merge($this->options['order'], $field); - } else { - $this->options['order'][] = $field; - } - - return $this; - } - - /** - * 分页查询 - * @access public - * @param int|array $listRows 每页数量 数组表示配置参数 - * @param int|bool $simple 是否简洁模式或者总记录数 - * @return Paginator - * @throws Exception - */ - public function paginate($listRows = null, $simple = false): Paginator - { - if (is_int($simple)) { - $total = $simple; - $simple = false; - } - - $defaultConfig = [ - 'query' => [], //url额外参数 - 'fragment' => '', //url锚点 - 'var_page' => 'page', //分页变量 - 'list_rows' => 15, //每页数量 - ]; - - if (is_array($listRows)) { - $config = array_merge($defaultConfig, $listRows); - $listRows = intval($config['list_rows']); - } else { - $config = $defaultConfig; - $listRows = intval($listRows ?: $config['list_rows']); - } - - $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']); - - $page = $page < 1 ? 1 : $page; - - $config['path'] = $config['path'] ?? Paginator::getCurrentPath(); - - if (!isset($total) && !$simple) { - $options = $this->getOptions(); - - unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']); - - $bind = $this->bind; - $total = $this->count(); - $results = $this->options($options)->bind($bind)->page($page, $listRows)->select(); - } elseif ($simple) { - $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select(); - $total = null; - } else { - $results = $this->page($page, $listRows)->select(); - } - - $this->removeOption('limit'); - $this->removeOption('page'); - - return Paginator::make($results, $listRows, $page, $total, $simple, $config); - } - - /** - * 根据数字类型字段进行分页查询(大数据) - * @access public - * @param int|array $listRows 每页数量或者分页配置 - * @param string $key 分页索引键 - * @param string $sort 索引键排序 asc|desc - * @return Paginator - * @throws Exception - */ - public function paginateX($listRows = null, string $key = null, string $sort = null): Paginator - { - $defaultConfig = [ - 'query' => [], //url额外参数 - 'fragment' => '', //url锚点 - 'var_page' => 'page', //分页变量 - 'list_rows' => 15, //每页数量 - ]; - - $config = is_array($listRows) ? array_merge($defaultConfig, $listRows) : $defaultConfig; - $listRows = is_int($listRows) ? $listRows : (int) $config['list_rows']; - $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']); - $page = $page < 1 ? 1 : $page; - - $config['path'] = $config['path'] ?? Paginator::getCurrentPath(); - - $key = $key ?: $this->getPk(); - $options = $this->getOptions(); - - if (is_null($sort)) { - $order = $options['order'] ?? ''; - if (!empty($order)) { - $sort = $order[$key] ?? 'desc'; - } else { - $this->order($key, 'desc'); - $sort = 'desc'; - } - } else { - $this->order($key, $sort); - } - - $newOption = $options; - unset($newOption['field'], $newOption['page']); - - $data = $this->newQuery() - ->options($newOption) - ->field($key) - ->where(true) - ->order($key, $sort) - ->limit(1) - ->find(); - - $result = $data[$key]; - - if (is_numeric($result)) { - $lastId = 'asc' == $sort ? ($result - 1) + ($page - 1) * $listRows : ($result + 1) - ($page - 1) * $listRows; - } else { - throw new Exception('not support type'); - } - - $results = $this->when($lastId, function ($query) use ($key, $sort, $lastId) { - $query->where($key, 'asc' == $sort ? '>' : '<', $lastId); - }) - ->limit($listRows) - ->select(); - - $this->options($options); - - return Paginator::make($results, $listRows, $page, null, true, $config); - } - - /** - * 根据最后ID查询更多N个数据 - * @access public - * @param int $limit LIMIT - * @param int|string $lastId LastId - * @param string $key 分页索引键 默认为主键 - * @param string $sort 索引键排序 asc|desc - * @return array - * @throws Exception - */ - public function more(int $limit, $lastId = null, string $key = null, string $sort = null): array - { - $key = $key ?: $this->getPk(); - - if (is_null($sort)) { - $order = $this->getOptions('order'); - if (!empty($order)) { - $sort = $order[$key] ?? 'desc'; - } else { - $this->order($key, 'desc'); - $sort = 'desc'; - } - } else { - $this->order($key, $sort); - } - - $result = $this->when($lastId, function ($query) use ($key, $sort, $lastId) { - $query->where($key, 'asc' == $sort ? '>' : '<', $lastId); - })->limit($limit)->select(); - - $last = $result->last(); - - $result->first(); - - return [ - 'data' => $result, - 'lastId' => $last[$key], - ]; - } - - /** - * 查询缓存 - * @access public - * @param mixed $key 缓存key - * @param integer|\DateTime $expire 缓存有效期 - * @param string|array $tag 缓存标签 - * @return $this - */ - public function cache($key = true, $expire = null, $tag = null) - { - if (false === $key || !$this->getConnection()->getCache()) { - return $this; - } - - if ($key instanceof \DateTimeInterface || $key instanceof \DateInterval || (is_int($key) && is_null($expire))) { - $expire = $key; - $key = true; - } - - $this->options['cache'] = [$key, $expire, $tag]; - - return $this; - } - - /** - * 指定查询lock - * @access public - * @param bool|string $lock 是否lock - * @return $this - */ - public function lock($lock = false) - { - $this->options['lock'] = $lock; - - if ($lock) { - $this->options['master'] = true; - } - - return $this; - } - - /** - * 指定数据表别名 - * @access public - * @param array|string $alias 数据表别名 - * @return $this - */ - public function alias($alias) - { - if (is_array($alias)) { - $this->options['alias'] = $alias; - } else { - $table = $this->getTable(); - - $this->options['alias'][$table] = $alias; - } - - return $this; - } - - /** - * 设置从主服务器读取数据 - * @access public - * @param bool $readMaster 是否从主服务器读取 - * @return $this - */ - public function master(bool $readMaster = true) - { - $this->options['master'] = $readMaster; - return $this; - } - - /** - * 设置是否严格检查字段名 - * @access public - * @param bool $strict 是否严格检查字段 - * @return $this - */ - public function strict(bool $strict = true) - { - $this->options['strict'] = $strict; - return $this; - } - - /** - * 设置JSON字段信息 - * @access public - * @param array $json JSON字段 - * @param bool $assoc 是否取出数组 - * @return $this - */ - public function json(array $json = [], bool $assoc = false) - { - $this->options['json'] = $json; - $this->options['json_assoc'] = $assoc; - return $this; - } - - /** - * 指定数据表主键 - * @access public - * @param string|array $pk 主键 - * @return $this - */ - public function pk($pk) - { - $this->pk = $pk; - return $this; - } - - /** - * 查询参数批量赋值 - * @access protected - * @param array $options 表达式参数 - * @return $this - */ - protected function options(array $options) - { - $this->options = $options; - return $this; - } - - /** - * 获取当前的查询参数 - * @access public - * @param string $name 参数名 - * @return mixed - */ - public function getOptions(string $name = '') - { - if ('' === $name) { - return $this->options; - } - - return $this->options[$name] ?? null; - } - - /** - * 设置当前的查询参数 - * @access public - * @param string $option 参数名 - * @param mixed $value 参数值 - * @return $this - */ - public function setOption(string $option, $value) - { - $this->options[$option] = $value; - return $this; - } - - /** - * 设置当前字段添加的表别名 - * @access public - * @param string $via 临时表别名 - * @return $this - */ - public function via(string $via = '') - { - $this->options['via'] = $via; - - return $this; - } - - /** - * 保存记录 自动判断insert或者update - * @access public - * @param array $data 数据 - * @param bool $forceInsert 是否强制insert - * @return integer - */ - public function save(array $data = [], bool $forceInsert = false) - { - if ($forceInsert) { - return $this->insert($data); - } - - $this->options['data'] = array_merge($this->options['data'] ?? [], $data); - - if (!empty($this->options['where'])) { - $isUpdate = true; - } else { - $isUpdate = $this->parseUpdateData($this->options['data']); - } - - return $isUpdate ? $this->update() : $this->insert(); - } - - /** - * 插入记录 - * @access public - * @param array $data 数据 - * @param boolean $getLastInsID 返回自增主键 - * @return integer|string - */ - public function insert(array $data = [], bool $getLastInsID = false) - { - if (!empty($data)) { - $this->options['data'] = $data; - } - - return $this->connection->insert($this, $getLastInsID); - } - - /** - * 插入记录并获取自增ID - * @access public - * @param array $data 数据 - * @return integer|string - */ - public function insertGetId(array $data) - { - return $this->insert($data, true); - } - - /** - * 批量插入记录 - * @access public - * @param array $dataSet 数据集 - * @param integer $limit 每次写入数据限制 - * @return integer - */ - public function insertAll(array $dataSet = [], int $limit = 0): int - { - if (empty($dataSet)) { - $dataSet = $this->options['data'] ?? []; - } - - if (empty($limit) && !empty($this->options['limit']) && is_numeric($this->options['limit'])) { - $limit = (int) $this->options['limit']; - } - - return $this->connection->insertAll($this, $dataSet, $limit); - } - - /** - * 通过Select方式插入记录 - * @access public - * @param array $fields 要插入的数据表字段名 - * @param string $table 要插入的数据表名 - * @return integer - */ - public function selectInsert(array $fields, string $table): int - { - return $this->connection->selectInsert($this, $fields, $table); - } - - /** - * 更新记录 - * @access public - * @param mixed $data 数据 - * @return integer - * @throws Exception - */ - public function update(array $data = []): int - { - if (!empty($data)) { - $this->options['data'] = array_merge($this->options['data'] ?? [], $data); - } - - if (empty($this->options['where'])) { - $this->parseUpdateData($this->options['data']); - } - - if (empty($this->options['where']) && $this->model) { - $this->where($this->model->getWhere()); - } - - if (empty($this->options['where'])) { - // 如果没有任何更新条件则不执行 - throw new Exception('miss update condition'); - } - - return $this->connection->update($this); - } - - /** - * 删除记录 - * @access public - * @param mixed $data 表达式 true 表示强制删除 - * @return int - * @throws Exception - */ - public function delete($data = null): int - { - if (!is_null($data) && true !== $data) { - // AR模式分析主键条件 - $this->parsePkWhere($data); - } - - if (empty($this->options['where']) && $this->model) { - $this->where($this->model->getWhere()); - } - - if (true !== $data && empty($this->options['where'])) { - // 如果条件为空 不进行删除操作 除非设置 1=1 - throw new Exception('delete without condition'); - } - - if (!empty($this->options['soft_delete'])) { - // 软删除 - list($field, $condition) = $this->options['soft_delete']; - if ($condition) { - unset($this->options['soft_delete']); - $this->options['data'] = [$field => $condition]; - - return $this->connection->update($this); - } - } - - $this->options['data'] = $data; - - return $this->connection->delete($this); - } - - /** - * 查找记录 - * @access public - * @param mixed $data 数据 - * @return Collection - * @throws Exception - * @throws ModelNotFoundException - * @throws DataNotFoundException - */ - public function select($data = null): Collection - { - if (!is_null($data)) { - // 主键条件分析 - $this->parsePkWhere($data); - } - - $resultSet = $this->connection->select($this); - - // 返回结果处理 - if (!empty($this->options['fail']) && count($resultSet) == 0) { - $this->throwNotFound(); - } - - // 数据列表读取后的处理 - if (!empty($this->model)) { - // 生成模型对象 - $resultSet = $this->resultSetToModelCollection($resultSet); - } else { - $this->resultSet($resultSet); - } - - return $resultSet; - } - - /** - * 查找单条记录 - * @access public - * @param mixed $data 查询数据 - * @return array|Model|null - * @throws Exception - * @throws ModelNotFoundException - * @throws DataNotFoundException - */ - public function find($data = null) - { - if (!is_null($data)) { - // AR模式分析主键条件 - $this->parsePkWhere($data); - } - - if (empty($this->options['where']) && empty($this->options['order'])) { - $result = []; - } else { - $result = $this->connection->find($this); - } - - // 数据处理 - if (empty($result)) { - return $this->resultToEmpty(); - } - - if (!empty($this->model)) { - // 返回模型对象 - $this->resultToModel($result, $this->options); - } else { - $this->result($result); - } - - return $result; - } - - /** - * 分析表达式(可用于查询或者写入操作) - * @access public - * @return array - */ - public function parseOptions(): array - { - $options = $this->getOptions(); - - // 获取数据表 - if (empty($options['table'])) { - $options['table'] = $this->getTable(); - } - - if (!isset($options['where'])) { - $options['where'] = []; - } elseif (isset($options['view'])) { - // 视图查询条件处理 - $this->parseView($options); - } - - if (!isset($options['field'])) { - $options['field'] = '*'; - } - - foreach (['data', 'order', 'join', 'union'] as $name) { - if (!isset($options[$name])) { - $options[$name] = []; - } - } - - if (!isset($options['strict'])) { - $options['strict'] = $this->connection->getConfig('fields_strict'); - } - - foreach (['master', 'lock', 'fetch_sql', 'array', 'distinct', 'procedure'] as $name) { - if (!isset($options[$name])) { - $options[$name] = false; - } - } - - foreach (['group', 'having', 'limit', 'force', 'comment', 'partition', 'duplicate', 'extra'] as $name) { - if (!isset($options[$name])) { - $options[$name] = ''; - } - } - - if (isset($options['page'])) { - // 根据页数计算limit - [$page, $listRows] = $options['page']; - $page = $page > 0 ? $page : 1; - $listRows = $listRows ?: (is_numeric($options['limit']) ? $options['limit'] : 20); - $offset = $listRows * ($page - 1); - $options['limit'] = $offset . ',' . $listRows; - } - - $this->options = $options; - - return $options; - } - - /** - * 分析数据是否存在更新条件 - * @access public - * @param array $data 数据 - * @return bool - * @throws Exception - */ - public function parseUpdateData(&$data): bool - { - $pk = $this->getPk(); - $isUpdate = false; - // 如果存在主键数据 则自动作为更新条件 - if (is_string($pk) && isset($data[$pk])) { - $this->where($pk, '=', $data[$pk]); - $this->options['key'] = $data[$pk]; - unset($data[$pk]); - $isUpdate = true; - } elseif (is_array($pk)) { - foreach ($pk as $field) { - if (isset($data[$field])) { - $this->where($field, '=', $data[$field]); - $isUpdate = true; - } else { - // 如果缺少复合主键数据则不执行 - throw new Exception('miss complex primary data'); - } - unset($data[$field]); - } - } - - return $isUpdate; - } - - /** - * 把主键值转换为查询条件 支持复合主键 - * @access public - * @param array|string $data 主键数据 - * @return void - * @throws Exception - */ - public function parsePkWhere($data): void - { - $pk = $this->getPk(); - - if (is_string($pk)) { - // 获取数据表 - if (empty($this->options['table'])) { - $this->options['table'] = $this->getTable(); - } - - $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table']; - - if (!empty($this->options['alias'][$table])) { - $alias = $this->options['alias'][$table]; - } - - $key = isset($alias) ? $alias . '.' . $pk : $pk; - // 根据主键查询 - if (is_array($data)) { - $this->where($key, 'in', $data); - } else { - $this->where($key, '=', $data); - $this->options['key'] = $data; - } - } - } - - /** - * 获取模型的更新条件 - * @access protected - * @param array $options 查询参数 - */ - protected function getModelUpdateCondition(array $options) - { - return $options['where']['AND'] ?? null; - } -} + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db; + +use think\Collection; +use think\db\exception\DataNotFoundException; +use think\db\exception\DbException as Exception; +use think\db\exception\ModelNotFoundException; +use think\helper\Str; +use think\Model; +use think\Paginator; + +/** + * 数据查询基础类 + */ +abstract class BaseQuery +{ + use concern\TimeFieldQuery; + use concern\AggregateQuery; + use concern\ModelRelationQuery; + use concern\ResultOperation; + use concern\Transaction; + use concern\WhereQuery; + + /** + * 当前数据库连接对象 + * @var Connection + */ + protected $connection; + + /** + * 当前数据表名称(不含前缀) + * @var string + */ + protected $name = ''; + + /** + * 当前数据表主键 + * @var string|array + */ + protected $pk; + + /** + * 当前数据表自增主键 + * @var string + */ + protected $autoinc; + + /** + * 当前数据表前缀 + * @var string + */ + protected $prefix = ''; + + /** + * 当前查询参数 + * @var array + */ + protected $options = []; + + /** + * 架构函数 + * @access public + * @param ConnectionInterface $connection 数据库连接对象 + */ + public function __construct(ConnectionInterface $connection) + { + $this->connection = $connection; + + $this->prefix = $this->connection->getConfig('prefix'); + } + + /** + * 利用__call方法实现一些特殊的Model方法 + * @access public + * @param string $method 方法名称 + * @param array $args 调用参数 + * @return mixed + * @throws Exception + */ + public function __call(string $method, array $args) + { + if (strtolower(substr($method, 0, 5)) == 'getby') { + // 根据某个字段获取记录 + $field = Str::snake(substr($method, 5)); + return $this->where($field, '=', $args[0])->find(); + } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { + // 根据某个字段获取记录的某个值 + $name = Str::snake(substr($method, 10)); + return $this->where($name, '=', $args[0])->value($args[1]); + } elseif (strtolower(substr($method, 0, 7)) == 'whereor') { + $name = Str::snake(substr($method, 7)); + array_unshift($args, $name); + return call_user_func_array([$this, 'whereOr'], $args); + } elseif (strtolower(substr($method, 0, 5)) == 'where') { + $name = Str::snake(substr($method, 5)); + array_unshift($args, $name); + return call_user_func_array([$this, 'where'], $args); + } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { + // 动态调用命名范围 + $method = 'scope' . $method; + array_unshift($args, $this); + + call_user_func_array([$this->model, $method], $args); + return $this; + } else { + throw new Exception('method not exist:' . static::class . '->' . $method); + } + } + + /** + * 创建一个新的查询对象 + * @access public + * @return BaseQuery + */ + public function newQuery(): BaseQuery + { + $query = new static($this->connection); + + if ($this->model) { + $query->model($this->model); + } + + if (isset($this->options['table'])) { + $query->table($this->options['table']); + } else { + $query->name($this->name); + } + + if (isset($this->options['json'])) { + $query->json($this->options['json'], $this->options['json_assoc']); + } + + if (isset($this->options['field_type'])) { + $query->setFieldType($this->options['field_type']); + } + + return $query; + } + + /** + * 获取当前的数据库Connection对象 + * @access public + * @return ConnectionInterface + */ + public function getConnection() + { + return $this->connection; + } + + /** + * 指定当前数据表名(不含前缀) + * @access public + * @param string $name 不含前缀的数据表名字 + * @return $this + */ + public function name(string $name) + { + $this->name = $name; + return $this; + } + + /** + * 获取当前的数据表名称 + * @access public + * @return string + */ + public function getName(): string + { + return $this->name ?: $this->model->getName(); + } + + /** + * 获取数据库的配置参数 + * @access public + * @param string $name 参数名称 + * @return mixed + */ + public function getConfig(string $name = '') + { + return $this->connection->getConfig($name); + } + + /** + * 得到当前或者指定名称的数据表 + * @access public + * @param string $name 不含前缀的数据表名字 + * @return mixed + */ + public function getTable(string $name = '') + { + if (empty($name) && isset($this->options['table'])) { + return $this->options['table']; + } + + $name = $name ?: $this->name; + + return $this->prefix . Str::snake($name); + } + + /** + * 设置字段类型信息 + * @access public + * @param array $type 字段类型信息 + * @return $this + */ + public function setFieldType(array $type) + { + $this->options['field_type'] = $type; + return $this; + } + + /** + * 获取最近一次查询的sql语句 + * @access public + * @return string + */ + public function getLastSql(): string + { + return $this->connection->getLastSql(); + } + + /** + * 获取返回或者影响的记录数 + * @access public + * @return integer + */ + public function getNumRows(): int + { + return $this->connection->getNumRows(); + } + + /** + * 获取最近插入的ID + * @access public + * @param string $sequence 自增序列名 + * @return mixed + */ + public function getLastInsID(string $sequence = null) + { + return $this->connection->getLastInsID($this, $sequence); + } + + /** + * 得到某个字段的值 + * @access public + * @param string $field 字段名 + * @param mixed $default 默认值 + * @return mixed + */ + public function value(string $field, $default = null) + { + return $this->connection->value($this, $field, $default); + } + + /** + * 得到某个列的数组 + * @access public + * @param string $field 字段名 多个字段用逗号分隔 + * @param string $key 索引 + * @return array + */ + public function column(string $field, string $key = ''): array + { + return $this->connection->column($this, $field, $key); + } + + /** + * 查询SQL组装 union + * @access public + * @param mixed $union UNION + * @param boolean $all 是否适用UNION ALL + * @return $this + */ + public function union($union, bool $all = false) + { + $this->options['union']['type'] = $all ? 'UNION ALL' : 'UNION'; + + if (is_array($union)) { + $this->options['union'] = array_merge($this->options['union'], $union); + } else { + $this->options['union'][] = $union; + } + + return $this; + } + + /** + * 查询SQL组装 union all + * @access public + * @param mixed $union UNION数据 + * @return $this + */ + public function unionAll($union) + { + return $this->union($union, true); + } + + /** + * 指定查询字段 + * @access public + * @param mixed $field 字段信息 + * @return $this + */ + public function field($field) + { + if (empty($field)) { + return $this; + } elseif ($field instanceof Raw) { + $this->options['field'][] = $field; + return $this; + } + + if (is_string($field)) { + if (preg_match('/[\<\'\"\(]/', $field)) { + return $this->fieldRaw($field); + } + + $field = array_map('trim', explode(',', $field)); + } + + if (true === $field) { + // 获取全部字段 + $fields = $this->getTableFields(); + $field = $fields ?: ['*']; + } + + if (isset($this->options['field'])) { + $field = array_merge((array) $this->options['field'], $field); + } + + $this->options['field'] = array_unique($field); + + return $this; + } + + /** + * 指定要排除的查询字段 + * @access public + * @param array|string $field 要排除的字段 + * @return $this + */ + public function withoutField($field) + { + if (empty($field)) { + return $this; + } + + if (is_string($field)) { + $field = array_map('trim', explode(',', $field)); + } + + // 字段排除 + $fields = $this->getTableFields(); + $field = $fields ? array_diff($fields, $field) : $field; + + if (isset($this->options['field'])) { + $field = array_merge((array) $this->options['field'], $field); + } + + $this->options['field'] = array_unique($field); + + return $this; + } + + /** + * 指定其它数据表的查询字段 + * @access public + * @param mixed $field 字段信息 + * @param string $tableName 数据表名 + * @param string $prefix 字段前缀 + * @param string $alias 别名前缀 + * @return $this + */ + public function tableField($field, string $tableName, string $prefix = '', string $alias = '') + { + if (empty($field)) { + return $this; + } + + if (is_string($field)) { + $field = array_map('trim', explode(',', $field)); + } + + if (true === $field) { + // 获取全部字段 + $fields = $this->getTableFields($tableName); + $field = $fields ?: ['*']; + } + + // 添加统一的前缀 + $prefix = $prefix ?: $tableName; + foreach ($field as $key => &$val) { + if (is_numeric($key) && $alias) { + $field[$prefix . '.' . $val] = $alias . $val; + unset($field[$key]); + } elseif (is_numeric($key)) { + $val = $prefix . '.' . $val; + } + } + + if (isset($this->options['field'])) { + $field = array_merge((array) $this->options['field'], $field); + } + + $this->options['field'] = array_unique($field); + + return $this; + } + + /** + * 设置数据 + * @access public + * @param array $data 数据 + * @return $this + */ + public function data(array $data) + { + $this->options['data'] = $data; + + return $this; + } + + /** + * 去除查询参数 + * @access public + * @param string $option 参数名 留空去除所有参数 + * @return $this + */ + public function removeOption(string $option = '') + { + if ('' === $option) { + $this->options = []; + $this->bind = []; + } elseif (isset($this->options[$option])) { + unset($this->options[$option]); + } + + return $this; + } + + /** + * 指定查询数量 + * @access public + * @param int $offset 起始位置 + * @param int $length 查询数量 + * @return $this + */ + public function limit(int $offset, int $length = null) + { + $this->options['limit'] = $offset . ($length ? ',' . $length : ''); + + return $this; + } + + /** + * 指定分页 + * @access public + * @param int $page 页数 + * @param int $listRows 每页数量 + * @return $this + */ + public function page(int $page, int $listRows = null) + { + $this->options['page'] = [$page, $listRows]; + + return $this; + } + + /** + * 指定当前操作的数据表 + * @access public + * @param mixed $table 表名 + * @return $this + */ + public function table($table) + { + if (is_string($table)) { + if (strpos($table, ')')) { + // 子查询 + } elseif (false === strpos($table, ',')) { + if (strpos($table, ' ')) { + [$item, $alias] = explode(' ', $table); + $table = []; + $this->alias([$item => $alias]); + $table[$item] = $alias; + } + } else { + $tables = explode(',', $table); + $table = []; + + foreach ($tables as $item) { + $item = trim($item); + if (strpos($item, ' ')) { + [$item, $alias] = explode(' ', $item); + $this->alias([$item => $alias]); + $table[$item] = $alias; + } else { + $table[] = $item; + } + } + } + } elseif (is_array($table)) { + $tables = $table; + $table = []; + + foreach ($tables as $key => $val) { + if (is_numeric($key)) { + $table[] = $val; + } else { + $this->alias([$key => $val]); + $table[$key] = $val; + } + } + } + + $this->options['table'] = $table; + + return $this; + } + + /** + * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc']) + * @access public + * @param string|array|Raw $field 排序字段 + * @param string $order 排序 + * @return $this + */ + public function order($field, string $order = '') + { + if (empty($field)) { + return $this; + } elseif ($field instanceof Raw) { + $this->options['order'][] = $field; + return $this; + } + + if (is_string($field)) { + if (!empty($this->options['via'])) { + $field = $this->options['via'] . '.' . $field; + } + if (strpos($field, ',')) { + $field = array_map('trim', explode(',', $field)); + } else { + $field = empty($order) ? $field : [$field => $order]; + } + } elseif (!empty($this->options['via'])) { + foreach ($field as $key => $val) { + if (is_numeric($key)) { + $field[$key] = $this->options['via'] . '.' . $val; + } else { + $field[$this->options['via'] . '.' . $key] = $val; + unset($field[$key]); + } + } + } + + if (!isset($this->options['order'])) { + $this->options['order'] = []; + } + + if (is_array($field)) { + $this->options['order'] = array_merge($this->options['order'], $field); + } else { + $this->options['order'][] = $field; + } + + return $this; + } + + /** + * 分页查询 + * @access public + * @param int|array $listRows 每页数量 数组表示配置参数 + * @param int|bool $simple 是否简洁模式或者总记录数 + * @return Paginator + * @throws Exception + */ + public function paginate($listRows = null, $simple = false): Paginator + { + if (is_int($simple)) { + $total = $simple; + $simple = false; + } + + $defaultConfig = [ + 'query' => [], //url额外参数 + 'fragment' => '', //url锚点 + 'var_page' => 'page', //分页变量 + 'list_rows' => 15, //每页数量 + ]; + + if (is_array($listRows)) { + $config = array_merge($defaultConfig, $listRows); + $listRows = intval($config['list_rows']); + } else { + $config = $defaultConfig; + $listRows = intval($listRows ?: $config['list_rows']); + } + + $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']); + + $page = $page < 1 ? 1 : $page; + + $config['path'] = $config['path'] ?? Paginator::getCurrentPath(); + + if (!isset($total) && !$simple) { + $options = $this->getOptions(); + + unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']); + + $bind = $this->bind; + $total = $this->count(); + $results = $this->options($options)->bind($bind)->page($page, $listRows)->select(); + } elseif ($simple) { + $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select(); + $total = null; + } else { + $results = $this->page($page, $listRows)->select(); + } + + $this->removeOption('limit'); + $this->removeOption('page'); + + return Paginator::make($results, $listRows, $page, $total, $simple, $config); + } + + /** + * 根据数字类型字段进行分页查询(大数据) + * @access public + * @param int|array $listRows 每页数量或者分页配置 + * @param string $key 分页索引键 + * @param string $sort 索引键排序 asc|desc + * @return Paginator + * @throws Exception + */ + public function paginateX($listRows = null, string $key = null, string $sort = null): Paginator + { + $defaultConfig = [ + 'query' => [], //url额外参数 + 'fragment' => '', //url锚点 + 'var_page' => 'page', //分页变量 + 'list_rows' => 15, //每页数量 + ]; + + $config = is_array($listRows) ? array_merge($defaultConfig, $listRows) : $defaultConfig; + $listRows = is_int($listRows) ? $listRows : (int) $config['list_rows']; + $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']); + $page = $page < 1 ? 1 : $page; + + $config['path'] = $config['path'] ?? Paginator::getCurrentPath(); + + $key = $key ?: $this->getPk(); + $options = $this->getOptions(); + + if (is_null($sort)) { + $order = $options['order'] ?? ''; + if (!empty($order)) { + $sort = $order[$key] ?? 'desc'; + } else { + $this->order($key, 'desc'); + $sort = 'desc'; + } + } else { + $this->order($key, $sort); + } + + $newOption = $options; + unset($newOption['field'], $newOption['page']); + + $data = $this->newQuery() + ->options($newOption) + ->field($key) + ->where(true) + ->order($key, $sort) + ->limit(1) + ->find(); + + $result = $data[$key]; + + if (is_numeric($result)) { + $lastId = 'asc' == $sort ? ($result - 1) + ($page - 1) * $listRows : ($result + 1) - ($page - 1) * $listRows; + } else { + throw new Exception('not support type'); + } + + $results = $this->when($lastId, function ($query) use ($key, $sort, $lastId) { + $query->where($key, 'asc' == $sort ? '>' : '<', $lastId); + }) + ->limit($listRows) + ->select(); + + $this->options($options); + + return Paginator::make($results, $listRows, $page, null, true, $config); + } + + /** + * 根据最后ID查询更多N个数据 + * @access public + * @param int $limit LIMIT + * @param int|string $lastId LastId + * @param string $key 分页索引键 默认为主键 + * @param string $sort 索引键排序 asc|desc + * @return array + * @throws Exception + */ + public function more(int $limit, $lastId = null, string $key = null, string $sort = null): array + { + $key = $key ?: $this->getPk(); + + if (is_null($sort)) { + $order = $this->getOptions('order'); + if (!empty($order)) { + $sort = $order[$key] ?? 'desc'; + } else { + $this->order($key, 'desc'); + $sort = 'desc'; + } + } else { + $this->order($key, $sort); + } + + $result = $this->when($lastId, function ($query) use ($key, $sort, $lastId) { + $query->where($key, 'asc' == $sort ? '>' : '<', $lastId); + })->limit($limit)->select(); + + $last = $result->last(); + + $result->first(); + + return [ + 'data' => $result, + 'lastId' => $last[$key], + ]; + } + + /** + * 查询缓存 + * @access public + * @param mixed $key 缓存key + * @param integer|\DateTime $expire 缓存有效期 + * @param string|array $tag 缓存标签 + * @return $this + */ + public function cache($key = true, $expire = null, $tag = null) + { + if (false === $key || !$this->getConnection()->getCache()) { + return $this; + } + + if ($key instanceof \DateTimeInterface || $key instanceof \DateInterval || (is_int($key) && is_null($expire))) { + $expire = $key; + $key = true; + } + + $this->options['cache'] = [$key, $expire, $tag]; + + return $this; + } + + /** + * 指定查询lock + * @access public + * @param bool|string $lock 是否lock + * @return $this + */ + public function lock($lock = false) + { + $this->options['lock'] = $lock; + + if ($lock) { + $this->options['master'] = true; + } + + return $this; + } + + /** + * 指定数据表别名 + * @access public + * @param array|string $alias 数据表别名 + * @return $this + */ + public function alias($alias) + { + if (is_array($alias)) { + $this->options['alias'] = $alias; + } else { + $table = $this->getTable(); + + $this->options['alias'][$table] = $alias; + } + + return $this; + } + + /** + * 设置从主服务器读取数据 + * @access public + * @param bool $readMaster 是否从主服务器读取 + * @return $this + */ + public function master(bool $readMaster = true) + { + $this->options['master'] = $readMaster; + return $this; + } + + /** + * 设置是否严格检查字段名 + * @access public + * @param bool $strict 是否严格检查字段 + * @return $this + */ + public function strict(bool $strict = true) + { + $this->options['strict'] = $strict; + return $this; + } + + /** + * 设置自增序列名 + * @access public + * @param string $sequence 自增序列名 + * @return $this + */ + public function sequence(string $sequence = null) + { + $this->options['sequence'] = $sequence; + return $this; + } + + /** + * 设置JSON字段信息 + * @access public + * @param array $json JSON字段 + * @param bool $assoc 是否取出数组 + * @return $this + */ + public function json(array $json = [], bool $assoc = false) + { + $this->options['json'] = $json; + $this->options['json_assoc'] = $assoc; + return $this; + } + + /** + * 指定数据表主键 + * @access public + * @param string|array $pk 主键 + * @return $this + */ + public function pk($pk) + { + $this->pk = $pk; + return $this; + } + + /** + * 查询参数批量赋值 + * @access protected + * @param array $options 表达式参数 + * @return $this + */ + protected function options(array $options) + { + $this->options = $options; + return $this; + } + + /** + * 获取当前的查询参数 + * @access public + * @param string $name 参数名 + * @return mixed + */ + public function getOptions(string $name = '') + { + if ('' === $name) { + return $this->options; + } + + return $this->options[$name] ?? null; + } + + /** + * 设置当前的查询参数 + * @access public + * @param string $option 参数名 + * @param mixed $value 参数值 + * @return $this + */ + public function setOption(string $option, $value) + { + $this->options[$option] = $value; + return $this; + } + + /** + * 设置当前字段添加的表别名 + * @access public + * @param string $via 临时表别名 + * @return $this + */ + public function via(string $via = '') + { + $this->options['via'] = $via; + + return $this; + } + + /** + * 保存记录 自动判断insert或者update + * @access public + * @param array $data 数据 + * @param bool $forceInsert 是否强制insert + * @return integer + */ + public function save(array $data = [], bool $forceInsert = false) + { + if ($forceInsert) { + return $this->insert($data); + } + + $this->options['data'] = array_merge($this->options['data'] ?? [], $data); + + if (!empty($this->options['where'])) { + $isUpdate = true; + } else { + $isUpdate = $this->parseUpdateData($this->options['data']); + } + + return $isUpdate ? $this->update() : $this->insert(); + } + + /** + * 插入记录 + * @access public + * @param array $data 数据 + * @param boolean $getLastInsID 返回自增主键 + * @return integer|string + */ + public function insert(array $data = [], bool $getLastInsID = false) + { + if (!empty($data)) { + $this->options['data'] = $data; + } + + return $this->connection->insert($this, $getLastInsID); + } + + /** + * 插入记录并获取自增ID + * @access public + * @param array $data 数据 + * @return integer|string + */ + public function insertGetId(array $data) + { + return $this->insert($data, true); + } + + /** + * 批量插入记录 + * @access public + * @param array $dataSet 数据集 + * @param integer $limit 每次写入数据限制 + * @return integer + */ + public function insertAll(array $dataSet = [], int $limit = 0): int + { + if (empty($dataSet)) { + $dataSet = $this->options['data'] ?? []; + } + + if (empty($limit) && !empty($this->options['limit']) && is_numeric($this->options['limit'])) { + $limit = (int) $this->options['limit']; + } + + return $this->connection->insertAll($this, $dataSet, $limit); + } + + /** + * 通过Select方式插入记录 + * @access public + * @param array $fields 要插入的数据表字段名 + * @param string $table 要插入的数据表名 + * @return integer + */ + public function selectInsert(array $fields, string $table): int + { + return $this->connection->selectInsert($this, $fields, $table); + } + + /** + * 更新记录 + * @access public + * @param mixed $data 数据 + * @return integer + * @throws Exception + */ + public function update(array $data = []): int + { + if (!empty($data)) { + $this->options['data'] = array_merge($this->options['data'] ?? [], $data); + } + + if (empty($this->options['where'])) { + $this->parseUpdateData($this->options['data']); + } + + if (empty($this->options['where']) && $this->model) { + $this->where($this->model->getWhere()); + } + + if (empty($this->options['where'])) { + // 如果没有任何更新条件则不执行 + throw new Exception('miss update condition'); + } + + return $this->connection->update($this); + } + + /** + * 删除记录 + * @access public + * @param mixed $data 表达式 true 表示强制删除 + * @return int + * @throws Exception + */ + public function delete($data = null): int + { + if (!is_null($data) && true !== $data) { + // AR模式分析主键条件 + $this->parsePkWhere($data); + } + + if (empty($this->options['where']) && $this->model) { + $this->where($this->model->getWhere()); + } + + if (true !== $data && empty($this->options['where'])) { + // 如果条件为空 不进行删除操作 除非设置 1=1 + throw new Exception('delete without condition'); + } + + if (!empty($this->options['soft_delete'])) { + // 软删除 + list($field, $condition) = $this->options['soft_delete']; + if ($condition) { + unset($this->options['soft_delete']); + $this->options['data'] = [$field => $condition]; + + return $this->connection->update($this); + } + } + + $this->options['data'] = $data; + + return $this->connection->delete($this); + } + + /** + * 查找记录 + * @access public + * @param mixed $data 数据 + * @return Collection + * @throws Exception + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function select($data = null): Collection + { + if (!is_null($data)) { + // 主键条件分析 + $this->parsePkWhere($data); + } + + $resultSet = $this->connection->select($this); + + // 返回结果处理 + if (!empty($this->options['fail']) && count($resultSet) == 0) { + $this->throwNotFound(); + } + + // 数据列表读取后的处理 + if (!empty($this->model)) { + // 生成模型对象 + $resultSet = $this->resultSetToModelCollection($resultSet); + } else { + $this->resultSet($resultSet); + } + + return $resultSet; + } + + /** + * 查找单条记录 + * @access public + * @param mixed $data 查询数据 + * @return array|Model|null + * @throws Exception + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function find($data = null) + { + if (!is_null($data)) { + // AR模式分析主键条件 + $this->parsePkWhere($data); + } + + if (empty($this->options['where']) && empty($this->options['order'])) { + $result = []; + } else { + $result = $this->connection->find($this); + } + + // 数据处理 + if (empty($result)) { + return $this->resultToEmpty(); + } + + if (!empty($this->model)) { + // 返回模型对象 + $this->resultToModel($result, $this->options); + } else { + $this->result($result); + } + + return $result; + } + + /** + * 分析表达式(可用于查询或者写入操作) + * @access public + * @return array + */ + public function parseOptions(): array + { + $options = $this->getOptions(); + + // 获取数据表 + if (empty($options['table'])) { + $options['table'] = $this->getTable(); + } + + if (!isset($options['where'])) { + $options['where'] = []; + } elseif (isset($options['view'])) { + // 视图查询条件处理 + $this->parseView($options); + } + + if (!isset($options['field'])) { + $options['field'] = '*'; + } + + foreach (['data', 'order', 'join', 'union'] as $name) { + if (!isset($options[$name])) { + $options[$name] = []; + } + } + + if (!isset($options['strict'])) { + $options['strict'] = $this->connection->getConfig('fields_strict'); + } + + foreach (['master', 'lock', 'fetch_sql', 'array', 'distinct', 'procedure'] as $name) { + if (!isset($options[$name])) { + $options[$name] = false; + } + } + + foreach (['group', 'having', 'limit', 'force', 'comment', 'partition', 'duplicate', 'extra'] as $name) { + if (!isset($options[$name])) { + $options[$name] = ''; + } + } + + if (isset($options['page'])) { + // 根据页数计算limit + [$page, $listRows] = $options['page']; + $page = $page > 0 ? $page : 1; + $listRows = $listRows ?: (is_numeric($options['limit']) ? $options['limit'] : 20); + $offset = $listRows * ($page - 1); + $options['limit'] = $offset . ',' . $listRows; + } + + $this->options = $options; + + return $options; + } + + /** + * 分析数据是否存在更新条件 + * @access public + * @param array $data 数据 + * @return bool + * @throws Exception + */ + public function parseUpdateData(&$data): bool + { + $pk = $this->getPk(); + $isUpdate = false; + // 如果存在主键数据 则自动作为更新条件 + if (is_string($pk) && isset($data[$pk])) { + $this->where($pk, '=', $data[$pk]); + $this->options['key'] = $data[$pk]; + unset($data[$pk]); + $isUpdate = true; + } elseif (is_array($pk)) { + foreach ($pk as $field) { + if (isset($data[$field])) { + $this->where($field, '=', $data[$field]); + $isUpdate = true; + } else { + // 如果缺少复合主键数据则不执行 + throw new Exception('miss complex primary data'); + } + unset($data[$field]); + } + } + + return $isUpdate; + } + + /** + * 把主键值转换为查询条件 支持复合主键 + * @access public + * @param array|string $data 主键数据 + * @return void + * @throws Exception + */ + public function parsePkWhere($data): void + { + $pk = $this->getPk(); + + if (is_string($pk)) { + // 获取数据表 + if (empty($this->options['table'])) { + $this->options['table'] = $this->getTable(); + } + + $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table']; + + if (!empty($this->options['alias'][$table])) { + $alias = $this->options['alias'][$table]; + } + + $key = isset($alias) ? $alias . '.' . $pk : $pk; + // 根据主键查询 + if (is_array($data)) { + $this->where($key, 'in', $data); + } else { + $this->where($key, '=', $data); + $this->options['key'] = $data; + } + } + } + + /** + * 获取模型的更新条件 + * @access protected + * @param array $options 查询参数 + */ + protected function getModelUpdateCondition(array $options) + { + return $options['where']['AND'] ?? null; + } +} diff --git a/src/db/Query.php b/src/db/Query.php index 4fc9a99..cb0c4a3 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -1,495 +1,483 @@ - -// +---------------------------------------------------------------------- -declare (strict_types = 1); - -namespace think\db; - -use PDOStatement; -use think\helper\Str; - -/** - * PDO数据查询类 - */ -class Query extends BaseQuery -{ - use concern\JoinAndViewQuery; - use concern\ParamsBind; - use concern\TableFieldInfo; - - /** - * 表达式方式指定Field排序 - * @access public - * @param string $field 排序字段 - * @param array $bind 参数绑定 - * @return $this - */ - public function orderRaw(string $field, array $bind = []) - { - if (!empty($bind)) { - $this->bindParams($field, $bind); - } - - $this->options['order'][] = new Raw($field); - - return $this; - } - - /** - * 表达式方式指定查询字段 - * @access public - * @param string $field 字段名 - * @return $this - */ - public function fieldRaw(string $field) - { - $this->options['field'][] = new Raw($field); - - return $this; - } - - /** - * 指定Field排序 orderField('id',[1,2,3],'desc') - * @access public - * @param string $field 排序字段 - * @param array $values 排序值 - * @param string $order 排序 desc/asc - * @return $this - */ - public function orderField(string $field, array $values, string $order = '') - { - if (!empty($values)) { - $values['sort'] = $order; - - $this->options['order'][$field] = $values; - } - - return $this; - } - - /** - * 随机排序 - * @access public - * @return $this - */ - public function orderRand() - { - $this->options['order'][] = '[rand]'; - return $this; - } - - /** - * 使用表达式设置数据 - * @access public - * @param string $field 字段名 - * @param string $value 字段值 - * @return $this - */ - public function exp(string $field, string $value) - { - $this->options['data'][$field] = new Raw($value); - return $this; - } - - /** - * 表达式方式指定当前操作的数据表 - * @access public - * @param mixed $table 表名 - * @return $this - */ - public function tableRaw(string $table) - { - $this->options['table'] = new Raw($table); - - return $this; - } - - /** - * 执行查询 返回数据集 - * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @return array - * @throws BindParamException - * @throws PDOException - */ - public function query(string $sql, array $bind = []): array - { - return $this->connection->query($this, $sql, $bind); - } - - /** - * 执行语句 - * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @return int - * @throws BindParamException - * @throws PDOException - */ - public function execute(string $sql, array $bind = []): int - { - return $this->connection->execute($this, $sql, $bind, true); - } - - /** - * 获取执行的SQL语句而不进行实际的查询 - * @access public - * @param bool $fetch 是否返回sql - * @return $this|Fetch - */ - public function fetchSql(bool $fetch = true) - { - $this->options['fetch_sql'] = $fetch; - - if ($fetch) { - return new Fetch($this); - } - - return $this; - } - - /** - * 批处理执行SQL语句 - * 批处理的指令都认为是execute操作 - * @access public - * @param array $sql SQL批处理指令 - * @return bool - */ - public function batchQuery(array $sql = []): bool - { - return $this->connection->batchQuery($this, $sql); - } - - /** - * USING支持 用于多表删除 - * @access public - * @param mixed $using USING - * @return $this - */ - public function using($using) - { - $this->options['using'] = $using; - return $this; - } - - /** - * 存储过程调用 - * @access public - * @param bool $procedure 是否为存储过程查询 - * @return $this - */ - public function procedure(bool $procedure = true) - { - $this->options['procedure'] = $procedure; - return $this; - } - - /** - * 指定group查询 - * @access public - * @param string|array $group GROUP - * @return $this - */ - public function group($group) - { - $this->options['group'] = $group; - return $this; - } - - /** - * 指定having查询 - * @access public - * @param string $having having - * @return $this - */ - public function having(string $having) - { - $this->options['having'] = $having; - return $this; - } - - /** - * 指定distinct查询 - * @access public - * @param bool $distinct 是否唯一 - * @return $this - */ - public function distinct(bool $distinct = true) - { - $this->options['distinct'] = $distinct; - return $this; - } - - /** - * 设置自增序列名 - * @access public - * @param string $sequence 自增序列名 - * @return $this - */ - public function sequence(string $sequence = null) - { - $this->options['sequence'] = $sequence; - return $this; - } - - /** - * 指定强制索引 - * @access public - * @param string $force 索引名称 - * @return $this - */ - public function force(string $force) - { - $this->options['force'] = $force; - return $this; - } - - /** - * 查询注释 - * @access public - * @param string $comment 注释 - * @return $this - */ - public function comment(string $comment) - { - $this->options['comment'] = $comment; - return $this; - } - - /** - * 设置是否REPLACE - * @access public - * @param bool $replace 是否使用REPLACE写入数据 - * @return $this - */ - public function replace(bool $replace = true) - { - $this->options['replace'] = $replace; - return $this; - } - - /** - * 设置当前查询所在的分区 - * @access public - * @param string|array $partition 分区名称 - * @return $this - */ - public function partition($partition) - { - $this->options['partition'] = $partition; - return $this; - } - - /** - * 设置DUPLICATE - * @access public - * @param array|string|Raw $duplicate DUPLICATE信息 - * @return $this - */ - public function duplicate($duplicate) - { - $this->options['duplicate'] = $duplicate; - return $this; - } - - /** - * 设置查询的额外参数 - * @access public - * @param string $extra 额外信息 - * @return $this - */ - public function extra(string $extra) - { - $this->options['extra'] = $extra; - return $this; - } - - /** - * 创建子查询SQL - * @access public - * @param bool $sub 是否添加括号 - * @return string - * @throws Exception - */ - public function buildSql(bool $sub = true): string - { - return $sub ? '( ' . $this->fetchSql()->select() . ' )' : $this->fetchSql()->select(); - } - - /** - * 获取当前数据表的主键 - * @access public - * @return string|array - */ - public function getPk() - { - if (empty($this->pk)) { - $this->pk = $this->connection->getPk($this->getTable()); - } - - return $this->pk; - } - - /** - * 指定数据表自增主键 - * @access public - * @param string $autoinc 自增键 - * @return $this - */ - public function autoinc(string $autoinc) - { - $this->autoinc = $autoinc; - return $this; - } - - /** - * 获取当前数据表的自增主键 - * @access public - * @return string|null - */ - public function getAutoInc() - { - $tableName = $this->getTable(); - - if (empty($this->autoinc) && $tableName) { - $this->autoinc = $this->connection->getAutoInc($tableName); - } - - return $this->autoinc; - } - - /** - * 字段值增长 - * @access public - * @param string $field 字段名 - * @param float $step 增长值 - * @return $this - */ - public function inc(string $field, float $step = 1) - { - $this->options['data'][$field] = ['INC', $step]; - - return $this; - } - - /** - * 字段值减少 - * @access public - * @param string $field 字段名 - * @param float $step 增长值 - * @return $this - */ - public function dec(string $field, float $step = 1) - { - $this->options['data'][$field] = ['DEC', $step]; - return $this; - } - - /** - * 获取当前的查询标识 - * @access public - * @param mixed $data 要序列化的数据 - * @return string - */ - public function getQueryGuid($data = null): string - { - return md5($this->getConfig('database') . serialize(var_export($data ?: $this->options, true)) . serialize($this->getBind(false))); - } - - /** - * 执行查询但只返回PDOStatement对象 - * @access public - * @return PDOStatement - */ - public function getPdo(): PDOStatement - { - return $this->connection->pdo($this); - } - - /** - * 使用游标查找记录 - * @access public - * @param mixed $data 数据 - * @return \Generator - */ - public function cursor($data = null) - { - if (!is_null($data)) { - // 主键条件分析 - $this->parsePkWhere($data); - } - - $this->options['data'] = $data; - - $connection = clone $this->connection; - - return $connection->cursor($this); - } - - /** - * 分批数据返回处理 - * @access public - * @param integer $count 每次处理的数据数量 - * @param callable $callback 处理回调方法 - * @param string|array $column 分批处理的字段名 - * @param string $order 字段排序 - * @return bool - * @throws Exception - */ - public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool - { - $options = $this->getOptions(); - $column = $column ?: $this->getPk(); - - if (isset($options['order'])) { - unset($options['order']); - } - - $bind = $this->bind; - - if (is_array($column)) { - $times = 1; - $query = $this->options($options)->page($times, $count); - } else { - $query = $this->options($options)->limit($count); - - if (strpos($column, '.')) { - [$alias, $key] = explode('.', $column); - } else { - $key = $column; - } - } - - $resultSet = $query->order($column, $order)->select(); - - while (count($resultSet) > 0) { - if (false === call_user_func($callback, $resultSet)) { - return false; - } - - if (isset($times)) { - $times++; - $query = $this->options($options)->page($times, $count); - } else { - $end = $resultSet->pop(); - $lastId = is_array($end) ? $end[$key] : $end->getData($key); - - $query = $this->options($options) - ->limit($count) - ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId); - } - - $resultSet = $query->bind($bind)->order($column, $order)->select(); - } - - return true; - } -} + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db; + +use PDOStatement; +use think\helper\Str; + +/** + * PDO数据查询类 + */ +class Query extends BaseQuery +{ + use concern\JoinAndViewQuery; + use concern\ParamsBind; + use concern\TableFieldInfo; + + /** + * 表达式方式指定Field排序 + * @access public + * @param string $field 排序字段 + * @param array $bind 参数绑定 + * @return $this + */ + public function orderRaw(string $field, array $bind = []) + { + if (!empty($bind)) { + $this->bindParams($field, $bind); + } + + $this->options['order'][] = new Raw($field); + + return $this; + } + + /** + * 表达式方式指定查询字段 + * @access public + * @param string $field 字段名 + * @return $this + */ + public function fieldRaw(string $field) + { + $this->options['field'][] = new Raw($field); + + return $this; + } + + /** + * 指定Field排序 orderField('id',[1,2,3],'desc') + * @access public + * @param string $field 排序字段 + * @param array $values 排序值 + * @param string $order 排序 desc/asc + * @return $this + */ + public function orderField(string $field, array $values, string $order = '') + { + if (!empty($values)) { + $values['sort'] = $order; + + $this->options['order'][$field] = $values; + } + + return $this; + } + + /** + * 随机排序 + * @access public + * @return $this + */ + public function orderRand() + { + $this->options['order'][] = '[rand]'; + return $this; + } + + /** + * 使用表达式设置数据 + * @access public + * @param string $field 字段名 + * @param string $value 字段值 + * @return $this + */ + public function exp(string $field, string $value) + { + $this->options['data'][$field] = new Raw($value); + return $this; + } + + /** + * 表达式方式指定当前操作的数据表 + * @access public + * @param mixed $table 表名 + * @return $this + */ + public function tableRaw(string $table) + { + $this->options['table'] = new Raw($table); + + return $this; + } + + /** + * 执行查询 返回数据集 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @return array + * @throws BindParamException + * @throws PDOException + */ + public function query(string $sql, array $bind = []): array + { + return $this->connection->query($this, $sql, $bind); + } + + /** + * 执行语句 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @return int + * @throws BindParamException + * @throws PDOException + */ + public function execute(string $sql, array $bind = []): int + { + return $this->connection->execute($this, $sql, $bind, true); + } + + /** + * 获取执行的SQL语句而不进行实际的查询 + * @access public + * @param bool $fetch 是否返回sql + * @return $this|Fetch + */ + public function fetchSql(bool $fetch = true) + { + $this->options['fetch_sql'] = $fetch; + + if ($fetch) { + return new Fetch($this); + } + + return $this; + } + + /** + * 批处理执行SQL语句 + * 批处理的指令都认为是execute操作 + * @access public + * @param array $sql SQL批处理指令 + * @return bool + */ + public function batchQuery(array $sql = []): bool + { + return $this->connection->batchQuery($this, $sql); + } + + /** + * USING支持 用于多表删除 + * @access public + * @param mixed $using USING + * @return $this + */ + public function using($using) + { + $this->options['using'] = $using; + return $this; + } + + /** + * 存储过程调用 + * @access public + * @param bool $procedure 是否为存储过程查询 + * @return $this + */ + public function procedure(bool $procedure = true) + { + $this->options['procedure'] = $procedure; + return $this; + } + + /** + * 指定group查询 + * @access public + * @param string|array $group GROUP + * @return $this + */ + public function group($group) + { + $this->options['group'] = $group; + return $this; + } + + /** + * 指定having查询 + * @access public + * @param string $having having + * @return $this + */ + public function having(string $having) + { + $this->options['having'] = $having; + return $this; + } + + /** + * 指定distinct查询 + * @access public + * @param bool $distinct 是否唯一 + * @return $this + */ + public function distinct(bool $distinct = true) + { + $this->options['distinct'] = $distinct; + return $this; + } + + /** + * 指定强制索引 + * @access public + * @param string $force 索引名称 + * @return $this + */ + public function force(string $force) + { + $this->options['force'] = $force; + return $this; + } + + /** + * 查询注释 + * @access public + * @param string $comment 注释 + * @return $this + */ + public function comment(string $comment) + { + $this->options['comment'] = $comment; + return $this; + } + + /** + * 设置是否REPLACE + * @access public + * @param bool $replace 是否使用REPLACE写入数据 + * @return $this + */ + public function replace(bool $replace = true) + { + $this->options['replace'] = $replace; + return $this; + } + + /** + * 设置当前查询所在的分区 + * @access public + * @param string|array $partition 分区名称 + * @return $this + */ + public function partition($partition) + { + $this->options['partition'] = $partition; + return $this; + } + + /** + * 设置DUPLICATE + * @access public + * @param array|string|Raw $duplicate DUPLICATE信息 + * @return $this + */ + public function duplicate($duplicate) + { + $this->options['duplicate'] = $duplicate; + return $this; + } + + /** + * 设置查询的额外参数 + * @access public + * @param string $extra 额外信息 + * @return $this + */ + public function extra(string $extra) + { + $this->options['extra'] = $extra; + return $this; + } + + /** + * 创建子查询SQL + * @access public + * @param bool $sub 是否添加括号 + * @return string + * @throws Exception + */ + public function buildSql(bool $sub = true): string + { + return $sub ? '( ' . $this->fetchSql()->select() . ' )' : $this->fetchSql()->select(); + } + + /** + * 获取当前数据表的主键 + * @access public + * @return string|array + */ + public function getPk() + { + if (empty($this->pk)) { + $this->pk = $this->connection->getPk($this->getTable()); + } + + return $this->pk; + } + + /** + * 指定数据表自增主键 + * @access public + * @param string $autoinc 自增键 + * @return $this + */ + public function autoinc(string $autoinc) + { + $this->autoinc = $autoinc; + return $this; + } + + /** + * 获取当前数据表的自增主键 + * @access public + * @return string|null + */ + public function getAutoInc() + { + $tableName = $this->getTable(); + + if (empty($this->autoinc) && $tableName) { + $this->autoinc = $this->connection->getAutoInc($tableName); + } + + return $this->autoinc; + } + + /** + * 字段值增长 + * @access public + * @param string $field 字段名 + * @param float $step 增长值 + * @return $this + */ + public function inc(string $field, float $step = 1) + { + $this->options['data'][$field] = ['INC', $step]; + + return $this; + } + + /** + * 字段值减少 + * @access public + * @param string $field 字段名 + * @param float $step 增长值 + * @return $this + */ + public function dec(string $field, float $step = 1) + { + $this->options['data'][$field] = ['DEC', $step]; + return $this; + } + + /** + * 获取当前的查询标识 + * @access public + * @param mixed $data 要序列化的数据 + * @return string + */ + public function getQueryGuid($data = null): string + { + return md5($this->getConfig('database') . serialize(var_export($data ?: $this->options, true)) . serialize($this->getBind(false))); + } + + /** + * 执行查询但只返回PDOStatement对象 + * @access public + * @return PDOStatement + */ + public function getPdo(): PDOStatement + { + return $this->connection->pdo($this); + } + + /** + * 使用游标查找记录 + * @access public + * @param mixed $data 数据 + * @return \Generator + */ + public function cursor($data = null) + { + if (!is_null($data)) { + // 主键条件分析 + $this->parsePkWhere($data); + } + + $this->options['data'] = $data; + + $connection = clone $this->connection; + + return $connection->cursor($this); + } + + /** + * 分批数据返回处理 + * @access public + * @param integer $count 每次处理的数据数量 + * @param callable $callback 处理回调方法 + * @param string|array $column 分批处理的字段名 + * @param string $order 字段排序 + * @return bool + * @throws Exception + */ + public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool + { + $options = $this->getOptions(); + $column = $column ?: $this->getPk(); + + if (isset($options['order'])) { + unset($options['order']); + } + + $bind = $this->bind; + + if (is_array($column)) { + $times = 1; + $query = $this->options($options)->page($times, $count); + } else { + $query = $this->options($options)->limit($count); + + if (strpos($column, '.')) { + [$alias, $key] = explode('.', $column); + } else { + $key = $column; + } + } + + $resultSet = $query->order($column, $order)->select(); + + while (count($resultSet) > 0) { + if (false === call_user_func($callback, $resultSet)) { + return false; + } + + if (isset($times)) { + $times++; + $query = $this->options($options)->page($times, $count); + } else { + $end = $resultSet->pop(); + $lastId = is_array($end) ? $end[$key] : $end->getData($key); + + $query = $this->options($options) + ->limit($count) + ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId); + } + + $resultSet = $query->bind($bind)->order($column, $order)->select(); + } + + return true; + } +} -- Gitee From 856ef778a8122cc3072aeb3d09ee91eeac9e1d2e Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 5 Feb 2020 11:39:14 +0800 Subject: [PATCH 146/365] =?UTF-8?q?=E6=A8=A1=E5=9E=8Bcreate=E3=80=81update?= =?UTF-8?q?=E5=92=8Csaveall=E6=96=B9=E6=B3=95=E6=94=AF=E6=8C=81suffix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 2092 +++++++++++++++++++++++++------------------------ 1 file changed, 1052 insertions(+), 1040 deletions(-) diff --git a/src/Model.php b/src/Model.php index be6df4a..dfb818a 100644 --- a/src/Model.php +++ b/src/Model.php @@ -1,1040 +1,1052 @@ - -// +---------------------------------------------------------------------- -declare (strict_types = 1); - -namespace think; - -use ArrayAccess; -use Closure; -use JsonSerializable; -use think\contract\Arrayable; -use think\contract\Jsonable; -use think\db\BaseQuery as Query; - -/** - * Class Model - * @package think - * @mixin Query - * @method void onAfterRead(Model $model) static after_read事件定义 - * @method mixed onBeforeInsert(Model $model) static before_insert事件定义 - * @method void onAfterInsert(Model $model) static after_insert事件定义 - * @method mixed onBeforeUpdate(Model $model) static before_update事件定义 - * @method void onAfterUpdate(Model $model) static after_update事件定义 - * @method mixed onBeforeWrite(Model $model) static before_write事件定义 - * @method void onAfterWrite(Model $model) static after_write事件定义 - * @method mixed onBeforeDelete(Model $model) static before_write事件定义 - * @method void onAfterDelete(Model $model) static after_delete事件定义 - * @method void onBeforeRestore(Model $model) static before_restore事件定义 - * @method void onAfterRestore(Model $model) static after_restore事件定义 - */ -abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonable -{ - use model\concern\Attribute; - use model\concern\RelationShip; - use model\concern\ModelEvent; - use model\concern\TimeStamp; - use model\concern\Conversion; - - /** - * 数据是否存在 - * @var bool - */ - private $exists = false; - - /** - * 是否强制更新所有数据 - * @var bool - */ - private $force = false; - - /** - * 是否Replace - * @var bool - */ - private $replace = false; - - /** - * 数据表后缀 - * @var string - */ - protected $suffix; - - /** - * 更新条件 - * @var array - */ - private $updateWhere; - - /** - * 数据库配置 - * @var string - */ - protected $connection; - - /** - * 模型名称 - * @var string - */ - protected $name; - - /** - * 主键值 - * @var string - */ - protected $key; - - /** - * 数据表名称 - * @var string - */ - protected $table; - - /** - * 初始化过的模型. - * @var array - */ - protected static $initialized = []; - - /** - * 软删除字段默认值 - * @var mixed - */ - protected $defaultSoftDelete; - - /** - * 全局查询范围 - * @var array - */ - protected $globalScope = []; - - /** - * 延迟保存信息 - * @var bool - */ - private $lazySave = false; - - /** - * Db对象 - * @var DbManager - */ - protected static $db; - - /** - * 容器对象的依赖注入方法 - * @var callable - */ - protected static $invoker; - - /** - * 服务注入 - * @var Closure[] - */ - protected static $maker = []; - - /** - * 设置服务注入 - * @access public - * @param Closure $maker - * @return void - */ - public static function maker(Closure $maker) - { - static::$maker[] = $maker; - } - - /** - * 设置Db对象 - * @access public - * @param DbManager $db Db对象 - * @return void - */ - public static function setDb(DbManager $db) - { - self::$db = $db; - } - - /** - * 设置容器对象的依赖注入方法 - * @access public - * @param callable $callable 依赖注入方法 - * @return void - */ - public static function setInvoker(callable $callable): void - { - self::$invoker = $callable; - } - - /** - * 调用反射执行模型方法 支持参数绑定 - * @access public - * @param mixed $method - * @param array $vars 参数 - * @return mixed - */ - public function invoke($method, array $vars = []) - { - if (self::$invoker) { - $call = self::$invoker; - return $call($method instanceof Closure ? $method : Closure::fromCallable([$this, $method]), $vars); - } - - return call_user_func_array($method instanceof Closure ? $method : [$this, $method], $vars); - } - - /** - * 架构函数 - * @access public - * @param array $data 数据 - */ - public function __construct(array $data = []) - { - $this->data = $data; - - if (!empty($this->data)) { - // 废弃字段 - foreach ((array) $this->disuse as $key) { - if (array_key_exists($key, $this->data)) { - unset($this->data[$key]); - } - } - } - - // 记录原始数据 - $this->origin = $this->data; - - if (empty($this->name)) { - // 当前模型名 - $name = str_replace('\\', '/', static::class); - $this->name = basename($name); - } - - if (!empty(static::$maker)) { - foreach (static::$maker as $maker) { - call_user_func($maker, $this); - } - } - - // 执行初始化操作 - $this->initialize(); - } - - /** - * 获取当前模型名称 - * @access public - * @return string - */ - public function getName(): string - { - return $this->name; - } - - /** - * 创建新的模型实例 - * @access public - * @param array $data 数据 - * @param mixed $where 更新条件 - * @return Model - */ - public function newInstance(array $data = [], $where = null): Model - { - $model = new static($data); - - if ($this->connection) { - $model->setConnection($this->connection); - } - - if ($this->suffix) { - $model->setSuffix($this->suffix); - } - - if (empty($data)) { - return $model; - } - - $model->exists(true); - - $model->setUpdateWhere($where); - - $model->trigger('AfterRead'); - - return $model; - } - - /** - * 设置模型的更新条件 - * @access protected - * @param mixed $where 更新条件 - * @return void - */ - protected function setUpdateWhere($where): void - { - $this->updateWhere = $where; - } - - /** - * 设置当前模型的数据库连接 - * @access public - * @param string $connection 数据表连接标识 - * @return $this - */ - public function setConnection(string $connection) - { - $this->connection = $connection; - return $this; - } - - /** - * 获取当前模型的数据库连接标识 - * @access public - * @return string - */ - public function getConnection(): string - { - return $this->connection ?: ''; - } - - /** - * 设置当前模型数据表的后缀 - * @access public - * @param string $suffix 数据表后缀 - * @return $this - */ - public function setSuffix(string $suffix) - { - $this->suffix = $suffix; - return $this; - } - - /** - * 获取当前模型的数据表后缀 - * @access public - * @return string - */ - public function getSuffix(): string - { - return $this->suffix ?: ''; - } - - /** - * 获取当前模型的数据库查询对象 - * @access public - * @param array $scope 设置不使用的全局查询范围 - * @return Query - */ - public function db($scope = []): Query - { - /** @var Query $query */ - $query = self::$db->connect($this->connection) - ->name($this->name . $this->suffix) - ->pk($this->pk); - - if (!empty($this->table)) { - $query->table($this->table . $this->suffix); - } - - $query->model($this) - ->json($this->json, $this->jsonAssoc) - ->setFieldType(array_merge($this->schema, $this->jsonType)); - - // 软删除 - if (property_exists($this, 'withTrashed') && !$this->withTrashed) { - $this->withNoTrashed($query); - } - - // 全局作用域 - if (is_array($scope)) { - $globalScope = array_diff($this->globalScope, $scope); - $query->scope($globalScope); - } - - // 返回当前模型的数据库查询对象 - return $query; - } - - /** - * 初始化模型 - * @access private - * @return void - */ - private function initialize(): void - { - if (!isset(static::$initialized[static::class])) { - static::$initialized[static::class] = true; - static::init(); - } - } - - /** - * 初始化处理 - * @access protected - * @return void - */ - protected static function init() - { - } - - protected function checkData(): void - { - } - - protected function checkResult($result): void - { - } - - /** - * 更新是否强制写入数据 而不做比较(亦可用于软删除的强制删除) - * @access public - * @param bool $force - * @return $this - */ - public function force(bool $force = true) - { - $this->force = $force; - return $this; - } - - /** - * 判断force - * @access public - * @return bool - */ - public function isForce(): bool - { - return $this->force; - } - - /** - * 新增数据是否使用Replace - * @access public - * @param bool $replace - * @return $this - */ - public function replace(bool $replace = true) - { - $this->replace = $replace; - return $this; - } - - /** - * 刷新模型数据 - * @access public - * @param bool $relation 是否刷新关联数据 - * @return $this - */ - public function refresh(bool $relation = false) - { - if ($this->exists) { - $this->data = $this->db()->find($this->getKey())->getData(); - $this->origin = $this->data; - - if ($relation) { - $this->relation = []; - } - } - - return $this; - } - - /** - * 设置数据是否存在 - * @access public - * @param bool $exists - * @return $this - */ - public function exists(bool $exists = true) - { - $this->exists = $exists; - return $this; - } - - /** - * 判断数据是否存在数据库 - * @access public - * @return bool - */ - public function isExists(): bool - { - return $this->exists; - } - - /** - * 判断模型是否为空 - * @access public - * @return bool - */ - public function isEmpty(): bool - { - return empty($this->data); - } - - /** - * 延迟保存当前数据对象 - * @access public - * @param array|bool $data 数据 - * @return void - */ - public function lazySave($data = []): void - { - if (false === $data) { - $this->lazySave = false; - } else { - if (is_array($data)) { - $this->setAttrs($data); - } - - $this->lazySave = true; - } - } - - /** - * 保存当前数据对象 - * @access public - * @param array $data 数据 - * @param string $sequence 自增序列名 - * @return bool - */ - public function save(array $data = [], string $sequence = null): bool - { - // 数据对象赋值 - $this->setAttrs($data); - - if ($this->isEmpty() || false === $this->trigger('BeforeWrite')) { - return false; - } - - $result = $this->exists ? $this->updateData() : $this->insertData($sequence); - - if (false === $result) { - return false; - } - - // 写入回调 - $this->trigger('AfterWrite'); - - // 重新记录原始数据 - $this->origin = $this->data; - $this->set = []; - $this->lazySave = false; - - return true; - } - - /** - * 检查数据是否允许写入 - * @access protected - * @return array - */ - protected function checkAllowFields(): array - { - // 检测字段 - if (empty($this->field)) { - if (!empty($this->schema)) { - $this->field = array_keys(array_merge($this->schema, $this->jsonType)); - } else { - $query = $this->db(); - $table = $this->table ? $this->table . $this->suffix : $query->getTable(); - - $this->field = $query->getConnection()->getTableFields($table); - } - - return $this->field; - } - - $field = $this->field; - - if ($this->autoWriteTimestamp) { - array_push($field, $this->createTime, $this->updateTime); - } - - if (!empty($this->disuse)) { - // 废弃字段 - $field = array_diff($field, $this->disuse); - } - - return $field; - } - - /** - * 保存写入数据 - * @access protected - * @return bool - */ - protected function updateData(): bool - { - // 事件回调 - if (false === $this->trigger('BeforeUpdate')) { - return false; - } - - $this->checkData(); - - // 获取有更新的数据 - $data = $this->getChangedData(); - - if (empty($data)) { - // 关联更新 - if (!empty($this->relationWrite)) { - $this->autoRelationUpdate(); - } - - return true; - } - - if ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) { - // 自动写入更新时间 - $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); - $this->data[$this->updateTime] = $data[$this->updateTime]; - } - - // 检查允许字段 - $allowFields = $this->checkAllowFields(); - - foreach ($this->relationWrite as $name => $val) { - if (!is_array($val)) { - continue; - } - - foreach ($val as $key) { - if (isset($data[$key])) { - unset($data[$key]); - } - } - } - - // 模型更新 - $db = $this->db(); - $db->startTrans(); - - try { - $this->key = null; - $where = $this->getWhere(); - - $result = $db->where($where) - ->strict(false) - ->cache(true) - ->setOption('key', $this->key) - ->field($allowFields) - ->update($data); - - $this->checkResult($result); - - // 关联更新 - if (!empty($this->relationWrite)) { - $this->autoRelationUpdate(); - } - - $db->commit(); - - // 更新回调 - $this->trigger('AfterUpdate'); - - return true; - } catch (\Exception $e) { - $db->rollback(); - throw $e; - } - } - - /** - * 新增写入数据 - * @access protected - * @param string $sequence 自增名 - * @return bool - */ - protected function insertData(string $sequence = null): bool - { - // 时间戳自动写入 - if ($this->autoWriteTimestamp) { - if ($this->createTime && !isset($this->data[$this->createTime])) { - $this->data[$this->createTime] = $this->autoWriteTimestamp($this->createTime); - } - - if ($this->updateTime && !isset($this->data[$this->updateTime])) { - $this->data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); - } - } - - if (false === $this->trigger('BeforeInsert')) { - return false; - } - - $this->checkData(); - - // 检查允许字段 - $allowFields = $this->checkAllowFields(); - - $db = $this->db(); - $db->startTrans(); - - try { - $result = $db->strict(false) - ->field($allowFields) - ->replace($this->replace) - ->sequence($sequence) - ->insert($this->data, true); - - // 获取自动增长主键 - if ($result) { - $pk = $this->getPk(); - - if (is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) { - $this->data[$pk] = $result; - } - } - - // 关联写入 - if (!empty($this->relationWrite)) { - $this->autoRelationInsert(); - } - - $db->commit(); - - // 标记数据已经存在 - $this->exists = true; - - // 新增回调 - $this->trigger('AfterInsert'); - - return true; - } catch (\Exception $e) { - $db->rollback(); - throw $e; - } - } - - /** - * 获取当前的更新条件 - * @access public - * @return mixed - */ - public function getWhere() - { - $pk = $this->getPk(); - - if (is_string($pk) && isset($this->origin[$pk])) { - $where = [[$pk, '=', $this->origin[$pk]]]; - $this->key = $this->origin[$pk]; - } elseif (is_array($pk)) { - foreach ($pk as $field) { - if (isset($this->origin[$field])) { - $where[] = [$field, '=', $this->origin[$field]]; - } - } - } - - if (empty($where)) { - $where = empty($this->updateWhere) ? null : $this->updateWhere; - } - - return $where; - } - - /** - * 保存多个数据到当前数据对象 - * @access public - * @param iterable $dataSet 数据 - * @param boolean $replace 是否自动识别更新和写入 - * @return Collection - * @throws \Exception - */ - public function saveAll(iterable $dataSet, bool $replace = true): Collection - { - $db = $this->db(); - $db->startTrans(); - - try { - $pk = $this->getPk(); - - if (is_string($pk) && $replace) { - $auto = true; - } - - $result = []; - - foreach ($dataSet as $key => $data) { - if ($this->exists || (!empty($auto) && isset($data[$pk]))) { - $result[$key] = self::update($data); - } else { - $result[$key] = self::create($data, $this->field, $this->replace); - } - } - - $db->commit(); - - return $this->toCollection($result); - } catch (\Exception $e) { - $db->rollback(); - throw $e; - } - } - - /** - * 删除当前的记录 - * @access public - * @return bool - */ - public function delete(): bool - { - if (!$this->exists || $this->isEmpty() || false === $this->trigger('BeforeDelete')) { - return false; - } - - // 读取更新条件 - $where = $this->getWhere(); - - $db = $this->db(); - $db->startTrans(); - - try { - // 删除当前模型数据 - $db->where($where)->delete(); - - // 关联删除 - if (!empty($this->relationWrite)) { - $this->autoRelationDelete(); - } - - $db->commit(); - - $this->trigger('AfterDelete'); - - $this->exists = false; - $this->lazySave = false; - - return true; - } catch (\Exception $e) { - $db->rollback(); - throw $e; - } - } - - /** - * 写入数据 - * @access public - * @param array $data 数据数组 - * @param array $allowField 允许字段 - * @param bool $replace 使用Replace - * @return static - */ - public static function create(array $data, array $allowField = [], bool $replace = false): Model - { - $model = new static(); - - if (!empty($allowField)) { - $model->allowField($allowField); - } - - $model->replace($replace)->save($data); - - return $model; - } - - /** - * 更新数据 - * @access public - * @param array $data 数据数组 - * @param mixed $where 更新条件 - * @param array $allowField 允许字段 - * @return static - */ - public static function update(array $data, $where = [], array $allowField = []) - { - $model = new static(); - - if (!empty($allowField)) { - $model->allowField($allowField); - } - - if (!empty($where)) { - $model->setUpdateWhere($where); - } - - $model->exists(true)->save($data); - - return $model; - } - - /** - * 删除记录 - * @access public - * @param mixed $data 主键列表 支持闭包查询条件 - * @param bool $force 是否强制删除 - * @return bool - */ - public static function destroy($data, bool $force = false): bool - { - if (empty($data) && 0 !== $data) { - return false; - } - - $model = new static(); - - $query = $model->db(); - - if (is_array($data) && key($data) !== 0) { - $query->where($data); - $data = null; - } elseif ($data instanceof \Closure) { - $data($query); - $data = null; - } - - $resultSet = $query->select($data); - - foreach ($resultSet as $result) { - $result->force($force)->delete(); - } - - return true; - } - - /** - * 解序列化后处理 - */ - public function __wakeup() - { - $this->initialize(); - } - - /** - * 修改器 设置数据对象的值 - * @access public - * @param string $name 名称 - * @param mixed $value 值 - * @return void - */ - public function __set(string $name, $value): void - { - $this->setAttr($name, $value); - } - - /** - * 获取器 获取数据对象的值 - * @access public - * @param string $name 名称 - * @return mixed - */ - public function __get(string $name) - { - return $this->getAttr($name); - } - - /** - * 检测数据对象的值 - * @access public - * @param string $name 名称 - * @return bool - */ - public function __isset(string $name): bool - { - return !is_null($this->getAttr($name)); - } - - /** - * 销毁数据对象的值 - * @access public - * @param string $name 名称 - * @return void - */ - public function __unset(string $name): void - { - unset($this->data[$name], $this->relation[$name]); - } - - // ArrayAccess - public function offsetSet($name, $value) - { - $this->setAttr($name, $value); - } - - public function offsetExists($name): bool - { - return $this->__isset($name); - } - - public function offsetUnset($name) - { - $this->__unset($name); - } - - public function offsetGet($name) - { - return $this->getAttr($name); - } - - /** - * 设置不使用的全局查询范围 - * @access public - * @param array $scope 不启用的全局查询范围 - * @return Query - */ - public static function withoutGlobalScope(array $scope = null) - { - $model = new static(); - - return $model->db($scope); - } - - /** - * 切换后缀进行查询 - * @access public - * @param string $suffix 切换的表后缀 - * @return Model - */ - public static function suffix(string $suffix) - { - $model = new static(); - $model->setSuffix($suffix); - - return $model; - } - - /** - * 切换数据库连接进行查询 - * @access public - * @param string $connection 数据库连接标识 - * @return Model - */ - public static function connect(string $connection) - { - $model = new static(); - $model->setConnection($connection); - - return $model; - } - - public function __call($method, $args) - { - if ('withattr' == strtolower($method)) { - return call_user_func_array([$this, 'withAttribute'], $args); - } - - return call_user_func_array([$this->db(), $method], $args); - } - - public static function __callStatic($method, $args) - { - $model = new static(); - - return call_user_func_array([$model->db(), $method], $args); - } - - /** - * 析构方法 - * @access public - */ - public function __destruct() - { - if ($this->lazySave) { - $this->save(); - } - } -} + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think; + +use ArrayAccess; +use Closure; +use JsonSerializable; +use think\contract\Arrayable; +use think\contract\Jsonable; +use think\db\BaseQuery as Query; + +/** + * Class Model + * @package think + * @mixin Query + * @method void onAfterRead(Model $model) static after_read事件定义 + * @method mixed onBeforeInsert(Model $model) static before_insert事件定义 + * @method void onAfterInsert(Model $model) static after_insert事件定义 + * @method mixed onBeforeUpdate(Model $model) static before_update事件定义 + * @method void onAfterUpdate(Model $model) static after_update事件定义 + * @method mixed onBeforeWrite(Model $model) static before_write事件定义 + * @method void onAfterWrite(Model $model) static after_write事件定义 + * @method mixed onBeforeDelete(Model $model) static before_write事件定义 + * @method void onAfterDelete(Model $model) static after_delete事件定义 + * @method void onBeforeRestore(Model $model) static before_restore事件定义 + * @method void onAfterRestore(Model $model) static after_restore事件定义 + */ +abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonable +{ + use model\concern\Attribute; + use model\concern\RelationShip; + use model\concern\ModelEvent; + use model\concern\TimeStamp; + use model\concern\Conversion; + + /** + * 数据是否存在 + * @var bool + */ + private $exists = false; + + /** + * 是否强制更新所有数据 + * @var bool + */ + private $force = false; + + /** + * 是否Replace + * @var bool + */ + private $replace = false; + + /** + * 数据表后缀 + * @var string + */ + protected $suffix; + + /** + * 更新条件 + * @var array + */ + private $updateWhere; + + /** + * 数据库配置 + * @var string + */ + protected $connection; + + /** + * 模型名称 + * @var string + */ + protected $name; + + /** + * 主键值 + * @var string + */ + protected $key; + + /** + * 数据表名称 + * @var string + */ + protected $table; + + /** + * 初始化过的模型. + * @var array + */ + protected static $initialized = []; + + /** + * 软删除字段默认值 + * @var mixed + */ + protected $defaultSoftDelete; + + /** + * 全局查询范围 + * @var array + */ + protected $globalScope = []; + + /** + * 延迟保存信息 + * @var bool + */ + private $lazySave = false; + + /** + * Db对象 + * @var DbManager + */ + protected static $db; + + /** + * 容器对象的依赖注入方法 + * @var callable + */ + protected static $invoker; + + /** + * 服务注入 + * @var Closure[] + */ + protected static $maker = []; + + /** + * 设置服务注入 + * @access public + * @param Closure $maker + * @return void + */ + public static function maker(Closure $maker) + { + static::$maker[] = $maker; + } + + /** + * 设置Db对象 + * @access public + * @param DbManager $db Db对象 + * @return void + */ + public static function setDb(DbManager $db) + { + self::$db = $db; + } + + /** + * 设置容器对象的依赖注入方法 + * @access public + * @param callable $callable 依赖注入方法 + * @return void + */ + public static function setInvoker(callable $callable): void + { + self::$invoker = $callable; + } + + /** + * 调用反射执行模型方法 支持参数绑定 + * @access public + * @param mixed $method + * @param array $vars 参数 + * @return mixed + */ + public function invoke($method, array $vars = []) + { + if (self::$invoker) { + $call = self::$invoker; + return $call($method instanceof Closure ? $method : Closure::fromCallable([$this, $method]), $vars); + } + + return call_user_func_array($method instanceof Closure ? $method : [$this, $method], $vars); + } + + /** + * 架构函数 + * @access public + * @param array $data 数据 + */ + public function __construct(array $data = []) + { + $this->data = $data; + + if (!empty($this->data)) { + // 废弃字段 + foreach ((array) $this->disuse as $key) { + if (array_key_exists($key, $this->data)) { + unset($this->data[$key]); + } + } + } + + // 记录原始数据 + $this->origin = $this->data; + + if (empty($this->name)) { + // 当前模型名 + $name = str_replace('\\', '/', static::class); + $this->name = basename($name); + } + + if (!empty(static::$maker)) { + foreach (static::$maker as $maker) { + call_user_func($maker, $this); + } + } + + // 执行初始化操作 + $this->initialize(); + } + + /** + * 获取当前模型名称 + * @access public + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * 创建新的模型实例 + * @access public + * @param array $data 数据 + * @param mixed $where 更新条件 + * @return Model + */ + public function newInstance(array $data = [], $where = null): Model + { + $model = new static($data); + + if ($this->connection) { + $model->setConnection($this->connection); + } + + if ($this->suffix) { + $model->setSuffix($this->suffix); + } + + if (empty($data)) { + return $model; + } + + $model->exists(true); + + $model->setUpdateWhere($where); + + $model->trigger('AfterRead'); + + return $model; + } + + /** + * 设置模型的更新条件 + * @access protected + * @param mixed $where 更新条件 + * @return void + */ + protected function setUpdateWhere($where): void + { + $this->updateWhere = $where; + } + + /** + * 设置当前模型的数据库连接 + * @access public + * @param string $connection 数据表连接标识 + * @return $this + */ + public function setConnection(string $connection) + { + $this->connection = $connection; + return $this; + } + + /** + * 获取当前模型的数据库连接标识 + * @access public + * @return string + */ + public function getConnection(): string + { + return $this->connection ?: ''; + } + + /** + * 设置当前模型数据表的后缀 + * @access public + * @param string $suffix 数据表后缀 + * @return $this + */ + public function setSuffix(string $suffix) + { + $this->suffix = $suffix; + return $this; + } + + /** + * 获取当前模型的数据表后缀 + * @access public + * @return string + */ + public function getSuffix(): string + { + return $this->suffix ?: ''; + } + + /** + * 获取当前模型的数据库查询对象 + * @access public + * @param array $scope 设置不使用的全局查询范围 + * @return Query + */ + public function db($scope = []): Query + { + /** @var Query $query */ + $query = self::$db->connect($this->connection) + ->name($this->name . $this->suffix) + ->pk($this->pk); + + if (!empty($this->table)) { + $query->table($this->table . $this->suffix); + } + + $query->model($this) + ->json($this->json, $this->jsonAssoc) + ->setFieldType(array_merge($this->schema, $this->jsonType)); + + // 软删除 + if (property_exists($this, 'withTrashed') && !$this->withTrashed) { + $this->withNoTrashed($query); + } + + // 全局作用域 + if (is_array($scope)) { + $globalScope = array_diff($this->globalScope, $scope); + $query->scope($globalScope); + } + + // 返回当前模型的数据库查询对象 + return $query; + } + + /** + * 初始化模型 + * @access private + * @return void + */ + private function initialize(): void + { + if (!isset(static::$initialized[static::class])) { + static::$initialized[static::class] = true; + static::init(); + } + } + + /** + * 初始化处理 + * @access protected + * @return void + */ + protected static function init() + { + } + + protected function checkData(): void + { + } + + protected function checkResult($result): void + { + } + + /** + * 更新是否强制写入数据 而不做比较(亦可用于软删除的强制删除) + * @access public + * @param bool $force + * @return $this + */ + public function force(bool $force = true) + { + $this->force = $force; + return $this; + } + + /** + * 判断force + * @access public + * @return bool + */ + public function isForce(): bool + { + return $this->force; + } + + /** + * 新增数据是否使用Replace + * @access public + * @param bool $replace + * @return $this + */ + public function replace(bool $replace = true) + { + $this->replace = $replace; + return $this; + } + + /** + * 刷新模型数据 + * @access public + * @param bool $relation 是否刷新关联数据 + * @return $this + */ + public function refresh(bool $relation = false) + { + if ($this->exists) { + $this->data = $this->db()->find($this->getKey())->getData(); + $this->origin = $this->data; + + if ($relation) { + $this->relation = []; + } + } + + return $this; + } + + /** + * 设置数据是否存在 + * @access public + * @param bool $exists + * @return $this + */ + public function exists(bool $exists = true) + { + $this->exists = $exists; + return $this; + } + + /** + * 判断数据是否存在数据库 + * @access public + * @return bool + */ + public function isExists(): bool + { + return $this->exists; + } + + /** + * 判断模型是否为空 + * @access public + * @return bool + */ + public function isEmpty(): bool + { + return empty($this->data); + } + + /** + * 延迟保存当前数据对象 + * @access public + * @param array|bool $data 数据 + * @return void + */ + public function lazySave($data = []): void + { + if (false === $data) { + $this->lazySave = false; + } else { + if (is_array($data)) { + $this->setAttrs($data); + } + + $this->lazySave = true; + } + } + + /** + * 保存当前数据对象 + * @access public + * @param array $data 数据 + * @param string $sequence 自增序列名 + * @return bool + */ + public function save(array $data = [], string $sequence = null): bool + { + // 数据对象赋值 + $this->setAttrs($data); + + if ($this->isEmpty() || false === $this->trigger('BeforeWrite')) { + return false; + } + + $result = $this->exists ? $this->updateData() : $this->insertData($sequence); + + if (false === $result) { + return false; + } + + // 写入回调 + $this->trigger('AfterWrite'); + + // 重新记录原始数据 + $this->origin = $this->data; + $this->set = []; + $this->lazySave = false; + + return true; + } + + /** + * 检查数据是否允许写入 + * @access protected + * @return array + */ + protected function checkAllowFields(): array + { + // 检测字段 + if (empty($this->field)) { + if (!empty($this->schema)) { + $this->field = array_keys(array_merge($this->schema, $this->jsonType)); + } else { + $query = $this->db(); + $table = $this->table ? $this->table . $this->suffix : $query->getTable(); + + $this->field = $query->getConnection()->getTableFields($table); + } + + return $this->field; + } + + $field = $this->field; + + if ($this->autoWriteTimestamp) { + array_push($field, $this->createTime, $this->updateTime); + } + + if (!empty($this->disuse)) { + // 废弃字段 + $field = array_diff($field, $this->disuse); + } + + return $field; + } + + /** + * 保存写入数据 + * @access protected + * @return bool + */ + protected function updateData(): bool + { + // 事件回调 + if (false === $this->trigger('BeforeUpdate')) { + return false; + } + + $this->checkData(); + + // 获取有更新的数据 + $data = $this->getChangedData(); + + if (empty($data)) { + // 关联更新 + if (!empty($this->relationWrite)) { + $this->autoRelationUpdate(); + } + + return true; + } + + if ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) { + // 自动写入更新时间 + $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); + $this->data[$this->updateTime] = $data[$this->updateTime]; + } + + // 检查允许字段 + $allowFields = $this->checkAllowFields(); + + foreach ($this->relationWrite as $name => $val) { + if (!is_array($val)) { + continue; + } + + foreach ($val as $key) { + if (isset($data[$key])) { + unset($data[$key]); + } + } + } + + // 模型更新 + $db = $this->db(); + $db->startTrans(); + + try { + $this->key = null; + $where = $this->getWhere(); + + $result = $db->where($where) + ->strict(false) + ->cache(true) + ->setOption('key', $this->key) + ->field($allowFields) + ->update($data); + + $this->checkResult($result); + + // 关联更新 + if (!empty($this->relationWrite)) { + $this->autoRelationUpdate(); + } + + $db->commit(); + + // 更新回调 + $this->trigger('AfterUpdate'); + + return true; + } catch (\Exception $e) { + $db->rollback(); + throw $e; + } + } + + /** + * 新增写入数据 + * @access protected + * @param string $sequence 自增名 + * @return bool + */ + protected function insertData(string $sequence = null): bool + { + // 时间戳自动写入 + if ($this->autoWriteTimestamp) { + if ($this->createTime && !isset($this->data[$this->createTime])) { + $this->data[$this->createTime] = $this->autoWriteTimestamp($this->createTime); + } + + if ($this->updateTime && !isset($this->data[$this->updateTime])) { + $this->data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); + } + } + + if (false === $this->trigger('BeforeInsert')) { + return false; + } + + $this->checkData(); + + // 检查允许字段 + $allowFields = $this->checkAllowFields(); + + $db = $this->db(); + $db->startTrans(); + + try { + $result = $db->strict(false) + ->field($allowFields) + ->replace($this->replace) + ->sequence($sequence) + ->insert($this->data, true); + + // 获取自动增长主键 + if ($result) { + $pk = $this->getPk(); + + if (is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) { + $this->data[$pk] = $result; + } + } + + // 关联写入 + if (!empty($this->relationWrite)) { + $this->autoRelationInsert(); + } + + $db->commit(); + + // 标记数据已经存在 + $this->exists = true; + + // 新增回调 + $this->trigger('AfterInsert'); + + return true; + } catch (\Exception $e) { + $db->rollback(); + throw $e; + } + } + + /** + * 获取当前的更新条件 + * @access public + * @return mixed + */ + public function getWhere() + { + $pk = $this->getPk(); + + if (is_string($pk) && isset($this->origin[$pk])) { + $where = [[$pk, '=', $this->origin[$pk]]]; + $this->key = $this->origin[$pk]; + } elseif (is_array($pk)) { + foreach ($pk as $field) { + if (isset($this->origin[$field])) { + $where[] = [$field, '=', $this->origin[$field]]; + } + } + } + + if (empty($where)) { + $where = empty($this->updateWhere) ? null : $this->updateWhere; + } + + return $where; + } + + /** + * 保存多个数据到当前数据对象 + * @access public + * @param iterable $dataSet 数据 + * @param boolean $replace 是否自动识别更新和写入 + * @return Collection + * @throws \Exception + */ + public function saveAll(iterable $dataSet, bool $replace = true): Collection + { + $db = $this->db(); + $db->startTrans(); + + try { + $pk = $this->getPk(); + + if (is_string($pk) && $replace) { + $auto = true; + } + + $result = []; + + $suffix = $this->getSuffix(); + + foreach ($dataSet as $key => $data) { + if ($this->exists || (!empty($auto) && isset($data[$pk]))) { + $result[$key] = self::update($data, [], [], $suffix); + } else { + $result[$key] = self::create($data, $this->field, $this->replace, $suffix); + } + } + + $db->commit(); + + return $this->toCollection($result); + } catch (\Exception $e) { + $db->rollback(); + throw $e; + } + } + + /** + * 删除当前的记录 + * @access public + * @return bool + */ + public function delete(): bool + { + if (!$this->exists || $this->isEmpty() || false === $this->trigger('BeforeDelete')) { + return false; + } + + // 读取更新条件 + $where = $this->getWhere(); + + $db = $this->db(); + $db->startTrans(); + + try { + // 删除当前模型数据 + $db->where($where)->delete(); + + // 关联删除 + if (!empty($this->relationWrite)) { + $this->autoRelationDelete(); + } + + $db->commit(); + + $this->trigger('AfterDelete'); + + $this->exists = false; + $this->lazySave = false; + + return true; + } catch (\Exception $e) { + $db->rollback(); + throw $e; + } + } + + /** + * 写入数据 + * @access public + * @param array $data 数据数组 + * @param array $allowField 允许字段 + * @param bool $replace 使用Replace + * @param string $suffix 数据表后缀 + * @return static + */ + public static function create(array $data, array $allowField = [], bool $replace = false, string $suffix = ''): Model + { + $model = new static(); + + if (!empty($allowField)) { + $model->allowField($allowField); + } + + if (!empty($suffix)) { + $model->setSuffix($suffix); + } + + $model->replace($replace)->save($data); + + return $model; + } + + /** + * 更新数据 + * @access public + * @param array $data 数据数组 + * @param mixed $where 更新条件 + * @param array $allowField 允许字段 + * @param string $suffix 数据表后缀 + * @return static + */ + public static function update(array $data, $where = [], array $allowField = [], string $suffix = '') + { + $model = new static(); + + if (!empty($allowField)) { + $model->allowField($allowField); + } + + if (!empty($where)) { + $model->setUpdateWhere($where); + } + + if (!empty($suffix)) { + $model->setSuffix($suffix); + } + + $model->exists(true)->save($data); + + return $model; + } + + /** + * 删除记录 + * @access public + * @param mixed $data 主键列表 支持闭包查询条件 + * @param bool $force 是否强制删除 + * @return bool + */ + public static function destroy($data, bool $force = false): bool + { + if (empty($data) && 0 !== $data) { + return false; + } + + $model = new static(); + + $query = $model->db(); + + if (is_array($data) && key($data) !== 0) { + $query->where($data); + $data = null; + } elseif ($data instanceof \Closure) { + $data($query); + $data = null; + } + + $resultSet = $query->select($data); + + foreach ($resultSet as $result) { + $result->force($force)->delete(); + } + + return true; + } + + /** + * 解序列化后处理 + */ + public function __wakeup() + { + $this->initialize(); + } + + /** + * 修改器 设置数据对象的值 + * @access public + * @param string $name 名称 + * @param mixed $value 值 + * @return void + */ + public function __set(string $name, $value): void + { + $this->setAttr($name, $value); + } + + /** + * 获取器 获取数据对象的值 + * @access public + * @param string $name 名称 + * @return mixed + */ + public function __get(string $name) + { + return $this->getAttr($name); + } + + /** + * 检测数据对象的值 + * @access public + * @param string $name 名称 + * @return bool + */ + public function __isset(string $name): bool + { + return !is_null($this->getAttr($name)); + } + + /** + * 销毁数据对象的值 + * @access public + * @param string $name 名称 + * @return void + */ + public function __unset(string $name): void + { + unset($this->data[$name], $this->relation[$name]); + } + + // ArrayAccess + public function offsetSet($name, $value) + { + $this->setAttr($name, $value); + } + + public function offsetExists($name): bool + { + return $this->__isset($name); + } + + public function offsetUnset($name) + { + $this->__unset($name); + } + + public function offsetGet($name) + { + return $this->getAttr($name); + } + + /** + * 设置不使用的全局查询范围 + * @access public + * @param array $scope 不启用的全局查询范围 + * @return Query + */ + public static function withoutGlobalScope(array $scope = null) + { + $model = new static(); + + return $model->db($scope); + } + + /** + * 切换后缀进行查询 + * @access public + * @param string $suffix 切换的表后缀 + * @return Model + */ + public static function suffix(string $suffix) + { + $model = new static(); + $model->setSuffix($suffix); + + return $model; + } + + /** + * 切换数据库连接进行查询 + * @access public + * @param string $connection 数据库连接标识 + * @return Model + */ + public static function connect(string $connection) + { + $model = new static(); + $model->setConnection($connection); + + return $model; + } + + public function __call($method, $args) + { + if ('withattr' == strtolower($method)) { + return call_user_func_array([$this, 'withAttribute'], $args); + } + + return call_user_func_array([$this->db(), $method], $args); + } + + public static function __callStatic($method, $args) + { + $model = new static(); + + return call_user_func_array([$model->db(), $method], $args); + } + + /** + * 析构方法 + * @access public + */ + public function __destruct() + { + if ($this->lazySave) { + $this->save(); + } + } +} -- Gitee From f2007ddb2f4de2fbd0e966a7b52a5a408272c2e2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 9 Feb 2020 21:39:32 +0800 Subject: [PATCH 147/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BMorphTo=E5=85=B3?= =?UTF-8?q?=E8=81=94=E7=9A=84eagerlyResultSet=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/MorphTo.php | 665 ++++++++++++++++----------------- 1 file changed, 332 insertions(+), 333 deletions(-) diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index c939c1d..eaa9890 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -1,333 +1,332 @@ - -// +---------------------------------------------------------------------- - -namespace think\model\relation; - -use Closure; -use think\db\exception\DbException as Exception; -use think\helper\Str; -use think\Model; -use think\model\Relation; - -/** - * 多态关联类 - */ -class MorphTo extends Relation -{ - /** - * 多态关联外键 - * @var string - */ - protected $morphKey; - - /** - * 多态字段 - * @var string - */ - protected $morphType; - - /** - * 多态别名 - * @var array - */ - protected $alias = []; - - /** - * 关联名 - * @var string - */ - protected $relation; - - /** - * 架构函数 - * @access public - * @param Model $parent 上级模型对象 - * @param string $morphType 多态字段名 - * @param string $morphKey 外键名 - * @param array $alias 多态别名定义 - * @param string $relation 关联名 - */ - public function __construct(Model $parent, string $morphType, string $morphKey, array $alias = [], string $relation = null) - { - $this->parent = $parent; - $this->morphType = $morphType; - $this->morphKey = $morphKey; - $this->alias = $alias; - $this->relation = $relation; - } - - /** - * 获取当前的关联模型类的实例 - * @access public - * @return Model - */ - public function getModel(): Model - { - $morphType = $this->morphType; - $model = $this->parseModel($this->parent->$morphType); - - return (new $model); - } - - /** - * 延迟获取关联数据 - * @access public - * @param array $subRelation 子关联名 - * @param Closure $closure 闭包查询条件 - * @return Model - */ - public function getRelation(array $subRelation = [], Closure $closure = null) - { - $morphKey = $this->morphKey; - $morphType = $this->morphType; - - // 多态模型 - $model = $this->parseModel($this->parent->$morphType); - - // 主键数据 - $pk = $this->parent->$morphKey; - - $relationModel = (new $model)->relation($subRelation)->find($pk); - - if ($relationModel) { - $relationModel->setParent(clone $this->parent); - } - - return $relationModel; - } - - /** - * 根据关联条件查询当前模型 - * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 - * @param Query $query Query对象 - * @return Query - */ - public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null) - { - return $this->parent; - } - - /** - * 根据关联条件查询当前模型 - * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 - * @param string $joinType JOIN类型 - * @param Query $query Query对象 - * @return Query - */ - public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null) - { - throw new Exception('relation not support: hasWhere'); - } - - /** - * 解析模型的完整命名空间 - * @access protected - * @param string $model 模型名(或者完整类名) - * @return string - */ - protected function parseModel(string $model): string - { - 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, Str::studly($model)); - $model = implode('\\', $path); - } - - return $model; - } - - /** - * 设置多态别名 - * @access public - * @param array $alias 别名定义 - * @return $this - */ - public function setAlias(array $alias) - { - $this->alias = $alias; - - return $this; - } - - /** - * 移除关联查询参数 - * @access public - * @return $this - */ - public function removeOption() - { - return $this; - } - - /** - * 预载入关联查询 - * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param Closure $closure 闭包 - * @param array $cache 关联缓存 - * @return void - * @throws Exception - */ - public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void - { - $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->with($subRelation) - ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) - ->select($val); - $data = []; - - foreach ($list as $k => $vo) { - $data[$vo->$pk] = $vo; - } - - foreach ($resultSet as $result) { - if ($key == $result->$morphType) { - // 关联模型 - if (!isset($data[$result->$morphKey])) { - $relationModel = null; - throw new Exception('relation data not exists :' . $this->model); - } else { - $relationModel = $data[$result->$morphKey]; - $relationModel->setParent(clone $result); - $relationModel->exists(true); - } - - $result->setRelation($relation, $relationModel); - } - } - } - } - } - - /** - * 预载入关联查询 - * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param Closure $closure 闭包 - * @param array $cache 关联缓存 - * @return void - */ - public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void - { - // 多态类型映射 - $model = $this->parseModel($result->{$this->morphType}); - - $this->eagerlyMorphToOne($model, $relation, $result, $subRelation, $cache); - } - - /** - * 关联统计 - * @access public - * @param Model $result 数据对象 - * @param Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 - * @return integer - */ - public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*') - {} - - /** - * 多态MorphTo 关联模型预查询 - * @access protected - * @param string $model 关联模型对象 - * @param string $relation 关联名 - * @param Model $result - * @param array $subRelation 子关联 - * @param array $cache 关联缓存 - * @return void - */ - protected function eagerlyMorphToOne(string $model, string $relation, Model $result, array $subRelation = [], array $cache = []): void - { - // 预载入关联查询 支持嵌套预载入 - $pk = $this->parent->{$this->morphKey}; - $data = (new $model)->with($subRelation) - ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) - ->find($pk); - - if ($data) { - $data->setParent(clone $result); - $data->exists(true); - } - - $result->setRelation($relation, $data ?: null); - } - - /** - * 添加关联数据 - * @access public - * @param Model $model 关联模型对象 - * @param string $type 多态类型 - * @return Model - */ - public function associate(Model $model, string $type = ''): Model - { - $morphKey = $this->morphKey; - $morphType = $this->morphType; - $pk = $model->getPk(); - - $this->parent->setAttr($morphKey, $model->$pk); - $this->parent->setAttr($morphType, $type ?: get_class($model)); - $this->parent->save(); - - return $this->parent->setRelation($this->relation, $model); - } - - /** - * 注销关联数据 - * @access public - * @return Model - */ - public function dissociate(): Model - { - $morphKey = $this->morphKey; - $morphType = $this->morphType; - - $this->parent->setAttr($morphKey, null); - $this->parent->setAttr($morphType, null); - $this->parent->save(); - - return $this->parent->setRelation($this->relation, null); - } - -} + +// +---------------------------------------------------------------------- + +namespace think\model\relation; + +use Closure; +use think\db\exception\DbException as Exception; +use think\helper\Str; +use think\Model; +use think\model\Relation; + +/** + * 多态关联类 + */ +class MorphTo extends Relation +{ + /** + * 多态关联外键 + * @var string + */ + protected $morphKey; + + /** + * 多态字段 + * @var string + */ + protected $morphType; + + /** + * 多态别名 + * @var array + */ + protected $alias = []; + + /** + * 关联名 + * @var string + */ + protected $relation; + + /** + * 架构函数 + * @access public + * @param Model $parent 上级模型对象 + * @param string $morphType 多态字段名 + * @param string $morphKey 外键名 + * @param array $alias 多态别名定义 + * @param string $relation 关联名 + */ + public function __construct(Model $parent, string $morphType, string $morphKey, array $alias = [], string $relation = null) + { + $this->parent = $parent; + $this->morphType = $morphType; + $this->morphKey = $morphKey; + $this->alias = $alias; + $this->relation = $relation; + } + + /** + * 获取当前的关联模型类的实例 + * @access public + * @return Model + */ + public function getModel(): Model + { + $morphType = $this->morphType; + $model = $this->parseModel($this->parent->$morphType); + + return (new $model); + } + + /** + * 延迟获取关联数据 + * @access public + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包查询条件 + * @return Model + */ + public function getRelation(array $subRelation = [], Closure $closure = null) + { + $morphKey = $this->morphKey; + $morphType = $this->morphType; + + // 多态模型 + $model = $this->parseModel($this->parent->$morphType); + + // 主键数据 + $pk = $this->parent->$morphKey; + + $relationModel = (new $model)->relation($subRelation)->find($pk); + + if ($relationModel) { + $relationModel->setParent(clone $this->parent); + } + + return $relationModel; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 + * @param Query $query Query对象 + * @return Query + */ + public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null) + { + return $this->parent; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 + * @param Query $query Query对象 + * @return Query + */ + public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null) + { + throw new Exception('relation not support: hasWhere'); + } + + /** + * 解析模型的完整命名空间 + * @access protected + * @param string $model 模型名(或者完整类名) + * @return string + */ + protected function parseModel(string $model): string + { + 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, Str::studly($model)); + $model = implode('\\', $path); + } + + return $model; + } + + /** + * 设置多态别名 + * @access public + * @param array $alias 别名定义 + * @return $this + */ + public function setAlias(array $alias) + { + $this->alias = $alias; + + return $this; + } + + /** + * 移除关联查询参数 + * @access public + * @return $this + */ + public function removeOption() + { + return $this; + } + + /** + * 预载入关联查询 + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 + * @param array $cache 关联缓存 + * @return void + * @throws Exception + */ + public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void + { + $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->with($subRelation) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) + ->select($val); + $data = []; + + foreach ($list as $k => $vo) { + $data[$vo->$pk] = $vo; + } + + foreach ($resultSet as $result) { + if ($key == $result->$morphType) { + // 关联模型 + if (!isset($data[$result->$morphKey])) { + $relationModel = null; + } else { + $relationModel = $data[$result->$morphKey]; + $relationModel->setParent(clone $result); + $relationModel->exists(true); + } + + $result->setRelation($relation, $relationModel); + } + } + } + } + } + + /** + * 预载入关联查询 + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param Closure $closure 闭包 + * @param array $cache 关联缓存 + * @return void + */ + public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void + { + // 多态类型映射 + $model = $this->parseModel($result->{$this->morphType}); + + $this->eagerlyMorphToOne($model, $relation, $result, $subRelation, $cache); + } + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @return integer + */ + public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*') + {} + + /** + * 多态MorphTo 关联模型预查询 + * @access protected + * @param string $model 关联模型对象 + * @param string $relation 关联名 + * @param Model $result + * @param array $subRelation 子关联 + * @param array $cache 关联缓存 + * @return void + */ + protected function eagerlyMorphToOne(string $model, string $relation, Model $result, array $subRelation = [], array $cache = []): void + { + // 预载入关联查询 支持嵌套预载入 + $pk = $this->parent->{$this->morphKey}; + $data = (new $model)->with($subRelation) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) + ->find($pk); + + if ($data) { + $data->setParent(clone $result); + $data->exists(true); + } + + $result->setRelation($relation, $data ?: null); + } + + /** + * 添加关联数据 + * @access public + * @param Model $model 关联模型对象 + * @param string $type 多态类型 + * @return Model + */ + public function associate(Model $model, string $type = ''): Model + { + $morphKey = $this->morphKey; + $morphType = $this->morphType; + $pk = $model->getPk(); + + $this->parent->setAttr($morphKey, $model->$pk); + $this->parent->setAttr($morphType, $type ?: get_class($model)); + $this->parent->save(); + + return $this->parent->setRelation($this->relation, $model); + } + + /** + * 注销关联数据 + * @access public + * @return Model + */ + public function dissociate(): Model + { + $morphKey = $this->morphKey; + $morphType = $this->morphType; + + $this->parent->setAttr($morphKey, null); + $this->parent->setAttr($morphType, null); + $this->parent->save(); + + return $this->parent->setRelation($this->relation, null); + } + +} -- Gitee From 6de4752265c7cd5579fd4a2c68cac796d312126a Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 4 Mar 2020 22:19:31 +0800 Subject: [PATCH 148/365] =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E7=B1=BB=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0macro=E6=96=B9=E6=B3=95=E7=94=A8=E4=BA=8E=E6=B3=A8?= =?UTF-8?q?=E5=85=A5=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Model.php b/src/Model.php index dfb818a..76e26d8 100644 --- a/src/Model.php +++ b/src/Model.php @@ -139,6 +139,12 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab */ protected static $maker = []; + /** + * 方法注入 + * @var Closure[] + */ + protected static $macro = []; + /** * 设置服务注入 * @access public @@ -150,6 +156,18 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab static::$maker[] = $maker; } + /** + * 设置方法注入 + * @access public + * @param string $method + * @param Closure $closure + * @return void + */ + public static function macro(string $method, Closure $closure) + { + static::$macro[$method] = $closure; + } + /** * 设置Db对象 * @access public @@ -1025,6 +1043,10 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab public function __call($method, $args) { + if (isset(static::$macro[$method])) { + return call_user_func_array(static::$macro[$method], $args); + } + if ('withattr' == strtolower($method)) { return call_user_func_array([$this, 'withAttribute'], $args); } @@ -1034,6 +1056,10 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab public static function __callStatic($method, $args) { + if (isset(static::$macro[$method])) { + return call_user_func_array(static::$macro[$method], $args); + } + $model = new static(); return call_user_func_array([$model->db(), $method], $args); -- Gitee From 8dbe81233505734c087f46d504c362b3300452d4 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Thu, 5 Mar 2020 11:54:46 +0800 Subject: [PATCH 149/365] =?UTF-8?q?=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Model.php b/src/Model.php index 76e26d8..5f3da74 100644 --- a/src/Model.php +++ b/src/Model.php @@ -1044,7 +1044,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab public function __call($method, $args) { if (isset(static::$macro[$method])) { - return call_user_func_array(static::$macro[$method], $args); + return call_user_func_array(static::$macro[$method]->bindTo($this, static::class), $args); } if ('withattr' == strtolower($method)) { @@ -1057,7 +1057,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab public static function __callStatic($method, $args) { if (isset(static::$macro[$method])) { - return call_user_func_array(static::$macro[$method], $args); + return call_user_func_array(static::$macro[$method]->bindTo(null, static::class), $args); } $model = new static(); -- Gitee From 413b448af660a29b9f90d470b8c20241d5ba6961 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Wed, 18 Mar 2020 19:18:03 +0800 Subject: [PATCH 150/365] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=85=B3=E8=81=94?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/RelationShip.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 4839228..feb9748 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -720,7 +720,7 @@ trait RelationShip { $relation = Str::camel($attr); - if (method_exists($this, $relation) && !method_exists('think\Model', $relation)) { + if ((method_exists($this, $relation) && !method_exists('think\Model', $relation)) || isset(static::$macro[$relation])) { return $relation; } -- Gitee From bd2a3ee9b84659b73a73524f6ff5d10a09565229 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Wed, 18 Mar 2020 19:27:15 +0800 Subject: [PATCH 151/365] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ResultOperation.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index f761d1e..d93c409 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -12,10 +12,14 @@ declare (strict_types = 1); namespace think\db\concern; +use Closure; use think\Collection; use think\db\exception\DataNotFoundException; +use think\db\exception\DbException; use think\db\exception\ModelNotFoundException; +use think\db\Query; use think\helper\Str; +use think\Model; /** * 查询数据处理 @@ -103,6 +107,7 @@ trait ResultOperation */ protected function filterResult(&$result): void { + $array = []; if (!empty($this->options['visible'])) { foreach ($this->options['visible'] as $key) { $array[] = $key; @@ -222,9 +227,6 @@ trait ResultOperation * @access public * @param array|string|Query|Closure $data 数据 * @return array|Model - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException */ public function selectOrFail($data = null) { @@ -236,9 +238,6 @@ trait ResultOperation * @access public * @param array|string|Query|Closure $data 数据 * @return array|Model - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException */ public function findOrFail($data = null) { -- Gitee From a14f49221d13b5df667da61ca14b833ccf95a252 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 18 Mar 2020 20:54:40 +0800 Subject: [PATCH 152/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3value=E5=92=8Cfind?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E6=9F=A5=E8=AF=A2=E7=BC=93=E5=AD=98=E6=B7=B7?= =?UTF-8?q?=E6=B7=86=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Connection.php | 12 +++++++----- src/db/PDOConnection.php | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/db/Connection.php b/src/db/Connection.php index fb9f74b..c0ee745 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -214,11 +214,12 @@ abstract class Connection * 分析缓存Key * @access protected * @param BaseQuery $query 查询对象 + * @param string $method查询方法 * @return string */ - protected function getCacheKey(BaseQuery $query): string + protected function getCacheKey(BaseQuery $query, string $method = ''): string { - if (!empty($query->getOptions('key'))) { + if (!empty($query->getOptions('key')) && empty($method)) { $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); } else { $key = $query->getQueryGuid(); @@ -231,10 +232,11 @@ abstract class Connection * 分析缓存 * @access protected * @param BaseQuery $query 查询对象 - * @param array $cache 缓存信息 + * @param array $cache 缓存信息 + * @param string $method查询方法 * @return CacheItem */ - protected function parseCache(BaseQuery $query, array $cache): CacheItem + protected function parseCache(BaseQuery $query, array $cache, string $method = ''): CacheItem { [$key, $expire, $tag] = $cache; @@ -242,7 +244,7 @@ abstract class Connection $cacheItem = $key; } else { if (true === $key) { - $key = $this->getCacheKey($query); + $key = $this->getCacheKey($query, $method); } $cacheItem = new CacheItem($key); diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 0d23b83..1373fae 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1033,7 +1033,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface $query->setOption('field', (array) $field); if (!empty($options['cache'])) { - $cacheItem = $this->parseCache($query, $options['cache']); + $cacheItem = $this->parseCache($query, $options['cache'], 'value'); $key = $cacheItem->getKey(); if ($this->cache->has($key)) { @@ -1118,7 +1118,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface if (!empty($options['cache'])) { // 判断查询缓存 - $cacheItem = $this->parseCache($query, $options['cache']); + $cacheItem = $this->parseCache($query, $options['cache'], 'column'); $name = $cacheItem->getKey(); if ($this->cache->has($name)) { -- Gitee From ac7db81987ba6abf9c849f4e580f10b0ad431548 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 20 Mar 2020 14:20:21 +0800 Subject: [PATCH 153/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BHasMany=E5=85=B3?= =?UTF-8?q?=E8=81=94=E7=9A=84hasWhere=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/HasMany.php | 2 +- src/model/relation/HasManyThrough.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index aa46a88..a67d41b 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -340,7 +340,7 @@ class HasMany extends Relation return $query->group($model . '.' . $this->localKey) ->field($fields) - ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey) + ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType) ->when($softDelete, function ($query) use ($softDelete, $relation) { $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); }) diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index 23367f3..30d5ca4 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -154,7 +154,7 @@ class HasManyThrough extends Relation $query = $query ?: $this->parent->db()->alias($model); return $query->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey) - ->join($modelTable, $modelTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk) + ->join($modelTable, $modelTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk, $joinType) ->when($softDelete, function ($query) use ($softDelete, $modelTable) { $query->where($modelTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); }) -- Gitee From 0a49e735c811aa20f48eac93e2572f8c4a2fe347 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 24 Mar 2020 22:39:05 +0800 Subject: [PATCH 154/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E5=99=A8=E6=96=B9=E6=B3=95=20=E7=AC=AC=E4=BA=8C=E4=B8=AA?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=94=AF=E6=8C=81=E4=BC=A0=E5=85=A5Request?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=20=E5=A2=9E=E5=8A=A0match=5Flike=5Ffields?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E8=AE=BE=E7=BD=AE=E6=90=9C=E7=B4=A2=E5=99=A8?= =?UTF-8?q?like=E6=9F=A5=E8=AF=A2=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 92e4417..ffb72de 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -140,13 +140,19 @@ trait ModelRelationQuery /** * 使用搜索器条件搜索字段 * @access public - * @param array $fields 搜索字段 - * @param array $data 搜索数据 - * @param string $prefix 字段前缀标识 + * @param string|array $fields 搜索字段 + * @param mixed $data 搜索数据 + * @param string $prefix 字段前缀标识 * @return $this */ - public function withSearch(array $fields, array $data = [], string $prefix = '') + public function withSearch($fields, $data = [], string $prefix = '') { + if (is_string($fields)) { + $fields = explode(',', $fields); + } + + $likeFields = $this->getConfig('match_like_fields') ?: []; + foreach ($fields as $key => $field) { if ($field instanceof Closure) { $field($this, $data[$key] ?? null, $data, $prefix); @@ -157,6 +163,8 @@ trait ModelRelationQuery if (method_exists($this->model, $method)) { $this->model->$method($this, $data[$field] ?? null, $data, $prefix); + } elseif (isset($data[$field])) { + $this->where($fieldName, in_array($fieldName, $likeFields) ? 'like' : '=', in_array($fieldName, $likeFields) ? '%' . $data[$field] . '%' : $data[$field]); } } } -- Gitee From a5f23df0a16f4b41c3a97fcae35e9620d4968bc2 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Mon, 6 Apr 2020 23:57:36 +0800 Subject: [PATCH 155/365] =?UTF-8?q?=E5=AE=8C=E5=96=84macro=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 17 ++++++++++------- src/model/concern/RelationShip.php | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Model.php b/src/Model.php index 5f3da74..3e49c3d 100644 --- a/src/Model.php +++ b/src/Model.php @@ -141,7 +141,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab /** * 方法注入 - * @var Closure[] + * @var Closure[][] */ protected static $macro = []; @@ -159,13 +159,16 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab /** * 设置方法注入 * @access public - * @param string $method + * @param string $method * @param Closure $closure * @return void */ public static function macro(string $method, Closure $closure) { - static::$macro[$method] = $closure; + if (!isset(static::$macro[static::class])) { + static::$macro[static::class] = [] + } + static::$macro[static::class][$method] = $closure; } /** @@ -1043,8 +1046,8 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab public function __call($method, $args) { - if (isset(static::$macro[$method])) { - return call_user_func_array(static::$macro[$method]->bindTo($this, static::class), $args); + if (isset(static::$macro[static::class][$method])) { + return call_user_func_array(static::$macro[static::class][$method]->bindTo($this, static::class), $args); } if ('withattr' == strtolower($method)) { @@ -1056,8 +1059,8 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab public static function __callStatic($method, $args) { - if (isset(static::$macro[$method])) { - return call_user_func_array(static::$macro[$method]->bindTo(null, static::class), $args); + if (isset(static::$macro[static::class][$method])) { + return call_user_func_array(static::$macro[static::class][$method]->bindTo(null, static::class), $args); } $model = new static(); diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index feb9748..f3da1c4 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -720,7 +720,7 @@ trait RelationShip { $relation = Str::camel($attr); - if ((method_exists($this, $relation) && !method_exists('think\Model', $relation)) || isset(static::$macro[$relation])) { + if ((method_exists($this, $relation) && !method_exists('think\Model', $relation)) || isset(static::$macro[static::class][$relation])) { return $relation; } -- Gitee From 58f0565c6231bc87f287a8aea5a6cf8cf830cf83 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Tue, 7 Apr 2020 00:02:33 +0800 Subject: [PATCH 156/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model.php b/src/Model.php index 3e49c3d..9ef8fb1 100644 --- a/src/Model.php +++ b/src/Model.php @@ -166,7 +166,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab public static function macro(string $method, Closure $closure) { if (!isset(static::$macro[static::class])) { - static::$macro[static::class] = [] + static::$macro[static::class] = []; } static::$macro[static::class][$method] = $closure; } -- Gitee From 5f24235ecb051c0fac304b442a54cd850cc2e5b1 Mon Sep 17 00:00:00 2001 From: "Tiger.Lian" <75706802@qq.com> Date: Tue, 7 Apr 2020 11:48:18 +0800 Subject: [PATCH 157/365] =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E5=AD=90=E7=B1=BBcreate|update=E9=87=8D=E8=BD=BD=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Model.php b/src/Model.php index 9ef8fb1..65ee831 100644 --- a/src/Model.php +++ b/src/Model.php @@ -784,9 +784,9 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab foreach ($dataSet as $key => $data) { if ($this->exists || (!empty($auto) && isset($data[$pk]))) { - $result[$key] = self::update($data, [], [], $suffix); + $result[$key] = static::update($data, [], [], $suffix); } else { - $result[$key] = self::create($data, $this->field, $this->replace, $suffix); + $result[$key] = static::create($data, $this->field, $this->replace, $suffix); } } -- Gitee From b28c7b624ee93f721317c6647953a5da7f468648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=92=8B=E6=96=87=E5=81=A5?= Date: Wed, 26 Feb 2020 10:32:01 +0800 Subject: [PATCH 158/365] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=B8=80=E5=A4=84PHP?= =?UTF-8?q?7.4=E5=85=BC=E5=AE=B9=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function ReflectionType::__toString() is deprecated `Relation::class`为字符型,与`$type`比较时,会触发`__toString()` --- src/model/Relation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/Relation.php b/src/model/Relation.php index 12ab8a8..d9d02e3 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -228,7 +228,7 @@ abstract class Relation if (!empty($params)) { $type = $params[0]->getType(); - return Relation::class == $type || is_null($type) ? $this : $this->query; + return is_null($type) || Relation::class == $type->getName() ? $this : $this->query; } return $this; -- Gitee From be6e37be4a7f2fa5b5ab15bcc406e25767bc7ecc Mon Sep 17 00:00:00 2001 From: auooru Date: Thu, 9 Apr 2020 18:41:31 +0800 Subject: [PATCH 159/365] =?UTF-8?q?fix:=20where=E7=A9=BA=E9=97=AD=E5=8C=85?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=94=9F=E6=88=90=E8=AF=AD=E5=8F=A5=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Builder.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/db/Builder.php b/src/db/Builder.php index 1b01c98..67cf3b3 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -368,7 +368,10 @@ abstract class Builder if ($value instanceof Closure) { // 使用闭包查询 - $where[] = $this->parseClosureWhere($query, $value, $logic); + $whereClosureStr = $this->parseClosureWhere($query, $value, $logic); + if ($whereClosureStr) { + $where[] = $whereClosureStr; + } } elseif (is_array($field)) { $where[] = $this->parseMultiWhereField($query, $value, $field, $logic, $binds); } elseif ($field instanceof Raw) { -- Gitee From dafeedadc95806e4fdca6ffa1d885f1402d155e4 Mon Sep 17 00:00:00 2001 From: auooru Date: Tue, 7 Apr 2020 16:35:56 +0800 Subject: [PATCH 160/365] =?UTF-8?q?fix:=20SoftDelete::destroy()=20?= =?UTF-8?q?=E7=9C=9F=E5=AE=9E=E5=88=A0=E9=99=A4=E6=97=A0=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/SoftDelete.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/model/concern/SoftDelete.php b/src/model/concern/SoftDelete.php index 7357bc5..ce5d392 100644 --- a/src/model/concern/SoftDelete.php +++ b/src/model/concern/SoftDelete.php @@ -13,9 +13,11 @@ declare (strict_types = 1); namespace think\model\concern; use think\db\BaseQuery as Query; +use think\Model; /** * 数据软删除 + * @mixin Model */ trait SoftDelete { @@ -149,7 +151,7 @@ trait SoftDelete public static function destroy($data, bool $force = false): bool { // 包含软删除数据 - $query = (new static())->db(false); + $query = (new static())->withTrashedData(true)->db(false); if (is_array($data) && key($data) !== 0) { $query->where($data); -- Gitee From 49014b960c2f92267d1e270010357557fad26c44 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 26 Apr 2020 21:14:53 +0800 Subject: [PATCH 161/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BBelongsToMany?= =?UTF-8?q?=E5=85=B3=E8=81=94=E7=9A=84find=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsToMany.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 69888c9..98d4aff 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -222,7 +222,7 @@ class BelongsToMany extends Relation */ public function find($data = null) { - $result = $this->buildQuery()->find($data); + $result = $this->buildQuery()->findOrEmpty($data); if (!$result->isEmpty()) { $this->hydratePivot([$result]); -- Gitee From 03aaaa4d8c4475115b3acaa5aa2498bf5792e017 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 26 Apr 2020 21:54:48 +0800 Subject: [PATCH 162/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bcolumn=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E6=96=B9=E6=B3=95=E5=AF=B9=E5=88=AB=E5=90=8D=E7=9A=84?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 1373fae..5f1fa7d 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1151,6 +1151,8 @@ abstract class PDOConnection extends Connection implements ConnectionInterface if (strpos($column, ',')) { $column = null; + } elseif (strpos($column, ' ')) { + $column = substr(strrchr(trim($column), ' '), 1); } elseif (strpos($column, '.')) { [$alias, $column] = explode('.', $column); } -- Gitee From b1852f7e98d3e2a41571ea20a465aaada74a1b75 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 2 May 2020 20:13:05 +0800 Subject: [PATCH 163/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BBelongsToMany?= =?UTF-8?q?=E5=85=B3=E8=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsToMany.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 98d4aff..2b61d4f 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -695,4 +695,14 @@ class BelongsToMany extends Relation return $changes; } + public function __call($method, $args) + { + if ($this->query) { + $result = call_user_func_array([$this->buildQuery(), $method], $args); + + return $result === $this->query ? $this : $result; + } + + throw new Exception('method not exists:' . __CLASS__ . '->' . $method); + } } -- Gitee From a99303a48052df09c50d184c003692711457e001 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 6 May 2020 23:07:37 +0800 Subject: [PATCH 164/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3whereTime=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E5=AF=B9date=E7=B1=BB=E5=9E=8B=E7=9A=84=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 2 ++ src/model/relation/BelongsToMany.php | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 5f1fa7d..9fdd2b5 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -285,6 +285,8 @@ abstract class PDOConnection extends Connection implements ConnectionInterface $result = 'timestamp'; } elseif (0 === strpos($type, 'datetime')) { $result = 'datetime'; + } elseif (0 === strpos($type, 'date')) { + $result = 'date'; } else { $result = 'string'; } diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 2b61d4f..ae542bb 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -222,9 +222,9 @@ class BelongsToMany extends Relation */ public function find($data = null) { - $result = $this->buildQuery()->findOrEmpty($data); + $result = $this->buildQuery()->find($data); - if (!$result->isEmpty()) { + if ($result && !$result->isEmpty()) { $this->hydratePivot([$result]); } -- Gitee From 25cf1c371a702a6a4d8acf5f524ed1c44400f04b Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 6 May 2020 23:38:44 +0800 Subject: [PATCH 165/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E6=96=87=E4=BB=B6=E5=A2=9E=E5=8A=A0=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E5=AD=90=E7=9B=AE=E5=BD=95=20=E5=B9=B6?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=87=AA=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 9fdd2b5..bcb8139 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -78,6 +78,8 @@ abstract class PDOConnection extends Connection implements ConnectionInterface 'break_match_str' => [], // 字段缓存路径 'schema_cache_path' => '', + // 字段缓存规则 支持闭包 + 'schema_cache_rule' => '', ]; /** @@ -319,6 +321,24 @@ abstract class PDOConnection extends Connection implements ConnectionInterface return $bind; } + /** + * 获取数据表信息缓存文件名 + * @access protected + * @param string $schema 数据表名称 + * @return string + */ + protected function getSchemaFileName(string $schema): string + { + if (!empty($this->config['schema_cache_rule']) && is_callable($this->config['schema_cache_rule'])) { + $rule = $this->config['schema_cache_rule']; + $name = $rule($schema); + } else { + $name = str_replace('.', DIRECTORY_SEPARATOR, $schema); + } + + return $this->config['schema_cache_path'] . $name . '.php'; + } + /** * 获取数据表信息 * @access public @@ -347,15 +367,16 @@ abstract class PDOConnection extends Connection implements ConnectionInterface if (!isset($this->info[$schema])) { // 读取字段缓存 - $cacheFile = $this->config['schema_cache_path'] . $schema . '.php'; + $cacheFile = $this->getSchemaFileName($schema); if ($this->config['fields_cache'] && is_file($cacheFile)) { $info = include $cacheFile; } else { $info = $this->getTableFieldsInfo($tableName); if ($this->config['fields_cache']) { - if (!is_dir($this->config['schema_cache_path'])) { - mkdir($this->config['schema_cache_path'], 0755, true); + $path = dirname($cacheFile); + if (!is_dir($path)) { + mkdir($path, 0755, true); } $content = ' Date: Thu, 7 May 2020 13:59:43 +0800 Subject: [PATCH 166/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E4=B8=80=E5=AF=B9?= =?UTF-8?q?=E4=B8=80=E5=85=B3=E8=81=94=E7=9A=84=E5=B1=9E=E6=80=A7=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=E5=BD=93=E5=85=B3=E8=81=94=E6=95=B0=E6=8D=AE=E4=B8=8D?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E7=9A=84=E6=97=B6=E5=80=99=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=A0=BC=E5=BC=8F=E7=9A=84=E4=B8=80=E8=87=B4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsTo.php | 10 +++++----- src/model/relation/HasOne.php | 10 +++++----- src/model/relation/OneToOne.php | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 76c7019..789c944 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -69,7 +69,7 @@ class BelongsTo extends OneToOne if ($relationModel) { if (!empty($this->bindAttr)) { // 绑定关联属性 - $this->bindAttr($relationModel, $this->parent); + $this->bindAttr($this->parent, $relationModel); } $relationModel->setParent(clone $this->parent); @@ -233,9 +233,9 @@ class BelongsTo extends OneToOne $relationModel->exists(true); } - if ($relationModel && !empty($this->bindAttr)) { + if (!empty($this->bindAttr)) { // 绑定关联属性 - $this->bindAttr($relationModel, $result); + $this->bindAttr($result, $relationModel); } else { // 设置关联属性 $result->setRelation($relation, $relationModel); @@ -274,9 +274,9 @@ class BelongsTo extends OneToOne $relationModel->exists(true); } - if ($relationModel && !empty($this->bindAttr)) { + if (!empty($this->bindAttr)) { // 绑定关联属性 - $this->bindAttr($relationModel, $result); + $this->bindAttr($result, $relationModel); } else { // 设置关联属性 $result->setRelation($relation, $relationModel); diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 98bdf89..d0dc21d 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -68,7 +68,7 @@ class HasOne extends OneToOne if ($relationModel) { if (!empty($this->bindAttr)) { // 绑定关联属性 - $this->bindAttr($relationModel, $this->parent); + $this->bindAttr($this->parent, $relationModel); } $relationModel->setParent(clone $this->parent); @@ -232,9 +232,9 @@ class HasOne extends OneToOne $relationModel->exists(true); } - if ($relationModel && !empty($this->bindAttr)) { + if (!empty($this->bindAttr)) { // 绑定关联属性 - $this->bindAttr($relationModel, $result); + $this->bindAttr($result, $relationModel); } else { // 设置关联属性 $result->setRelation($relation, $relationModel); @@ -273,9 +273,9 @@ class HasOne extends OneToOne $relationModel->exists(true); } - if ($relationModel && !empty($this->bindAttr)) { + if (!empty($this->bindAttr)) { // 绑定关联属性 - $this->bindAttr($relationModel, $result); + $this->bindAttr($result, $relationModel); } else { $result->setRelation($relation, $relationModel); } diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index 540fcc2..efb2359 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -253,8 +253,8 @@ abstract class OneToOne extends Relation $relationModel->exists(true); } - if ($relationModel && !empty($this->bindAttr)) { - $this->bindAttr($relationModel, $result); + if (!empty($this->bindAttr)) { + $this->bindAttr($result, $relationModel); } } else { $relationModel = null; @@ -266,12 +266,12 @@ abstract class OneToOne extends Relation /** * 绑定关联属性到父模型 * @access protected - * @param Model $model 关联模型对象 * @param Model $result 父模型对象 + * @param Model $model 关联模型对象 * @return void * @throws Exception */ - protected function bindAttr(Model $model, Model $result): void + protected function bindAttr(Model $result, Model $model = null): void { foreach ($this->bindAttr as $key => $attr) { $key = is_numeric($key) ? $attr : $key; -- Gitee From d215f2af895ae644aca46688fa57a298f779f077 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 9 May 2020 13:59:34 +0800 Subject: [PATCH 167/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bmysql=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/connector/Mysql.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/db/connector/Mysql.php b/src/db/connector/Mysql.php index fd0d544..483b447 100644 --- a/src/db/connector/Mysql.php +++ b/src/db/connector/Mysql.php @@ -74,10 +74,10 @@ class Mysql extends PDOConnection $info[$val['field']] = [ 'name' => $val['field'], 'type' => $val['type'], - 'notnull' => (bool) ('' === $val['null']), // not null is empty, null is yes + 'notnull' => 'NO' == $val['null'], 'default' => $val['default'], - 'primary' => (strtolower($val['key']) == 'pri'), - 'autoinc' => (strtolower($val['extra']) == 'auto_increment'), + 'primary' => strtolower($val['key']) == 'pri', + 'autoinc' => strtolower($val['extra']) == 'auto_increment', 'comment' => $val['comment'], ]; } -- Gitee From 9b5ccef989bd089663321577bc563d364d6e5c7d Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 9 May 2020 22:24:37 +0800 Subject: [PATCH 168/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=A4=9A=E5=AF=B9?= =?UTF-8?q?=E5=A4=9A=E5=85=B3=E8=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsToMany.php | 78 ++++++++++------------------ src/model/relation/MorphToMany.php | 16 ------ 2 files changed, 26 insertions(+), 68 deletions(-) diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index ae542bb..798e714 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -146,22 +146,6 @@ class BelongsToMany extends Relation } } - /** - * 创建关联查询Query对象 - * @access protected - * @return Query - */ - protected function buildQuery(): Query - { - $foreignKey = $this->foreignKey; - $localKey = $this->localKey; - - // 关联查询 - $condition = ['pivot.' . $localKey, '=', $this->parent->getKey()]; - - return $this->belongsToManyQuery($foreignKey, $localKey, [$condition]); - } - /** * 延迟获取关联数据 * @access public @@ -175,8 +159,7 @@ class BelongsToMany extends Relation $closure($this->getClosureType($closure)); } - $result = $this->buildQuery() - ->relation($subRelation) + $result = $this->relation($subRelation) ->select() ->setParent(clone $this->parent); @@ -193,7 +176,8 @@ class BelongsToMany extends Relation */ public function select($data = null): Collection { - $result = $this->buildQuery()->select($data); + $this->baseQuery(); + $result = $this->query->select($data); $this->hydratePivot($result); return $result; @@ -208,7 +192,8 @@ class BelongsToMany extends Relation */ public function paginate($listRows = null, $simple = false): Paginator { - $result = $this->buildQuery()->paginate($listRows, $simple); + $this->baseQuery(); + $result = $this->query->paginate($listRows, $simple); $this->hydratePivot($result); return $result; @@ -222,7 +207,8 @@ class BelongsToMany extends Relation */ public function find($data = null) { - $result = $this->buildQuery()->find($data); + $this->baseQuery(); + $result = $this->query->find($data); if ($result && !$result->isEmpty()) { $this->hydratePivot([$result]); @@ -231,28 +217,6 @@ class BelongsToMany extends Relation return $result; } - /** - * 查找多条记录 如果不存在则抛出异常 - * @access public - * @param array|string|Query|\Closure $data - * @return Collection - */ - public function selectOrFail($data = null): Collection - { - return $this->buildQuery()->failException(true)->select($data); - } - - /** - * 查找单条记录 如果不存在则抛出异常 - * @access public - * @param array|string|Query|\Closure $data - * @return Model - */ - public function findOrFail($data = null): Model - { - return $this->buildQuery()->failException(true)->find($data); - } - /** * 根据关联条件查询当前模型 * @access public @@ -485,17 +449,17 @@ class BelongsToMany extends Relation $this->query->limit($this->withLimit); } - $query = $this->query + $this->query ->field($fields) ->tableField(true, $table, 'pivot', 'pivot__'); if (empty($this->baseQuery)) { $relationFk = $this->query->getPk(); - $query->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk) + $this->query->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk) ->where($condition); } - return $query; + return $this->query; } /** @@ -695,14 +659,24 @@ class BelongsToMany extends Relation return $changes; } - public function __call($method, $args) + /** + * 执行基础查询(仅执行一次) + * @access protected + * @return void + */ + protected function baseQuery(): void { - if ($this->query) { - $result = call_user_func_array([$this->buildQuery(), $method], $args); + if (empty($this->baseQuery)) { + $foreignKey = $this->foreignKey; + $localKey = $this->localKey; - return $result === $this->query ? $this : $result; - } + // 关联查询 + $condition = ['pivot.' . $localKey, '=', $this->parent->getKey()]; + + $this->belongsToManyQuery($foreignKey, $localKey, [$condition]); - throw new Exception('method not exists:' . __CLASS__ . '->' . $method); + $this->baseQuery = true; + } } + } diff --git a/src/model/relation/MorphToMany.php b/src/model/relation/MorphToMany.php index 7b8438b..9da207b 100644 --- a/src/model/relation/MorphToMany.php +++ b/src/model/relation/MorphToMany.php @@ -438,22 +438,6 @@ class MorphToMany extends BelongsToMany return $changes; } - /** - * 创建关联查询Query对象 - * @access protected - * @return Query - */ - protected function buildQuery(): Query - { - // 关联查询 - $condition = [ - ['pivot.' . $this->morphKey, '=', $this->parent->getKey()], - ['pivot.' . $this->morphType, '=', $this->morphClass], - ]; - - return $this->belongsToManyQuery($this->morphKey, $this->localKey, $condition); - } - /** * 执行基础查询(仅执行一次) * @access protected -- Gitee From 9e7f8234122c5e3e36f54287460fb71a022ddddb Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 9 May 2020 22:54:26 +0800 Subject: [PATCH 169/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsToMany.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 798e714..08072ca 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -451,13 +451,9 @@ class BelongsToMany extends Relation $this->query ->field($fields) - ->tableField(true, $table, 'pivot', 'pivot__'); - - if (empty($this->baseQuery)) { - $relationFk = $this->query->getPk(); - $this->query->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk) - ->where($condition); - } + ->tableField(true, $table, 'pivot', 'pivot__') + ->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $this->query->getPk()) + ->where($condition); return $this->query; } -- Gitee From ce6375507be9746ee5316c8945cc7647458e206a Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Thu, 14 May 2020 16:18:47 +0800 Subject: [PATCH 170/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Connection.php | 7 +++---- src/db/PDOConnection.php | 6 ++++-- src/model/relation/BelongsToMany.php | 1 - 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/db/Connection.php b/src/db/Connection.php index c0ee745..9feccc1 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -14,7 +14,6 @@ namespace think\db; use Psr\SimpleCache\CacheInterface; use think\DbManager; -use think\db\CacheItem; /** * 数据库连接基础类 @@ -108,7 +107,7 @@ abstract class Connection /** * 缓存对象 - * @var Cache + * @var CacheInterface */ protected $cache; @@ -214,7 +213,7 @@ abstract class Connection * 分析缓存Key * @access protected * @param BaseQuery $query 查询对象 - * @param string $method查询方法 + * @param string $method 查询方法 * @return string */ protected function getCacheKey(BaseQuery $query, string $method = ''): string @@ -233,7 +232,7 @@ abstract class Connection * @access protected * @param BaseQuery $query 查询对象 * @param array $cache 缓存信息 - * @param string $method查询方法 + * @param string $method 查询方法 * @return CacheItem */ protected function parseCache(BaseQuery $query, array $cache, string $method = ''): CacheItem diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index bcb8139..ead1c58 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -646,8 +646,10 @@ abstract class PDOConnection extends Connection implements ConnectionInterface $cacheItem = $this->parseCache($query, $query->getOptions('cache')); $key = $cacheItem->getKey(); - if ($this->cache->has($key)) { - return $this->cache->get($key); + $data = $this->cache->get($key); + + if ($data !== null) { + return $data; } } diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 08072ca..a3c8aa4 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -16,7 +16,6 @@ use think\Collection; use think\db\BaseQuery as Query; use think\db\exception\DbException as Exception; use think\db\Raw; -use think\helper\Str; use think\Model; use think\model\Pivot; use think\model\Relation; -- Gitee From 4530e47bbf414c0b43cc428e819245c1231bb56b Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Thu, 14 May 2020 16:39:03 +0800 Subject: [PATCH 171/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E7=9A=84=E4=BA=8B=E5=8A=A1=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 67 ++++++++++++++++++--------------------------------- 1 file changed, 23 insertions(+), 44 deletions(-) diff --git a/src/Model.php b/src/Model.php index 65ee831..7d92bd2 100644 --- a/src/Model.php +++ b/src/Model.php @@ -632,9 +632,8 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab // 模型更新 $db = $this->db(); - $db->startTrans(); - try { + $db->transaction(function () use ($data, $allowFields, $db) { $this->key = null; $where = $this->getWhere(); @@ -651,17 +650,12 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab if (!empty($this->relationWrite)) { $this->autoRelationUpdate(); } + }); - $db->commit(); + // 更新回调 + $this->trigger('AfterUpdate'); - // 更新回调 - $this->trigger('AfterUpdate'); - - return true; - } catch (\Exception $e) { - $db->rollback(); - throw $e; - } + return true; } /** @@ -693,9 +687,8 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $allowFields = $this->checkAllowFields(); $db = $this->db(); - $db->startTrans(); - try { + $db->transaction(function () use ($sequence, $allowFields, $db) { $result = $db->strict(false) ->field($allowFields) ->replace($this->replace) @@ -715,20 +708,15 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab if (!empty($this->relationWrite)) { $this->autoRelationInsert(); } + }); - $db->commit(); + // 标记数据已经存在 + $this->exists = true; - // 标记数据已经存在 - $this->exists = true; + // 新增回调 + $this->trigger('AfterInsert'); - // 新增回调 - $this->trigger('AfterInsert'); - - return true; - } catch (\Exception $e) { - $db->rollback(); - throw $e; - } + return true; } /** @@ -769,9 +757,9 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab public function saveAll(iterable $dataSet, bool $replace = true): Collection { $db = $this->db(); - $db->startTrans(); - try { + $result = $db->transaction(function () use ($replace, $dataSet) { + $pk = $this->getPk(); if (is_string($pk) && $replace) { @@ -790,13 +778,10 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } } - $db->commit(); + return $result; + }); - return $this->toCollection($result); - } catch (\Exception $e) { - $db->rollback(); - throw $e; - } + return $this->toCollection($result); } /** @@ -814,9 +799,8 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $where = $this->getWhere(); $db = $this->db(); - $db->startTrans(); - try { + $db->transaction(function () use ($where, $db) { // 删除当前模型数据 $db->where($where)->delete(); @@ -824,19 +808,14 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab if (!empty($this->relationWrite)) { $this->autoRelationDelete(); } + }); - $db->commit(); + $this->trigger('AfterDelete'); - $this->trigger('AfterDelete'); - - $this->exists = false; - $this->lazySave = false; + $this->exists = false; + $this->lazySave = false; - return true; - } catch (\Exception $e) { - $db->rollback(); - throw $e; - } + return true; } /** -- Gitee From 876b05a4962f73fa882d384e007faadc1264652f Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Tue, 26 May 2020 12:19:52 +0800 Subject: [PATCH 172/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BDbManager=E7=9A=84con?= =?UTF-8?q?nect=E6=96=B9=E6=B3=95=20=20=E8=BF=94=E5=9B=9E=E4=B8=80?= =?UTF-8?q?=E4=B8=AAconnection=E7=9A=84=E4=BB=A3=E7=90=86=E7=B1=BB?= =?UTF-8?q?=EF=BC=8C=E5=8F=AF=E4=BB=A5=E4=BF=9D=E5=AD=98=E4=B8=8B=E6=9D=A5?= =?UTF-8?q?=E5=A4=9A=E6=AC=A1=E4=BD=BF=E7=94=A8=E8=AF=A5=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 15 ++++--------- src/db/Connection.php | 40 ++++++++++++++++++++++++++++++++-- src/db/ConnectionInterface.php | 23 ++++++++++++------- src/db/ConnectionProxy.php | 38 ++++++++++++++++++++++++++++++++ src/db/Fetch.php | 5 +++-- src/db/Mongo.php | 21 +++++++++++------- src/db/PDOConnection.php | 18 +-------------- src/db/connector/Mongo.php | 33 ++++++++-------------------- 8 files changed, 121 insertions(+), 72 deletions(-) create mode 100644 src/db/ConnectionProxy.php diff --git a/src/DbManager.php b/src/DbManager.php index ceb508f..3471aa7 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -17,6 +17,7 @@ use Psr\Log\LoggerInterface; use Psr\SimpleCache\CacheInterface; use think\db\BaseQuery; use think\db\ConnectionInterface; +use think\db\ConnectionProxy; use think\db\Query; use think\db\Raw; @@ -229,21 +230,13 @@ class DbManager * @access public * @param string|null $name 连接配置标识 * @param bool $force 强制重新连接 - * @return BaseQuery + * @return ConnectionProxy */ - public function connect(string $name = null, bool $force = false): BaseQuery + public function connect(string $name = null, bool $force = false) { $connection = $this->instance($name, $force); - $class = $connection->getQueryClass(); - $query = new $class($connection); - - $timeRule = $this->getConfig('time_query_rule'); - if (!empty($timeRule)) { - $query->timeRule($timeRule); - } - - return $query; + return new ConnectionProxy($connection); } /** diff --git a/src/db/Connection.php b/src/db/Connection.php index 9feccc1..9a694d8 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -18,7 +18,7 @@ use think\DbManager; /** * 数据库连接基础类 */ -abstract class Connection +abstract class Connection implements ConnectionInterface { /** @@ -89,7 +89,7 @@ abstract class Connection /** * Db对象 - * @var Db + * @var DbManager */ protected $db; @@ -111,6 +111,23 @@ abstract class Connection */ protected $cache; + /** + * 架构函数 读取数据库配置信息 + * @access public + * @param array $config 数据库配置数组 + */ + public function __construct(array $config = []) + { + if (!empty($config)) { + $this->config = array_merge($this->config, $config); + } + + // 创建Builder对象 + $class = $this->getBuilderClass(); + + $this->builder = new $class($this); + } + /** * 获取当前的builder实例对象 * @access public @@ -121,6 +138,25 @@ abstract class Connection return $this->builder; } + + /** + * 创建查询对象 + */ + public function newQuery() + { + $class = $this->getQueryClass(); + + /** @var BaseQuery $query */ + $query = new $class($this); + + $timeRule = $this->db->getConfig('time_query_rule'); + if (!empty($timeRule)) { + $query->timeRule($timeRule); + } + + return $query; + } + /** * 设置当前的数据库Db对象 * @access public diff --git a/src/db/ConnectionInterface.php b/src/db/ConnectionInterface.php index f6b1e08..ea695cb 100644 --- a/src/db/ConnectionInterface.php +++ b/src/db/ConnectionInterface.php @@ -13,6 +13,9 @@ declare (strict_types = 1); namespace think\db; use Psr\SimpleCache\CacheInterface; +use think\db\exception\DataNotFoundException; +use think\db\exception\DbException; +use think\db\exception\ModelNotFoundException; use think\DbManager; /** @@ -27,6 +30,11 @@ interface ConnectionInterface */ public function getQueryClass(): string; + /** + * 创建查询对象 + */ + public function newQuery(); + /** * 连接数据库方法 * @access public @@ -114,8 +122,8 @@ interface ConnectionInterface * @access public * @param BaseQuery $query 查询对象 * @return integer - * @throws Exception - * @throws PDOException + * @throws \Exception + * @throws \PDOException */ public function update(BaseQuery $query): int; @@ -124,8 +132,8 @@ interface ConnectionInterface * @access public * @param BaseQuery $query 查询对象 * @return int - * @throws Exception - * @throws PDOException + * @throws \Exception + * @throws \PDOException */ public function delete(BaseQuery $query): int; @@ -135,7 +143,6 @@ interface ConnectionInterface * @param BaseQuery $query 查询对象 * @param string $field 字段名 * @param mixed $default 默认值 - * @param bool $one 返回一个值 * @return mixed */ public function value(BaseQuery $query, string $field, $default = null); @@ -155,7 +162,7 @@ interface ConnectionInterface * @access public * @param callable $callback 数据操作方法回调 * @return mixed - * @throws PDOException + * @throws \PDOException * @throws \Exception * @throws \Throwable */ @@ -174,7 +181,7 @@ interface ConnectionInterface * 用于非自动提交状态下面的查询提交 * @access public * @return void - * @throws PDOException + * @throws \PDOException */ public function commit(); @@ -182,7 +189,7 @@ interface ConnectionInterface * 事务回滚 * @access public * @return void - * @throws PDOException + * @throws \PDOException */ public function rollback(); diff --git a/src/db/ConnectionProxy.php b/src/db/ConnectionProxy.php new file mode 100644 index 0000000..93f32c4 --- /dev/null +++ b/src/db/ConnectionProxy.php @@ -0,0 +1,38 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\db; + +/** + * Class ConnectionProxy + * @package think\db + * @mixin Query + */ +class ConnectionProxy +{ + protected $connection; + + public function __construct(ConnectionInterface $connection) + { + $this->connection = $connection; + } + + public function getConnection() + { + return $this->connection; + } + + public function __call($method, $params) + { + return call_user_func([$this->connection->newQuery(), $method], ...$params); + } +} diff --git a/src/db/Fetch.php b/src/db/Fetch.php index 76658e6..16caed2 100644 --- a/src/db/Fetch.php +++ b/src/db/Fetch.php @@ -69,8 +69,9 @@ class Fetch /** * 得到某个字段的值 * @access public - * @param string $field 字段名 - * @param mixed $default 默认值 + * @param string $field 字段名 + * @param mixed $default 默认值 + * @param bool $one * @return string */ public function value(string $field, $default = null, bool $one = true): string diff --git a/src/db/Mongo.php b/src/db/Mongo.php index 7d2e469..c464585 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -20,13 +20,17 @@ use MongoDB\Driver\Exception\RuntimeException; use MongoDB\Driver\Query as MongoQuery; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; -use think\Collection; -use think\db\connector\Mongo as Connection; use think\db\exception\DbException as Exception; use think\Paginator; class Mongo extends BaseQuery { + /** + * 当前数据库连接对象 + * @var \think\db\connector\Mongo + */ + protected $connection; + /** * 执行查询 返回数据集 * @access public @@ -699,12 +703,13 @@ class Mongo extends BaseQuery if (isset($options['page'])) { // 根据页数计算limit - list($page, $listRows) = $options['page']; - $page = $page > 0 ? $page : 1; - $listRows = $listRows > 0 ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20); - $offset = $listRows * ($page - 1); - $options['skip'] = intval($offset); - $options['limit'] = intval($listRows); + [$page, $listRows] = $options['page']; + + $page = $page > 0 ? $page : 1; + $listRows = $listRows > 0 ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20); + $offset = $listRows * ($page - 1); + $options['skip'] = intval($offset); + $options['limit'] = intval($listRows); } $this->options = $options; diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index ead1c58..8ee159c 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -23,7 +23,7 @@ use think\db\exception\PDOException; /** * 数据库连接基础类 */ -abstract class PDOConnection extends Connection implements ConnectionInterface +abstract class PDOConnection extends Connection { const PARAM_FLOAT = 21; @@ -182,22 +182,6 @@ abstract class PDOConnection extends Connection implements ConnectionInterface */ protected $bind = []; - /** - * 架构函数 读取数据库配置信息 - * @access public - * @param array $config 数据库配置数组 - */ - public function __construct(array $config = []) - { - if (!empty($config)) { - $this->config = array_merge($this->config, $config); - } - - // 创建Builder对象 - $class = $this->getBuilderClass(); - - $this->builder = new $class($this); - } /** * 获取当前连接器类对应的Query类 diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 809246f..8f16018 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -27,14 +27,13 @@ use MongoDB\Driver\WriteConcern; use think\db\BaseQuery; use think\db\builder\Mongo as Builder; use think\db\Connection; -use think\db\ConnectionInterface; use think\db\exception\DbException as Exception; use think\db\Mongo as Query; /** * Mongo数据库驱动 */ -class Mongo extends Connection implements ConnectionInterface +class Mongo extends Connection { // 查询数据类型 @@ -45,6 +44,9 @@ class Mongo extends Connection implements ConnectionInterface protected $session_uuid; // sessions会话列表当前会话数组key 随机生成 protected $sessions = []; // 会话列表 + /** @var Builder */ + protected $builder; + // 数据库连接参数配置 protected $config = [ // 数据库类型 @@ -97,23 +99,6 @@ class Mongo extends Connection implements ConnectionInterface 'type_map' => ['root' => 'array', 'document' => 'array'], ]; - /** - * 架构函数 读取数据库配置信息 - * @access public - * @param array $config 数据库配置数组 - */ - public function __construct(array $config = []) - { - if (!empty($config)) { - $this->config = array_merge($this->config, $config); - } - - // 创建Builder对象 - $class = $this->getBuilderClass(); - - $this->builder = new $class($this); - } - /** * 获取当前连接器类对应的Query类 * @access public @@ -129,7 +114,7 @@ class Mongo extends Connection implements ConnectionInterface * @access public * @return Builder */ - public function getBuilder(): Builder + public function getBuilder() { return $this->builder; } @@ -200,8 +185,8 @@ class Mongo extends Connection implements ConnectionInterface /** * 设置/获取当前操作的database * @access public - * @param string $db db - * @throws Exception + * @param string $db db + * @return string */ public function db(string $db = null) { @@ -215,10 +200,10 @@ class Mongo extends Connection implements ConnectionInterface /** * 执行查询但只返回Cursor对象 * @access public - * @param BaseQuery $query 查询对象 + * @param Query $query 查询对象 * @return Cursor */ - public function cursor(BaseQuery $query) + public function cursor($query) { // 分析查询表达式 $options = $query->parseOptions(); -- Gitee From 692515afa12d66722001083e389e5e257f8fe3ae Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Tue, 26 May 2020 13:52:48 +0800 Subject: [PATCH 173/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 7 ++----- src/db/Connection.php | 20 ++++++++++++++++++ src/db/ConnectionInterface.php | 35 ++++++++++--------------------- src/db/ConnectionProxy.php | 38 ---------------------------------- src/db/PDOConnection.php | 7 +------ 5 files changed, 34 insertions(+), 73 deletions(-) delete mode 100644 src/db/ConnectionProxy.php diff --git a/src/DbManager.php b/src/DbManager.php index 3471aa7..414706a 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -17,7 +17,6 @@ use Psr\Log\LoggerInterface; use Psr\SimpleCache\CacheInterface; use think\db\BaseQuery; use think\db\ConnectionInterface; -use think\db\ConnectionProxy; use think\db\Query; use think\db\Raw; @@ -230,13 +229,11 @@ class DbManager * @access public * @param string|null $name 连接配置标识 * @param bool $force 强制重新连接 - * @return ConnectionProxy + * @return ConnectionInterface */ public function connect(string $name = null, bool $force = false) { - $connection = $this->instance($name, $force); - - return new ConnectionProxy($connection); + return $this->instance($name, $force); } /** diff --git a/src/db/Connection.php b/src/db/Connection.php index 9a694d8..cb6a912 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -157,6 +157,26 @@ abstract class Connection implements ConnectionInterface return $query; } + /** + * 指定表名开始查询 + * @param $table + * @return BaseQuery + */ + public function table($table) + { + return $this->newQuery()->table($table); + } + + /** + * 指定表名开始查询(不带前缀) + * @param $name + * @return BaseQuery + */ + public function name($name) + { + return $this->newQuery()->name($name); + } + /** * 设置当前的数据库Db对象 * @access public diff --git a/src/db/ConnectionInterface.php b/src/db/ConnectionInterface.php index ea695cb..207a424 100644 --- a/src/db/ConnectionInterface.php +++ b/src/db/ConnectionInterface.php @@ -13,9 +13,6 @@ declare (strict_types = 1); namespace think\db; use Psr\SimpleCache\CacheInterface; -use think\db\exception\DataNotFoundException; -use think\db\exception\DbException; -use think\db\exception\ModelNotFoundException; use think\DbManager; /** @@ -31,9 +28,18 @@ interface ConnectionInterface public function getQueryClass(): string; /** - * 创建查询对象 + * 指定表名开始查询 + * @param $table + * @return BaseQuery */ - public function newQuery(); + public function table($table); + + /** + * 指定表名开始查询(不带前缀) + * @param $name + * @return BaseQuery + */ + public function name($name); /** * 连接数据库方法 @@ -80,9 +86,6 @@ interface ConnectionInterface * @access public * @param BaseQuery $query 查询对象 * @return array - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException */ public function find(BaseQuery $query): array; @@ -91,9 +94,6 @@ interface ConnectionInterface * @access public * @param BaseQuery $query 查询对象 * @return array - * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException */ public function select(BaseQuery $query): array; @@ -112,8 +112,6 @@ interface ConnectionInterface * @param BaseQuery $query 查询对象 * @param mixed $dataSet 数据集 * @return integer - * @throws \Exception - * @throws \Throwable */ public function insertAll(BaseQuery $query, array $dataSet = []): int; @@ -122,8 +120,6 @@ interface ConnectionInterface * @access public * @param BaseQuery $query 查询对象 * @return integer - * @throws \Exception - * @throws \PDOException */ public function update(BaseQuery $query): int; @@ -132,8 +128,6 @@ interface ConnectionInterface * @access public * @param BaseQuery $query 查询对象 * @return int - * @throws \Exception - * @throws \PDOException */ public function delete(BaseQuery $query): int; @@ -162,9 +156,6 @@ interface ConnectionInterface * @access public * @param callable $callback 数据操作方法回调 * @return mixed - * @throws \PDOException - * @throws \Exception - * @throws \Throwable */ public function transaction(callable $callback); @@ -172,8 +163,6 @@ interface ConnectionInterface * 启动事务 * @access public * @return void - * @throws \PDOException - * @throws \Exception */ public function startTrans(); @@ -181,7 +170,6 @@ interface ConnectionInterface * 用于非自动提交状态下面的查询提交 * @access public * @return void - * @throws \PDOException */ public function commit(); @@ -189,7 +177,6 @@ interface ConnectionInterface * 事务回滚 * @access public * @return void - * @throws \PDOException */ public function rollback(); diff --git a/src/db/ConnectionProxy.php b/src/db/ConnectionProxy.php deleted file mode 100644 index 93f32c4..0000000 --- a/src/db/ConnectionProxy.php +++ /dev/null @@ -1,38 +0,0 @@ - -// +---------------------------------------------------------------------- -declare (strict_types = 1); - -namespace think\db; - -/** - * Class ConnectionProxy - * @package think\db - * @mixin Query - */ -class ConnectionProxy -{ - protected $connection; - - public function __construct(ConnectionInterface $connection) - { - $this->connection = $connection; - } - - public function getConnection() - { - return $this->connection; - } - - public function __call($method, $params) - { - return call_user_func([$this->connection->newQuery(), $method], ...$params); - } -} diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 8ee159c..4451910 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -16,8 +16,7 @@ use Closure; use PDO; use PDOStatement; use think\db\exception\BindParamException; -use think\db\exception\DataNotFoundException; -use think\db\exception\ModelNotFoundException; +use think\db\exception\DbException; use think\db\exception\PDOException; /** @@ -793,8 +792,6 @@ abstract class PDOConnection extends Connection * @param BaseQuery $query 查询对象 * @return array * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException */ public function find(BaseQuery $query): array { @@ -839,8 +836,6 @@ abstract class PDOConnection extends Connection * @param BaseQuery $query 查询对象 * @return array * @throws DbException - * @throws ModelNotFoundException - * @throws DataNotFoundException */ public function select(BaseQuery $query): array { -- Gitee From 130ab7035a5db16a9dd8ea969742d44fe0f8fe3f Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 26 May 2020 23:09:06 +0800 Subject: [PATCH 174/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BRaw=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Builder.php | 37 ++++++++++++++++++++++++++--------- src/db/Query.php | 6 +----- src/db/Raw.php | 21 +++++++++++++++++++- src/db/builder/Mysql.php | 10 +++++----- src/db/builder/Pgsql.php | 2 +- src/db/builder/Sqlite.php | 2 +- src/db/builder/Sqlsrv.php | 4 ++-- src/db/concern/ParamsBind.php | 2 +- src/db/concern/WhereQuery.php | 12 ++---------- 9 files changed, 61 insertions(+), 35 deletions(-) diff --git a/src/db/Builder.php b/src/db/Builder.php index 67cf3b3..cbfbd44 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -149,7 +149,7 @@ abstract class Builder $item = $this->parseKey($query, $key, true); if ($val instanceof Raw) { - $result[$item] = $val->getValue(); + $result[$item] = $this->parseRaw($query, $val); continue; } elseif (!is_scalar($val) && (in_array($key, (array) $query->getOptions('json')) || 'json' == $query->getFieldType($key))) { $val = json_encode($val); @@ -195,7 +195,7 @@ abstract class Builder protected function parseDataBind(Query $query, string $key, $data, array $bind = []): string { if ($data instanceof Raw) { - return $data->getValue(); + return $this->parseRaw($query, $data); } $name = $query->bindValue($data, $bind[$key] ?? PDO::PARAM_STR); @@ -243,7 +243,7 @@ abstract class Builder foreach ($fields as $key => $field) { if ($field instanceof Raw) { - $array[] = $field->getValue(); + $array[] = $this->parseRaw($query, $field); } elseif (!is_numeric($key)) { $array[] = $this->parseKey($query, $key) . ' AS ' . $this->parseKey($query, $field, true); } else { @@ -273,7 +273,7 @@ abstract class Builder foreach ((array) $tables as $key => $table) { if ($table instanceof Raw) { - $item[] = $table->getValue(); + $item[] = $this->parseRaw($query, $table); } elseif (!is_numeric($key)) { $item[] = $this->parseKey($query, $key) . ' ' . $this->parseKey($query, $table); } elseif (isset($options['alias'][$table])) { @@ -350,7 +350,7 @@ abstract class Builder $where = []; foreach ($val as $value) { if ($value instanceof Raw) { - $where[] = ' ' . $logic . ' ( ' . $value->getValue() . ' )'; + $where[] = ' ' . $logic . ' ( ' . $this->parseRaw($query, $value) . ' )'; continue; } @@ -577,7 +577,7 @@ abstract class Builder protected function parseExp(Query $query, string $key, string $exp, Raw $value, string $field, int $bindType): string { // 表达式查询 - return '( ' . $key . ' ' . $value->getValue() . ' )'; + return '( ' . $key . ' ' . $this->parseRaw($query, $value) . ' )'; } /** @@ -659,7 +659,7 @@ abstract class Builder if ($value instanceof Closure) { $value = $this->parseClosure($query, $value, false); } elseif ($value instanceof Raw) { - $value = $value->getValue(); + $value = $this->parseRaw($query, $value); } else { throw new Exception('where express error:' . $value); } @@ -753,7 +753,7 @@ abstract class Builder if ($value instanceof Closure) { $value = $this->parseClosure($query, $value, false); } elseif ($value instanceof Raw) { - $value = $value->getValue(); + $value = $this->parseRaw($query, $value); } else { $value = array_unique(is_array($value) ? $value : explode(',', $value)); $array = []; @@ -891,7 +891,7 @@ abstract class Builder $array = []; foreach ($order as $key => $val) { if ($val instanceof Raw) { - $array[] = $val->getValue(); + $array[] = $this->parseRaw($query, $val); } elseif (is_array($val) && preg_match('/^[\w\.]+$/', $key)) { $array[] = $this->parseOrderField($query, $key, $val); } elseif ('[rand]' == $val) { @@ -916,6 +916,25 @@ abstract class Builder return empty($array) ? '' : ' ORDER BY ' . implode(',', $array); } + /** + * 分析Raw对象 + * @access protected + * @param Query $query 查询对象 + * @param Raw $raw Raw对象 + * @return string + */ + protected function parseRaw(Query $query, Raw $raw): string + { + $sql = $raw->getValue(); + $bind = $raw->getBind(); + + if ($bind) { + $query->bindParams($sql, $bind); + } + + return $sql; + } + /** * 随机排序 * @access protected diff --git a/src/db/Query.php b/src/db/Query.php index cb0c4a3..62e334c 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -33,11 +33,7 @@ class Query extends BaseQuery */ public function orderRaw(string $field, array $bind = []) { - if (!empty($bind)) { - $this->bindParams($field, $bind); - } - - $this->options['order'][] = new Raw($field); + $this->options['order'][] = new Raw($field, $bind); return $this; } diff --git a/src/db/Raw.php b/src/db/Raw.php index 0091a5d..833fbf0 100644 --- a/src/db/Raw.php +++ b/src/db/Raw.php @@ -24,15 +24,24 @@ class Raw */ protected $value; + /** + * 参数绑定 + * + * @var array + */ + protected $bind = []; + /** * 创建一个查询表达式 * * @param string $value + * @param array $bind * @return void */ - public function __construct(string $value) + public function __construct(string $value, array $bind = []) { $this->value = $value; + $this->bind = $bind; } /** @@ -45,6 +54,16 @@ class Raw return $this->value; } + /** + * 获取参数绑定 + * + * @return string + */ + public function getBind(): array + { + return $this->bind; + } + public function __toString() { return (string) $this->value; diff --git a/src/db/builder/Mysql.php b/src/db/builder/Mysql.php index 977ff9b..df6bffd 100644 --- a/src/db/builder/Mysql.php +++ b/src/db/builder/Mysql.php @@ -270,7 +270,7 @@ class Mysql extends Builder protected function parseRegexp(Query $query, string $key, string $exp, $value, string $field): string { if ($value instanceof Raw) { - $value = $value->getValue(); + $value = $this->parseRaw($query, $value); } return $key . ' ' . $exp . ' ' . $value; @@ -289,7 +289,7 @@ class Mysql extends Builder protected function parseFindInSet(Query $query, string $key, string $exp, $value, string $field): string { if ($value instanceof Raw) { - $value = $value->getValue(); + $value = $this->parseRaw($query, $value); } return 'FIND_IN_SET(' . $value . ', ' . $key . ')'; @@ -308,7 +308,7 @@ class Mysql extends Builder if (is_int($key)) { return (string) $key; } elseif ($key instanceof Raw) { - return $key->getValue(); + return $this->parseRaw($query, $key); } $key = trim($key); @@ -396,7 +396,7 @@ class Mysql extends Builder } if ($duplicate instanceof Raw) { - return ' ON DUPLICATE KEY UPDATE ' . $duplicate->getValue() . ' '; + return ' ON DUPLICATE KEY UPDATE ' . $this->parseRaw($query, $duplicate) . ' '; } if (is_string($duplicate)) { @@ -409,7 +409,7 @@ class Mysql extends Builder $val = $this->parseKey($query, $val); $updates[] = $val . ' = VALUES(' . $val . ')'; } elseif ($val instanceof Raw) { - $updates[] = $this->parseKey($query, $key) . " = " . $val->getValue(); + $updates[] = $this->parseKey($query, $key) . " = " . $this->parseRaw($query, $val); } else { $name = $query->bindValue($val, $query->getConnection()->getFieldBindType($key)); $updates[] = $this->parseKey($query, $key) . " = :" . $name; diff --git a/src/db/builder/Pgsql.php b/src/db/builder/Pgsql.php index 4b581df..4eace0a 100644 --- a/src/db/builder/Pgsql.php +++ b/src/db/builder/Pgsql.php @@ -69,7 +69,7 @@ class Pgsql extends Builder if (is_int($key)) { return (string) $key; } elseif ($key instanceof Raw) { - return $key->getValue(); + return $this->parseRaw($query, $key); } $key = trim($key); diff --git a/src/db/builder/Sqlite.php b/src/db/builder/Sqlite.php index 87a84eb..40cab7f 100644 --- a/src/db/builder/Sqlite.php +++ b/src/db/builder/Sqlite.php @@ -68,7 +68,7 @@ class Sqlite extends Builder if (is_int($key)) { return (string) $key; } elseif ($key instanceof Raw) { - return $key->getValue(); + return $this->parseRaw($query, $key); } $key = trim($key); diff --git a/src/db/builder/Sqlsrv.php b/src/db/builder/Sqlsrv.php index 80cb166..779b5e3 100644 --- a/src/db/builder/Sqlsrv.php +++ b/src/db/builder/Sqlsrv.php @@ -73,7 +73,7 @@ class Sqlsrv extends Builder foreach ($order as $key => $val) { if ($val instanceof Raw) { - $array[] = $val->getValue(); + $array[] = $this->parseRaw($query, $val); } elseif ('[rand]' == $val) { $array[] = $this->parseRand($query); } else { @@ -115,7 +115,7 @@ class Sqlsrv extends Builder if (is_int($key)) { return (string) $key; } elseif ($key instanceof Raw) { - return $key->getValue(); + return $this->parseRaw($query, $key); } $key = trim($key); diff --git a/src/db/concern/ParamsBind.php b/src/db/concern/ParamsBind.php index 2ae858c..296e221 100644 --- a/src/db/concern/ParamsBind.php +++ b/src/db/concern/ParamsBind.php @@ -71,7 +71,7 @@ trait ParamsBind * @param array $bind 参数绑定 * @return void */ - protected function bindParams(string &$sql, array $bind = []): void + public function bindParams(string &$sql, array $bind = []): void { foreach ($bind as $key => $value) { if (is_array($value)) { diff --git a/src/db/concern/WhereQuery.php b/src/db/concern/WhereQuery.php index 33b0763..62dc983 100644 --- a/src/db/concern/WhereQuery.php +++ b/src/db/concern/WhereQuery.php @@ -290,11 +290,7 @@ trait WhereQuery */ public function whereExp(string $field, string $where, array $bind = [], string $logic = 'AND') { - if (!empty($bind)) { - $this->bindParams($where, $bind); - } - - $this->options['where'][$logic][] = [$field, 'EXP', new Raw($where)]; + $this->options['where'][$logic][] = [$field, 'EXP', new Raw($where, $bind)]; return $this; } @@ -329,11 +325,7 @@ trait WhereQuery */ public function whereRaw(string $where, array $bind = [], string $logic = 'AND') { - if (!empty($bind)) { - $this->bindParams($where, $bind); - } - - $this->options['where'][$logic][] = new Raw($where); + $this->options['where'][$logic][] = new Raw($where, $bind); return $this; } -- Gitee From 1c1f5c40728aa52bb99100f5368b459757237863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=92=8B=E6=96=87=E5=81=A5?= Date: Mon, 25 May 2020 16:36:11 +0800 Subject: [PATCH 175/365] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Paginator.php | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Paginator.php b/src/Paginator.php index 06999a1..f5d8006 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -239,6 +239,10 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J static::$currentPathResolver = $resolver; } + /** + * 获取数据总条数 + * @return int + */ public function total(): int { if ($this->simple) { @@ -248,16 +252,28 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J return $this->total; } + /** + * 获取每页数量 + * @return int + */ public function listRows(): int { return $this->listRows; } + /** + * 获取当前页页码 + * @return int + */ public function currentPage(): int { return $this->currentPage; } + /** + * 获取最后一页页码 + * @return int + */ public function lastPage(): int { if ($this->simple) { @@ -445,7 +461,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J } /** - * Count elements of an object + * 统计数据集条数 + * @return int */ public function count(): int { @@ -457,6 +474,10 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J return (string) $this->render(); } + /** + * 转换为数组 + * @return array + */ public function toArray(): array { try { -- Gitee From 8da16530b32f963741fee9f1c7c97d1a5cf5c210 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Wed, 3 Jun 2020 15:06:05 +0800 Subject: [PATCH 176/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 4451910..9104205 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -75,10 +75,6 @@ abstract class PDOConnection extends Connection 'break_reconnect' => false, // 断线标识字符串 'break_match_str' => [], - // 字段缓存路径 - 'schema_cache_path' => '', - // 字段缓存规则 支持闭包 - 'schema_cache_rule' => '', ]; /** @@ -305,21 +301,14 @@ abstract class PDOConnection extends Connection } /** - * 获取数据表信息缓存文件名 + * 获取数据表信息缓存key * @access protected * @param string $schema 数据表名称 * @return string */ - protected function getSchemaFileName(string $schema): string + protected function getSchemaCacheKey(string $schema): string { - if (!empty($this->config['schema_cache_rule']) && is_callable($this->config['schema_cache_rule'])) { - $rule = $this->config['schema_cache_rule']; - $name = $rule($schema); - } else { - $name = str_replace('.', DIRECTORY_SEPARATOR, $schema); - } - - return $this->config['schema_cache_path'] . $name . '.php'; + return $this->getConfig('hostname') . ':' . $this->getConfig('hostport') . '@' . $schema; } /** @@ -350,20 +339,17 @@ abstract class PDOConnection extends Connection if (!isset($this->info[$schema])) { // 读取字段缓存 - $cacheFile = $this->getSchemaFileName($schema); + $cacheKey = $this->getSchemaCacheKey($schema); + $cacheField = $this->config['fields_cache'] && !empty($this->cache); - if ($this->config['fields_cache'] && is_file($cacheFile)) { - $info = include $cacheFile; - } else { + if ($cacheField) { + $info = $this->cache->get($cacheKey); + } + + if (empty($info)) { $info = $this->getTableFieldsInfo($tableName); - if ($this->config['fields_cache']) { - $path = dirname($cacheFile); - if (!is_dir($path)) { - mkdir($path, 0755, true); - } - - $content = 'cache->set($cacheKey, $info); } } -- Gitee From df1999ade81f5597813a6063b2ab22f641404e69 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Wed, 3 Jun 2020 18:03:48 +0800 Subject: [PATCH 177/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 52 ++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 9104205..09643ed 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -312,37 +312,24 @@ abstract class PDOConnection extends Connection } /** - * 获取数据表信息 - * @access public - * @param mixed $tableName 数据表名 留空自动获取 - * @param string $fetch 获取信息类型 包括 fields type bind pk - * @return mixed + * @param string $tableName 数据表名称 + * @param bool $force 强制从数据库获取 + * @return array */ - public function getTableInfo($tableName, string $fetch = '') + public function getSchemaInfo(string $tableName, $force = false) { - if (is_array($tableName)) { - $tableName = key($tableName) ?: current($tableName); - } - - if (strpos($tableName, ',') || strpos($tableName, ')')) { - // 多表不获取字段信息 - return []; - } - - [$tableName] = explode(' ', $tableName); - if (!strpos($tableName, '.')) { $schema = $this->getConfig('database') . '.' . $tableName; } else { $schema = $tableName; } - if (!isset($this->info[$schema])) { + if (!isset($this->info[$schema]) || $force) { // 读取字段缓存 $cacheKey = $this->getSchemaCacheKey($schema); $cacheField = $this->config['fields_cache'] && !empty($this->cache); - if ($cacheField) { + if ($cacheField && !$force) { $info = $this->cache->get($cacheKey); } @@ -371,7 +358,32 @@ abstract class PDOConnection extends Connection ]; } - return $fetch ? $this->info[$schema][$fetch] : $this->info[$schema]; + return $this->info[$schema]; + } + + /** + * 获取数据表信息 + * @access public + * @param mixed $tableName 数据表名 留空自动获取 + * @param string $fetch 获取信息类型 包括 fields type bind pk + * @return mixed + */ + public function getTableInfo($tableName, string $fetch = '') + { + if (is_array($tableName)) { + $tableName = key($tableName) ?: current($tableName); + } + + if (strpos($tableName, ',') || strpos($tableName, ')')) { + // 多表不获取字段信息 + return []; + } + + [$tableName] = explode(' ', $tableName); + + $info = $this->getSchemaInfo($tableName); + + return $fetch ? $info[$fetch] : $info; } /** -- Gitee From 667896c20244894eadd706a7adc3bdca512f7bc7 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 4 Jun 2020 15:11:09 +0800 Subject: [PATCH 178/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Builder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/Builder.php b/src/db/Builder.php index cbfbd44..c807f82 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -165,7 +165,7 @@ abstract class Builder } } elseif (is_null($val)) { $result[$item] = 'NULL'; - } elseif (is_array($val) && !empty($val)) { + } elseif (is_array($val) && !empty($val) && is_string($val[0])) { switch (strtoupper($val[0])) { case 'INC': $result[$item] = $item . ' + ' . floatval($val[1]); -- Gitee From 156d3d20c4ea03caf28cddbad711982f91331d23 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Thu, 18 Jun 2020 02:52:11 +0800 Subject: [PATCH 179/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3MorphToMany=E5=85=B3?= =?UTF-8?q?=E8=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/MorphToMany.php | 74 +++++++++++++++--------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/model/relation/MorphToMany.php b/src/model/relation/MorphToMany.php index 9da207b..88bbf9a 100644 --- a/src/model/relation/MorphToMany.php +++ b/src/model/relation/MorphToMany.php @@ -12,11 +12,11 @@ namespace think\model\relation; use Closure; +use Exception; use think\db\BaseQuery as Query; use think\db\Raw; -use think\helper\Str; use think\Model; -use think\model\Relation; +use think\model\Pivot; /** * 多态多对多关联 @@ -24,12 +24,6 @@ use think\model\Relation; class MorphToMany extends BelongsToMany { - /** - * 多态关联外键 - * @var string - */ - protected $morphKey; - /** * 多态字段名 * @var string @@ -61,12 +55,14 @@ class MorphToMany extends BelongsToMany */ public function __construct(Model $parent, string $model, string $middle, string $morphType, string $morphKey, string $localKey, bool $inverse = false) { - $this->morphKey = $morphKey; $this->morphType = $morphType; $this->inverse = $inverse; $this->morphClass = $inverse ? $model : get_class($parent); - parent::__construct($parent, $model, $middle, $morphKey, $localKey); + $foreignKey = $inverse ? $morphKey : $localKey; + $localKey = $inverse ? $localKey : $morphKey; + + parent::__construct($parent, $model, $middle, $foreignKey, $localKey); } /** @@ -94,7 +90,7 @@ class MorphToMany extends BelongsToMany if (!empty($range)) { // 查询关联数据 $data = $this->eagerlyManyToMany([ - ['pivot.' . $this->morphKey, 'in', $range], + ['pivot.' . $this->localKey, 'in', $range], ['pivot.' . $this->morphType, '=', $this->morphClass], ], $subRelation, $closure, $cache); @@ -127,7 +123,7 @@ class MorphToMany extends BelongsToMany $pk = $result->$pk; // 查询管理数据 $data = $this->eagerlyManyToMany([ - ['pivot.' . $this->morphKey, '=', $pk], + ['pivot.' . $this->localKey, '=', $pk], ['pivot.' . $this->morphType, '=', $this->morphClass], ], $subRelation, $closure, $cache); @@ -164,8 +160,8 @@ class MorphToMany extends BelongsToMany $closure($this->getClosureType($closure), $name); } - return $this->belongsToManyQuery($this->morphKey, $this->localKey, [ - ['pivot.' . ($this->inverse ? $this->localKey : $this->morphKey), '=', $pk], + return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ + ['pivot.' . $this->localKey, '=', $pk], ['pivot.' . $this->morphType, '=', $this->morphClass], ])->$aggregate($field); } @@ -185,8 +181,8 @@ class MorphToMany extends BelongsToMany $closure($this->getClosureType($closure), $name); } - return $this->belongsToManyQuery($this->morphKey, $this->localKey, [ - ['pivot.' . ($this->inverse ? $this->localKey : $this->morphKey), 'exp', new Raw('=' . $this->parent->db(false)->getTable() . '.' . $this->parent->getPk())], + return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ + ['pivot.' . $this->localKey, 'exp', new Raw('=' . $this->parent->db(false)->getTable() . '.' . $this->parent->getPk())], ['pivot.' . $this->morphType, '=', $this->morphClass], ])->fetchSql()->$aggregate($field); } @@ -239,7 +235,7 @@ class MorphToMany extends BelongsToMany } // 预载入关联查询 支持嵌套预载入 - $list = $this->belongsToManyQuery($this->morphKey, $this->localKey, $where) + $list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where) ->with($subRelation) ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) ->select(); @@ -258,7 +254,7 @@ class MorphToMany extends BelongsToMany } } - $key = $pivot[$this->morphKey]; + $key = $pivot[$this->localKey]; if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { continue; @@ -278,7 +274,6 @@ class MorphToMany extends BelongsToMany * @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键 * @param array $pivot 中间表额外数据 * @return array|Pivot - * @throws Exception */ public function attach($data, array $pivot = []) { @@ -290,23 +285,24 @@ class MorphToMany extends BelongsToMany $model = new $this->model; $id = $model->insertGetId($data); } - } elseif (is_numeric($data) || is_string($data)) { + } else if (is_numeric($data) || is_string($data)) { // 根据关联表主键直接写入中间表 $id = $data; - } elseif ($data instanceof Model) { + } else if ($data instanceof Model) { // 根据关联表主键直接写入中间表 $id = $data->getKey(); } if (!empty($id)) { // 保存中间表数据 - $pivot[$this->inverse ? $this->localKey : $this->morphKey] = $this->parent->getKey(); - + $pivot[$this->localKey] = $this->parent->getKey(); $pivot[$this->morphType] = $this->morphClass; $ids = (array) $id; + $result = []; + foreach ($ids as $id) { - $pivot[$this->inverse ? $this->morphKey : $this->localKey] = $id; + $pivot[$this->foreignKey] = $id; $this->pivot->replace() ->exists(false) @@ -341,9 +337,9 @@ class MorphToMany extends BelongsToMany } $pivot = $this->pivot - ->where($this->inverse ? $this->localKey : $this->morphKey, $this->parent->getKey()) + ->where($this->localKey, $this->parent->getKey()) ->where($this->morphType, $this->morphClass) - ->where($this->inverse ? $this->morphKey : $this->localKey, $id) + ->where($this->foreignKey, $id) ->find(); return $pivot ?: false; @@ -360,22 +356,22 @@ class MorphToMany extends BelongsToMany { if (is_array($data)) { $id = $data; - } elseif (is_numeric($data) || is_string($data)) { + } else if (is_numeric($data) || is_string($data)) { // 根据关联表主键直接写入中间表 $id = $data; - } elseif ($data instanceof Model) { + } else if ($data instanceof Model) { // 根据关联表主键直接写入中间表 $id = $data->getKey(); } // 删除中间表数据 $pivot = [ - [$this->inverse ? $this->localKey : $this->morphKey, '=', $this->parent->getKey()], + [$this->localKey, '=', $this->parent->getKey()], [$this->morphType, '=', $this->morphClass], ]; if (isset($id)) { - $pivot[] = [$this->inverse ? $this->morphKey : $this->localKey, is_array($id) ? 'in' : '=', $id]; + $pivot[] = [$this->foreignKey, is_array($id) ? 'in' : '=', $id]; } $result = $this->pivot->where($pivot)->delete(); @@ -405,9 +401,9 @@ class MorphToMany extends BelongsToMany ]; $current = $this->pivot - ->where($this->inverse ? $this->localKey : $this->morphKey, $this->parent->getKey()) + ->where($this->localKey, $this->parent->getKey()) ->where($this->morphType, $this->morphClass) - ->column($this->inverse ? $this->morphKey : $this->localKey); + ->column($this->foreignKey); $records = []; @@ -430,7 +426,7 @@ class MorphToMany extends BelongsToMany if (!in_array($id, $current)) { $this->attach($id, $attributes); $changes['attached'][] = $id; - } elseif (count($attributes) > 0 && $this->attach($id, $attributes)) { + } else if (count($attributes) > 0 && $this->attach($id, $attributes)) { $changes['updated'][] = $id; } } @@ -445,10 +441,14 @@ class MorphToMany extends BelongsToMany */ protected function baseQuery(): void { - if (empty($this->baseQuery) && $this->parent->getData()) { - $this->query->where([ - [$this->morphKey, '=', $this->parent->getKey()], - [$this->morphType, '=', $this->morphClass], + if (empty($this->baseQuery)) { + $foreignKey = $this->foreignKey; + $localKey = $this->localKey; + + // 关联查询 + $this->belongsToManyQuery($foreignKey, $localKey, [ + ['pivot.' . $localKey, '=', $this->parent->getKey()], + ['pivot.' . $this->morphType, '=', $this->morphClass], ]); $this->baseQuery = true; -- Gitee From 5cc247645f80f7aee8f9d75b96ce1b9abb59c0f0 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 20 Jun 2020 22:29:16 +0800 Subject: [PATCH 180/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E7=9A=84query=E5=92=8Cexecute=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Mongo.php | 34 ------------- src/db/PDOConnection.php | 99 ++++++++++++++++++++++++-------------- src/db/Query.php | 28 ----------- src/db/connector/Mongo.php | 71 +++++++++++++++++++-------- 4 files changed, 114 insertions(+), 118 deletions(-) diff --git a/src/db/Mongo.php b/src/db/Mongo.php index c464585..5e8a09a 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -9,15 +9,12 @@ declare (strict_types = 1); namespace think\db; -use MongoDB\Driver\BulkWrite; use MongoDB\Driver\Command; use MongoDB\Driver\Cursor; use MongoDB\Driver\Exception\AuthenticationException; -use MongoDB\Driver\Exception\BulkWriteException; use MongoDB\Driver\Exception\ConnectionException; use MongoDB\Driver\Exception\InvalidArgumentException; use MongoDB\Driver\Exception\RuntimeException; -use MongoDB\Driver\Query as MongoQuery; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; use think\db\exception\DbException as Exception; @@ -31,21 +28,6 @@ class Mongo extends BaseQuery */ protected $connection; - /** - * 执行查询 返回数据集 - * @access public - * @param MongoQuery $query 查询对象 - * @return mixed - * @throws AuthenticationException - * @throws InvalidArgumentException - * @throws ConnectionException - * @throws RuntimeException - */ - public function query(MongoQuery $query) - { - return $this->connection->query($this, $query); - } - /** * 执行指令 返回数据集 * @access public @@ -64,22 +46,6 @@ class Mongo extends BaseQuery return $this->connection->command($command, $dbName, $readPreference, $typeMap); } - /** - * 执行语句 - * @access public - * @param BulkWrite $bulk - * @return int - * @throws AuthenticationException - * @throws InvalidArgumentException - * @throws ConnectionException - * @throws RuntimeException - * @throws BulkWriteException - */ - public function execute(BulkWrite $bulk) - { - return $this->connection->execute($this, $bulk); - } - /** * 执行command * @access public diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 09643ed..3289219 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -32,49 +32,49 @@ abstract class PDOConnection extends Connection */ protected $config = [ // 数据库类型 - 'type' => '', + 'type' => '', // 服务器地址 - 'hostname' => '', + 'hostname' => '', // 数据库名 - 'database' => '', + 'database' => '', // 用户名 - 'username' => '', + 'username' => '', // 密码 - 'password' => '', + 'password' => '', // 端口 - 'hostport' => '', + 'hostport' => '', // 连接dsn - 'dsn' => '', + 'dsn' => '', // 数据库连接参数 - 'params' => [], + 'params' => [], // 数据库编码默认采用utf8 - 'charset' => 'utf8', + 'charset' => 'utf8', // 数据库表前缀 - 'prefix' => '', + 'prefix' => '', // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, + 'deploy' => 0, // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, + 'rw_separate' => false, // 读写分离后 主服务器数量 - 'master_num' => 1, + 'master_num' => 1, // 指定从服务器序号 - 'slave_no' => '', + 'slave_no' => '', // 模型写入后自动读取主服务器 - 'read_master' => false, + 'read_master' => false, // 是否严格检查字段是否存在 - 'fields_strict' => true, + 'fields_strict' => true, // 开启字段缓存 - 'fields_cache' => false, + 'fields_cache' => false, // 监听SQL - 'trigger_sql' => true, + 'trigger_sql' => true, // Builder类 - 'builder' => '', + 'builder' => '', // Query类 - 'query' => '', + 'query' => '', // 是否需要断线重连 - 'break_reconnect' => false, + 'break_reconnect' => false, // 断线标识字符串 - 'break_match_str' => [], + 'break_match_str' => [], ]; /** @@ -177,7 +177,6 @@ abstract class PDOConnection extends Connection */ protected $bind = []; - /** * 获取当前连接器类对应的Query类 * @access public @@ -608,6 +607,34 @@ abstract class PDOConnection extends Connection /** * 执行查询 返回数据集 * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @return array + * @throws BindParamException + * @throws \PDOException + */ + public function query(string $sql, array $bind = []): array + { + return $this->pdoQuery($this->newQuery(), $sql, $bind); + } + + /** + * 执行语句 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @return int + * @throws BindParamException + * @throws \PDOException + */ + public function execute(string $sql, array $bind = []): int + { + return $this->pdoExecute($this->newQuery(), $sql, $bind, true); + } + + /** + * 执行查询 返回数据集 + * @access protected * @param BaseQuery $query 查询对象 * @param mixed $sql sql指令 * @param array $bind 参数绑定 @@ -617,7 +644,7 @@ abstract class PDOConnection extends Connection * @throws \Exception * @throws \Throwable */ - public function query(BaseQuery $query, $sql, array $bind = []): array + protected function pdoQuery(BaseQuery $query, $sql, array $bind = []): array { // 分析查询表达式 $query->parseOptions(); @@ -629,7 +656,7 @@ abstract class PDOConnection extends Connection $data = $this->cache->get($key); - if ($data !== null) { + if (null !== $data) { return $data; } } @@ -734,7 +761,7 @@ abstract class PDOConnection extends Connection /** * 执行语句 - * @access public + * @access protected * @param BaseQuery $query 查询对象 * @param string $sql sql指令 * @param array $bind 参数绑定 @@ -745,7 +772,7 @@ abstract class PDOConnection extends Connection * @throws \Exception * @throws \Throwable */ - public function execute(BaseQuery $query, string $sql, array $bind = [], bool $origin = false): int + protected function pdoExecute(BaseQuery $query, string $sql, array $bind = [], bool $origin = false): int { if ($origin) { $query->parseOptions(); @@ -798,7 +825,7 @@ abstract class PDOConnection extends Connection if (!$result) { // 执行查询 - $resultSet = $this->query($query, function ($query) { + $resultSet = $this->pdoQuery($query, function ($query) { return $this->builder->select($query, true); }); @@ -841,7 +868,7 @@ abstract class PDOConnection extends Connection if (!$resultSet) { // 执行查询操作 - $resultSet = $this->query($query, function ($query) { + $resultSet = $this->pdoQuery($query, function ($query) { return $this->builder->select($query); }); } @@ -865,7 +892,7 @@ abstract class PDOConnection extends Connection $sql = $this->builder->insert($query); // 执行操作 - $result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind()); + $result = '' == $sql ? 0 : $this->pdoExecute($query, $sql, $query->getBind()); if ($result) { $sequence = $options['sequence'] ?? null; @@ -925,7 +952,7 @@ abstract class PDOConnection extends Connection foreach ($array as $item) { $sql = $this->builder->insertAll($query, $item, $replace); - $count += $this->execute($query, $sql, $query->getBind()); + $count += $this->pdoExecute($query, $sql, $query->getBind()); } // 提交事务 @@ -940,7 +967,7 @@ abstract class PDOConnection extends Connection $sql = $this->builder->insertAll($query, $dataSet, $replace); - return $this->execute($query, $sql, $query->getBind()); + return $this->pdoExecute($query, $sql, $query->getBind()); } /** @@ -959,7 +986,7 @@ abstract class PDOConnection extends Connection $sql = $this->builder->selectInsert($query, $fields, $table); - return $this->execute($query, $sql, $query->getBind()); + return $this->pdoExecute($query, $sql, $query->getBind()); } /** @@ -977,7 +1004,7 @@ abstract class PDOConnection extends Connection $sql = $this->builder->update($query); // 执行操作 - $result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind()); + $result = '' == $sql ? 0 : $this->pdoExecute($query, $sql, $query->getBind()); if ($result) { $this->db->trigger('after_update', $query); @@ -1002,7 +1029,7 @@ abstract class PDOConnection extends Connection $sql = $this->builder->delete($query); // 执行操作 - $result = $this->execute($query, $sql, $query->getBind()); + $result = $this->pdoExecute($query, $sql, $query->getBind()); if ($result) { $this->db->trigger('after_delete', $query); @@ -1459,7 +1486,7 @@ abstract class PDOConnection extends Connection try { foreach ($sqlArray as $sql) { - $this->execute($query, $sql, $bind); + $this->pdoExecute($query, $sql, $bind); } // 提交事务 $this->commit(); diff --git a/src/db/Query.php b/src/db/Query.php index 62e334c..80e01cd 100644 --- a/src/db/Query.php +++ b/src/db/Query.php @@ -107,34 +107,6 @@ class Query extends BaseQuery return $this; } - /** - * 执行查询 返回数据集 - * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @return array - * @throws BindParamException - * @throws PDOException - */ - public function query(string $sql, array $bind = []): array - { - return $this->connection->query($this, $sql, $bind); - } - - /** - * 执行语句 - * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @return int - * @throws BindParamException - * @throws PDOException - */ - public function execute(string $sql, array $bind = []): int - { - return $this->connection->execute($this, $sql, $bind, true); - } - /** * 获取执行的SQL语句而不进行实际的查询 * @access public diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 8f16018..ec39939 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -256,7 +256,7 @@ class Mongo extends Connection if ($session = $this->getSession()) { $this->cursor = $this->mongo->executeQuery($namespace, $query, [ 'readPreference' => is_null($readPreference) ? new ReadPreference(ReadPreference::RP_PRIMARY) : $readPreference, - 'session' => $session + 'session' => $session, ]); } else { $this->cursor = $this->mongo->executeQuery($namespace, $mongoQuery, $readPreference); @@ -271,8 +271,39 @@ class Mongo extends Connection } /** - * 执行查询 + * 执行查询 返回数据集 + * @access public + * @param MongoQuery $query 查询对象 + * @return mixed + * @throws AuthenticationException + * @throws InvalidArgumentException + * @throws ConnectionException + * @throws RuntimeException + */ + public function query(MongoQuery $query) + { + return $this->mongoQuery($this->newQuery(), $query); + } + + /** + * 执行语句 * @access public + * @param BulkWrite $bulk + * @return int + * @throws AuthenticationException + * @throws InvalidArgumentException + * @throws ConnectionException + * @throws RuntimeException + * @throws BulkWriteException + */ + public function execute(BulkWrite $bulk) + { + return $this->mongoExecute($this->newQuery(), $bulk); + } + + /** + * 执行查询 + * @access protected * @param BaseQuery $query 查询对象 * @param MongoQuery|Closure $mongoQuery Mongo查询对象 * @return array @@ -281,7 +312,7 @@ class Mongo extends Connection * @throws ConnectionException * @throws RuntimeException */ - public function query(BaseQuery $query, $mongoQuery): array + protected function mongoQuery(BaseQuery $query, $mongoQuery): array { $options = $query->parseOptions(); @@ -315,7 +346,7 @@ class Mongo extends Connection /** * 执行写操作 - * @access public + * @access protected * @param BaseQuery $query * @param BulkWrite $bulk * @@ -326,7 +357,7 @@ class Mongo extends Connection * @throws RuntimeException * @throws BulkWriteException */ - public function execute(BaseQuery $query, BulkWrite $bulk) + protected function mongoExecute(BaseQuery $query, BulkWrite $bulk) { $this->initConnect(true); $this->db->updateQueryTimes(); @@ -348,8 +379,8 @@ class Mongo extends Connection if ($session = $this->getSession()) { $writeResult = $this->mongo->executeBulkWrite($namespace, $bulk, [ - 'session' => $session, - 'writeConcern' => is_null($writeConcern) ? new WriteConcern(1) : $writeConcern + 'session' => $session, + 'writeConcern' => is_null($writeConcern) ? new WriteConcern(1) : $writeConcern, ]); } else { $writeResult = $this->mongo->executeBulkWrite($namespace, $bulk, $writeConcern); @@ -408,7 +439,7 @@ class Mongo extends Connection if ($session = $this->getSession()) { $this->cursor = $this->mongo->executeCommand($dbName, $command, [ 'readPreference' => is_null($readPreference) ? new ReadPreference(ReadPreference::RP_PRIMARY) : $readPreference, - 'session' => $session + 'session' => $session, ]); } else { $this->cursor = $this->mongo->executeCommand($dbName, $command, $readPreference); @@ -690,7 +721,7 @@ class Mongo extends Connection // 生成bulk对象 $bulk = $this->builder->insert($query); - $writeResult = $this->execute($query, $bulk); + $writeResult = $this->mongoExecute($query, $bulk); $result = $writeResult->getInsertedCount(); if ($result) { @@ -761,7 +792,7 @@ class Mongo extends Connection // 生成bulkWrite对象 $bulk = $this->builder->insertAll($query, $dataSet); - $writeResult = $this->execute($query, $bulk); + $writeResult = $this->mongoExecute($query, $bulk); return $writeResult->getInsertedCount(); } @@ -785,7 +816,7 @@ class Mongo extends Connection // 生成bulkWrite对象 $bulk = $this->builder->update($query); - $writeResult = $this->execute($query, $bulk); + $writeResult = $this->mongoExecute($query, $bulk); $result = $writeResult->getModifiedCount(); @@ -817,7 +848,7 @@ class Mongo extends Connection $bulk = $this->builder->delete($query); // 执行操作 - $writeResult = $this->execute($query, $bulk); + $writeResult = $this->mongoExecute($query, $bulk); $result = $writeResult->getDeletedCount(); @@ -845,7 +876,7 @@ class Mongo extends Connection $resultSet = $this->db->trigger('before_select', $query); if (!$resultSet) { - $resultSet = $this->query($query, function ($query) { + $resultSet = $this->mongoQuery($query, function ($query) { return $this->builder->select($query); }); } @@ -872,7 +903,7 @@ class Mongo extends Connection if (!$result) { // 执行查询 - $resultSet = $this->query($query, function ($query) { + $resultSet = $this->mongoQuery($query, function ($query) { return $this->builder->select($query, true); }); @@ -917,7 +948,7 @@ class Mongo extends Connection } // 执行查询操作 - $resultSet = $this->query($query, $mongoQuery); + $resultSet = $this->mongoQuery($query, $mongoQuery); if (!empty($resultSet)) { $data = array_shift($resultSet); @@ -977,7 +1008,7 @@ class Mongo extends Connection } // 执行查询操作 - $resultSet = $this->query($query, $mongoQuery); + $resultSet = $this->mongoQuery($query, $mongoQuery); if (('*' == $field || strpos($field, ',')) && $key) { $result = array_column($resultSet, null, $key); @@ -1020,7 +1051,7 @@ class Mongo extends Connection return $this->command($command, $db); } - + /** * 获取数据库字段 * @access public @@ -1070,7 +1101,7 @@ class Mongo extends Connection public function startTrans() { $this->initConnect(true); - $this->session_uuid = uniqid(); + $this->session_uuid = uniqid(); $this->sessions[$this->session_uuid] = $this->getMongo()->startSession(); $this->sessions[$this->session_uuid]->startTransaction([]); @@ -1130,7 +1161,7 @@ class Mongo extends Connection public function getSession() { return ($this->session_uuid && isset($this->sessions[$this->session_uuid])) - ? $this->sessions[$this->session_uuid] - : null; + ? $this->sessions[$this->session_uuid] + : null; } } -- Gitee From a03c5ec03a65063813c5fca9c019faed5cb0ac44 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 21 Jun 2020 22:08:36 +0800 Subject: [PATCH 181/365] =?UTF-8?q?=E7=AE=80=E5=8C=96=E4=BA=8B=E5=8A=A1?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/Transaction.php | 43 ---------------------------------- 1 file changed, 43 deletions(-) diff --git a/src/db/concern/Transaction.php b/src/db/concern/Transaction.php index f804ae2..13f86d8 100644 --- a/src/db/concern/Transaction.php +++ b/src/db/concern/Transaction.php @@ -71,47 +71,4 @@ trait Transaction } } - /** - * 执行数据库事务 - * @access public - * @param callable $callback 数据操作方法回调 - * @return mixed - */ - public function transaction(callable $callback) - { - return $this->connection->transaction($callback); - } - - /** - * 启动事务 - * @access public - * @return void - */ - public function startTrans(): void - { - $this->connection->startTrans(); - } - - /** - * 用于非自动提交状态下面的查询提交 - * @access public - * @return void - * @throws PDOException - */ - public function commit(): void - { - $this->connection->commit(); - } - - /** - * 事务回滚 - * @access public - * @return void - * @throws PDOException - */ - public function rollback(): void - { - $this->connection->rollback(); - } - } -- Gitee From 0d4b9d061fc5e603bccc874d6d1975dd7b8debfa Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 21 Jun 2020 22:56:05 +0800 Subject: [PATCH 182/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E8=A7=86=E5=9B=BE?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=20query=E6=9F=A5=E8=AF=A2=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0master=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 3289219..540f135 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -544,6 +544,17 @@ abstract class PDOConnection extends Connection } } + /** + * 视图查询 + * @access public + * @param array $args + * @return BaseQuery + */ + public function view(...$args) + { + return $this->newQuery()->view(...$args); + } + /** * 创建PDO实例 * @param $dsn @@ -609,13 +620,14 @@ abstract class PDOConnection extends Connection * @access public * @param string $sql sql指令 * @param array $bind 参数绑定 + * @param bool $master 主库读取 * @return array * @throws BindParamException * @throws \PDOException */ - public function query(string $sql, array $bind = []): array + public function query(string $sql, array $bind = [], bool $master = false): array { - return $this->pdoQuery($this->newQuery(), $sql, $bind); + return $this->pdoQuery($this->newQuery(), $sql, $bind, $master); } /** @@ -638,13 +650,14 @@ abstract class PDOConnection extends Connection * @param BaseQuery $query 查询对象 * @param mixed $sql sql指令 * @param array $bind 参数绑定 + * @param bool $master 主库读取 * @return array * @throws BindParamException * @throws \PDOException * @throws \Exception * @throws \Throwable */ - protected function pdoQuery(BaseQuery $query, $sql, array $bind = []): array + protected function pdoQuery(BaseQuery $query, $sql, array $bind = [], bool $master = null): array { // 分析查询表达式 $query->parseOptions(); @@ -666,7 +679,10 @@ abstract class PDOConnection extends Connection $bind = $query->getBind(); } - $master = $query->getOptions('master') ? true : false; + if (!isset($master)) { + $master = $query->getOptions('master') ? true : false; + } + $procedure = $query->getOptions('procedure') ? true : in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); $this->getPDOStatement($sql, $bind, $master, $procedure); -- Gitee From 35ca511a1e4d671b39f7afb4c887703c16ef6957 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 22 Jun 2020 22:57:28 +0800 Subject: [PATCH 183/365] =?UTF-8?q?=E8=BF=98=E5=8E=9FQuery=E7=B1=BB?= =?UTF-8?q?=E7=9A=84=E4=BA=8B=E5=8A=A1=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/Transaction.php | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/db/concern/Transaction.php b/src/db/concern/Transaction.php index 13f86d8..f804ae2 100644 --- a/src/db/concern/Transaction.php +++ b/src/db/concern/Transaction.php @@ -71,4 +71,47 @@ trait Transaction } } + /** + * 执行数据库事务 + * @access public + * @param callable $callback 数据操作方法回调 + * @return mixed + */ + public function transaction(callable $callback) + { + return $this->connection->transaction($callback); + } + + /** + * 启动事务 + * @access public + * @return void + */ + public function startTrans(): void + { + $this->connection->startTrans(); + } + + /** + * 用于非自动提交状态下面的查询提交 + * @access public + * @return void + * @throws PDOException + */ + public function commit(): void + { + $this->connection->commit(); + } + + /** + * 事务回滚 + * @access public + * @return void + * @throws PDOException + */ + public function rollback(): void + { + $this->connection->rollback(); + } + } -- Gitee From fabaac7e7aa37a847f357edbe631cd3d069f1183 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 27 Jun 2020 22:35:55 +0800 Subject: [PATCH 184/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=A4=9A=E5=AF=B9?= =?UTF-8?q?=E5=A4=9A=E5=85=B3=E8=81=94=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsToMany.php | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index a3c8aa4..df8fb21 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -440,19 +440,22 @@ class BelongsToMany extends Relation protected function belongsToManyQuery(string $foreignKey, string $localKey, array $condition = []): Query { // 关联查询封装 - $tableName = $this->query->getTable(); - $table = $this->pivot->db()->getTable(); - $fields = $this->getQueryFields($tableName); + if (empty($this->baseQuery)) { + $tableName = $this->query->getTable(); + $table = $this->pivot->db()->getTable(); + $fields = $this->getQueryFields($tableName); - if ($this->withLimit) { - $this->query->limit($this->withLimit); - } + if ($this->withLimit) { + $this->query->limit($this->withLimit); + } - $this->query - ->field($fields) - ->tableField(true, $table, 'pivot', 'pivot__') - ->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $this->query->getPk()) - ->where($condition); + $this->query + ->field($fields) + ->tableField(true, $table, 'pivot', 'pivot__') + ->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $this->query->getPk()) + ->where($condition); + + } return $this->query; } -- Gitee From c479283e45db530e1b0df4ba6736d24d5036568b Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 29 Jun 2020 13:00:54 +0800 Subject: [PATCH 185/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E7=9A=84=E8=BF=9E=E6=8E=A5=E5=BC=82=E5=B8=B8=E6=8D=95?= =?UTF-8?q?=E8=8E=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 540f135..3b1fca0 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -728,16 +728,13 @@ abstract class PDOConnection extends Connection */ public function getPDOStatement(string $sql, array $bind = [], bool $master = false, bool $procedure = false): PDOStatement { - $this->initConnect($this->readMaster ?: $master); - - // 记录SQL语句 - $this->queryStr = $sql; - - $this->bind = $bind; - - $this->db->updateQueryTimes(); - try { + $this->initConnect($this->readMaster ?: $master); + // 记录SQL语句 + $this->queryStr = $sql; + $this->bind = $bind; + + $this->db->updateQueryTimes(); $this->queryStartTime = microtime(true); // 预处理 @@ -1394,11 +1391,11 @@ abstract class PDOConnection extends Connection */ public function startTrans(): void { - $this->initConnect(true); + try { + $this->initConnect(true); - ++$this->transTimes; + ++$this->transTimes; - try { if (1 == $this->transTimes) { $this->linkID->beginTransaction(); } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { -- Gitee From 9b28eb9e529842929c8008e8b402a184fd38eb4c Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 6 Jul 2020 14:09:52 +0800 Subject: [PATCH 186/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bwhen=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E6=9F=A5=E8=AF=A2=E7=9A=84=E9=97=AD=E5=8C=85=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/WhereQuery.php | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/db/concern/WhereQuery.php b/src/db/concern/WhereQuery.php index 62dc983..5b57736 100644 --- a/src/db/concern/WhereQuery.php +++ b/src/db/concern/WhereQuery.php @@ -514,17 +514,9 @@ trait WhereQuery } if ($condition) { - if ($query instanceof Closure) { - $query($this, $condition); - } elseif (is_array($query)) { - $this->where($query); - } + $this->where($query); } elseif ($otherwise) { - if ($otherwise instanceof Closure) { - $otherwise($this, $condition); - } elseif (is_array($otherwise)) { - $this->where($otherwise); - } + $this->where($otherwise); } return $this; -- Gitee From 4b30a083d9768ef5b2671a24d5f5716c1c043330 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 7 Jul 2020 17:44:07 +0800 Subject: [PATCH 187/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E4=B8=80=E5=AF=B9?= =?UTF-8?q?=E4=B8=80=E5=85=B3=E8=81=94=E6=9F=A5=E8=AF=A2=E5=AF=B9order?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/OneToOne.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index efb2359..65bbfda 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -307,10 +307,6 @@ abstract class OneToOne extends Relation $this->query->field($this->withField); } - if ($this->query->getOptions('order')) { - $this->query->group($key); - } - $list = $this->query ->where($where) ->with($subRelation) -- Gitee From 5b61928ac4cd7377ee2ec72167e5dc37e4883226 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 12 Jul 2020 21:24:50 +0800 Subject: [PATCH 188/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BDbException=E7=B1=BB?= =?UTF-8?q?=E7=BB=A7=E6=89=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/exception/DbException.php | 135 +++++++++++++++++++------------ 1 file changed, 82 insertions(+), 53 deletions(-) diff --git a/src/db/exception/DbException.php b/src/db/exception/DbException.php index d5bfc3f..7d1deaa 100644 --- a/src/db/exception/DbException.php +++ b/src/db/exception/DbException.php @@ -17,65 +17,94 @@ use Exception; /** * Database相关异常处理类 */ -class DbException extends Exception -{ - /** - * DbException constructor. - * @access public - * @param string $message - * @param array $config - * @param string $sql - * @param int $code - */ - public function __construct(string $message, array $config = [], string $sql = '', int $code = 10500) +if (class_exists('think\Exception')) { + class DbException extends \think\Exception { - $this->message = $message; - $this->code = $code; + /** + * DbException constructor. + * @access public + * @param string $message + * @param array $config + * @param string $sql + * @param int $code + */ + public function __construct(string $message, array $config = [], string $sql = '', int $code = 10500) + { + $this->message = $message; + $this->code = $code; - $this->setData('Database Status', [ - 'Error Code' => $code, - 'Error Message' => $message, - 'Error SQL' => $sql, - ]); + $this->setData('Database Status', [ + 'Error Code' => $code, + 'Error Message' => $message, + 'Error SQL' => $sql, + ]); - unset($config['username'], $config['password']); - $this->setData('Database Config', $config); + unset($config['username'], $config['password']); + $this->setData('Database Config', $config); + } } +} else { - /** - * 保存异常页面显示的额外Debug数据 - * @var array - */ - protected $data = []; - - /** - * 设置异常额外的Debug数据 - * 数据将会显示为下面的格式 - * - * Exception Data - * -------------------------------------------------- - * Label 1 - * key1 value1 - * key2 value2 - * Label 2 - * key1 value1 - * key2 value2 - * - * @param string $label 数据分类,用于异常页面显示 - * @param array $data 需要显示的数据,必须为关联数组 - */ - final protected function setData($label, array $data) + class DbException extends Exception { - $this->data[$label] = $data; - } + /** + * DbException constructor. + * @access public + * @param string $message + * @param array $config + * @param string $sql + * @param int $code + */ + public function __construct(string $message, array $config = [], string $sql = '', int $code = 10500) + { + $this->message = $message; + $this->code = $code; - /** - * 获取异常额外Debug数据 - * 主要用于输出到异常页面便于调试 - * @return array 由setData设置的Debug数据 - */ - final public function getData() - { - return $this->data; + $this->setData('Database Status', [ + 'Error Code' => $code, + 'Error Message' => $message, + 'Error SQL' => $sql, + ]); + + unset($config['username'], $config['password']); + $this->setData('Database Config', $config); + } + + /** + * 保存异常页面显示的额外Debug数据 + * @var array + */ + protected $data = []; + + /** + * 设置异常额外的Debug数据 + * 数据将会显示为下面的格式 + * + * Exception Data + * -------------------------------------------------- + * Label 1 + * key1 value1 + * key2 value2 + * Label 2 + * key1 value1 + * key2 value2 + * + * @param string $label 数据分类,用于异常页面显示 + * @param array $data 需要显示的数据,必须为关联数组 + */ + final protected function setData($label, array $data) + { + $this->data[$label] = $data; + } + + /** + * 获取异常额外Debug数据 + * 主要用于输出到异常页面便于调试 + * @return array 由setData设置的Debug数据 + */ + final public function getData() + { + return $this->data; + } } } -- Gitee From 3c0323c2b7a6b76054a199679a2a69ada732b87b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=92=8B=E6=96=87=E5=81=A5?= Date: Fri, 3 Jul 2020 15:00:48 +0800 Subject: [PATCH 189/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=A4=9A=E5=AF=B9?= =?UTF-8?q?=E5=A4=9A=E5=85=B3=E8=81=94=E6=9F=A5=E8=AF=A2=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE#161?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsToMany.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index df8fb21..6b64d95 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -669,7 +669,11 @@ class BelongsToMany extends Relation $localKey = $this->localKey; // 关联查询 - $condition = ['pivot.' . $localKey, '=', $this->parent->getKey()]; + if (null === $this->parent->getKey()) { + $condition = ['pivot.' . $localKey, 'exp', new Raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk())]; + } else { + $condition = ['pivot.' . $localKey, '=', $this->parent->getKey()]; + } $this->belongsToManyQuery($foreignKey, $localKey, [$condition]); -- Gitee From 6738a32cf227840c0297c3eea905a7b9d43dd11d Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 14 Jul 2020 14:40:45 +0800 Subject: [PATCH 190/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=8F=AA=E8=AF=BB?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 9e4367b..8b6649d 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -302,7 +302,7 @@ trait Attribute // 只读字段不允许更新 foreach ($this->readonly as $key => $field) { - if (isset($data[$field])) { + if (array_key_exists($field, $data)) { unset($data[$field]); } } -- Gitee From 594cbb0a0c91e46021446faa834254dd173a2cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=88=E5=94=81?= <52o@qq52o.cn> Date: Thu, 23 Jul 2020 15:52:31 +0800 Subject: [PATCH 191/365] Update HasOne.php --- src/model/relation/HasOne.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index d0dc21d..7fcd20a 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -181,7 +181,7 @@ class HasOne extends OneToOne $fields = $this->getRelationQueryFields($fields, $model); $softDelete = $this->query->getOptions('soft_delete'); - $query = $query ?: $this->parent->db()->alias($model); + $query = $query ? $query->alias($model) : $this->parent->db()->alias($model); return $query->field($fields) ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType ?: $this->joinType) -- Gitee From 2970eca82e173b4a8857114208778fb1aea8cd4e Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 4 Aug 2020 20:54:42 +0800 Subject: [PATCH 192/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bwhere=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/WhereQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/concern/WhereQuery.php b/src/db/concern/WhereQuery.php index 5b57736..a95c697 100644 --- a/src/db/concern/WhereQuery.php +++ b/src/db/concern/WhereQuery.php @@ -378,7 +378,7 @@ trait WhereQuery } elseif (is_string($field)) { if (preg_match('/[,=\<\'\"\(\s]/', $field)) { return $this->whereRaw($field, is_array($op) ? $op : [], $logic); - } elseif (is_string($op) && strtolower($op) == 'exp') { + } elseif (is_string($op) && strtolower($op) == 'exp' && !is_null($condition)) { $bind = isset($param[2]) && is_array($param[2]) ? $param[2] : []; return $this->whereExp($field, $condition, $bind, $logic); } -- Gitee From 08ed926d399216b4f3b76a80be25d96a89b00568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=AE=E8=B1=AA=E5=BD=AA?= Date: Mon, 3 Aug 2020 17:49:27 +0800 Subject: [PATCH 193/365] =?UTF-8?q?startTrans()=E6=96=B9=E6=B3=95=E5=B9=B6?= =?UTF-8?q?=E6=9C=AA=E7=BB=88=E6=AD=A2=E6=8A=9B=E5=BC=82=E5=B8=B8=EF=BC=8C?= =?UTF-8?q?=E9=87=8D=E8=BF=9E=E6=88=90=E5=8A=9F=E7=BB=88=E6=AD=A2=E6=8A=9B?= =?UTF-8?q?=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 3b1fca0..0db99dc 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1409,8 +1409,9 @@ abstract class PDOConnection extends Connection --$this->transTimes; ++$this->reConnectTimes; $this->close()->startTrans(); + }else { + throw $e; } - throw $e; } } -- Gitee From 9e709e62d2f3c1fe2c1637c27cb4b5d2f2256667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=AE=E8=B1=AA=E5=BD=AA?= Date: Mon, 3 Aug 2020 18:09:02 +0800 Subject: [PATCH 194/365] =?UTF-8?q?startTrans()=E6=96=B9=E6=B3=95=E5=B9=B6?= =?UTF-8?q?=E6=9C=AA=E7=BB=88=E6=AD=A2=E6=8A=9B=E5=BC=82=E5=B8=B8=EF=BC=8C?= =?UTF-8?q?=E9=87=8D=E8=BF=9E=E6=88=90=E5=8A=9F=E7=BB=88=E6=AD=A2=E6=8A=9B?= =?UTF-8?q?=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 0db99dc..7f17264 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1409,7 +1409,7 @@ abstract class PDOConnection extends Connection --$this->transTimes; ++$this->reConnectTimes; $this->close()->startTrans(); - }else { + } else { throw $e; } } -- Gitee From b608205c03a6360cd961a61c95abcae9d2572dcd Mon Sep 17 00:00:00 2001 From: bison Date: Thu, 3 Sep 2020 12:05:33 +0800 Subject: [PATCH 195/365] =?UTF-8?q?upd:=20=E4=BF=AE=E6=94=B9PDOConnector?= =?UTF-8?q?=E6=8A=BD=E8=B1=A1=E6=96=B9=E6=B3=95=E5=8F=82=E6=95=B0=E5=92=8C?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E7=B1=BB=E5=9E=8B=EF=BC=8C=E4=BF=9D=E6=8C=81?= =?UTF-8?q?=E4=B8=8E=E5=AD=90=E7=B1=BB=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 7f17264..e8a1dbf 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -203,7 +203,7 @@ abstract class PDOConnection extends Connection * @param array $config 连接信息 * @return string */ - abstract protected function parseDsn(array $config); + abstract protected function parseDsn(array $config): string; /** * 取得数据表的字段信息 @@ -211,7 +211,7 @@ abstract class PDOConnection extends Connection * @param string $tableName 数据表名称 * @return array */ - abstract public function getFields(string $tableName); + abstract public function getFields(string $tableName): array; /** * 取得数据库的表信息 @@ -219,7 +219,7 @@ abstract class PDOConnection extends Connection * @param string $dbName 数据库名称 * @return array */ - abstract public function getTables(string $dbName); + abstract public function getTables(string $dbName = ''): array; /** * 对返数据表字段信息进行大小写转换出来 -- Gitee From 04285f467a90dd1bad13b1c15c4b2782d174f178 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 14 Sep 2020 19:29:01 +0800 Subject: [PATCH 196/365] =?UTF-8?q?Relation=E7=B1=BB=E5=A2=9E=E5=8A=A0getF?= =?UTF-8?q?oreignKey=E5=92=8CgetLocalKey=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Relation.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/model/Relation.php b/src/model/Relation.php index d9d02e3..e823bd9 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -99,6 +99,26 @@ abstract class Relation return $this->query; } + /** + * 获取关联表外键 + * @access public + * @return string + */ + public function getForeignKey() + { + return $this->foreignKey; + } + + /** + * 获取关联表主键 + * @access public + * @return string + */ + public function getLocalKey() + { + return $this->localKey; + } + /** * 获取当前的关联模型类的实例 * @access public -- Gitee From ce2695d5ad7b8639f666bd88a6ae681299826515 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 28 Sep 2020 15:53:13 +0800 Subject: [PATCH 197/365] =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E7=B1=BB=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0convertNameToCamel=E5=B1=9E=E6=80=A7=20=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E8=AE=BE=E7=BD=AE=E6=A8=A1=E5=9E=8B=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=9A=84=E5=91=BD=E5=90=8D=E5=BC=BA=E5=88=B6=E9=87=87=E7=94=A8?= =?UTF-8?q?=E9=A9=BC=E5=B3=B0=E5=91=BD=E5=90=8D=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Conversion.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index e12d0af..f81850c 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -48,6 +48,24 @@ trait Conversion */ protected $resultSetType; + /** + * 数据命名是否自动转为驼峰 + * @var bool + */ + protected $convertNameToCamel; + + /** + * 转换数据为驼峰命名(用于输出) + * @access public + * @param bool $toCamel 是否自动驼峰命名 + * @return $this + */ + public function convertNameToCamel(bool $toCamel = true) + { + $this->convertNameToCamel = $toCamel; + return $this; + } + /** * 设置需要附加的输出属性 * @access public @@ -181,6 +199,16 @@ trait Conversion $this->appendAttrToArray($item, $key, $name); } + if ($this->convertNameToCamel) { + foreach ($item as $key => $val) { + $name = Str::camel($key); + if ($name !== $key) { + $item[$name] = $val; + unset($item[$key]); + } + } + } + return $item; } -- Gitee From 57f9b98895b0ff4ae7b7b75e51456fd8cb8fb629 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 28 Sep 2020 16:24:57 +0800 Subject: [PATCH 198/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8Bget?= =?UTF-8?q?RealFieldName=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 8b6649d..6358bf8 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -185,7 +185,11 @@ trait Attribute */ protected function getRealFieldName(string $name): string { - return $this->strict ? $name : Str::snake($name); + if ($this->convertNameToCamel || !$this->strict) { + return Str::snake($name); + } + + return $name; } /** -- Gitee From 56e70de8445537df6c818acbf4215b2842f8398d Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 3 Oct 2020 18:36:05 +0800 Subject: [PATCH 199/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0mysql=20json=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=AD=97=E6=AE=B5->>=E6=96=B9=E5=BC=8F=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/builder/Mysql.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/db/builder/Mysql.php b/src/db/builder/Mysql.php index df6bffd..10c8d1d 100644 --- a/src/db/builder/Mysql.php +++ b/src/db/builder/Mysql.php @@ -313,10 +313,15 @@ class Mysql extends Builder $key = trim($key); - if (strpos($key, '->') && false === strpos($key, '(')) { + if (strpos($key, '->>') && false === strpos($key, '(')) { + // JSON字段支持 + [$field, $name] = explode('->>', $key, 2); + + return $this->parseKey($query, $field, true) . '->>\'$' . (strpos($name, '[') === 0 ? '' : '.') . str_replace('->>', '.', $name) . '\''; + } elseif (strpos($key, '->') && false === strpos($key, '(')) { // JSON字段支持 [$field, $name] = explode('->', $key, 2); - return 'json_extract(' . $this->parseKey($query, $field) . ', \'$' . (strpos($name, '[') === 0 ? '' : '.') . str_replace('->', '.', $name) . '\')'; + return 'json_extract(' . $this->parseKey($query, $field, true) . ', \'$' . (strpos($name, '[') === 0 ? '' : '.') . str_replace('->', '.', $name) . '\')'; } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { [$table, $key] = explode('.', $key, 2); -- Gitee From a0dd4c2ef90d9935191a301413bf38b2e38b841e Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 3 Oct 2020 18:37:19 +0800 Subject: [PATCH 200/365] =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E5=A4=84=E7=90=86=20=E4=B8=8D=E7=BA=B3?= =?UTF-8?q?=E5=85=A5=E8=8E=B7=E5=8F=96=E5=99=A8=E5=92=8C=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 5 +++++ src/model/concern/Attribute.php | 38 +++++++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/Model.php b/src/Model.php index 7d92bd2..225eb1f 100644 --- a/src/Model.php +++ b/src/Model.php @@ -266,6 +266,8 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab */ public function newInstance(array $data = [], $where = null): Model { + $this->readDataType($data); + $model = new static($data); if ($this->connection) { @@ -607,6 +609,8 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } return true; + } else { + $this->writeDataType($data); } if ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) { @@ -682,6 +686,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } $this->checkData(); + $this->writeDataType($this->data); // 检查允许字段 $allowFields = $this->checkAllowFields(); diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 6358bf8..a22d052 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -374,9 +374,6 @@ trait Attribute if (is_null($value) && $array !== $this->data) { return; } - } elseif (isset($this->type[$name])) { - // 类型转换 - $value = $this->writeTransform($value, $this->type[$name]); } } @@ -506,9 +503,6 @@ trait Attribute } $value = $this->$method($value, $this->data); - } elseif (isset($this->type[$fieldName])) { - // 类型转换 - $value = $this->readTransform($value, $this->type[$fieldName]); } elseif ($this->autoWriteTimestamp && in_array($fieldName, [$this->createTime, $this->updateTime])) { $value = $this->getTimestampValue($value); } elseif ($relation) { @@ -520,6 +514,38 @@ trait Attribute return $value; } + /** + * 读取数据类型处理 + * @access protected + * @param array $data 数据 + * @return void + */ + protected function readDataType(array $data): void + { + foreach ($data as $name => &$value) { + if (isset($this->type[$name])) { + // 类型转换 + $value = $this->readTransform($value, $this->type[$name]); + } + } + } + + /** + * 写入数据类型处理 + * @access protected + * @param array $data 数据 + * @return void + */ + protected function writeDataType(array $data): void + { + foreach ($data as $name => &$value) { + if (isset($this->type[$name])) { + // 类型转换 + $value = $this->writeTransform($value, $this->type[$name]); + } + } + } + /** * 获取JSON字段属性值 * @access protected -- Gitee From 2d6f1ce61da498911e9a2cd20736c049b5c056e3 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 4 Oct 2020 21:39:49 +0800 Subject: [PATCH 201/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index a22d052..2ba9da9 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -520,7 +520,7 @@ trait Attribute * @param array $data 数据 * @return void */ - protected function readDataType(array $data): void + protected function readDataType(array &$data): void { foreach ($data as $name => &$value) { if (isset($this->type[$name])) { @@ -536,7 +536,7 @@ trait Attribute * @param array $data 数据 * @return void */ - protected function writeDataType(array $data): void + protected function writeDataType(array &$data): void { foreach ($data as $name => &$value) { if (isset($this->type[$name])) { -- Gitee From 2cd57e0508212ca436b15438529f02c388a0e695 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 4 Oct 2020 22:24:13 +0800 Subject: [PATCH 202/365] =?UTF-8?q?=E6=97=B6=E9=97=B4=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=A4=84=E7=90=86=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 2ba9da9..fcc2428 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -358,22 +358,17 @@ trait Attribute return; } - if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { - // 自动写入的时间戳字段 - $value = $this->autoWriteTimestamp(); - } else { - // 检测修改器 - $method = 'set' . Str::studly($name) . 'Attr'; + // 检测修改器 + $method = 'set' . Str::studly($name) . 'Attr'; - if (method_exists($this, $method)) { - $array = $this->data; + if (method_exists($this, $method)) { + $array = $this->data; - $value = $this->$method($value, array_merge($this->data, $data)); + $value = $this->$method($value, array_merge($this->data, $data)); - $this->set[$name] = true; - if (is_null($value) && $array !== $this->data) { - return; - } + $this->set[$name] = true; + if (is_null($value) && $array !== $this->data) { + return; } } @@ -503,8 +498,6 @@ trait Attribute } $value = $this->$method($value, $this->data); - } elseif ($this->autoWriteTimestamp && in_array($fieldName, [$this->createTime, $this->updateTime])) { - $value = $this->getTimestampValue($value); } elseif ($relation) { $value = $this->getRelationValue($relation); // 保存关联对象值 @@ -526,6 +519,8 @@ trait Attribute if (isset($this->type[$name])) { // 类型转换 $value = $this->readTransform($value, $this->type[$name]); + } elseif ($this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { + $value = $this->getTimestampValue($value); } } } @@ -542,6 +537,9 @@ trait Attribute if (isset($this->type[$name])) { // 类型转换 $value = $this->writeTransform($value, $this->type[$name]); + } elseif (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { + // 自动写入的时间戳字段 + $value = $this->autoWriteTimestamp(); } } } -- Gitee From 66af64126cf289a811fe914623a666b7b668fef8 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 12 Oct 2020 22:28:51 +0800 Subject: [PATCH 203/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E8=BE=93=E5=87=BA=E7=9A=84=E6=98=A0=E5=B0=84?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Conversion.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index f81850c..bc1bec2 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -42,6 +42,12 @@ trait Conversion */ protected $append = []; + /** + * 数据输出字段映射 + * @var array + */ + protected $mapping = []; + /** * 数据集对象名 * @var string @@ -137,6 +143,19 @@ trait Conversion return $this; } + /** + * 设置属性的映射输出 + * @access public + * @param array $map + * @return $this + */ + public function mapping(array $map) + { + $this->mapping = $map; + + return $this; + } + /** * 转换当前模型对象为数组 * @access public @@ -192,6 +211,13 @@ trait Conversion } elseif (!isset($this->hidden[$key]) && !$hasVisible) { $item[$key] = $this->getAttr($key); } + + if (isset($this->mapping[$key])) { + // 检查字段映射 + $mapName = $this->mapping[$key]; + $item[$mapName] = $item[$key]; + unset($item[$key]); + } } // 追加属性(必须定义获取器) -- Gitee From 1974f5adbe38bf58eea51d17b4d13e9f4d5cd5f0 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 15 Nov 2020 21:53:03 +0800 Subject: [PATCH 204/365] =?UTF-8?q?=E8=BF=98=E5=8E=9Fwhen=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E8=B0=83=E6=95=B4=20=E7=94=A8=E4=BA=8E=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=9C=A8=E9=97=AD=E5=8C=85=E5=86=85=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=9D=A1=E4=BB=B6=E4=B9=8B=E5=A4=96=E7=9A=84?= =?UTF-8?q?=E9=93=BE=E5=BC=8F=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/WhereQuery.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/db/concern/WhereQuery.php b/src/db/concern/WhereQuery.php index a95c697..1311628 100644 --- a/src/db/concern/WhereQuery.php +++ b/src/db/concern/WhereQuery.php @@ -514,9 +514,17 @@ trait WhereQuery } if ($condition) { - $this->where($query); + if ($query instanceof Closure) { + $query($this, $condition); + } elseif (is_array($query)) { + $this->where($query); + } } elseif ($otherwise) { - $this->where($otherwise); + if ($otherwise instanceof Closure) { + $otherwise($this, $condition); + } elseif (is_array($otherwise)) { + $this->where($otherwise); + } } return $this; -- Gitee From d26c1ff31f965bfd9c139a8e3ee2391fb3d74b8d Mon Sep 17 00:00:00 2001 From: Qichang He Date: Wed, 11 Nov 2020 10:33:24 +0800 Subject: [PATCH 205/365] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20column=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95=20field=20=E5=AD=97=E6=AE=B5=E4=BC=A0=20'*'=20?= =?UTF-8?q?=E6=97=B6=E8=BF=94=E5=9B=9E=E7=A9=BA=E6=95=B0=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index e8a1dbf..3aeee81 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1191,7 +1191,7 @@ abstract class PDOConnection extends Connection $key = null; } - if (strpos($column, ',')) { + if ('*' == $column || strpos($column, ',')) { $column = null; } elseif (strpos($column, ' ')) { $column = substr(strrchr(trim($column), ' '), 1); -- Gitee From 2023e514d2b471bcad665cbf3752e39774b4ec77 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 26 Nov 2020 22:28:01 +0800 Subject: [PATCH 206/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BgetRealSql=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=85=BC=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 3aeee81..eabd423 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1236,8 +1236,8 @@ abstract class PDOConnection extends Connection // 判断占位符 $sql = is_numeric($key) ? - substr_replace($sql, $value, strpos($sql, '?'), 1) : - substr_replace($sql, $value, strpos($sql, ':' . $key), strlen(':' . $key)); + substr_replace($sql, (string) $value, strpos($sql, '?'), 1) : + substr_replace($sql, (string) $value, strpos($sql, ':' . $key), strlen(':' . $key)); } return rtrim($sql); -- Gitee From aa32153d8eb59d26e26711d23e9e84a0a9ba14ec Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 26 Nov 2020 22:39:04 +0800 Subject: [PATCH 207/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BgetRealSql=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=85=BC=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 3aeee81..eabd423 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1236,8 +1236,8 @@ abstract class PDOConnection extends Connection // 判断占位符 $sql = is_numeric($key) ? - substr_replace($sql, $value, strpos($sql, '?'), 1) : - substr_replace($sql, $value, strpos($sql, ':' . $key), strlen(':' . $key)); + substr_replace($sql, (string) $value, strpos($sql, '?'), 1) : + substr_replace($sql, (string) $value, strpos($sql, ':' . $key), strlen(':' . $key)); } return rtrim($sql); -- Gitee From 7e043f2742ece2561478c215832bb85e8caf13f6 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 13 Dec 2020 23:36:29 +0800 Subject: [PATCH 208/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E6=8D=95=E8=8E=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/exception/PDOException.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/db/exception/PDOException.php b/src/db/exception/PDOException.php index 41c0d73..efe78b9 100644 --- a/src/db/exception/PDOException.php +++ b/src/db/exception/PDOException.php @@ -28,14 +28,17 @@ class PDOException extends DbException */ public function __construct(\PDOException $exception, array $config = [], string $sql = '', int $code = 10501) { - $error = $exception->errorInfo; + $error = $exception->errorInfo; + $message = $exception->getMessage(); - $this->setData('PDO Error Info', [ - 'SQLSTATE' => $error[0], - 'Driver Error Code' => isset($error[1]) ? $error[1] : 0, - 'Driver Error Message' => isset($error[2]) ? $error[2] : '', - ]); + if (!empty($error)) { + $this->setData('PDO Error Info', [ + 'SQLSTATE' => $error[0], + 'Driver Error Code' => isset($error[1]) ? $error[1] : 0, + 'Driver Error Message' => isset($error[2]) ? $error[2] : '', + ]); + } - parent::__construct($exception->getMessage(), $config, $sql, $code); + parent::__construct($message, $config, $sql, $code); } } -- Gitee From b70e1a0e95038978a3ab2c528cb897744bdc97e7 Mon Sep 17 00:00:00 2001 From: Tiger <75706802@qq.com> Date: Thu, 15 Oct 2020 18:17:56 +0800 Subject: [PATCH 209/365] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E8=A1=8C=E7=94=9F=E6=88=90=E6=95=B0=E6=8D=AE=E5=BA=93=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E7=BC=93=E5=AD=98=E4=B8=BA=E7=A9=BA=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/connector/Sqlsrv.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/connector/Sqlsrv.php b/src/db/connector/Sqlsrv.php index 63b2df0..10d944f 100644 --- a/src/db/connector/Sqlsrv.php +++ b/src/db/connector/Sqlsrv.php @@ -56,7 +56,7 @@ class Sqlsrv extends PDOConnection public function getFields(string $tableName): array { [$tableName] = explode(' ', $tableName); - + strpos($tableName,'.') && $tableName = substr($tableName,strpos($tableName,'.') + 1); $sql = "SELECT column_name, data_type, column_default, is_nullable FROM information_schema.tables AS t JOIN information_schema.columns AS c -- Gitee From 3fa7226aff0f58623efcf03691b00d363318848a Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 6 Jan 2021 20:21:37 +0800 Subject: [PATCH 210/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BupdateTime=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=86=99=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Model.php b/src/Model.php index 225eb1f..cee7bc3 100644 --- a/src/Model.php +++ b/src/Model.php @@ -609,11 +609,11 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } return true; - } else { - $this->writeDataType($data); } - if ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) { + $this->writeDataType($data); + + if ($this->autoWriteTimestamp && $this->updateTime && (!isset($data[$this->updateTime]) || is_object($data[$this->updateTime]))) { // 自动写入更新时间 $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); $this->data[$this->updateTime] = $data[$this->updateTime]; -- Gitee From f0bba0b15610d4015655ff6f07ea3d639212076f Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 6 Jan 2021 20:55:12 +0800 Subject: [PATCH 211/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E6=88=B3=E8=87=AA=E5=8A=A8=E5=86=99=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Model.php b/src/Model.php index cee7bc3..2b4b2e9 100644 --- a/src/Model.php +++ b/src/Model.php @@ -613,9 +613,9 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $this->writeDataType($data); - if ($this->autoWriteTimestamp && $this->updateTime && (!isset($data[$this->updateTime]) || is_object($data[$this->updateTime]))) { + if ($this->autoWriteTimestamp && $this->updateTime) { // 自动写入更新时间 - $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); + $data[$this->updateTime] = $this->autoWriteTimestamp(); $this->data[$this->updateTime] = $data[$this->updateTime]; } @@ -673,11 +673,11 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab // 时间戳自动写入 if ($this->autoWriteTimestamp) { if ($this->createTime && !isset($this->data[$this->createTime])) { - $this->data[$this->createTime] = $this->autoWriteTimestamp($this->createTime); + $this->data[$this->createTime] = $this->autoWriteTimestamp(); } if ($this->updateTime && !isset($this->data[$this->updateTime])) { - $this->data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); + $this->data[$this->updateTime] = $this->autoWriteTimestamp(); } } -- Gitee From f48dc09050f25029d41a66bfc9c3c403e4f82024 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 12 Jan 2021 17:08:52 +0800 Subject: [PATCH 212/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=86=99=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 8 ++++---- src/model/concern/Attribute.php | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Model.php b/src/Model.php index 2b4b2e9..28a37d7 100644 --- a/src/Model.php +++ b/src/Model.php @@ -611,7 +611,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab return true; } - $this->writeDataType($data); + $data = $this->writeDataType($data); if ($this->autoWriteTimestamp && $this->updateTime) { // 自动写入更新时间 @@ -686,19 +686,19 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } $this->checkData(); - $this->writeDataType($this->data); + $data = $this->writeDataType($this->data); // 检查允许字段 $allowFields = $this->checkAllowFields(); $db = $this->db(); - $db->transaction(function () use ($sequence, $allowFields, $db) { + $db->transaction(function () use ($data, $sequence, $allowFields, $db) { $result = $db->strict(false) ->field($allowFields) ->replace($this->replace) ->sequence($sequence) - ->insert($this->data, true); + ->insert($data, true); // 获取自动增长主键 if ($result) { diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index fcc2428..abf79a8 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -529,9 +529,9 @@ trait Attribute * 写入数据类型处理 * @access protected * @param array $data 数据 - * @return void + * @return array */ - protected function writeDataType(array &$data): void + protected function writeDataType(array $data): array { foreach ($data as $name => &$value) { if (isset($this->type[$name])) { @@ -542,6 +542,8 @@ trait Attribute $value = $this->autoWriteTimestamp(); } } + + return $data; } /** -- Gitee From 92b5e74e5a58d4054d7d673d3721a59f4c399803 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 21 Jan 2021 16:07:47 +0800 Subject: [PATCH 213/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BgetRealsql=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index eabd423..5c46009 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1195,7 +1195,9 @@ abstract class PDOConnection extends Connection $column = null; } elseif (strpos($column, ' ')) { $column = substr(strrchr(trim($column), ' '), 1); - } elseif (strpos($column, '.')) { + } + + if (strpos($column, '.')) { [$alias, $column] = explode('.', $column); } @@ -1228,7 +1230,7 @@ abstract class PDOConnection extends Connection $value = is_array($val) ? $val[0] : $val; $type = is_array($val) ? $val[1] : PDO::PARAM_STR; - if ((self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) && is_string($value)) { + if (self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) { $value = '\'' . addslashes($value) . '\''; } elseif (PDO::PARAM_INT == $type && '' === $value) { $value = 0; -- Gitee From e1b746dac12a79167ddb4c2ccde49a834235c589 Mon Sep 17 00:00:00 2001 From: auooru Date: Sat, 23 Jan 2021 15:09:00 +0800 Subject: [PATCH 214/365] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A6=96=E4=B8=AA?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=E4=B8=8Egithub-actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/codecov.yml | 68 ++++++++++++++++++++++++++++++++ .github/workflows/tests.yml | 73 +++++++++++++++++++++++++++++++++++ composer.json | 11 ++++++ phpunit.xml | 31 +++++++++++++++ tests/bootstrap.php | 30 ++++++++++++++ tests/orm/DbTest.php | 49 +++++++++++++++++++++++ 6 files changed, 262 insertions(+) create mode 100644 .github/workflows/codecov.yml create mode 100644 .github/workflows/tests.yml create mode 100644 phpunit.xml create mode 100644 tests/bootstrap.php create mode 100644 tests/orm/DbTest.php diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml new file mode 100644 index 0000000..798cef4 --- /dev/null +++ b/.github/workflows/codecov.yml @@ -0,0 +1,68 @@ +name: codecov + +on: [push, pull_request] + +jobs: + phpunit: + runs-on: ubuntu-latest + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: testing + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + + steps: + - name: Checkout + uses: actions/checkout@v1 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + extensions: pdo, pdo_mysql, mbstring #optional, setup extensions + coverage: xdebug #optional, setup coverage driver + + - name: Check Version + run: | + php -v + php -m + composer -V + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Get composer cache directory + id: composercache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache vendor + uses: actions/cache@v2 + env: + cache-name: composer-cache + with: + path: ${{ steps.composercache.outputs.dir }} + key: ${{ runner.os }}-build-${{ env.cache-name }} + + - name: Install dependencies (composer.lock) + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Run test suite + run: composer exec -- phpunit --coverage-clover=coverage.xml -v + env: + TESTS_DB_MYSQL_HOST: 127.0.0.1 + TESTS_DB_MYSQL_PORT: 3306 + TESTS_DB_MYSQL_USERNAME: root + TESTS_DB_MYSQL_PASSWORD: password + TESTS_DB_MYSQL_DATABASE: testing + + - name: Codecov + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} #required + file: ./coverage.xml #optional + flags: unittests #optional + name: codecov-umbrella #optional diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..33ac60a --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,73 @@ +name: tests + +on: [push, pull_request] + +jobs: + phpunit: + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental }} + strategy: + fail-fast: false + matrix: + php: + - 7.1 + - 7.2 + - 7.3 + - 7.4 + experimental: [false] + include: + - php: 8.0 + experimental: true + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: testing + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + + steps: + - name: Checkout + uses: actions/checkout@v1 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: pdo, pdo_mysql, mbstring #optional, setup extensions + coverage: none #optional, setup coverage driver + + - name: Check Version + run: | + php -v + php -m + composer -V + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Get composer cache directory + id: composercache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache vendor + uses: actions/cache@v2 + env: + cache-name: composer-cache + with: + path: ${{ steps.composercache.outputs.dir }} + key: ${{ runner.os }}-${{ matrix.php }}-build-${{ env.cache-name }} + + - name: Install dependencies (composer.lock) + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Run test suite + run: composer exec -- phpunit -v + env: + TESTS_DB_MYSQL_HOST: 127.0.0.1 + TESTS_DB_MYSQL_PORT: 3306 + TESTS_DB_MYSQL_USERNAME: root + TESTS_DB_MYSQL_PASSWORD: password + TESTS_DB_MYSQL_DATABASE: testing \ No newline at end of file diff --git a/composer.json b/composer.json index 1a5b9e4..38550d6 100644 --- a/composer.json +++ b/composer.json @@ -19,10 +19,21 @@ "psr/log": "~1.0", "topthink/think-helper":"^3.1" }, + "require-dev": { + "phpunit/phpunit": "^7|^8|^9.5" + }, "autoload": { "psr-4": { "think\\": "src" }, "files": [] + }, + "autoload-dev": { + "psr-4": { + "tests\\": "tests" + } + }, + "config": { + "sort-packages": true } } diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..97f50ab --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,31 @@ + + + + + src + + + + + tests + + + + + + + + + diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..128c853 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,30 @@ + 'mysql', + // 数据库连接信息 + 'connections' => [ + 'mysql' => [ + // 数据库类型 + 'type' => 'mysql', + // 主机地址 + 'hostname' => getenv('TESTS_DB_MYSQL_HOSTNAME'), + // 数据库名 + 'database' => getenv('TESTS_DB_MYSQL_DATABASE'), + // 用户名 + 'username' => getenv('TESTS_DB_MYSQL_USERNAME'), + // 密码 + 'password' => getenv('TESTS_DB_MYSQL_PASSWORD'), + // 数据库编码默认采用utf8 + 'charset' => 'utf8', + // 数据库表前缀 + 'prefix' => 'test_', + // 数据库调试模式 + 'debug' => false, + ], + ], +]); \ No newline at end of file diff --git a/tests/orm/DbTest.php b/tests/orm/DbTest.php new file mode 100644 index 0000000..7354989 --- /dev/null +++ b/tests/orm/DbTest.php @@ -0,0 +1,49 @@ + '1', 'type' => '3', 'username' => 'qweqwe', 'nickname' => 'asdasd', 'password' => '123123'], + ['id' => '2', 'type' => '2', 'username' => 'rtyrty', 'nickname' => 'fghfgh', 'password' => '456456'], + ['id' => '3', 'type' => '1', 'username' => 'uiouio', 'nickname' => 'jkljkl', 'password' => '789789'], + ]; + + Db::table('test_user')->insertAll($users); + $result = Db::table('test_user')->column('*', 'id'); + + $this->assertCount(3, $result); + $this->assertEquals($users, array_values($result)); + $this->assertEquals(array_column($users, 'id'), array_keys($result)); + + $result = Db::table('test_user')->column('username'); + + $this->assertEquals(array_column($users, 'username'), $result); + } +} -- Gitee From 80035d7760ffac1350351a5fbf4a49c927999281 Mon Sep 17 00:00:00 2001 From: auooru Date: Sat, 23 Jan 2021 16:27:01 +0800 Subject: [PATCH 215/365] =?UTF-8?q?=E5=AF=BC=E5=87=BA=E5=8C=85=E6=97=B6?= =?UTF-8?q?=E5=BF=BD=E7=95=A5=E6=8E=89=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..36c9cd1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +/.github export-ignore +/tests export-ignore +/phpunit.xml export-ignore \ No newline at end of file -- Gitee From 47a44c7e64247f5ccb04135f9a932c54b472b396 Mon Sep 17 00:00:00 2001 From: auooru Date: Sat, 23 Jan 2021 16:56:52 +0800 Subject: [PATCH 216/365] =?UTF-8?q?=E8=A1=A5=E5=85=85=20column=20=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/bootstrap.php | 1 + tests/functions.php | 32 ++++++++++++++++++++++++++++++++ tests/orm/DbTest.php | 21 ++++++++++++++++----- 3 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 tests/functions.php diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 128c853..2b1d83b 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,5 +1,6 @@ '1', 'type' => '3', 'username' => 'qweqwe', 'nickname' => 'asdasd', 'password' => '123123'], - ['id' => '2', 'type' => '2', 'username' => 'rtyrty', 'nickname' => 'fghfgh', 'password' => '456456'], - ['id' => '3', 'type' => '1', 'username' => 'uiouio', 'nickname' => 'jkljkl', 'password' => '789789'], + ['id' => 1, 'type' => 3, 'username' => 'qweqwe', 'nickname' => 'asdasd', 'password' => '123123'], + ['id' => 2, 'type' => 2, 'username' => 'rtyrty', 'nickname' => 'fghfgh', 'password' => '456456'], + ['id' => 3, 'type' => 1, 'username' => 'uiouio', 'nickname' => 'jkljkl', 'password' => '789789'], + ['id' => 5, 'type' => 2, 'username' => 'qazqaz', 'nickname' => 'wsxwsx', 'password' => '098098'], + ['id' => 7, 'type' => 2, 'username' => 'rfvrfv', 'nickname' => 'tgbtgb', 'password' => '765765'], ]; + // 获取全部列 Db::table('test_user')->insertAll($users); $result = Db::table('test_user')->column('*', 'id'); - $this->assertCount(3, $result); + $this->assertCount(5, $result); $this->assertEquals($users, array_values($result)); $this->assertEquals(array_column($users, 'id'), array_keys($result)); + // 获取某一个字段 $result = Db::table('test_user')->column('username'); - $this->assertEquals(array_column($users, 'username'), $result); + + // 获取若干列 + $result = Db::table('test_user')->column('username,nickname', 'id'); + $expected = array_column_ex($users, ['username', 'nickname', 'id'], 'id'); + $this->assertEquals($expected, $result); } } -- Gitee From 02739c39ae042f16cc0af4f76217e37085c57586 Mon Sep 17 00:00:00 2001 From: auooru Date: Sat, 23 Jan 2021 11:56:52 +0800 Subject: [PATCH 217/365] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=20column=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 4 +-- src/db/ConnectionInterface.php | 4 +-- src/db/PDOConnection.php | 57 ++++++++++++++++------------------ src/db/connector/Mongo.php | 12 +++++-- 4 files changed, 39 insertions(+), 38 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 447b749..42b7ba0 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -266,11 +266,11 @@ abstract class BaseQuery /** * 得到某个列的数组 * @access public - * @param string $field 字段名 多个字段用逗号分隔 + * @param string|array $field 字段名 多个字段用逗号分隔 * @param string $key 索引 * @return array */ - public function column(string $field, string $key = ''): array + public function column($field, string $key = ''): array { return $this->connection->column($this, $field, $key); } diff --git a/src/db/ConnectionInterface.php b/src/db/ConnectionInterface.php index 207a424..18fe131 100644 --- a/src/db/ConnectionInterface.php +++ b/src/db/ConnectionInterface.php @@ -145,11 +145,11 @@ interface ConnectionInterface * 得到某个列的数组 * @access public * @param BaseQuery $query 查询对象 - * @param string $column 字段名 多个字段用逗号分隔 + * @param string|array $column 字段名 多个字段用逗号分隔 * @param string $key 索引 * @return array */ - public function column(BaseQuery $query, string $column, string $key = ''): array; + public function column(BaseQuery $query, $column, string $key = ''): array; /** * 执行数据库事务 diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 5c46009..55f592b 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1135,12 +1135,12 @@ abstract class PDOConnection extends Connection /** * 得到某个列的数组 * @access public - * @param BaseQuery $query 查询对象 - * @param string $column 字段名 多个字段用逗号分隔 - * @param string $key 索引 + * @param BaseQuery $query 查询对象 + * @param string|array $column 字段名 多个字段用逗号分隔 + * @param string $key 索引 * @return array */ - public function column(BaseQuery $query, string $column, string $key = ''): array + public function column(BaseQuery $query, $column, string $key = ''): array { $options = $query->parseOptions(); @@ -1148,14 +1148,25 @@ abstract class PDOConnection extends Connection $query->removeOption('field'); } - if ($key && '*' != $column) { - $field = $key . ',' . $column; + if (empty($key) || trim($key) === '') { + $key = null; + } + if (\is_string($column)) { + $column = \trim($column); + if ($column !== '*') { + $column = \array_map('\trim', \explode(',', $column)); + } + } elseif (\is_array($column)) { + if (\in_array('*', $column)) { + $column = '*'; + } } else { - $field = $column; + throw new DbException('not support type'); + } + $field = $column; + if ($column !== '*' && $key && !\in_array($key, $column)) { + $field[] = $key; } - - $field = array_map('trim', explode(',', $field)); - $query->setOption('field', $field); if (!empty($options['cache'])) { @@ -1184,28 +1195,12 @@ abstract class PDOConnection extends Connection if (empty($resultSet)) { $result = []; - } elseif (('*' == $column || strpos($column, ',')) && $key) { - $result = array_column($resultSet, null, $key); + } elseif ($field !== '*' && \count($field) === 1) { + $result = \array_column($resultSet, \array_shift($field), $key); + } elseif ($key) { + $result = \array_column($resultSet, null, $key); } else { - if (empty($key)) { - $key = null; - } - - if ('*' == $column || strpos($column, ',')) { - $column = null; - } elseif (strpos($column, ' ')) { - $column = substr(strrchr(trim($column), ' '), 1); - } - - if (strpos($column, '.')) { - [$alias, $column] = explode('.', $column); - } - - if (is_string($key) && strpos($key, '.')) { - [$alias, $key] = explode('.', $key); - } - - $result = array_column($resultSet, $column, $key); + $result = $resultSet; } if (isset($cacheItem)) { diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index ec39939..b497f8d 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -29,6 +29,8 @@ use think\db\builder\Mongo as Builder; use think\db\Connection; use think\db\exception\DbException as Exception; use think\db\Mongo as Query; +use function implode; +use function is_array; /** * Mongo数据库驱动 @@ -969,11 +971,12 @@ class Mongo extends Connection /** * 得到某个列的数组 * @access public - * @param string $field 字段名 多个字段用逗号分隔 - * @param string $key 索引 + * @param BaseQuery $query + * @param string|array $field 字段名 多个字段用逗号分隔 + * @param string $key 索引 * @return array */ - public function column(BaseQuery $query, string $field, string $key = ''): array + public function column(BaseQuery $query, $field, string $key = ''): array { $options = $query->parseOptions(); @@ -981,6 +984,9 @@ class Mongo extends Connection $query->removeOption('projection'); } + if (is_array($field)) { + $field = implode(',', $field); + } if ($key && '*' != $field) { $projection = $key . ',' . $field; } else { -- Gitee From a10b4ee7b1ad1d8443744eeb76bf6675309c9a17 Mon Sep 17 00:00:00 2001 From: auooru Date: Sat, 23 Jan 2021 17:39:30 +0800 Subject: [PATCH 218/365] =?UTF-8?q?=E6=B7=BB=E5=8A=A0column=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/functions.php | 13 +++++++++++-- tests/orm/DbTest.php | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/tests/functions.php b/tests/functions.php index abe4483..62fc4e3 100644 --- a/tests/functions.php +++ b/tests/functions.php @@ -5,14 +5,23 @@ namespace tests; use function array_column; use function array_combine; use function array_map; +use function call_user_func; +use function is_callable; +use function is_int; use function sort; function array_column_ex(array $arr, array $column, ?string $key = null): array { $result = array_map(function ($val) use ($column) { $item = []; - foreach ($column as $key) { - $item[$key] = $val[$key]; + foreach ($column as $index => $key) { + if (is_callable($key)) { + $item[$index] = call_user_func($key, $val); + } elseif (is_int($index)) { + $item[$key] = $val[$key]; + } else { + $item[$key] = $val[$index]; + } } return $item; }, $arr); diff --git a/tests/orm/DbTest.php b/tests/orm/DbTest.php index c6802d9..0ffeb82 100644 --- a/tests/orm/DbTest.php +++ b/tests/orm/DbTest.php @@ -4,11 +4,13 @@ declare(strict_types=1); namespace tests\orm; use PHPUnit\Framework\TestCase; +use think\db\Raw; use think\facade\Db; use function array_column; use function array_keys; use function array_values; use function tests\array_column_ex; +use function tests\array_value_sort; class DbTest extends TestCase { @@ -56,5 +58,45 @@ SQL $result = Db::table('test_user')->column('username,nickname', 'id'); $expected = array_column_ex($users, ['username', 'nickname', 'id'], 'id'); $this->assertEquals($expected, $result); + + // 获取若干列不指定key时不报错 + $result = Db::table('test_user')->column('username,nickname,id'); + $expected = array_column_ex($users, ['username', 'nickname', 'id']); + $this->assertEquals($expected, $result); + + // 数组方式获取 + $result = Db::table('test_user')->column(['username', 'nickname', 'type'], 'id'); + $expected = array_column_ex($users, ['username', 'nickname', 'type', 'id'], 'id'); + $this->assertEquals($expected, $result); + + // 数组方式获取(重命名字段) + $result = Db::table('test_user')->column(['username' => 'my_name', 'nickname'], 'id'); + $expected = array_column_ex($users, ['username' => 'my_name', 'nickname', 'id'], 'id'); + array_value_sort($result); + array_value_sort($expected); + $this->assertEquals($expected, $result); + + // 数组方式获取(定义表达式) + $result = Db::table('test_user') + ->column([ + 'username' => 'my_name', + 'nickname', + new Raw('`type`+1000 as type2'), + ], 'id'); + $expected = array_column_ex( + $users, + [ + 'username' => 'my_name', + 'nickname', + 'type2' => function ($value) { + return $value['type'] + 1000; + }, + 'id' + ], + 'id' + ); + array_value_sort($result); + array_value_sort($expected); + $this->assertEquals($expected, $result); } } -- Gitee From b60367c2357083f490deca4e55a8cf1cb7ff7a86 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 24 Jan 2021 00:11:41 +0800 Subject: [PATCH 219/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bcolumn=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 55f592b..260020e 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1151,9 +1151,10 @@ abstract class PDOConnection extends Connection if (empty($key) || trim($key) === '') { $key = null; } + if (\is_string($column)) { $column = \trim($column); - if ($column !== '*') { + if ('*' !== $column) { $column = \array_map('\trim', \explode(',', $column)); } } elseif (\is_array($column)) { @@ -1163,10 +1164,12 @@ abstract class PDOConnection extends Connection } else { throw new DbException('not support type'); } + $field = $column; - if ($column !== '*' && $key && !\in_array($key, $column)) { + if ('*' !== $column && $key && !\in_array($key, $column)) { $field[] = $key; } + $query->setOption('field', $field); if (!empty($options['cache'])) { @@ -1189,14 +1192,26 @@ abstract class PDOConnection extends Connection } // 执行查询操作 - $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']); - + $pdo = $this->getPDOStatement($sql, $query->getBind(), $options['master']); $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC); + if (is_string($key) && strpos($key, '.')) { + [$alias, $key] = explode('.', $key); + } + if (empty($resultSet)) { $result = []; - } elseif ($field !== '*' && \count($field) === 1) { - $result = \array_column($resultSet, \array_shift($field), $key); + } elseif ('*' !== $field && \count($field) === 1) { + $column = \array_shift($field); + if (strpos($column, ' ')) { + $column = substr(strrchr(trim($column), ' '), 1); + } + + if (strpos($column, '.')) { + [$alias, $column] = explode('.', $column); + } + + $result = \array_column($resultSet, $column, $key); } elseif ($key) { $result = \array_column($resultSet, null, $key); } else { -- Gitee From e3014fa17d05070ed7e61d0844d1bb1114800cc6 Mon Sep 17 00:00:00 2001 From: auooru Date: Sun, 24 Jan 2021 01:13:31 +0800 Subject: [PATCH 220/365] =?UTF-8?q?=E8=A1=A5=E5=85=85=20column=20=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 8 ++++---- tests/orm/DbTest.php | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 260020e..104d28d 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1203,12 +1203,12 @@ abstract class PDOConnection extends Connection $result = []; } elseif ('*' !== $field && \count($field) === 1) { $column = \array_shift($field); - if (strpos($column, ' ')) { - $column = substr(strrchr(trim($column), ' '), 1); + if (\strpos($column, ' ')) { + $column = \substr(\strrchr(\trim($column), ' '), 1); } - if (strpos($column, '.')) { - [$alias, $column] = explode('.', $column); + if (\strpos($column, '.')) { + [$alias, $column] = \explode('.', $column); } $result = \array_column($resultSet, $column, $key); diff --git a/tests/orm/DbTest.php b/tests/orm/DbTest.php index 0ffeb82..0fc1929 100644 --- a/tests/orm/DbTest.php +++ b/tests/orm/DbTest.php @@ -8,6 +8,7 @@ use think\db\Raw; use think\facade\Db; use function array_column; use function array_keys; +use function array_unique; use function array_values; use function tests\array_column_ex; use function tests\array_value_sort; @@ -54,6 +55,21 @@ SQL $result = Db::table('test_user')->column('username'); $this->assertEquals(array_column($users, 'username'), $result); + // 获取某字段唯一 + $result = Db::table('test_user')->distinct(true)->column('type'); + $expected = array_unique(array_column($users, 'type')); + $this->assertEquals($expected, $result); + + // 字段别名 + $result = Db::table('test_user')->column('username as name2'); + $expected = array_column($users, 'username'); + $this->assertEquals($expected, $result); + + // 表别名 + $result = Db::table('test_user')->alias('test2')->column('test2.username'); + $expected = array_column($users, 'username'); + $this->assertEquals($expected, $result); + // 获取若干列 $result = Db::table('test_user')->column('username,nickname', 'id'); $expected = array_column_ex($users, ['username', 'nickname', 'id'], 'id'); -- Gitee From f7c7cd42163acc8facac3e530f4d010e462963c1 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 24 Jan 2021 17:49:24 +0800 Subject: [PATCH 221/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 260020e..53e618c 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1237,19 +1237,19 @@ abstract class PDOConnection extends Connection public function getRealSql(string $sql, array $bind = []): string { foreach ($bind as $key => $val) { - $value = is_array($val) ? $val[0] : $val; + $value = strval(is_array($val) ? $val[0] : $val); $type = is_array($val) ? $val[1] : PDO::PARAM_STR; if (self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) { $value = '\'' . addslashes($value) . '\''; } elseif (PDO::PARAM_INT == $type && '' === $value) { - $value = 0; + $value = '0'; } // 判断占位符 $sql = is_numeric($key) ? - substr_replace($sql, (string) $value, strpos($sql, '?'), 1) : - substr_replace($sql, (string) $value, strpos($sql, ':' . $key), strlen(':' . $key)); + substr_replace($sql, $value, strpos($sql, '?'), 1) : + substr_replace($sql, $value, strpos($sql, ':' . $key), strlen(':' . $key)); } return rtrim($sql); -- Gitee From ab4d5d33166b8d2f8f666146278bda2c2384c7be Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 25 Jan 2021 09:47:04 +0800 Subject: [PATCH 222/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3column=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 4dd896a..0f98d25 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1201,8 +1201,8 @@ abstract class PDOConnection extends Connection if (empty($resultSet)) { $result = []; - } elseif ('*' !== $field && \count($field) === 1) { - $column = \array_shift($field); + } elseif ('*' !== $column && \count($column) === 1) { + $column = \array_shift($column); if (\strpos($column, ' ')) { $column = \substr(\strrchr(\trim($column), ' '), 1); } -- Gitee From ed4e268646c5824840e80c7f2d2b428e1e68c3c1 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 26 Jan 2021 15:09:31 +0800 Subject: [PATCH 223/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E7=9A=84getBindAttr=E6=96=B9=E6=B3=95=E5=92=8C=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=99=A8=E5=86=B2=E7=AA=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Conversion.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index bc1bec2..21b3978 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -255,11 +255,11 @@ trait Conversion $value = $this->getAttr($name); $item[$name] = $value; - $this->getBindAttr($name, $value, $item); + $this->getBindAttrValue($name, $value, $item); } } - protected function getBindAttr(string $name, $value, array &$item = []) + protected function getBindAttrValue(string $name, $value, array &$item = []) { $relation = $this->isRelationAttr($name); if (!$relation) { -- Gitee From d6dff307cd3d30fe833c84e7ca3e462a76cc3422 Mon Sep 17 00:00:00 2001 From: liuqiandev <78799157@qq.com> Date: Wed, 27 Jan 2021 01:12:41 +0800 Subject: [PATCH 224/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=99=A8=E5=9C=BA=E6=99=AF=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 18 ++++++++++++++++++ src/model/concern/Conversion.php | 15 ++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index abf79a8..f83b7fe 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -94,6 +94,12 @@ trait Attribute */ protected $strict = true; + /** + * 获取器场景 [sceneFilter:string => appendFieldNotInTable[]][] + * @var array + */ + protected $appendsScene = []; + /** * 修改器执行记录 * @var array @@ -678,4 +684,16 @@ trait Attribute return $this; } + /** + * 获取场景下的获取器字段 + * @access public + * @param string $scene + * @return array|mixed + */ + public function getAppendsFieldsByScene(string $scene = ''):array + { + $scene = empty($scene) ? 'default' : $scene; + return $this->appendsScene[$scene] ?? []; + } + } diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index 21b3978..39d3a49 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -80,11 +80,24 @@ trait Conversion */ public function append(array $append = []) { - $this->append = $append; + $this->append = array_unique(array_merge($this->append,$append)); return $this; } + /** + * 根据追加场景设置需要附加的输出属性 + * @access public + * @param string $scene 追加场景名称 + * @return $this + */ + public function appendByScene(string $scene = '') + { + $appends = $this->getAppendsFieldsByScene($scene); + $this->append($appends); + return $this; + } + /** * 设置附加关联对象的属性 * @access public -- Gitee From b2bf4e9e64e52f8d984c63a8b87e6eae9abf9da0 Mon Sep 17 00:00:00 2001 From: liuqiandev <78799157@qq.com> Date: Wed, 27 Jan 2021 01:40:47 +0800 Subject: [PATCH 225/365] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E9=9B=86=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Collection.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/model/Collection.php b/src/model/Collection.php index fd54272..3c651dd 100644 --- a/src/model/Collection.php +++ b/src/model/Collection.php @@ -117,6 +117,21 @@ class Collection extends BaseCollection return $this; } + /** + * 根据追加场景设置需要附加的输出属性 + * @access public + * @param string $scene 追加场景名称 + * @return $this + */ + public function appendByScene(string $scene = '') + { + $this->each(function (Model $model) use ($scene) { + $model->appendByScene($scene); + }); + + return $this; + } + /** * 设置父模型 * @access public -- Gitee From bd347aad5b88268352a8aa3c5afe0473f0963d81 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 28 Jan 2021 16:01:33 +0800 Subject: [PATCH 226/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E7=9A=84=E5=9C=BA=E6=99=AF=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=20=E6=94=AF=E6=8C=81hidden=20visible=20appen?= =?UTF-8?q?d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Collection.php | 8 ++++---- src/model/concern/Attribute.php | 18 ------------------ src/model/concern/Conversion.php | 25 +++++++++++++++++++------ 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/model/Collection.php b/src/model/Collection.php index 3c651dd..f017e32 100644 --- a/src/model/Collection.php +++ b/src/model/Collection.php @@ -118,15 +118,15 @@ class Collection extends BaseCollection } /** - * 根据追加场景设置需要附加的输出属性 + * 设置模型输出场景 * @access public - * @param string $scene 追加场景名称 + * @param string $scene 场景名称 * @return $this */ - public function appendByScene(string $scene = '') + public function scene(string $scene) { $this->each(function (Model $model) use ($scene) { - $model->appendByScene($scene); + $model->scene($scene); }); return $this; diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index f83b7fe..abf79a8 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -94,12 +94,6 @@ trait Attribute */ protected $strict = true; - /** - * 获取器场景 [sceneFilter:string => appendFieldNotInTable[]][] - * @var array - */ - protected $appendsScene = []; - /** * 修改器执行记录 * @var array @@ -684,16 +678,4 @@ trait Attribute return $this; } - /** - * 获取场景下的获取器字段 - * @access public - * @param string $scene - * @return array|mixed - */ - public function getAppendsFieldsByScene(string $scene = ''):array - { - $scene = empty($scene) ? 'default' : $scene; - return $this->appendsScene[$scene] ?? []; - } - } diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index 39d3a49..35d96d0 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -42,6 +42,12 @@ trait Conversion */ protected $append = []; + /** + * 场景 + * @var array + */ + protected $scene = []; + /** * 数据输出字段映射 * @var array @@ -80,21 +86,28 @@ trait Conversion */ public function append(array $append = []) { - $this->append = array_unique(array_merge($this->append,$append)); + $this->append = $append; return $this; } /** - * 根据追加场景设置需要附加的输出属性 + * 设置输出层场景 * @access public - * @param string $scene 追加场景名称 + * @param string $scene 场景名称 * @return $this */ - public function appendByScene(string $scene = '') + public function scene(string $scene) { - $appends = $this->getAppendsFieldsByScene($scene); - $this->append($appends); + if (isset($this->scene[$scene])) { + $data = $this->scene[$scene]; + foreach (['append', 'hidden', 'visible'] as $name) { + if (isset($data[$name])) { + $this->$name($data[$name]); + } + } + } + return $this; } -- Gitee From 92d9d5126ac54bb7cecdebbb030b0e8f05e9fc54 Mon Sep 17 00:00:00 2001 From: auooru Date: Fri, 29 Jan 2021 11:34:44 +0800 Subject: [PATCH 227/365] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20WhereIn=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/orm/DbTest.php | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/tests/orm/DbTest.php b/tests/orm/DbTest.php index 0fc1929..75ca7bb 100644 --- a/tests/orm/DbTest.php +++ b/tests/orm/DbTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace tests\orm; use PHPUnit\Framework\TestCase; +use think\Collection; use think\db\Raw; use think\facade\Db; use function array_column; @@ -15,6 +16,8 @@ use function tests\array_value_sort; class DbTest extends TestCase { + protected static $testUserData; + public static function setUpBeforeClass(): void { Db::execute('DROP TABLE IF EXISTS `test_user`;'); @@ -30,21 +33,24 @@ SQL ); } - - - public function testColumn() + public function setUp(): void { Db::execute('TRUNCATE TABLE `test_user`;'); - $users = [ + self::$testUserData = [ ['id' => 1, 'type' => 3, 'username' => 'qweqwe', 'nickname' => 'asdasd', 'password' => '123123'], ['id' => 2, 'type' => 2, 'username' => 'rtyrty', 'nickname' => 'fghfgh', 'password' => '456456'], ['id' => 3, 'type' => 1, 'username' => 'uiouio', 'nickname' => 'jkljkl', 'password' => '789789'], ['id' => 5, 'type' => 2, 'username' => 'qazqaz', 'nickname' => 'wsxwsx', 'password' => '098098'], ['id' => 7, 'type' => 2, 'username' => 'rfvrfv', 'nickname' => 'tgbtgb', 'password' => '765765'], ]; + Db::table('test_user')->insertAll(self::$testUserData); + } + + public function testColumn() + { + $users = self::$testUserData; // 获取全部列 - Db::table('test_user')->insertAll($users); $result = Db::table('test_user')->column('*', 'id'); $this->assertCount(5, $result); @@ -115,4 +121,29 @@ SQL array_value_sort($expected); $this->assertEquals($expected, $result); } + + public function testWhereIn() + { + $sqlLogs = []; + Db::listen(function ($sql) use (&$sqlLogs) { + $sqlLogs[] = $sql; + }); + + $expected = Collection::make(self::$testUserData)->whereIn('type', [1, 3])->values()->toArray(); + $result = Db::table('test_user')->whereIn('type', [1, 3])->column('*'); + $this->assertEquals($expected, $result); + + $expected = Collection::make(self::$testUserData)->whereIn('type', [1])->values()->toArray(); + $result = Db::table('test_user')->whereIn('type', [1])->column('*'); + $this->assertEquals($expected, $result); + + $result = Db::table('test_user')->whereIn('type', [])->column('*'); + $this->assertEquals([], $result); + + $this->assertEquals([ + "SELECT * FROM `test_user` WHERE `type` IN (1,3)", + "SELECT * FROM `test_user` WHERE `type` = 1", + "SELECT * FROM `test_user` WHERE `type` IN ('')", + ], $sqlLogs); + } } -- Gitee From 5f94493f0d7904be48ce13b80fc137e408a291d1 Mon Sep 17 00:00:00 2001 From: auooru Date: Fri, 29 Jan 2021 12:00:46 +0800 Subject: [PATCH 228/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20WhereIn=20?= =?UTF-8?q?=E7=A9=BA=E6=95=B0=E7=BB=84=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Builder.php | 11 +++++++---- tests/orm/DbTest.php | 7 ++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/db/Builder.php b/src/db/Builder.php index c807f82..3b00943 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -15,6 +15,7 @@ namespace think\db; use Closure; use PDO; use think\db\exception\DbException as Exception; +use function count; /** * Db Builder @@ -490,7 +491,7 @@ abstract class Builder // 字段分析 $key = $field ? $this->parseKey($query, $field, true) : ''; - list($exp, $value) = $val; + [$exp, $value] = $val; // 检测操作符 if (!is_string($exp)) { @@ -756,6 +757,9 @@ abstract class Builder $value = $this->parseRaw($query, $value); } else { $value = array_unique(is_array($value) ? $value : explode(',', $value)); + if (count($value) === 0) { + return '0 = 1'; + } $array = []; foreach ($value as $v) { @@ -766,8 +770,7 @@ abstract class Builder if (count($array) == 1) { return $key . ('IN' == $exp ? ' = ' : ' <> ') . $array[0]; } else { - $zone = implode(',', $array); - $value = empty($zone) ? "''" : $zone; + $value = implode(',', $array); } } @@ -898,7 +901,7 @@ abstract class Builder $array[] = $this->parseRand($query); } elseif (is_string($val)) { if (is_numeric($key)) { - list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' '); + [$key, $sort] = explode(' ', strpos($val, ' ') ? $val : $val . ' '); } else { $sort = $val; } diff --git a/tests/orm/DbTest.php b/tests/orm/DbTest.php index 75ca7bb..dd0a35f 100644 --- a/tests/orm/DbTest.php +++ b/tests/orm/DbTest.php @@ -137,13 +137,18 @@ SQL $result = Db::table('test_user')->whereIn('type', [1])->column('*'); $this->assertEquals($expected, $result); + $expected = Collection::make(self::$testUserData)->whereIn('type', [1, ''])->values()->toArray(); + $result = Db::table('test_user')->whereIn('type', [1, ''])->column('*'); + $this->assertEquals($expected, $result); + $result = Db::table('test_user')->whereIn('type', [])->column('*'); $this->assertEquals([], $result); $this->assertEquals([ "SELECT * FROM `test_user` WHERE `type` IN (1,3)", "SELECT * FROM `test_user` WHERE `type` = 1", - "SELECT * FROM `test_user` WHERE `type` IN ('')", + "SELECT * FROM `test_user` WHERE `type` IN (1,0)", + "SELECT * FROM `test_user` WHERE 0 = 1", ], $sqlLogs); } } -- Gitee From 66b0557cd13c5835cec5ec5144f588fc8449e68d Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 2 Feb 2021 15:10:50 +0800 Subject: [PATCH 229/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Btrigger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 28 ---------------------------- src/db/Connection.php | 38 +++++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 39 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index 414706a..e2f55f1 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -92,8 +92,6 @@ class DbManager */ protected function modelMaker() { - $this->triggerSql(); - Model::setDb($this); if (is_object($this->event)) { @@ -117,32 +115,6 @@ class DbManager }); } - /** - * 监听SQL - * @access protected - * @return void - */ - protected function triggerSql(): void - { - // 监听SQL - $this->listen(function ($sql, $time, $master) { - if (0 === strpos($sql, 'CONNECT:')) { - $this->log($sql); - return; - } - - // 记录SQL - if (is_bool($master)) { - // 分布式记录当前操作的主从 - $master = $master ? 'master|' : 'slave|'; - } else { - $master = ''; - } - - $this->log($sql . ' [ ' . $master . 'RunTime:' . $time . 's ]'); - }); - } - /** * 初始化配置参数 * @access public diff --git a/src/db/Connection.php b/src/db/Connection.php index cb6a912..e57acfa 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -138,7 +138,6 @@ abstract class Connection implements ConnectionInterface return $this->builder; } - /** * 创建查询对象 */ @@ -234,21 +233,38 @@ abstract class Connection implements ConnectionInterface protected function trigger(string $sql = '', bool $master = false): void { $listen = $this->db->getListen(); + if (empty($listen)) { + $listen[] = function ($sql, $time, $master) { + if (0 === strpos($sql, 'CONNECT:')) { + $this->db->log($sql); + return; + } - if (!empty($listen)) { - $runtime = number_format((microtime(true) - $this->queryStartTime), 6); - $sql = $sql ?: $this->getLastsql(); + // 记录SQL + if (is_bool($master)) { + // 分布式记录当前操作的主从 + $master = $master ? 'master|' : 'slave|'; + } else { + $master = ''; + } - if (empty($this->config['deploy'])) { - $master = null; - } + $this->db->log($sql . ' [ ' . $master . 'RunTime:' . $time . 's ]'); + }; + } - foreach ($listen as $callback) { - if (is_callable($callback)) { - $callback($sql, $runtime, $master); - } + $runtime = number_format((microtime(true) - $this->queryStartTime), 6); + $sql = $sql ?: $this->getLastsql(); + + if (empty($this->config['deploy'])) { + $master = null; + } + + foreach ($listen as $callback) { + if (is_callable($callback)) { + $callback($sql, $runtime, $master); } } + } /** -- Gitee From 981058f72735129265ab8e5d377609b2e36648c4 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 2 Feb 2021 15:13:57 +0800 Subject: [PATCH 230/365] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 8 ++++++++ src/db/Connection.php | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/DbManager.php b/src/DbManager.php index e2f55f1..147d9f6 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -115,6 +115,14 @@ class DbManager }); } + /** + * 监听SQL + * @access protected + * @return void + */ + public function triggerSql(): void + {} + /** * 初始化配置参数 * @access public diff --git a/src/db/Connection.php b/src/db/Connection.php index e57acfa..4400ece 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -264,7 +264,6 @@ abstract class Connection implements ConnectionInterface $callback($sql, $runtime, $master); } } - } /** -- Gitee From 78a48494d77a96783a6f48b35caff1534f2441af Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 6 Feb 2021 16:31:12 +0800 Subject: [PATCH 231/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E5=86=99=E5=85=A5=E5=90=8E=E5=AE=9E=E6=97=B6?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 26 ++++++++++++++------------ src/model/concern/Attribute.php | 3 --- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/Model.php b/src/Model.php index 28a37d7..645f7fb 100644 --- a/src/Model.php +++ b/src/Model.php @@ -616,7 +616,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab if ($this->autoWriteTimestamp && $this->updateTime) { // 自动写入更新时间 $data[$this->updateTime] = $this->autoWriteTimestamp(); - $this->data[$this->updateTime] = $data[$this->updateTime]; + $this->data[$this->updateTime] = $this->getTimestampValue($data[$this->updateTime]); } // 检查允许字段 @@ -670,17 +670,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab */ protected function insertData(string $sequence = null): bool { - // 时间戳自动写入 - if ($this->autoWriteTimestamp) { - if ($this->createTime && !isset($this->data[$this->createTime])) { - $this->data[$this->createTime] = $this->autoWriteTimestamp(); - } - - if ($this->updateTime && !isset($this->data[$this->updateTime])) { - $this->data[$this->updateTime] = $this->autoWriteTimestamp(); - } - } - if (false === $this->trigger('BeforeInsert')) { return false; } @@ -688,6 +677,19 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $this->checkData(); $data = $this->writeDataType($this->data); + // 时间戳自动写入 + if ($this->autoWriteTimestamp) { + if ($this->createTime && !isset($data[$this->createTime])) { + $data[$this->createTime] = $this->autoWriteTimestamp(); + $this->data[$this->createTime] = $this->getTimestampValue($data[$this->createTime]); + } + + if ($this->updateTime && !isset($data[$this->updateTime])) { + $data[$this->updateTime] = $this->autoWriteTimestamp(); + $this->data[$this->updateTime] = $this->getTimestampValue($data[$this->updateTime]); + } + } + // 检查允许字段 $allowFields = $this->checkAllowFields(); diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index abf79a8..464daf3 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -537,9 +537,6 @@ trait Attribute if (isset($this->type[$name])) { // 类型转换 $value = $this->writeTransform($value, $this->type[$name]); - } elseif (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { - // 自动写入的时间戳字段 - $value = $this->autoWriteTimestamp(); } } -- Gitee From 8a2225c3533f5ccae4a77d4988ed4c5523284871 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 18 Feb 2021 10:01:00 +0800 Subject: [PATCH 232/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 3 +-- src/model/concern/Attribute.php | 14 ++++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Model.php b/src/Model.php index 645f7fb..7c53499 100644 --- a/src/Model.php +++ b/src/Model.php @@ -266,9 +266,8 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab */ public function newInstance(array $data = [], $where = null): Model { - $this->readDataType($data); - $model = new static($data); + $model->readDataType(); if ($this->connection) { $model->setConnection($this->connection); diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 464daf3..c1a4edc 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -510,17 +510,15 @@ trait Attribute /** * 读取数据类型处理 * @access protected - * @param array $data 数据 * @return void */ - protected function readDataType(array &$data): void + protected function readDataType(): void { - foreach ($data as $name => &$value) { - if (isset($this->type[$name])) { - // 类型转换 - $value = $this->readTransform($value, $this->type[$name]); - } elseif ($this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { - $value = $this->getTimestampValue($value); + foreach ($this->data as $key => $value) { + if (isset($this->type[$key])) { + $this->data[$key] = $this->readTransform($value, $this->type[$key]); + } elseif ($this->autoWriteTimestamp && in_array($key, [$this->createTime, $this->updateTime])) { + $this->data[$key] = $this->getTimestampValue($value); } } } -- Gitee From f233d5795248ed7665a81e239bbe5d9162662b7b Mon Sep 17 00:00:00 2001 From: auooru Date: Fri, 19 Feb 2021 12:43:58 +0800 Subject: [PATCH 233/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E7=B1=BB=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 0f98d25..b70bb79 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1431,7 +1431,7 @@ abstract class PDOConnection extends Connection * 用于非自动提交状态下面的查询提交 * @access public * @return void - * @throws PDOException + * @throws \PDOException */ public function commit(): void { @@ -1448,7 +1448,7 @@ abstract class PDOConnection extends Connection * 事务回滚 * @access public * @return void - * @throws PDOException + * @throws \PDOException */ public function rollback(): void { -- Gitee From d88ce46356588c90251ce5208427489da83901fe Mon Sep 17 00:00:00 2001 From: auooru Date: Fri, 19 Feb 2021 12:51:03 +0800 Subject: [PATCH 234/365] =?UTF-8?q?`Connection->link*`=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E7=B1=BB=E5=9E=8B=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 4 ++++ src/db/connector/Mongo.php | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index b70bb79..64034b0 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -21,6 +21,10 @@ use think\db\exception\PDOException; /** * 数据库连接基础类 + * @property PDO[] $links + * @property PDO $linkID + * @property PDO $linkRead + * @property PDO $linkWrite */ abstract class PDOConnection extends Connection { diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index b497f8d..4b05b79 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -34,6 +34,9 @@ use function is_array; /** * Mongo数据库驱动 + * @property Manager[] $links + * @property Manager $linkRead + * @property Manager $linkWrite */ class Mongo extends Connection { -- Gitee From 201294f7b322eb9cc7986f6a4c7e2bd7abd73289 Mon Sep 17 00:00:00 2001 From: auooru Date: Fri, 19 Feb 2021 12:54:34 +0800 Subject: [PATCH 235/365] =?UTF-8?q?composer.json=20=E5=A3=B0=E6=98=8E=20pd?= =?UTF-8?q?o=20=E6=89=A9=E5=B1=95=E6=98=AF=E5=BF=85=E9=A1=BB=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 38550d6..1932441 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ "require": { "php": ">=7.1.0", "ext-json": "*", + "ext-pdo": "*", "psr/simple-cache": "^1.0", "psr/log": "~1.0", "topthink/think-helper":"^3.1" -- Gitee From 3225ffd15a8b6d91cbef2486efe57facdba81598 Mon Sep 17 00:00:00 2001 From: auooru Date: Wed, 3 Feb 2021 14:39:40 +0800 Subject: [PATCH 236/365] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=89=80=E9=9C=80=E7=9A=84=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 4 +- src/db/exception/DbException.php | 108 ++++++------------------------- src/facade/Db.php | 57 +--------------- stubs/Exception.php | 59 +++++++++++++++++ stubs/Facade.php | 65 +++++++++++++++++++ stubs/load_stubs.php | 9 +++ tests/orm/DbTest.php | 13 ++++ 7 files changed, 171 insertions(+), 144 deletions(-) create mode 100644 stubs/Exception.php create mode 100644 stubs/Facade.php create mode 100644 stubs/load_stubs.php diff --git a/composer.json b/composer.json index 1932441..5e86ac8 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,9 @@ "psr-4": { "think\\": "src" }, - "files": [] + "files": [ + "stubs/load_stubs.php" + ] }, "autoload-dev": { "psr-4": { diff --git a/src/db/exception/DbException.php b/src/db/exception/DbException.php index 7d1deaa..f68b21c 100644 --- a/src/db/exception/DbException.php +++ b/src/db/exception/DbException.php @@ -12,99 +12,33 @@ declare (strict_types = 1); namespace think\db\exception; -use Exception; +use think\Exception; /** * Database相关异常处理类 */ -if (class_exists('think\Exception')) { - class DbException extends \think\Exception +class DbException extends Exception +{ + /** + * DbException constructor. + * @access public + * @param string $message + * @param array $config + * @param string $sql + * @param int $code + */ + public function __construct(string $message, array $config = [], string $sql = '', int $code = 10500) { - /** - * DbException constructor. - * @access public - * @param string $message - * @param array $config - * @param string $sql - * @param int $code - */ - public function __construct(string $message, array $config = [], string $sql = '', int $code = 10500) - { - $this->message = $message; - $this->code = $code; + $this->message = $message; + $this->code = $code; - $this->setData('Database Status', [ - 'Error Code' => $code, - 'Error Message' => $message, - 'Error SQL' => $sql, - ]); + $this->setData('Database Status', [ + 'Error Code' => $code, + 'Error Message' => $message, + 'Error SQL' => $sql, + ]); - unset($config['username'], $config['password']); - $this->setData('Database Config', $config); - } - } -} else { - - class DbException extends Exception - { - /** - * DbException constructor. - * @access public - * @param string $message - * @param array $config - * @param string $sql - * @param int $code - */ - public function __construct(string $message, array $config = [], string $sql = '', int $code = 10500) - { - $this->message = $message; - $this->code = $code; - - $this->setData('Database Status', [ - 'Error Code' => $code, - 'Error Message' => $message, - 'Error SQL' => $sql, - ]); - - unset($config['username'], $config['password']); - $this->setData('Database Config', $config); - } - - /** - * 保存异常页面显示的额外Debug数据 - * @var array - */ - protected $data = []; - - /** - * 设置异常额外的Debug数据 - * 数据将会显示为下面的格式 - * - * Exception Data - * -------------------------------------------------- - * Label 1 - * key1 value1 - * key2 value2 - * Label 2 - * key1 value1 - * key2 value2 - * - * @param string $label 数据分类,用于异常页面显示 - * @param array $data 需要显示的数据,必须为关联数组 - */ - final protected function setData($label, array $data) - { - $this->data[$label] = $data; - } - - /** - * 获取异常额外Debug数据 - * 主要用于输出到异常页面便于调试 - * @return array 由setData设置的Debug数据 - */ - final public function getData() - { - return $this->data; - } + unset($config['username'], $config['password']); + $this->setData('Database Config', $config); } } diff --git a/src/facade/Db.php b/src/facade/Db.php index 174a4f0..b0296c6 100644 --- a/src/facade/Db.php +++ b/src/facade/Db.php @@ -11,62 +11,7 @@ namespace think\facade; -if (class_exists('think\Facade')) { - class Facade extends \think\Facade - {} -} else { - class Facade - { - /** - * 始终创建新的对象实例 - * @var bool - */ - protected static $alwaysNewInstance; - - protected static $instance; - - /** - * 获取当前Facade对应类名 - * @access protected - * @return string - */ - protected static function getFacadeClass() - {} - - /** - * 创建Facade实例 - * @static - * @access protected - * @param bool $newInstance 是否每次创建新的实例 - * @return object - */ - protected static function createFacade(bool $newInstance = false) - { - $class = static::getFacadeClass() ?: 'think\DbManager'; - - if (static::$alwaysNewInstance) { - $newInstance = true; - } - - if ($newInstance) { - return new $class(); - } - - if (!self::$instance) { - self::$instance = new $class(); - } - - return self::$instance; - - } - - // 调用实际类的方法 - public static function __callStatic($method, $params) - { - return call_user_func_array([static::createFacade(), $method], $params); - } - } -} +use think\Facade; /** * @see \think\DbManager diff --git a/stubs/Exception.php b/stubs/Exception.php new file mode 100644 index 0000000..0fdba9c --- /dev/null +++ b/stubs/Exception.php @@ -0,0 +1,59 @@ + +// +---------------------------------------------------------------------- +declare (strict_types=1); + +namespace think; + +/** + * 异常基础类 + * @package think + */ +class Exception extends \Exception +{ + /** + * 保存异常页面显示的额外Debug数据 + * @var array + */ + protected $data = []; + + /** + * 设置异常额外的Debug数据 + * 数据将会显示为下面的格式 + * + * Exception Data + * -------------------------------------------------- + * Label 1 + * key1 value1 + * key2 value2 + * Label 2 + * key1 value1 + * key2 value2 + * + * @access protected + * @param string $label 数据分类,用于异常页面显示 + * @param array $data 需要显示的数据,必须为关联数组 + */ + final protected function setData(string $label, array $data) + { + $this->data[$label] = $data; + } + + /** + * 获取异常额外Debug数据 + * 主要用于输出到异常页面便于调试 + * @access public + * @return array 由setData设置的Debug数据 + */ + final public function getData() + { + return $this->data; + } +} diff --git a/stubs/Facade.php b/stubs/Facade.php new file mode 100644 index 0000000..d801d8b --- /dev/null +++ b/stubs/Facade.php @@ -0,0 +1,65 @@ + +// +---------------------------------------------------------------------- +declare(strict_types=1); + +namespace think; + +class Facade +{ + /** + * 始终创建新的对象实例 + * @var bool + */ + protected static $alwaysNewInstance; + + protected static $instance; + + /** + * 获取当前Facade对应类名 + * @access protected + * @return string + */ + protected static function getFacadeClass() + {} + + /** + * 创建Facade实例 + * @static + * @access protected + * @param bool $newInstance 是否每次创建新的实例 + * @return object + */ + protected static function createFacade(bool $newInstance = false) + { + $class = static::getFacadeClass() ?: 'think\DbManager'; + + if (static::$alwaysNewInstance) { + $newInstance = true; + } + + if ($newInstance) { + return new $class(); + } + + if (!self::$instance) { + self::$instance = new $class(); + } + + return self::$instance; + + } + + // 调用实际类的方法 + public static function __callStatic($method, $params) + { + return call_user_func_array([static::createFacade(), $method], $params); + } +} diff --git a/stubs/load_stubs.php b/stubs/load_stubs.php new file mode 100644 index 0000000..5854cda --- /dev/null +++ b/stubs/load_stubs.php @@ -0,0 +1,9 @@ +expectException(DbException::class); + try { + Db::query("wrong syntax"); + } catch (DbException $exception) { + $this->assertInstanceOf(ThinkException::class, $exception); + throw $exception; + } + } } -- Gitee From 211d60f29e2e4c6e6db64be5de49bfe1717ff745 Mon Sep 17 00:00:00 2001 From: auooru Date: Fri, 19 Feb 2021 14:18:07 +0800 Subject: [PATCH 237/365] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BA=8B=E5=8A=A1?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E6=96=AD=E8=BF=9E=E9=87=8D=E8=AF=95=E5=A4=84?= =?UTF-8?q?=E7=90=86=EF=BC=8C=E9=81=BF=E5=85=8D=E6=95=B0=E6=8D=AE=E6=B1=A1?= =?UTF-8?q?=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 99 ++++++++----- tests/bootstrap.php | 22 +++ tests/functions.php | 13 ++ tests/orm/DbTransactionTest.php | 237 ++++++++++++++++++++++++++++++++ 4 files changed, 333 insertions(+), 38 deletions(-) create mode 100644 tests/orm/DbTransactionTest.php diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 64034b0..d29c943 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -18,6 +18,7 @@ use PDOStatement; use think\db\exception\BindParamException; use think\db\exception\DbException; use think\db\exception\PDOException; +use think\Model; /** * 数据库连接基础类 @@ -173,6 +174,26 @@ abstract class PDOConnection extends Connection 'Error writing data to the connection', 'Resource deadlock avoided', 'failed with errno', + 'child connection forced to terminate due to client_idle_limit', + 'query_wait_timeout', + 'reset by peer', + 'Physical connection is not usable', + 'TCP Provider: Error code 0x68', + 'ORA-03114', + 'Packets out of order. Expected', + 'Adaptive Server connection failed', + 'Communication link failure', + 'connection is no longer usable', + 'Login timeout expired', + 'SQLSTATE[HY000] [2002] Connection refused', + 'running with the --read-only option so it cannot execute this statement', + 'The connection is broken and recovery is not possible. The connection is marked by the client driver as unrecoverable. No attempt was made to restore the connection.', + 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Try again', + 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known', + 'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: EOF detected', + 'SQLSTATE[HY000] [2002] Connection timed out', + 'SSL: Connection timed out', + 'SQLSTATE[HY000]: General error: 1105 The last transaction was aborted due to Seamless Scaling. Please retry.', ]; /** @@ -584,7 +605,7 @@ abstract class PDOConnection extends Connection /** * 获取PDO对象 * @access public - * @return \PDO|false + * @return PDO|false */ public function getPdo() { @@ -598,12 +619,13 @@ abstract class PDOConnection extends Connection /** * 执行查询 使用生成器返回数据 * @access public - * @param BaseQuery $query 查询对象 - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param \think\Model $model 模型对象实例 - * @param array $condition 查询条件 + * @param BaseQuery $query 查询对象 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param Model|null $model 模型对象实例 + * @param null $condition 查询条件 * @return \Generator + * @throws DbException */ public function getCursor(BaseQuery $query, string $sql, array $bind = [], $model = null, $condition = null) { @@ -622,12 +644,11 @@ abstract class PDOConnection extends Connection /** * 执行查询 返回数据集 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 + * @param string $sql sql指令 + * @param array $bind 参数绑定 * @param bool $master 主库读取 * @return array - * @throws BindParamException - * @throws \PDOException + * @throws DbException */ public function query(string $sql, array $bind = [], bool $master = false): array { @@ -640,8 +661,7 @@ abstract class PDOConnection extends Connection * @param string $sql sql指令 * @param array $bind 参数绑定 * @return int - * @throws BindParamException - * @throws \PDOException + * @throws DbException */ public function execute(string $sql, array $bind = []): int { @@ -651,15 +671,12 @@ abstract class PDOConnection extends Connection /** * 执行查询 返回数据集 * @access protected - * @param BaseQuery $query 查询对象 - * @param mixed $sql sql指令 - * @param array $bind 参数绑定 + * @param BaseQuery $query 查询对象 + * @param mixed $sql sql指令 + * @param array $bind 参数绑定 * @param bool $master 主库读取 * @return array - * @throws BindParamException - * @throws \PDOException - * @throws \Exception - * @throws \Throwable + * @throws DbException */ protected function pdoQuery(BaseQuery $query, $sql, array $bind = [], bool $master = null): array { @@ -707,6 +724,7 @@ abstract class PDOConnection extends Connection * @access public * @param BaseQuery $query 查询对象 * @return \PDOStatement + * @throws DbException */ public function pdo(BaseQuery $query): PDOStatement { @@ -725,10 +743,7 @@ abstract class PDOConnection extends Connection * @param bool $master 是否在主服务器读操作 * @param bool $procedure 是否为存储过程调用 * @return PDOStatement - * @throws BindParamException - * @throws \PDOException - * @throws \Exception - * @throws \Throwable + * @throws DbException */ public function getPDOStatement(string $sql, array $bind = [], bool $master = false, bool $procedure = false): PDOStatement { @@ -762,17 +777,19 @@ abstract class PDOConnection extends Connection $this->reConnectTimes = 0; return $this->PDOStatement; - } catch (\Throwable | \Exception $e) { - if ($this->reConnectTimes < 4 && $this->isBreak($e)) { - ++$this->reConnectTimes; - return $this->close()->getPDOStatement($sql, $bind, $master, $procedure); - } - - if ($e instanceof \PDOException) { - throw new PDOException($e, $this->config, $this->getLastsql()); + } catch (\PDOException $e) { + if ($this->transTimes > 0) { + // 事务活动中时不应该进行重试,应直接中断执行,防止造成污染。 + $this->transTimes = 0; + $this->reConnectTimes = 0; } else { - throw $e; + if ($this->reConnectTimes < 4 && $this->isBreak($e)) { + ++$this->reConnectTimes; + return $this->close()->getPDOStatement($sql, $bind, $master, $procedure); + } } + + throw new PDOException($e, $this->config, $this->getLastsql()); } } @@ -784,10 +801,7 @@ abstract class PDOConnection extends Connection * @param array $bind 参数绑定 * @param bool $origin 是否原生查询 * @return int - * @throws BindParamException - * @throws \PDOException - * @throws \Exception - * @throws \Throwable + * @throws DbException */ protected function pdoExecute(BaseQuery $query, string $sql, array $bind = [], bool $origin = false): int { @@ -819,6 +833,13 @@ abstract class PDOConnection extends Connection return $this->numRows; } + /** + * @param BaseQuery $query + * @param string $sql + * @param array $bind + * @return PDOStatement + * @throws DbException + */ protected function queryPDOStatement(BaseQuery $query, string $sql, array $bind = []): PDOStatement { $options = $query->getOptions(); @@ -1420,12 +1441,13 @@ abstract class PDOConnection extends Connection ); } $this->reConnectTimes = 0; - } catch (\Exception $e) { - if ($this->reConnectTimes < 4 && $this->isBreak($e)) { + } catch (\PDOException $e) { + if ($this->transTimes === 1 && $this->reConnectTimes < 4 && $this->isBreak($e)) { --$this->transTimes; ++$this->reConnectTimes; $this->close()->startTrans(); } else { + $this->transTimes = 0; throw $e; } } @@ -1539,6 +1561,7 @@ abstract class PDOConnection extends Connection $this->linkWrite = null; $this->linkRead = null; $this->links = []; + $this->transTimes = 0; $this->free(); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 2b1d83b..e712c59 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -10,6 +10,28 @@ Db::setConfig([ // 数据库连接信息 'connections' => [ 'mysql' => [ + // 数据库类型 + 'type' => 'mysql', + // 主机地址 + 'hostname' => getenv('TESTS_DB_MYSQL_HOSTNAME'), + // 数据库名 + 'database' => getenv('TESTS_DB_MYSQL_DATABASE'), + // 用户名 + 'username' => getenv('TESTS_DB_MYSQL_USERNAME'), + // 密码 + 'password' => getenv('TESTS_DB_MYSQL_PASSWORD'), + // 数据库编码默认采用utf8 + 'charset' => 'utf8', + // 数据库表前缀 + 'prefix' => 'test_', + // 是否需要断线重连 + 'break_reconnect' => false, + // 断线标识字符串 + 'break_match_str' => [], + // 数据库调试模式 + 'debug' => false, + ], + 'mysql_manage' => [ // 数据库类型 'type' => 'mysql', // 主机地址 diff --git a/tests/functions.php b/tests/functions.php index 62fc4e3..d531610 100644 --- a/tests/functions.php +++ b/tests/functions.php @@ -2,6 +2,8 @@ namespace tests; +use think\db\ConnectionInterface; +use think\facade\Db; use function array_column; use function array_combine; use function array_map; @@ -38,4 +40,15 @@ function array_value_sort(array $arr) foreach ($arr as &$value) { sort($value); } +} + +function query_mysql_connection_id(ConnectionInterface $connect): int +{ + $cid = $connect->query('SELECT CONNECTION_ID() as cid')[0]['cid']; + return (int) $cid; +} + +function mysql_kill_connection(string $name, $cid) +{ + Db::connect($name)->execute("KILL {$cid}"); } \ No newline at end of file diff --git a/tests/orm/DbTransactionTest.php b/tests/orm/DbTransactionTest.php new file mode 100644 index 0000000..91ab1a8 --- /dev/null +++ b/tests/orm/DbTransactionTest.php @@ -0,0 +1,237 @@ + 1, 'type' => 9, 'username' => '1-9-a'], + ['id' => 2, 'type' => 8, 'username' => '2-8-a'], + ['id' => 3, 'type' => 7, 'username' => '3-7-a'], + ]; + } + + public function testTransaction() + { + $testData = self::$testData; + $connect = Db::connect(); + + $connect->table('test_tran_a')->startTrans(); + $connect->table('test_tran_a')->insertAll($testData); + $connect->table('test_tran_a')->rollback(); + + $this->assertEmpty($connect->table('test_tran_a')->column('*')); + + $connect->execute('TRUNCATE TABLE `test_tran_a`;'); + $connect->table('test_tran_a')->startTrans(); + $connect->table('test_tran_a')->insertAll($testData); + $connect->table('test_tran_a')->commit(); + $this->assertEquals($testData, $connect->table('test_tran_a')->column('*')); + $connect->table('test_tran_a')->startTrans(); + $connect->table('test_tran_a')->where('id', '=', 2)->update([ + 'username' => '2-8-b', + ]); + $connect->table('test_tran_a')->commit(); + $this->assertEquals( + '2-8-b', + $connect->table('test_tran_a')->where('id', '=', 2)->value('username') + ); + } + + public function testBreakReconnect() + { + $testData = self::$testData; + // 初始化配置 + $config = Db::getConfig(); + $config['connections']['mysql']['break_reconnect'] = true; + $config['connections']['mysql']['break_match_str'] = [ + 'query execution was interrupted', + ]; + Db::setConfig($config); + // 初始化数据 + $connect = Db::connect(null, true); + $connect->table('test_tran_a')->insertAll($testData); + + $cid = query_mysql_connection_id($connect); + mysql_kill_connection('mysql_manage', $cid); + // 触发重连 + $connect->table('test_tran_a')->where('id', '=', 2)->value('username'); + + $newCid = query_mysql_connection_id($connect); + $this->assertNotEquals($cid, $newCid); + $cid = $newCid; + + // 事务前重连 + mysql_kill_connection('mysql_manage', $cid); + Db::table('test_tran_a')->startTrans(); + $connect->table('test_tran_a')->where('id', '=', 2)->update([ + 'username' => '2-8-b', + ]); + Db::table('test_tran_a')->commit(); + $newCid = query_mysql_connection_id($connect); + $this->assertNotEquals($cid, $newCid); + $cid = $newCid; + $this->assertEquals( + '2-8-b', + Db::table('test_tran_a')->where('id', '=', 2)->value('username') + ); + + // 事务中不能重连 + try { + Db::table('test_tran_a')->startTrans(); + $connect->table('test_tran_a')->where('id', '=', 2)->update([ + 'username' => '2-8-c', + ]); + mysql_kill_connection('mysql_manage', $cid); + $connect->table('test_tran_a')->where('id', '=', 3)->update([ + 'username' => '3-7-b', + ]); + Db::table('test_tran_a')->commit(); + } catch (DbException $exception) { + try { + Db::table('test_tran_a')->rollback(); + } catch (\Exception $rollbackException) { + // Ignore exception + $this->assertMatchesRegularExpression( + '~(server has gone away)~', + $rollbackException->getMessage() + ); + } + // Ignore exception + $this->assertMatchesRegularExpression( + '~(server has gone away)~', + $exception->getMessage() + ); + } + // 预期应该没有发生任何更改 + $this->assertEquals( + '2-8-b', + Db::table('test_tran_a')->where('id', '=', 2)->value('username') + ); + $this->assertEquals( + '3-7-a', + Db::table('test_tran_a')->where('id', '=', 3)->value('username') + ); + } + + public function testTransactionSavepoint() + { + $testData = self::$testData; + // 初始化数据 + $connect = Db::connect(null, true); + $connect->table('test_tran_a')->insertAll($testData); + + Db::table('test_tran_a')->transaction(function () use ($connect) { + $cid = query_mysql_connection_id($connect); + // tran 1 + Db::table('test_tran_a')->startTrans(); + $connect->table('test_tran_a')->where('id', '=', 2)->update([ + 'username' => '2-8-c', + ]); + Db::table('test_tran_a')->commit(); + // tran 2 + Db::table('test_tran_a')->startTrans(); + $connect->table('test_tran_a')->where('id', '=', 3)->update([ + 'username' => '3-7-b', + ]); + Db::table('test_tran_a')->commit(); + }); + + // 预期变化 + $this->assertEquals( + '2-8-c', + Db::table('test_tran_a')->where('id', '=', 2)->value('username') + ); + $this->assertEquals( + '3-7-b', + Db::table('test_tran_a')->where('id', '=', 3)->value('username') + ); + } + + public function testTransactionSavepointBreakReconnect() + { + $testData = self::$testData; + // 初始化配置 + $config = Db::getConfig(); + $config['connections']['mysql']['break_reconnect'] = true; + $config['connections']['mysql']['break_match_str'] = [ + 'query execution was interrupted', + ]; + Db::setConfig($config); + // 初始化数据 + $connect = Db::connect(null, true); + $connect->table('test_tran_a')->insertAll($testData); + + // 事务中不能重连 + try { + // tran 0 + Db::table('test_tran_a')->startTrans(); + $cid = query_mysql_connection_id($connect); + // tran 1 + Db::table('test_tran_a')->startTrans(); + $connect->table('test_tran_a')->where('id', '=', 2)->update([ + 'username' => '2-8-c', + ]); + Db::table('test_tran_a')->commit(); + // kill + mysql_kill_connection('mysql_manage', $cid); + // tran 2 + Db::table('test_tran_a')->startTrans(); + $connect->table('test_tran_a')->where('id', '=', 3)->update([ + 'username' => '3-7-b', + ]); + Db::table('test_tran_a')->commit(); + // tran 0 + Db::table('test_tran_a')->commit(); + } catch (DbException | \PDOException $exception) { + try { + Db::table('test_tran_a')->rollback(); + } catch (\Exception $rollbackException) { + // Ignore exception + $this->assertMatchesRegularExpression( + '~(server has gone away)~', + $rollbackException->getMessage() + ); + } + // Ignore exception + $this->assertMatchesRegularExpression( + '~(server has gone away)~', + $exception->getMessage() + ); + } + // 预期应该没有发生任何更改 + $this->assertEquals( + '2-8-a', + Db::table('test_tran_a')->where('id', '=', 2)->value('username') + ); + $this->assertEquals( + '3-7-a', + Db::table('test_tran_a')->where('id', '=', 3)->value('username') + ); + } +} -- Gitee From 1f1322cc5a866ac5682c0a034e2f668f2a343899 Mon Sep 17 00:00:00 2001 From: auooru Date: Fri, 19 Feb 2021 17:22:38 +0800 Subject: [PATCH 238/365] =?UTF-8?q?=E4=BF=AE=E5=A4=8D7.4=E4=BB=A5=E4=B8=8B?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=AD=98=E5=9C=A8=E6=97=A0=E6=B3=95=E6=8A=8A?= =?UTF-8?q?PDO=E8=AD=A6=E5=91=8A=E8=BD=AC=E6=8D=A2=E4=B8=BA=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E5=AF=BC=E8=87=B4=E4=B8=8D=E5=85=BC=E5=AE=B9=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 21 +++++++++++++++------ tests/orm/DbTransactionTest.php | 5 ++--- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index d29c943..0a51c0c 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -777,11 +777,13 @@ abstract class PDOConnection extends Connection $this->reConnectTimes = 0; return $this->PDOStatement; - } catch (\PDOException $e) { + } catch (\Throwable | \Exception $e) { if ($this->transTimes > 0) { // 事务活动中时不应该进行重试,应直接中断执行,防止造成污染。 - $this->transTimes = 0; - $this->reConnectTimes = 0; + if ($this->isBreak($e)) { + // 尝试对事务计数进行重置 + $this->transTimes = 0; + } } else { if ($this->reConnectTimes < 4 && $this->isBreak($e)) { ++$this->reConnectTimes; @@ -789,7 +791,11 @@ abstract class PDOConnection extends Connection } } - throw new PDOException($e, $this->config, $this->getLastsql()); + if ($e instanceof \PDOException) { + throw new PDOException($e, $this->config, $this->getLastsql()); + } else { + throw $e; + } } } @@ -1441,13 +1447,16 @@ abstract class PDOConnection extends Connection ); } $this->reConnectTimes = 0; - } catch (\PDOException $e) { + } catch (\Throwable | \Exception $e) { if ($this->transTimes === 1 && $this->reConnectTimes < 4 && $this->isBreak($e)) { --$this->transTimes; ++$this->reConnectTimes; $this->close()->startTrans(); } else { - $this->transTimes = 0; + if ($this->isBreak($e)) { + // 尝试对事务计数进行重置 + $this->transTimes = 0; + } throw $e; } } diff --git a/tests/orm/DbTransactionTest.php b/tests/orm/DbTransactionTest.php index 91ab1a8..f6f9b73 100644 --- a/tests/orm/DbTransactionTest.php +++ b/tests/orm/DbTransactionTest.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace tests\orm; use PHPUnit\Framework\TestCase; -use think\db\exception\DbException; use think\facade\Db; use function tests\mysql_kill_connection; use function tests\query_mysql_connection_id; @@ -112,7 +111,7 @@ SQL 'username' => '3-7-b', ]); Db::table('test_tran_a')->commit(); - } catch (DbException $exception) { + } catch (\Throwable | \Exception $exception) { try { Db::table('test_tran_a')->rollback(); } catch (\Exception $rollbackException) { @@ -208,7 +207,7 @@ SQL Db::table('test_tran_a')->commit(); // tran 0 Db::table('test_tran_a')->commit(); - } catch (DbException | \PDOException $exception) { + } catch (\Throwable | \Exception $exception) { try { Db::table('test_tran_a')->rollback(); } catch (\Exception $rollbackException) { -- Gitee From 123a095cd40ef044c7b3f7bcbf22e85082f264bd Mon Sep 17 00:00:00 2001 From: auooru Date: Fri, 19 Feb 2021 17:32:03 +0800 Subject: [PATCH 239/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3phpunit=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Base.php | 20 ++++++++++++++++++++ tests/orm/DbTest.php | 4 ++-- tests/orm/DbTransactionTest.php | 22 ++++++++++++---------- 3 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 tests/Base.php diff --git a/tests/Base.php b/tests/Base.php new file mode 100644 index 0000000..b7cdbed --- /dev/null +++ b/tests/Base.php @@ -0,0 +1,20 @@ +=')) { + $this->assertMatchesRegularExpression($pattern, $string, $message); + } else { + $this->assertRegExp($pattern, $string, $message); + } + } +} diff --git a/tests/orm/DbTest.php b/tests/orm/DbTest.php index 5596a06..c16eb8c 100644 --- a/tests/orm/DbTest.php +++ b/tests/orm/DbTest.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace tests\orm; -use PHPUnit\Framework\TestCase; +use tests\Base; use think\Collection; use think\db\exception\DbException; use think\db\Raw; @@ -16,7 +16,7 @@ use function array_values; use function tests\array_column_ex; use function tests\array_value_sort; -class DbTest extends TestCase +class DbTest extends Base { protected static $testUserData; diff --git a/tests/orm/DbTransactionTest.php b/tests/orm/DbTransactionTest.php index f6f9b73..9e02e54 100644 --- a/tests/orm/DbTransactionTest.php +++ b/tests/orm/DbTransactionTest.php @@ -3,12 +3,14 @@ declare(strict_types=1); namespace tests\orm; -use PHPUnit\Framework\TestCase; +use Exception; +use tests\Base; use think\facade\Db; +use Throwable; use function tests\mysql_kill_connection; use function tests\query_mysql_connection_id; -class DbTransactionTest extends TestCase +class DbTransactionTest extends Base { protected static $testData; @@ -111,18 +113,18 @@ SQL 'username' => '3-7-b', ]); Db::table('test_tran_a')->commit(); - } catch (\Throwable | \Exception $exception) { + } catch (Throwable | Exception $exception) { try { Db::table('test_tran_a')->rollback(); - } catch (\Exception $rollbackException) { + } catch (Exception $rollbackException) { // Ignore exception - $this->assertMatchesRegularExpression( + $this->proxyAssertMatchesRegularExpression( '~(server has gone away)~', $rollbackException->getMessage() ); } // Ignore exception - $this->assertMatchesRegularExpression( + $this->proxyAssertMatchesRegularExpression( '~(server has gone away)~', $exception->getMessage() ); @@ -207,18 +209,18 @@ SQL Db::table('test_tran_a')->commit(); // tran 0 Db::table('test_tran_a')->commit(); - } catch (\Throwable | \Exception $exception) { + } catch (Throwable | Exception $exception) { try { Db::table('test_tran_a')->rollback(); - } catch (\Exception $rollbackException) { + } catch (Exception $rollbackException) { // Ignore exception - $this->assertMatchesRegularExpression( + $this->proxyAssertMatchesRegularExpression( '~(server has gone away)~', $rollbackException->getMessage() ); } // Ignore exception - $this->assertMatchesRegularExpression( + $this->proxyAssertMatchesRegularExpression( '~(server has gone away)~', $exception->getMessage() ); -- Gitee From 48e01bd25fdd51afee95343a4381fabc5a9cb764 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 24 Feb 2021 08:54:36 +0800 Subject: [PATCH 240/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 3 +++ src/model/concern/Attribute.php | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Model.php b/src/Model.php index 7c53499..7d19615 100644 --- a/src/Model.php +++ b/src/Model.php @@ -456,6 +456,9 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab if ($this->exists) { $this->data = $this->db()->find($this->getKey())->getData(); $this->origin = $this->data; + $this->get = []; + $this->set = []; + $this->readDataType(); if ($relation) { $this->relation = []; diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index c1a4edc..99c9f80 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -94,6 +94,12 @@ trait Attribute */ protected $strict = true; + /** + * 获取器数据 + * @var array + */ + private $get = []; + /** * 修改器执行记录 * @var array @@ -266,7 +272,7 @@ trait Attribute } /** - * 获取对象原始数据 如果不存在指定字段返回false + * 获取当前对象数据 如果不存在指定字段返回false * @access public * @param string $name 字段名 留空获取全部 * @return mixed @@ -479,8 +485,12 @@ trait Attribute { // 检测属性获取器 $fieldName = $this->getRealFieldName($name); - $method = 'get' . Str::studly($name) . 'Attr'; + if (array_key_exists($fieldName, $this->get)) { + return $this->get[$fieldName]; + } + + $method = 'get' . Str::studly($name) . 'Attr'; if (isset($this->withAttr[$fieldName])) { if ($relation) { $value = $this->getRelationValue($relation); @@ -504,6 +514,8 @@ trait Attribute $this->relation[$name] = $value; } + $this->get[$fieldName] = $value; + return $value; } @@ -516,9 +528,9 @@ trait Attribute { foreach ($this->data as $key => $value) { if (isset($this->type[$key])) { - $this->data[$key] = $this->readTransform($value, $this->type[$key]); + $this->get[$key] = $this->readTransform($value, $this->type[$key]); } elseif ($this->autoWriteTimestamp && in_array($key, [$this->createTime, $this->updateTime])) { - $this->data[$key] = $this->getTimestampValue($value); + $this->get[$key] = $this->getTimestampValue($value); } } } -- Gitee From e9a33f9b35f38ee1b4dbf3d90f9e7b9ef2f43483 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 26 Feb 2021 13:40:21 +0800 Subject: [PATCH 241/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3parseIn=E6=96=B9?= =?UTF-8?q?=E6=B3=95=20=E8=AE=BE=E7=BD=AE=E6=95=B0=E6=8D=AE=E5=90=8E?= =?UTF-8?q?=E6=B8=85=E7=90=86=E8=8E=B7=E5=8F=96=E5=99=A8=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Builder.php | 5 ++--- src/model/concern/Attribute.php | 6 +++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/db/Builder.php b/src/db/Builder.php index 3b00943..914dd70 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -15,7 +15,6 @@ namespace think\db; use Closure; use PDO; use think\db\exception\DbException as Exception; -use function count; /** * Db Builder @@ -758,7 +757,7 @@ abstract class Builder } else { $value = array_unique(is_array($value) ? $value : explode(',', $value)); if (count($value) === 0) { - return '0 = 1'; + return 'IN' == $exp ? '0 = 1' : '1 = 1'; } $array = []; @@ -770,7 +769,7 @@ abstract class Builder if (count($array) == 1) { return $key . ('IN' == $exp ? ' = ' : ' <> ') . $array[0]; } else { - $value = implode(',', $array); + $value = implode(',', $array); } } diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 99c9f80..737e29d 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -268,7 +268,9 @@ trait Attribute return $this->origin; } - return array_key_exists($name, $this->origin) ? $this->origin[$name] : null; + $fieldName = $this->getRealFieldName($name); + + return array_key_exists($fieldName, $this->origin) ? $this->origin[$fieldName] : null; } /** @@ -332,6 +334,7 @@ trait Attribute $name = $this->getRealFieldName($name); $this->data[$name] = $value; + unset($this->get[$name]); } /** @@ -380,6 +383,7 @@ trait Attribute // 设置数据对象属性 $this->data[$name] = $value; + unset($this->get[$name]); } /** -- Gitee From 8e846558713f845cf9c3ce6dfde816b2529eb25c Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 26 Feb 2021 14:23:26 +0800 Subject: [PATCH 242/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=5F=5Funset=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Model.php b/src/Model.php index 7d19615..5319edc 100644 --- a/src/Model.php +++ b/src/Model.php @@ -967,7 +967,10 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab */ public function __unset(string $name): void { - unset($this->data[$name], $this->relation[$name]); + unset($this->data[$name], + $this->get[$name], + $this->set[$name], + $this->relation[$name]); } // ArrayAccess -- Gitee From 58d5c5d16fcb2574d42e371713035a98f1d00d71 Mon Sep 17 00:00:00 2001 From: auooru Date: Fri, 26 Feb 2021 14:53:31 +0800 Subject: [PATCH 243/365] =?UTF-8?q?=E5=AE=8C=E5=96=84=20column=20distinct?= =?UTF-8?q?=20=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/orm/DbTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/orm/DbTest.php b/tests/orm/DbTest.php index c16eb8c..d8f2340 100644 --- a/tests/orm/DbTest.php +++ b/tests/orm/DbTest.php @@ -64,7 +64,7 @@ SQL $this->assertEquals(array_column($users, 'username'), $result); // 获取某字段唯一 - $result = Db::table('test_user')->distinct(true)->column('type'); + $result = Db::table('test_user')->column('DISTINCT type'); $expected = array_unique(array_column($users, 'type')); $this->assertEquals($expected, $result); -- Gitee From 39a9d0a0e52d9b8bad9d98484d8484cdf5b683a7 Mon Sep 17 00:00:00 2001 From: auooru Date: Fri, 26 Feb 2021 14:54:03 +0800 Subject: [PATCH 244/365] =?UTF-8?q?=E8=A1=A5=E5=85=85=20whereNotIn=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/orm/DbTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/orm/DbTest.php b/tests/orm/DbTest.php index d8f2340..d788630 100644 --- a/tests/orm/DbTest.php +++ b/tests/orm/DbTest.php @@ -146,11 +146,21 @@ SQL $result = Db::table('test_user')->whereIn('type', [])->column('*'); $this->assertEquals([], $result); + $expected = Collection::make(self::$testUserData)->whereNotIn('type', [1, 3])->values()->toArray(); + $result = Db::table('test_user')->whereNotIn('type', [1, 3])->column('*'); + $this->assertEquals($expected, $result); + + $expected = Collection::make(self::$testUserData)->values()->toArray(); + $result = Db::table('test_user')->whereNotIn('type', [])->column('*'); + $this->assertEquals($expected, $result); + $this->assertEquals([ "SELECT * FROM `test_user` WHERE `type` IN (1,3)", "SELECT * FROM `test_user` WHERE `type` = 1", "SELECT * FROM `test_user` WHERE `type` IN (1,0)", "SELECT * FROM `test_user` WHERE 0 = 1", + "SELECT * FROM `test_user` WHERE `type` NOT IN (1,3)", + "SELECT * FROM `test_user` WHERE 1 = 1", ], $sqlLogs); } -- Gitee From e512b8d5a4e46602b5dbf05b7c0888980b53e193 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 3 Mar 2021 09:14:10 +0800 Subject: [PATCH 245/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Model.php b/src/Model.php index 5319edc..6dcb2f9 100644 --- a/src/Model.php +++ b/src/Model.php @@ -547,6 +547,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab // 重新记录原始数据 $this->origin = $this->data; $this->set = []; + $this->get = []; $this->lazySave = false; return true; @@ -709,6 +710,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $pk = $this->getPk(); if (is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) { + unset($this->get[$pk]); $this->data[$pk] = $result; } } -- Gitee From 0922f110808591fd450256ad4fa35eca0ad0ff55 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 19 Mar 2021 19:42:49 +0800 Subject: [PATCH 246/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Model.php b/src/Model.php index 6dcb2f9..577a875 100644 --- a/src/Model.php +++ b/src/Model.php @@ -723,6 +723,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab // 标记数据已经存在 $this->exists = true; + $this->origin = $this->data; // 新增回调 $this->trigger('AfterInsert'); -- Gitee From f6e2d76c19c2344c33cf2ec8b2faafeca9ae1893 Mon Sep 17 00:00:00 2001 From: "weiwei163@foxmail.com" Date: Fri, 12 Mar 2021 17:50:00 +0800 Subject: [PATCH 247/365] =?UTF-8?q?fixed:=20=E5=85=BC=E5=AE=B9=20symfony/c?= =?UTF-8?q?ache=20=E7=BB=84=E4=BB=B6=E8=A7=84=E5=88=99=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E8=83=BD=E5=9C=A8=20key=20=E6=88=96=20tag=20=E4=B8=AD=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=9A=84=E4=BF=9D=E7=95=99=E5=AD=97=E7=AC=A6=20?= =?UTF-8?q?=EF=BC=9A'{}()/\@:'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Connection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/Connection.php b/src/db/Connection.php index 4400ece..aa86ba8 100644 --- a/src/db/Connection.php +++ b/src/db/Connection.php @@ -290,7 +290,7 @@ abstract class Connection implements ConnectionInterface protected function getCacheKey(BaseQuery $query, string $method = ''): string { if (!empty($query->getOptions('key')) && empty($method)) { - $key = 'think:' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); + $key = 'think_' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key'); } else { $key = $query->getQueryGuid(); } -- Gitee From 0a5412967eb78339f1160e1f9140aa8778169d74 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 31 Mar 2021 17:52:30 +0800 Subject: [PATCH 248/365] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=99=A8=E5=92=8C=E7=B1=BB=E5=9E=8B=E8=BD=AC=E6=8D=A2=E7=9A=84?= =?UTF-8?q?=E5=A4=84=E7=90=86=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 9 +---- src/model/concern/Attribute.php | 53 +++++------------------------- src/model/concern/RelationShip.php | 7 ++-- src/model/concern/SoftDelete.php | 7 ++-- 4 files changed, 17 insertions(+), 59 deletions(-) diff --git a/src/Model.php b/src/Model.php index 577a875..35c9b25 100644 --- a/src/Model.php +++ b/src/Model.php @@ -267,7 +267,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab public function newInstance(array $data = [], $where = null): Model { $model = new static($data); - $model->readDataType(); if ($this->connection) { $model->setConnection($this->connection); @@ -457,8 +456,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $this->data = $this->db()->find($this->getKey())->getData(); $this->origin = $this->data; $this->get = []; - $this->set = []; - $this->readDataType(); if ($relation) { $this->relation = []; @@ -546,7 +543,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab // 重新记录原始数据 $this->origin = $this->data; - $this->set = []; $this->get = []; $this->lazySave = false; @@ -614,8 +610,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab return true; } - $data = $this->writeDataType($data); - if ($this->autoWriteTimestamp && $this->updateTime) { // 自动写入更新时间 $data[$this->updateTime] = $this->autoWriteTimestamp(); @@ -678,7 +672,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } $this->checkData(); - $data = $this->writeDataType($this->data); + $data = $this->data; // 时间戳自动写入 if ($this->autoWriteTimestamp) { @@ -972,7 +966,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab { unset($this->data[$name], $this->get[$name], - $this->set[$name], $this->relation[$name]); } diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 737e29d..8f6f918 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -100,12 +100,6 @@ trait Attribute */ private $get = []; - /** - * 修改器执行记录 - * @var array - */ - private $set = []; - /** * 动态获取器 * @var array @@ -363,10 +357,6 @@ trait Attribute { $name = $this->getRealFieldName($name); - if (isset($this->set[$name])) { - return; - } - // 检测修改器 $method = 'set' . Str::studly($name) . 'Attr'; @@ -375,10 +365,12 @@ trait Attribute $value = $this->$method($value, array_merge($this->data, $data)); - $this->set[$name] = true; if (is_null($value) && $array !== $this->data) { return; } + } elseif (isset($this->type[$name])) { + // 类型转换 + $value = $this->writeTransform($value, $this->type[$name]); } // 设置数据对象属性 @@ -512,6 +504,11 @@ trait Attribute } $value = $this->$method($value, $this->data); + } elseif (isset($this->type[$fieldName])) { + // 类型转换 + $value = $this->readTransform($value, $this->type[$fieldName]); + } elseif ($this->autoWriteTimestamp && in_array($fieldName, [$this->createTime, $this->updateTime])) { + $value = $this->getTimestampValue($value); } elseif ($relation) { $value = $this->getRelationValue($relation); // 保存关联对象值 @@ -523,40 +520,6 @@ trait Attribute return $value; } - /** - * 读取数据类型处理 - * @access protected - * @return void - */ - protected function readDataType(): void - { - foreach ($this->data as $key => $value) { - if (isset($this->type[$key])) { - $this->get[$key] = $this->readTransform($value, $this->type[$key]); - } elseif ($this->autoWriteTimestamp && in_array($key, [$this->createTime, $this->updateTime])) { - $this->get[$key] = $this->getTimestampValue($value); - } - } - } - - /** - * 写入数据类型处理 - * @access protected - * @param array $data 数据 - * @return array - */ - protected function writeDataType(array $data): array - { - foreach ($data as $name => &$value) { - if (isset($this->type[$name])) { - // 类型转换 - $value = $this->writeTransform($value, $this->type[$name]); - } - } - - return $data; - } - /** * 获取JSON字段属性值 * @access protected diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index f3da1c4..49a5655 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -810,19 +810,20 @@ trait RelationShip /** * 自动关联数据删除(支持一对一及一对多关联) * @access protected + * @param bool $force 强制删除 * @return void */ - protected function autoRelationDelete(): void + protected function autoRelationDelete($force = false): void { foreach ($this->relationWrite as $key => $name) { $name = is_numeric($key) ? $name : $key; $result = $this->getRelation($name, true); if ($result instanceof Model) { - $result->delete(); + $result->force($force)->delete(); } elseif ($result instanceof Collection) { foreach ($result as $model) { - $model->delete(); + $model->force($force)->delete(); } } } diff --git a/src/model/concern/SoftDelete.php b/src/model/concern/SoftDelete.php index ce5d392..5a9a56d 100644 --- a/src/model/concern/SoftDelete.php +++ b/src/model/concern/SoftDelete.php @@ -107,9 +107,10 @@ trait SoftDelete return false; } - $name = $this->getDeleteTimeField(); + $name = $this->getDeleteTimeField(); + $force = $this->isForce(); - if ($name && !$this->isForce()) { + if ($name && !$force) { // 软删除 $this->set($name, $this->autoWriteTimestamp($name)); @@ -131,7 +132,7 @@ trait SoftDelete // 关联删除 if (!empty($this->relationWrite)) { - $this->autoRelationDelete(); + $this->autoRelationDelete($force); } $this->trigger('AfterDelete'); -- Gitee From ddf15dc6e1c1f3f79e392f3578adefa22f7e4bdd Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 16 Apr 2021 10:29:16 +0800 Subject: [PATCH 249/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=85=B3=E8=81=94?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/RelationShip.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 49a5655..33c1b89 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -136,7 +136,7 @@ trait RelationShip public function relationQuery(array $relations, array $withRelationAttr = []): void { foreach ($relations as $key => $relation) { - $subRelation = ''; + $subRelation = []; $closure = null; if ($relation instanceof Closure) { @@ -161,7 +161,7 @@ trait RelationShip $relationResult->withAttr($withRelationAttr[$relationName]); } - $this->relation[$relation] = $relationResult->getRelation($subRelation, $closure); + $this->relation[$relation] = $relationResult->getRelation((array) $subRelation, $closure); } } -- Gitee From 7b1d38425306c61de27a8c42eb90303650a162fa Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 16 Apr 2021 22:49:23 +0800 Subject: [PATCH 250/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=A4=9A=E6=AC=A1?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E7=9A=84=E6=97=B6=E5=80=99field=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=A4=B1=E6=95=88=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 4 ---- src/db/Builder.php | 2 +- src/db/builder/Mysql.php | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 42b7ba0..4e9a20d 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -1163,10 +1163,6 @@ abstract class BaseQuery $this->parseView($options); } - if (!isset($options['field'])) { - $options['field'] = '*'; - } - foreach (['data', 'order', 'join', 'union'] as $name) { if (!isset($options[$name])) { $options[$name] = []; diff --git a/src/db/Builder.php b/src/db/Builder.php index 914dd70..0fea9cf 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -1125,7 +1125,7 @@ abstract class Builder $this->parseTable($query, $options['table']), $this->parseDistinct($query, $options['distinct']), $this->parseExtra($query, $options['extra']), - $this->parseField($query, $options['field']), + $this->parseField($query, $options['field'] ?? '*'), $this->parseJoin($query, $options['join']), $this->parseWhere($query, $options['where']), $this->parseGroup($query, $options['group']), diff --git a/src/db/builder/Mysql.php b/src/db/builder/Mysql.php index 10c8d1d..693debe 100644 --- a/src/db/builder/Mysql.php +++ b/src/db/builder/Mysql.php @@ -89,7 +89,7 @@ class Mysql extends Builder $this->parsePartition($query, $options['partition']), $this->parseDistinct($query, $options['distinct']), $this->parseExtra($query, $options['extra']), - $this->parseField($query, $options['field']), + $this->parseField($query, $options['field'] ?? '*'), $this->parseJoin($query, $options['join']), $this->parseWhere($query, $options['where']), $this->parseGroup($query, $options['group']), -- Gitee From 34575769f2231c79e64ac8ed158db84cd682c6b2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 19 Apr 2021 10:54:46 +0800 Subject: [PATCH 251/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Builder.php | 4 ++-- src/db/builder/Mysql.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/db/Builder.php b/src/db/Builder.php index 0fea9cf..73244f4 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -136,7 +136,7 @@ abstract class Builder } if (empty($fields)) { - if ('*' == $options['field']) { + if (empty($options['field']) || '*' == $options['field']) { $fields = array_keys($bind); } else { $fields = $options['field']; @@ -1187,7 +1187,7 @@ abstract class Builder $bind = $query->getFieldsBindType(); // 获取合法的字段 - if ('*' == $options['field']) { + if (empty($options['field']) || '*' == $options['field']) { $allowFields = array_keys($bind); } else { $allowFields = $options['field']; diff --git a/src/db/builder/Mysql.php b/src/db/builder/Mysql.php index 693debe..136b0de 100644 --- a/src/db/builder/Mysql.php +++ b/src/db/builder/Mysql.php @@ -155,7 +155,7 @@ class Mysql extends Builder $bind = $query->getFieldsBindType(); // 获取合法的字段 - if ('*' == $options['field']) { + if (empty($options['field']) || '*' == $options['field']) { $allowFields = array_keys($bind); } else { $allowFields = $options['field']; -- Gitee From 1119d979b850849f3725856460cf108eec1c3eb8 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 19 Apr 2021 21:29:37 +0800 Subject: [PATCH 252/365] =?UTF-8?q?MorphOne=E5=85=B3=E8=81=94=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E7=BB=91=E5=AE=9A=E5=85=B3=E8=81=94=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/MorphOne.php | 71 ++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php index 6789c76..bc89c0b 100644 --- a/src/model/relation/MorphOne.php +++ b/src/model/relation/MorphOne.php @@ -41,6 +41,12 @@ class MorphOne extends Relation */ protected $type; + /** + * 绑定的关联属性 + * @var array + */ + protected $bindAttr = []; + /** * 构造函数 * @access public @@ -78,6 +84,11 @@ class MorphOne extends Relation $relationModel = $this->query->relation($subRelation)->find(); if ($relationModel) { + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($this->parent, $relationModel); + } + $relationModel->setParent(clone $this->parent); } @@ -154,7 +165,13 @@ class MorphOne extends Relation $relationModel->exists(true); } - $result->setRelation($relation, $relationModel); + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($result, $relationModel); + } else { + // 设置关联属性 + $result->setRelation($relation, $relationModel); + } } } } @@ -188,7 +205,13 @@ class MorphOne extends Relation $relationModel = null; } - $result->setRelation($relation, $relationModel); + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($result, $relationModel); + } else { + // 设置关联属性 + $result->setRelation($relation, $relationModel); + } } } @@ -277,4 +300,48 @@ class MorphOne extends Relation } } + /** + * 绑定关联表的属性到父模型属性 + * @access public + * @param array $attr 要绑定的属性列表 + * @return $this + */ + public function bind(array $attr) + { + $this->bindAttr = $attr; + + return $this; + } + + /** + * 获取绑定属性 + * @access public + * @return array + */ + public function getBindAttr(): array + { + return $this->bindAttr; + } + + /** + * 绑定关联属性到父模型 + * @access protected + * @param Model $result 父模型对象 + * @param Model $model 关联模型对象 + * @return void + * @throws Exception + */ + protected function bindAttr(Model $result, Model $model = null): void + { + foreach ($this->bindAttr as $key => $attr) { + $key = is_numeric($key) ? $attr : $key; + $value = $result->getOrigin($key); + + if (!is_null($value)) { + throw new Exception('bind attr has exists:' . $key); + } + + $result->setAttr($key, $model ? $model->$attr : null); + } + } } -- Gitee From d58b7bd4505b575ec48f4ca7d13203745d429b45 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Tue, 27 Apr 2021 14:14:21 +0800 Subject: [PATCH 253/365] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 4 ++-- src/db/concern/ResultOperation.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 4e9a20d..29bcbf2 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -1075,7 +1075,7 @@ abstract class BaseQuery * 查找记录 * @access public * @param mixed $data 数据 - * @return Collection + * @return Collection|array|static[] * @throws Exception * @throws ModelNotFoundException * @throws DataNotFoundException @@ -1109,7 +1109,7 @@ abstract class BaseQuery * 查找单条记录 * @access public * @param mixed $data 查询数据 - * @return array|Model|null + * @return array|Model|null|static * @throws Exception * @throws ModelNotFoundException * @throws DataNotFoundException diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index d93c409..2860100 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -149,7 +149,7 @@ trait ResultOperation /** * 处理空数据 * @access protected - * @return array|Model|null + * @return array|Model|null|static * @throws DbException * @throws ModelNotFoundException * @throws DataNotFoundException @@ -167,7 +167,7 @@ trait ResultOperation * 查找单条记录 不存在返回空数据(或者空模型) * @access public * @param mixed $data 数据 - * @return array|Model + * @return array|Model|static */ public function findOrEmpty($data = null) { @@ -226,7 +226,7 @@ trait ResultOperation * 查找多条记录 如果不存在则抛出异常 * @access public * @param array|string|Query|Closure $data 数据 - * @return array|Model + * @return array|Collection|static[] */ public function selectOrFail($data = null) { @@ -237,7 +237,7 @@ trait ResultOperation * 查找单条记录 如果不存在则抛出异常 * @access public * @param array|string|Query|Closure $data 数据 - * @return array|Model + * @return array|Model|static */ public function findOrFail($data = null) { -- Gitee From affdf391065804ad8fdf53b8e7e82d4dbcc54fa1 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 29 Apr 2021 22:47:02 +0800 Subject: [PATCH 254/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E6=B3=A8=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/ModelEvent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/concern/ModelEvent.php b/src/model/concern/ModelEvent.php index f560379..d2388ab 100644 --- a/src/model/concern/ModelEvent.php +++ b/src/model/concern/ModelEvent.php @@ -74,7 +74,7 @@ trait ModelEvent if (method_exists(static::class, $call)) { $result = call_user_func([static::class, $call], $this); } elseif (is_object(self::$event) && method_exists(self::$event, 'trigger')) { - $result = self::$event->trigger(static::class . '.' . $event, $this); + $result = self::$event->trigger('model.' . static::class . '.' . $event, $this); $result = empty($result) ? true : end($result); } else { $result = true; -- Gitee From 7acb555961437d9af5d8c2c8d47805df22572f61 Mon Sep 17 00:00:00 2001 From: liuqiandev <310227477@qq.com> Date: Thu, 1 Jul 2021 14:45:39 +0800 Subject: [PATCH 255/365] =?UTF-8?q?=E8=BD=AF=E5=88=A0=E9=99=A4destroy?= =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=E4=BB=85=E5=BD=93=E5=BC=BA=E5=88=B6?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=B6=E5=8C=85=E5=90=AB=E8=BD=AF=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E6=95=B0=E6=8D=AE=EF=BC=9B=E4=BC=A0=E5=85=A5=E7=A9=BA?= =?UTF-8?q?=E5=80=BC=EF=BC=88=E5=8C=85=E6=8B=AC=E7=A9=BA=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E5=92=8C=E7=A9=BA=E6=95=B0=E7=BB=84=EF=BC=89=E7=9A=84?= =?UTF-8?q?=E6=97=B6=E5=80=99=E4=B8=8D=E4=BC=9A=E5=81=9A=E4=BB=BB=E4=BD=95?= =?UTF-8?q?=E7=9A=84=E6=95=B0=E6=8D=AE=E5=88=A0=E9=99=A4=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=EF=BC=8C=E4=BD=86=E4=BC=A0=E5=85=A50=E5=88=99=E6=98=AF?= =?UTF-8?q?=E6=9C=89=E6=95=88=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/SoftDelete.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/model/concern/SoftDelete.php b/src/model/concern/SoftDelete.php index 5a9a56d..8d76bb0 100644 --- a/src/model/concern/SoftDelete.php +++ b/src/model/concern/SoftDelete.php @@ -151,8 +151,16 @@ trait SoftDelete */ public static function destroy($data, bool $force = false): bool { - // 包含软删除数据 - $query = (new static())->withTrashedData(true)->db(false); + // 传入空值(包括空字符串和空数组)的时候不会做任何的数据删除操作,但传入0则是有效的 + if(empty($data) && $data !== 0){ + return false; + } + // 仅当强制删除时包含软删除数据 + $model = (new static()); + if($force){ + $model->withTrashedData(true); + } + $query = $model->db(false); if (is_array($data) && key($data) !== 0) { $query->where($data); -- Gitee From 341f3c5d9d6ecb9ebbcbf63874e2e55c89583c72 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 3 Jul 2021 21:49:57 +0800 Subject: [PATCH 256/365] =?UTF-8?q?=E5=85=B3=E8=81=94=E6=94=AF=E6=8C=81wit?= =?UTF-8?q?houtField=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Relation.php | 18 ++++++++++++++++++ src/model/relation/BelongsToMany.php | 7 ++++++- src/model/relation/HasMany.php | 4 ++++ src/model/relation/HasManyThrough.php | 7 ++++++- src/model/relation/MorphToMany.php | 7 ++++++- src/model/relation/OneToOne.php | 2 ++ 6 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/model/Relation.php b/src/model/Relation.php index e823bd9..0bd33d4 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -79,6 +79,12 @@ abstract class Relation */ protected $withField; + /** + * 排除关联数据字段 + * @var array + */ + protected $withoutField; + /** * 获取关联的所属模型 * @access public @@ -236,6 +242,18 @@ abstract class Relation return $this; } + /** + * 排除关联数据的字段 + * @access public + * @param array $field 关联字段限制 + * @return $this + */ + public function withoutField(array $field) + { + $this->withoutField = $field; + return $this; + } + /** * 判断闭包的参数类型 * @access protected diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 6b64d95..5f177c1 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -443,7 +443,12 @@ class BelongsToMany extends Relation if (empty($this->baseQuery)) { $tableName = $this->query->getTable(); $table = $this->pivot->db()->getTable(); - $fields = $this->getQueryFields($tableName); + + if ($this->withoutField) { + $this->query->withoutField($this->withoutField); + } + + $fields = $this->getQueryFields($tableName); if ($this->withLimit) { $this->query->limit($this->withLimit); diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index a67d41b..49f4c93 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -206,6 +206,10 @@ class HasMany extends Relation $closure($this->getClosureType($closure)); } + if ($this->withoutField) { + $this->query->withoutField($this->withoutField); + } + $list = $this->query ->where($where) ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index 30d5ca4..4de5060 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -366,7 +366,12 @@ class HasManyThrough extends Relation $pk = $this->throughPk; $throughKey = $this->throughKey; $modelTable = $this->parent->getTable(); - $fields = $this->getQueryFields($alias); + + if ($this->withoutField) { + $this->query->withoutField($this->withoutField); + } + + $fields = $this->getQueryFields($alias); $this->query ->field($fields) diff --git a/src/model/relation/MorphToMany.php b/src/model/relation/MorphToMany.php index 88bbf9a..32f853f 100644 --- a/src/model/relation/MorphToMany.php +++ b/src/model/relation/MorphToMany.php @@ -200,7 +200,12 @@ class MorphToMany extends BelongsToMany // 关联查询封装 $tableName = $this->query->getTable(); $table = $this->pivot->db()->getTable(); - $fields = $this->getQueryFields($tableName); + + if ($this->withoutField) { + $this->query->withoutField($this->withoutField); + } + + $fields = $this->getQueryFields($tableName); if ($this->withLimit) { $this->query->limit($this->withLimit); diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index 65bbfda..d47d7f3 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -305,6 +305,8 @@ abstract class OneToOne extends Relation if ($this->withField) { $this->query->field($this->withField); + } elseif ($this->withoutField) { + $this->query->withoutField($this->withoutField); } $list = $this->query -- Gitee From 375a3b11578a1740db60c4db05b8244c1de701cf Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 8 Jul 2021 17:10:33 +0800 Subject: [PATCH 257/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BgetValue=E5=AF=B9?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E8=8E=B7=E5=8F=96=E5=99=A8=E7=9A=84=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 8f6f918..34cb59a 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -496,7 +496,9 @@ trait Attribute $value = $this->getJsonValue($fieldName, $value); } else { $closure = $this->withAttr[$fieldName]; - $value = $closure($value, $this->data); + if ($closure instanceof \Closure) { + $value = $closure($value, $this->data); + } } } elseif (method_exists($this, $method)) { if ($relation) { -- Gitee From 64bbfdde01f4fd6939c2f695fceb08e6d764ac6b Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 14 Jul 2021 15:51:15 +0800 Subject: [PATCH 258/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Paginator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Paginator.php b/src/Paginator.php index f5d8006..d8d43d7 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -175,7 +175,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J $path = $this->options['path']; } else { $parameters = []; - $path = str_replace('[PAGE]', $page, $this->options['path']); + $path = str_replace('[PAGE]', (string) $page, $this->options['path']); } if (count($this->options['query']) > 0) { -- Gitee From ab531a4b6b31f4dd74ff0768eae57124c4ba52ee Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 15 Jul 2021 10:36:56 +0800 Subject: [PATCH 259/365] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=20=E6=94=B9=E8=BF=9B=E8=81=9A=E5=90=88?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 2 +- src/db/PDOConnection.php | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 29bcbf2..0e85e5a 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -621,7 +621,7 @@ abstract class BaseQuery $bind = $this->bind; $total = $this->count(); - $results = $this->options($options)->bind($bind)->page($page, $listRows)->select(); + $results = $total > 0 ? $this->options($options)->bind($bind)->page($page, $listRows)->select() : []; } elseif ($simple) { $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select(); $total = null; diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 0a51c0c..fba0734 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1158,7 +1158,7 @@ abstract class PDOConnection extends Connection $field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS think_' . strtolower($aggregate); - $result = $this->value($query, $field, 0, false); + $result = $this->value($query, $field, 0); return $force ? (float) $result : $result; } @@ -1448,7 +1448,7 @@ abstract class PDOConnection extends Connection } $this->reConnectTimes = 0; } catch (\Throwable | \Exception $e) { - if ($this->transTimes === 1 && $this->reConnectTimes < 4 && $this->isBreak($e)) { + if (1 === $this->transTimes && $this->reConnectTimes < 4 && $this->isBreak($e)) { --$this->transTimes; ++$this->reConnectTimes; $this->close()->startTrans(); @@ -1566,10 +1566,10 @@ abstract class PDOConnection extends Connection */ public function close() { - $this->linkID = null; - $this->linkWrite = null; - $this->linkRead = null; - $this->links = []; + $this->linkID = null; + $this->linkWrite = null; + $this->linkRead = null; + $this->links = []; $this->transTimes = 0; $this->free(); -- Gitee From cc94b5edf0bf371035e4e7cb5440c8186f434a39 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 15 Jul 2021 21:19:23 +0800 Subject: [PATCH 260/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=99=9A=E6=8B=9F?= =?UTF-8?q?=E6=A8=A1=E5=9E=8Btrait?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Virtual.php | 95 +++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/model/concern/Virtual.php diff --git a/src/model/concern/Virtual.php b/src/model/concern/Virtual.php new file mode 100644 index 0000000..f954b6f --- /dev/null +++ b/src/model/concern/Virtual.php @@ -0,0 +1,95 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think\model\concern; + +use think\db\BaseQuery as Query; +use think\db\exception\DbException as Exception; + +/** + * 虚拟模型 + */ +trait Virtual +{ + /** + * 获取当前模型的数据库查询对象 + * @access public + * @param array $scope 设置不使用的全局查询范围 + * @return Query + */ + public function db($scope = []): Query + { + throw new Exception('virtual model not support db query'); + } + + /** + * 获取字段类型信息 + * @access public + * @param string $field 字段名 + * @return string|null + */ + public function getFieldType(string $field) + {} + + /** + * 保存当前数据对象 + * @access public + * @param array $data 数据 + * @param string $sequence 自增序列名 + * @return bool + */ + public function save(array $data = [], string $sequence = null): bool + { + // 数据对象赋值 + $this->setAttrs($data); + + if ($this->isEmpty() || false === $this->trigger('BeforeWrite')) { + return false; + } + + // 写入回调 + $this->trigger('AfterWrite'); + + // 重新记录原始数据 + $this->origin = $this->data; + $this->get = []; + $this->lazySave = false; + $this->exists = true; + + return true; + } + + /** + * 删除当前的记录 + * @access public + * @return bool + */ + public function delete(): bool + { + if (!$this->exists || $this->isEmpty() || false === $this->trigger('BeforeDelete')) { + return false; + } + + // 关联删除 + if (!empty($this->relationWrite)) { + $this->autoRelationDelete(); + } + + $this->trigger('AfterDelete'); + + $this->exists = false; + $this->lazySave = false; + + return true; + } + +} -- Gitee From 4e556f185677b081ce22b2cc6cd03c758246c56b Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 15 Jul 2021 21:50:07 +0800 Subject: [PATCH 261/365] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Virtual.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/model/concern/Virtual.php b/src/model/concern/Virtual.php index f954b6f..66cdfb7 100644 --- a/src/model/concern/Virtual.php +++ b/src/model/concern/Virtual.php @@ -59,11 +59,7 @@ trait Virtual // 写入回调 $this->trigger('AfterWrite'); - // 重新记录原始数据 - $this->origin = $this->data; - $this->get = []; - $this->lazySave = false; - $this->exists = true; + $this->exists(true); return true; } @@ -75,7 +71,7 @@ trait Virtual */ public function delete(): bool { - if (!$this->exists || $this->isEmpty() || false === $this->trigger('BeforeDelete')) { + if (!$this->isExists() || $this->isEmpty() || false === $this->trigger('BeforeDelete')) { return false; } @@ -86,8 +82,7 @@ trait Virtual $this->trigger('AfterDelete'); - $this->exists = false; - $this->lazySave = false; + $this->exists(false); return true; } -- Gitee From 3811b3af0f99fef88bd95bd633409bb4e48f47e7 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 15 Jul 2021 22:09:35 +0800 Subject: [PATCH 262/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E8=99=9A=E6=8B=9F?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E6=97=B6=E9=97=B4=E5=AD=97=E6=AE=B5=E5=86=99?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Virtual.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/model/concern/Virtual.php b/src/model/concern/Virtual.php index 66cdfb7..0362c08 100644 --- a/src/model/concern/Virtual.php +++ b/src/model/concern/Virtual.php @@ -56,6 +56,10 @@ trait Virtual return false; } + if ($this->autoWriteTimestamp) { + $this->appendTimestamp($data); + } + // 写入回调 $this->trigger('AfterWrite'); @@ -64,6 +68,25 @@ trait Virtual return true; } + /** + * 追加时间字段数据 + * @access private + * @param array $data 数据 + * @return void + */ + private function appendTimestamp(array $data) + { + if ($this->updateTime && !isset($data[$this->updateTime])) { + $updateTime = $this->autoWriteTimestamp(); + $this->set($this->updateTime, $this->getTimestampValue($updateTime)); + } + + if (!$this->isExists() && $this->createTime && !isset($data[$this->createTime])) { + $createTime = $this->autoWriteTimestamp(); + $this->set($this->createTime, $this->getTimestampValue($createTime)); + } + } + /** * 删除当前的记录 * @access public -- Gitee From 77fce2b44a7860b37d5cd1a57c3539f4aebec5a6 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 16 Jul 2021 13:29:51 +0800 Subject: [PATCH 263/365] =?UTF-8?q?=E8=99=9A=E6=8B=9F=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E6=97=B6=E9=97=B4=E8=87=AA=E5=8A=A8=E5=A4=84?= =?UTF-8?q?=E7=90=86=20=E5=A2=9E=E5=8A=A0=E6=97=B6=E9=97=B4=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E7=BB=9F=E4=B8=80=E9=85=8D=E7=BD=AE=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/TimeStamp.php | 15 +++++++++++++++ src/model/concern/Virtual.php | 23 ----------------------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/model/concern/TimeStamp.php b/src/model/concern/TimeStamp.php index e207961..2492e06 100644 --- a/src/model/concern/TimeStamp.php +++ b/src/model/concern/TimeStamp.php @@ -77,6 +77,21 @@ trait TimeStamp return $type; } + /** + * 设置时间字段名称 + * @access public + * @param string $createTime + * @param string $updateTime + * @return $this + */ + public function setTimeField(string $createTime, string $updateTime) + { + $this->createTime = $createTime; + $this->updateTime = $updateTime; + + return $this; + } + /** * 获取自动写入时间字段 * @access public diff --git a/src/model/concern/Virtual.php b/src/model/concern/Virtual.php index 0362c08..66cdfb7 100644 --- a/src/model/concern/Virtual.php +++ b/src/model/concern/Virtual.php @@ -56,10 +56,6 @@ trait Virtual return false; } - if ($this->autoWriteTimestamp) { - $this->appendTimestamp($data); - } - // 写入回调 $this->trigger('AfterWrite'); @@ -68,25 +64,6 @@ trait Virtual return true; } - /** - * 追加时间字段数据 - * @access private - * @param array $data 数据 - * @return void - */ - private function appendTimestamp(array $data) - { - if ($this->updateTime && !isset($data[$this->updateTime])) { - $updateTime = $this->autoWriteTimestamp(); - $this->set($this->updateTime, $this->getTimestampValue($updateTime)); - } - - if (!$this->isExists() && $this->createTime && !isset($data[$this->createTime])) { - $createTime = $this->autoWriteTimestamp(); - $this->set($this->createTime, $this->getTimestampValue($createTime)); - } - } - /** * 删除当前的记录 * @access public -- Gitee From 7a6ece32443aed49cb071a09a9a1299e985ea1be Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 18 Jul 2021 16:44:23 +0800 Subject: [PATCH 264/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=85=B3=E8=81=94?= =?UTF-8?q?=E7=9A=84withoutfield=E6=96=B9=E6=B3=95=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AD=97=E7=AC=A6=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Relation.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/model/Relation.php b/src/model/Relation.php index 0bd33d4..1779896 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -245,11 +245,15 @@ abstract class Relation /** * 排除关联数据的字段 * @access public - * @param array $field 关联字段限制 + * @param array|string $field 关联字段限制 * @return $this */ - public function withoutField(array $field) + public function withoutField($field) { + if (is_string($field)) { + $field = array_map('trim', explode(',', $field)); + } + $this->withoutField = $field; return $this; } -- Gitee From 008b991ebd65db0b51f7f7bfb45def352174a688 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 19 Jul 2021 09:12:52 +0800 Subject: [PATCH 265/365] =?UTF-8?q?=E6=B3=A8=E9=87=8A=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ResultOperation.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index 2860100..de01093 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -227,6 +227,8 @@ trait ResultOperation * @access public * @param array|string|Query|Closure $data 数据 * @return array|Collection|static[] + * @throws ModelNotFoundException + * @throws DataNotFoundException */ public function selectOrFail($data = null) { @@ -238,6 +240,8 @@ trait ResultOperation * @access public * @param array|string|Query|Closure $data 数据 * @return array|Model|static + * @throws ModelNotFoundException + * @throws DataNotFoundException */ public function findOrFail($data = null) { -- Gitee From fed6308912982320fe57cf62ceef2ca69fbb796a Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 19 Jul 2021 15:46:49 +0800 Subject: [PATCH 266/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3orderField=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/Builder.php b/src/db/Builder.php index 73244f4..1a1921f 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -969,8 +969,8 @@ abstract class Builder $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : ''; $bind = $query->getFieldsBindType(); - foreach ($val as $item) { - $val[] = $this->parseDataBind($query, $key, $item, $bind); + foreach ($val as $k => $item) { + $val[$k] = $this->parseDataBind($query, $key, $item, $bind); } return 'field(' . $this->parseKey($query, $key, true) . ',' . implode(',', $val) . ')' . $sort; -- Gitee From 3ee8c5982271e901838c631bb7641d8c932129a5 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 21 Jul 2021 10:09:16 +0800 Subject: [PATCH 267/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E6=9F=A5=E8=AF=A2=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 11 ++++++--- src/db/PDOConnection.php | 32 ++++++++++++--------------- src/db/exception/DbEventException.php | 19 ++++++++++++++++ 3 files changed, 41 insertions(+), 21 deletions(-) create mode 100644 src/db/exception/DbEventException.php diff --git a/src/DbManager.php b/src/DbManager.php index 147d9f6..c200c7d 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -17,6 +17,7 @@ use Psr\Log\LoggerInterface; use Psr\SimpleCache\CacheInterface; use think\db\BaseQuery; use think\db\ConnectionInterface; +use think\db\exception\DbEventException; use think\db\Query; use think\db\Raw; @@ -362,10 +363,14 @@ class DbManager */ public function trigger(string $event, $params = null) { - if (isset($this->event[$event])) { - foreach ($this->event[$event] as $callback) { - call_user_func_array($callback, [$this]); + try { + if (isset($this->event[$event])) { + foreach ($this->event[$event] as $callback) { + call_user_func_array($callback, [$params]); + } } + } catch (DbEventException $e) { + return false; } } diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index fba0734..1cd8e59 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -865,18 +865,16 @@ abstract class PDOConnection extends Connection public function find(BaseQuery $query): array { // 事件回调 - $result = $this->db->trigger('before_find', $query); - - if (!$result) { - // 执行查询 - $resultSet = $this->pdoQuery($query, function ($query) { - return $this->builder->select($query, true); - }); - - $result = $resultSet[0] ?? []; + if (false === $this->db->trigger('before_find', $query)) { + return []; } - return $result; + // 执行查询 + $resultSet = $this->pdoQuery($query, function ($query) { + return $this->builder->select($query, true); + }); + + return $resultSet[0] ?? []; } /** @@ -908,16 +906,14 @@ abstract class PDOConnection extends Connection */ public function select(BaseQuery $query): array { - $resultSet = $this->db->trigger('before_select', $query); - - if (!$resultSet) { - // 执行查询操作 - $resultSet = $this->pdoQuery($query, function ($query) { - return $this->builder->select($query); - }); + if (false === $this->db->trigger('before_select', $query)) { + return []; } - return $resultSet; + // 执行查询操作 + return $this->pdoQuery($query, function ($query) { + return $this->builder->select($query); + }); } /** diff --git a/src/db/exception/DbEventException.php b/src/db/exception/DbEventException.php new file mode 100644 index 0000000..394a1e8 --- /dev/null +++ b/src/db/exception/DbEventException.php @@ -0,0 +1,19 @@ + +// +---------------------------------------------------------------------- + +namespace think\db\exception; + +/** + * Db事件异常 + */ +class DbEventException extends DbException +{ +} -- Gitee From 5d3d5c1ebf8bfccf34bacd90edb42989b16ea409 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 21 Jul 2021 10:22:31 +0800 Subject: [PATCH 268/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DbManager.php | 11 +++-------- src/db/PDOConnection.php | 9 +++++++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/DbManager.php b/src/DbManager.php index c200c7d..f223cd2 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -17,7 +17,6 @@ use Psr\Log\LoggerInterface; use Psr\SimpleCache\CacheInterface; use think\db\BaseQuery; use think\db\ConnectionInterface; -use think\db\exception\DbEventException; use think\db\Query; use think\db\Raw; @@ -363,14 +362,10 @@ class DbManager */ public function trigger(string $event, $params = null) { - try { - if (isset($this->event[$event])) { - foreach ($this->event[$event] as $callback) { - call_user_func_array($callback, [$params]); - } + if (isset($this->event[$event])) { + foreach ($this->event[$event] as $callback) { + call_user_func_array($callback, [$params]); } - } catch (DbEventException $e) { - return false; } } diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 1cd8e59..60679af 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -16,6 +16,7 @@ use Closure; use PDO; use PDOStatement; use think\db\exception\BindParamException; +use think\db\exception\DbEventException; use think\db\exception\DbException; use think\db\exception\PDOException; use think\Model; @@ -865,7 +866,9 @@ abstract class PDOConnection extends Connection public function find(BaseQuery $query): array { // 事件回调 - if (false === $this->db->trigger('before_find', $query)) { + try { + $this->db->trigger('before_find', $query); + } catch (DbEventException $e) { return []; } @@ -906,7 +909,9 @@ abstract class PDOConnection extends Connection */ public function select(BaseQuery $query): array { - if (false === $this->db->trigger('before_select', $query)) { + try { + $this->db->trigger('before_select', $query); + } catch (DbEventException $e) { return []; } -- Gitee From bb240b8bdb8ccba3de94b39d6c5235daa5200c10 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 21 Jul 2021 21:01:22 +0800 Subject: [PATCH 269/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Builder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/Builder.php b/src/db/Builder.php index 1a1921f..06e689f 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -755,7 +755,7 @@ abstract class Builder } elseif ($value instanceof Raw) { $value = $this->parseRaw($query, $value); } else { - $value = array_unique(is_array($value) ? $value : explode(',', $value)); + $value = array_unique(is_array($value) ? $value : explode(',', (string) $value)); if (count($value) === 0) { return 'IN' == $exp ? '0 = 1' : '1 = 1'; } -- Gitee From 1af5d8d82a6a928ba19ea3dba70fd0cf7f2129ac Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 22 Jul 2021 17:07:06 +0800 Subject: [PATCH 270/365] =?UTF-8?q?Mongo=E9=A9=B1=E5=8A=A8=E6=94=B9?= =?UTF-8?q?=E8=BF=9B=E6=95=B0=E6=8D=AE=E5=BA=93=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/connector/Mongo.php | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/db/connector/Mongo.php b/src/db/connector/Mongo.php index 4b05b79..418dc50 100644 --- a/src/db/connector/Mongo.php +++ b/src/db/connector/Mongo.php @@ -27,10 +27,9 @@ use MongoDB\Driver\WriteConcern; use think\db\BaseQuery; use think\db\builder\Mongo as Builder; use think\db\Connection; +use think\db\exception\DbEventException; use think\db\exception\DbException as Exception; use think\db\Mongo as Query; -use function implode; -use function is_array; /** * Mongo数据库驱动 @@ -878,15 +877,15 @@ class Mongo extends Connection */ public function select(BaseQuery $query): array { - $resultSet = $this->db->trigger('before_select', $query); - - if (!$resultSet) { - $resultSet = $this->mongoQuery($query, function ($query) { - return $this->builder->select($query); - }); + try { + $this->db->trigger('before_select', $query); + } catch (DbEventException $e) { + return []; } - return $resultSet; + return $this->mongoQuery($query, function ($query) { + return $this->builder->select($query); + }); } /** @@ -904,18 +903,18 @@ class Mongo extends Connection public function find(BaseQuery $query): array { // 事件回调 - $result = $this->db->trigger('before_find', $query); - - if (!$result) { - // 执行查询 - $resultSet = $this->mongoQuery($query, function ($query) { - return $this->builder->select($query, true); - }); - - $result = $resultSet[0] ?? []; + try { + $this->db->trigger('before_find', $query); + } catch (DbEventException $e) { + return []; } - return $result; + // 执行查询 + $resultSet = $this->mongoQuery($query, function ($query) { + return $this->builder->select($query, true); + }); + + return $resultSet[0] ?? []; } /** -- Gitee From 1ca1cd7304ae97ed58a9d2c34269006497602fee Mon Sep 17 00:00:00 2001 From: Chason Ma Date: Fri, 23 Jul 2021 02:01:27 +0000 Subject: [PATCH 271/365] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20more=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E4=B8=AD=E5=BD=93=E5=89=8D=E6=9F=A5=E8=AF=A2=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=A9=BA=E6=97=B6=E8=8E=B7=E5=8F=96=20lastId=20?= =?UTF-8?q?=E4=BC=9A=E6=8A=A5=E9=94=99=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 0e85e5a..986b93b 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -741,7 +741,7 @@ abstract class BaseQuery return [ 'data' => $result, - 'lastId' => $last[$key], + 'lastId' => $last ? $last[$key] : null, ]; } -- Gitee From af77138cc7f09aeac6742a673e9fb30574ab5470 Mon Sep 17 00:00:00 2001 From: dtid_4cb73ba1d0cd43af <396751927@qq.com> Date: Sat, 24 Jul 2021 09:54:08 +0800 Subject: [PATCH 272/365] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E6=88=B3int=E7=B1=BB=E5=9E=8B=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=BF=AE=E6=94=B9=E8=BF=94=E5=9B=9E1970?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Model.php b/src/Model.php index 35c9b25..0345358 100644 --- a/src/Model.php +++ b/src/Model.php @@ -613,7 +613,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab if ($this->autoWriteTimestamp && $this->updateTime) { // 自动写入更新时间 $data[$this->updateTime] = $this->autoWriteTimestamp(); - $this->data[$this->updateTime] = $this->getTimestampValue($data[$this->updateTime]); + $this->data[$this->updateTime] = $data[$this->updateTime]; } // 检查允许字段 @@ -678,12 +678,12 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab if ($this->autoWriteTimestamp) { if ($this->createTime && !isset($data[$this->createTime])) { $data[$this->createTime] = $this->autoWriteTimestamp(); - $this->data[$this->createTime] = $this->getTimestampValue($data[$this->createTime]); + $this->data[$this->createTime] = $data[$this->createTime]; } if ($this->updateTime && !isset($data[$this->updateTime])) { $data[$this->updateTime] = $this->autoWriteTimestamp(); - $this->data[$this->updateTime] = $this->getTimestampValue($data[$this->updateTime]); + $this->data[$this->updateTime] = $data[$this->updateTime]; } } -- Gitee From 3c0575b7d3dd645c8f51ff81203726a8495cb7c4 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 5 Aug 2021 21:28:41 +0800 Subject: [PATCH 273/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3transactionXa?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 59 +++++++++++++++++++++-- src/db/concern/Transaction.php | 85 ++++++++++++++++++---------------- src/db/connector/Mysql.php | 8 ++-- 3 files changed, 104 insertions(+), 48 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 60679af..ec97c7e 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1755,13 +1755,64 @@ abstract class PDOConnection extends Connection return $this->connect($dbConfig, $r, $r == $m ? false : $dbMaster); } + /** + * 执行数据库Xa事务 + * @access public + * @param callable $callback 数据操作方法回调 + * @param array $dbs 多个查询对象或者连接对象 + * @return mixed + * @throws PDOException + * @throws \Exception + * @throws \Throwable + */ + public function transactionXa(callable $callback, array $dbs = []) + { + $xid = uniqid('xa'); + + if (empty($dbs)) { + $dbs[] = $this; + } + + foreach ($dbs as $key => $db) { + if ($db instanceof BaseQuery) { + $db = $db->getConnection(); + + $dbs[$key] = $db; + } + + $db->startTransXa($xid); + } + + try { + $result = null; + if (is_callable($callback)) { + $result = $callback($this); + } + + foreach ($dbs as $db) { + $db->prepareXa($xid); + } + + foreach ($dbs as $db) { + $db->commitXa($xid); + } + + return $result; + } catch (\Exception | \Throwable $e) { + foreach ($dbs as $db) { + $db->rollbackXa($xid); + } + throw $e; + } + } + /** * 启动XA事务 * @access public * @param string $xid XA事务id * @return void */ - public function startTransXa(string $xid) + public function startTransXa(string $xid): void {} /** @@ -1770,7 +1821,7 @@ abstract class PDOConnection extends Connection * @param string $xid XA事务id * @return void */ - public function prepareXa(string $xid) + public function prepareXa(string $xid): void {} /** @@ -1779,7 +1830,7 @@ abstract class PDOConnection extends Connection * @param string $xid XA事务id * @return void */ - public function commitXa(string $xid) + public function commitXa(string $xid): void {} /** @@ -1788,6 +1839,6 @@ abstract class PDOConnection extends Connection * @param string $xid XA事务id * @return void */ - public function rollbackXa(string $xid) + public function rollbackXa(string $xid): void {} } diff --git a/src/db/concern/Transaction.php b/src/db/concern/Transaction.php index f804ae2..b586132 100644 --- a/src/db/concern/Transaction.php +++ b/src/db/concern/Transaction.php @@ -12,8 +12,6 @@ declare (strict_types = 1); namespace think\db\concern; -use think\db\BaseQuery; - /** * 事务支持 */ @@ -30,45 +28,9 @@ trait Transaction * @throws \Exception * @throws \Throwable */ - public function transactionXa($callback, array $dbs = []) + public function transactionXa(callable $callback, array $dbs = []) { - $xid = uniqid('xa'); - - if (empty($dbs)) { - $dbs[] = $this->getConnection(); - } - - foreach ($dbs as $key => $db) { - if ($db instanceof BaseQuery) { - $db = $db->getConnection(); - - $dbs[$key] = $db; - } - - $db->startTransXa($xid); - } - - try { - $result = null; - if (is_callable($callback)) { - $result = call_user_func_array($callback, [$this]); - } - - foreach ($dbs as $db) { - $db->prepareXa($xid); - } - - foreach ($dbs as $db) { - $db->commitXa($xid); - } - - return $result; - } catch (\Exception | \Throwable $e) { - foreach ($dbs as $db) { - $db->rollbackXa($xid); - } - throw $e; - } + return $this->connection->transactionXa($callback, $dbs); } /** @@ -114,4 +76,47 @@ trait Transaction $this->connection->rollback(); } + /** + * 启动XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function startTransXa(string $xid): void + { + $this->connection->startTransXa($xid); + } + + /** + * 预编译XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function prepareXa(string $xid): void + { + $this->connection->prepareXa($xid); + } + + /** + * 提交XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function commitXa(string $xid): void + { + $this->connection->commitXa($xid); + } + + /** + * 回滚XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function rollbackXa(string $xid): void + { + $this->connection->rollbackXa($xid); + } } diff --git a/src/db/connector/Mysql.php b/src/db/connector/Mysql.php index 483b447..fd9e63a 100644 --- a/src/db/connector/Mysql.php +++ b/src/db/connector/Mysql.php @@ -117,7 +117,7 @@ class Mysql extends PDOConnection * @param string $xid XA事务id * @return void */ - public function startTransXa(string $xid) + public function startTransXa(string $xid): void { $this->initConnect(true); $this->linkID->exec("XA START '$xid'"); @@ -129,7 +129,7 @@ class Mysql extends PDOConnection * @param string $xid XA事务id * @return void */ - public function prepareXa(string $xid) + public function prepareXa(string $xid): void { $this->initConnect(true); $this->linkID->exec("XA END '$xid'"); @@ -142,7 +142,7 @@ class Mysql extends PDOConnection * @param string $xid XA事务id * @return void */ - public function commitXa(string $xid) + public function commitXa(string $xid): void { $this->initConnect(true); $this->linkID->exec("XA COMMIT '$xid'"); @@ -154,7 +154,7 @@ class Mysql extends PDOConnection * @param string $xid XA事务id * @return void */ - public function rollbackXa(string $xid) + public function rollbackXa(string $xid): void { $this->initConnect(true); $this->linkID->exec("XA ROLLBACK '$xid'"); -- Gitee From 9918723c4cb9e8f1b6279517487481ce17898c43 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 5 Aug 2021 21:31:20 +0800 Subject: [PATCH 274/365] =?UTF-8?q?=E5=88=A0=E9=99=A4Relation=E7=B1=BBupda?= =?UTF-8?q?te=E5=92=8Cdelete=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Relation.php | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/model/Relation.php b/src/model/Relation.php index 1779896..97b6c59 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -194,30 +194,6 @@ abstract class Relation } } - /** - * 更新数据 - * @access public - * @param array $data 更新数据 - * @return integer - */ - public function update(array $data = []): int - { - return $this->query->update($data); - } - - /** - * 删除记录 - * @access public - * @param mixed $data 表达式 true 表示强制删除 - * @return int - * @throws Exception - * @throws PDOException - */ - public function delete($data = null): int - { - return $this->query->delete($data); - } - /** * 限制关联数据的数量 * @access public -- Gitee From ce143de28b07c5330c9dd60b92eea252d1944a53 Mon Sep 17 00:00:00 2001 From: yaobiao131 <1315508912@qq.com> Date: Wed, 4 Aug 2021 15:45:15 +0800 Subject: [PATCH 275/365] =?UTF-8?q?=E4=BF=AE=E5=A4=8DhasWhere=20closure?= =?UTF-8?q?=E4=B8=BA=E7=A9=BA=E6=97=B6foreach=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/WhereQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/concern/WhereQuery.php b/src/db/concern/WhereQuery.php index 1311628..d2deb03 100644 --- a/src/db/concern/WhereQuery.php +++ b/src/db/concern/WhereQuery.php @@ -49,7 +49,7 @@ trait WhereQuery */ protected function parseQueryWhere(BaseQuery $query): void { - $this->options['where'] = $query->getOptions('where'); + $this->options['where'] = $query->getOptions('where') ?? []; if ($query->getOptions('via')) { $via = $query->getOptions('via'); -- Gitee From b90511ccf39a3a147e7b297091160363673a78b4 Mon Sep 17 00:00:00 2001 From: laterz <916612538@qq.com> Date: Fri, 6 Aug 2021 15:22:39 +0800 Subject: [PATCH 276/365] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dpaginate=20=E8=BF=94?= =?UTF-8?q?=E5=9B=9E[]=20=E8=80=8C=E4=B8=8D=E6=98=AF=E7=A9=BA=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E9=9B=86=EF=BC=8C=E8=80=8C=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?hidden=E7=AD=89=E6=96=B9=E6=B3=95=E6=97=A0=E6=B3=95=E8=B0=83?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 如:class 'think\Collection' does not have a method 'hidden' --- src/db/BaseQuery.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 986b93b..ecfc9d4 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -621,7 +621,15 @@ abstract class BaseQuery $bind = $this->bind; $total = $this->count(); - $results = $total > 0 ? $this->options($options)->bind($bind)->page($page, $listRows)->select() : []; + if ($total > 0) { + $results = $this->options($options)->bind($bind)->page($page, $listRows)->select(); + } else { + if (!empty($this->model)) { + $results = new \think\model\Collection([]); + } else { + $results = new \think\Collection([]); + } + } } elseif ($simple) { $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select(); $total = null; -- Gitee From 924e00ecf3cf283672e13ea27e2ba47e7b539a94 Mon Sep 17 00:00:00 2001 From: aspirantzhang Date: Sat, 21 Aug 2021 16:59:59 +0800 Subject: [PATCH 277/365] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dphp8=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E6=8F=90=E4=BA=A4=E4=BA=8B=E5=8A=A1=E6=97=B6=E7=9A=84?= =?UTF-8?q?=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index ec97c7e..1a3d4a2 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1442,7 +1442,7 @@ abstract class PDOConnection extends Connection if (1 == $this->transTimes) { $this->linkID->beginTransaction(); - } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { + } elseif ($this->transTimes > 1 && $this->supportSavepoint() && $this->linkID->inTransaction()) { $this->linkID->exec( $this->parseSavepoint('trans' . $this->transTimes) ); @@ -1473,7 +1473,7 @@ abstract class PDOConnection extends Connection { $this->initConnect(true); - if (1 == $this->transTimes) { + if (1 == $this->transTimes && $this->linkID->inTransaction()) { $this->linkID->commit(); } @@ -1490,12 +1490,14 @@ abstract class PDOConnection extends Connection { $this->initConnect(true); - if (1 == $this->transTimes) { - $this->linkID->rollBack(); - } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { - $this->linkID->exec( - $this->parseSavepointRollBack('trans' . $this->transTimes) - ); + if ($this->linkID->inTransaction()) { + if (1 == $this->transTimes) { + $this->linkID->rollBack(); + } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { + $this->linkID->exec( + $this->parseSavepointRollBack('trans' . $this->transTimes) + ); + } } $this->transTimes = max(0, $this->transTimes - 1); -- Gitee From 04911389100773a0dce47ff8d6febfee34907255 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 26 Aug 2021 20:46:13 +0800 Subject: [PATCH 278/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3hasWhere=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsTo.php | 3 ++- src/model/relation/HasMany.php | 3 ++- src/model/relation/HasManyThrough.php | 3 ++- src/model/relation/HasOne.php | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 789c944..d5c0c11 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -182,7 +182,8 @@ class BelongsTo extends OneToOne $fields = $this->getRelationQueryFields($fields, $model); $softDelete = $this->query->getOptions('soft_delete'); - $query = $query ?: $this->parent->db()->alias($model); + $query = $query ?: $this->parent->db(); + $query->alias($model); return $query->field($fields) ->join([$table => $relation], $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $joinType ?: $this->joinType) diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index 49f4c93..0c7d59f 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -340,7 +340,8 @@ class HasMany extends Relation $fields = $this->getRelationQueryFields($fields, $model); $softDelete = $this->query->getOptions('soft_delete'); - $query = $query ?: $this->parent->db()->alias($model); + $query = $query ?: $this->parent->db(); + $query->alias($model); return $query->group($model . '.' . $this->localKey) ->field($fields) diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index 4de5060..40117b3 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -151,7 +151,8 @@ class HasManyThrough extends Relation $fields = $this->getRelationQueryFields($fields, $model); $softDelete = $this->query->getOptions('soft_delete'); - $query = $query ?: $this->parent->db()->alias($model); + $query = $query ?: $this->parent->db(); + $query->alias($model); return $query->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey) ->join($modelTable, $modelTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk, $joinType) diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 7fcd20a..a6780a8 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -181,7 +181,8 @@ class HasOne extends OneToOne $fields = $this->getRelationQueryFields($fields, $model); $softDelete = $this->query->getOptions('soft_delete'); - $query = $query ? $query->alias($model) : $this->parent->db()->alias($model); + $query = $query ?: $this->parent->db(); + $query->alias($model); return $query->field($fields) ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType ?: $this->joinType) -- Gitee From 1cc7f2d130d406125580654ec44d0189b19a57ff Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Thu, 26 Aug 2021 21:19:56 +0800 Subject: [PATCH 279/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsTo.php | 4 ++-- src/model/relation/HasMany.php | 4 ++-- src/model/relation/HasManyThrough.php | 4 ++-- src/model/relation/HasOne.php | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index d5c0c11..7a590af 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -183,9 +183,9 @@ class BelongsTo extends OneToOne $fields = $this->getRelationQueryFields($fields, $model); $softDelete = $this->query->getOptions('soft_delete'); $query = $query ?: $this->parent->db(); - $query->alias($model); - return $query->field($fields) + return $query->alias($model) + ->field($fields) ->join([$table => $relation], $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $joinType ?: $this->joinType) ->when($softDelete, function ($query) use ($softDelete, $relation) { $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index 0c7d59f..77d9e4d 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -341,9 +341,9 @@ class HasMany extends Relation $fields = $this->getRelationQueryFields($fields, $model); $softDelete = $this->query->getOptions('soft_delete'); $query = $query ?: $this->parent->db(); - $query->alias($model); - return $query->group($model . '.' . $this->localKey) + return $query->alias($model) + ->group($model . '.' . $this->localKey) ->field($fields) ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType) ->when($softDelete, function ($query) use ($softDelete, $relation) { diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index 40117b3..d4b7d91 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -152,9 +152,9 @@ class HasManyThrough extends Relation $fields = $this->getRelationQueryFields($fields, $model); $softDelete = $this->query->getOptions('soft_delete'); $query = $query ?: $this->parent->db(); - $query->alias($model); - return $query->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey) + return $query->alias($model) + ->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey) ->join($modelTable, $modelTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk, $joinType) ->when($softDelete, function ($query) use ($softDelete, $modelTable) { $query->where($modelTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index a6780a8..a37eca4 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -182,9 +182,9 @@ class HasOne extends OneToOne $fields = $this->getRelationQueryFields($fields, $model); $softDelete = $this->query->getOptions('soft_delete'); $query = $query ?: $this->parent->db(); - $query->alias($model); - return $query->field($fields) + return $query->alias($model) + ->field($fields) ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType ?: $this->joinType) ->when($softDelete, function ($query) use ($softDelete, $relation) { $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null); -- Gitee From 6bd50005002384d5393f251243228cb42adbe096 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Mon, 6 Sep 2021 22:15:56 +0800 Subject: [PATCH 280/365] =?UTF-8?q?value=E5=92=8Ccolumn=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E6=94=AF=E6=8C=81json=E5=AD=97=E6=AE=B5=E5=92=8C=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E8=8E=B7=E5=8F=96=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 19 +++++++++++++------ src/db/concern/ResultOperation.php | 7 +++++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index ecfc9d4..4bd4bfb 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -260,19 +260,26 @@ abstract class BaseQuery */ public function value(string $field, $default = null) { - return $this->connection->value($this, $field, $default); + $result = $this->connection->value($this, $field, $default); + + $array[$field] = $result; + $this->result($array); + + return $array[$field]; } /** * 得到某个列的数组 * @access public * @param string|array $field 字段名 多个字段用逗号分隔 - * @param string $key 索引 + * @param string $key 索引 * @return array */ public function column($field, string $key = ''): array { - return $this->connection->column($this, $field, $key); + $result = $this->connection->column($this, $field, $key); + $this->resultSet($result, false); + return $result; } /** @@ -619,10 +626,10 @@ abstract class BaseQuery unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']); - $bind = $this->bind; - $total = $this->count(); + $bind = $this->bind; + $total = $this->count(); if ($total > 0) { - $results = $this->options($options)->bind($bind)->page($page, $listRows)->select(); + $results = $this->options($options)->bind($bind)->page($page, $listRows)->select(); } else { if (!empty($this->model)) { $results = new \think\model\Collection([]); diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index de01093..77409d1 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -73,9 +73,10 @@ trait ResultOperation * 处理数据集 * @access public * @param array $resultSet 数据集 + * @param bool $toCollection 是否转为对象 * @return void */ - protected function resultSet(array &$resultSet): void + protected function resultSet(array &$resultSet, bool $toCollection = true): void { if (!empty($this->options['json'])) { foreach ($resultSet as &$result) { @@ -96,7 +97,9 @@ trait ResultOperation } // 返回Collection对象 - $resultSet = new Collection($resultSet); + if ($toCollection) { + $resultSet = new Collection($resultSet); + } } /** -- Gitee From 40f384806ba6a2a74bb3342fd724ec71afc1d09e Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Tue, 14 Sep 2021 20:14:32 +0800 Subject: [PATCH 281/365] =?UTF-8?q?=E5=85=B3=E8=81=94=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0withDefault=E6=96=B9=E6=B3=95=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E5=85=B3=E8=81=94=E6=95=B0=E6=8D=AE=E4=B8=8D=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E6=97=B6=E5=80=99=E7=9A=84=E9=BB=98=E8=AE=A4=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Relation.php | 38 ++++++++++++++++++++++++++++ src/model/relation/BelongsTo.php | 6 +++-- src/model/relation/HasOne.php | 6 +++-- src/model/relation/HasOneThrough.php | 6 +++-- src/model/relation/MorphOne.php | 6 +++-- 5 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/model/Relation.php b/src/model/Relation.php index 97b6c59..358842d 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -85,6 +85,12 @@ abstract class Relation */ protected $withoutField; + /** + * 默认数据 + * @var mixed + */ + protected $default; + /** * 获取关联的所属模型 * @access public @@ -234,6 +240,38 @@ abstract class Relation return $this; } + /** + * 设置关联数据不存在的时候默认值 + * @access public + * @param mixed $data 默认值 + * @return $this + */ + public function withDefault($data = null) + { + $this->default = $data; + return $this; + } + + /** + * 获取关联数据默认值 + * @access protected + * @return mixed + */ + protected function getDefaultModel() + { + if (is_array($this->default)) { + $model = (new $this->model)->data($this->default); + } elseif ($this->default instanceof Closure) { + $closure = $this->default; + $model = new $this->model; + $closure($model); + } else { + $model = $this->default; + } + + return $model; + } + /** * 判断闭包的参数类型 * @access protected diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 7a590af..941e1d4 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -73,6 +73,8 @@ class BelongsTo extends OneToOne } $relationModel->setParent(clone $this->parent); + } else { + $relationModel = $this->getDefaultModel(); } return $relationModel; @@ -227,7 +229,7 @@ class BelongsTo extends OneToOne foreach ($resultSet as $result) { // 关联模型 if (!isset($data[$result->$foreignKey])) { - $relationModel = null; + $relationModel = $this->getDefaultModel(); } else { $relationModel = $data[$result->$foreignKey]; $relationModel->setParent(clone $result); @@ -268,7 +270,7 @@ class BelongsTo extends OneToOne // 关联模型 if (!isset($data[$result->$foreignKey])) { - $relationModel = null; + $relationModel = $this->getDefaultModel(); } else { $relationModel = $data[$result->$foreignKey]; $relationModel->setParent(clone $result); diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index a37eca4..be4927b 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -72,6 +72,8 @@ class HasOne extends OneToOne } $relationModel->setParent(clone $this->parent); + } else { + $relationModel = $this->getDefaultModel(); } return $relationModel; @@ -226,7 +228,7 @@ class HasOne extends OneToOne foreach ($resultSet as $result) { // 关联模型 if (!isset($data[$result->$localKey])) { - $relationModel = null; + $relationModel = $this->getDefaultModel(); } else { $relationModel = $data[$result->$localKey]; $relationModel->setParent(clone $result); @@ -267,7 +269,7 @@ class HasOne extends OneToOne // 关联模型 if (!isset($data[$result->$localKey])) { - $relationModel = null; + $relationModel = $this->getDefaultModel(); } else { $relationModel = $data[$result->$localKey]; $relationModel->setParent(clone $result); diff --git a/src/model/relation/HasOneThrough.php b/src/model/relation/HasOneThrough.php index 8ec42df..0278533 100644 --- a/src/model/relation/HasOneThrough.php +++ b/src/model/relation/HasOneThrough.php @@ -40,6 +40,8 @@ class HasOneThrough extends HasManyThrough if ($relationModel) { $relationModel->setParent(clone $this->parent); + } else { + $relationModel = $this->getDefaultModel(); } return $relationModel; @@ -79,7 +81,7 @@ class HasOneThrough extends HasManyThrough foreach ($resultSet as $result) { // 关联模型 if (!isset($data[$result->$localKey])) { - $relationModel = null; + $relationModel = $this->getDefaultModel(); } else { $relationModel = $data[$result->$localKey]; $relationModel->setParent(clone $result); @@ -115,7 +117,7 @@ class HasOneThrough extends HasManyThrough // 关联模型 if (!isset($data[$result->$localKey])) { - $relationModel = null; + $relationModel = $this->getDefaultModel(); } else { $relationModel = $data[$result->$localKey]; $relationModel->setParent(clone $result); diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php index bc89c0b..788dd29 100644 --- a/src/model/relation/MorphOne.php +++ b/src/model/relation/MorphOne.php @@ -90,6 +90,8 @@ class MorphOne extends Relation } $relationModel->setParent(clone $this->parent); + } else { + $relationModel = $this->getDefaultModel(); } return $relationModel; @@ -158,7 +160,7 @@ class MorphOne extends Relation // 关联数据封装 foreach ($resultSet as $result) { if (!isset($data[$result->$pk])) { - $relationModel = null; + $relationModel = $this->getDefaultModel(); } else { $relationModel = $data[$result->$pk]; $relationModel->setParent(clone $result); @@ -202,7 +204,7 @@ class MorphOne extends Relation $relationModel->setParent(clone $result); $relationModel->exists(true); } else { - $relationModel = null; + $relationModel = $this->getDefaultModel(); } if (!empty($this->bindAttr)) { -- Gitee From 3dcf9af447b048103093840833e8c74ab849152f Mon Sep 17 00:00:00 2001 From: Nzzz964 <2582787306@qq.com> Date: Tue, 12 Oct 2021 15:18:10 +0800 Subject: [PATCH 282/365] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20whereNotBetweenTim?= =?UTF-8?q?e=20=E9=80=BB=E8=BE=91=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/TimeFieldQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/concern/TimeFieldQuery.php b/src/db/concern/TimeFieldQuery.php index 1267e54..69b7eae 100644 --- a/src/db/concern/TimeFieldQuery.php +++ b/src/db/concern/TimeFieldQuery.php @@ -182,7 +182,7 @@ trait TimeFieldQuery public function whereNotBetweenTime(string $field, $startTime, $endTime) { return $this->whereTime($field, '<', $startTime) - ->whereTime($field, '>', $endTime); + ->whereTime($field, '>', $endTime, 'OR'); } /** -- Gitee From 5425f2ca05e02c5ba15db77098ab9433bd2963bf Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Thu, 9 Dec 2021 22:21:28 +0800 Subject: [PATCH 283/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3column=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 4bd4bfb..7ff93d6 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -278,7 +278,9 @@ abstract class BaseQuery public function column($field, string $key = ''): array { $result = $this->connection->column($this, $field, $key); - $this->resultSet($result, false); + if (count($result) != count($result, 1)) { + $this->resultSet($result, false); + } return $result; } -- Gitee From c422dbaa80671f120c0fef2acfe28ecb7dd1b794 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Wed, 15 Dec 2021 12:14:40 +0800 Subject: [PATCH 284/365] =?UTF-8?q?=E5=85=BC=E5=AE=B98.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 4 ++++ src/Paginator.php | 10 ++++++++-- src/model/concern/Conversion.php | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Model.php b/src/Model.php index 0345358..2d2f86f 100644 --- a/src/Model.php +++ b/src/Model.php @@ -970,21 +970,25 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } // ArrayAccess + #[\ReturnTypeWillChange] public function offsetSet($name, $value) { $this->setAttr($name, $value); } + #[\ReturnTypeWillChange] public function offsetExists($name): bool { return $this->__isset($name); } + #[\ReturnTypeWillChange] public function offsetUnset($name) { $this->__unset($name); } + #[\ReturnTypeWillChange] public function offsetGet($name) { return $this->getAttr($name); diff --git a/src/Paginator.php b/src/Paginator.php index d8d43d7..2f755ef 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -410,7 +410,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J * @return Traversable An instance of an object implementing Iterator or * Traversable */ - public function getIterator() + #[\ReturnTypeWillChange] + public function getIterator(): Traversable { return new ArrayIterator($this->items->all()); } @@ -421,7 +422,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J * @param mixed $offset * @return bool */ - public function offsetExists($offset) + #[\ReturnTypeWillChange] + public function offsetExists($offset): bool { return $this->items->offsetExists($offset); } @@ -432,6 +434,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J * @param mixed $offset * @return mixed */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { return $this->items->offsetGet($offset); @@ -443,6 +446,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J * @param mixed $offset * @param mixed $value */ + #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { $this->items->offsetSet($offset, $value); @@ -455,6 +459,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J * @return void * @since 5.0.0 */ + #[\ReturnTypeWillChange] public function offsetUnset($offset) { $this->items->offsetUnset($offset); @@ -498,6 +503,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Specify data which should be serialized to JSON */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->toArray(); diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index 35d96d0..22d6256 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -330,6 +330,7 @@ trait Conversion } // JsonSerializable + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->toArray(); -- Gitee From 3af9219b110bdd66391b5c25b4ad141b989274bd Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Mon, 20 Dec 2021 21:58:42 +0800 Subject: [PATCH 285/365] =?UTF-8?q?Db=E5=92=8C=E6=A8=A1=E5=9E=8B=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0filter=E6=95=B0=E6=8D=AE=E5=A4=84=E7=90=86=E6=9C=BA?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 56 +++++++++++- src/db/BaseQuery.php | 5 +- src/db/concern/ModelRelationQuery.php | 126 +++++++++++--------------- src/db/concern/ResultOperation.php | 67 +++++--------- src/model/concern/Attribute.php | 24 +++++ src/model/relation/BelongsToMany.php | 90 ++++++------------ 6 files changed, 186 insertions(+), 182 deletions(-) diff --git a/src/Model.php b/src/Model.php index 2d2f86f..89d3b40 100644 --- a/src/Model.php +++ b/src/Model.php @@ -243,6 +243,50 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } } + $this->filter(function ($result, $options) { + // JSON 数据处理 + if (!empty($options['json'])) { + $this->jsonResult($result, $options['json'], $options['json_assoc'], $options['with_relation_attr']); + } + + // 动态获取器 + if (!empty($options['with_attr'])) { + $result->withAttribute($options['with_attr']); + } + + // 输出属性控制 + if (!empty($options['visible'])) { + $result->visible($options['visible']); + } elseif (!empty($options['hidden'])) { + $result->hidden($options['hidden']); + } + + if (!empty($options['append'])) { + $result->append($options['append']); + } + + // 关联查询 + if (!empty($options['relation'])) { + $result->relationQuery($options['relation'], $options['with_relation_attr']); + } + + // 预载入查询 + if (!empty($options['with'])) { + $result->eagerlyResult($result, $options['with'], $options['with_relation_attr'], false, $options['with_cache'] ?? false); + } + + // JOIN预载入查询 + if (!empty($options['with_join'])) { + $result->eagerlyResult($result, $options['with_join'], $options['with_relation_attr'], true, $options['with_cache'] ?? false); + } + + // 关联统计 + if (!empty($options['with_count'])) { + foreach ($options['with_count'] as $val) { + $result->relationCount($this, (array) $val[0], $val[1], $val[2], false); + } + } + }); // 执行初始化操作 $this->initialize(); } @@ -260,11 +304,12 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab /** * 创建新的模型实例 * @access public - * @param array $data 数据 - * @param mixed $where 更新条件 + * @param array $data 数据 + * @param mixed $where 更新条件 + * @param array $options 参数 * @return Model */ - public function newInstance(array $data = [], $where = null): Model + public function newInstance(array $data = [], $where = null, array $options = []): Model { $model = new static($data); @@ -284,6 +329,11 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $model->setUpdateWhere($where); + // 查询数据处理 + foreach ($this->filter as $filter) { + call_user_func_array($filter, [$model, $options]); + } + $model->trigger('AfterRead'); return $model; diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 7ff93d6..c2fee3a 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -869,7 +869,10 @@ abstract class BaseQuery { $this->options['json'] = $json; $this->options['json_assoc'] = $assoc; - return $this; + + return $this->filter(function (&$result) use ($json) { + $this->jsonResult($result, $json, true); + }, 'json'); } /** diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index ffb72de..e0d0631 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -60,7 +60,10 @@ trait ModelRelationQuery public function hidden(array $hidden) { $this->options['hidden'] = $hidden; - return $this; + + return $this->filter(function (&$result) { + $result = $this->dataVisibleFilter($result); + }, 'visible'); } /** @@ -72,7 +75,26 @@ trait ModelRelationQuery public function visible(array $visible) { $this->options['visible'] = $visible; - return $this; + + return $this->filter(function (&$result) { + $result = $this->dataVisibleFilter($result); + }, 'visible'); + } + + protected function dataVisibleFilter($result) + { + if (!empty($this->options['visible'])) { + foreach ($this->options['visible'] as $key) { + $array[] = $key; + } + $result = array_intersect_key($result, array_flip($array)); + } elseif (!empty($this->options['hidden'])) { + foreach ($this->options['hidden'] as $key) { + $array[] = $key; + } + $result = array_diff_key($result, array_flip($array)); + } + return $result; } /** @@ -187,7 +209,9 @@ trait ModelRelationQuery $this->options['with_attr'][$name] = $callback; } - return $this; + return $this->filter(function (&$result) { + $this->getResultAttr($result, $this->options['with_attr']); + }, 'with_attr'); } /** @@ -418,20 +442,7 @@ trait ModelRelationQuery return $this->model->toCollection(); } - // 检查动态获取器 - if (!empty($this->options['with_attr'])) { - foreach ($this->options['with_attr'] as $name => $val) { - if (strpos($name, '.')) { - [$relation, $field] = explode('.', $name); - - $withRelationAttr[$relation][$field] = $val; - unset($this->options['with_attr'][$name]); - } - } - } - - $withRelationAttr = $withRelationAttr ?? []; - + $withRelationAttr = $this->getWithRelationAttr(); foreach ($resultSet as $key => &$result) { // 数据转换为模型对象 $this->resultToModel($result, $this->options, true, $withRelationAttr); @@ -452,73 +463,46 @@ trait ModelRelationQuery } /** - * 查询数据转换为模型对象 + * 检查动态获取器 * @access protected - * @param array $result 查询数据 - * @param array $options 查询参数 - * @param bool $resultSet 是否为数据集查询 - * @param array $withRelationAttr 关联字段获取器 - * @return void + * @return array */ - protected function resultToModel(array &$result, array $options = [], bool $resultSet = false, array $withRelationAttr = []): void + protected function getWithRelationAttr(): array { - // 动态获取器 - if (!empty($options['with_attr']) && empty($withRelationAttr)) { - foreach ($options['with_attr'] as $name => $val) { + if (isset($this->options['with_relation_attr'])) { + return $this->options['with_relation_attr']; + } + + $withRelationAttr = []; + if (!empty($this->options['with_attr'])) { + foreach ($this->options['with_attr'] as $name => $val) { if (strpos($name, '.')) { [$relation, $field] = explode('.', $name); $withRelationAttr[$relation][$field] = $val; - unset($options['with_attr'][$name]); + unset($this->options['with_attr'][$name]); } } } - // JSON 数据处理 - if (!empty($options['json'])) { - $this->jsonResult($result, $options['json'], $options['json_assoc'], $withRelationAttr); - } - - $result = $this->model - ->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options)); - - // 动态获取器 - if (!empty($options['with_attr'])) { - $result->withAttribute($options['with_attr']); - } - - // 输出属性控制 - if (!empty($options['visible'])) { - $result->visible($options['visible']); - } elseif (!empty($options['hidden'])) { - $result->hidden($options['hidden']); - } - - if (!empty($options['append'])) { - $result->append($options['append']); - } - - // 关联查询 - if (!empty($options['relation'])) { - $result->relationQuery($options['relation'], $withRelationAttr); - } - - // 预载入查询 - if (!$resultSet && !empty($options['with'])) { - $result->eagerlyResult($result, $options['with'], $withRelationAttr, false, $options['with_cache'] ?? false); - } + $this->options['with_relation_attr'] = $withRelationAttr; + return $withRelationAttr; + } - // JOIN预载入查询 - if (!$resultSet && !empty($options['with_join'])) { - $result->eagerlyResult($result, $options['with_join'], $withRelationAttr, true, $options['with_cache'] ?? false); - } + /** + * 查询数据转换为模型对象 + * @access protected + * @param array $result 查询数据 + * @param array $options 查询参数 + * @param bool $resultSet 是否为数据集查询 + * @param array $withRelationAttr 关联字段获取器 + * @return void + */ + protected function resultToModel(array &$result, array $options = [], bool $resultSet = false, array $withRelationAttr = []): void + { + $options['with_relation_attr'] = $this->getWithRelationAttr(); - // 关联统计 - if (!empty($options['with_count'])) { - foreach ($options['with_count'] as $val) { - $result->relationCount($this, (array) $val[0], $val[1], $val[2], false); - } - } + $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options), $options); } } diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index 77409d1..45dcf33 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -26,6 +26,23 @@ use think\Model; */ trait ResultOperation { + /** + * 设置数据处理 + * @access public + * @param callable $filter 数据处理Callable + * @param string $index 索引(唯一) + * @return $this + */ + public function filter(callable $filter, string $index = null) + { + if ($index) { + $this->options['filter'][$index] = $filter; + } else { + $this->options['filter'][] = $filter; + } + return $this; + } + /** * 是否允许返回空数据(或空模型) * @access public @@ -58,15 +75,9 @@ trait ResultOperation */ protected function result(array &$result): void { - if (!empty($this->options['json'])) { - $this->jsonResult($result, $this->options['json'], true); - } - - if (!empty($this->options['with_attr'])) { - $this->getResultAttr($result, $this->options['with_attr']); + foreach ($this->options['filter'] as $filter) { + call_user_func($filter, $result); } - - $this->filterResult($result); } /** @@ -78,22 +89,8 @@ trait ResultOperation */ protected function resultSet(array &$resultSet, bool $toCollection = true): void { - if (!empty($this->options['json'])) { - foreach ($resultSet as &$result) { - $this->jsonResult($result, $this->options['json'], true); - } - } - - if (!empty($this->options['with_attr'])) { - foreach ($resultSet as &$result) { - $this->getResultAttr($result, $this->options['with_attr']); - } - } - - if (!empty($this->options['visible']) || !empty($this->options['hidden'])) { - foreach ($resultSet as &$result) { - $this->filterResult($result); - } + foreach ($resultSet as &$result) { + $this->result($result); } // 返回Collection对象 @@ -102,28 +99,6 @@ trait ResultOperation } } - /** - * 处理数据的可见和隐藏 - * @access protected - * @param array $result 查询数据 - * @return void - */ - protected function filterResult(&$result): void - { - $array = []; - if (!empty($this->options['visible'])) { - foreach ($this->options['visible'] as $key) { - $array[] = $key; - } - $result = array_intersect_key($result, array_flip($array)); - } elseif (!empty($this->options['hidden'])) { - foreach ($this->options['hidden'] as $key) { - $array[] = $key; - } - $result = array_diff_key($result, array_flip($array)); - } - } - /** * 使用获取器处理数据 * @access protected diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 34cb59a..272ad4d 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -106,6 +106,12 @@ trait Attribute */ private $withAttr = []; + /** + * 数据处理 + * @var array + */ + private $filter = []; + /** * 获取模型对象的主键 * @access public @@ -177,6 +183,24 @@ trait Attribute return $this; } + /** + * 设置模型数据处理 + * @access public + * @param callable $filter 数据处理Callable + * @param string $index 索引(唯一) + * @return $this + */ + public function filter(callable $filter, string $index = null) + { + if ($index) { + $this->filter[$index] = $filter; + } else { + $this->filter[] = $filter; + } + + return $this; + } + /** * 获取实际的字段名 * @access protected diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 5f177c1..55aba83 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -121,25 +121,37 @@ class BelongsToMany extends Relation } /** - * 合成中间表模型 + * 解析中间表数据 * @access protected - * @param array|Collection|Paginator $models + * @param Model $model */ - protected function hydratePivot(iterable $models) + protected function getPivotData(Model $model) { - foreach ($models as $model) { - $pivot = []; + $pivot = []; - foreach ($model->getData() as $key => $val) { - if (strpos($key, '__')) { - [$name, $attr] = explode('__', $key, 2); + foreach ($model->getData() as $key => $val) { + if (strpos($key, '__')) { + [$name, $attr] = explode('__', $key, 2); - if ('pivot' == $name) { - $pivot[$attr] = $val; - unset($model->$key); - } + if ('pivot' == $name) { + $pivot[$attr] = $val; + unset($model->$key); } } + } + + return $pivot; + } + + /** + * 合成中间表模型 + * @access protected + * @param array|Collection|Paginator $models + */ + protected function hydratePivot(iterable $models) + { + foreach ($models as $model) { + $pivot = $this->getPivotData($model); $model->setRelation($this->pivotDataName, $this->newPivot($pivot)); } @@ -167,55 +179,6 @@ class BelongsToMany extends Relation return $result; } - /** - * 重载select方法 - * @access public - * @param mixed $data - * @return Collection - */ - public function select($data = null): Collection - { - $this->baseQuery(); - $result = $this->query->select($data); - $this->hydratePivot($result); - - return $result; - } - - /** - * 重载paginate方法 - * @access public - * @param int|array $listRows - * @param int|bool $simple - * @return Paginator - */ - public function paginate($listRows = null, $simple = false): Paginator - { - $this->baseQuery(); - $result = $this->query->paginate($listRows, $simple); - $this->hydratePivot($result); - - return $result; - } - - /** - * 重载find方法 - * @access public - * @param mixed $data - * @return Model - */ - public function find($data = null) - { - $this->baseQuery(); - $result = $this->query->find($data); - - if ($result && !$result->isEmpty()) { - $this->hydratePivot([$result]); - } - - return $result; - } - /** * 根据关联条件查询当前模型 * @access public @@ -673,6 +636,11 @@ class BelongsToMany extends Relation $foreignKey = $this->foreignKey; $localKey = $this->localKey; + $this->model->filter(function ($result, $options) { + $pivot = $this->getPivotData($result); + $result->setRelation($this->pivotDataName, $this->newPivot($pivot)); + }); + // 关联查询 if (null === $this->parent->getKey()) { $condition = ['pivot.' . $localKey, 'exp', new Raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk())]; -- Gitee From 39a9390629de8a41374c5951f667185c2e3928f3 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Mon, 20 Dec 2021 22:04:15 +0800 Subject: [PATCH 286/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index c2fee3a..962d8db 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -1183,7 +1183,7 @@ abstract class BaseQuery $this->parseView($options); } - foreach (['data', 'order', 'join', 'union'] as $name) { + foreach (['data', 'order', 'join', 'union', 'filter'] as $name) { if (!isset($options[$name])) { $options[$name] = []; } -- Gitee From 92abd99342264d3c69100d7339d67491ad1917e8 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Mon, 20 Dec 2021 22:37:00 +0800 Subject: [PATCH 287/365] =?UTF-8?q?=E8=B0=83=E6=95=B4json=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 5 ----- src/db/BaseQuery.php | 4 ---- src/db/concern/ModelRelationQuery.php | 10 +++++++--- src/db/concern/ResultOperation.php | 2 ++ 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/Model.php b/src/Model.php index 89d3b40..17b030d 100644 --- a/src/Model.php +++ b/src/Model.php @@ -244,11 +244,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } $this->filter(function ($result, $options) { - // JSON 数据处理 - if (!empty($options['json'])) { - $this->jsonResult($result, $options['json'], $options['json_assoc'], $options['with_relation_attr']); - } - // 动态获取器 if (!empty($options['with_attr'])) { $result->withAttribute($options['with_attr']); diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 962d8db..88018b9 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -869,10 +869,6 @@ abstract class BaseQuery { $this->options['json'] = $json; $this->options['json_assoc'] = $assoc; - - return $this->filter(function (&$result) use ($json) { - $this->jsonResult($result, $json, true); - }, 'json'); } /** diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index e0d0631..e8f18e4 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -445,7 +445,7 @@ trait ModelRelationQuery $withRelationAttr = $this->getWithRelationAttr(); foreach ($resultSet as $key => &$result) { // 数据转换为模型对象 - $this->resultToModel($result, $this->options, true, $withRelationAttr); + $this->resultToModel($result, $this->options, true); } if (!empty($this->options['with'])) { @@ -495,13 +495,17 @@ trait ModelRelationQuery * @param array $result 查询数据 * @param array $options 查询参数 * @param bool $resultSet 是否为数据集查询 - * @param array $withRelationAttr 关联字段获取器 * @return void */ - protected function resultToModel(array &$result, array $options = [], bool $resultSet = false, array $withRelationAttr = []): void + protected function resultToModel(array &$result, array $options = [], bool $resultSet = false): void { $options['with_relation_attr'] = $this->getWithRelationAttr(); + // JSON 数据处理 + if (!empty($options['json'])) { + $this->jsonResult($result, $options['json'], $options['json_assoc'], $options['with_relation_attr']); + } + $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options), $options); } diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index 45dcf33..0629065 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -75,6 +75,8 @@ trait ResultOperation */ protected function result(array &$result): void { + $this->jsonResult($result, $this->options['json'], true); + foreach ($this->options['filter'] as $filter) { call_user_func($filter, $result); } -- Gitee From 0f3727e1bf65f932895f51804a734648bc542790 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Mon, 20 Dec 2021 22:41:57 +0800 Subject: [PATCH 288/365] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 4 ++-- src/db/concern/ResultOperation.php | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 88018b9..368028e 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -137,7 +137,7 @@ abstract class BaseQuery $query->name($this->name); } - if (isset($this->options['json'])) { + if (!empty($this->options['json'])) { $query->json($this->options['json'], $this->options['json_assoc']); } @@ -1179,7 +1179,7 @@ abstract class BaseQuery $this->parseView($options); } - foreach (['data', 'order', 'join', 'union', 'filter'] as $name) { + foreach (['data', 'order', 'join', 'union', 'filter', 'json'] as $name) { if (!isset($options[$name])) { $options[$name] = []; } diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index 0629065..bd9f0b6 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -75,7 +75,9 @@ trait ResultOperation */ protected function result(array &$result): void { - $this->jsonResult($result, $this->options['json'], true); + if (!empty($this->options['json'])) { + $this->jsonResult($result, $this->options['json'], true); + } foreach ($this->options['filter'] as $filter) { call_user_func($filter, $result); -- Gitee From e95b0de15702f19becb62ffd21d94fe2c1ffed8f Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Tue, 21 Dec 2021 13:19:30 +0800 Subject: [PATCH 289/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3sqlite=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/builder/Sqlite.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/db/builder/Sqlite.php b/src/db/builder/Sqlite.php index 40cab7f..78a307d 100644 --- a/src/db/builder/Sqlite.php +++ b/src/db/builder/Sqlite.php @@ -94,4 +94,16 @@ class Sqlite extends Builder return $key; } + + /** + * 设置锁机制 + * @access protected + * @param Query $query 查询对象 + * @param bool|string $lock + * @return string + */ + protected function parseLock(Query $query, $lock = false): string + { + return ''; + } } -- Gitee From 28e79af00bdecbac18f078c9479123af2850cb71 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Tue, 21 Dec 2021 17:12:57 +0800 Subject: [PATCH 290/365] =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E5=AD=97=E6=AE=B5=E7=B1=BB=E5=9E=8B=E4=B8=8D=E5=8C=BA?= =?UTF-8?q?=E5=88=86=E5=A4=A7=E5=B0=8F=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 1a3d4a2..69bfbbc 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -279,7 +279,7 @@ abstract class PDOConnection extends Connection */ protected function getFieldType(string $type): string { - if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) { + if (0 === stripos($type, 'set') || 0 === stripos($type, 'enum')) { $result = 'string'; } elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) { $result = 'float'; @@ -287,11 +287,11 @@ abstract class PDOConnection extends Connection $result = 'int'; } elseif (preg_match('/bool/is', $type)) { $result = 'bool'; - } elseif (0 === strpos($type, 'timestamp')) { + } elseif (0 === stripos($type, 'timestamp')) { $result = 'timestamp'; - } elseif (0 === strpos($type, 'datetime')) { + } elseif (0 === stripos($type, 'datetime')) { $result = 'datetime'; - } elseif (0 === strpos($type, 'date')) { + } elseif (0 === stripos($type, 'date')) { $result = 'date'; } else { $result = 'string'; -- Gitee From b74c97e44686a10f89344388c0ef9f743bb992d0 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Wed, 22 Dec 2021 13:40:58 +0800 Subject: [PATCH 291/365] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 2 +- src/db/concern/ResultOperation.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 368028e..bfccb8c 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -1125,7 +1125,7 @@ abstract class BaseQuery * 查找单条记录 * @access public * @param mixed $data 查询数据 - * @return array|Model|null|static + * @return array|Model|null|static|mixed * @throws Exception * @throws ModelNotFoundException * @throws DataNotFoundException diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index bd9f0b6..05e6f3b 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -149,7 +149,7 @@ trait ResultOperation * 查找单条记录 不存在返回空数据(或者空模型) * @access public * @param mixed $data 数据 - * @return array|Model|static + * @return array|Model|static|mixed */ public function findOrEmpty($data = null) { @@ -221,7 +221,7 @@ trait ResultOperation * 查找单条记录 如果不存在则抛出异常 * @access public * @param array|string|Query|Closure $data 数据 - * @return array|Model|static + * @return array|Model|static|mixed * @throws ModelNotFoundException * @throws DataNotFoundException */ -- Gitee From 06fcb0b3bfffb27ae8e4ee50a4383ddb724726be Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Wed, 22 Dec 2021 14:31:13 +0800 Subject: [PATCH 292/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3json=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 368028e..4e6a1a2 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -869,6 +869,8 @@ abstract class BaseQuery { $this->options['json'] = $json; $this->options['json_assoc'] = $assoc; + + return $this; } /** -- Gitee From 4b7e026d14eca4dec6eb7685adf0b363814a1d10 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Wed, 22 Dec 2021 14:44:57 +0800 Subject: [PATCH 293/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=A4=9A=E5=AF=B9?= =?UTF-8?q?=E5=A4=9A=E5=85=B3=E8=81=94=E4=B8=AD=E9=97=B4=E8=A1=A8=E6=95=B0?= =?UTF-8?q?=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsToMany.php | 50 ++++++++++++---------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 55aba83..491752d 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -19,7 +19,6 @@ use think\db\Raw; use think\Model; use think\model\Pivot; use think\model\Relation; -use think\Paginator; /** * 多对多关联类 @@ -123,38 +122,25 @@ class BelongsToMany extends Relation /** * 解析中间表数据 * @access protected - * @param Model $model + * @param array $result */ - protected function getPivotData(Model $model) + protected function getPivotData(array $result): array { $pivot = []; - foreach ($model->getData() as $key => $val) { + foreach ($result as $key => $val) { if (strpos($key, '__')) { [$name, $attr] = explode('__', $key, 2); if ('pivot' == $name) { $pivot[$attr] = $val; - unset($model->$key); + unset($result[$key]); } } } - return $pivot; - } - - /** - * 合成中间表模型 - * @access protected - * @param array|Collection|Paginator $models - */ - protected function hydratePivot(iterable $models) - { - foreach ($models as $model) { - $pivot = $this->getPivotData($model); - - $model->setRelation($this->pivotDataName, $this->newPivot($pivot)); - } + $result[$this->pivotDataName] = $this->newPivot($pivot); + return $result; } /** @@ -170,13 +156,9 @@ class BelongsToMany extends Relation $closure($this->getClosureType($closure)); } - $result = $this->relation($subRelation) + return $this->relation($subRelation) ->select() ->setParent(clone $this->parent); - - $this->hydratePivot($result); - - return $result; } /** @@ -636,9 +618,21 @@ class BelongsToMany extends Relation $foreignKey = $this->foreignKey; $localKey = $this->localKey; - $this->model->filter(function ($result, $options) { - $pivot = $this->getPivotData($result); - $result->setRelation($this->pivotDataName, $this->newPivot($pivot)); + $this->query->filter(function (&$result) { + $pivot = []; + + foreach ($result as $key => $val) { + if (strpos($key, '__')) { + [$name, $attr] = explode('__', $key, 2); + + if ('pivot' == $name) { + $pivot[$attr] = $val; + unset($result[$key]); + } + } + } + + $result[$this->pivotDataName] = $this->newPivot($pivot); }); // 关联查询 -- Gitee From 6ada0dd595627d786458812295c26ce5b2d89442 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Wed, 22 Dec 2021 22:49:48 +0800 Subject: [PATCH 294/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=A4=9A=E5=AF=B9?= =?UTF-8?q?=E5=A4=9A=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 30 +++++-------------------- src/db/concern/ResultOperation.php | 12 ++++++++++ src/model/relation/BelongsToMany.php | 32 ++++----------------------- 3 files changed, 22 insertions(+), 52 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index e8f18e4..87c95cc 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -60,10 +60,7 @@ trait ModelRelationQuery public function hidden(array $hidden) { $this->options['hidden'] = $hidden; - - return $this->filter(function (&$result) { - $result = $this->dataVisibleFilter($result); - }, 'visible'); + return $this; } /** @@ -75,26 +72,7 @@ trait ModelRelationQuery public function visible(array $visible) { $this->options['visible'] = $visible; - - return $this->filter(function (&$result) { - $result = $this->dataVisibleFilter($result); - }, 'visible'); - } - - protected function dataVisibleFilter($result) - { - if (!empty($this->options['visible'])) { - foreach ($this->options['visible'] as $key) { - $array[] = $key; - } - $result = array_intersect_key($result, array_flip($array)); - } elseif (!empty($this->options['hidden'])) { - foreach ($this->options['hidden'] as $key) { - $array[] = $key; - } - $result = array_diff_key($result, array_flip($array)); - } - return $result; + return $this; } /** @@ -506,6 +484,10 @@ trait ModelRelationQuery $this->jsonResult($result, $options['json'], $options['json_assoc'], $options['with_relation_attr']); } + foreach ($this->options['filter'] as $filter) { + call_user_func($filter, $result); + } + $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options), $options); } diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index 05e6f3b..247542d 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -82,6 +82,18 @@ trait ResultOperation foreach ($this->options['filter'] as $filter) { call_user_func($filter, $result); } + + if (!empty($this->options['visible'])) { + foreach ($this->options['visible'] as $key) { + $array[] = $key; + } + $result = array_intersect_key($result, array_flip($array)); + } elseif (!empty($this->options['hidden'])) { + foreach ($this->options['hidden'] as $key) { + $array[] = $key; + } + $result = array_diff_key($result, array_flip($array)); + } } /** diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 491752d..75eab1e 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -119,30 +119,6 @@ class BelongsToMany extends Relation } } - /** - * 解析中间表数据 - * @access protected - * @param array $result - */ - protected function getPivotData(array $result): array - { - $pivot = []; - - foreach ($result as $key => $val) { - if (strpos($key, '__')) { - [$name, $attr] = explode('__', $key, 2); - - if ('pivot' == $name) { - $pivot[$attr] = $val; - unset($result[$key]); - } - } - } - - $result[$this->pivotDataName] = $this->newPivot($pivot); - return $result; - } - /** * 延迟获取关联数据 * @access public @@ -618,21 +594,21 @@ class BelongsToMany extends Relation $foreignKey = $this->foreignKey; $localKey = $this->localKey; - $this->query->filter(function (&$result) { + $this->query->getModel()->filter(function ($result, $options) { $pivot = []; - foreach ($result as $key => $val) { + foreach ($result->getData() as $key => $val) { if (strpos($key, '__')) { [$name, $attr] = explode('__', $key, 2); if ('pivot' == $name) { $pivot[$attr] = $val; - unset($result[$key]); + unset($result->$key); } } } - $result[$this->pivotDataName] = $this->newPivot($pivot); + $result->setRelation($this->pivotDataName, $this->newPivot($pivot)); }); // 关联查询 -- Gitee From 5ce2242d869dc84bcebc612c616e9251121981f1 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Wed, 22 Dec 2021 23:18:29 +0800 Subject: [PATCH 295/365] =?UTF-8?q?=E8=B0=83=E6=95=B4db=E7=B1=BBfilter?= =?UTF-8?q?=E5=A4=84=E7=90=86=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 2 +- src/db/concern/ResultOperation.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 87c95cc..41ab320 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -485,7 +485,7 @@ trait ModelRelationQuery } foreach ($this->options['filter'] as $filter) { - call_user_func($filter, $result); + $result = call_user_func($filter, $result); } $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options), $options); diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index 247542d..eaccfcd 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -80,7 +80,7 @@ trait ResultOperation } foreach ($this->options['filter'] as $filter) { - call_user_func($filter, $result); + $result = call_user_func($filter, $result); } if (!empty($this->options['visible'])) { -- Gitee From 39be9523f88c250ab7e6a8b2680ba9a95ba1194e Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Thu, 23 Dec 2021 23:23:02 +0800 Subject: [PATCH 296/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bdb=E7=B1=BBhidden=20v?= =?UTF-8?q?isible=20append=20=E6=96=B9=E6=B3=95=E5=A4=84=E7=90=86=E6=9C=BA?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 11 -------- src/db/BaseQuery.php | 6 +++++ src/db/concern/ModelRelationQuery.php | 36 --------------------------- src/db/concern/ResultOperation.php | 12 --------- 4 files changed, 6 insertions(+), 59 deletions(-) diff --git a/src/Model.php b/src/Model.php index 17b030d..8f2318d 100644 --- a/src/Model.php +++ b/src/Model.php @@ -249,17 +249,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $result->withAttribute($options['with_attr']); } - // 输出属性控制 - if (!empty($options['visible'])) { - $result->visible($options['visible']); - } elseif (!empty($options['hidden'])) { - $result->hidden($options['hidden']); - } - - if (!empty($options['append'])) { - $result->append($options['append']); - } - // 关联查询 if (!empty($options['relation'])) { $result->relationQuery($options['relation'], $options['with_relation_attr']); diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index ba7620a..157e196 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -106,6 +106,12 @@ abstract class BaseQuery $name = Str::snake(substr($method, 5)); array_unshift($args, $name); return call_user_func_array([$this, 'where'], $args); + } elseif ($this->model && in_array($method, ['hidden', 'visible', 'append'])) { + // 调用模型类方法 + $this->model->filter(function ($model, $options) use ($method, $args) { + call_user_func_array([$model, $method], $args); + }); + return $this; } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { // 动态调用命名范围 $method = 'scope' . $method; diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 41ab320..645eea2 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -51,42 +51,6 @@ trait ModelRelationQuery return $this->model; } - /** - * 设置需要隐藏的输出属性 - * @access public - * @param array $hidden 需要隐藏的字段名 - * @return $this - */ - public function hidden(array $hidden) - { - $this->options['hidden'] = $hidden; - return $this; - } - - /** - * 设置需要输出的属性 - * @access public - * @param array $visible 需要输出的属性 - * @return $this - */ - public function visible(array $visible) - { - $this->options['visible'] = $visible; - return $this; - } - - /** - * 设置需要追加输出的属性 - * @access public - * @param array $append 需要追加的属性 - * @return $this - */ - public function append(array $append) - { - $this->options['append'] = $append; - return $this; - } - /** * 添加查询范围 * @access public diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index eaccfcd..d95edd6 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -82,18 +82,6 @@ trait ResultOperation foreach ($this->options['filter'] as $filter) { $result = call_user_func($filter, $result); } - - if (!empty($this->options['visible'])) { - foreach ($this->options['visible'] as $key) { - $array[] = $key; - } - $result = array_intersect_key($result, array_flip($array)); - } elseif (!empty($this->options['hidden'])) { - foreach ($this->options['hidden'] as $key) { - $array[] = $key; - } - $result = array_diff_key($result, array_flip($array)); - } } /** -- Gitee From 6a977dfe730a9738729abfa612044f3cd1822d9a Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Fri, 24 Dec 2021 23:42:41 +0800 Subject: [PATCH 297/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=85=B3=E8=81=94?= =?UTF-8?q?=E7=9A=84=E8=BD=AF=E5=88=A0=E9=99=A4=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 19 ++++++++++++++++ src/model/concern/SoftDelete.php | 31 +++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 645eea2..daac3c0 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -455,4 +455,23 @@ trait ModelRelationQuery $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options), $options); } + /** + * 查询软删除数据 + * @access public + * @return Query + */ + public function withTrashed() + { + return $this->model ? $this->model->queryWithTrashed() : $this; + } + + /** + * 只查询软删除数据 + * @access public + * @return Query + */ + public function onlyTrashed() + { + return $this->model ? $this->model->queryOnlyTrashed() : $this; + } } diff --git a/src/model/concern/SoftDelete.php b/src/model/concern/SoftDelete.php index 8d76bb0..117f1ef 100644 --- a/src/model/concern/SoftDelete.php +++ b/src/model/concern/SoftDelete.php @@ -55,6 +55,16 @@ trait SoftDelete return $model->withTrashedData(true)->db(); } + /** + * 查询软删除数据 + * @access public + * @return Query + */ + public function queryWithTrashed(): Query + { + return $this->withTrashedData(true)->db(); + } + /** * 是否包含软删除数据 * @access protected @@ -86,6 +96,23 @@ trait SoftDelete return $model->db(); } + /** + * 只查询软删除数据 + * @access public + * @return Query + */ + public function queryOnlyTrashed(): Query + { + $field = $this->getDeleteTimeField(true); + + if ($field) { + return $this->db() + ->useSoftDelete($field, $this->getWithTrashedExp()); + } + + return $this->db(); + } + /** * 获取软删除数据的查询条件 * @access protected @@ -152,12 +179,12 @@ trait SoftDelete public static function destroy($data, bool $force = false): bool { // 传入空值(包括空字符串和空数组)的时候不会做任何的数据删除操作,但传入0则是有效的 - if(empty($data) && $data !== 0){ + if (empty($data) && 0 !== $data) { return false; } // 仅当强制删除时包含软删除数据 $model = (new static()); - if($force){ + if ($force) { $model->withTrashedData(true); } $query = $model->db(false); -- Gitee From 77716982094b8108abe2b54d42f03866f083b7ec Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Tue, 28 Dec 2021 11:41:41 +0800 Subject: [PATCH 298/365] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=8E=B7=E5=8F=96sql?= =?UTF-8?q?=E6=97=B6=E7=9A=84=E5=AD=97=E7=AC=A6=E8=BD=AC=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 69bfbbc..40ac99e 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1273,7 +1273,7 @@ abstract class PDOConnection extends Connection $type = is_array($val) ? $val[1] : PDO::PARAM_STR; if (self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) { - $value = '\'' . addslashes($value) . '\''; + $value = '\'' . addcslashes($value, "'") . '\''; } elseif (PDO::PARAM_INT == $type && '' === $value) { $value = '0'; } -- Gitee From 51ec287abdb99521dbbc66526b114f2e32b8fff4 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Wed, 29 Dec 2021 13:48:27 +0800 Subject: [PATCH 299/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BwithAttr=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 9 --------- src/db/concern/ModelRelationQuery.php | 4 ++-- src/db/concern/ResultOperation.php | 6 ++++-- src/model/Collection.php | 2 +- src/model/concern/Attribute.php | 4 ++-- 5 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/Model.php b/src/Model.php index 8f2318d..cb4d913 100644 --- a/src/Model.php +++ b/src/Model.php @@ -244,11 +244,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } $this->filter(function ($result, $options) { - // 动态获取器 - if (!empty($options['with_attr'])) { - $result->withAttribute($options['with_attr']); - } - // 关联查询 if (!empty($options['relation'])) { $result->relationQuery($options['relation'], $options['with_relation_attr']); @@ -1075,10 +1070,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab return call_user_func_array(static::$macro[static::class][$method]->bindTo($this, static::class), $args); } - if ('withattr' == strtolower($method)) { - return call_user_func_array([$this, 'withAttribute'], $args); - } - return call_user_func_array([$this->db(), $method], $args); } diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index daac3c0..ffec2fb 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -151,8 +151,8 @@ trait ModelRelationQuery $this->options['with_attr'][$name] = $callback; } - return $this->filter(function (&$result) { - $this->getResultAttr($result, $this->options['with_attr']); + return $this->filter(function ($result) { + return $this->getResultAttr($result, $this->options['with_attr']); }, 'with_attr'); } diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index d95edd6..d8b5a4c 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -108,9 +108,9 @@ trait ResultOperation * @access protected * @param array $result 查询数据 * @param array $withAttr 字段获取器 - * @return void + * @return array */ - protected function getResultAttr(array &$result, array $withAttr = []): void + protected function getResultAttr(array $result, array $withAttr = []): array { foreach ($withAttr as $name => $closure) { $name = Str::snake($name); @@ -126,6 +126,8 @@ trait ResultOperation $result[$name] = $closure($result[$name] ?? null, $result); } } + + return $result; } /** diff --git a/src/model/Collection.php b/src/model/Collection.php index f017e32..c8ff385 100644 --- a/src/model/Collection.php +++ b/src/model/Collection.php @@ -157,7 +157,7 @@ class Collection extends BaseCollection public function withAttr($name, $callback = null) { $this->each(function (Model $model) use ($name, $callback) { - $model->withAttribute($name, $callback); + $model->withAttr($name, $callback); }); return $this; diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 272ad4d..a48c74e 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -657,11 +657,11 @@ trait Attribute * @param callable $callback 闭包获取器 * @return $this */ - public function withAttribute($name, callable $callback = null) + public function withAttr($name, callable $callback = null) { if (is_array($name)) { foreach ($name as $key => $val) { - $this->withAttribute($key, $val); + $this->withAttr($key, $val); } } else { $name = $this->getRealFieldName($name); -- Gitee From 326e962bd880b6b42ab4efd1c2a7346b5fd0873a Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Fri, 31 Dec 2021 14:06:24 +0800 Subject: [PATCH 300/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=85=B3=E8=81=94?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E9=9B=86=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 4 ++-- src/db/concern/ModelRelationQuery.php | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Model.php b/src/Model.php index cb4d913..ead8611 100644 --- a/src/Model.php +++ b/src/Model.php @@ -250,12 +250,12 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } // 预载入查询 - if (!empty($options['with'])) { + if (empty($optios['is_resultSet']) && !empty($options['with'])) { $result->eagerlyResult($result, $options['with'], $options['with_relation_attr'], false, $options['with_cache'] ?? false); } // JOIN预载入查询 - if (!empty($options['with_join'])) { + if (empty($optios['is_resultSet']) && !empty($options['with_join'])) { $result->eagerlyResult($result, $options['with_join'], $options['with_relation_attr'], true, $options['with_cache'] ?? false); } diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index ffec2fb..3213f75 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -442,6 +442,7 @@ trait ModelRelationQuery protected function resultToModel(array &$result, array $options = [], bool $resultSet = false): void { $options['with_relation_attr'] = $this->getWithRelationAttr(); + $options['is_resultSet'] = $resultSet; // JSON 数据处理 if (!empty($options['json'])) { -- Gitee From 8861b60f0876447b9ba8ad0ad4c6997f82767dac Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Fri, 31 Dec 2021 14:09:31 +0800 Subject: [PATCH 301/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3parseWhereExp?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/WhereQuery.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/db/concern/WhereQuery.php b/src/db/concern/WhereQuery.php index d2deb03..ef845f5 100644 --- a/src/db/concern/WhereQuery.php +++ b/src/db/concern/WhereQuery.php @@ -361,9 +361,7 @@ trait WhereQuery $field = $this->options['via'] . '.' . $field; } - if ($field instanceof Raw) { - return $this->whereRaw($field, is_array($op) ? $op : [], $logic); - } elseif ($strict) { + if ($strict) { // 使用严格模式查询 if ('=' == $op) { $where = $this->whereEq($field, $condition); -- Gitee From e69151fba9dd21f86e392a0ae208825904d6d49a Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Fri, 31 Dec 2021 14:12:13 +0800 Subject: [PATCH 302/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Model.php b/src/Model.php index ead8611..ac59192 100644 --- a/src/Model.php +++ b/src/Model.php @@ -250,12 +250,12 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } // 预载入查询 - if (empty($optios['is_resultSet']) && !empty($options['with'])) { + if (empty($options['is_resultSet']) && !empty($options['with'])) { $result->eagerlyResult($result, $options['with'], $options['with_relation_attr'], false, $options['with_cache'] ?? false); } // JOIN预载入查询 - if (empty($optios['is_resultSet']) && !empty($options['with_join'])) { + if (empty($options['is_resultSet']) && !empty($options['with_join'])) { $result->eagerlyResult($result, $options['with_join'], $options['with_relation_attr'], true, $options['with_cache'] ?? false); } -- Gitee From c6712643961a1b73f5641ecebf5e8b5596cdeb4e Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Sun, 2 Jan 2022 14:41:28 +0800 Subject: [PATCH 303/365] =?UTF-8?q?=E5=8F=96=E6=B6=88=E6=A8=A1=E5=9E=8Bfil?= =?UTF-8?q?ter=E6=96=B9=E6=B3=95=20=E7=BB=9F=E4=B8=80=E8=B0=83=E7=94=A8Db?= =?UTF-8?q?=E7=B1=BBfilter=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 58 +++++++++++++++------------ src/db/concern/ModelRelationQuery.php | 7 ++-- src/db/concern/ResultOperation.php | 3 +- src/model/concern/Attribute.php | 24 ----------- 4 files changed, 38 insertions(+), 54 deletions(-) diff --git a/src/Model.php b/src/Model.php index ac59192..f05c1e5 100644 --- a/src/Model.php +++ b/src/Model.php @@ -243,29 +243,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab } } - $this->filter(function ($result, $options) { - // 关联查询 - if (!empty($options['relation'])) { - $result->relationQuery($options['relation'], $options['with_relation_attr']); - } - - // 预载入查询 - if (empty($options['is_resultSet']) && !empty($options['with'])) { - $result->eagerlyResult($result, $options['with'], $options['with_relation_attr'], false, $options['with_cache'] ?? false); - } - - // JOIN预载入查询 - if (empty($options['is_resultSet']) && !empty($options['with_join'])) { - $result->eagerlyResult($result, $options['with_join'], $options['with_relation_attr'], true, $options['with_cache'] ?? false); - } - - // 关联统计 - if (!empty($options['with_count'])) { - foreach ($options['with_count'] as $val) { - $result->relationCount($this, (array) $val[0], $val[1], $val[2], false); - } - } - }); // 执行初始化操作 $this->initialize(); } @@ -309,15 +286,44 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $model->setUpdateWhere($where); // 查询数据处理 - foreach ($this->filter as $filter) { - call_user_func_array($filter, [$model, $options]); - } + $model->filterData($options); $model->trigger('AfterRead'); return $model; } + /** + * 处理模型数据 + * @access protected + * @param array $options 查询参数 + * @return void + */ + protected function filterData(array $options): void + { + // 关联查询 + if (!empty($options['relation'])) { + $this->relationQuery($options['relation'], $options['with_relation_attr']); + } + + // 预载入查询 + if (empty($options['is_resultSet']) && !empty($options['with'])) { + $this->eagerlyResult($result, $options['with'], $options['with_relation_attr'], false, $options['with_cache'] ?? false); + } + + // JOIN预载入查询 + if (empty($options['is_resultSet']) && !empty($options['with_join'])) { + $this->eagerlyResult($result, $options['with_join'], $options['with_relation_attr'], true, $options['with_cache'] ?? false); + } + + // 关联统计 + if (!empty($options['with_count'])) { + foreach ($options['with_count'] as $val) { + $this->relationCount($this, (array) $val[0], $val[1], $val[2], false); + } + } + } + /** * 设置模型的更新条件 * @access protected diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 3213f75..6a74e4f 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -449,11 +449,12 @@ trait ModelRelationQuery $this->jsonResult($result, $options['json'], $options['json_assoc'], $options['with_relation_attr']); } + $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options), $options); + + // 模型数据处理 foreach ($this->options['filter'] as $filter) { - $result = call_user_func($filter, $result); + call_user_func($filter, $result); } - - $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options), $options); } /** diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index d8b5a4c..a6aac3e 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -27,7 +27,7 @@ use think\Model; trait ResultOperation { /** - * 设置数据处理 + * 设置数据处理(支持模型) * @access public * @param callable $filter 数据处理Callable * @param string $index 索引(唯一) @@ -79,6 +79,7 @@ trait ResultOperation $this->jsonResult($result, $this->options['json'], true); } + // 查询数据处理 foreach ($this->options['filter'] as $filter) { $result = call_user_func($filter, $result); } diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index a48c74e..fb19fd1 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -106,12 +106,6 @@ trait Attribute */ private $withAttr = []; - /** - * 数据处理 - * @var array - */ - private $filter = []; - /** * 获取模型对象的主键 * @access public @@ -183,24 +177,6 @@ trait Attribute return $this; } - /** - * 设置模型数据处理 - * @access public - * @param callable $filter 数据处理Callable - * @param string $index 索引(唯一) - * @return $this - */ - public function filter(callable $filter, string $index = null) - { - if ($index) { - $this->filter[$index] = $filter; - } else { - $this->filter[] = $filter; - } - - return $this; - } - /** * 获取实际的字段名 * @access protected -- Gitee From c914df37a6f70aaef265a629ac889436f5b80cd8 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Mon, 3 Jan 2022 12:50:16 +0800 Subject: [PATCH 304/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Model.php | 34 ------ src/db/BaseQuery.php | 18 ++- src/db/concern/ModelRelationQuery.php | 161 ++++++++++++++++---------- src/db/concern/ResultOperation.php | 20 +--- src/model/concern/Attribute.php | 11 ++ 5 files changed, 126 insertions(+), 118 deletions(-) diff --git a/src/Model.php b/src/Model.php index f05c1e5..b3a99ea 100644 --- a/src/Model.php +++ b/src/Model.php @@ -285,45 +285,11 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab $model->setUpdateWhere($where); - // 查询数据处理 - $model->filterData($options); - $model->trigger('AfterRead'); return $model; } - /** - * 处理模型数据 - * @access protected - * @param array $options 查询参数 - * @return void - */ - protected function filterData(array $options): void - { - // 关联查询 - if (!empty($options['relation'])) { - $this->relationQuery($options['relation'], $options['with_relation_attr']); - } - - // 预载入查询 - if (empty($options['is_resultSet']) && !empty($options['with'])) { - $this->eagerlyResult($result, $options['with'], $options['with_relation_attr'], false, $options['with_cache'] ?? false); - } - - // JOIN预载入查询 - if (empty($options['is_resultSet']) && !empty($options['with_join'])) { - $this->eagerlyResult($result, $options['with_join'], $options['with_relation_attr'], true, $options['with_cache'] ?? false); - } - - // 关联统计 - if (!empty($options['with_count'])) { - foreach ($options['with_count'] as $val) { - $this->relationCount($this, (array) $val[0], $val[1], $val[2], false); - } - } - } - /** * 设置模型的更新条件 * @access protected diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 157e196..9911482 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -876,7 +876,19 @@ abstract class BaseQuery $this->options['json'] = $json; $this->options['json_assoc'] = $assoc; - return $this; + if ($this->model) { + return $this->filter(function ($result) use ($json, $assoc) { + if (!empty($json)) { + $this->jsonModelResult($result, $json, $assoc); + } + }); + } + + return $this->filter(function ($result) use ($json) { + if (!empty($json)) { + $this->jsonResult($result, $json); + } + }); } /** @@ -1187,7 +1199,7 @@ abstract class BaseQuery $this->parseView($options); } - foreach (['data', 'order', 'join', 'union', 'filter', 'json'] as $name) { + foreach (['data', 'order', 'join', 'union', 'filter', 'json', 'with_relation_attr'] as $name) { if (!isset($options[$name])) { $options[$name] = []; } @@ -1197,7 +1209,7 @@ abstract class BaseQuery $options['strict'] = $this->connection->getConfig('fields_strict'); } - foreach (['master', 'lock', 'fetch_sql', 'array', 'distinct', 'procedure'] as $name) { + foreach (['master', 'lock', 'fetch_sql', 'array', 'distinct', 'procedure', 'with_cache'] as $name) { if (!isset($options[$name])) { $options[$name] = false; } diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 6a74e4f..e21724c 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -94,11 +94,13 @@ trait ModelRelationQuery */ public function relation(array $relation) { - if (!empty($relation)) { - $this->options['relation'] = $relation; + if (empty($this->model) || empty($relation)) { + return $this; } - return $this; + return $this->filter(function ($result, $options) use ($relation) { + $result->relationQuery($relation, $this->options['with_relation_attr']); + }); } /** @@ -145,15 +147,30 @@ trait ModelRelationQuery */ public function withAttr($name, callable $callback = null) { + if (empty($this->model)) { + if (is_array($name)) { + $this->options['with_attr'] = $name; + } else { + $this->options['with_attr'][$name] = $callback; + } + return $this->filter(function ($result) { + return $this->getResultAttr($result, $this->options['with_attr']); + }, 'with_attr'); + } + if (is_array($name)) { - $this->options['with_attr'] = $name; + foreach ($name as $key => $val) { + if (strpos($key, '.')) { + [$relation, $field] = explode('.', $key); + + $this->options['with_relation_attr'][$relation][$field] = $val; + } + } } else { - $this->options['with_attr'][$name] = $callback; + $this->options['with_relation_attr'][$name] = $callback; } - return $this->filter(function ($result) { - return $this->getResultAttr($result, $this->options['with_attr']); - }, 'with_attr'); + return $this; } /** @@ -164,11 +181,17 @@ trait ModelRelationQuery */ public function with($with) { - if (!empty($with)) { - $this->options['with'] = (array) $with; + if (empty($this->model) || empty($with)) { + return $this; } - return $this; + $with = (array) $with; + $this->options['with'] = $with; + return $this->filter(function ($result) use ($with) { + if (empty($this->options['is_resultSet'])) { + $result->eagerlyResult($with, $this->options['with_relation_attr'], false, $this->options['with_cache'] ?? false); + } + }, 'with'); } /** @@ -180,7 +203,7 @@ trait ModelRelationQuery */ public function withJoin($with, string $joinType = '') { - if (empty($with)) { + if (empty($this->model) || empty($with)) { return $this; } @@ -212,10 +235,14 @@ trait ModelRelationQuery } $this->via(); - $this->options['with_join'] = $with; - return $this; + return $this->filter(function ($result) use ($with) { + // JOIN预载入查询 + if (empty($this->options['is_resultSet'])) { + $result->eagerlyResult($with, $this->options['with_relation_attr'], true, $this->options['with_cache'] ?? false); + } + }, 'with_join'); } /** @@ -229,16 +256,24 @@ trait ModelRelationQuery */ protected function withAggregate($relations, string $aggregate = 'count', $field = '*', bool $subQuery = true) { + if (empty($this->model)) { + return $this; + } + if (!$subQuery) { - $this->options['with_count'][] = [$relations, $aggregate, $field]; - } else { - if (!isset($this->options['field'])) { - $this->field('*'); - } + $this->options['with_aggregate'][] = [$relations, $aggregate, $field]; + return $this->filter(function ($result) use ($withAggregate) { + foreach ($this->options['with_aggregate'] as $val) { + $result->relationCount($this, (array) $val[0], $val[1], $val[2], false); + } + }); + } - $this->model->relationCount($this, (array) $relations, $aggregate, $field, true); + if (!isset($this->options['field'])) { + $this->field('*'); } + $this->model->relationCount($this, (array) $relations, $aggregate, $field, true); return $this; } @@ -253,6 +288,10 @@ trait ModelRelationQuery */ public function withCache($relation = true, $key = true, $expire = null, string $tag = null) { + if (empty($this->model)) { + return $this; + } + if (false === $relation || false === $key || !$this->getConnection()->getCache()) { return $this; } @@ -372,6 +411,34 @@ trait ModelRelationQuery return $this->model->hasWhere($relation, $where, $fields, $joinType, $this); } + /** + * JSON字段数据转换 + * @access protected + * @param Model $result 查询数据 + * @param array $json JSON字段 + * @param bool $assoc 是否转换为数组 + * @return void + */ + protected function jsonModelResult(Model $result, array $json = [], bool $assoc = false): void + { + $withRelationAttr = $this->options['with_relation_attr']; + foreach ($json as $name) { + if (!isset($result->$name)) { + continue; + } + + $jsonData = json_decode($result->getData($name), true); + + if (isset($withRelationAttr[$name])) { + foreach ($withRelationAttr[$name] as $key => $closure) { + $jsonData[$key] = $closure($jsonData[$key] ?? null, $jsonData); + } + } + + $result->set($name, !$assoc ? (object) $jsonData : $jsonData); + } + } + /** * 查询数据转换为模型数据集对象 * @access protected @@ -384,77 +451,45 @@ trait ModelRelationQuery return $this->model->toCollection(); } - $withRelationAttr = $this->getWithRelationAttr(); + $this->options['is_resultSet'] = true; foreach ($resultSet as $key => &$result) { // 数据转换为模型对象 - $this->resultToModel($result, $this->options, true); + $this->resultToModel($result); } if (!empty($this->options['with'])) { // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with'], $withRelationAttr, false, $this->options['with_cache'] ?? false); + $result->eagerlyResultSet($resultSet, $this->options['with'], $this->options['with_relation_attr'], false, $this->options['with_cache'] ?? false); } if (!empty($this->options['with_join'])) { // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with_join'], $withRelationAttr, true, $this->options['with_cache'] ?? false); + $result->eagerlyResultSet($resultSet, $this->options['with_join'], $this->options['with_relation_attr'], true, $this->options['with_cache'] ?? false); } // 模型数据集转换 return $this->model->toCollection($resultSet); } - /** - * 检查动态获取器 - * @access protected - * @return array - */ - protected function getWithRelationAttr(): array - { - if (isset($this->options['with_relation_attr'])) { - return $this->options['with_relation_attr']; - } - - $withRelationAttr = []; - if (!empty($this->options['with_attr'])) { - foreach ($this->options['with_attr'] as $name => $val) { - if (strpos($name, '.')) { - [$relation, $field] = explode('.', $name); - - $withRelationAttr[$relation][$field] = $val; - unset($this->options['with_attr'][$name]); - } - } - } - - $this->options['with_relation_attr'] = $withRelationAttr; - return $withRelationAttr; - } - /** * 查询数据转换为模型对象 * @access protected * @param array $result 查询数据 - * @param array $options 查询参数 - * @param bool $resultSet 是否为数据集查询 * @return void */ - protected function resultToModel(array &$result, array $options = [], bool $resultSet = false): void + protected function resultToModel(array &$result): void { - $options['with_relation_attr'] = $this->getWithRelationAttr(); - $options['is_resultSet'] = $resultSet; + $options = $this->options; - // JSON 数据处理 - if (!empty($options['json'])) { - $this->jsonResult($result, $options['json'], $options['json_assoc'], $options['with_relation_attr']); - } - - $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options), $options); + $result = $this->model->newInstance($result, !empty($options['is_resultSet']) ? null : $this->getModelUpdateCondition($options), $options); // 模型数据处理 foreach ($this->options['filter'] as $filter) { - call_user_func($filter, $result); + call_user_func_array($filter, [$result, $options]); } + + // 刷新原始数据 + $result->refreshOrigin(); } /** diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index a6aac3e..2a6f147 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -75,13 +75,9 @@ trait ResultOperation */ protected function result(array &$result): void { - if (!empty($this->options['json'])) { - $this->jsonResult($result, $this->options['json'], true); - } - // 查询数据处理 foreach ($this->options['filter'] as $filter) { - $result = call_user_func($filter, $result); + $result = call_user_func_array($filter, [$result, $this->options]); } } @@ -164,11 +160,9 @@ trait ResultOperation * @access protected * @param array $result 查询数据 * @param array $json JSON字段 - * @param bool $assoc 是否转换为数组 - * @param array $withRelationAttr 关联获取器 * @return void */ - protected function jsonResult(array &$result, array $json = [], bool $assoc = false, array $withRelationAttr = []): void + protected function jsonResult(array &$result, array $json = []): void { foreach ($json as $name) { if (!isset($result[$name])) { @@ -176,16 +170,6 @@ trait ResultOperation } $result[$name] = json_decode($result[$name], true); - - if (isset($withRelationAttr[$name])) { - foreach ($withRelationAttr[$name] as $key => $closure) { - $result[$name][$key] = $closure($result[$name][$key] ?? null, $result[$name]); - } - } - - if (!$assoc) { - $result[$name] = (object) $result[$name]; - } } } diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index fb19fd1..e9e5f9c 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -250,6 +250,17 @@ trait Attribute return $this; } + /** + * 刷新对象原始数据(为当前数据) + * @access public + * @return $this + */ + public function refreshOrigin() + { + $this->origin = $this->data; + return $this; + } + /** * 获取对象原始数据 如果不存在指定字段返回null * @access public -- Gitee From 5260c10ba8f15df644a39f8aa1b87a33f22e5405 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Mon, 3 Jan 2022 13:16:30 +0800 Subject: [PATCH 305/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/RelationShip.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 33c1b89..dd8346c 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -298,7 +298,7 @@ trait RelationShip * @param mixed $cache 关联缓存 * @return void */ - public function eagerlyResult(Model $result, array $relations, array $withRelationAttr = [], bool $join = false, $cache = false): void + public function eagerlyResult(array $relations, array $withRelationAttr = [], bool $join = false, $cache = false): void { foreach ($relations as $key => $relation) { $subRelation = []; @@ -333,7 +333,7 @@ trait RelationShip $relationCache = $cache[$relationName] ?? []; } - $relationResult->eagerlyResult($result, $relationName, $subRelation, $closure, $relationCache, $join); + $relationResult->eagerlyResult($this, $relationName, $subRelation, $closure, $relationCache, $join); } } -- Gitee From ec19283cfe85b2a1a9290757ed6e3d1d978046ff Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Mon, 3 Jan 2022 13:25:32 +0800 Subject: [PATCH 306/365] =?UTF-8?q?=E6=B3=A8=E9=87=8A=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/RelationShip.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index dd8346c..8faadf4 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -291,7 +291,6 @@ trait RelationShip /** * 预载入关联查询 返回模型对象 * @access public - * @param Model $result 数据对象 * @param array $relations 关联 * @param array $withRelationAttr 关联获取器 * @param bool $join 是否为JOIN方式 -- Gitee From 2edd4b2d1b91394151e3b8a3195ad566f6c065e2 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Mon, 3 Jan 2022 23:23:11 +0800 Subject: [PATCH 307/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3value=E5=92=8Ccolumn?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 14 ++++++++++---- src/db/concern/ModelRelationQuery.php | 20 +++++++++++--------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 9911482..0188bc1 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -269,7 +269,9 @@ abstract class BaseQuery $result = $this->connection->value($this, $field, $default); $array[$field] = $result; - $this->result($array); + + $this->jsonResult($array, $this->options['json']); + $array = $this->getResultAttr($array, $this->options['with_attr']); return $array[$field]; } @@ -284,8 +286,12 @@ abstract class BaseQuery public function column($field, string $key = ''): array { $result = $this->connection->column($this, $field, $key); + if (count($result) != count($result, 1)) { - $this->resultSet($result, false); + foreach ($result as &$val) { + $this->jsonResult($val, $this->options['json']); + $val = $this->getResultAttr($val, $this->options['with_attr']); + } } return $result; } @@ -1170,7 +1176,7 @@ abstract class BaseQuery if (!empty($this->model)) { // 返回模型对象 - $this->resultToModel($result, $this->options); + $this->resultToModel($result); } else { $this->result($result); } @@ -1199,7 +1205,7 @@ abstract class BaseQuery $this->parseView($options); } - foreach (['data', 'order', 'join', 'union', 'filter', 'json', 'with_relation_attr'] as $name) { + foreach (['data', 'order', 'join', 'union', 'filter', 'json', 'with_attr'] as $name) { if (!isset($options[$name])) { $options[$name] = []; } diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index e21724c..3312836 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -99,7 +99,7 @@ trait ModelRelationQuery } return $this->filter(function ($result, $options) use ($relation) { - $result->relationQuery($relation, $this->options['with_relation_attr']); + $result->relationQuery($relation, $this->options['with_attr']); }); } @@ -163,14 +163,16 @@ trait ModelRelationQuery if (strpos($key, '.')) { [$relation, $field] = explode('.', $key); - $this->options['with_relation_attr'][$relation][$field] = $val; + $this->options['with_attr'][$relation][$field] = $val; } } } else { - $this->options['with_relation_attr'][$name] = $callback; + $this->options['with_attr'][$name] = $callback; } - return $this; + return $this->filter(function ($result) { + $result->withAttr($this->options['with_attr']); + }, 'with_attr'); } /** @@ -189,7 +191,7 @@ trait ModelRelationQuery $this->options['with'] = $with; return $this->filter(function ($result) use ($with) { if (empty($this->options['is_resultSet'])) { - $result->eagerlyResult($with, $this->options['with_relation_attr'], false, $this->options['with_cache'] ?? false); + $result->eagerlyResult($with, $this->options['with_attr'], false, $this->options['with_cache'] ?? false); } }, 'with'); } @@ -240,7 +242,7 @@ trait ModelRelationQuery return $this->filter(function ($result) use ($with) { // JOIN预载入查询 if (empty($this->options['is_resultSet'])) { - $result->eagerlyResult($with, $this->options['with_relation_attr'], true, $this->options['with_cache'] ?? false); + $result->eagerlyResult($with, $this->options['with_attr'], true, $this->options['with_cache'] ?? false); } }, 'with_join'); } @@ -421,7 +423,7 @@ trait ModelRelationQuery */ protected function jsonModelResult(Model $result, array $json = [], bool $assoc = false): void { - $withRelationAttr = $this->options['with_relation_attr']; + $withRelationAttr = $this->options['with_attr']; foreach ($json as $name) { if (!isset($result->$name)) { continue; @@ -459,12 +461,12 @@ trait ModelRelationQuery if (!empty($this->options['with'])) { // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with'], $this->options['with_relation_attr'], false, $this->options['with_cache'] ?? false); + $result->eagerlyResultSet($resultSet, $this->options['with'], $this->options['with_attr'], false, $this->options['with_cache'] ?? false); } if (!empty($this->options['with_join'])) { // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with_join'], $this->options['with_relation_attr'], true, $this->options['with_cache'] ?? false); + $result->eagerlyResultSet($resultSet, $this->options['with_join'], $this->options['with_attr'], true, $this->options['with_cache'] ?? false); } // 模型数据集转换 -- Gitee From 91be961b091a7409d6bfdc83abb704e7f1c1d60b Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Tue, 4 Jan 2022 09:16:01 +0800 Subject: [PATCH 308/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=99=A8=E5=AF=B9json=E6=95=B0=E6=8D=AE=E7=9A=84=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 21 ++++++++++---- src/db/concern/ModelRelationQuery.php | 40 ++++++++++++--------------- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 0188bc1..f6f751f 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -270,8 +270,13 @@ abstract class BaseQuery $array[$field] = $result; - $this->jsonResult($array, $this->options['json']); - $array = $this->getResultAttr($array, $this->options['with_attr']); + if (!empty($this->options['json'])) { + $this->jsonResult($array, $this->options['json']); + } + + if (!empty($this->options['with_attr'])) { + $array = $this->getResultAttr($array, $this->options['with_attr']); + } return $array[$field]; } @@ -289,10 +294,16 @@ abstract class BaseQuery if (count($result) != count($result, 1)) { foreach ($result as &$val) { - $this->jsonResult($val, $this->options['json']); - $val = $this->getResultAttr($val, $this->options['with_attr']); + if (!empty($this->options['json'])) { + $this->jsonResult($val, $this->options['json']); + } + + if (!empty($this->options['with_attr'])) { + $val = $this->getResultAttr($val, $this->options['with_attr']); + } } } + return $result; } @@ -1205,7 +1216,7 @@ abstract class BaseQuery $this->parseView($options); } - foreach (['data', 'order', 'join', 'union', 'filter', 'json', 'with_attr'] as $name) { + foreach (['data', 'order', 'join', 'union', 'filter', 'json', 'with_attr', 'with_relatioin_attr'] as $name) { if (!isset($options[$name])) { $options[$name] = []; } diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 3312836..c4dee98 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -99,7 +99,7 @@ trait ModelRelationQuery } return $this->filter(function ($result, $options) use ($relation) { - $result->relationQuery($relation, $this->options['with_attr']); + $result->relationQuery($relation, $this->options['with_relatioin_attr']); }); } @@ -141,33 +141,26 @@ trait ModelRelationQuery /** * 设置数据字段获取器 * @access public - * @param string|array $name 字段名 - * @param callable $callback 闭包获取器 + * @param string $name 字段名 + * @param callable $callback 闭包获取器 * @return $this */ - public function withAttr($name, callable $callback = null) + public function withAttr(string $name, callable $callback) { if (empty($this->model)) { - if (is_array($name)) { - $this->options['with_attr'] = $name; - } else { - $this->options['with_attr'][$name] = $callback; - } + $this->options['with_attr'][$name] = $callback; + return $this->filter(function ($result) { return $this->getResultAttr($result, $this->options['with_attr']); }, 'with_attr'); } - if (is_array($name)) { - foreach ($name as $key => $val) { - if (strpos($key, '.')) { - [$relation, $field] = explode('.', $key); + $this->options['with_attr'][$name] = $callback; - $this->options['with_attr'][$relation][$field] = $val; - } - } - } else { - $this->options['with_attr'][$name] = $callback; + if (strpos($name, '.')) { + [$relation, $field] = explode('.', $name); + + $this->options['with_relatioin_attr'][$relation][$field] = $callback; } return $this->filter(function ($result) { @@ -187,11 +180,12 @@ trait ModelRelationQuery return $this; } - $with = (array) $with; + $with = (array) $with; + $this->options['with'] = $with; return $this->filter(function ($result) use ($with) { if (empty($this->options['is_resultSet'])) { - $result->eagerlyResult($with, $this->options['with_attr'], false, $this->options['with_cache'] ?? false); + $result->eagerlyResult($with, $this->options['with_relatioin_attr'], false, $this->options['with_cache'] ?? false); } }, 'with'); } @@ -242,7 +236,7 @@ trait ModelRelationQuery return $this->filter(function ($result) use ($with) { // JOIN预载入查询 if (empty($this->options['is_resultSet'])) { - $result->eagerlyResult($with, $this->options['with_attr'], true, $this->options['with_cache'] ?? false); + $result->eagerlyResult($with, $this->options['with_relatioin_attr'], true, $this->options['with_cache'] ?? false); } }, 'with_join'); } @@ -461,12 +455,12 @@ trait ModelRelationQuery if (!empty($this->options['with'])) { // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with'], $this->options['with_attr'], false, $this->options['with_cache'] ?? false); + $result->eagerlyResultSet($resultSet, $this->options['with'], $this->options['with_relatioin_attr'], false, $this->options['with_cache'] ?? false); } if (!empty($this->options['with_join'])) { // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with_join'], $this->options['with_attr'], true, $this->options['with_cache'] ?? false); + $result->eagerlyResultSet($resultSet, $this->options['with_join'], $this->options['with_relatioin_attr'], true, $this->options['with_cache'] ?? false); } // 模型数据集转换 -- Gitee From 856649f1fad01a3cf227fec790f2fa1b63869ac7 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Tue, 4 Jan 2022 11:22:16 +0800 Subject: [PATCH 309/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 30 ++++++++++++++++++--------- src/model/concern/Attribute.php | 4 ++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index c4dee98..99fcc55 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -141,18 +141,17 @@ trait ModelRelationQuery /** * 设置数据字段获取器 * @access public - * @param string $name 字段名 - * @param callable $callback 闭包获取器 + * @param string|array $name 字段名 + * @param callable $callback 闭包获取器 * @return $this */ - public function withAttr(string $name, callable $callback) + public function withAttr($name, callable $callback = null) { - if (empty($this->model)) { - $this->options['with_attr'][$name] = $callback; - - return $this->filter(function ($result) { - return $this->getResultAttr($result, $this->options['with_attr']); - }, 'with_attr'); + if (is_array($name)) { + foreach ($name as $key => $val) { + $this->withAttr($key, $val); + } + return $this; } $this->options['with_attr'][$name] = $callback; @@ -160,7 +159,18 @@ trait ModelRelationQuery if (strpos($name, '.')) { [$relation, $field] = explode('.', $name); - $this->options['with_relatioin_attr'][$relation][$field] = $callback; + if (!empty($this->options['json']) && in_array($relation, $this->options['json'])) { + + } else { + $this->options['with_relatioin_attr'][$relation][$field] = $callback; + unset($this->options['with_attr'][$name]); + } + } + + if (empty($this->model)) { + return $this->filter(function ($result) { + return $this->getResultAttr($result, $this->options['with_attr']); + }, 'with_attr'); } return $this->filter(function ($result) { diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index e9e5f9c..9201f90 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -542,6 +542,10 @@ trait Attribute */ protected function getJsonValue($name, $value) { + if (is_null($value)) { + return $value; + } + foreach ($this->withAttr[$name] as $key => $closure) { if ($this->jsonAssoc) { $value[$key] = $closure($value[$key], $value); -- Gitee From 85045c460c26f1770056dfe183ba84efc3fad446 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Wed, 5 Jan 2022 21:28:44 +0800 Subject: [PATCH 310/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3mongo=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 2 +- src/db/Mongo.php | 6 +----- src/db/concern/ModelRelationQuery.php | 12 ++++++------ 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index f6f751f..6cd6b01 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -1216,7 +1216,7 @@ abstract class BaseQuery $this->parseView($options); } - foreach (['data', 'order', 'join', 'union', 'filter', 'json', 'with_attr', 'with_relatioin_attr'] as $name) { + foreach (['data', 'order', 'join', 'union', 'filter', 'json', 'with_attr', 'with_relation_attr'] as $name) { if (!isset($options[$name])) { $options[$name] = []; } diff --git a/src/db/Mongo.php b/src/db/Mongo.php index 5e8a09a..cf6e9c4 100644 --- a/src/db/Mongo.php +++ b/src/db/Mongo.php @@ -630,7 +630,7 @@ class Mongo extends BaseQuery $options['table'] = $this->getTable(); } - foreach (['where', 'data'] as $name) { + foreach (['where', 'data', 'projection', 'filter', 'json', 'with_attr', 'with_relation_attr'] as $name) { if (!isset($options[$name])) { $options[$name] = []; } @@ -649,10 +649,6 @@ class Mongo extends BaseQuery $options['modifiers'] = $modifiers; } - if (!isset($options['projection'])) { - $options['projection'] = []; - } - if (!isset($options['typeMap'])) { $options['typeMap'] = $this->getConfig('type_map'); } diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 99fcc55..2821bf3 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -99,7 +99,7 @@ trait ModelRelationQuery } return $this->filter(function ($result, $options) use ($relation) { - $result->relationQuery($relation, $this->options['with_relatioin_attr']); + $result->relationQuery($relation, $this->options['with_relation_attr']); }); } @@ -162,7 +162,7 @@ trait ModelRelationQuery if (!empty($this->options['json']) && in_array($relation, $this->options['json'])) { } else { - $this->options['with_relatioin_attr'][$relation][$field] = $callback; + $this->options['with_relation_attr'][$relation][$field] = $callback; unset($this->options['with_attr'][$name]); } } @@ -195,7 +195,7 @@ trait ModelRelationQuery $this->options['with'] = $with; return $this->filter(function ($result) use ($with) { if (empty($this->options['is_resultSet'])) { - $result->eagerlyResult($with, $this->options['with_relatioin_attr'], false, $this->options['with_cache'] ?? false); + $result->eagerlyResult($with, $this->options['with_relation_attr'], false, $this->options['with_cache'] ?? false); } }, 'with'); } @@ -246,7 +246,7 @@ trait ModelRelationQuery return $this->filter(function ($result) use ($with) { // JOIN预载入查询 if (empty($this->options['is_resultSet'])) { - $result->eagerlyResult($with, $this->options['with_relatioin_attr'], true, $this->options['with_cache'] ?? false); + $result->eagerlyResult($with, $this->options['with_relation_attr'], true, $this->options['with_cache'] ?? false); } }, 'with_join'); } @@ -465,12 +465,12 @@ trait ModelRelationQuery if (!empty($this->options['with'])) { // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with'], $this->options['with_relatioin_attr'], false, $this->options['with_cache'] ?? false); + $result->eagerlyResultSet($resultSet, $this->options['with'], $this->options['with_relation_attr'], false, $this->options['with_cache'] ?? false); } if (!empty($this->options['with_join'])) { // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with_join'], $this->options['with_relatioin_attr'], true, $this->options['with_cache'] ?? false); + $result->eagerlyResultSet($resultSet, $this->options['with_join'], $this->options['with_relation_attr'], true, $this->options['with_cache'] ?? false); } // 模型数据集转换 -- Gitee From ffaa727afe6b3f0b8acb1d5854d6bd8f71a80b20 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Wed, 5 Jan 2022 21:53:53 +0800 Subject: [PATCH 311/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bhidden=20visible=20ap?= =?UTF-8?q?pend=E6=96=B9=E6=B3=95=E7=9A=84=E5=A4=84=E7=90=86=E6=9C=BA?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 6 ---- src/db/concern/ModelRelationQuery.php | 45 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 6cd6b01..5076120 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -106,12 +106,6 @@ abstract class BaseQuery $name = Str::snake(substr($method, 5)); array_unshift($args, $name); return call_user_func_array([$this, 'where'], $args); - } elseif ($this->model && in_array($method, ['hidden', 'visible', 'append'])) { - // 调用模型类方法 - $this->model->filter(function ($model, $options) use ($method, $args) { - call_user_func_array([$model, $method], $args); - }); - return $this; } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { // 动态调用命名范围 $method = 'scope' . $method; diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 2821bf3..a23803c 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -51,6 +51,45 @@ trait ModelRelationQuery return $this->model; } + /** + * 设置需要隐藏的输出属性 + * @access public + * @param array $hidden 属性列表 + * @return $this + */ + public function hidden(array $hidden = []) + { + $this->options['hidden'] = $hidden; + + return $this; + } + + /** + * 设置需要输出的属性 + * @access public + * @param array $visible + * @return $this + */ + public function visible(array $visible = []) + { + $this->options['visible'] = $visible; + + return $this; + } + + /** + * 设置需要附加的输出属性 + * @access public + * @param array $append 属性列表 + * @return $this + */ + public function append(array $append = []) + { + $this->options['append'] = $append; + + return $this; + } + /** * 添加查询范围 * @access public @@ -494,6 +533,12 @@ trait ModelRelationQuery call_user_func_array($filter, [$result, $options]); } + foreach (['hidden', 'visible', 'append'] as $name) { + if (!empty($this->options[$name])) { + $result->$name($this->options[$name]); + } + } + // 刷新原始数据 $result->refreshOrigin(); } -- Gitee From 17accf90d16f892e0e8129a5ccb9ef59e1b40356 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Wed, 5 Jan 2022 22:14:07 +0800 Subject: [PATCH 312/365] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index a23803c..ae1aea4 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -466,7 +466,7 @@ trait ModelRelationQuery */ protected function jsonModelResult(Model $result, array $json = [], bool $assoc = false): void { - $withRelationAttr = $this->options['with_attr']; + $withAttr = $this->options['with_attr']; foreach ($json as $name) { if (!isset($result->$name)) { continue; @@ -474,8 +474,8 @@ trait ModelRelationQuery $jsonData = json_decode($result->getData($name), true); - if (isset($withRelationAttr[$name])) { - foreach ($withRelationAttr[$name] as $key => $closure) { + if (isset($withAttr[$name])) { + foreach ($withAttr[$name] as $key => $closure) { $jsonData[$key] = $closure($jsonData[$key] ?? null, $jsonData); } } @@ -502,14 +502,11 @@ trait ModelRelationQuery $this->resultToModel($result); } - if (!empty($this->options['with'])) { - // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with'], $this->options['with_relation_attr'], false, $this->options['with_cache'] ?? false); - } - - if (!empty($this->options['with_join'])) { - // 预载入 - $result->eagerlyResultSet($resultSet, $this->options['with_join'], $this->options['with_relation_attr'], true, $this->options['with_cache'] ?? false); + foreach (['with', 'with_join'] as $with) { + // 关联预载入 + if (!empty($this->options[$with])) { + $result->eagerlyResultSet($resultSet, $this->options[$with], $this->options['with_relation_attr'], false, $this->options['with_cache'] ?? false); + } } // 模型数据集转换 @@ -524,13 +521,11 @@ trait ModelRelationQuery */ protected function resultToModel(array &$result): void { - $options = $this->options; - - $result = $this->model->newInstance($result, !empty($options['is_resultSet']) ? null : $this->getModelUpdateCondition($options), $options); + $result = $this->model->newInstance($result, !empty($this->options['is_resultSet']) ? null : $this->getModelUpdateCondition($this->options), $this->options); // 模型数据处理 foreach ($this->options['filter'] as $filter) { - call_user_func_array($filter, [$result, $options]); + call_user_func_array($filter, [$result, $this->options]); } foreach (['hidden', 'visible', 'append'] as $name) { -- Gitee From 091ad5e023c15fcce4ceaea2f3814bdf71045cde Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Thu, 6 Jan 2022 10:48:16 +0800 Subject: [PATCH 313/365] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 33 +------- src/db/concern/ModelRelationQuery.php | 105 +++++++++++++++----------- src/db/concern/ResultOperation.php | 23 ++++-- 3 files changed, 81 insertions(+), 80 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 5076120..cccd8ef 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -263,14 +263,7 @@ abstract class BaseQuery $result = $this->connection->value($this, $field, $default); $array[$field] = $result; - - if (!empty($this->options['json'])) { - $this->jsonResult($array, $this->options['json']); - } - - if (!empty($this->options['with_attr'])) { - $array = $this->getResultAttr($array, $this->options['with_attr']); - } + $this->result($array); return $array[$field]; } @@ -287,15 +280,7 @@ abstract class BaseQuery $result = $this->connection->column($this, $field, $key); if (count($result) != count($result, 1)) { - foreach ($result as &$val) { - if (!empty($this->options['json'])) { - $this->jsonResult($val, $this->options['json']); - } - - if (!empty($this->options['with_attr'])) { - $val = $this->getResultAttr($val, $this->options['with_attr']); - } - } + $this->resultSet($result, false); } return $result; @@ -887,19 +872,7 @@ abstract class BaseQuery $this->options['json'] = $json; $this->options['json_assoc'] = $assoc; - if ($this->model) { - return $this->filter(function ($result) use ($json, $assoc) { - if (!empty($json)) { - $this->jsonModelResult($result, $json, $assoc); - } - }); - } - - return $this->filter(function ($result) use ($json) { - if (!empty($json)) { - $this->jsonResult($result, $json); - } - }); + return $this; } /** diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index ae1aea4..0015a24 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -137,9 +137,8 @@ trait ModelRelationQuery return $this; } - return $this->filter(function ($result, $options) use ($relation) { - $result->relationQuery($relation, $this->options['with_relation_attr']); - }); + $this->options['relation'] = $relation; + return $this; } /** @@ -206,15 +205,7 @@ trait ModelRelationQuery } } - if (empty($this->model)) { - return $this->filter(function ($result) { - return $this->getResultAttr($result, $this->options['with_attr']); - }, 'with_attr'); - } - - return $this->filter(function ($result) { - $result->withAttr($this->options['with_attr']); - }, 'with_attr'); + return $this; } /** @@ -229,14 +220,8 @@ trait ModelRelationQuery return $this; } - $with = (array) $with; - - $this->options['with'] = $with; - return $this->filter(function ($result) use ($with) { - if (empty($this->options['is_resultSet'])) { - $result->eagerlyResult($with, $this->options['with_relation_attr'], false, $this->options['with_cache'] ?? false); - } - }, 'with'); + $this->options['with'] = (array) $with; + return $this; } /** @@ -282,12 +267,7 @@ trait ModelRelationQuery $this->via(); $this->options['with_join'] = $with; - return $this->filter(function ($result) use ($with) { - // JOIN预载入查询 - if (empty($this->options['is_resultSet'])) { - $result->eagerlyResult($with, $this->options['with_relation_attr'], true, $this->options['with_cache'] ?? false); - } - }, 'with_join'); + return $this; } /** @@ -306,12 +286,8 @@ trait ModelRelationQuery } if (!$subQuery) { - $this->options['with_aggregate'][] = [$relations, $aggregate, $field]; - return $this->filter(function ($result) use ($withAggregate) { - foreach ($this->options['with_aggregate'] as $val) { - $result->relationCount($this, (array) $val[0], $val[1], $val[2], false); - } - }); + $this->options['with_aggregate'][] = [(array) $relations, $aggregate, $field]; + return $this; } if (!isset($this->options['field'])) { @@ -459,20 +435,18 @@ trait ModelRelationQuery /** * JSON字段数据转换 * @access protected - * @param Model $result 查询数据 - * @param array $json JSON字段 - * @param bool $assoc 是否转换为数组 + * @param array $result 查询数据 * @return void */ - protected function jsonModelResult(Model $result, array $json = [], bool $assoc = false): void + protected function jsonModelResult(array &$result): void { $withAttr = $this->options['with_attr']; - foreach ($json as $name) { - if (!isset($result->$name)) { + foreach ($this->options['json'] as $name) { + if (!isset($result[$name])) { continue; } - $jsonData = json_decode($result->getData($name), true); + $jsonData = json_decode($result[$name], true); if (isset($withAttr[$name])) { foreach ($withAttr[$name] as $key => $closure) { @@ -480,7 +454,7 @@ trait ModelRelationQuery } } - $result->set($name, !$assoc ? (object) $jsonData : $jsonData); + $result[$name] = !$this->options['json_assoc'] ? (object) $jsonData : $jsonData; } } @@ -497,6 +471,7 @@ trait ModelRelationQuery } $this->options['is_resultSet'] = true; + foreach ($resultSet as $key => &$result) { // 数据转换为模型对象 $this->resultToModel($result); @@ -505,7 +480,13 @@ trait ModelRelationQuery foreach (['with', 'with_join'] as $with) { // 关联预载入 if (!empty($this->options[$with])) { - $result->eagerlyResultSet($resultSet, $this->options[$with], $this->options['with_relation_attr'], false, $this->options['with_cache'] ?? false); + $result->eagerlyResultSet( + $resultSet, + $this->options[$with], + $this->options['with_relation_attr'], + 'with_join' == $with ? true : false, + $this->options['with_cache'] ?? false + ); } } @@ -521,13 +502,53 @@ trait ModelRelationQuery */ protected function resultToModel(array &$result): void { - $result = $this->model->newInstance($result, !empty($this->options['is_resultSet']) ? null : $this->getModelUpdateCondition($this->options), $this->options); + // JSON数据处理 + if (!empty($this->options['json'])) { + $this->jsonModelResult($result); + } + + $result = $this->model->newInstance( + $result, + !empty($this->options['is_resultSet']) ? null : $this->getModelUpdateCondition($this->options), + $this->options + ); // 模型数据处理 foreach ($this->options['filter'] as $filter) { call_user_func_array($filter, [$result, $this->options]); } + // 关联查询 + if (!empty($this->options['relation'])) { + $result->relationQuery($this->options['relation'], $this->options['with_relation_attr']); + } + + // 关联预载入查询 + if (empty($this->options['is_resultSet'])) { + foreach (['with', 'with_join'] as $with) { + if (!empty($this->options[$with])) { + $result->eagerlyResult( + $this->options[$with], + $this->options['with_relation_attr'], + 'with_join' == $with ? true : false, + $this->options['with_cache'] ?? false + ); + } + } + } + + // 关联统计查询 + if (!empty($this->options['with_aggregate'])) { + foreach ($this->options['with_aggregate'] as $val) { + $result->relationCount($this, $val[0], $val[1], $val[2], false); + } + } + + // 动态获取器 + if (!empty($this->options['with_attr'])) { + $result->withAttr($this->options['with_attr']); + } + foreach (['hidden', 'visible', 'append'] as $name) { if (!empty($this->options[$name])) { $result->$name($this->options[$name]); diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php index 2a6f147..ea26916 100644 --- a/src/db/concern/ResultOperation.php +++ b/src/db/concern/ResultOperation.php @@ -75,10 +75,20 @@ trait ResultOperation */ protected function result(array &$result): void { + // JSON数据处理 + if (!empty($this->options['json'])) { + $this->jsonResult($result); + } + // 查询数据处理 foreach ($this->options['filter'] as $filter) { $result = call_user_func_array($filter, [$result, $this->options]); } + + // 获取器 + if (!empty($this->options['with_attr'])) { + $this->getResultAttr($result, $this->options['with_attr']); + } } /** @@ -105,9 +115,9 @@ trait ResultOperation * @access protected * @param array $result 查询数据 * @param array $withAttr 字段获取器 - * @return array + * @return void */ - protected function getResultAttr(array $result, array $withAttr = []): array + protected function getResultAttr(array &$result, array $withAttr = []): void { foreach ($withAttr as $name => $closure) { $name = Str::snake($name); @@ -123,8 +133,6 @@ trait ResultOperation $result[$name] = $closure($result[$name] ?? null, $result); } } - - return $result; } /** @@ -158,13 +166,12 @@ trait ResultOperation /** * JSON字段数据转换 * @access protected - * @param array $result 查询数据 - * @param array $json JSON字段 + * @param array $result 查询数据 * @return void */ - protected function jsonResult(array &$result, array $json = []): void + protected function jsonResult(array &$result): void { - foreach ($json as $name) { + foreach ($this->options['json'] as $name) { if (!isset($result[$name])) { continue; } -- Gitee From 874c34e368d154c954b25c977acf5401536cabd2 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Tue, 11 Jan 2022 17:33:42 +0800 Subject: [PATCH 314/365] =?UTF-8?q?=E5=AE=8C=E5=96=84sqlite=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/builder/Sqlite.php | 24 ++++++++++++++---------- src/db/connector/Sqlite.php | 2 +- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/db/builder/Sqlite.php b/src/db/builder/Sqlite.php index 78a307d..ff17c5d 100644 --- a/src/db/builder/Sqlite.php +++ b/src/db/builder/Sqlite.php @@ -24,8 +24,8 @@ class Sqlite extends Builder /** * limit * @access public - * @param Query $query 查询对象 - * @param mixed $limit + * @param Query $query 查询对象 + * @param mixed $limit * @return string */ public function parseLimit(Query $query, string $limit): string @@ -47,7 +47,7 @@ class Sqlite extends Builder /** * 随机排序 * @access protected - * @param Query $query 查询对象 + * @param Query $query 查询对象 * @return string */ protected function parseRand(Query $query): string @@ -58,9 +58,9 @@ class Sqlite extends Builder /** * 字段和表名处理 * @access public - * @param Query $query 查询对象 - * @param mixed $key 字段名 - * @param bool $strict 严格检测 + * @param Query $query 查询对象 + * @param mixed $key 字段名 + * @param bool $strict 严格检测 * @return string */ public function parseKey(Query $query, $key, bool $strict = false): string @@ -73,7 +73,7 @@ class Sqlite extends Builder $key = trim($key); - if (strpos($key, '.')) { + if (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { [$table, $key] = explode('.', $key, 2); $alias = $query->getOptions('alias'); @@ -88,8 +88,12 @@ class Sqlite extends Builder } } + if ('*' != $key && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) { + $key = '`' . $key . '`'; + } + if (isset($table)) { - $key = $table . '.' . $key; + $key = '`' . $table . '`.' . $key; } return $key; @@ -98,8 +102,8 @@ class Sqlite extends Builder /** * 设置锁机制 * @access protected - * @param Query $query 查询对象 - * @param bool|string $lock + * @param Query $query 查询对象 + * @param bool|string $lock * @return string */ protected function parseLock(Query $query, $lock = false): string diff --git a/src/db/connector/Sqlite.php b/src/db/connector/Sqlite.php index c664f20..3e42a90 100644 --- a/src/db/connector/Sqlite.php +++ b/src/db/connector/Sqlite.php @@ -42,7 +42,7 @@ class Sqlite extends PDOConnection public function getFields(string $tableName): array { [$tableName] = explode(' ', $tableName); - $sql = 'PRAGMA table_info( ' . $tableName . ' )'; + $sql = 'PRAGMA table_info( \'' . $tableName . '\' )'; $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); -- Gitee From 7e7b34bb94049ffe7d277d87d1860277febf2f2d Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Tue, 11 Jan 2022 20:43:29 +0800 Subject: [PATCH 315/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3Fetch=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Fetch.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/db/Fetch.php b/src/db/Fetch.php index 16caed2..a997a85 100644 --- a/src/db/Fetch.php +++ b/src/db/Fetch.php @@ -421,10 +421,8 @@ class Fetch if (!empty($options['group'])) { // 支持GROUP - $bind = $this->query->getBind(); - $subSql = $this->query->options($options)->field('count(' . $field . ') AS think_count')->bind($bind)->buildSql(); - - $query = $this->query->newQuery()->table([$subSql => '_group_count_']); + $subSql = $this->query->field('count(' . $field . ') AS think_count')->buildSql(); + $query = $this->query->newQuery()->table([$subSql => '_group_count_']); return $query->fetchsql()->aggregate('COUNT', '*'); } else { -- Gitee From 9f785471bed3c09cdaa4889c89596c3044bc6444 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Wed, 12 Jan 2022 12:31:30 +0800 Subject: [PATCH 316/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=A4=9A=E5=AF=B9?= =?UTF-8?q?=E5=A4=9A=E5=85=B3=E8=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsToMany.php | 62 +++++++++++++++------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 75eab1e..f82a64a 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -132,9 +132,39 @@ class BelongsToMany extends Relation $closure($this->getClosureType($closure)); } - return $this->relation($subRelation) + $resultSet = $this->relation($subRelation) ->select() ->setParent(clone $this->parent); + + foreach ($resultSet as $result) { + $this->matchPivot($result); + } + + return $resultSet; + } + + /** + * 组装Pivot模型 + * @access public + * @param Model $result 模型对象 + * @return array + */ + protected function matchPivot(Model $result): array + { + $pivot = []; + foreach ($result->getData() as $key => $val) { + if (strpos($key, '__')) { + [$name, $attr] = explode('__', $key, 2); + + if ('pivot' == $name) { + $pivot[$attr] = $val; + unset($result->$key); + } + } + } + + $result->setRelation($this->pivotDataName, $this->newPivot($pivot)); + return $pivot; } /** @@ -326,24 +356,13 @@ class BelongsToMany extends Relation // 组装模型数据 $data = []; foreach ($list as $set) { - $pivot = []; - foreach ($set->getData() as $key => $val) { - if (strpos($key, '__')) { - [$name, $attr] = explode('__', $key, 2); - if ('pivot' == $name) { - $pivot[$attr] = $val; - unset($set->$key); - } - } - } - $key = $pivot[$this->localKey]; + $pivot = $this->matchPivot($set); + $key = $pivot[$this->localKey]; if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { continue; } - $set->setRelation($this->pivotDataName, $this->newPivot($pivot)); - $data[$key][] = $set; } @@ -595,20 +614,7 @@ class BelongsToMany extends Relation $localKey = $this->localKey; $this->query->getModel()->filter(function ($result, $options) { - $pivot = []; - - foreach ($result->getData() as $key => $val) { - if (strpos($key, '__')) { - [$name, $attr] = explode('__', $key, 2); - - if ('pivot' == $name) { - $pivot[$attr] = $val; - unset($result->$key); - } - } - } - - $result->setRelation($this->pivotDataName, $this->newPivot($pivot)); + $this->matchPivot($result); }); // 关联查询 -- Gitee From 2bd628d70444fb4053bc87f3c855c62df0259109 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Wed, 12 Jan 2022 14:55:51 +0800 Subject: [PATCH 317/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=A4=9A=E5=AF=B9?= =?UTF-8?q?=E5=A4=9A=E5=85=B3=E8=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsToMany.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index f82a64a..9890906 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -132,15 +132,9 @@ class BelongsToMany extends Relation $closure($this->getClosureType($closure)); } - $resultSet = $this->relation($subRelation) + return $this->relation($subRelation) ->select() ->setParent(clone $this->parent); - - foreach ($resultSet as $result) { - $this->matchPivot($result); - } - - return $resultSet; } /** @@ -613,7 +607,7 @@ class BelongsToMany extends Relation $foreignKey = $this->foreignKey; $localKey = $this->localKey; - $this->query->getModel()->filter(function ($result, $options) { + $this->query->filter(function ($result, $options) { $this->matchPivot($result); }); -- Gitee From 14606afecb309d746799e5bd6e2d63f1a6a66112 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Thu, 13 Jan 2022 14:06:27 +0800 Subject: [PATCH 318/365] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=86=99=E5=85=A5=E5=AF=B9=E5=AF=B9=E8=B1=A1=E5=80=BC=E7=9A=84?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 9201f90..e4aa07b 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -382,6 +382,9 @@ trait Attribute } elseif (isset($this->type[$name])) { // 类型转换 $value = $this->writeTransform($value, $this->type[$name]); + } elseif (is_object($value) && method_exists($value, '__toString')) { + // 对象类型 + $value = $value->__toString(); } // 设置数据对象属性 -- Gitee From 6f5c7f60f90f54125b139b7b362121f92735c901 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Mon, 17 Jan 2022 23:47:39 +0800 Subject: [PATCH 319/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E4=B8=80=E5=AF=B9?= =?UTF-8?q?=E4=B8=80=E5=85=B3=E8=81=94=E5=86=99=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsTo.php | 6 +++--- src/model/relation/HasOne.php | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 941e1d4..08aecaa 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -277,12 +277,12 @@ class BelongsTo extends OneToOne $relationModel->exists(true); } + // 设置关联属性 + $result->setRelation($relation, $relationModel); + if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); - } else { - // 设置关联属性 - $result->setRelation($relation, $relationModel); } } diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index be4927b..e1b6486 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -276,11 +276,12 @@ class HasOne extends OneToOne $relationModel->exists(true); } + // 设置关联属性 + $result->setRelation($relation, $relationModel); + if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); - } else { - $result->setRelation($relation, $relationModel); } } -- Gitee From 2eb8d027c06b7d1b711d41079ae43fa192735885 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Mon, 17 Jan 2022 23:59:22 +0800 Subject: [PATCH 320/365] =?UTF-8?q?=E4=B8=80=E5=AF=B9=E4=B8=80=E5=85=B3?= =?UTF-8?q?=E8=81=94=E6=9F=A5=E8=AF=A2=E7=BB=91=E5=AE=9A=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsTo.php | 5 ++--- src/model/relation/HasOne.php | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 08aecaa..0802b11 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -236,12 +236,11 @@ class BelongsTo extends OneToOne $relationModel->exists(true); } + // 设置关联属性 + $result->setRelation($relation, $relationModel); if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); - } else { - // 设置关联属性 - $result->setRelation($relation, $relationModel); } } } diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index e1b6486..269f0d7 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -234,13 +234,12 @@ class HasOne extends OneToOne $relationModel->setParent(clone $result); $relationModel->exists(true); } + // 设置关联属性 + $result->setRelation($relation, $relationModel); if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); - } else { - // 设置关联属性 - $result->setRelation($relation, $relationModel); } } } -- Gitee From c171243c2ef67a98b077051ad90b7ccc4f4a4b34 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 21 Jan 2022 14:22:15 +0800 Subject: [PATCH 321/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E8=BF=9C=E7=A8=8B?= =?UTF-8?q?=E4=B8=80=E5=AF=B9=E5=A4=9A=E5=85=B3=E8=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/HasManyThrough.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index d4b7d91..fc62026 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -258,8 +258,14 @@ class HasManyThrough extends Relation $closure($this->getClosureType($closure)); } + $throughKey = $this->throughKey; + + if ($this->baseQuery) { + $throughKey = Str::snake(class_basename($this->model)) . "." . $this->throughKey; + } + $list = $this->query - ->where($this->throughKey, 'in', $keys) + ->where($throughKey, 'in', $keys) ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) ->select(); -- Gitee From 407a60658f37fc57422ab95a9922c6f69af90f46 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Tue, 25 Jan 2022 14:00:05 +0800 Subject: [PATCH 322/365] =?UTF-8?q?=E8=B0=83=E6=95=B4Raw=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=20=E5=8F=96=E6=B6=88=5F=5FtoString=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Raw.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/db/Raw.php b/src/db/Raw.php index 833fbf0..b956ff6 100644 --- a/src/db/Raw.php +++ b/src/db/Raw.php @@ -64,8 +64,4 @@ class Raw return $this->bind; } - public function __toString() - { - return (string) $this->value; - } } -- Gitee From 4046be994d9a16c9d96bb3738f0a5297f7a2061b Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 15 Feb 2022 14:32:41 +0800 Subject: [PATCH 323/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0cacheAlways=E6=96=B9?= =?UTF-8?q?=E6=B3=95=20=E6=97=A0=E8=AE=BA=E6=95=B0=E6=8D=AE=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E4=B8=BA=E7=A9=BA=E9=83=BD=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 16 +++++++++++++++- src/db/PDOConnection.php | 5 +++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index cccd8ef..f454ae4 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -765,7 +765,7 @@ abstract class BaseQuery } /** - * 查询缓存 + * 查询缓存 数据为空不缓存 * @access public * @param mixed $key 缓存key * @param integer|\DateTime $expire 缓存有效期 @@ -788,6 +788,20 @@ abstract class BaseQuery return $this; } + /** + * 查询缓存 允许缓存空数据 + * @access public + * @param mixed $key 缓存key + * @param integer|\DateTime $expire 缓存有效期 + * @param string|array $tag 缓存标签 + * @return $this + */ + public function cacheAlways($key = true, $expire = null, $tag = null) + { + $this->options['cache_always'] = true; + return $this->cache($key, $expire, $tag); + } + /** * 指定查询lock * @access public diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 40ac99e..77a0b01 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -709,9 +709,10 @@ abstract class PDOConnection extends Connection $this->getPDOStatement($sql, $bind, $master, $procedure); - $resultSet = $this->getResult($procedure); + $resultSet = $this->getResult($procedure); + $requireCache = $query->getOptions('cache_always') || !empty($resultSet); - if (isset($cacheItem) && $resultSet) { + if (isset($cacheItem) && $requireCache) { // 缓存数据集 $cacheItem->set($resultSet); $this->cacheData($cacheItem); -- Gitee From cce667578a96204d9a774cef6b41480d6cf4a820 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 15 Feb 2022 14:37:57 +0800 Subject: [PATCH 324/365] =?UTF-8?q?=E6=94=B9=E8=BF=9Bcache=E6=96=B9?= =?UTF-8?q?=E6=B3=95=20=E5=A2=9E=E5=8A=A0always=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index f454ae4..960bcc0 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -770,9 +770,10 @@ abstract class BaseQuery * @param mixed $key 缓存key * @param integer|\DateTime $expire 缓存有效期 * @param string|array $tag 缓存标签 + * @param bool $always 始终缓存 * @return $this */ - public function cache($key = true, $expire = null, $tag = null) + public function cache($key = true, $expire = null, $tag = null, bool $always = false) { if (false === $key || !$this->getConnection()->getCache()) { return $this; @@ -783,7 +784,8 @@ abstract class BaseQuery $key = true; } - $this->options['cache'] = [$key, $expire, $tag]; + $this->options['cache'] = [$key, $expire, $tag]; + $this->options['cache_always'] = $always; return $this; } @@ -798,8 +800,7 @@ abstract class BaseQuery */ public function cacheAlways($key = true, $expire = null, $tag = null) { - $this->options['cache_always'] = true; - return $this->cache($key, $expire, $tag); + return $this->cache($key, $expire, $tag, true); } /** -- Gitee From c5f168443a727b623a5a98aee6e0c3646c2daf72 Mon Sep 17 00:00:00 2001 From: augushong Date: Thu, 9 Dec 2021 09:38:11 +0800 Subject: [PATCH 325/365] =?UTF-8?q?=E4=BD=BFwithField=E7=9A=84=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E6=94=AF=E6=8C=81=E6=95=B0=E7=BB=84=E6=88=96=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E4=B8=B2,=E4=BD=BF=E5=85=B6=E8=A1=A8=E7=8E=B0?= =?UTF-8?q?=E4=B8=8EwithoutField=E5=92=8C=E6=96=87=E6=A1=A3=E4=B8=80?= =?UTF-8?q?=E8=87=B4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Relation.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/model/Relation.php b/src/model/Relation.php index 358842d..59f207d 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -215,11 +215,15 @@ abstract class Relation /** * 限制关联数据的字段 * @access public - * @param array $field 关联字段限制 + * @param array|string $field 关联字段限制 * @return $this */ public function withField(array $field) { + if (is_string($field)) { + $field = array_map('trim', explode(',', $field)); + } + $this->withField = $field; return $this; } -- Gitee From 6d8a2892d4895a4275f1fd6e566a1268fd3563c8 Mon Sep 17 00:00:00 2001 From: augushong <31880431+augushong@users.noreply.github.com> Date: Tue, 21 Dec 2021 14:05:15 +0800 Subject: [PATCH 326/365] Update Relation.php --- src/model/Relation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/Relation.php b/src/model/Relation.php index 59f207d..3aa22be 100644 --- a/src/model/Relation.php +++ b/src/model/Relation.php @@ -218,7 +218,7 @@ abstract class Relation * @param array|string $field 关联字段限制 * @return $this */ - public function withField(array $field) + public function withField($field) { if (is_string($field)) { $field = array_map('trim', explode(',', $field)); -- Gitee From 820cde79ecbb2948610dcc16b1f5fa6dcf9f59a8 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 20 Feb 2022 23:52:29 +0800 Subject: [PATCH 327/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BsetAttr=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index e4aa07b..90949e5 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -15,6 +15,7 @@ namespace think\model\concern; use InvalidArgumentException; use think\db\Raw; use think\helper\Str; +use think\Model; use think\model\Relation; /** @@ -382,6 +383,10 @@ trait Attribute } elseif (isset($this->type[$name])) { // 类型转换 $value = $this->writeTransform($value, $this->type[$name]); + } elseif ($value instanceof Model) { + $this->relation[$name] = $value; + unset($this->get[$name]); + return; } elseif (is_object($value) && method_exists($value, '__toString')) { // 对象类型 $value = $value->__toString(); -- Gitee From 52f6721a1ce7a5cfbcce6619504d8398afdf8dec Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 22 Feb 2022 21:10:39 +0800 Subject: [PATCH 328/365] =?UTF-8?q?=E8=B0=83=E6=95=B4setAttr=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 90949e5..897d327 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -383,11 +383,7 @@ trait Attribute } elseif (isset($this->type[$name])) { // 类型转换 $value = $this->writeTransform($value, $this->type[$name]); - } elseif ($value instanceof Model) { - $this->relation[$name] = $value; - unset($this->get[$name]); - return; - } elseif (is_object($value) && method_exists($value, '__toString')) { + } elseif (array_key_exists($name, $this->origin) && is_object($value) && method_exists($value, '__toString')) { // 对象类型 $value = $value->__toString(); } -- Gitee From bc3f8ad05d4d2b52186cd3cfa7df793c0ff8ad26 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Thu, 24 Feb 2022 20:14:16 +0800 Subject: [PATCH 329/365] =?UTF-8?q?=E4=BC=98=E5=8C=96IDE=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Collection.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/model/Collection.php b/src/model/Collection.php index c8ff385..9d31e20 100644 --- a/src/model/Collection.php +++ b/src/model/Collection.php @@ -18,6 +18,11 @@ use think\Paginator; /** * 模型数据集类 + * + * @template TKey of array-key + * @template TModel of \think\Model + * + * @extends BaseCollection */ class Collection extends BaseCollection { @@ -184,8 +189,8 @@ class Collection extends BaseCollection * 按指定键整理数据 * * @access public - * @param mixed $items 数据 - * @param string $indexKey 键名 + * @param mixed $items 数据 + * @param string|null $indexKey 键名 * @return array */ public function dictionary($items = null, string &$indexKey = null) @@ -211,8 +216,8 @@ class Collection extends BaseCollection * 比较数据集,返回差集 * * @access public - * @param mixed $items 数据 - * @param string $indexKey 指定比较的键名 + * @param mixed $items 数据 + * @param string|null $indexKey 指定比较的键名 * @return static */ public function diff($items, string $indexKey = null) @@ -239,8 +244,8 @@ class Collection extends BaseCollection * 比较数据集,返回交集 * * @access public - * @param mixed $items 数据 - * @param string $indexKey 指定比较的键名 + * @param mixed $items 数据 + * @param string|null $indexKey 指定比较的键名 * @return static */ public function intersect($items, string $indexKey = null) -- Gitee From 622a143bf146821f864d301256edbd288c3bcfb2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 26 Feb 2022 23:16:19 +0800 Subject: [PATCH 330/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3field=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 960bcc0..56bd1b7 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -350,7 +350,7 @@ abstract class BaseQuery $field = array_merge((array) $this->options['field'], $field); } - $this->options['field'] = array_unique($field); + $this->options['field'] = array_unique($field, SORT_REGULAR); return $this; } -- Gitee From ef7e15f11c89d69c8182743806d2973a2d2a48a2 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sat, 26 Feb 2022 23:19:03 +0800 Subject: [PATCH 331/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=85=B6=E5=AE=83fie?= =?UTF-8?q?ld=E7=9B=B8=E5=85=B3=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 56bd1b7..0880add 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -379,7 +379,7 @@ abstract class BaseQuery $field = array_merge((array) $this->options['field'], $field); } - $this->options['field'] = array_unique($field); + $this->options['field'] = array_unique($field, SORT_REGULAR); return $this; } @@ -424,7 +424,7 @@ abstract class BaseQuery $field = array_merge((array) $this->options['field'], $field); } - $this->options['field'] = array_unique($field); + $this->options['field'] = array_unique($field, SORT_REGULAR); return $this; } -- Gitee From 06783eda65547a70ea686360a897759e1f873fff Mon Sep 17 00:00:00 2001 From: thinkphp Date: Mon, 28 Feb 2022 22:54:22 +0800 Subject: [PATCH 332/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3Raw=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/Builder.php | 2 ++ src/db/concern/WhereQuery.php | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/db/Builder.php b/src/db/Builder.php index 06e689f..938ed4d 100644 --- a/src/db/Builder.php +++ b/src/db/Builder.php @@ -703,6 +703,8 @@ abstract class Builder // 比较运算 if ($value instanceof Closure) { $value = $this->parseClosure($query, $value); + } elseif ($value instanceof Raw) { + $value = $this->parseRaw($query, $value); } if ('=' == $exp && is_null($value)) { diff --git a/src/db/concern/WhereQuery.php b/src/db/concern/WhereQuery.php index ef845f5..5f2ed47 100644 --- a/src/db/concern/WhereQuery.php +++ b/src/db/concern/WhereQuery.php @@ -374,7 +374,9 @@ trait WhereQuery } elseif ($field instanceof Closure) { $where = $field; } elseif (is_string($field)) { - if (preg_match('/[,=\<\'\"\(\s]/', $field)) { + if ($condition instanceof Raw) { + + } elseif (preg_match('/[,=\<\'\"\(\s]/', $field)) { return $this->whereRaw($field, is_array($op) ? $op : [], $logic); } elseif (is_string($op) && strtolower($op) == 'exp' && !is_null($condition)) { $bind = isset($param[2]) && is_array($param[2]) ? $param[2] : []; -- Gitee From 91d038921c137bad16a31e48aaef09ef1e4debd6 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Thu, 10 Mar 2022 12:09:55 +0800 Subject: [PATCH 333/365] =?UTF-8?q?OneToOne=E5=A2=9E=E5=8A=A0make=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/OneToOne.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index d47d7f3..ba51753 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -186,18 +186,30 @@ abstract class OneToOne extends Relation * @return Model|false */ public function save($data, bool $replace = true) + { + $model = $this->make(); + + return $model->replace($replace)->save($data) ? $model : false; + } + + /** + * 创建关联对象实例 + * @param array|Model $data + * @return Model + */ + public function make($data = []): Model { if ($data instanceof Model) { $data = $data->getData(); } - $model = new $this->model; // 保存关联表数据 $data[$this->foreignKey] = $this->parent->{$this->localKey}; - return $model->replace($replace)->save($data) ? $model : false; + return new $this->model($data); } + /** * 绑定关联表的属性到父模型属性 * @access public -- Gitee From 1ea520fd9677f9ac8a89203fe446271b396a0751 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Thu, 10 Mar 2022 22:59:43 +0800 Subject: [PATCH 334/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E4=B8=80=E5=AF=B9?= =?UTF-8?q?=E4=B8=80=E5=85=B3=E8=81=94=E7=9A=84=E5=B1=9E=E6=80=A7=E7=BB=91?= =?UTF-8?q?=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsTo.php | 2 ++ src/model/relation/HasOne.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 0802b11..52663fb 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -241,6 +241,7 @@ class BelongsTo extends OneToOne if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); + $result->hidden($relation); } } } @@ -282,6 +283,7 @@ class BelongsTo extends OneToOne if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); + $result->hidden($relation); } } diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 269f0d7..2d2e4cb 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -240,6 +240,7 @@ class HasOne extends OneToOne if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); + $result->hidden($relation); } } } @@ -281,6 +282,7 @@ class HasOne extends OneToOne if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); + $result->hidden($relation); } } -- Gitee From 2959b8271e79af5d33ba2ac052f0027e775351f9 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 15 Mar 2022 11:36:03 +0800 Subject: [PATCH 335/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Conversion.php | 27 +++++++++++++++++++++------ src/model/relation/BelongsTo.php | 4 ++-- src/model/relation/HasOne.php | 4 ++-- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index 22d6256..da4391f 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -82,11 +82,16 @@ trait Conversion * 设置需要附加的输出属性 * @access public * @param array $append 属性列表 + * @param bool $merge 是否合并 * @return $this */ - public function append(array $append = []) + public function append(array $append = [], bool $merge = true) { - $this->append = $append; + if ($merge) { + $this->append = array_merge($this->append, $append); + } else { + $this->append = $append; + } return $this; } @@ -147,11 +152,16 @@ trait Conversion * 设置需要隐藏的输出属性 * @access public * @param array $hidden 属性列表 + * @param bool $merge 是否合并 * @return $this */ - public function hidden(array $hidden = []) + public function hidden(array $hidden = [], bool $merge = true) { - $this->hidden = $hidden; + if ($merge) { + $this->hidden = array_merge($this->hidden, $hidden); + } else { + $this->hidden = $hidden; + } return $this; } @@ -160,11 +170,16 @@ trait Conversion * 设置需要输出的属性 * @access public * @param array $visible + * @param bool $merge 是否合并 * @return $this */ - public function visible(array $visible = []) + public function visible(array $visible = [], bool $merge = true) { - $this->visible = $visible; + if ($merge) { + $this->visible = array_merge($this->visible, $visible); + } else { + $this->visible = $visible; + } return $this; } diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index 52663fb..dd54f52 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -241,7 +241,7 @@ class BelongsTo extends OneToOne if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); - $result->hidden($relation); + $result->hidden([$relation]); } } } @@ -283,7 +283,7 @@ class BelongsTo extends OneToOne if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); - $result->hidden($relation); + $result->hidden([$relation]); } } diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 2d2e4cb..43f220c 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -240,7 +240,7 @@ class HasOne extends OneToOne if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); - $result->hidden($relation); + $result->hidden([$relation]); } } } @@ -282,7 +282,7 @@ class HasOne extends OneToOne if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); - $result->hidden($relation); + $result->hidden([$relation]); } } -- Gitee From ccdbf7e991c689716833e0d72213944c20434fa1 Mon Sep 17 00:00:00 2001 From: augushong Date: Mon, 21 Mar 2022 15:07:25 +0800 Subject: [PATCH 336/365] =?UTF-8?q?=E5=BD=93count=E4=B8=8Egroup=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E4=BD=BF=E7=94=A8=E6=97=B6,=E5=AF=B9count=E4=BC=A0?= =?UTF-8?q?=E5=85=A5=E5=AD=97=E6=AE=B5=E5=81=9A=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/AggregateQuery.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/db/concern/AggregateQuery.php b/src/db/concern/AggregateQuery.php index dabfb92..0eab363 100644 --- a/src/db/concern/AggregateQuery.php +++ b/src/db/concern/AggregateQuery.php @@ -12,6 +12,7 @@ declare (strict_types = 1); namespace think\db\concern; +use think\db\exception\DbException; use think\db\Raw; /** @@ -42,6 +43,11 @@ trait AggregateQuery { if (!empty($this->options['group'])) { // 支持GROUP + + if (!preg_match('/^[\w\.\*]+$/', $field)) { + throw new DbException('not support data:' . $field); + } + $options = $this->getOptions(); $subSql = $this->options($options) ->field('count(' . $field . ') AS think_count') -- Gitee From 1b00ad225f009466c61a1c954abc48b0107286c1 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Sun, 3 Apr 2022 23:12:17 +0800 Subject: [PATCH 337/365] =?UTF-8?q?=E8=BD=AC=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/PDOConnection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php index 77a0b01..1748d20 100644 --- a/src/db/PDOConnection.php +++ b/src/db/PDOConnection.php @@ -1274,7 +1274,7 @@ abstract class PDOConnection extends Connection $type = is_array($val) ? $val[1] : PDO::PARAM_STR; if (self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) { - $value = '\'' . addcslashes($value, "'") . '\''; + $value = '\'' . addslashes($value) . '\''; } elseif (PDO::PARAM_INT == $type && '' === $value) { $value = '0'; } -- Gitee From 11c52fdb816f5ec59dbe0057654db9f3cbf8a2d6 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Tue, 12 Apr 2022 13:16:43 +0800 Subject: [PATCH 338/365] =?UTF-8?q?=E5=A4=9A=E5=AF=B9=E5=A4=9A=E5=85=B3?= =?UTF-8?q?=E8=81=94=E4=B8=AD=E9=97=B4=E8=A1=A8=E6=94=AF=E6=8C=81=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Pivot.php | 23 ++++++++++++++++++++--- src/model/relation/BelongsToMany.php | 7 ++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/model/Pivot.php b/src/model/Pivot.php index 893c01b..ac16d9e 100644 --- a/src/model/Pivot.php +++ b/src/model/Pivot.php @@ -35,9 +35,9 @@ class Pivot extends Model /** * 架构函数 * @access public - * @param array $data 数据 - * @param Model $parent 上级模型 - * @param string $table 中间数据表名 + * @param array $data 数据 + * @param Model|null $parent 上级模型 + * @param string $table 中间数据表名 */ public function __construct(array $data = [], Model $parent = null, string $table = '') { @@ -50,4 +50,21 @@ class Pivot extends Model parent::__construct($data); } + /** + * 创建新的模型实例 + * @access public + * @param array $data 数据 + * @param mixed $where 更新条件 + * @param array $options 参数 + * @return Model + */ + public function newInstance(array $data = [], $where = null, array $options = []): Model + { + $model = parent::newInstance($data, $where, $options); + + $model->parent = $this->parent; + $model->name = $this->name; + + return $model; + } } diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 9890906..6049f01 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -157,7 +157,12 @@ class BelongsToMany extends Relation } } - $result->setRelation($this->pivotDataName, $this->newPivot($pivot)); + $pivotData = $this->pivot->newInstance($pivot, [ + [$this->localKey, '=', $this->parent->getKey(), null], + [$this->foreignKey, '=', $result->getKey(), null], + ]); + + $result->setRelation($this->pivotDataName, $pivotData); return $pivot; } -- Gitee From d267daf92f8e9a7326800c2761ff5e2df242f541 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 3 May 2022 11:41:36 +0800 Subject: [PATCH 339/365] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E9=9B=86=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1hidden=20visible=20append=E6=96=B9=E6=B3=95=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0merge=E5=8F=82=E6=95=B0=20=E8=B0=83=E6=95=B4merge?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E9=BB=98=E8=AE=A4=E5=80=BC=E4=B8=BAfalse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Collection.php | 21 ++++++++++++--------- src/model/concern/Conversion.php | 6 +++--- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/model/Collection.php b/src/model/Collection.php index 9d31e20..079e856 100644 --- a/src/model/Collection.php +++ b/src/model/Collection.php @@ -81,12 +81,13 @@ class Collection extends BaseCollection * 设置需要隐藏的输出属性 * @access public * @param array $hidden 属性列表 + * @param bool $merge 是否合并 * @return $this */ - public function hidden(array $hidden) + public function hidden(array $hidden, bool $merge = false) { - $this->each(function (Model $model) use ($hidden) { - $model->hidden($hidden); + $this->each(function (Model $model) use ($hidden, $merge) { + $model->hidden($hidden, $merge); }); return $this; @@ -96,12 +97,13 @@ class Collection extends BaseCollection * 设置需要输出的属性 * @access public * @param array $visible + * @param bool $merge 是否合并 * @return $this */ - public function visible(array $visible) + public function visible(array $visible, bool $merge = false) { - $this->each(function (Model $model) use ($visible) { - $model->visible($visible); + $this->each(function (Model $model) use ($visible, $merge) { + $model->visible($visible, $merge); }); return $this; @@ -111,12 +113,13 @@ class Collection extends BaseCollection * 设置需要追加的输出属性 * @access public * @param array $append 属性列表 + * @param bool $merge 是否合并 * @return $this */ - public function append(array $append) + public function append(array $append, bool $merge = false) { - $this->each(function (Model $model) use ($append) { - $model->append($append); + $this->each(function (Model $model) use ($append, $merge) { + $model->append($append, $merge); }); return $this; diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php index da4391f..b584ba9 100644 --- a/src/model/concern/Conversion.php +++ b/src/model/concern/Conversion.php @@ -85,7 +85,7 @@ trait Conversion * @param bool $merge 是否合并 * @return $this */ - public function append(array $append = [], bool $merge = true) + public function append(array $append = [], bool $merge = false) { if ($merge) { $this->append = array_merge($this->append, $append); @@ -155,7 +155,7 @@ trait Conversion * @param bool $merge 是否合并 * @return $this */ - public function hidden(array $hidden = [], bool $merge = true) + public function hidden(array $hidden = [], bool $merge = false) { if ($merge) { $this->hidden = array_merge($this->hidden, $hidden); @@ -173,7 +173,7 @@ trait Conversion * @param bool $merge 是否合并 * @return $this */ - public function visible(array $visible = [], bool $merge = true) + public function visible(array $visible = [], bool $merge = false) { if ($merge) { $this->visible = array_merge($this->visible, $visible); -- Gitee From 3f1458f7e7d733444fff7877e418ed51c5090e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=AC=E9=A3=8E=E5=90=B9=E9=9B=A8?= <396751927@qq.com> Date: Sun, 10 Apr 2022 16:10:06 +0800 Subject: [PATCH 340/365] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5e86ac8..8e4a9ca 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "ext-json": "*", "ext-pdo": "*", "psr/simple-cache": "^1.0", - "psr/log": "~1.0", + "psr/log": "^1.0|^2.0", "topthink/think-helper":"^3.1" }, "require-dev": { -- Gitee From e74640cb9272b1f9397206afbc88992d9a9ab6ce Mon Sep 17 00:00:00 2001 From: yuanzhihai <396751927@qq.com> Date: Tue, 3 May 2022 15:26:31 +0800 Subject: [PATCH 341/365] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20psr/log=20=20psr/s?= =?UTF-8?q?imple-cache=20=E5=85=BC=E5=AE=B9=202.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8e4a9ca..097d924 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ "php": ">=7.1.0", "ext-json": "*", "ext-pdo": "*", - "psr/simple-cache": "^1.0", + "psr/simple-cache": "^1.0|^2.0", "psr/log": "^1.0|^2.0", "topthink/think-helper":"^3.1" }, -- Gitee From a1a952e5822b2d6ebda73c10832dcef3be618888 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 19 May 2022 11:18:21 +0800 Subject: [PATCH 342/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=8C=87=E5=AE=9A=E7=BC=93=E5=AD=98=E6=A0=87?= =?UTF-8?q?=E8=AF=86=E7=9A=84=E5=87=BA=E9=94=99=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 0880add..1434e63 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -628,7 +628,7 @@ abstract class BaseQuery if (!isset($total) && !$simple) { $options = $this->getOptions(); - unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']); + unset($this->options['order'], $this->options['cache'], $this->options['limit'], $this->options['page'], $this->options['field']); $bind = $this->bind; $total = $this->count(); -- Gitee From 5c37eecf0e05563a0745aedeb476c05f7f51a6e1 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Wed, 25 May 2022 18:17:41 +0800 Subject: [PATCH 343/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BpaginateX=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/BaseQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php index 1434e63..460a730 100644 --- a/src/db/BaseQuery.php +++ b/src/db/BaseQuery.php @@ -705,7 +705,7 @@ abstract class BaseQuery ->limit(1) ->find(); - $result = $data[$key]; + $result = $data[$key] ?? 0; if (is_numeric($result)) { $lastId = 'asc' == $sort ? ($result - 1) + ($page - 1) * $listRows : ($result + 1) - ($page - 1) * $listRows; -- Gitee From c82d4a3d42fca18cd1c6da9e1fa48cb11d22de0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=92=8B=E6=96=87=E5=81=A5?= Date: Thu, 26 May 2022 10:34:29 +0800 Subject: [PATCH 344/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=A4=9A=E6=80=81=E5=85=B3=E8=81=94=E5=A4=96=E9=94=AE/?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E5=90=8D/=E7=B1=BB=E5=9E=8B=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/MorphMany.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/model/relation/MorphMany.php b/src/model/relation/MorphMany.php index 82910cb..39209ae 100644 --- a/src/model/relation/MorphMany.php +++ b/src/model/relation/MorphMany.php @@ -30,6 +30,7 @@ class MorphMany extends Relation * @var string */ protected $morphKey; + /** * 多态字段名 * @var string @@ -331,6 +332,33 @@ class MorphMany extends Relation return empty($result) ? false : $result; } + /** + * 获取多态关联外键 + * @return string + */ + public function getMorphKey() + { + return $this->morphKey; + } + + /** + * 获取多态字段名 + * @return string + */ + public function getMorphType() + { + return $this->morphType; + } + + /** + * 获取多态类型 + * @return string + */ + public function getType() + { + return $this->type; + } + /** * 执行基础查询(仅执行一次) * @access protected -- Gitee From 9b81b359f9d4606910f5fc9a11e9f1621fb045cb Mon Sep 17 00:00:00 2001 From: Tiger <75706802@qq.com> Date: Fri, 17 Jun 2022 11:08:25 +0800 Subject: [PATCH 345/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E6=98=AF=E5=90=A6=E4=BF=A1=E4=BB=BB=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E8=87=AA=E5=BB=BA=E8=AF=81=E4=B9=A6=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/connector/Sqlsrv.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/db/connector/Sqlsrv.php b/src/db/connector/Sqlsrv.php index 10d944f..aee3343 100644 --- a/src/db/connector/Sqlsrv.php +++ b/src/db/connector/Sqlsrv.php @@ -44,6 +44,10 @@ class Sqlsrv extends PDOConnection $dsn .= ',' . $config['hostport']; } + if (!empty($config['trust_server_certificate'])) { + $dsn .= ';TrustServerCertificate=' . $config['trust_server_certificate']; + } + return $dsn; } -- Gitee From cf17218123e530418ff84493f8e7cf3bdcdf39e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A2=A8=E5=A8=98?= Date: Thu, 16 Jun 2022 18:11:29 +0800 Subject: [PATCH 346/365] =?UTF-8?q?feat=20=E5=A4=9A=E6=80=81=E5=85=B3?= =?UTF-8?q?=E8=81=94=E6=94=AF=E6=8C=81=E4=BD=BF=E7=94=A8=E9=97=AD=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/MorphTo.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index eaa9890..599b8ca 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -208,6 +208,9 @@ class MorphTo extends Relation // 多态类型映射 $model = $this->parseModel($key); $obj = new $model; + if (!\is_null($closure)) { + $obj = $closure($obj); + } $pk = $obj->getPk(); $list = $obj->with($subRelation) ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) -- Gitee From a15a5f3d40042bc6d3bf2f3fc6bb0cf6026d28c3 Mon Sep 17 00:00:00 2001 From: Tiger <75706802@qq.com> Date: Fri, 24 Jun 2022 10:17:20 +0800 Subject: [PATCH 347/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/builder/Oracle.php | 40 ++++++++++++++++++++++++++++++++++--- src/db/connector/Oracle.php | 14 +++++++------ 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/db/builder/Oracle.php b/src/db/builder/Oracle.php index 77ad3c8..38feb5d 100644 --- a/src/db/builder/Oracle.php +++ b/src/db/builder/Oracle.php @@ -12,6 +12,8 @@ namespace think\db\builder; use think\db\Builder; use think\db\Query; +use think\db\exception\DbException as Exception; +use think\db\Raw; /** * Oracle数据库驱动 @@ -64,19 +66,51 @@ class Oracle extends Builder /** * 字段和表名处理 * @access public - * @param Query $query 查询对象 - * @param string $key - * @param string $strict + * @param Query $query 查询对象 + * @param string $key + * @param bool $strict * @return string + * @throws Exception */ public function parseKey(Query $query, $key, bool $strict = false): string { + if (is_int($key)) { + return (string) $key; + } elseif ($key instanceof Raw) { + return $this->parseRaw($query, $key); + } + $key = trim($key); if (strpos($key, '->') && false === strpos($key, '(')) { // JSON字段支持 [$field, $name] = explode($key, '->'); $key = $field . '."' . $name . '"'; + } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) { + [$table, $key] = explode('.', $key, 2); + + $alias = $query->getOptions('alias'); + + if ('__TABLE__' == $table) { + $table = $query->getOptions('table'); + $table = is_array($table) ? array_shift($table) : $table; + } + + if (isset($alias[$table])) { + $table = $alias[$table]; + } + } + + if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) { + throw new Exception('not support data:' . $key); + } + + if ('*' != $key && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) { + $key = '"' . $key . '"'; + } + + if (isset($table)) { + $key = '"' . $table . '".' . $key; } return $key; diff --git a/src/db/connector/Oracle.php b/src/db/connector/Oracle.php index 236d8bf..024e4da 100644 --- a/src/db/connector/Oracle.php +++ b/src/db/connector/Oracle.php @@ -51,7 +51,7 @@ class Oracle extends PDOConnection public function getFields(string $tableName): array { [$tableName] = explode(' ', $tableName); - $sql = "select a.column_name,data_type,DECODE (nullable, 'Y', 0, 1) notnull,data_default, DECODE (A .column_name,b.column_name,1,0) pk from all_tab_columns a,(select column_name from all_constraints c, all_cons_columns col where c.constraint_name = col.constraint_name and c.constraint_type = 'P' and c.table_name = '" . strtoupper($tableName) . "' ) b where table_name = '" . strtoupper($tableName) . "' and a.column_name = b.column_name (+)"; + $sql = "select a.column_name,data_type,DECODE (nullable, 'Y', 0, 1) notnull,data_default, DECODE (A .column_name,b.column_name,1,0) pk from all_tab_columns a,(select column_name from all_constraints c, all_cons_columns col where c.constraint_name = col.constraint_name and c.constraint_type = 'P' and c.table_name = '" . $tableName . "' ) b where table_name = '" . $tableName . "' and a.column_name = b.column_name (+)"; $pdo = $this->getPDOStatement($sql); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); @@ -98,16 +98,18 @@ class Oracle extends PDOConnection /** * 获取最近插入的ID * @access public - * @param BaseQuery $query 查询对象 - * @param string $sequence 自增序列名 + * @param BaseQuery $query 查询对象 + * @param string|null $sequence 自增序列名 * @return mixed */ public function getLastInsID(BaseQuery $query, string $sequence = null) { - $pdo = $this->linkID->query("select {$sequence}.currval as id from dual"); - $result = $pdo->fetchColumn(); + if(is_null($sequence)) { + $pdo = $this->linkID->query("select {$sequence}.currval as id from dual"); + $result = $pdo->fetchColumn(); + } - return $result; + return $result ?? null; } protected function supportSavepoint(): bool -- Gitee From 140619a959c3bee32972ee1aacaf456125b0866b Mon Sep 17 00:00:00 2001 From: season886 <75706802@qq.com> Date: Thu, 30 Jun 2022 13:44:36 +0800 Subject: [PATCH 348/365] Update Oracle.php --- src/db/connector/Oracle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/connector/Oracle.php b/src/db/connector/Oracle.php index 024e4da..c8e957a 100644 --- a/src/db/connector/Oracle.php +++ b/src/db/connector/Oracle.php @@ -104,7 +104,7 @@ class Oracle extends PDOConnection */ public function getLastInsID(BaseQuery $query, string $sequence = null) { - if(is_null($sequence)) { + if(!is_null($sequence)) { $pdo = $this->linkID->query("select {$sequence}.currval as id from dual"); $result = $pdo->fetchColumn(); } -- Gitee From 97b061b47616301ff29fbd4c35ed9184e1162e4e Mon Sep 17 00:00:00 2001 From: OHOM Date: Fri, 29 Apr 2022 19:44:47 +0800 Subject: [PATCH 349/365] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20=E5=A4=9A=E6=80=81?= =?UTF-8?q?=E5=85=B3=E7=B3=BB=E7=9A=84=E6=A8=A1=E5=9E=8B=E5=90=8D=E6=98=A0?= =?UTF-8?q?=E5=B0=84=E5=88=AB=E5=90=8D=E7=9A=84=E6=95=B0=E7=BB=84$morphMap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加 多态关系的模型名映射别名的数组$morphMap --- src/model/relation/MorphToMany.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/model/relation/MorphToMany.php b/src/model/relation/MorphToMany.php index 32f853f..c566488 100644 --- a/src/model/relation/MorphToMany.php +++ b/src/model/relation/MorphToMany.php @@ -24,6 +24,13 @@ use think\model\Pivot; class MorphToMany extends BelongsToMany { + /** + * 多态关系的模型名映射别名的数组 + * + * @var array + */ + protected static $morphMap = []; + /** * 多态字段名 * @var string @@ -58,6 +65,9 @@ class MorphToMany extends BelongsToMany $this->morphType = $morphType; $this->inverse = $inverse; $this->morphClass = $inverse ? $model : get_class($parent); + if (isset(static::$morphMap[$this->morphClass])) { + $this->morphClass = static::$morphMap[$this->morphClass]; + } $foreignKey = $inverse ? $morphKey : $localKey; $localKey = $inverse ? $localKey : $morphKey; @@ -460,4 +470,21 @@ class MorphToMany extends BelongsToMany } } + /** + * 设置或获取多态关系的模型名映射别名的数组 + * + * @param array|null $map + * @param bool $merge + * @return array + */ + public static function morphMap(array $map = null, $merge = true): array + { + if (is_array($map)) { + static::$morphMap = $merge && static::$morphMap + ? $map + static::$morphMap : $map; + } + + return static::$morphMap; + } + } -- Gitee From 00d8299b2323dd003f74b7ab0d325e92ad6dec9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E6=99=AF=E7=AB=8B?= Date: Mon, 18 Jul 2022 16:22:56 +0800 Subject: [PATCH 350/365] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20with&bind=20?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E5=A4=9A=E6=AC=A1=E5=AF=BC=E5=85=A5=E5=89=8D?= =?UTF-8?q?=E9=9D=A2=E7=9A=84=20hidden=20=E8=A2=AB=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/BelongsTo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php index dd54f52..741a2e8 100644 --- a/src/model/relation/BelongsTo.php +++ b/src/model/relation/BelongsTo.php @@ -241,7 +241,7 @@ class BelongsTo extends OneToOne if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); - $result->hidden([$relation]); + $result->hidden([$relation], true); } } } @@ -283,7 +283,7 @@ class BelongsTo extends OneToOne if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); - $result->hidden([$relation]); + $result->hidden([$relation], true); } } -- Gitee From e1bf3627b85d4c2e3343f88869a77e561432d5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E6=99=AF=E7=AB=8B?= Date: Mon, 18 Jul 2022 16:25:27 +0800 Subject: [PATCH 351/365] =?UTF-8?q?=20=E4=BF=AE=E5=A4=8D=20with&bind=20?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E5=A4=9A=E6=AC=A1=E5=AF=BC=E8=87=B3=E5=89=8D?= =?UTF-8?q?=E9=9D=A2=E7=9A=84=20hidden=20=E8=A2=AB=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/HasOne.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php index 43f220c..5e5e501 100644 --- a/src/model/relation/HasOne.php +++ b/src/model/relation/HasOne.php @@ -240,7 +240,7 @@ class HasOne extends OneToOne if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); - $result->hidden([$relation]); + $result->hidden([$relation], true); } } } @@ -282,7 +282,7 @@ class HasOne extends OneToOne if (!empty($this->bindAttr)) { // 绑定关联属性 $this->bindAttr($result, $relationModel); - $result->hidden([$relation]); + $result->hidden([$relation], true); } } -- Gitee From 0cd1f2f43375d8ddd3e000c501df00a9552ed2cc Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Thu, 11 Aug 2022 19:47:34 +0800 Subject: [PATCH 352/365] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E8=BD=AF=E5=88=A0?= =?UTF-8?q?=E9=99=A4=EF=BC=8C=E6=94=AF=E6=8C=81=E5=8A=A8=E6=80=81=E8=B0=83?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 19 ----- src/model/concern/SoftDelete.php | 105 +++++++------------------- src/model/relation/MorphTo.php | 39 +++++++--- 3 files changed, 57 insertions(+), 106 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index 0015a24..cabd65b 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -559,23 +559,4 @@ trait ModelRelationQuery $result->refreshOrigin(); } - /** - * 查询软删除数据 - * @access public - * @return Query - */ - public function withTrashed() - { - return $this->model ? $this->model->queryWithTrashed() : $this; - } - - /** - * 只查询软删除数据 - * @access public - * @return Query - */ - public function onlyTrashed() - { - return $this->model ? $this->model->queryOnlyTrashed() : $this; - } } diff --git a/src/model/concern/SoftDelete.php b/src/model/concern/SoftDelete.php index 117f1ef..b7b1092 100644 --- a/src/model/concern/SoftDelete.php +++ b/src/model/concern/SoftDelete.php @@ -18,14 +18,18 @@ use think\Model; /** * 数据软删除 * @mixin Model + * @method $this withTrashed() + * @method $this onlyTrashed() */ trait SoftDelete { - /** - * 是否包含软删除数据 - * @var bool - */ - protected $withTrashed = false; + + public function db($scope = []): Query + { + $query = parent::db($scope); + $this->withNoTrashed($query); + return $query; + } /** * 判断当前实例是否被软删除 @@ -43,74 +47,18 @@ trait SoftDelete return false; } - /** - * 查询软删除数据 - * @access public - * @return Query - */ - public static function withTrashed(): Query + public function scopeWithTrashed(Query $query) { - $model = new static(); - - return $model->withTrashedData(true)->db(); + $query->removeOption('soft_delete'); } - /** - * 查询软删除数据 - * @access public - * @return Query - */ - public function queryWithTrashed(): Query - { - return $this->withTrashedData(true)->db(); - } - - /** - * 是否包含软删除数据 - * @access protected - * @param bool $withTrashed 是否包含软删除数据 - * @return $this - */ - protected function withTrashedData(bool $withTrashed) - { - $this->withTrashed = $withTrashed; - return $this; - } - - /** - * 只查询软删除数据 - * @access public - * @return Query - */ - public static function onlyTrashed(): Query - { - $model = new static(); - $field = $model->getDeleteTimeField(true); - - if ($field) { - return $model - ->db() - ->useSoftDelete($field, $model->getWithTrashedExp()); - } - - return $model->db(); - } - - /** - * 只查询软删除数据 - * @access public - * @return Query - */ - public function queryOnlyTrashed(): Query + public function scopeOnlyTrashed(Query $query) { $field = $this->getDeleteTimeField(true); if ($field) { - return $this->db() - ->useSoftDelete($field, $this->getWithTrashedExp()); + $query->useSoftDelete($field, $this->getWithTrashedExp()); } - - return $this->db(); } /** @@ -139,9 +87,9 @@ trait SoftDelete if ($name && !$force) { // 软删除 - $this->set($name, $this->autoWriteTimestamp($name)); + $this->set($name, $this->autoWriteTimestamp()); - $result = $this->exists()->withEvent(false)->save(); + $this->exists()->withEvent(false)->save(); $this->withEvent(true); } else { @@ -149,7 +97,7 @@ trait SoftDelete $where = $this->getWhere(); // 删除当前模型数据 - $result = $this->db() + $this->db() ->where($where) ->removeOption('soft_delete') ->delete(); @@ -172,8 +120,8 @@ trait SoftDelete /** * 删除记录 * @access public - * @param mixed $data 主键列表 支持闭包查询条件 - * @param bool $force 是否强制删除 + * @param mixed $data 主键列表 支持闭包查询条件 + * @param bool $force 是否强制删除 * @return bool */ public static function destroy($data, bool $force = false): bool @@ -182,18 +130,20 @@ trait SoftDelete if (empty($data) && 0 !== $data) { return false; } - // 仅当强制删除时包含软删除数据 $model = (new static()); + + $query = $model->db(false); + + // 仅当强制删除时包含软删除数据 if ($force) { - $model->withTrashedData(true); + $query->removeOption('soft_delete'); } - $query = $model->db(false); if (is_array($data) && key($data) !== 0) { $query->where($data); $data = null; } elseif ($data instanceof \Closure) { - call_user_func_array($data, [ & $query]); + call_user_func_array($data, [&$query]); $data = null; } elseif (is_null($data)) { return false; @@ -202,6 +152,7 @@ trait SoftDelete $resultSet = $query->select($data); foreach ($resultSet as $result) { + /** @var Model $result */ $result->force($force)->delete(); } @@ -211,7 +162,7 @@ trait SoftDelete /** * 恢复被软删除的记录 * @access public - * @param array $where 更新条件 + * @param array $where 更新条件 * @return bool */ public function restore($where = []): bool @@ -243,7 +194,7 @@ trait SoftDelete /** * 获取软删除字段 * @access protected - * @param bool $read 是否查询操作 写操作的时候会自动去掉表别名 + * @param bool $read 是否查询操作 写操作的时候会自动去掉表别名 * @return string|false */ protected function getDeleteTimeField(bool $read = false) @@ -269,7 +220,7 @@ trait SoftDelete /** * 查询的时候默认排除软删除数据 * @access protected - * @param Query $query + * @param Query $query * @return void */ protected function withNoTrashed(Query $query): void diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index 599b8ca..cd4eb80 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -13,6 +13,7 @@ namespace think\model\relation; use Closure; use think\db\exception\DbException as Exception; +use think\db\Query; use think\helper\Str; use think\Model; use think\model\Relation; @@ -46,6 +47,8 @@ class MorphTo extends Relation */ protected $relation; + protected $queryCaller = []; + /** * 架构函数 * @access public @@ -53,7 +56,7 @@ class MorphTo extends Relation * @param string $morphType 多态字段名 * @param string $morphKey 外键名 * @param array $alias 多态别名定义 - * @param string $relation 关联名 + * @param ?string $relation 关联名 */ public function __construct(Model $parent, string $morphType, string $morphKey, array $alias = [], string $relation = null) { @@ -80,8 +83,8 @@ class MorphTo extends Relation /** * 延迟获取关联数据 * @access public - * @param array $subRelation 子关联名 - * @param Closure $closure 闭包查询条件 + * @param array $subRelation 子关联名 + * @param ?Closure $closure 闭包查询条件 * @return Model */ public function getRelation(array $subRelation = [], Closure $closure = null) @@ -95,7 +98,7 @@ class MorphTo extends Relation // 主键数据 $pk = $this->parent->$morphKey; - $relationModel = (new $model)->relation($subRelation)->find($pk); + $relationModel = $this->buildQuery((new $model)->relation($subRelation))->find($pk); if ($relationModel) { $relationModel->setParent(clone $this->parent); @@ -125,7 +128,7 @@ class MorphTo extends Relation * @param mixed $where 查询条件(数组或者闭包) * @param mixed $fields 字段 * @param string $joinType JOIN类型 - * @param Query $query Query对象 + * @param ?Query $query Query对象 * @return Query */ public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null) @@ -137,7 +140,7 @@ class MorphTo extends Relation * 解析模型的完整命名空间 * @access protected * @param string $model 模型名(或者完整类名) - * @return string + * @return Model */ protected function parseModel(string $model): string { @@ -173,7 +176,7 @@ class MorphTo extends Relation * @access public * @return $this */ - public function removeOption() + public function removeOption(string $option = '') { return $this; } @@ -184,7 +187,7 @@ class MorphTo extends Relation * @param array $resultSet 数据集 * @param string $relation 当前关联名 * @param array $subRelation 子关联名 - * @param Closure $closure 闭包 + * @param ?Closure $closure 闭包 * @param array $cache 关联缓存 * @return void * @throws Exception @@ -245,7 +248,7 @@ class MorphTo extends Relation * @param Model $result 数据对象 * @param string $relation 当前关联名 * @param array $subRelation 子关联名 - * @param Closure $closure 闭包 + * @param ?Closure $closure 闭包 * @param array $cache 关联缓存 * @return void */ @@ -261,7 +264,7 @@ class MorphTo extends Relation * 关联统计 * @access public * @param Model $result 数据对象 - * @param Closure $closure 闭包 + * @param ?Closure $closure 闭包 * @param string $aggregate 聚合查询方法 * @param string $field 字段 * @return integer @@ -332,4 +335,20 @@ class MorphTo extends Relation return $this->parent->setRelation($this->relation, null); } + protected function buildQuery(Model $model) + { + $query = $model->db(); + + foreach ($this->queryCaller as $caller) { + call_user_func_array([$this->query, $caller[0]], $caller[1]); + } + + return $query; + } + + public function __call($method, $args) + { + $this->queryCaller[] = [$method, $args]; + return $this; + } } -- Gitee From 4a7082a36c910d3ce5c7dea7ec27ac9ee09b4ac1 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Thu, 11 Aug 2022 19:52:41 +0800 Subject: [PATCH 353/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/MorphTo.php | 85 +++++++++++++++++----------------- 1 file changed, 42 insertions(+), 43 deletions(-) diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index cd4eb80..1ef628b 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -52,11 +52,11 @@ class MorphTo extends Relation /** * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $morphType 多态字段名 - * @param string $morphKey 外键名 - * @param array $alias 多态别名定义 - * @param ?string $relation 关联名 + * @param Model $parent 上级模型对象 + * @param string $morphType 多态字段名 + * @param string $morphKey 外键名 + * @param array $alias 多态别名定义 + * @param ?string $relation 关联名 */ public function __construct(Model $parent, string $morphType, string $morphKey, array $alias = [], string $relation = null) { @@ -110,11 +110,11 @@ class MorphTo extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 - * @param Query $query Query对象 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 + * @param Query $query Query对象 * @return Query */ public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = '', Query $query = null) @@ -125,10 +125,10 @@ class MorphTo extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) - * @param mixed $fields 字段 - * @param string $joinType JOIN类型 - * @param ?Query $query Query对象 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @param string $joinType JOIN类型 + * @param ?Query $query Query对象 * @return Query */ public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null) @@ -139,7 +139,7 @@ class MorphTo extends Relation /** * 解析模型的完整命名空间 * @access protected - * @param string $model 模型名(或者完整类名) + * @param string $model 模型名(或者完整类名) * @return Model */ protected function parseModel(string $model): string @@ -161,7 +161,7 @@ class MorphTo extends Relation /** * 设置多态别名 * @access public - * @param array $alias 别名定义 + * @param array $alias 别名定义 * @return $this */ public function setAlias(array $alias) @@ -184,11 +184,11 @@ class MorphTo extends Relation /** * 预载入关联查询 * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param ?Closure $closure 闭包 - * @param array $cache 关联缓存 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param ?Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void * @throws Exception */ @@ -214,8 +214,8 @@ class MorphTo extends Relation if (!\is_null($closure)) { $obj = $closure($obj); } - $pk = $obj->getPk(); - $list = $obj->with($subRelation) + $pk = $obj->getPk(); + $list = $obj->with($subRelation) ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) ->select($val); $data = []; @@ -245,11 +245,11 @@ class MorphTo extends Relation /** * 预载入关联查询 * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param array $subRelation 子关联名 - * @param ?Closure $closure 闭包 - * @param array $cache 关联缓存 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param array $subRelation 子关联名 + * @param ?Closure $closure 闭包 + * @param array $cache 关联缓存 * @return void */ public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): void @@ -263,23 +263,24 @@ class MorphTo extends Relation /** * 关联统计 * @access public - * @param Model $result 数据对象 + * @param Model $result 数据对象 * @param ?Closure $closure 闭包 - * @param string $aggregate 聚合查询方法 - * @param string $field 字段 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 * @return integer */ public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*') - {} + { + } /** * 多态MorphTo 关联模型预查询 * @access protected - * @param string $model 关联模型对象 - * @param string $relation 关联名 - * @param Model $result - * @param array $subRelation 子关联 - * @param array $cache 关联缓存 + * @param string $model 关联模型对象 + * @param string $relation 关联名 + * @param Model $result + * @param array $subRelation 子关联 + * @param array $cache 关联缓存 * @return void */ protected function eagerlyMorphToOne(string $model, string $relation, Model $result, array $subRelation = [], array $cache = []): void @@ -301,8 +302,8 @@ class MorphTo extends Relation /** * 添加关联数据 * @access public - * @param Model $model 关联模型对象 - * @param string $type 多态类型 + * @param Model $model 关联模型对象 + * @param string $type 多态类型 * @return Model */ public function associate(Model $model, string $type = ''): Model @@ -335,12 +336,10 @@ class MorphTo extends Relation return $this->parent->setRelation($this->relation, null); } - protected function buildQuery(Model $model) + protected function buildQuery(Query $query) { - $query = $model->db(); - foreach ($this->queryCaller as $caller) { - call_user_func_array([$this->query, $caller[0]], $caller[1]); + call_user_func_array([$query, $caller[0]], $caller[1]); } return $query; -- Gitee From 9fe46c93362a8b857f188a3a1b8755f7c1905fac Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Mon, 22 Aug 2022 12:28:01 +0800 Subject: [PATCH 354/365] =?UTF-8?q?morphTo=E5=85=B3=E8=81=94=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E6=94=AF=E6=8C=81hasWhere=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/MorphTo.php | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index 1ef628b..046edaf 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -133,7 +133,30 @@ class MorphTo extends Relation */ public function hasWhere($where = [], $fields = null, string $joinType = '', Query $query = null) { - throw new Exception('relation not support: hasWhere'); + $alias = class_basename($this->parent); + + $types = $this->parent->distinct()->column($this->morphType); + + $query = $query ?: $this->parent->db(); + + return $query->alias($alias) + ->where(function (Query $query) use ($types, $where, $alias) { + foreach ($types as $type) { + if ($type) { + $query->whereExists(function (Query $query) use ($type, $where, $alias) { + /** @var Model $model */ + $model = new ($this->parseModel($type)); + + $table = $model->getTable(); + $query + ->table($table) + ->where($alias . '.' . $this->morphType, $type) + ->whereRaw("`{$alias}`.`{$this->morphKey}`=`{$table}`.`{$model->getPk()}`") + ->where($where); + }, 'OR'); + } + } + }); } /** -- Gitee From d86a204fc086817f61b9af386a990c891cd2ad55 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 15 Sep 2022 16:28:06 +0800 Subject: [PATCH 355/365] =?UTF-8?q?=E6=94=B9=E8=BF=9BsetAttr=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index 897d327..b14d25a 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -383,7 +383,7 @@ trait Attribute } elseif (isset($this->type[$name])) { // 类型转换 $value = $this->writeTransform($value, $this->type[$name]); - } elseif (array_key_exists($name, $this->origin) && is_object($value) && method_exists($value, '__toString')) { + } elseif ((array_key_exists($name, $this->origin) || empty($this->origin)) && is_object($value) && method_exists($value, '__toString')) { // 对象类型 $value = $value->__toString(); } -- Gitee From e1974a4c3b1b4c5b808fcc0863fc254e711dee13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E7=AB=8B=E6=B0=91?= <461353919@qq.com> Date: Mon, 18 Jul 2022 05:00:08 +0800 Subject: [PATCH 356/365] =?UTF-8?q?fix:=20=E5=A4=9A=E6=80=81=E5=85=B3?= =?UTF-8?q?=E8=81=94=E6=A8=A1=E5=9E=8B=E5=A4=9A=E6=80=81=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=9C=AA=E5=AE=9A=E4=B9=89=E6=97=B6=E6=8A=9B=E5=87=BA=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/MorphTo.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index 046edaf..27683b2 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -310,13 +310,18 @@ class MorphTo extends Relation { // 预载入关联查询 支持嵌套预载入 $pk = $this->parent->{$this->morphKey}; - $data = (new $model)->with($subRelation) - ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) - ->find($pk); - if ($data) { - $data->setParent(clone $result); - $data->exists(true); + $data = null; + + if(\class_exists($model)){ + $data = (new $model)->with($subRelation) + ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) + ->find($pk); + + if ($data) { + $data->setParent(clone $result); + $data->exists(true); + } } $result->setRelation($relation, $data ?: null); -- Gitee From ed6a7676d0422938d1b16baef59e8ca855ff0402 Mon Sep 17 00:00:00 2001 From: yunwuxin <448901948@qq.com> Date: Fri, 28 Oct 2022 23:59:04 +0800 Subject: [PATCH 357/365] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/MorphTo.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/model/relation/MorphTo.php b/src/model/relation/MorphTo.php index 27683b2..040f2c4 100644 --- a/src/model/relation/MorphTo.php +++ b/src/model/relation/MorphTo.php @@ -144,8 +144,9 @@ class MorphTo extends Relation foreach ($types as $type) { if ($type) { $query->whereExists(function (Query $query) use ($type, $where, $alias) { + $class = $this->parseModel($type); /** @var Model $model */ - $model = new ($this->parseModel($type)); + $model = new $class(); $table = $model->getTable(); $query @@ -309,11 +310,11 @@ class MorphTo extends Relation protected function eagerlyMorphToOne(string $model, string $relation, Model $result, array $subRelation = [], array $cache = []): void { // 预载入关联查询 支持嵌套预载入 - $pk = $this->parent->{$this->morphKey}; + $pk = $this->parent->{$this->morphKey}; $data = null; - if(\class_exists($model)){ + if (\class_exists($model)) { $data = (new $model)->with($subRelation) ->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null) ->find($pk); -- Gitee From 91693ac4ee20f3d24d1ec8656aa411d041e37d64 Mon Sep 17 00:00:00 2001 From: ichynul Date: Wed, 12 Oct 2022 18:42:41 +0800 Subject: [PATCH 358/365] =?UTF-8?q?=E6=B7=BB=E5=8A=A0getInstance=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E6=89=80=E6=9C=89=E8=BF=9E=E6=8E=A5=E5=AE=9E=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 常驻内存型的框架长连接,需要精细控制每个连接 --- src/DbManager.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/DbManager.php b/src/DbManager.php index f223cd2..1c1314e 100644 --- a/src/DbManager.php +++ b/src/DbManager.php @@ -340,6 +340,16 @@ class DbManager { return $this->listen; } + + /** + * 获取所有连接实列 + * @access public + * @return array + */ + public function getInstance(): array + { + return $this->instance; + } /** * 注册回调方法 -- Gitee From 91000f88a880d0f4db464a895ac63fb8ee9167a0 Mon Sep 17 00:00:00 2001 From: Gang Wu Date: Tue, 30 Aug 2022 08:35:19 +0800 Subject: [PATCH 359/365] =?UTF-8?q?=E5=88=86=E9=A1=B5=E7=B1=BB=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20setCollection()=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Paginator.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Paginator.php b/src/Paginator.php index 2f755ef..eace6ba 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -377,6 +377,19 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J return $this->items; } + /** + * 设置数据集 + * + * @param Collection $items + * @return $this + */ + public function setCollection(Collection $items) + { + $this->items = $items; + + return $this; + } + public function isEmpty(): bool { return $this->items->isEmpty(); -- Gitee From 958b96c9c6cb3d6dfa24c670f5f86a47c34d44ba Mon Sep 17 00:00:00 2001 From: riverdance <514792926@qq.com> Date: Mon, 27 Jun 2022 23:00:45 +0800 Subject: [PATCH 360/365] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E4=B8=BA=200=20=E6=97=B6=EF=BC=8C=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=88=B0=E7=9A=84=E5=80=BC=E4=B8=BA=20null=20?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/TimeStamp.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/concern/TimeStamp.php b/src/model/concern/TimeStamp.php index 2492e06..9440c3e 100644 --- a/src/model/concern/TimeStamp.php +++ b/src/model/concern/TimeStamp.php @@ -179,7 +179,7 @@ trait TimeStamp protected function formatDateTime($format, $time = 'now', bool $timestamp = false) { if (empty($time)) { - return; + return $time; } if (false === $format) { -- Gitee From 75b8512736daaa056d511f42c15bed87c9f3605a Mon Sep 17 00:00:00 2001 From: augushong Date: Mon, 14 Nov 2022 16:47:52 +0800 Subject: [PATCH 361/365] =?UTF-8?q?=E4=B8=80=E5=AF=B9=E4=B8=80=E5=85=B3?= =?UTF-8?q?=E8=81=94=E9=A2=84=E6=9F=A5=E5=AF=BB=E6=94=AF=E6=8C=81=E5=85=B6?= =?UTF-8?q?=E4=BB=96=E5=85=B3=E8=81=94=E8=A1=A8=E5=AD=97=E6=AE=B5=E4=BD=9C?= =?UTF-8?q?=E4=B8=BA=E5=A4=96=E9=94=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/OneToOne.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index ba51753..b3eab59 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -91,9 +91,23 @@ abstract class OneToOne extends Relation $query->via($joinAlias); if ($this instanceof BelongsTo) { - $joinOn = $name . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey; + + $foreignKeyExp = $this->foreignKey; + + if (strpos($foreignKeyExp, '.') === false) { + $foreignKeyExp = $name . '.' . $this->foreignKey; + } + + $joinOn = $foreignKeyExp . '=' . $joinAlias . '.' . $this->localKey; } else { - $joinOn = $name . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey; + + $foreignKeyExp = $this->foreignKey; + + if (strpos($foreignKeyExp, '.') === false) { + $foreignKeyExp = $joinAlias . '.' . $this->foreignKey; + } + + $joinOn = $name . '.' . $this->localKey . '=' . $foreignKeyExp; } if ($closure) { -- Gitee From 091586062bb3c1c7460dab70f23df474904699f6 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 27 Jan 2023 12:17:28 +0800 Subject: [PATCH 362/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=B8=80=E5=AF=B9?= =?UTF-8?q?=E4=B8=80=E5=85=B3=E8=81=94=E5=8A=A8=E6=80=81=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E5=85=B3=E8=81=94=E5=B1=9E=E6=80=A7=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E5=85=B3=E8=81=94=E6=95=B0=E6=8D=AE=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E5=AD=98=E5=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/RelationShip.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php index 8faadf4..8e0d498 100644 --- a/src/model/concern/RelationShip.php +++ b/src/model/concern/RelationShip.php @@ -346,7 +346,7 @@ trait RelationShip */ public function bindAttr(string $relation, array $attrs = []) { - $relation = $this->getRelation($relation); + $relation = $this->getRelation($relation, true); foreach ($attrs as $key => $attr) { $key = is_numeric($key) ? $attr : $key; -- Gitee From 58faff9475c9426c7f76968cd2c5c8727bc34b68 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 27 Jan 2023 15:18:47 +0800 Subject: [PATCH 363/365] =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E7=B1=BBsetAttr?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E8=87=AA=E5=8A=A8=E5=88=A4=E6=96=AD=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E5=85=B3=E8=81=94=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/concern/Attribute.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php index b14d25a..561aef8 100644 --- a/src/model/concern/Attribute.php +++ b/src/model/concern/Attribute.php @@ -383,6 +383,8 @@ trait Attribute } elseif (isset($this->type[$name])) { // 类型转换 $value = $this->writeTransform($value, $this->type[$name]); + } elseif ($this->isRelationAttr($name)) { + $this->relation[$name] = $value; } elseif ((array_key_exists($name, $this->origin) || empty($this->origin)) && is_object($value) && method_exists($value, '__toString')) { // 对象类型 $value = $value->__toString(); -- Gitee From 3551948e16bcef97833835e921daa1322fb4c64f Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 27 Jan 2023 16:40:25 +0800 Subject: [PATCH 364/365] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=85=B3=E8=81=94?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=86=99=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/relation/HasMany.php | 4 ++-- src/model/relation/MorphOne.php | 4 ++-- src/model/relation/OneToOne.php | 7 +++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index 77d9e4d..97a62d4 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -241,9 +241,9 @@ class HasMany extends Relation */ public function save($data, bool $replace = true) { - $model = $this->make(); + $model = $this->make($data); - return $model->replace($replace)->save($data) ? $model : false; + return $model->replace($replace)->save() ? $model : false; } /** diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php index 788dd29..b5fd5a7 100644 --- a/src/model/relation/MorphOne.php +++ b/src/model/relation/MorphOne.php @@ -260,8 +260,8 @@ class MorphOne extends Relation */ public function save($data, bool $replace = true) { - $model = $this->make(); - return $model->replace($replace)->save($data) ? $model : false; + $model = $this->make($data); + return $model->replace($replace)->save() ? $model : false; } /** diff --git a/src/model/relation/OneToOne.php b/src/model/relation/OneToOne.php index b3eab59..035e579 100644 --- a/src/model/relation/OneToOne.php +++ b/src/model/relation/OneToOne.php @@ -100,7 +100,7 @@ abstract class OneToOne extends Relation $joinOn = $foreignKeyExp . '=' . $joinAlias . '.' . $this->localKey; } else { - + $foreignKeyExp = $this->foreignKey; if (strpos($foreignKeyExp, '.') === false) { @@ -201,9 +201,9 @@ abstract class OneToOne extends Relation */ public function save($data, bool $replace = true) { - $model = $this->make(); + $model = $this->make($data); - return $model->replace($replace)->save($data) ? $model : false; + return $model->replace($replace)->save() ? $model : false; } /** @@ -223,7 +223,6 @@ abstract class OneToOne extends Relation return new $this->model($data); } - /** * 绑定关联表的属性到父模型属性 * @access public -- Gitee From f31efc796a0d7b3d50b379074520fa5d12c0c45d Mon Sep 17 00:00:00 2001 From: thinkphp Date: Fri, 27 Jan 2023 17:16:47 +0800 Subject: [PATCH 365/365] =?UTF-8?q?Query=E7=B1=BB=E5=A2=9E=E5=8A=A0withLim?= =?UTF-8?q?it=E6=96=B9=E6=B3=95=E7=94=A8=E4=BA=8E=E9=99=90=E5=88=B6?= =?UTF-8?q?=E5=85=B3=E8=81=94=E6=95=B0=E6=8D=AE=E7=9A=84=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/concern/ModelRelationQuery.php | 12 ++++++++++++ src/model/relation/BelongsToMany.php | 11 +++++++---- src/model/relation/HasMany.php | 10 ++++++---- src/model/relation/HasManyThrough.php | 5 +++-- src/model/relation/MorphMany.php | 11 +++++++---- src/model/relation/MorphToMany.php | 13 ++++++++----- 6 files changed, 43 insertions(+), 19 deletions(-) diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php index cabd65b..74f6e6a 100644 --- a/src/db/concern/ModelRelationQuery.php +++ b/src/db/concern/ModelRelationQuery.php @@ -176,6 +176,18 @@ trait ModelRelationQuery return $this; } + /** + * 限制关联数据的数量 + * @access public + * @param int $limit 关联数量限制 + * @return $this + */ + public function withLimit(int $limit) + { + $this->options['with_limit'] = $limit; + return $this; + } + /** * 设置数据字段获取器 * @access public diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php index 6049f01..eb6812e 100644 --- a/src/model/relation/BelongsToMany.php +++ b/src/model/relation/BelongsToMany.php @@ -353,12 +353,14 @@ class BelongsToMany extends Relation ->select(); // 组装模型数据 - $data = []; + $data = []; + $withLimit = $this->withLimit ?: $this->query->getOptions('with_limit'); + foreach ($list as $set) { $pivot = $this->matchPivot($set); $key = $pivot[$this->localKey]; - if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { + if ($withLimit && isset($data[$key]) && count($data[$key]) >= $withLimit) { continue; } @@ -389,8 +391,9 @@ class BelongsToMany extends Relation $fields = $this->getQueryFields($tableName); - if ($this->withLimit) { - $this->query->limit($this->withLimit); + $withLimit = $this->withLimit ?: $this->query->getOptions('with_limit'); + if ($withLimit) { + $this->query->limit($withLimit); } $this->query diff --git a/src/model/relation/HasMany.php b/src/model/relation/HasMany.php index 97a62d4..dc034e4 100644 --- a/src/model/relation/HasMany.php +++ b/src/model/relation/HasMany.php @@ -58,8 +58,9 @@ class HasMany extends Relation $closure($this->getClosureType($closure)); } - if ($this->withLimit) { - $this->query->limit($this->withLimit); + $withLimit = $this->withLimit ?: $this->query->getOptions('with_limit'); + if ($withLimit) { + $this->query->limit($withLimit); } return $this->query @@ -217,12 +218,13 @@ class HasMany extends Relation ->select(); // 组装模型数据 - $data = []; + $data = []; + $withLimit = $this->withLimit ?: $this->query->getOptions('with_limit'); foreach ($list as $set) { $key = $set->$foreignKey; - if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { + if ($withLimit && isset($data[$key]) && count($data[$key]) >= $withLimit) { continue; } diff --git a/src/model/relation/HasManyThrough.php b/src/model/relation/HasManyThrough.php index fc62026..2d6fa58 100644 --- a/src/model/relation/HasManyThrough.php +++ b/src/model/relation/HasManyThrough.php @@ -79,8 +79,9 @@ class HasManyThrough extends Relation $this->baseQuery(); - if ($this->withLimit) { - $this->query->limit($this->withLimit); + $withLimit = $this->withLimit ?: $this->query->getOptions('with_limit'); + if ($withLimit) { + $this->query->limit($withLimit); } return $this->query->relation($subRelation) diff --git a/src/model/relation/MorphMany.php b/src/model/relation/MorphMany.php index 39209ae..5cd3dfc 100644 --- a/src/model/relation/MorphMany.php +++ b/src/model/relation/MorphMany.php @@ -77,8 +77,9 @@ class MorphMany extends Relation $this->baseQuery(); - if ($this->withLimit) { - $this->query->limit($this->withLimit); + $withLimit = $this->withLimit ?: $this->query->getOptions('with_limit'); + if ($withLimit) { + $this->query->limit($withLimit); } return $this->query->relation($subRelation) @@ -266,11 +267,13 @@ class MorphMany extends Relation $morphKey = $this->morphKey; // 组装模型数据 - $data = []; + $data = []; + $withLimit = $this->withLimit ?: $this->query->getOptions('with_limit'); + foreach ($list as $set) { $key = $set->$morphKey; - if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { + if ($withLimit && isset($data[$key]) && count($data[$key]) >= $withLimit) { continue; } diff --git a/src/model/relation/MorphToMany.php b/src/model/relation/MorphToMany.php index c566488..7a914da 100644 --- a/src/model/relation/MorphToMany.php +++ b/src/model/relation/MorphToMany.php @@ -217,8 +217,9 @@ class MorphToMany extends BelongsToMany $fields = $this->getQueryFields($tableName); - if ($this->withLimit) { - $this->query->limit($this->withLimit); + $withLimit = $this->withLimit ?: $this->query->getOptions('with_limit'); + if ($withLimit) { + $this->query->limit($withLimit); } $query = $this->query @@ -256,7 +257,9 @@ class MorphToMany extends BelongsToMany ->select(); // 组装模型数据 - $data = []; + $data = []; + $withLimit = $this->withLimit ?: $this->query->getOptions('with_limit'); + foreach ($list as $set) { $pivot = []; foreach ($set->getData() as $key => $val) { @@ -271,7 +274,7 @@ class MorphToMany extends BelongsToMany $key = $pivot[$this->localKey]; - if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) { + if ($withLimit && isset($data[$key]) && count($data[$key]) >= $withLimit) { continue; } @@ -481,7 +484,7 @@ class MorphToMany extends BelongsToMany { if (is_array($map)) { static::$morphMap = $merge && static::$morphMap - ? $map + static::$morphMap : $map; + ? $map+static::$morphMap : $map; } return static::$morphMap; -- Gitee