初始化代码
This commit is contained in:
3
vendor/topthink/think-orm/.gitignore
vendored
Normal file
3
vendor/topthink/think-orm/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.idea
|
||||
composer.lock
|
||||
vendor
|
||||
201
vendor/topthink/think-orm/LICENSE
vendored
Normal file
201
vendor/topthink/think-orm/LICENSE
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
27
vendor/topthink/think-orm/README.md
vendored
Normal file
27
vendor/topthink/think-orm/README.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# ThinkORM
|
||||
|
||||
基于PHP7.1+ 和PDO实现的ORM,支持多数据库,2.0版本主要特性包括:
|
||||
|
||||
* 基于PDO和PHP强类型实现
|
||||
* 支持原生查询和查询构造器
|
||||
* 自动参数绑定和预查询
|
||||
* 简洁易用的查询功能
|
||||
* 强大灵活的模型用法
|
||||
* 支持预载入关联查询和延迟关联查询
|
||||
* 支持多数据库及动态切换
|
||||
* 支持`MongoDb`
|
||||
* 支持分布式及事务
|
||||
* 支持断点重连
|
||||
* 支持`JSON`查询
|
||||
* 支持数据库日志
|
||||
* 支持`PSR-16`缓存及`PSR-3`日志规范
|
||||
|
||||
|
||||
## 安装
|
||||
~~~
|
||||
composer require topthink/think-orm
|
||||
~~~
|
||||
|
||||
## 文档
|
||||
|
||||
详细参考 [ThinkORM开发指南](https://www.kancloud.cn/manual/think-orm/content)
|
||||
28
vendor/topthink/think-orm/composer.json
vendored
Normal file
28
vendor/topthink/think-orm/composer.json
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "topthink/think-orm",
|
||||
"description": "think orm",
|
||||
"keywords": [
|
||||
"orm",
|
||||
"database"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"authors": [
|
||||
{
|
||||
"name": "liu21st",
|
||||
"email": "liu21st@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1.0",
|
||||
"ext-json": "*",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"psr/log": "~1.0",
|
||||
"topthink/think-helper":"^3.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"think\\": "src"
|
||||
},
|
||||
"files": []
|
||||
}
|
||||
}
|
||||
406
vendor/topthink/think-orm/src/DbManager.php
vendored
Normal file
406
vendor/topthink/think-orm/src/DbManager.php
vendored
Normal file
@@ -0,0 +1,406 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use think\db\BaseQuery;
|
||||
use think\db\ConnectionInterface;
|
||||
use think\db\Query;
|
||||
use think\db\Raw;
|
||||
|
||||
/**
|
||||
* Class DbManager
|
||||
* @package think
|
||||
* @mixin BaseQuery
|
||||
* @mixin Query
|
||||
*/
|
||||
class DbManager
|
||||
{
|
||||
/**
|
||||
* 数据库连接实例
|
||||
* @var array
|
||||
*/
|
||||
protected $instance = [];
|
||||
|
||||
/**
|
||||
* 数据库配置
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* Event对象或者数组
|
||||
* @var array|object
|
||||
*/
|
||||
protected $event;
|
||||
|
||||
/**
|
||||
* SQL监听
|
||||
* @var array
|
||||
*/
|
||||
protected $listen = [];
|
||||
|
||||
/**
|
||||
* SQL日志
|
||||
* @var array
|
||||
*/
|
||||
protected $dbLog = [];
|
||||
|
||||
/**
|
||||
* 查询次数
|
||||
* @var int
|
||||
*/
|
||||
protected $queryTimes = 0;
|
||||
|
||||
/**
|
||||
* 查询缓存对象
|
||||
* @var CacheInterface
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* 查询日志对象
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $log;
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->modelMaker();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入模型对象
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
protected function modelMaker()
|
||||
{
|
||||
$this->triggerSql();
|
||||
|
||||
Model::setDb($this);
|
||||
|
||||
if (is_object($this->event)) {
|
||||
Model::setEvent($this->event);
|
||||
}
|
||||
|
||||
Model::maker(function (Model $model) {
|
||||
$isAutoWriteTimestamp = $model->getAutoWriteTimestamp();
|
||||
|
||||
if (is_null($isAutoWriteTimestamp)) {
|
||||
// 自动写入时间戳
|
||||
$model->isAutoWriteTimestamp($this->getConfig('auto_timestamp', true));
|
||||
}
|
||||
|
||||
$dateFormat = $model->getDateFormat();
|
||||
|
||||
if (is_null($dateFormat)) {
|
||||
// 设置时间戳格式
|
||||
$model->setDateFormat($this->getConfig('datetime_format', 'Y-m-d H:i:s'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听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
|
||||
* @param array $config 连接配置
|
||||
* @return void
|
||||
*/
|
||||
public function setConfig($config): void
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缓存对象
|
||||
* @access public
|
||||
* @param CacheInterface $cache 缓存对象
|
||||
* @return void
|
||||
*/
|
||||
public function setCache(CacheInterface $cache): void
|
||||
{
|
||||
$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(string $log, string $type = 'sql')
|
||||
{
|
||||
if ($this->log) {
|
||||
$this->log->log($type, $log);
|
||||
} else {
|
||||
$this->dbLog[$type][] = $log;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得查询日志(没有设置日志对象使用)
|
||||
* @access public
|
||||
* @param bool $clear 是否清空
|
||||
* @return array
|
||||
*/
|
||||
public function getDbLog(bool $clear = false): array
|
||||
{
|
||||
$logs = $this->dbLog;
|
||||
if ($clear) {
|
||||
$this->dbLog = [];
|
||||
}
|
||||
|
||||
return $logs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置参数
|
||||
* @access public
|
||||
* @param string $name 配置参数
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $name = '', $default = null)
|
||||
{
|
||||
if ('' === $name) {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
return $this->config[$name] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建/切换数据库连接查询
|
||||
* @access public
|
||||
* @param string|null $name 连接配置标识
|
||||
* @param bool $force 强制重新连接
|
||||
* @return BaseQuery
|
||||
*/
|
||||
public function connect(string $name = null, bool $force = false): BaseQuery
|
||||
{
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据库连接实例
|
||||
* @access protected
|
||||
* @param string|null $name 连接标识
|
||||
* @param bool $force 强制重新连接
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
protected function instance(string $name = null, bool $force = false): ConnectionInterface
|
||||
{
|
||||
if (empty($name)) {
|
||||
$name = $this->getConfig('default', 'mysql');
|
||||
}
|
||||
|
||||
if ($force || !isset($this->instance[$name])) {
|
||||
$this->instance[$name] = $this->createConnection($name);
|
||||
}
|
||||
|
||||
return $this->instance[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接配置
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
protected function getConnectionConfig(string $name): array
|
||||
{
|
||||
$connections = $this->getConfig('connections');
|
||||
if (!isset($connections[$name])) {
|
||||
throw new InvalidArgumentException('Undefined db config:' . $name);
|
||||
}
|
||||
|
||||
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;
|
||||
} else {
|
||||
$class = '\\think\\db\\connector\\' . ucfirst($type);
|
||||
}
|
||||
|
||||
/** @var ConnectionInterface $connection */
|
||||
$connection = new $class($config);
|
||||
$connection->setDb($this);
|
||||
|
||||
if ($this->cache) {
|
||||
$connection->setCache($this->cache);
|
||||
}
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用表达式设置数据
|
||||
* @access public
|
||||
* @param string $value 表达式
|
||||
* @return Raw
|
||||
*/
|
||||
public function raw(string $value): Raw
|
||||
{
|
||||
return new Raw($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新查询次数
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function updateQueryTimes(): void
|
||||
{
|
||||
$this->queryTimes++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置查询次数
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function clearQueryTimes(): void
|
||||
{
|
||||
$this->queryTimes = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得查询次数
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getQueryTimes(): int
|
||||
{
|
||||
return $this->queryTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听SQL执行
|
||||
* @access public
|
||||
* @param callable $callback 回调方法
|
||||
* @return void
|
||||
*/
|
||||
public function listen(callable $callback): void
|
||||
{
|
||||
$this->listen[] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取监听SQL执行
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getListen(): array
|
||||
{
|
||||
return $this->listen;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册回调方法
|
||||
* @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 传入参数
|
||||
* @return mixed
|
||||
*/
|
||||
public function trigger(string $event, $params = null)
|
||||
{
|
||||
if (isset($this->event[$event])) {
|
||||
foreach ($this->event[$event] as $callback) {
|
||||
call_user_func_array($callback, [$this]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
return call_user_func_array([$this->connect(), $method], $args);
|
||||
}
|
||||
}
|
||||
993
vendor/topthink/think-orm/src/Model.php
vendored
Normal file
993
vendor/topthink/think-orm/src/Model.php
vendored
Normal file
@@ -0,0 +1,993 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
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
|
||||
{
|
||||
if (empty($data)) {
|
||||
return new static();
|
||||
}
|
||||
|
||||
$model = (new static($data))->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 $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->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])) {
|
||||
$where[] = [$field, '=', $this->data[$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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
497
vendor/topthink/think-orm/src/Paginator.php
vendored
Normal file
497
vendor/topthink/think-orm/src/Paginator.php
vendored
Normal file
@@ -0,0 +1,497 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 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;
|
||||
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use Closure;
|
||||
use Countable;
|
||||
use DomainException;
|
||||
use IteratorAggregate;
|
||||
use JsonSerializable;
|
||||
use think\paginator\driver\Bootstrap;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* 分页基础类
|
||||
* @mixin Collection
|
||||
*/
|
||||
abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
|
||||
{
|
||||
/**
|
||||
* 是否简洁模式
|
||||
* @var bool
|
||||
*/
|
||||
protected $simple = false;
|
||||
|
||||
/**
|
||||
* 数据集
|
||||
* @var Collection
|
||||
*/
|
||||
protected $items;
|
||||
|
||||
/**
|
||||
* 当前页
|
||||
* @var int
|
||||
*/
|
||||
protected $currentPage;
|
||||
|
||||
/**
|
||||
* 最后一页
|
||||
* @var int
|
||||
*/
|
||||
protected $lastPage;
|
||||
|
||||
/**
|
||||
* 数据总数
|
||||
* @var integer|null
|
||||
*/
|
||||
protected $total;
|
||||
|
||||
/**
|
||||
* 每页数量
|
||||
* @var int
|
||||
*/
|
||||
protected $listRows;
|
||||
|
||||
/**
|
||||
* 是否有下一页
|
||||
* @var bool
|
||||
*/
|
||||
protected $hasMore;
|
||||
|
||||
/**
|
||||
* 分页配置
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
'var_page' => 'page',
|
||||
'path' => '/',
|
||||
'query' => [],
|
||||
'fragment' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取当前页码
|
||||
* @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);
|
||||
|
||||
$this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path'];
|
||||
|
||||
$this->simple = $simple;
|
||||
$this->listRows = $listRows;
|
||||
|
||||
if (!$items instanceof Collection) {
|
||||
$items = Collection::make($items);
|
||||
}
|
||||
|
||||
if ($simple) {
|
||||
$this->currentPage = $this->setCurrentPage($currentPage);
|
||||
$this->hasMore = count($items) > ($this->listRows);
|
||||
$items = $items->slice(0, $this->listRows);
|
||||
} else {
|
||||
$this->total = $total;
|
||||
$this->lastPage = (int) ceil($total / $listRows);
|
||||
$this->currentPage = $this->setCurrentPage($currentPage);
|
||||
$this->hasMore = $this->currentPage < $this->lastPage;
|
||||
}
|
||||
$this->items = $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @access public
|
||||
* @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, int $listRows, int $currentPage = 1, int $total = null, bool $simple = false, array $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
|
||||
{
|
||||
if (!$this->simple && $currentPage > $this->lastPage) {
|
||||
return $this->lastPage > 0 ? $this->lastPage : 1;
|
||||
}
|
||||
|
||||
return $currentPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取页码对应的链接
|
||||
*
|
||||
* @access protected
|
||||
* @param int $page
|
||||
* @return string
|
||||
*/
|
||||
protected function url(int $page): string
|
||||
{
|
||||
if ($page <= 0) {
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
if (strpos($this->options['path'], '[PAGE]') === false) {
|
||||
$parameters = [$this->options['var_page'] => $page];
|
||||
$path = $this->options['path'];
|
||||
} else {
|
||||
$parameters = [];
|
||||
$path = str_replace('[PAGE]', $page, $this->options['path']);
|
||||
}
|
||||
|
||||
if (count($this->options['query']) > 0) {
|
||||
$parameters = array_merge($this->options['query'], $parameters);
|
||||
}
|
||||
|
||||
$url = $path;
|
||||
if (!empty($parameters)) {
|
||||
$url .= '?' . http_build_query($parameters, '', '&');
|
||||
}
|
||||
|
||||
return $url . $this->buildFragment();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动获取当前页码
|
||||
* @access public
|
||||
* @param string $varPage
|
||||
* @param int $default
|
||||
* @return int
|
||||
*/
|
||||
public static function getCurrentPage(string $varPage = 'page', int $default = 1): int
|
||||
{
|
||||
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($default = '/'): string
|
||||
{
|
||||
if (isset(static::$currentPathResolver)) {
|
||||
return call_user_func(static::$currentPathResolver);
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置获取当前路径闭包
|
||||
* @param Closure $resolver
|
||||
*/
|
||||
public static function currentPathResolver(Closure $resolver)
|
||||
{
|
||||
static::$currentPathResolver = $resolver;
|
||||
}
|
||||
|
||||
public function total(): int
|
||||
{
|
||||
if ($this->simple) {
|
||||
throw new DomainException('not support total');
|
||||
}
|
||||
|
||||
return $this->total;
|
||||
}
|
||||
|
||||
public function listRows(): int
|
||||
{
|
||||
return $this->listRows;
|
||||
}
|
||||
|
||||
public function currentPage(): int
|
||||
{
|
||||
return $this->currentPage;
|
||||
}
|
||||
|
||||
public function lastPage(): int
|
||||
{
|
||||
if ($this->simple) {
|
||||
throw new DomainException('not support last');
|
||||
}
|
||||
|
||||
return $this->lastPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据是否足够分页
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPages(): bool
|
||||
{
|
||||
return !(1 == $this->currentPage && !$this->hasMore);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一组分页链接
|
||||
*
|
||||
* @access public
|
||||
* @param int $start
|
||||
* @param int $end
|
||||
* @return array
|
||||
*/
|
||||
public function getUrlRange(int $start, int $end): array
|
||||
{
|
||||
$urls = [];
|
||||
|
||||
for ($page = $start; $page <= $end; $page++) {
|
||||
$urls[$page] = $this->url($page);
|
||||
}
|
||||
|
||||
return $urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置URL锚点
|
||||
*
|
||||
* @access public
|
||||
* @param string|null $fragment
|
||||
* @return $this
|
||||
*/
|
||||
public function fragment(string $fragment = null)
|
||||
{
|
||||
$this->options['fragment'] = $fragment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加URL参数
|
||||
*
|
||||
* @access public
|
||||
* @param array $append
|
||||
* @return $this
|
||||
*/
|
||||
public function appends(array $append)
|
||||
{
|
||||
foreach ($append as $k => $v) {
|
||||
if ($k !== $this->options['var_page']) {
|
||||
$this->options['query'][$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造锚点字符串
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
protected function buildFragment(): string
|
||||
{
|
||||
return $this->options['fragment'] ? '#' . $this->options['fragment'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染分页html
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function render();
|
||||
|
||||
public function items()
|
||||
{
|
||||
return $this->items->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据集
|
||||
*
|
||||
* @return Collection|\think\model\Collection
|
||||
*/
|
||||
public function getCollection()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return $this->items->isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 给每个元素执行个回调
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
* @access public
|
||||
* @return Traversable An instance of an object implementing <b>Iterator</b> or
|
||||
* <b>Traversable</b>
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->items->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a offset exists
|
||||
* @access public
|
||||
* @param mixed $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return $this->items->offsetExists($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to retrieve
|
||||
* @access public
|
||||
* @param mixed $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->items->offsetGet($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to set
|
||||
* @access public
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->items->offsetSet($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to unset
|
||||
* @access public
|
||||
* @param mixed $offset
|
||||
* @return void
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
$this->items->offsetUnset($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count elements of an object
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return $this->items->count();
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->render();
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
try {
|
||||
$total = $this->total();
|
||||
} catch (DomainException $e) {
|
||||
$total = null;
|
||||
}
|
||||
|
||||
return [
|
||||
'total' => $total,
|
||||
'per_page' => $this->listRows(),
|
||||
'current_page' => $this->currentPage(),
|
||||
'last_page' => $this->lastPage,
|
||||
'data' => $this->items->toArray(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
$result = call_user_func_array([$this->items, $name], $arguments);
|
||||
|
||||
if ($result instanceof Collection) {
|
||||
$this->items = $result;
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
1270
vendor/topthink/think-orm/src/db/BaseQuery.php
vendored
Normal file
1270
vendor/topthink/think-orm/src/db/BaseQuery.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1281
vendor/topthink/think-orm/src/db/Builder.php
vendored
Normal file
1281
vendor/topthink/think-orm/src/db/Builder.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
209
vendor/topthink/think-orm/src/db/CacheItem.php
vendored
Normal file
209
vendor/topthink/think-orm/src/db/CacheItem.php
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db;
|
||||
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
use think\db\exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* CacheItem实现类
|
||||
*/
|
||||
class CacheItem
|
||||
{
|
||||
/**
|
||||
* 缓存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|array
|
||||
*/
|
||||
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|array $tag
|
||||
* @return $this
|
||||
*/
|
||||
public function tag($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;
|
||||
}
|
||||
|
||||
}
|
||||
275
vendor/topthink/think-orm/src/db/Connection.php
vendored
Normal file
275
vendor/topthink/think-orm/src/db/Connection.php
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use think\DbManager;
|
||||
use think\db\CacheItem;
|
||||
|
||||
/**
|
||||
* 数据库连接基础类
|
||||
*/
|
||||
abstract class Connection
|
||||
{
|
||||
|
||||
/**
|
||||
* 当前SQL指令
|
||||
* @var string
|
||||
*/
|
||||
protected $queryStr = '';
|
||||
|
||||
/**
|
||||
* 返回或者影响记录数
|
||||
* @var int
|
||||
*/
|
||||
protected $numRows = 0;
|
||||
|
||||
/**
|
||||
* 事务指令数
|
||||
* @var int
|
||||
*/
|
||||
protected $transTimes = 0;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
* @var string
|
||||
*/
|
||||
protected $error = '';
|
||||
|
||||
/**
|
||||
* 数据库连接ID 支持多个连接
|
||||
* @var array
|
||||
*/
|
||||
protected $links = [];
|
||||
|
||||
/**
|
||||
* 当前连接ID
|
||||
* @var object
|
||||
*/
|
||||
protected $linkID;
|
||||
|
||||
/**
|
||||
* 当前读连接ID
|
||||
* @var object
|
||||
*/
|
||||
protected $linkRead;
|
||||
|
||||
/**
|
||||
* 当前写连接ID
|
||||
* @var object
|
||||
*/
|
||||
protected $linkWrite;
|
||||
|
||||
/**
|
||||
* 数据表信息
|
||||
* @var array
|
||||
*/
|
||||
protected $info = [];
|
||||
|
||||
/**
|
||||
* 查询开始时间
|
||||
* @var float
|
||||
*/
|
||||
protected $queryStartTime;
|
||||
|
||||
/**
|
||||
* Builder对象
|
||||
* @var Builder
|
||||
*/
|
||||
protected $builder;
|
||||
|
||||
/**
|
||||
* Db对象
|
||||
* @var Db
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* 是否读取主库
|
||||
* @var bool
|
||||
*/
|
||||
protected $readMaster = false;
|
||||
|
||||
/**
|
||||
* 数据库连接参数配置
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* 缓存对象
|
||||
* @var Cache
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* 获取当前的builder实例对象
|
||||
* @access public
|
||||
* @return Builder
|
||||
*/
|
||||
public function getBuilder()
|
||||
{
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前的数据库Db对象
|
||||
* @access public
|
||||
* @param DbManager $db
|
||||
* @return void
|
||||
*/
|
||||
public function setDb(DbManager $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前的缓存对象
|
||||
* @access public
|
||||
* @param CacheInterface $cache
|
||||
* @return void
|
||||
*/
|
||||
public function setCache(CacheInterface $cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的缓存对象
|
||||
* @access public
|
||||
* @return CacheInterface|null
|
||||
*/
|
||||
public function getCache()
|
||||
{
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库的配置参数
|
||||
* @access public
|
||||
* @param string $config 配置名称
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $config = '')
|
||||
{
|
||||
if ('' === $config) {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
return $this->config[$config] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库SQL监控
|
||||
* @access protected
|
||||
* @param string $sql 执行的SQL语句 留空自动获取
|
||||
* @param bool $master 主从标记
|
||||
* @return void
|
||||
*/
|
||||
protected function trigger(string $sql = '', bool $master = false): void
|
||||
{
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存数据
|
||||
* @access protected
|
||||
* @param CacheItem $cacheItem 缓存Item
|
||||
*/
|
||||
protected function cacheData(CacheItem $cacheItem)
|
||||
{
|
||||
if ($cacheItem->getTag() && method_exists($this->cache, 'tag')) {
|
||||
$this->cache->tag($cacheItem->getTag())->set($cacheItem->getKey(), $cacheItem->get(), $cacheItem->getExpire());
|
||||
} else {
|
||||
$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 = $query->getQueryGuid();
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析缓存
|
||||
* @access protected
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @param array $cache 缓存信息
|
||||
* @return CacheItem
|
||||
*/
|
||||
protected function parseCache(BaseQuery $query, array $cache): CacheItem
|
||||
{
|
||||
[$key, $expire, $tag] = $cache;
|
||||
|
||||
if ($key instanceof CacheItem) {
|
||||
$cacheItem = $key;
|
||||
} else {
|
||||
if (true === $key) {
|
||||
$key = $this->getCacheKey($query);
|
||||
}
|
||||
|
||||
$cacheItem = new CacheItem($key);
|
||||
$cacheItem->expire($expire);
|
||||
$cacheItem->tag($tag);
|
||||
}
|
||||
|
||||
return $cacheItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取返回或者影响的记录数
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getNumRows(): int
|
||||
{
|
||||
return $this->numRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* 析构方法
|
||||
* @access public
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
// 关闭连接
|
||||
$this->close();
|
||||
}
|
||||
}
|
||||
196
vendor/topthink/think-orm/src/db/ConnectionInterface.php
vendored
Normal file
196
vendor/topthink/think-orm/src/db/ConnectionInterface.php
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
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
|
||||
* @param string $config 配置名称
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $config = '');
|
||||
|
||||
/**
|
||||
* 关闭数据库(或者重新连接)
|
||||
* @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 查询对象
|
||||
* @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;
|
||||
|
||||
}
|
||||
493
vendor/topthink/think-orm/src/db/Fetch.php
vendored
Normal file
493
vendor/topthink/think-orm/src/db/Fetch.php
vendored
Normal file
@@ -0,0 +1,493 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db;
|
||||
|
||||
use think\db\exception\DbException as Exception;
|
||||
use think\helper\Str;
|
||||
|
||||
/**
|
||||
* SQL获取类
|
||||
*/
|
||||
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 protected
|
||||
* @param string $aggregate 聚合方法
|
||||
* @param string $field 字段名
|
||||
* @return string
|
||||
*/
|
||||
protected function aggregate(string $aggregate, string $field): string
|
||||
{
|
||||
$this->query->parseOptions();
|
||||
|
||||
$field = $aggregate . '(' . $this->builder->parseKey($this->query, $field) . ') AS think_' . strtolower($aggregate);
|
||||
|
||||
return $this->value($field, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到某个字段的值
|
||||
* @access public
|
||||
* @param string $field 字段名
|
||||
* @param mixed $default 默认值
|
||||
* @return string
|
||||
*/
|
||||
public function value(string $field, $default = null, bool $one = true): string
|
||||
{
|
||||
$options = $this->query->parseOptions();
|
||||
|
||||
if (isset($options['field'])) {
|
||||
$this->query->removeOption('field');
|
||||
}
|
||||
|
||||
$this->query->setOption('field', (array) $field);
|
||||
|
||||
// 生成查询SQL
|
||||
$sql = $this->builder->select($this->query, $one);
|
||||
|
||||
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 数据
|
||||
* @return string
|
||||
*/
|
||||
public function insert(array $data = []): string
|
||||
{
|
||||
$options = $this->query->parseOptions();
|
||||
|
||||
if (!empty($data)) {
|
||||
$this->query->setOption('data', $data);
|
||||
}
|
||||
|
||||
$sql = $this->builder->insert($this->query);
|
||||
|
||||
return $this->fetch($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入记录并获取自增ID
|
||||
* @access public
|
||||
* @param array $data 数据
|
||||
* @return string
|
||||
*/
|
||||
public function insertGetId(array $data = []): string
|
||||
{
|
||||
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 ($forceInsert) {
|
||||
return $this->insert($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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入记录
|
||||
* @access public
|
||||
* @param array $dataSet 数据集
|
||||
* @param integer $limit 每次写入数据限制
|
||||
* @return string
|
||||
*/
|
||||
public function insertAll(array $dataSet = [], 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);
|
||||
$fetchSql = [];
|
||||
foreach ($array as $item) {
|
||||
$sql = $this->builder->insertAll($this->query, $item);
|
||||
$bind = $this->query->getBind();
|
||||
|
||||
$fetchSql[] = $this->connection->getRealSql($sql, $bind);
|
||||
}
|
||||
|
||||
return implode(';', $fetchSql);
|
||||
}
|
||||
|
||||
$sql = $this->builder->insertAll($this->query, $dataSet);
|
||||
|
||||
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();
|
||||
|
||||
$data = !empty($data) ? $data : $options['data'];
|
||||
|
||||
$pk = $this->query->getPk();
|
||||
|
||||
if (empty($options['where'])) {
|
||||
// 如果存在主键数据 则自动作为更新条件
|
||||
if (is_string($pk) && isset($data[$pk])) {
|
||||
$this->query->where($pk, '=', $data[$pk]);
|
||||
unset($data[$pk]);
|
||||
} elseif (is_array($pk)) {
|
||||
// 增加复合主键支持
|
||||
foreach ($pk as $field) {
|
||||
if (isset($data[$field])) {
|
||||
$this->query->where($field, '=', $data[$field]);
|
||||
} else {
|
||||
// 如果缺少复合主键数据则不执行
|
||||
throw new Exception('miss complex primary data');
|
||||
}
|
||||
unset($data[$field]);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->query->getOptions('where'))) {
|
||||
// 如果没有任何更新条件则不执行
|
||||
throw new Exception('miss update condition');
|
||||
}
|
||||
}
|
||||
|
||||
// 更新数据
|
||||
$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'])) {
|
||||
// 软删除
|
||||
[$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);
|
||||
}
|
||||
}
|
||||
|
||||
// 生成删除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);
|
||||
}
|
||||
|
||||
// 生成查询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);
|
||||
}
|
||||
|
||||
// 生成查询SQL
|
||||
$sql = $this->builder->select($this->query, true);
|
||||
|
||||
// 获取实际执行的SQL语句
|
||||
return $this->fetch($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找多条记录 如果不存在则抛出异常
|
||||
* @access public
|
||||
* @param mixed $data
|
||||
* @return string
|
||||
*/
|
||||
public function selectOrFail($data = null): string
|
||||
{
|
||||
return $this->select($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找单条记录 如果不存在则抛出异常
|
||||
* @access public
|
||||
* @param mixed $data
|
||||
* @return string
|
||||
*/
|
||||
public function findOrFail($data = null): string
|
||||
{
|
||||
return $this->find($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找单条记录 不存在返回空数据(或者空模型)
|
||||
* @access public
|
||||
* @param mixed $data 数据
|
||||
* @return string
|
||||
*/
|
||||
public function findOrEmpty($data = null)
|
||||
{
|
||||
return $this->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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = 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]);
|
||||
}
|
||||
|
||||
$result = call_user_func_array([$this->query, $method], $args);
|
||||
return $result === $this->query ? $this : $result;
|
||||
}
|
||||
}
|
||||
741
vendor/topthink/think-orm/src/db/Mongo.php
vendored
Normal file
741
vendor/topthink/think-orm/src/db/Mongo.php
vendored
Normal file
@@ -0,0 +1,741 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
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\Collection;
|
||||
use think\db\connector\Mongo as Connection;
|
||||
use think\db\exception\DbException as Exception;
|
||||
use think\Paginator;
|
||||
|
||||
class Mongo extends BaseQuery
|
||||
{
|
||||
/**
|
||||
* 执行查询 返回数据集
|
||||
* @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
|
||||
* @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, $typeMap = null)
|
||||
{
|
||||
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
|
||||
* @param string|array|object $command 指令
|
||||
* @param mixed $extra 额外参数
|
||||
* @param string $db 数据库名
|
||||
* @return array
|
||||
*/
|
||||
public function cmd($command, $extra = null, string $db = ''): array
|
||||
{
|
||||
$this->parseOptions();
|
||||
return $this->connection->cmd($this, $command, $extra, $db);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定distinct查询
|
||||
* @access public
|
||||
* @param string $field 字段名
|
||||
* @return array
|
||||
*/
|
||||
public function getDistinct(string $field)
|
||||
{
|
||||
$result = $this->cmd('distinct', $field);
|
||||
return $result[0]['values'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库的所有collection
|
||||
* @access public
|
||||
* @param string $db 数据库名称 留空为当前数据库
|
||||
* @throws Exception
|
||||
*/
|
||||
public function listCollections(string $db = '')
|
||||
{
|
||||
$cursor = $this->cmd('listCollections', null, $db);
|
||||
$result = [];
|
||||
foreach ($cursor as $collection) {
|
||||
$result[] = $collection['name'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* COUNT查询
|
||||
* @access public
|
||||
* @param string $field 字段名
|
||||
* @return integer
|
||||
*/
|
||||
public function count(string $field = null): int
|
||||
{
|
||||
$result = $this->cmd('count');
|
||||
|
||||
return $result[0]['n'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚合查询
|
||||
* @access public
|
||||
* @param string $aggregate 聚合指令
|
||||
* @param string $field 字段名
|
||||
* @param bool $force 强制转为数字类型
|
||||
* @return mixed
|
||||
*/
|
||||
public function aggregate(string $aggregate, $field, bool $force = false)
|
||||
{
|
||||
$result = $this->cmd('aggregate', [strtolower($aggregate), $field]);
|
||||
$value = $result[0]['aggregate'] ?? 0;
|
||||
|
||||
if ($force) {
|
||||
$value += 0;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 多聚合操作
|
||||
*
|
||||
* @param array $aggregate 聚合指令, 可以聚合多个参数, 如 ['sum' => 'field1', 'avg' => 'field2']
|
||||
* @param array $groupBy 类似mysql里面的group字段, 可以传入多个字段, 如 ['field_a', 'field_b', 'field_c']
|
||||
* @return array 查询结果
|
||||
*/
|
||||
public function multiAggregate(array $aggregate, array $groupBy): array
|
||||
{
|
||||
$result = $this->cmd('multiAggregate', [$aggregate, $groupBy]);
|
||||
|
||||
foreach ($result as &$row) {
|
||||
if (isset($row['_id']) && !empty($row['_id'])) {
|
||||
foreach ($row['_id'] as $k => $v) {
|
||||
$row[$k] = $v;
|
||||
}
|
||||
unset($row['_id']);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段值增长
|
||||
* @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)
|
||||
{
|
||||
return $this->inc($field, -1 * $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定当前操作的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
|
||||
*/
|
||||
public function collection(string $collection)
|
||||
{
|
||||
return $this->table($collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置typeMap
|
||||
* @access public
|
||||
* @param string|array $typeMap
|
||||
* @return $this
|
||||
*/
|
||||
public function typeMap($typeMap)
|
||||
{
|
||||
$this->options['typeMap'] = $typeMap;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* awaitData
|
||||
* @access public
|
||||
* @param bool $awaitData
|
||||
* @return $this
|
||||
*/
|
||||
public function awaitData(bool $awaitData)
|
||||
{
|
||||
$this->options['awaitData'] = $awaitData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* batchSize
|
||||
* @access public
|
||||
* @param integer $batchSize
|
||||
* @return $this
|
||||
*/
|
||||
public function batchSize(int $batchSize)
|
||||
{
|
||||
$this->options['batchSize'] = $batchSize;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* exhaust
|
||||
* @access public
|
||||
* @param bool $exhaust
|
||||
* @return $this
|
||||
*/
|
||||
public function exhaust(bool $exhaust)
|
||||
{
|
||||
$this->options['exhaust'] = $exhaust;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置modifiers
|
||||
* @access public
|
||||
* @param array $modifiers
|
||||
* @return $this
|
||||
*/
|
||||
public function modifiers(array $modifiers)
|
||||
{
|
||||
$this->options['modifiers'] = $modifiers;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置noCursorTimeout
|
||||
* @access public
|
||||
* @param bool $noCursorTimeout
|
||||
* @return $this
|
||||
*/
|
||||
public function noCursorTimeout(bool $noCursorTimeout)
|
||||
{
|
||||
$this->options['noCursorTimeout'] = $noCursorTimeout;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置oplogReplay
|
||||
* @access public
|
||||
* @param bool $oplogReplay
|
||||
* @return $this
|
||||
*/
|
||||
public function oplogReplay(bool $oplogReplay)
|
||||
{
|
||||
$this->options['oplogReplay'] = $oplogReplay;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置partial
|
||||
* @access public
|
||||
* @param bool $partial
|
||||
* @return $this
|
||||
*/
|
||||
public function partial(bool $partial)
|
||||
{
|
||||
$this->options['partial'] = $partial;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* maxTimeMS
|
||||
* @access public
|
||||
* @param string $maxTimeMS
|
||||
* @return $this
|
||||
*/
|
||||
public function maxTimeMS(string $maxTimeMS)
|
||||
{
|
||||
$this->options['maxTimeMS'] = $maxTimeMS;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* collation
|
||||
* @access public
|
||||
* @param array $collation
|
||||
* @return $this
|
||||
*/
|
||||
public function collation(array $collation)
|
||||
{
|
||||
$this->options['collation'] = $collation;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否REPLACE
|
||||
* @access public
|
||||
* @param bool $replace 是否使用REPLACE写入数据
|
||||
* @return $this
|
||||
*/
|
||||
public function replace(bool $replace = true)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置返回字段
|
||||
* @access public
|
||||
* @param mixed $field 字段信息
|
||||
* @return $this
|
||||
*/
|
||||
public function field($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] = 1;
|
||||
} else {
|
||||
$projection[$key] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
$this->options['projection'] = $projection;
|
||||
|
||||
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
|
||||
* @param integer $skip
|
||||
* @return $this
|
||||
*/
|
||||
public function skip(int $skip)
|
||||
{
|
||||
$this->options['skip'] = $skip;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置slaveOk
|
||||
* @access public
|
||||
* @param bool $slaveOk
|
||||
* @return $this
|
||||
*/
|
||||
public function slaveOk(bool $slaveOk)
|
||||
{
|
||||
$this->options['slaveOk'] = $slaveOk;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定查询数量
|
||||
* @access public
|
||||
* @param int $offset 起始位置
|
||||
* @param int $length 查询数量
|
||||
* @return $this
|
||||
*/
|
||||
public function limit(int $offset, int $length = null)
|
||||
{
|
||||
if (is_null($length)) {
|
||||
$length = $offset;
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
$this->options['skip'] = $offset;
|
||||
$this->options['limit'] = $length;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置sort
|
||||
* @access public
|
||||
* @param array|string $field
|
||||
* @param string $order
|
||||
* @return $this
|
||||
*/
|
||||
public function order($field, string $order = '')
|
||||
{
|
||||
if (is_array($field)) {
|
||||
$this->options['sort'] = $field;
|
||||
} else {
|
||||
$this->options['sort'][$field] = 'asc' == strtolower($order) ? 1 : -1;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置tailable
|
||||
* @access public
|
||||
* @param bool $tailable
|
||||
* @return $this
|
||||
*/
|
||||
public function tailable(bool $tailable)
|
||||
{
|
||||
$this->options['tailable'] = $tailable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置writeConcern对象
|
||||
* @access public
|
||||
* @param WriteConcern $writeConcern
|
||||
* @return $this
|
||||
*/
|
||||
public function writeConcern(WriteConcern $writeConcern)
|
||||
{
|
||||
$this->options['writeConcern'] = $writeConcern;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前数据表的主键
|
||||
* @access public
|
||||
* @return string|array
|
||||
*/
|
||||
public function getPk()
|
||||
{
|
||||
return $this->pk ?: $this->connection->getConfig('pk');
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询但只返回Cursor对象
|
||||
* @access public
|
||||
* @return Cursor
|
||||
*/
|
||||
public function getCursor(): Cursor
|
||||
{
|
||||
$this->parseOptions();
|
||||
|
||||
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
|
||||
* @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, '.')) {
|
||||
[$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
|
||||
* @return array
|
||||
*/
|
||||
public function parseOptions(): array
|
||||
{
|
||||
$options = $this->options;
|
||||
|
||||
// 获取数据表
|
||||
if (empty($options['table'])) {
|
||||
$options['table'] = $this->getTable();
|
||||
}
|
||||
|
||||
foreach (['where', 'data'] as $name) {
|
||||
if (!isset($options[$name])) {
|
||||
$options[$name] = [];
|
||||
}
|
||||
}
|
||||
|
||||
$modifiers = empty($options['modifiers']) ? [] : $options['modifiers'];
|
||||
if (isset($options['comment'])) {
|
||||
$modifiers['$comment'] = $options['comment'];
|
||||
}
|
||||
|
||||
if (isset($options['maxTimeMS'])) {
|
||||
$modifiers['$maxTimeMS'] = $options['maxTimeMS'];
|
||||
}
|
||||
|
||||
if (!empty($modifiers)) {
|
||||
$options['modifiers'] = $modifiers;
|
||||
}
|
||||
|
||||
if (!isset($options['projection'])) {
|
||||
$options['projection'] = [];
|
||||
}
|
||||
|
||||
if (!isset($options['typeMap'])) {
|
||||
$options['typeMap'] = $this->getConfig('type_map');
|
||||
}
|
||||
|
||||
if (!isset($options['limit'])) {
|
||||
$options['limit'] = 0;
|
||||
}
|
||||
|
||||
foreach (['master', 'fetch_sql', 'fetch_cursor'] as $name) {
|
||||
if (!isset($options[$name])) {
|
||||
$options[$name] = false;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
$this->options = $options;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
1697
vendor/topthink/think-orm/src/db/PDOConnection.php
vendored
Normal file
1697
vendor/topthink/think-orm/src/db/PDOConnection.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
493
vendor/topthink/think-orm/src/db/Query.php
vendored
Normal file
493
vendor/topthink/think-orm/src/db/Query.php
vendored
Normal file
@@ -0,0 +1,493 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
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
|
||||
*/
|
||||
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, '.')) {
|
||||
[$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;
|
||||
}
|
||||
}
|
||||
52
vendor/topthink/think-orm/src/db/Raw.php
vendored
Normal file
52
vendor/topthink/think-orm/src/db/Raw.php
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db;
|
||||
|
||||
/**
|
||||
* SQL Raw
|
||||
*/
|
||||
class Raw
|
||||
{
|
||||
/**
|
||||
* 查询表达式
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* 创建一个查询表达式
|
||||
*
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表达式
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->value;
|
||||
}
|
||||
}
|
||||
182
vendor/topthink/think-orm/src/db/Where.php
vendored
Normal file
182
vendor/topthink/think-orm/src/db/Where.php
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db;
|
||||
|
||||
use ArrayAccess;
|
||||
|
||||
/**
|
||||
* 数组查询对象
|
||||
*/
|
||||
class Where implements ArrayAccess
|
||||
{
|
||||
/**
|
||||
* 查询表达式
|
||||
* @var array
|
||||
*/
|
||||
protected $where = [];
|
||||
|
||||
/**
|
||||
* 是否需要把查询条件两边增加括号
|
||||
* @var bool
|
||||
*/
|
||||
protected $enclose = false;
|
||||
|
||||
/**
|
||||
* 创建一个查询表达式
|
||||
*
|
||||
* @param array $where 查询条件数组
|
||||
* @param bool $enclose 是否增加括号
|
||||
*/
|
||||
public function __construct(array $where = [], bool $enclose = false)
|
||||
{
|
||||
$this->where = $where;
|
||||
$this->enclose = $enclose;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否添加括号
|
||||
* @access public
|
||||
* @param bool $enclose
|
||||
* @return $this
|
||||
*/
|
||||
public function enclose(bool $enclose = true)
|
||||
{
|
||||
$this->enclose = $enclose;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析为Query对象可识别的查询条件数组
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function parse(): array
|
||||
{
|
||||
$where = [];
|
||||
|
||||
foreach ($this->where as $key => $val) {
|
||||
if ($val instanceof Raw) {
|
||||
$where[] = [$key, 'exp', $val];
|
||||
} elseif (is_null($val)) {
|
||||
$where[] = [$key, 'NULL', ''];
|
||||
} elseif (is_array($val)) {
|
||||
$where[] = $this->parseItem($key, $val);
|
||||
} else {
|
||||
$where[] = [$key, '=', $val];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->enclose ? [$where] : $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析查询表达式
|
||||
* @access protected
|
||||
* @param string $field 查询字段
|
||||
* @param array $where 查询条件
|
||||
* @return array
|
||||
*/
|
||||
protected function parseItem(string $field, array $where = []): array
|
||||
{
|
||||
$op = $where[0];
|
||||
$condition = $where[1] ?? null;
|
||||
|
||||
if (is_array($op)) {
|
||||
// 同一字段多条件查询
|
||||
array_unshift($where, $field);
|
||||
} elseif (is_null($condition)) {
|
||||
if (is_string($op) && in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) {
|
||||
// null查询
|
||||
$where = [$field, $op, ''];
|
||||
} elseif (is_null($op) || '=' == $op) {
|
||||
$where = [$field, 'NULL', ''];
|
||||
} elseif ('<>' == $op) {
|
||||
$where = [$field, 'NOTNULL', ''];
|
||||
} else {
|
||||
// 字段相等查询
|
||||
$where = [$field, '=', $op];
|
||||
}
|
||||
} else {
|
||||
$where = [$field, $op, $condition];
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改器 设置数据对象的值
|
||||
* @access public
|
||||
* @param string $name 名称
|
||||
* @param mixed $value 值
|
||||
* @return void
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->where[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取器 获取数据对象的值
|
||||
* @access public
|
||||
* @param string $name 名称
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->where[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测数据对象的值
|
||||
* @access public
|
||||
* @param string $name 名称
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return isset($this->where[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁数据对象的值
|
||||
* @access public
|
||||
* @param string $name 名称
|
||||
* @return void
|
||||
*/
|
||||
public function __unset($name)
|
||||
{
|
||||
unset($this->where[$name]);
|
||||
}
|
||||
|
||||
// ArrayAccess
|
||||
public function offsetSet($name, $value)
|
||||
{
|
||||
$this->__set($name, $value);
|
||||
}
|
||||
|
||||
public function offsetExists($name)
|
||||
{
|
||||
return $this->__isset($name);
|
||||
}
|
||||
|
||||
public function offsetUnset($name)
|
||||
{
|
||||
$this->__unset($name);
|
||||
}
|
||||
|
||||
public function offsetGet($name)
|
||||
{
|
||||
return $this->__get($name);
|
||||
}
|
||||
|
||||
}
|
||||
675
vendor/topthink/think-orm/src/db/builder/Mongo.php
vendored
Normal file
675
vendor/topthink/think-orm/src/db/builder/Mongo.php
vendored
Normal file
@@ -0,0 +1,675 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
namespace think\db\builder;
|
||||
|
||||
use MongoDB\BSON\Javascript;
|
||||
use MongoDB\BSON\ObjectID;
|
||||
use MongoDB\BSON\Regex;
|
||||
use MongoDB\Driver\BulkWrite;
|
||||
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;
|
||||
|
||||
class Mongo
|
||||
{
|
||||
// connection对象实例
|
||||
protected $connection;
|
||||
// 最后插入ID
|
||||
protected $insertId = [];
|
||||
// 查询表达式
|
||||
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'];
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Connection $connection 数据库连接对象实例
|
||||
*/
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的连接对象实例
|
||||
* @access public
|
||||
* @return Connection
|
||||
*/
|
||||
public function getConnection(): Connection
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* key分析
|
||||
* @access protected
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function parseKey(Query $query, string $key): string
|
||||
{
|
||||
if (0 === strpos($key, '__TABLE__.')) {
|
||||
[$collection, $key] = explode('.', $key, 2);
|
||||
}
|
||||
|
||||
if ('id' == $key && $this->connection->getConfig('pk_convert_id')) {
|
||||
$key = '_id';
|
||||
}
|
||||
|
||||
return trim($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* value分析
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $value
|
||||
* @param string $field
|
||||
* @return string
|
||||
*/
|
||||
protected function parseValue(Query $query, $value, $field = '')
|
||||
{
|
||||
if ('_id' == $field && 'ObjectID' == $this->connection->getConfig('pk_type') && is_string($value)) {
|
||||
try {
|
||||
return new ObjectID($value);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
return new ObjectID();
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert数据分析
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @param array $data 数据
|
||||
* @return array
|
||||
*/
|
||||
protected function parseData(Query $query, array $data): array
|
||||
{
|
||||
if (empty($data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($data as $key => $val) {
|
||||
$item = $this->parseKey($query, $key);
|
||||
|
||||
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 {
|
||||
$result[$item] = $this->parseValue($query, $val, $key);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set数据分析
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @param array $data 数据
|
||||
* @return array
|
||||
*/
|
||||
protected function parseSet(Query $query, array $data): array
|
||||
{
|
||||
if (empty($data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($data as $key => $val) {
|
||||
$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);
|
||||
} else {
|
||||
$result['$set'][$item] = $this->parseValue($query, $val, $key);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成查询过滤条件
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $where
|
||||
* @return array
|
||||
*/
|
||||
public function parseWhere(Query $query, array $where): array
|
||||
{
|
||||
if (empty($where)) {
|
||||
$where = [];
|
||||
}
|
||||
|
||||
$filter = [];
|
||||
foreach ($where as $logic => $val) {
|
||||
$logic = '$' . strtolower($logic);
|
||||
foreach ($val as $field => $value) {
|
||||
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) {
|
||||
// 使用闭包查询
|
||||
$query = new Query($this->connection);
|
||||
call_user_func_array($value, [ & $query]);
|
||||
$filter[$logic][] = $this->parseWhere($query, $query->getOptions('where'));
|
||||
} else {
|
||||
if (strpos($field, '|')) {
|
||||
// 不同字段使用相同查询条件(OR)
|
||||
$array = explode('|', $field);
|
||||
foreach ($array as $k) {
|
||||
$filter['$or'][] = $this->parseWhereItem($query, $k, $value);
|
||||
}
|
||||
} elseif (strpos($field, '&')) {
|
||||
// 不同字段使用相同查询条件(AND)
|
||||
$array = explode('&', $field);
|
||||
foreach ($array as $k) {
|
||||
$filter['$and'][] = $this->parseWhereItem($query, $k, $value);
|
||||
}
|
||||
} else {
|
||||
// 对字段使用表达式查询
|
||||
$field = is_string($field) ? $field : '';
|
||||
$filter[$logic][] = $this->parseWhereItem($query, $field, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$options = $query->getOptions();
|
||||
if (!empty($options['soft_delete'])) {
|
||||
// 附加软删除条件
|
||||
[$field, $condition] = $options['soft_delete'];
|
||||
$filter['$and'][] = $this->parseWhereItem($query, $field, $condition);
|
||||
}
|
||||
|
||||
return $filter;
|
||||
}
|
||||
|
||||
// where子单元分析
|
||||
protected function parseWhereItem(Query $query, $field, $val): array
|
||||
{
|
||||
$key = $field ? $this->parseKey($query, $field) : '';
|
||||
// 查询规则和条件
|
||||
if (!is_array($val)) {
|
||||
$val = ['=', $val];
|
||||
}
|
||||
[$exp, $value] = $val;
|
||||
|
||||
// 对一个字段使用多个查询条件
|
||||
if (is_array($exp)) {
|
||||
$data = [];
|
||||
foreach ($val as $value) {
|
||||
$exp = $value[0];
|
||||
$value = $value[1];
|
||||
if (!in_array($exp, $this->exp)) {
|
||||
$exp = strtolower($exp);
|
||||
if (isset($this->exp[$exp])) {
|
||||
$exp = $this->exp[$exp];
|
||||
}
|
||||
}
|
||||
$k = '$' . $exp;
|
||||
$data[$k] = $value;
|
||||
}
|
||||
$result[$key] = $data;
|
||||
return $result;
|
||||
} elseif (!in_array($exp, $this->exp)) {
|
||||
$exp = strtolower($exp);
|
||||
if (isset($this->exp[$exp])) {
|
||||
$exp = $this->exp[$exp];
|
||||
} else {
|
||||
throw new Exception('where express error:' . $exp);
|
||||
}
|
||||
}
|
||||
|
||||
$result = [];
|
||||
if ('=' == $exp) {
|
||||
// 普通查询
|
||||
$result[$key] = $this->parseValue($query, $value, $key);
|
||||
} elseif (in_array($exp, ['neq', 'ne', 'gt', 'egt', 'gte', 'lt', 'lte', 'elt', 'mod'])) {
|
||||
// 比较运算
|
||||
$k = '$' . $exp;
|
||||
$result[$key] = [$k => $this->parseValue($query, $value, $key)];
|
||||
} elseif ('null' == $exp) {
|
||||
// NULL 查询
|
||||
$result[$key] = null;
|
||||
} elseif ('not null' == $exp) {
|
||||
$result[$key] = ['$ne' => null];
|
||||
} elseif ('all' == $exp) {
|
||||
// 满足所有指定条件
|
||||
$result[$key] = ['$all', $this->parseValue($query, $value, $key)];
|
||||
} elseif ('between' == $exp) {
|
||||
// 区间查询
|
||||
$value = is_array($value) ? $value : explode(',', $value);
|
||||
$result[$key] = ['$gte' => $this->parseValue($query, $value[0], $key), '$lte' => $this->parseValue($query, $value[1], $key)];
|
||||
} elseif ('not between' == $exp) {
|
||||
// 范围查询
|
||||
$value = is_array($value) ? $value : explode(',', $value);
|
||||
$result[$key] = ['$lt' => $this->parseValue($query, $value[0], $key), '$gt' => $this->parseValue($query, $value[1], $key)];
|
||||
} elseif ('exists' == $exp) {
|
||||
// 字段是否存在
|
||||
$result[$key] = ['$exists' => (bool) $value];
|
||||
} elseif ('type' == $exp) {
|
||||
// 类型查询
|
||||
$result[$key] = ['$type' => intval($value)];
|
||||
} elseif ('exp' == $exp) {
|
||||
// 表达式查询
|
||||
$result['$where'] = $value instanceof Javascript ? $value : new Javascript($value);
|
||||
} elseif ('like' == $exp) {
|
||||
// 模糊查询 采用正则方式
|
||||
$result[$key] = $value instanceof Regex ? $value : new Regex($value, 'i');
|
||||
} elseif (in_array($exp, ['nin', 'in'])) {
|
||||
// IN 查询
|
||||
$value = is_array($value) ? $value : explode(',', $value);
|
||||
foreach ($value as $k => $val) {
|
||||
$value[$k] = $this->parseValue($query, $val, $key);
|
||||
}
|
||||
$result[$key] = ['$' . $exp => $value];
|
||||
} elseif ('regex' == $exp) {
|
||||
$result[$key] = $value instanceof Regex ? $value : new Regex($value, 'i');
|
||||
} elseif ('< time' == $exp) {
|
||||
$result[$key] = ['$lt' => $this->parseDateTime($query, $value, $field)];
|
||||
} elseif ('> time' == $exp) {
|
||||
$result[$key] = ['$gt' => $this->parseDateTime($query, $value, $field)];
|
||||
} elseif ('between time' == $exp) {
|
||||
// 区间查询
|
||||
$value = is_array($value) ? $value : explode(',', $value);
|
||||
$result[$key] = ['$gte' => $this->parseDateTime($query, $value[0], $field), '$lte' => $this->parseDateTime($query, $value[1], $field)];
|
||||
} elseif ('not between time' == $exp) {
|
||||
// 范围查询
|
||||
$value = is_array($value) ? $value : explode(',', $value);
|
||||
$result[$key] = ['$lt' => $this->parseDateTime($query, $value[0], $field), '$gt' => $this->parseDateTime($query, $value[1], $field)];
|
||||
} elseif ('near' == $exp) {
|
||||
// 经纬度查询
|
||||
$result[$key] = ['$near' => $this->parseValue($query, $value, $key)];
|
||||
} elseif ('size' == $exp) {
|
||||
// 元素长度查询
|
||||
$result[$key] = ['$size' => intval($value)];
|
||||
} else {
|
||||
// 普通查询
|
||||
$result[$key] = $this->parseValue($query, $value, $key);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期时间条件解析
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @param string $value
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDateTime(Query $query, $value, $key)
|
||||
{
|
||||
// 获取时间字段类型
|
||||
$type = $query->getFieldType($key);
|
||||
|
||||
if ($type) {
|
||||
if (is_string($value)) {
|
||||
$value = strtotime($value) ?: $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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最后写入的ID 如果是insertAll方法的话 返回所有写入的ID
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLastInsID()
|
||||
{
|
||||
return $this->insertId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成insert BulkWrite对象
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @return BulkWrite
|
||||
*/
|
||||
public function insert(Query $query): BulkWrite
|
||||
{
|
||||
// 分析并处理数据
|
||||
$options = $query->getOptions();
|
||||
|
||||
$data = $this->parseData($query, $options['data']);
|
||||
|
||||
$bulk = new BulkWrite;
|
||||
|
||||
if ($insertId = $bulk->insert($data)) {
|
||||
$this->insertId = $insertId;
|
||||
}
|
||||
|
||||
$this->log('insert', $data, $options);
|
||||
|
||||
return $bulk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成insertall BulkWrite对象
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param array $dataSet 数据集
|
||||
* @return BulkWrite
|
||||
*/
|
||||
public function insertAll(Query $query, array $dataSet): BulkWrite
|
||||
{
|
||||
$bulk = new BulkWrite;
|
||||
$options = $query->getOptions();
|
||||
|
||||
$this->insertId = [];
|
||||
foreach ($dataSet as $data) {
|
||||
// 分析并处理数据
|
||||
$data = $this->parseData($query, $data);
|
||||
if ($insertId = $bulk->insert($data)) {
|
||||
$this->insertId[] = $insertId;
|
||||
}
|
||||
}
|
||||
|
||||
$this->log('insert', $dataSet, $options);
|
||||
|
||||
return $bulk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成update BulkWrite对象
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @return BulkWrite
|
||||
*/
|
||||
public function update(Query $query): BulkWrite
|
||||
{
|
||||
$options = $query->getOptions();
|
||||
|
||||
$data = $this->parseSet($query, $options['data']);
|
||||
$where = $this->parseWhere($query, $options['where']);
|
||||
|
||||
if (1 == $options['limit']) {
|
||||
$updateOptions = ['multi' => false];
|
||||
} else {
|
||||
$updateOptions = ['multi' => true];
|
||||
}
|
||||
|
||||
$bulk = new BulkWrite;
|
||||
|
||||
$bulk->update($where, $data, $updateOptions);
|
||||
|
||||
$this->log('update', $data, $where);
|
||||
|
||||
return $bulk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成delete BulkWrite对象
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @return BulkWrite
|
||||
*/
|
||||
public function delete(Query $query): BulkWrite
|
||||
{
|
||||
$options = $query->getOptions();
|
||||
$where = $this->parseWhere($query, $options['where']);
|
||||
|
||||
$bulk = new BulkWrite;
|
||||
|
||||
if (1 == $options['limit']) {
|
||||
$deleteOptions = ['limit' => 1];
|
||||
} else {
|
||||
$deleteOptions = ['limit' => 0];
|
||||
}
|
||||
|
||||
$bulk->delete($where, $deleteOptions);
|
||||
|
||||
$this->log('remove', $where, $deleteOptions);
|
||||
|
||||
return $bulk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成Mongo查询对象
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param bool $one 是否仅获取一个记录
|
||||
* @return MongoQuery
|
||||
*/
|
||||
public function select(Query $query, bool $one = false): MongoQuery
|
||||
{
|
||||
$options = $query->getOptions();
|
||||
|
||||
$where = $this->parseWhere($query, $options['where']);
|
||||
|
||||
if ($one) {
|
||||
$options['limit'] = 1;
|
||||
}
|
||||
|
||||
$query = new MongoQuery($where, $options);
|
||||
|
||||
$this->log('find', $where, $options);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成Count命令
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @return Command
|
||||
*/
|
||||
public function count(Query $query): Command
|
||||
{
|
||||
$options = $query->getOptions();
|
||||
|
||||
$cmd['count'] = $options['table'];
|
||||
$cmd['query'] = (object) $this->parseWhere($query, $options['where']);
|
||||
|
||||
foreach (['hint', 'limit', 'maxTimeMS', 'skip'] as $option) {
|
||||
if (isset($options[$option])) {
|
||||
$cmd[$option] = $options[$option];
|
||||
}
|
||||
}
|
||||
|
||||
$command = new Command($cmd);
|
||||
$this->log('cmd', 'count', $cmd);
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚合查询命令
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param array $extra 指令和字段
|
||||
* @return Command
|
||||
*/
|
||||
public function aggregate(Query $query, array $extra): Command
|
||||
{
|
||||
$options = $query->getOptions();
|
||||
[$fun, $field] = $extra;
|
||||
|
||||
if ('id' == $field && $this->connection->getConfig('pk_convert_id')) {
|
||||
$field = '_id';
|
||||
}
|
||||
|
||||
$group = isset($options['group']) ? '$' . $options['group'] : null;
|
||||
|
||||
$pipeline = [
|
||||
['$match' => (object) $this->parseWhere($query, $options['where'])],
|
||||
['$group' => ['_id' => $group, 'aggregate' => ['$' . $fun => '$' . $field]]],
|
||||
];
|
||||
|
||||
$cmd = [
|
||||
'aggregate' => $options['table'],
|
||||
'allowDiskUse' => true,
|
||||
'pipeline' => $pipeline,
|
||||
'cursor' => new \stdClass,
|
||||
];
|
||||
|
||||
foreach (['explain', 'collation', 'bypassDocumentValidation', 'readConcern'] as $option) {
|
||||
if (isset($options[$option])) {
|
||||
$cmd[$option] = $options[$option];
|
||||
}
|
||||
}
|
||||
|
||||
$command = new Command($cmd);
|
||||
|
||||
$this->log('aggregate', $cmd);
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* 多聚合查询命令, 可以对多个字段进行 group by 操作
|
||||
*
|
||||
* @param Query $query 查询对象
|
||||
* @param array $extra 指令和字段
|
||||
* @return Command
|
||||
*/
|
||||
public function multiAggregate(Query $query, $extra): Command
|
||||
{
|
||||
$options = $query->getOptions();
|
||||
|
||||
[$aggregate, $groupBy] = $extra;
|
||||
|
||||
$groups = ['_id' => []];
|
||||
|
||||
foreach ($groupBy as $field) {
|
||||
$groups['_id'][$field] = '$' . $field;
|
||||
}
|
||||
|
||||
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,
|
||||
'pipeline' => $pipeline,
|
||||
'cursor' => new \stdClass,
|
||||
];
|
||||
|
||||
foreach (['explain', 'collation', 'bypassDocumentValidation', 'readConcern'] as $option) {
|
||||
if (isset($options[$option])) {
|
||||
$cmd[$option] = $options[$option];
|
||||
}
|
||||
}
|
||||
|
||||
$command = new Command($cmd);
|
||||
$this->log('group', $cmd);
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成distinct命令
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param string $field 字段名
|
||||
* @return Command
|
||||
*/
|
||||
public function distinct(Query $query, $field): Command
|
||||
{
|
||||
$options = $query->getOptions();
|
||||
|
||||
$cmd = [
|
||||
'distinct' => $options['table'],
|
||||
'key' => $field,
|
||||
];
|
||||
|
||||
if (!empty($options['where'])) {
|
||||
$cmd['query'] = (object) $this->parseWhere($query, $options['where']);
|
||||
}
|
||||
|
||||
if (isset($options['maxTimeMS'])) {
|
||||
$cmd['maxTimeMS'] = $options['maxTimeMS'];
|
||||
}
|
||||
|
||||
$command = new Command($cmd);
|
||||
|
||||
$this->log('cmd', 'distinct', $cmd);
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有的collection
|
||||
* @access public
|
||||
* @return Command
|
||||
*/
|
||||
public function listcollections(): Command
|
||||
{
|
||||
$cmd = ['listCollections' => 1];
|
||||
$command = new Command($cmd);
|
||||
|
||||
$this->log('cmd', 'listCollections', $cmd);
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据表的状态信息
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @return Command
|
||||
*/
|
||||
public function collStats(Query $query): Command
|
||||
{
|
||||
$options = $query->getOptions();
|
||||
|
||||
$cmd = ['collStats' => $options['table']];
|
||||
$command = new Command($cmd);
|
||||
|
||||
$this->log('cmd', 'collStats', $cmd);
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
protected function log($type, $data, $options = [])
|
||||
{
|
||||
$this->connection->mongoLog($type, $data, $options);
|
||||
}
|
||||
}
|
||||
421
vendor/topthink/think-orm/src/db/builder/Mysql.php
vendored
Normal file
421
vendor/topthink/think-orm/src/db/builder/Mysql.php
vendored
Normal file
@@ -0,0 +1,421 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
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;
|
||||
|
||||
/**
|
||||
* mysql数据库驱动
|
||||
*/
|
||||
class Mysql extends Builder
|
||||
{
|
||||
/**
|
||||
* 查询表达式解析
|
||||
* @var array
|
||||
*/
|
||||
protected $parser = [
|
||||
'parseCompare' => ['=', '<>', '>', '>=', '<', '<='],
|
||||
'parseLike' => ['LIKE', 'NOT LIKE'],
|
||||
'parseBetween' => ['NOT BETWEEN', 'BETWEEN'],
|
||||
'parseIn' => ['NOT IN', 'IN'],
|
||||
'parseExp' => ['EXP'],
|
||||
'parseRegexp' => ['REGEXP', 'NOT REGEXP'],
|
||||
'parseNull' => ['NOT NULL', 'NULL'],
|
||||
'parseBetweenTime' => ['BETWEEN TIME', 'NOT BETWEEN TIME'],
|
||||
'parseTime' => ['< TIME', '> TIME', '<= TIME', '>= TIME'],
|
||||
'parseExists' => ['NOT EXISTS', 'EXISTS'],
|
||||
'parseColumn' => ['COLUMN'],
|
||||
'parseFindInSet' => ['FIND IN SET'],
|
||||
];
|
||||
|
||||
/**
|
||||
* 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 查询对象
|
||||
* @return string
|
||||
*/
|
||||
public function insert(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(
|
||||
['%INSERT%', '%EXTRA%', '%TABLE%', '%PARTITION%', '%SET%', '%DUPLICATE%', '%COMMENT%'],
|
||||
[
|
||||
!empty($options['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
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param array $dataSet 数据集
|
||||
* @param bool $replace 是否replace
|
||||
* @return string
|
||||
*/
|
||||
public function insertAll(Query $query, array $dataSet, bool $replace = false): string
|
||||
{
|
||||
$options = $query->getOptions();
|
||||
|
||||
// 获取绑定信息
|
||||
$bind = $query->getFieldsBindType();
|
||||
|
||||
// 获取合法的字段
|
||||
if ('*' == $options['field']) {
|
||||
$allowFields = array_keys($bind);
|
||||
} else {
|
||||
$allowFields = $options['field'];
|
||||
}
|
||||
|
||||
$fields = [];
|
||||
$values = [];
|
||||
|
||||
foreach ($dataSet as $data) {
|
||||
$data = $this->parseData($query, $data, $allowFields, $bind);
|
||||
|
||||
$values[] = '( ' . implode(',', array_values($data)) . ' )';
|
||||
|
||||
if (!isset($insertFields)) {
|
||||
$insertFields = array_keys($data);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($insertFields as $field) {
|
||||
$fields[] = $this->parseKey($query, $field);
|
||||
}
|
||||
|
||||
return str_replace(
|
||||
['%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 string $exp
|
||||
* @param mixed $value
|
||||
* @param string $field
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRegexp(Query $query, string $key, string $exp, $value, string $field): string
|
||||
{
|
||||
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 . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和表名处理
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $key 字段名
|
||||
* @param bool $strict 严格检测
|
||||
* @return string
|
||||
*/
|
||||
public function parseKey(Query $query, $key, bool $strict = false): string
|
||||
{
|
||||
if (is_int($key)) {
|
||||
return (string) $key;
|
||||
} elseif ($key instanceof Raw) {
|
||||
return $key->getValue();
|
||||
}
|
||||
|
||||
$key = trim($key);
|
||||
|
||||
if (strpos($key, '->') && false === strpos($key, '(')) {
|
||||
// JSON字段支持
|
||||
[$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)) {
|
||||
[$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)) {
|
||||
if (strpos($table, '.')) {
|
||||
$table = str_replace('.', '`.`', $table);
|
||||
}
|
||||
|
||||
$key = '`' . $table . '`.' . $key;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand(Query $query): string
|
||||
{
|
||||
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->getConnection()->getFieldBindType($key));
|
||||
$updates[] = $this->parseKey($query, $key) . " = :" . $name;
|
||||
}
|
||||
}
|
||||
|
||||
return ' ON DUPLICATE KEY UPDATE ' . implode(' , ', $updates) . ' ';
|
||||
}
|
||||
}
|
||||
95
vendor/topthink/think-orm/src/db/builder/Oracle.php
vendored
Normal file
95
vendor/topthink/think-orm/src/db/builder/Oracle.php
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db\builder;
|
||||
|
||||
use think\db\Builder;
|
||||
use think\db\Query;
|
||||
|
||||
/**
|
||||
* Oracle数据库驱动
|
||||
*/
|
||||
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
|
||||
* @return string
|
||||
*/
|
||||
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 {
|
||||
$limitStr = "(numrow>0 AND numrow<=" . $limit[0] . ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $limitStr ? ' WHERE ' . $limitStr : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置锁机制
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @param bool|false $lock
|
||||
* @return string
|
||||
*/
|
||||
protected function parseLock(Query $query, $lock = false): string
|
||||
{
|
||||
if (!$lock) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return ' FOR UPDATE NOWAIT ';
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和表名处理
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param string $key
|
||||
* @param string $strict
|
||||
* @return string
|
||||
*/
|
||||
public function parseKey(Query $query, $key, bool $strict = false): string
|
||||
{
|
||||
$key = trim($key);
|
||||
|
||||
if (strpos($key, '->') && false === strpos($key, '(')) {
|
||||
// JSON字段支持
|
||||
[$field, $name] = explode($key, '->');
|
||||
$key = $field . '."' . $name . '"';
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand(Query $query): string
|
||||
{
|
||||
return 'DBMS_RANDOM.value';
|
||||
}
|
||||
}
|
||||
118
vendor/topthink/think-orm/src/db/builder/Pgsql.php
vendored
Normal file
118
vendor/topthink/think-orm/src/db/builder/Pgsql.php
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db\builder;
|
||||
|
||||
use think\db\Builder;
|
||||
use think\db\Query;
|
||||
use think\db\Raw;
|
||||
|
||||
/**
|
||||
* Pgsql数据库驱动
|
||||
*/
|
||||
class Pgsql extends Builder
|
||||
{
|
||||
/**
|
||||
* 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%';
|
||||
|
||||
/**
|
||||
* limit分析
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $limit
|
||||
* @return string
|
||||
*/
|
||||
public function parseLimit(Query $query, string $limit): string
|
||||
{
|
||||
$limitStr = '';
|
||||
|
||||
if (!empty($limit)) {
|
||||
$limit = explode(',', $limit);
|
||||
if (count($limit) > 1) {
|
||||
$limitStr .= ' LIMIT ' . $limit[1] . ' OFFSET ' . $limit[0] . ' ';
|
||||
} else {
|
||||
$limitStr .= ' LIMIT ' . $limit[0] . ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return $limitStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和表名处理
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $key 字段名
|
||||
* @param bool $strict 严格检测
|
||||
* @return string
|
||||
*/
|
||||
public function parseKey(Query $query, $key, bool $strict = false): string
|
||||
{
|
||||
if (is_int($key)) {
|
||||
return (string) $key;
|
||||
} elseif ($key instanceof Raw) {
|
||||
return $key->getValue();
|
||||
}
|
||||
|
||||
$key = trim($key);
|
||||
|
||||
if (strpos($key, '->') && false === strpos($key, '(')) {
|
||||
// JSON字段支持
|
||||
[$field, $name] = explode('->', $key);
|
||||
$key = '"' . $field . '"' . '->>\'' . $name . '\'';
|
||||
} elseif (strpos($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 ('*' != $key && !preg_match('/[,\"\*\(\).\s]/', $key)) {
|
||||
$key = '"' . $key . '"';
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($table)) {
|
||||
$key = $table . '.' . $key;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand(Query $query): string
|
||||
{
|
||||
return 'RANDOM()';
|
||||
}
|
||||
|
||||
}
|
||||
97
vendor/topthink/think-orm/src/db/builder/Sqlite.php
vendored
Normal file
97
vendor/topthink/think-orm/src/db/builder/Sqlite.php
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db\builder;
|
||||
|
||||
use think\db\Builder;
|
||||
use think\db\Query;
|
||||
use think\db\Raw;
|
||||
|
||||
/**
|
||||
* Sqlite数据库驱动
|
||||
*/
|
||||
class Sqlite extends Builder
|
||||
{
|
||||
/**
|
||||
* limit
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $limit
|
||||
* @return string
|
||||
*/
|
||||
public function parseLimit(Query $query, string $limit): string
|
||||
{
|
||||
$limitStr = '';
|
||||
|
||||
if (!empty($limit)) {
|
||||
$limit = explode(',', $limit);
|
||||
if (count($limit) > 1) {
|
||||
$limitStr .= ' LIMIT ' . $limit[1] . ' OFFSET ' . $limit[0] . ' ';
|
||||
} else {
|
||||
$limitStr .= ' LIMIT ' . $limit[0] . ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return $limitStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand(Query $query): string
|
||||
{
|
||||
return 'RANDOM()';
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和表名处理
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $key 字段名
|
||||
* @param bool $strict 严格检测
|
||||
* @return string
|
||||
*/
|
||||
public function parseKey(Query $query, $key, bool $strict = false): string
|
||||
{
|
||||
if (is_int($key)) {
|
||||
return (string) $key;
|
||||
} elseif ($key instanceof Raw) {
|
||||
return $key->getValue();
|
||||
}
|
||||
|
||||
$key = trim($key);
|
||||
|
||||
if (strpos($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 (isset($table)) {
|
||||
$key = $table . '.' . $key;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
184
vendor/topthink/think-orm/src/db/builder/Sqlsrv.php
vendored
Normal file
184
vendor/topthink/think-orm/src/db/builder/Sqlsrv.php
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\builder;
|
||||
|
||||
use think\db\Builder;
|
||||
use think\db\exception\DbException as Exception;
|
||||
use think\db\Query;
|
||||
use think\db\Raw;
|
||||
|
||||
/**
|
||||
* Sqlsrv数据库驱动
|
||||
*/
|
||||
class Sqlsrv extends Builder
|
||||
{
|
||||
/**
|
||||
* 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%';
|
||||
|
||||
/**
|
||||
* 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分析
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $order
|
||||
* @return string
|
||||
*/
|
||||
protected function parseOrder(Query $query, array $order): string
|
||||
{
|
||||
if (empty($order)) {
|
||||
return ' ORDER BY rand()';
|
||||
}
|
||||
|
||||
$array = [];
|
||||
|
||||
foreach ($order as $key => $val) {
|
||||
if ($val instanceof Raw) {
|
||||
$array[] = $val->getValue();
|
||||
} elseif ('[rand]' == $val) {
|
||||
$array[] = $this->parseRand($query);
|
||||
} else {
|
||||
if (is_numeric($key)) {
|
||||
[$key, $sort] = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
|
||||
} else {
|
||||
$sort = $val;
|
||||
}
|
||||
|
||||
$sort = in_array(strtolower($sort), ['asc', 'desc'], true) ? ' ' . $sort : '';
|
||||
$array[] = $this->parseKey($query, $key, true) . $sort;
|
||||
}
|
||||
}
|
||||
|
||||
return ' ORDER BY ' . implode(',', $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand(Query $query): string
|
||||
{
|
||||
return 'rand()';
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和表名处理
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $key 字段名
|
||||
* @param bool $strict 严格检测
|
||||
* @return string
|
||||
*/
|
||||
public function parseKey(Query $query, $key, bool $strict = false): string
|
||||
{
|
||||
if (is_int($key)) {
|
||||
return (string) $key;
|
||||
} elseif ($key instanceof Raw) {
|
||||
return $key->getValue();
|
||||
}
|
||||
|
||||
$key = trim($key);
|
||||
|
||||
if (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;
|
||||
}
|
||||
|
||||
/**
|
||||
* limit
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $limit
|
||||
* @return string
|
||||
*/
|
||||
protected function parseLimit(Query $query, string $limit): string
|
||||
{
|
||||
if (empty($limit)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$limit = explode(',', $limit);
|
||||
|
||||
if (count($limit) > 1) {
|
||||
$limitStr = '(T1.ROW_NUMBER BETWEEN ' . $limit[0] . ' + 1 AND ' . $limit[0] . ' + ' . $limit[1] . ')';
|
||||
} else {
|
||||
$limitStr = '(T1.ROW_NUMBER BETWEEN 1 AND ' . $limit[0] . ")";
|
||||
}
|
||||
|
||||
return 'WHERE ' . $limitStr;
|
||||
}
|
||||
|
||||
public function selectInsert(Query $query, array $fields, string $table): string
|
||||
{
|
||||
$this->selectSql = $this->selectInsertSql;
|
||||
|
||||
return parent::selectInsert($query, $fields, $table);
|
||||
}
|
||||
|
||||
}
|
||||
107
vendor/topthink/think-orm/src/db/concern/AggregateQuery.php
vendored
Normal file
107
vendor/topthink/think-orm/src/db/concern/AggregateQuery.php
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
229
vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php
vendored
Normal file
229
vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db\concern;
|
||||
|
||||
use think\db\Raw;
|
||||
use think\helper\Str;
|
||||
|
||||
/**
|
||||
* 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, ' ')) {
|
||||
// 使用别名
|
||||
[$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 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, ' ')) {
|
||||
[$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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
516
vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php
vendored
Normal file
516
vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php
vendored
Normal file
@@ -0,0 +1,516 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db\concern;
|
||||
|
||||
use Closure;
|
||||
use think\helper\Str;
|
||||
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
|
||||
* @return Model|null
|
||||
*/
|
||||
public function getModel()
|
||||
{
|
||||
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
|
||||
* @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' . Str::studly($fieldName) . '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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联预载入 In方式
|
||||
* @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;
|
||||
}
|
||||
|
||||
$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
|
||||
* @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('*');
|
||||
}
|
||||
|
||||
$this->model->relationCount($this, (array) $relations, $aggregate, $field, true);
|
||||
}
|
||||
|
||||
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'][$name] = is_array($relation) ? $relation : [$key, $relation, $tag];
|
||||
} else {
|
||||
$this->options['with_cache'][$relation] = [$key, $expire, $tag];
|
||||
}
|
||||
}
|
||||
|
||||
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 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, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @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, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据转换为模型数据集对象
|
||||
* @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, '.')) {
|
||||
[$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, 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);
|
||||
}
|
||||
|
||||
// 模型数据集转换
|
||||
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, '.')) {
|
||||
[$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));
|
||||
|
||||
// 动态获取器
|
||||
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);
|
||||
}
|
||||
|
||||
// JOIN预载入查询
|
||||
if (!$resultSet && !empty($options['with_join'])) {
|
||||
$result->eagerlyResult($result, $options['with_join'], $withRelationAttr, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
106
vendor/topthink/think-orm/src/db/concern/ParamsBind.php
vendored
Normal file
106
vendor/topthink/think-orm/src/db/concern/ParamsBind.php
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
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) . '_' . mt_rand() . '_';
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
248
vendor/topthink/think-orm/src/db/concern/ResultOperation.php
vendored
Normal file
248
vendor/topthink/think-orm/src/db/concern/ResultOperation.php
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db\concern;
|
||||
|
||||
use think\Collection;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\helper\Str;
|
||||
|
||||
/**
|
||||
* 查询数据处理
|
||||
*/
|
||||
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 = Str::snake($name);
|
||||
|
||||
if (strpos($name, '.')) {
|
||||
// 支持JSON字段 获取器定义
|
||||
[$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() : [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找单条记录 不存在返回空数据(或者空模型)
|
||||
* @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);
|
||||
}
|
||||
|
||||
}
|
||||
99
vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php
vendored
Normal file
99
vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
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 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 ?: '');
|
||||
}
|
||||
|
||||
}
|
||||
218
vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php
vendored
Normal file
218
vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db\concern;
|
||||
|
||||
/**
|
||||
* 时间查询支持
|
||||
*/
|
||||
trait TimeFieldQuery
|
||||
{
|
||||
/**
|
||||
* 日期查询表达式
|
||||
* @var array
|
||||
*/
|
||||
protected $timeRule = [
|
||||
'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'],
|
||||
];
|
||||
|
||||
/**
|
||||
* 添加日期或者时间查询规则
|
||||
* @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 - 1] : [$endTime, $startTime - 1], $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');
|
||||
}
|
||||
|
||||
}
|
||||
117
vendor/topthink/think-orm/src/db/concern/Transaction.php
vendored
Normal file
117
vendor/topthink/think-orm/src/db/concern/Transaction.php
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
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
|
||||
* @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();
|
||||
}
|
||||
|
||||
}
|
||||
540
vendor/topthink/think-orm/src/db/concern/WhereQuery.php
vendored
Normal file
540
vendor/topthink/think-orm/src/db/concern/WhereQuery.php
vendored
Normal file
@@ -0,0 +1,540 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
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 (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] : [];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
1066
vendor/topthink/think-orm/src/db/connector/Mongo.php
vendored
Normal file
1066
vendor/topthink/think-orm/src/db/connector/Mongo.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
162
vendor/topthink/think-orm/src/db/connector/Mysql.php
vendored
Normal file
162
vendor/topthink/think-orm/src/db/connector/Mysql.php
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db\connector;
|
||||
|
||||
use PDO;
|
||||
use think\db\PDOConnection;
|
||||
|
||||
/**
|
||||
* mysql数据库驱动
|
||||
*/
|
||||
class Mysql extends PDOConnection
|
||||
{
|
||||
|
||||
/**
|
||||
* 解析pdo连接的dsn信息
|
||||
* @access protected
|
||||
* @param array $config 连接信息
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDsn(array $config): string
|
||||
{
|
||||
if (!empty($config['socket'])) {
|
||||
$dsn = 'mysql:unix_socket=' . $config['socket'];
|
||||
} elseif (!empty($config['hostport'])) {
|
||||
$dsn = 'mysql:host=' . $config['hostname'] . ';port=' . $config['hostport'];
|
||||
} else {
|
||||
$dsn = 'mysql:host=' . $config['hostname'];
|
||||
}
|
||||
$dsn .= ';dbname=' . $config['database'];
|
||||
|
||||
if (!empty($config['charset'])) {
|
||||
$dsn .= ';charset=' . $config['charset'];
|
||||
}
|
||||
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getFields(string $tableName): array
|
||||
{
|
||||
[$tableName] = explode(' ', $tableName);
|
||||
|
||||
if (false === strpos($tableName, '`')) {
|
||||
if (strpos($tableName, '.')) {
|
||||
$tableName = str_replace('.', '`.`', $tableName);
|
||||
}
|
||||
$tableName = '`' . $tableName . '`';
|
||||
}
|
||||
|
||||
$sql = 'SHOW FULL COLUMNS FROM ' . $tableName;
|
||||
$pdo = $this->getPDOStatement($sql);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
if (!empty($result)) {
|
||||
foreach ($result as $key => $val) {
|
||||
$val = array_change_key_case($val);
|
||||
|
||||
$info[$val['field']] = [
|
||||
'name' => $val['field'],
|
||||
'type' => $val['type'],
|
||||
'notnull' => (bool) ('' === $val['null']), // not null is empty, null is yes
|
||||
'default' => $val['default'],
|
||||
'primary' => (strtolower($val['key']) == 'pri'),
|
||||
'autoinc' => (strtolower($val['extra']) == 'auto_increment'),
|
||||
'comment' => $val['comment'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->fieldCase($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据库的表信息
|
||||
* @access public
|
||||
* @param string $dbName
|
||||
* @return array
|
||||
*/
|
||||
public function getTables(string $dbName = ''): array
|
||||
{
|
||||
$sql = !empty($dbName) ? 'SHOW TABLES FROM ' . $dbName : 'SHOW TABLES ';
|
||||
$pdo = $this->getPDOStatement($sql);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
foreach ($result as $key => $val) {
|
||||
$info[$key] = current($val);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
protected function supportSavepoint(): bool
|
||||
{
|
||||
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'");
|
||||
}
|
||||
}
|
||||
117
vendor/topthink/think-orm/src/db/connector/Oracle.php
vendored
Normal file
117
vendor/topthink/think-orm/src/db/connector/Oracle.php
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\connector;
|
||||
|
||||
use PDO;
|
||||
use think\db\BaseQuery;
|
||||
use think\db\PDOConnection;
|
||||
|
||||
/**
|
||||
* Oracle数据库驱动
|
||||
*/
|
||||
class Oracle extends PDOConnection
|
||||
{
|
||||
/**
|
||||
* 解析pdo连接的dsn信息
|
||||
* @access protected
|
||||
* @param array $config 连接信息
|
||||
* @return string
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
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 (+)";
|
||||
|
||||
$pdo = $this->getPDOStatement($sql);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
if ($result) {
|
||||
foreach ($result as $key => $val) {
|
||||
$val = array_change_key_case($val);
|
||||
|
||||
$info[$val['column_name']] = [
|
||||
'name' => $val['column_name'],
|
||||
'type' => $val['data_type'],
|
||||
'notnull' => $val['notnull'],
|
||||
'default' => $val['data_default'],
|
||||
'primary' => $val['pk'],
|
||||
'autoinc' => $val['pk'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->fieldCase($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据库的表信息(暂时实现取得用户表信息)
|
||||
* @access public
|
||||
* @param string $dbName
|
||||
* @return array
|
||||
*/
|
||||
public function getTables(string $dbName = ''): array
|
||||
{
|
||||
$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 BaseQuery $query 查询对象
|
||||
* @param string $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();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function supportSavepoint(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
108
vendor/topthink/think-orm/src/db/connector/Pgsql.php
vendored
Normal file
108
vendor/topthink/think-orm/src/db/connector/Pgsql.php
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\connector;
|
||||
|
||||
use PDO;
|
||||
use think\db\PDOConnection;
|
||||
|
||||
/**
|
||||
* Pgsql数据库驱动
|
||||
*/
|
||||
class Pgsql extends PDOConnection
|
||||
{
|
||||
|
||||
/**
|
||||
* 默认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连接的dsn信息
|
||||
* @access protected
|
||||
* @param array $config 连接信息
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDsn(array $config): string
|
||||
{
|
||||
$dsn = 'pgsql:dbname=' . $config['database'] . ';host=' . $config['hostname'];
|
||||
|
||||
if (!empty($config['hostport'])) {
|
||||
$dsn .= ';port=' . $config['hostport'];
|
||||
}
|
||||
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getFields(string $tableName): array
|
||||
{
|
||||
[$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);
|
||||
$info = [];
|
||||
|
||||
if (!empty($result)) {
|
||||
foreach ($result as $key => $val) {
|
||||
$val = array_change_key_case($val);
|
||||
|
||||
$info[$val['field']] = [
|
||||
'name' => $val['field'],
|
||||
'type' => $val['type'],
|
||||
'notnull' => (bool) ('' !== $val['null']),
|
||||
'default' => $val['default'],
|
||||
'primary' => !empty($val['key']),
|
||||
'autoinc' => (0 === strpos($val['extra'], 'nextval(')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->fieldCase($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据库的表信息
|
||||
* @access public
|
||||
* @param string $dbName
|
||||
* @return array
|
||||
*/
|
||||
public function getTables(string $dbName = ''): array
|
||||
{
|
||||
$sql = "select tablename as Tables_in_test from pg_tables where schemaname ='public'";
|
||||
$pdo = $this->getPDOStatement($sql);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
foreach ($result as $key => $val) {
|
||||
$info[$key] = current($val);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
protected function supportSavepoint(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
96
vendor/topthink/think-orm/src/db/connector/Sqlite.php
vendored
Normal file
96
vendor/topthink/think-orm/src/db/connector/Sqlite.php
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\connector;
|
||||
|
||||
use PDO;
|
||||
use think\db\PDOConnection;
|
||||
|
||||
/**
|
||||
* Sqlite数据库驱动
|
||||
*/
|
||||
class Sqlite extends PDOConnection
|
||||
{
|
||||
|
||||
/**
|
||||
* 解析pdo连接的dsn信息
|
||||
* @access protected
|
||||
* @param array $config 连接信息
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDsn(array $config): string
|
||||
{
|
||||
$dsn = 'sqlite:' . $config['database'];
|
||||
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getFields(string $tableName): array
|
||||
{
|
||||
[$tableName] = explode(' ', $tableName);
|
||||
$sql = 'PRAGMA table_info( ' . $tableName . ' )';
|
||||
|
||||
$pdo = $this->getPDOStatement($sql);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
if (!empty($result)) {
|
||||
foreach ($result as $key => $val) {
|
||||
$val = array_change_key_case($val);
|
||||
|
||||
$info[$val['name']] = [
|
||||
'name' => $val['name'],
|
||||
'type' => $val['type'],
|
||||
'notnull' => 1 === $val['notnull'],
|
||||
'default' => $val['dflt_value'],
|
||||
'primary' => '1' == $val['pk'],
|
||||
'autoinc' => '1' == $val['pk'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->fieldCase($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据库的表信息
|
||||
* @access public
|
||||
* @param string $dbName
|
||||
* @return array
|
||||
*/
|
||||
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->getPDOStatement($sql);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
foreach ($result as $key => $val) {
|
||||
$info[$key] = current($val);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
protected function supportSavepoint(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
122
vendor/topthink/think-orm/src/db/connector/Sqlsrv.php
vendored
Normal file
122
vendor/topthink/think-orm/src/db/connector/Sqlsrv.php
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\connector;
|
||||
|
||||
use PDO;
|
||||
use think\db\PDOConnection;
|
||||
|
||||
/**
|
||||
* Sqlsrv数据库驱动
|
||||
*/
|
||||
class Sqlsrv extends PDOConnection
|
||||
{
|
||||
/**
|
||||
* 默认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连接的dsn信息
|
||||
* @access protected
|
||||
* @param array $config 连接信息
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDsn(array $config): string
|
||||
{
|
||||
$dsn = 'sqlsrv:Database=' . $config['database'] . ';Server=' . $config['hostname'];
|
||||
|
||||
if (!empty($config['hostport'])) {
|
||||
$dsn .= ',' . $config['hostport'];
|
||||
}
|
||||
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getFields(string $tableName): array
|
||||
{
|
||||
[$tableName] = explode(' ', $tableName);
|
||||
|
||||
$sql = "SELECT column_name, data_type, column_default, is_nullable
|
||||
FROM information_schema.tables AS t
|
||||
JOIN information_schema.columns AS c
|
||||
ON t.table_catalog = c.table_catalog
|
||||
AND t.table_schema = c.table_schema
|
||||
AND t.table_name = c.table_name
|
||||
WHERE t.table_name = '$tableName'";
|
||||
|
||||
$pdo = $this->getPDOStatement($sql);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
if (!empty($result)) {
|
||||
foreach ($result as $key => $val) {
|
||||
$val = array_change_key_case($val);
|
||||
|
||||
$info[$val['column_name']] = [
|
||||
'name' => $val['column_name'],
|
||||
'type' => $val['data_type'],
|
||||
'notnull' => (bool) ('' === $val['is_nullable']), // not null is empty, null is yes
|
||||
'default' => $val['column_default'],
|
||||
'primary' => false,
|
||||
'autoinc' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$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) {
|
||||
$info[$result['column_name']]['primary'] = true;
|
||||
}
|
||||
|
||||
return $this->fieldCase($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $dbName
|
||||
* @return array
|
||||
*/
|
||||
public function getTables(string $dbName = ''): array
|
||||
{
|
||||
$sql = "SELECT TABLE_NAME
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_TYPE = 'BASE TABLE'
|
||||
";
|
||||
|
||||
$pdo = $this->getPDOStatement($sql);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
foreach ($result as $key => $val) {
|
||||
$info[$key] = current($val);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
}
|
||||
117
vendor/topthink/think-orm/src/db/connector/pgsql.sql
vendored
Normal file
117
vendor/topthink/think-orm/src/db/connector/pgsql.sql
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
CREATE OR REPLACE FUNCTION pgsql_type(a_type varchar) RETURNS varchar AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
v_type varchar;
|
||||
BEGIN
|
||||
IF a_type='int8' THEN
|
||||
v_type:='bigint';
|
||||
ELSIF a_type='int4' THEN
|
||||
v_type:='integer';
|
||||
ELSIF a_type='int2' THEN
|
||||
v_type:='smallint';
|
||||
ELSIF a_type='bpchar' THEN
|
||||
v_type:='char';
|
||||
ELSE
|
||||
v_type:=a_type;
|
||||
END IF;
|
||||
RETURN v_type;
|
||||
END;
|
||||
$BODY$
|
||||
LANGUAGE PLPGSQL;
|
||||
|
||||
CREATE TYPE "public"."tablestruct" AS (
|
||||
"fields_key_name" varchar(100),
|
||||
"fields_name" VARCHAR(200),
|
||||
"fields_type" VARCHAR(20),
|
||||
"fields_length" BIGINT,
|
||||
"fields_not_null" VARCHAR(10),
|
||||
"fields_default" VARCHAR(500),
|
||||
"fields_comment" VARCHAR(1000)
|
||||
);
|
||||
|
||||
CREATE OR REPLACE FUNCTION "public"."table_msg" (a_schema_name varchar, a_table_name varchar) RETURNS SETOF "public"."tablestruct" AS
|
||||
$body$
|
||||
DECLARE
|
||||
v_ret tablestruct;
|
||||
v_oid oid;
|
||||
v_sql varchar;
|
||||
v_rec RECORD;
|
||||
v_key varchar;
|
||||
BEGIN
|
||||
SELECT
|
||||
pg_class.oid INTO v_oid
|
||||
FROM
|
||||
pg_class
|
||||
INNER JOIN pg_namespace ON (pg_class.relnamespace = pg_namespace.oid AND lower(pg_namespace.nspname) = a_schema_name)
|
||||
WHERE
|
||||
pg_class.relname=a_table_name;
|
||||
IF NOT FOUND THEN
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
v_sql='
|
||||
SELECT
|
||||
pg_attribute.attname AS fields_name,
|
||||
pg_attribute.attnum AS fields_index,
|
||||
pgsql_type(pg_type.typname::varchar) AS fields_type,
|
||||
pg_attribute.atttypmod-4 as fields_length,
|
||||
CASE WHEN pg_attribute.attnotnull THEN ''not null''
|
||||
ELSE ''''
|
||||
END AS fields_not_null,
|
||||
pg_attrdef.adsrc AS fields_default,
|
||||
pg_description.description AS fields_comment
|
||||
FROM
|
||||
pg_attribute
|
||||
INNER JOIN pg_class ON pg_attribute.attrelid = pg_class.oid
|
||||
INNER JOIN pg_type ON pg_attribute.atttypid = pg_type.oid
|
||||
LEFT OUTER JOIN pg_attrdef ON pg_attrdef.adrelid = pg_class.oid AND pg_attrdef.adnum = pg_attribute.attnum
|
||||
LEFT OUTER JOIN pg_description ON pg_description.objoid = pg_class.oid AND pg_description.objsubid = pg_attribute.attnum
|
||||
WHERE
|
||||
pg_attribute.attnum > 0
|
||||
AND attisdropped <> ''t''
|
||||
AND pg_class.oid = ' || v_oid || '
|
||||
ORDER BY pg_attribute.attnum' ;
|
||||
|
||||
FOR v_rec IN EXECUTE v_sql LOOP
|
||||
v_ret.fields_name=v_rec.fields_name;
|
||||
v_ret.fields_type=v_rec.fields_type;
|
||||
IF v_rec.fields_length > 0 THEN
|
||||
v_ret.fields_length:=v_rec.fields_length;
|
||||
ELSE
|
||||
v_ret.fields_length:=NULL;
|
||||
END IF;
|
||||
v_ret.fields_not_null=v_rec.fields_not_null;
|
||||
v_ret.fields_default=v_rec.fields_default;
|
||||
v_ret.fields_comment=v_rec.fields_comment;
|
||||
SELECT constraint_name INTO v_key FROM information_schema.key_column_usage WHERE table_schema=a_schema_name AND table_name=a_table_name AND column_name=v_rec.fields_name;
|
||||
IF FOUND THEN
|
||||
v_ret.fields_key_name=v_key;
|
||||
ELSE
|
||||
v_ret.fields_key_name='';
|
||||
END IF;
|
||||
RETURN NEXT v_ret;
|
||||
END LOOP;
|
||||
RETURN ;
|
||||
END;
|
||||
$body$
|
||||
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;
|
||||
|
||||
COMMENT ON FUNCTION "public"."table_msg"(a_schema_name varchar, a_table_name varchar)
|
||||
IS '获得表信息';
|
||||
|
||||
---重载一个函数
|
||||
CREATE OR REPLACE FUNCTION "public"."table_msg" (a_table_name varchar) RETURNS SETOF "public"."tablestruct" AS
|
||||
$body$
|
||||
DECLARE
|
||||
v_ret tablestruct;
|
||||
BEGIN
|
||||
FOR v_ret IN SELECT * FROM table_msg('public',a_table_name) LOOP
|
||||
RETURN NEXT v_ret;
|
||||
END LOOP;
|
||||
RETURN;
|
||||
END;
|
||||
$body$
|
||||
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;
|
||||
|
||||
COMMENT ON FUNCTION "public"."table_msg"(a_table_name varchar)
|
||||
IS '获得表信息';
|
||||
35
vendor/topthink/think-orm/src/db/exception/BindParamException.php
vendored
Normal file
35
vendor/topthink/think-orm/src/db/exception/BindParamException.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db\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);
|
||||
}
|
||||
}
|
||||
43
vendor/topthink/think-orm/src/db/exception/DataNotFoundException.php
vendored
Normal file
43
vendor/topthink/think-orm/src/db/exception/DataNotFoundException.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db\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;
|
||||
}
|
||||
}
|
||||
81
vendor/topthink/think-orm/src/db/exception/DbException.php
vendored
Normal file
81
vendor/topthink/think-orm/src/db/exception/DbException.php
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db\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数据
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
21
vendor/topthink/think-orm/src/db/exception/InvalidArgumentException.php
vendored
Normal file
21
vendor/topthink/think-orm/src/db/exception/InvalidArgumentException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
namespace think\db\exception;
|
||||
|
||||
use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInvalidArgumentInterface;
|
||||
|
||||
/**
|
||||
* 非法数据异常
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements SimpleCacheInvalidArgumentInterface
|
||||
{
|
||||
}
|
||||
19
vendor/topthink/think-orm/src/db/exception/ModelEventException.php
vendored
Normal file
19
vendor/topthink/think-orm/src/db/exception/ModelEventException.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\exception;
|
||||
|
||||
/**
|
||||
* 模型事件异常
|
||||
*/
|
||||
class ModelEventException extends DbException
|
||||
{
|
||||
}
|
||||
44
vendor/topthink/think-orm/src/db/exception/ModelNotFoundException.php
vendored
Normal file
44
vendor/topthink/think-orm/src/db/exception/ModelNotFoundException.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db\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;
|
||||
}
|
||||
|
||||
}
|
||||
41
vendor/topthink/think-orm/src/db/exception/PDOException.php
vendored
Normal file
41
vendor/topthink/think-orm/src/db/exception/PDOException.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\db\exception;
|
||||
|
||||
/**
|
||||
* PDO异常处理类
|
||||
* 重新封装了系统的\PDOException类
|
||||
*/
|
||||
class PDOException extends DbException
|
||||
{
|
||||
/**
|
||||
* PDOException constructor.
|
||||
* @access public
|
||||
* @param \PDOException $exception
|
||||
* @param array $config
|
||||
* @param string $sql
|
||||
* @param int $code
|
||||
*/
|
||||
public function __construct(\PDOException $exception, array $config = [], string $sql = '', int $code = 10501)
|
||||
{
|
||||
$error = $exception->errorInfo;
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
86
vendor/topthink/think-orm/src/facade/Db.php
vendored
Normal file
86
vendor/topthink/think-orm/src/facade/Db.php
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \think\DbManager
|
||||
* @mixin \think\DbManager
|
||||
*/
|
||||
class Db extends Facade
|
||||
{
|
||||
/**
|
||||
* 获取当前Facade对应类名(或者已经绑定的容器对象标识)
|
||||
* @access protected
|
||||
* @return string
|
||||
*/
|
||||
protected static function getFacadeClass()
|
||||
{
|
||||
return 'think\DbManager';
|
||||
}
|
||||
}
|
||||
250
vendor/topthink/think-orm/src/model/Collection.php
vendored
Normal file
250
vendor/topthink/think-orm/src/model/Collection.php
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 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;
|
||||
|
||||
use think\Collection as BaseCollection;
|
||||
use think\Model;
|
||||
use think\Paginator;
|
||||
|
||||
/**
|
||||
* 模型数据集类
|
||||
*/
|
||||
class Collection extends BaseCollection
|
||||
{
|
||||
/**
|
||||
* 延迟预载入关联查询
|
||||
* @access public
|
||||
* @param array|string $relation 关联
|
||||
* @param mixed $cache 关联缓存
|
||||
* @return $this
|
||||
*/
|
||||
public function load($relation, $cache = false)
|
||||
{
|
||||
if (!$this->isEmpty()) {
|
||||
$item = current($this->items);
|
||||
$item->eagerlyResultSet($this->items, (array) $relation, [], false, $cache);
|
||||
}
|
||||
|
||||
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
|
||||
* @param array $hidden 属性列表
|
||||
* @return $this
|
||||
*/
|
||||
public function hidden(array $hidden)
|
||||
{
|
||||
$this->each(function (Model $model) use ($hidden) {
|
||||
$model->hidden($hidden);
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要输出的属性
|
||||
* @access public
|
||||
* @param array $visible
|
||||
* @return $this
|
||||
*/
|
||||
public function visible(array $visible)
|
||||
{
|
||||
$this->each(function (Model $model) use ($visible) {
|
||||
$model->visible($visible);
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要追加的输出属性
|
||||
* @access public
|
||||
* @param array $append 属性列表
|
||||
* @return $this
|
||||
*/
|
||||
public function append(array $append)
|
||||
{
|
||||
$this->each(function (Model $model) use ($append) {
|
||||
$model->append($append);
|
||||
});
|
||||
|
||||
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
|
||||
* @param string|array $name 字段名
|
||||
* @param callable $callback 闭包获取器
|
||||
* @return $this
|
||||
*/
|
||||
public function withAttr($name, $callback = null)
|
||||
{
|
||||
$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(string $relation, array $attrs = [])
|
||||
{
|
||||
$this->each(function (Model $model) use ($relation, $attrs) {
|
||||
$model->bindAttr($relation, $attrs);
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
53
vendor/topthink/think-orm/src/model/Pivot.php
vendored
Normal file
53
vendor/topthink/think-orm/src/model/Pivot.php
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 多对多中间表模型类
|
||||
*/
|
||||
class Pivot extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* 父模型
|
||||
* @var Model
|
||||
*/
|
||||
public $parent;
|
||||
|
||||
/**
|
||||
* 是否时间自动写入
|
||||
* @var bool
|
||||
*/
|
||||
protected $autoWriteTimestamp = false;
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $data 数据
|
||||
* @param Model $parent 上级模型
|
||||
* @param string $table 中间数据表名
|
||||
*/
|
||||
public function __construct(array $data = [], Model $parent = null, string $table = '')
|
||||
{
|
||||
$this->parent = $parent;
|
||||
|
||||
if (is_null($this->name)) {
|
||||
$this->name = $table;
|
||||
}
|
||||
|
||||
parent::__construct($data);
|
||||
}
|
||||
|
||||
}
|
||||
258
vendor/topthink/think-orm/src/model/Relation.php
vendored
Normal file
258
vendor/topthink/think-orm/src/model/Relation.php
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
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;
|
||||
|
||||
/**
|
||||
* 模型关联基础类
|
||||
* @package think\model
|
||||
* @mixin Query
|
||||
*/
|
||||
abstract class Relation
|
||||
{
|
||||
/**
|
||||
* 父模型对象
|
||||
* @var Model
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* 当前关联的模型类名
|
||||
* @var string
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* 关联模型查询对象
|
||||
* @var Query
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* 关联表外键
|
||||
* @var string
|
||||
*/
|
||||
protected $foreignKey;
|
||||
|
||||
/**
|
||||
* 关联表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $localKey;
|
||||
|
||||
/**
|
||||
* 是否执行关联基础查询
|
||||
* @var bool
|
||||
*/
|
||||
protected $baseQuery;
|
||||
|
||||
/**
|
||||
* 是否为自关联
|
||||
* @var bool
|
||||
*/
|
||||
protected $selfRelation = false;
|
||||
|
||||
/**
|
||||
* 关联数据数量限制
|
||||
* @var int
|
||||
*/
|
||||
protected $withLimit;
|
||||
|
||||
/**
|
||||
* 关联数据字段限制
|
||||
* @var array
|
||||
*/
|
||||
protected $withField;
|
||||
|
||||
/**
|
||||
* 获取关联的所属模型
|
||||
* @access public
|
||||
* @return Model
|
||||
*/
|
||||
public function getParent(): Model
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的关联模型类的Query实例
|
||||
* @access public
|
||||
* @return Query
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的关联模型类的实例
|
||||
* @access public
|
||||
* @return Model
|
||||
*/
|
||||
public function getModel(): Model
|
||||
{
|
||||
return $this->query->getModel();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前关联是否为自关联
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isSelfRelation(): bool
|
||||
{
|
||||
return $this->selfRelation;
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装关联数据集
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param Model $parent 父模型
|
||||
* @return mixed
|
||||
*/
|
||||
protected function resultSetBuild(array $resultSet, Model $parent = null)
|
||||
{
|
||||
return (new $this->model)->toCollection($resultSet)->setParent($parent);
|
||||
}
|
||||
|
||||
protected function getQueryFields(string $model)
|
||||
{
|
||||
$fields = $this->query->getOptions('field');
|
||||
return $this->getRelationQueryFields($fields, $model);
|
||||
}
|
||||
|
||||
protected function getRelationQueryFields($fields, string $model)
|
||||
{
|
||||
if (empty($fields) || '*' == $fields) {
|
||||
return $model . '.*';
|
||||
}
|
||||
|
||||
if (is_string($fields)) {
|
||||
$fields = explode(',', $fields);
|
||||
}
|
||||
|
||||
foreach ($fields as &$field) {
|
||||
if (false === strpos($field, '.')) {
|
||||
$field = $model . '.' . $field;
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
protected function getQueryWhere(array &$where, string $relation): void
|
||||
{
|
||||
foreach ($where as $key => &$val) {
|
||||
if (is_string($key)) {
|
||||
$where[] = [false === strpos($key, '.') ? $relation . '.' . $key : $key, '=', $val];
|
||||
unset($where[$key]);
|
||||
} elseif (isset($val[0]) && false === strpos($val[0], '.')) {
|
||||
$val[0] = $relation . '.' . $val[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @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
|
||||
* @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
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getClosureType(Closure $closure)
|
||||
{
|
||||
$reflect = new ReflectionFunction($closure);
|
||||
$params = $reflect->getParameters();
|
||||
|
||||
if (!empty($params)) {
|
||||
$type = $params[0]->getType();
|
||||
return Relation::class == $type || is_null($type) ? $this : $this->query;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(仅执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery(): void
|
||||
{}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if ($this->query) {
|
||||
// 执行基础查询
|
||||
$this->baseQuery();
|
||||
|
||||
$result = call_user_func_array([$this->query, $method], $args);
|
||||
|
||||
return $result === $this->query ? $this : $result;
|
||||
}
|
||||
|
||||
throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
|
||||
}
|
||||
}
|
||||
651
vendor/topthink/think-orm/src/model/concern/Attribute.php
vendored
Normal file
651
vendor/topthink/think-orm/src/model/concern/Attribute.php
vendored
Normal file
@@ -0,0 +1,651 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\model\concern;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use think\db\Raw;
|
||||
use think\helper\Str;
|
||||
use think\model\Relation;
|
||||
|
||||
/**
|
||||
* 模型数据处理
|
||||
*/
|
||||
trait Attribute
|
||||
{
|
||||
/**
|
||||
* 数据表主键 复合主键使用数组定义
|
||||
* @var string|array
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
/**
|
||||
* 数据表字段信息 留空则自动获取
|
||||
* @var array
|
||||
*/
|
||||
protected $schema = [];
|
||||
|
||||
/**
|
||||
* 当前允许写入的字段
|
||||
* @var array
|
||||
*/
|
||||
protected $field = [];
|
||||
|
||||
/**
|
||||
* 字段自动类型转换
|
||||
* @var array
|
||||
*/
|
||||
protected $type = [];
|
||||
|
||||
/**
|
||||
* 数据表废弃字段
|
||||
* @var array
|
||||
*/
|
||||
protected $disuse = [];
|
||||
|
||||
/**
|
||||
* 数据表只读字段
|
||||
* @var array
|
||||
*/
|
||||
protected $readonly = [];
|
||||
|
||||
/**
|
||||
* 当前模型数据
|
||||
* @var array
|
||||
*/
|
||||
private $data = [];
|
||||
|
||||
/**
|
||||
* 原始数据
|
||||
* @var array
|
||||
*/
|
||||
private $origin = [];
|
||||
|
||||
/**
|
||||
* JSON数据表字段
|
||||
* @var array
|
||||
*/
|
||||
protected $json = [];
|
||||
|
||||
/**
|
||||
* JSON数据表字段类型
|
||||
* @var array
|
||||
*/
|
||||
protected $jsonType = [];
|
||||
|
||||
/**
|
||||
* JSON数据取出是否需要转换为数组
|
||||
* @var bool
|
||||
*/
|
||||
protected $jsonAssoc = false;
|
||||
|
||||
/**
|
||||
* 是否严格字段大小写
|
||||
* @var bool
|
||||
*/
|
||||
protected $strict = true;
|
||||
|
||||
/**
|
||||
* 修改器执行记录
|
||||
* @var array
|
||||
*/
|
||||
private $set = [];
|
||||
|
||||
/**
|
||||
* 动态获取器
|
||||
* @var array
|
||||
*/
|
||||
private $withAttr = [];
|
||||
|
||||
/**
|
||||
* 获取模型对象的主键
|
||||
* @access public
|
||||
* @return string|array
|
||||
*/
|
||||
public function getPk()
|
||||
{
|
||||
return $this->pk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断一个字段名是否为主键字段
|
||||
* @access public
|
||||
* @param string $key 名称
|
||||
* @return bool
|
||||
*/
|
||||
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)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型对象的主键值
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function getKey()
|
||||
{
|
||||
$pk = $this->getPk();
|
||||
|
||||
if (is_string($pk) && array_key_exists($pk, $this->data)) {
|
||||
return $this->data[$pk];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置允许写入的字段
|
||||
* @access public
|
||||
* @param array $field 允许写入的字段
|
||||
* @return $this
|
||||
*/
|
||||
public function allowField(array $field)
|
||||
{
|
||||
$this->field = $field;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置只读字段
|
||||
* @access public
|
||||
* @param array $field 只读字段
|
||||
* @return $this
|
||||
*/
|
||||
public function readOnly(array $field)
|
||||
{
|
||||
$this->readonly = $field;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实际的字段名
|
||||
* @access protected
|
||||
* @param string $name 字段名
|
||||
* @return string
|
||||
*/
|
||||
protected function getRealFieldName(string $name): string
|
||||
{
|
||||
return $this->strict ? $name : Str::snake($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置数据对象值
|
||||
* @access public
|
||||
* @param array $data 数据
|
||||
* @param bool $set 是否调用修改器
|
||||
* @param array $allow 允许的字段名
|
||||
* @return $this
|
||||
*/
|
||||
public function data(array $data, bool $set = false, array $allow = [])
|
||||
{
|
||||
// 清空数据
|
||||
$this->data = [];
|
||||
|
||||
// 废弃字段
|
||||
foreach ($this->disuse as $key) {
|
||||
if (array_key_exists($key, $data)) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($allow)) {
|
||||
$result = [];
|
||||
foreach ($allow as $name) {
|
||||
if (isset($data[$name])) {
|
||||
$result[$name] = $data[$name];
|
||||
}
|
||||
}
|
||||
$data = $result;
|
||||
}
|
||||
|
||||
if ($set) {
|
||||
// 数据对象赋值
|
||||
$this->setAttrs($data);
|
||||
} else {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量追加数据对象值
|
||||
* @access public
|
||||
* @param array $data 数据
|
||||
* @param bool $set 是否需要进行数据处理
|
||||
* @return $this
|
||||
*/
|
||||
public function appendData(array $data, bool $set = false)
|
||||
{
|
||||
if ($set) {
|
||||
$this->setAttrs($data);
|
||||
} else {
|
||||
$this->data = array_merge($this->data, $data);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象原始数据 如果不存在指定字段返回null
|
||||
* @access public
|
||||
* @param string $name 字段名 留空获取全部
|
||||
* @return mixed
|
||||
*/
|
||||
public function getOrigin(string $name = null)
|
||||
{
|
||||
if (is_null($name)) {
|
||||
return $this->origin;
|
||||
}
|
||||
|
||||
return array_key_exists($name, $this->origin) ? $this->origin[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象原始数据 如果不存在指定字段返回false
|
||||
* @access public
|
||||
* @param string $name 字段名 留空获取全部
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getData(string $name = null)
|
||||
{
|
||||
if (is_null($name)) {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
$fieldName = $this->getRealFieldName($name);
|
||||
|
||||
if (array_key_exists($fieldName, $this->data)) {
|
||||
return $this->data[$fieldName];
|
||||
} elseif (array_key_exists($fieldName, $this->relation)) {
|
||||
return $this->relation[$fieldName];
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取变化的数据 并排除只读数据
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getChangedData(): array
|
||||
{
|
||||
$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;
|
||||
});
|
||||
|
||||
// 只读字段不允许更新
|
||||
foreach ($this->readonly as $key => $field) {
|
||||
if (isset($data[$field])) {
|
||||
unset($data[$field]);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接设置数据对象值
|
||||
* @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 array $data 数据
|
||||
* @return void
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) {
|
||||
// 自动写入的时间戳字段
|
||||
$value = $this->autoWriteTimestamp();
|
||||
} else {
|
||||
// 检测修改器
|
||||
$method = 'set' . Str::studly($name) . '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]);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置数据对象属性
|
||||
$this->data[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据写入 类型转换
|
||||
* @access protected
|
||||
* @param mixed $value 值
|
||||
* @param string|array $type 要转换的类型
|
||||
* @return mixed
|
||||
*/
|
||||
protected function writeTransform($value, $type)
|
||||
{
|
||||
if (is_null($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($value instanceof Raw) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_array($type)) {
|
||||
[$type, $param] = $type;
|
||||
} elseif (strpos($type, ':')) {
|
||||
[$type, $param] = explode(':', $type, 2);
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'integer':
|
||||
$value = (int) $value;
|
||||
break;
|
||||
case 'float':
|
||||
if (empty($param)) {
|
||||
$value = (float) $value;
|
||||
} else {
|
||||
$value = (float) number_format($value, $param, '.', '');
|
||||
}
|
||||
break;
|
||||
case 'boolean':
|
||||
$value = (bool) $value;
|
||||
break;
|
||||
case 'timestamp':
|
||||
if (!is_numeric($value)) {
|
||||
$value = strtotime($value);
|
||||
}
|
||||
break;
|
||||
case 'datetime':
|
||||
$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)) {
|
||||
$value = json_encode($value, JSON_FORCE_OBJECT);
|
||||
}
|
||||
break;
|
||||
case 'array':
|
||||
$value = (array) $value;
|
||||
case 'json':
|
||||
$option = !empty($param) ? (int) $param : JSON_UNESCAPED_UNICODE;
|
||||
$value = json_encode($value, $option);
|
||||
break;
|
||||
case 'serialize':
|
||||
$value = serialize($value);
|
||||
break;
|
||||
default:
|
||||
if (is_object($value) && false !== strpos($type, '\\') && method_exists($value, '__toString')) {
|
||||
// 对象类型
|
||||
$value = $value->__toString();
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取器 获取数据对象的值
|
||||
* @access public
|
||||
* @param string $name 名称
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getAttr(string $name)
|
||||
{
|
||||
try {
|
||||
$relation = false;
|
||||
$value = $this->getData($name);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$relation = $this->isRelationAttr($name);
|
||||
$value = null;
|
||||
}
|
||||
|
||||
return $this->getValue($name, $value, $relation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取经过获取器处理后的数据对象的值
|
||||
* @access protected
|
||||
* @param string $name 字段名称
|
||||
* @param mixed $value 字段值
|
||||
* @param bool|string $relation 是否为关联属性或者关联名
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function getValue(string $name, $value, $relation = false)
|
||||
{
|
||||
// 检测属性获取器
|
||||
$fieldName = $this->getRealFieldName($name);
|
||||
$method = 'get' . Str::studly($name) . 'Attr';
|
||||
|
||||
if (isset($this->withAttr[$fieldName])) {
|
||||
if ($relation) {
|
||||
$value = $this->getRelationValue($relation);
|
||||
}
|
||||
|
||||
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($relation);
|
||||
}
|
||||
|
||||
$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);
|
||||
// 保存关联对象值
|
||||
$this->relation[$name] = $value;
|
||||
}
|
||||
|
||||
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
|
||||
* @param string $relation 关联名
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getRelationValue(string $relation)
|
||||
{
|
||||
$modelRelation = $this->$relation();
|
||||
|
||||
return $modelRelation instanceof Relation ? $this->getRelationData($modelRelation) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据读取 类型转换
|
||||
* @access protected
|
||||
* @param mixed $value 值
|
||||
* @param string|array $type 要转换的类型
|
||||
* @return mixed
|
||||
*/
|
||||
protected function readTransform($value, $type)
|
||||
{
|
||||
if (is_null($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_array($type)) {
|
||||
[$type, $param] = $type;
|
||||
} elseif (strpos($type, ':')) {
|
||||
[$type, $param] = explode(':', $type, 2);
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'integer':
|
||||
$value = (int) $value;
|
||||
break;
|
||||
case 'float':
|
||||
if (empty($param)) {
|
||||
$value = (float) $value;
|
||||
} else {
|
||||
$value = (float) number_format($value, $param, '.', '');
|
||||
}
|
||||
break;
|
||||
case 'boolean':
|
||||
$value = (bool) $value;
|
||||
break;
|
||||
case 'timestamp':
|
||||
if (!is_null($value)) {
|
||||
$format = !empty($param) ? $param : $this->dateFormat;
|
||||
$value = $this->formatDateTime($format, $value, true);
|
||||
}
|
||||
break;
|
||||
case 'datetime':
|
||||
if (!is_null($value)) {
|
||||
$format = !empty($param) ? $param : $this->dateFormat;
|
||||
$value = $this->formatDateTime($format, $value);
|
||||
}
|
||||
break;
|
||||
case 'json':
|
||||
$value = json_decode($value, true);
|
||||
break;
|
||||
case 'array':
|
||||
$value = empty($value) ? [] : json_decode($value, true);
|
||||
break;
|
||||
case 'object':
|
||||
$value = empty($value) ? new \stdClass() : json_decode($value);
|
||||
break;
|
||||
case 'serialize':
|
||||
try {
|
||||
$value = unserialize($value);
|
||||
} catch (\Exception $e) {
|
||||
$value = null;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (false !== strpos($type, '\\')) {
|
||||
// 对象类型
|
||||
$value = new $type($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置数据字段获取器
|
||||
* @access public
|
||||
* @param string|array $name 字段名
|
||||
* @param callable $callback 闭包获取器
|
||||
* @return $this
|
||||
*/
|
||||
public function withAttribute($name, callable $callback = null)
|
||||
{
|
||||
if (is_array($name)) {
|
||||
foreach ($name as $key => $val) {
|
||||
$this->withAttribute($key, $val);
|
||||
}
|
||||
} else {
|
||||
$name = $this->getRealFieldName($name);
|
||||
|
||||
if (strpos($name, '.')) {
|
||||
[$name, $key] = explode('.', $name);
|
||||
|
||||
$this->withAttr[$name][$key] = $callback;
|
||||
} else {
|
||||
$this->withAttr[$name] = $callback;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
285
vendor/topthink/think-orm/src/model/concern/Conversion.php
vendored
Normal file
285
vendor/topthink/think-orm/src/model/concern/Conversion.php
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\model\concern;
|
||||
|
||||
use think\Collection;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 模型数据转换处理
|
||||
*/
|
||||
trait Conversion
|
||||
{
|
||||
/**
|
||||
* 数据输出显示的属性
|
||||
* @var array
|
||||
*/
|
||||
protected $visible = [];
|
||||
|
||||
/**
|
||||
* 数据输出隐藏的属性
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = [];
|
||||
|
||||
/**
|
||||
* 数据输出需要追加的属性
|
||||
* @var array
|
||||
*/
|
||||
protected $append = [];
|
||||
|
||||
/**
|
||||
* 数据集对象名
|
||||
* @var string
|
||||
*/
|
||||
protected $resultSetType;
|
||||
|
||||
/**
|
||||
* 设置需要附加的输出属性
|
||||
* @access public
|
||||
* @param array $append 属性列表
|
||||
* @return $this
|
||||
*/
|
||||
public function append(array $append = [])
|
||||
{
|
||||
$this->append = $append;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置附加关联对象的属性
|
||||
* @access public
|
||||
* @param string $attr 关联属性
|
||||
* @param string|array $append 追加属性名
|
||||
* @return $this
|
||||
* @throws Exception
|
||||
*/
|
||||
public function appendRelationAttr(string $attr, array $append)
|
||||
{
|
||||
$relation = Str::camel($attr);
|
||||
|
||||
if (isset($this->relation[$relation])) {
|
||||
$model = $this->relation[$relation];
|
||||
} else {
|
||||
$model = $this->getRelationData($this->$relation());
|
||||
}
|
||||
|
||||
if ($model instanceof Model) {
|
||||
foreach ($append as $key => $attr) {
|
||||
$key = is_numeric($key) ? $attr : $key;
|
||||
if (isset($this->data[$key])) {
|
||||
throw new Exception('bind attr has exists:' . $key);
|
||||
}
|
||||
|
||||
$this->data[$key] = $model->$attr;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要隐藏的输出属性
|
||||
* @access public
|
||||
* @param array $hidden 属性列表
|
||||
* @return $this
|
||||
*/
|
||||
public function hidden(array $hidden = [])
|
||||
{
|
||||
$this->hidden = $hidden;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要输出的属性
|
||||
* @access public
|
||||
* @param array $visible
|
||||
* @return $this
|
||||
*/
|
||||
public function visible(array $visible = [])
|
||||
{
|
||||
$this->visible = $visible;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换当前模型对象为数组
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$item = [];
|
||||
$hasVisible = false;
|
||||
|
||||
|
||||
foreach ($this->visible as $key => $val) {
|
||||
if (is_string($val)) {
|
||||
if (strpos($val, '.')) {
|
||||
[$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, '.')) {
|
||||
[$relation, $name] = explode('.', $val);
|
||||
$this->hidden[$relation][] = $name;
|
||||
} else {
|
||||
$this->hidden[$val] = true;
|
||||
}
|
||||
unset($this->hidden[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// 合并关联数据
|
||||
$data = array_merge($this->data, $this->relation);
|
||||
|
||||
foreach ($data as $key => $val) {
|
||||
if ($val instanceof Model || $val instanceof ModelCollection) {
|
||||
// 关联模型对象
|
||||
if (isset($this->visible[$key]) && is_array($this->visible[$key])) {
|
||||
$val->visible($this->visible[$key]);
|
||||
} elseif (isset($this->hidden[$key]) && is_array($this->hidden[$key])) {
|
||||
$val->hidden($this->hidden[$key]);
|
||||
}
|
||||
// 关联模型对象
|
||||
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) {
|
||||
$item[$key] = $this->getAttr($key);
|
||||
}
|
||||
}
|
||||
|
||||
// 追加属性(必须定义获取器)
|
||||
foreach ($this->append as $key => $name) {
|
||||
$this->appendAttrToArray($item, $key, $name);
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
protected function appendAttrToArray(array &$item, $key, $name)
|
||||
{
|
||||
if (is_array($name)) {
|
||||
|
||||
|
||||
// 追加关联对象属性
|
||||
$relation = $this->getRelation($key, true);
|
||||
$item[$key] = $relation ? $relation->append($name)
|
||||
->toArray() : [];
|
||||
} elseif (strpos($name, '.')) {
|
||||
|
||||
|
||||
[$key, $attr] = explode('.', $name);
|
||||
// 追加关联对象属性
|
||||
$relation = $this->getRelation($key, true);
|
||||
$item[$key] = $relation ? $relation->append([$attr])
|
||||
->toArray() : [];
|
||||
} else {
|
||||
|
||||
$value = $this->getAttr($name);
|
||||
|
||||
$item[$name] = $value;
|
||||
|
||||
$this->getBindAttr($name, $value, $item);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getBindAttr(string $name, $value, array &$item = [])
|
||||
{
|
||||
$relation = $this->isRelationAttr($name);
|
||||
if (!$relation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换当前模型对象为JSON字符串
|
||||
* @access public
|
||||
* @param integer $options json参数
|
||||
* @return string
|
||||
*/
|
||||
public function toJson(int $options = JSON_UNESCAPED_UNICODE): string
|
||||
{
|
||||
return json_encode($this->toArray(), $options);
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->toJson();
|
||||
}
|
||||
|
||||
// JsonSerializable
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换数据集为数据集对象
|
||||
* @access public
|
||||
* @param array|Collection $collection 数据集
|
||||
* @param string $resultSetType 数据集类
|
||||
* @return Collection
|
||||
*/
|
||||
public function toCollection(iterable $collection = [], string $resultSetType = null): Collection
|
||||
{
|
||||
$resultSetType = $resultSetType ?: $this->resultSetType;
|
||||
|
||||
if ($resultSetType && false !== strpos($resultSetType, '\\')) {
|
||||
$collection = new $resultSetType($collection);
|
||||
} else {
|
||||
$collection = new ModelCollection($collection);
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
}
|
||||
88
vendor/topthink/think-orm/src/model/concern/ModelEvent.php
vendored
Normal file
88
vendor/topthink/think-orm/src/model/concern/ModelEvent.php
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\model\concern;
|
||||
|
||||
use think\db\exception\ModelEventException;
|
||||
use think\helper\Str;
|
||||
|
||||
/**
|
||||
* 模型事件处理
|
||||
*/
|
||||
trait ModelEvent
|
||||
{
|
||||
|
||||
/**
|
||||
* Event对象
|
||||
* @var object
|
||||
*/
|
||||
protected static $event;
|
||||
|
||||
/**
|
||||
* 是否需要事件响应
|
||||
* @var bool
|
||||
*/
|
||||
protected $withEvent = true;
|
||||
|
||||
/**
|
||||
* 设置Event对象
|
||||
* @access public
|
||||
* @param object $event Event对象
|
||||
* @return void
|
||||
*/
|
||||
public static function setEvent($event)
|
||||
{
|
||||
self::$event = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前操作的事件响应
|
||||
* @access protected
|
||||
* @param bool $event 是否需要事件响应
|
||||
* @return $this
|
||||
*/
|
||||
public function withEvent(bool $event)
|
||||
{
|
||||
$this->withEvent = $event;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
* @access protected
|
||||
* @param string $event 事件名
|
||||
* @return bool
|
||||
*/
|
||||
protected function trigger(string $event): bool
|
||||
{
|
||||
if (!$this->withEvent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$call = 'on' . Str::studly($event);
|
||||
|
||||
try {
|
||||
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 = empty($result) ? true : end($result);
|
||||
} else {
|
||||
$result = true;
|
||||
}
|
||||
|
||||
return false === $result ? false : true;
|
||||
} catch (ModelEventException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
85
vendor/topthink/think-orm/src/model/concern/OptimLock.php
vendored
Normal file
85
vendor/topthink/think-orm/src/model/concern/OptimLock.php
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\model\concern;
|
||||
|
||||
use think\db\exception\DbException as Exception;
|
||||
|
||||
/**
|
||||
* 乐观锁
|
||||
*/
|
||||
trait OptimLock
|
||||
{
|
||||
protected function getOptimLockField()
|
||||
{
|
||||
return property_exists($this, 'optimLock') && isset($this->optimLock) ? $this->optimLock : 'lock_version';
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据检查
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function checkData(): void
|
||||
{
|
||||
$this->isExists() ? $this->updateLockVersion() : $this->recordLockVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录乐观锁
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function recordLockVersion(): void
|
||||
{
|
||||
$optimLock = $this->getOptimLockField();
|
||||
|
||||
if ($optimLock) {
|
||||
$this->set($optimLock, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新乐观锁
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function updateLockVersion(): void
|
||||
{
|
||||
$optimLock = $this->getOptimLockField();
|
||||
|
||||
if ($optimLock && $lockVer = $this->getOrigin($optimLock)) {
|
||||
// 更新乐观锁
|
||||
$this->set($optimLock, $lockVer + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public function getWhere()
|
||||
{
|
||||
$where = parent::getWhere();
|
||||
$optimLock = $this->getOptimLockField();
|
||||
|
||||
if ($optimLock && $lockVer = $this->getOrigin($optimLock)) {
|
||||
$where[] = [$optimLock, '=', $lockVer];
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
protected function checkResult($result): void
|
||||
{
|
||||
if (!$result) {
|
||||
throw new Exception('record has update');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
781
vendor/topthink/think-orm/src/model/concern/RelationShip.php
vendored
Normal file
781
vendor/topthink/think-orm/src/model/concern/RelationShip.php
vendored
Normal file
@@ -0,0 +1,781 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\model\concern;
|
||||
|
||||
use Closure;
|
||||
use think\Collection;
|
||||
use think\db\BaseQuery as Query;
|
||||
use think\db\exception\DbException as Exception;
|
||||
use think\helper\Str;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
use think\model\relation\BelongsTo;
|
||||
use think\model\relation\BelongsToMany;
|
||||
use think\model\relation\HasMany;
|
||||
use think\model\relation\HasManyThrough;
|
||||
use think\model\relation\HasOne;
|
||||
use think\model\relation\HasOneThrough;
|
||||
use think\model\relation\MorphMany;
|
||||
use think\model\relation\MorphOne;
|
||||
use think\model\relation\MorphTo;
|
||||
use think\model\relation\OneToOne;
|
||||
|
||||
/**
|
||||
* 模型关联处理
|
||||
*/
|
||||
trait RelationShip
|
||||
{
|
||||
/**
|
||||
* 父关联模型对象
|
||||
* @var object
|
||||
*/
|
||||
private $parent;
|
||||
|
||||
/**
|
||||
* 模型关联数据
|
||||
* @var array
|
||||
*/
|
||||
private $relation = [];
|
||||
|
||||
/**
|
||||
* 关联写入定义信息
|
||||
* @var array
|
||||
*/
|
||||
private $together = [];
|
||||
|
||||
/**
|
||||
* 关联自动写入信息
|
||||
* @var array
|
||||
*/
|
||||
protected $relationWrite = [];
|
||||
|
||||
/**
|
||||
* 设置父关联对象
|
||||
* @access public
|
||||
* @param Model $model 模型对象
|
||||
* @return $this
|
||||
*/
|
||||
public function setParent(Model $model)
|
||||
{
|
||||
$this->parent = $model;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取父关联对象
|
||||
* @access public
|
||||
* @return Model
|
||||
*/
|
||||
public function getParent(): Model
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前模型的关联模型数据
|
||||
* @access public
|
||||
* @param string $name 关联方法名
|
||||
* @param bool $auto 不存在是否自动获取
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRelation(string $name = null, bool $auto = false)
|
||||
{
|
||||
if (is_null($name)) {
|
||||
return $this->relation;
|
||||
}
|
||||
|
||||
if (array_key_exists($name, $this->relation)) {
|
||||
return $this->relation[$name];
|
||||
} elseif ($auto) {
|
||||
$relation = Str::camel($name);
|
||||
return $this->getRelationValue($relation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置关联数据对象值
|
||||
* @access public
|
||||
* @param string $name 属性名
|
||||
* @param mixed $value 属性值
|
||||
* @param array $data 数据
|
||||
* @return $this
|
||||
*/
|
||||
public function setRelation(string $name, $value, array $data = [])
|
||||
{
|
||||
// 检测修改器
|
||||
$method = 'set' . Str::studly($name) . 'Attr';
|
||||
|
||||
if (method_exists($this, $method)) {
|
||||
$value = $this->$method($value, array_merge($this->data, $data));
|
||||
}
|
||||
|
||||
$this->relation[$this->getRealFieldName($name)] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前模型的关联数据
|
||||
* @access public
|
||||
* @param array $relations 关联名
|
||||
* @param array $withRelationAttr 关联获取器
|
||||
* @return void
|
||||
*/
|
||||
public function relationQuery(array $relations, array $withRelationAttr = []): void
|
||||
{
|
||||
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, '.')) {
|
||||
[$relation, $subRelation] = explode('.', $relation, 2);
|
||||
}
|
||||
|
||||
$method = Str::camel($relation);
|
||||
$relationName = Str::snake($relation);
|
||||
|
||||
$relationResult = $this->$method();
|
||||
|
||||
if (isset($withRelationAttr[$relationName])) {
|
||||
$relationResult->withAttr($withRelationAttr[$relationName]);
|
||||
}
|
||||
|
||||
$this->relation[$relation] = $relationResult->getRelation($subRelation, $closure);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联数据写入
|
||||
* @access public
|
||||
* @param array $relation 关联
|
||||
* @return $this
|
||||
*/
|
||||
public function together(array $relation)
|
||||
{
|
||||
$this->together = $relation;
|
||||
|
||||
$this->checkAutoRelationWrite();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $relation 关联方法名
|
||||
* @param mixed $operator 比较操作符
|
||||
* @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 $query = null): Query
|
||||
{
|
||||
return (new static())
|
||||
->$relation()
|
||||
->has($operator, $count, $id, $joinType, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $relation 关联方法名
|
||||
* @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 $query = null): Query
|
||||
{
|
||||
return (new static())
|
||||
->$relation()
|
||||
->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
|
||||
* @param array $resultSet 数据集
|
||||
* @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, $cache = false): void
|
||||
{
|
||||
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, '.')) {
|
||||
[$relation, $subRelation] = explode('.', $relation, 2);
|
||||
|
||||
$subRelation = [$subRelation];
|
||||
}
|
||||
|
||||
$relationName = $relation;
|
||||
$relation = Str::camel($relation);
|
||||
|
||||
$relationResult = $this->$relation();
|
||||
|
||||
if (isset($withRelationAttr[$relationName])) {
|
||||
$relationResult->withAttr($withRelationAttr[$relationName]);
|
||||
}
|
||||
|
||||
if (is_scalar($cache)) {
|
||||
$relationCache = [$cache];
|
||||
} else {
|
||||
$relationCache = $cache[$relationName] ?? $cache;
|
||||
}
|
||||
|
||||
$relationResult->eagerlyResultSet($resultSet, $relationName, $subRelation, $closure, $relationCache, $join);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询 返回模型对象
|
||||
* @access public
|
||||
* @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, $cache = false): void
|
||||
{
|
||||
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, '.')) {
|
||||
[$relation, $subRelation] = explode('.', $relation, 2);
|
||||
|
||||
$subRelation = [$subRelation];
|
||||
}
|
||||
|
||||
$relationName = $relation;
|
||||
$relation = Str::camel($relation);
|
||||
|
||||
$relationResult = $this->$relation();
|
||||
|
||||
if (isset($withRelationAttr[$relationName])) {
|
||||
$relationResult->withAttr($withRelationAttr[$relationName]);
|
||||
}
|
||||
|
||||
if (is_scalar($cache)) {
|
||||
$relationCache = [$cache];
|
||||
} else {
|
||||
$relationCache = $cache[$relationName] ?? [];
|
||||
}
|
||||
|
||||
$relationResult->eagerlyResult($result, $relationName, $subRelation, $closure, $relationCache, $join);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定(一对一)关联属性到当前模型
|
||||
* @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);
|
||||
}
|
||||
|
||||
$this->set($key, $relation ? $relation->$attr : null);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @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
|
||||
{
|
||||
foreach ($relations as $key => $relation) {
|
||||
$closure = $name = null;
|
||||
|
||||
if ($relation instanceof Closure) {
|
||||
$closure = $relation;
|
||||
$relation = $key;
|
||||
} elseif (is_string($key)) {
|
||||
$name = $relation;
|
||||
$relation = $key;
|
||||
}
|
||||
|
||||
$relation = Str::camel($relation);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if ($useSubQuery) {
|
||||
$query->field(['(' . $count . ')' => $name]);
|
||||
} else {
|
||||
$this->setAttr($name, $count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HAS ONE 关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 当前主键
|
||||
* @return HasOne
|
||||
*/
|
||||
public function hasOne(string $model, string $foreignKey = '', string $localKey = ''): HasOne
|
||||
{
|
||||
// 记录当前关联信息
|
||||
$model = $this->parseModel($model);
|
||||
$localKey = $localKey ?: $this->getPk();
|
||||
$foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
|
||||
|
||||
return new HasOne($this, $model, $foreignKey, $localKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* BELONGS TO 关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function belongsTo(string $model, string $foreignKey = '', string $localKey = ''): BelongsTo
|
||||
{
|
||||
// 记录当前关联信息
|
||||
$model = $this->parseModel($model);
|
||||
$foreignKey = $foreignKey ?: $this->getForeignKey((new $model)->getName());
|
||||
$localKey = $localKey ?: (new $model)->getPk();
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
$relation = Str::snake($trace[1]['function']);
|
||||
|
||||
return new BelongsTo($this, $model, $foreignKey, $localKey, $relation);
|
||||
}
|
||||
|
||||
/**
|
||||
* HAS MANY 关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 当前主键
|
||||
* @return HasMany
|
||||
*/
|
||||
public function hasMany(string $model, string $foreignKey = '', string $localKey = ''): HasMany
|
||||
{
|
||||
// 记录当前关联信息
|
||||
$model = $this->parseModel($model);
|
||||
$localKey = $localKey ?: $this->getPk();
|
||||
$foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
|
||||
|
||||
return new HasMany($this, $model, $foreignKey, $localKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* HAS MANY 远程关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string $through 中间模型名
|
||||
* @param string $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 = '', 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);
|
||||
$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 HasOneThrough($this, $model, $through, $foreignKey, $throughKey, $localKey, $throughPk);
|
||||
}
|
||||
|
||||
/**
|
||||
* BELONGS TO MANY 关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string $middle 中间表/模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 当前模型关联键
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function belongsToMany(string $model, string $middle = '', string $foreignKey = '', string $localKey = ''): BelongsToMany
|
||||
{
|
||||
// 记录当前关联信息
|
||||
$model = $this->parseModel($model);
|
||||
$name = Str::snake(class_basename($model));
|
||||
$middle = $middle ?: Str::snake($this->name) . '_' . $name;
|
||||
$foreignKey = $foreignKey ?: $name . '_id';
|
||||
$localKey = $localKey ?: $this->getForeignKey($this->name);
|
||||
|
||||
return new BelongsToMany($this, $model, $middle, $foreignKey, $localKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* MORPH One 关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string|array $morph 多态字段信息
|
||||
* @param string $type 多态类型
|
||||
* @return MorphOne
|
||||
*/
|
||||
public function morphOne(string $model, $morph = null, string $type = ''): MorphOne
|
||||
{
|
||||
// 记录当前关联信息
|
||||
$model = $this->parseModel($model);
|
||||
|
||||
if (is_null($morph)) {
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
$morph = Str::snake($trace[1]['function']);
|
||||
}
|
||||
|
||||
if (is_array($morph)) {
|
||||
[$morphType, $foreignKey] = $morph;
|
||||
} else {
|
||||
$morphType = $morph . '_type';
|
||||
$foreignKey = $morph . '_id';
|
||||
}
|
||||
|
||||
$type = $type ?: get_class($this);
|
||||
|
||||
return new MorphOne($this, $model, $foreignKey, $morphType, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* MORPH MANY 关联定义
|
||||
* @access public
|
||||
* @param string $model 模型名
|
||||
* @param string|array $morph 多态字段信息
|
||||
* @param string $type 多态类型
|
||||
* @return MorphMany
|
||||
*/
|
||||
public function morphMany(string $model, $morph = null, string $type = ''): MorphMany
|
||||
{
|
||||
// 记录当前关联信息
|
||||
$model = $this->parseModel($model);
|
||||
|
||||
if (is_null($morph)) {
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
$morph = Str::snake($trace[1]['function']);
|
||||
}
|
||||
|
||||
$type = $type ?: get_class($this);
|
||||
|
||||
if (is_array($morph)) {
|
||||
[$morphType, $foreignKey] = $morph;
|
||||
} else {
|
||||
$morphType = $morph . '_type';
|
||||
$foreignKey = $morph . '_id';
|
||||
}
|
||||
|
||||
return new MorphMany($this, $model, $foreignKey, $morphType, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* MORPH TO 关联定义
|
||||
* @access public
|
||||
* @param string|array $morph 多态字段信息
|
||||
* @param array $alias 多态别名定义
|
||||
* @return MorphTo
|
||||
*/
|
||||
public function morphTo($morph = null, array $alias = []): MorphTo
|
||||
{
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
$relation = Str::snake($trace[1]['function']);
|
||||
|
||||
if (is_null($morph)) {
|
||||
$morph = $relation;
|
||||
}
|
||||
|
||||
// 记录当前关联信息
|
||||
if (is_array($morph)) {
|
||||
[$morphType, $foreignKey] = $morph;
|
||||
} else {
|
||||
$morphType = $morph . '_type';
|
||||
$foreignKey = $morph . '_id';
|
||||
}
|
||||
|
||||
return new MorphTo($this, $morphType, $foreignKey, $alias, $relation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析模型的完整命名空间
|
||||
* @access protected
|
||||
* @param string $model 模型名(或者完整类名)
|
||||
* @return string
|
||||
*/
|
||||
protected function parseModel(string $model): string
|
||||
{
|
||||
if (false === strpos($model, '\\')) {
|
||||
$path = explode('\\', static::class);
|
||||
array_pop($path);
|
||||
array_push($path, Str::studly($model));
|
||||
$model = implode('\\', $path);
|
||||
}
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型的默认外键名
|
||||
* @access protected
|
||||
* @param string $name 模型名
|
||||
* @return string
|
||||
*/
|
||||
protected function getForeignKey(string $name): string
|
||||
{
|
||||
if (strpos($name, '\\')) {
|
||||
$name = class_basename($name);
|
||||
}
|
||||
|
||||
return Str::snake($name) . '_id';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查属性是否为关联属性 如果是则返回关联方法名
|
||||
* @access protected
|
||||
* @param string $attr 关联属性名
|
||||
* @return string|false
|
||||
*/
|
||||
protected function isRelationAttr(string $attr)
|
||||
{
|
||||
$relation = Str::camel($attr);
|
||||
|
||||
if (method_exists($this, $relation) && !method_exists('think\Model', $relation)) {
|
||||
return $relation;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能获取关联模型数据
|
||||
* @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())) {
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
// 获取关联数据
|
||||
return $modelRelation->getRelation();
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联数据自动写入检查
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function checkAutoRelationWrite(): void
|
||||
{
|
||||
foreach ($this->together as $key => $name) {
|
||||
if (is_array($name)) {
|
||||
if (key($name) === 0) {
|
||||
$this->relationWrite[$key] = [];
|
||||
// 绑定关联属性
|
||||
foreach ($name as $val) {
|
||||
if (isset($this->data[$val])) {
|
||||
$this->relationWrite[$key][$val] = $this->data[$val];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 直接传入关联数据
|
||||
$this->relationWrite[$key] = $name;
|
||||
}
|
||||
} elseif (isset($this->relation[$name])) {
|
||||
$this->relationWrite[$name] = $this->relation[$name];
|
||||
} elseif (isset($this->data[$name])) {
|
||||
$this->relationWrite[$name] = $this->data[$name];
|
||||
unset($this->data[$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动关联数据更新(针对一对一关联)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function autoRelationUpdate(): void
|
||||
{
|
||||
foreach ($this->relationWrite as $name => $val) {
|
||||
if ($val instanceof Model) {
|
||||
$val->exists(true)->save();
|
||||
} else {
|
||||
$model = $this->getRelation($name, true);
|
||||
|
||||
if ($model instanceof Model) {
|
||||
$model->exists(true)->save($val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动关联数据写入(针对一对一关联)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function autoRelationInsert(): void
|
||||
{
|
||||
foreach ($this->relationWrite as $name => $val) {
|
||||
$method = Str::camel($name);
|
||||
$this->$method()->save($val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动关联数据删除(支持一对一及一对多关联)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function autoRelationDelete(): void
|
||||
{
|
||||
foreach ($this->relationWrite as $key => $name) {
|
||||
$name = is_numeric($key) ? $name : $key;
|
||||
$result = $this->getRelation($name, true);
|
||||
|
||||
if ($result instanceof Model) {
|
||||
$result->delete();
|
||||
} elseif ($result instanceof Collection) {
|
||||
foreach ($result as $model) {
|
||||
$model->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除当前模型的关联属性
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function removeRelation()
|
||||
{
|
||||
$this->relation = [];
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
246
vendor/topthink/think-orm/src/model/concern/SoftDelete.php
vendored
Normal file
246
vendor/topthink/think-orm/src/model/concern/SoftDelete.php
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\model\concern;
|
||||
|
||||
use think\db\BaseQuery as Query;
|
||||
|
||||
/**
|
||||
* 数据软删除
|
||||
*/
|
||||
trait SoftDelete
|
||||
{
|
||||
/**
|
||||
* 是否包含软删除数据
|
||||
* @var bool
|
||||
*/
|
||||
protected $withTrashed = false;
|
||||
|
||||
/**
|
||||
* 判断当前实例是否被软删除
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function trashed(): bool
|
||||
{
|
||||
$field = $this->getDeleteTimeField();
|
||||
|
||||
if ($field && !empty($this->getOrigin($field))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询软删除数据
|
||||
* @access public
|
||||
* @return Query
|
||||
*/
|
||||
public static function withTrashed(): Query
|
||||
{
|
||||
$model = new static();
|
||||
|
||||
return $model->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 protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getWithTrashedExp(): array
|
||||
{
|
||||
return is_null($this->defaultSoftDelete) ? ['notnull', ''] : ['<>', $this->defaultSoftDelete];
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前的记录
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function delete(): bool
|
||||
{
|
||||
if (!$this->isExists() || $this->isEmpty() || false === $this->trigger('BeforeDelete')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$name = $this->getDeleteTimeField();
|
||||
|
||||
if ($name && !$this->isForce()) {
|
||||
// 软删除
|
||||
$this->set($name, $this->autoWriteTimestamp($name));
|
||||
|
||||
$result = $this->exists()->withEvent(false)->save();
|
||||
|
||||
$this->withEvent(true);
|
||||
} else {
|
||||
// 读取更新条件
|
||||
$where = $this->getWhere();
|
||||
|
||||
// 删除当前模型数据
|
||||
$result = $this->db()
|
||||
->where($where)
|
||||
->removeOption('soft_delete')
|
||||
->delete();
|
||||
|
||||
$this->lazySave(false);
|
||||
}
|
||||
|
||||
// 关联删除
|
||||
if (!empty($this->relationWrite)) {
|
||||
$this->autoRelationDelete();
|
||||
}
|
||||
|
||||
$this->trigger('AfterDelete');
|
||||
|
||||
$this->exists(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除记录
|
||||
* @access public
|
||||
* @param mixed $data 主键列表 支持闭包查询条件
|
||||
* @param bool $force 是否强制删除
|
||||
* @return bool
|
||||
*/
|
||||
public static function destroy($data, bool $force = false): bool
|
||||
{
|
||||
// 包含软删除数据
|
||||
$query = (new static())->db(false);
|
||||
|
||||
if (is_array($data) && key($data) !== 0) {
|
||||
$query->where($data);
|
||||
$data = null;
|
||||
} elseif ($data instanceof \Closure) {
|
||||
call_user_func_array($data, [ & $query]);
|
||||
$data = null;
|
||||
} elseif (is_null($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$resultSet = $query->select($data);
|
||||
|
||||
foreach ($resultSet as $result) {
|
||||
$result->force($force)->delete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复被软删除的记录
|
||||
* @access public
|
||||
* @param array $where 更新条件
|
||||
* @return bool
|
||||
*/
|
||||
public function restore($where = []): bool
|
||||
{
|
||||
$name = $this->getDeleteTimeField();
|
||||
|
||||
if (!$name || false === $this->trigger('BeforeRestore')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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('AfterRestore');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取软删除字段
|
||||
* @access protected
|
||||
* @param bool $read 是否查询操作 写操作的时候会自动去掉表别名
|
||||
* @return string|false
|
||||
*/
|
||||
protected function getDeleteTimeField(bool $read = false)
|
||||
{
|
||||
$field = property_exists($this, 'deleteTime') && isset($this->deleteTime) ? $this->deleteTime : 'delete_time';
|
||||
|
||||
if (false === $field) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false === strpos($field, '.')) {
|
||||
$field = '__TABLE__.' . $field;
|
||||
}
|
||||
|
||||
if (!$read && strpos($field, '.')) {
|
||||
$array = explode('.', $field);
|
||||
$field = array_pop($array);
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询的时候默认排除软删除数据
|
||||
* @access protected
|
||||
* @param Query $query
|
||||
* @return void
|
||||
*/
|
||||
protected function withNoTrashed(Query $query): void
|
||||
{
|
||||
$field = $this->getDeleteTimeField(true);
|
||||
|
||||
if ($field) {
|
||||
$condition = is_null($this->defaultSoftDelete) ? ['null', ''] : ['=', $this->defaultSoftDelete];
|
||||
$query->useSoftDelete($field, $condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
208
vendor/topthink/think-orm/src/model/concern/TimeStamp.php
vendored
Normal file
208
vendor/topthink/think-orm/src/model/concern/TimeStamp.php
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
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;
|
||||
|
||||
/**
|
||||
* 是否需要自动写入时间字段
|
||||
* @access public
|
||||
* @param bool|string $auto
|
||||
* @return $this
|
||||
*/
|
||||
public function isAutoWriteTimestamp($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
|
||||
* @return bool|string
|
||||
*/
|
||||
public function getAutoWriteTimestamp()
|
||||
{
|
||||
return $this->autoWriteTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置时间字段格式化
|
||||
* @access public
|
||||
* @param string|false $format
|
||||
* @return $this
|
||||
*/
|
||||
public function setDateFormat($format)
|
||||
{
|
||||
$this->dateFormat = $format;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自动写入时间字段
|
||||
* @access public
|
||||
* @return string|false
|
||||
*/
|
||||
public function getDateFormat()
|
||||
{
|
||||
return $this->dateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动写入时间戳
|
||||
* @access protected
|
||||
* @return mixed
|
||||
*/
|
||||
protected function autoWriteTimestamp()
|
||||
{
|
||||
// 检测时间字段类型
|
||||
$type = $this->checkTimeFieldType($this->autoWriteTimestamp);
|
||||
|
||||
return is_string($type) ? $this->getTimeTypeValue($type) : time();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定类型的时间字段值
|
||||
* @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, '\\')) {
|
||||
// 对象数据写入
|
||||
$obj = new $type();
|
||||
if (method_exists($obj, '__toString')) {
|
||||
// 对象数据写入
|
||||
$value = $obj->__toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间日期字段格式化处理
|
||||
* @access protected
|
||||
* @param mixed $format 日期格式
|
||||
* @param mixed $time 时间日期表达式
|
||||
* @param bool $timestamp 时间表达式是否为时间戳
|
||||
* @return mixed
|
||||
*/
|
||||
protected function formatDateTime($format, $time = 'now', bool $timestamp = false)
|
||||
{
|
||||
if (empty($time)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (false === $format) {
|
||||
return $time;
|
||||
} elseif (false !== strpos($format, '\\')) {
|
||||
return new $format($time);
|
||||
}
|
||||
|
||||
if ($time instanceof DateTime) {
|
||||
$dateTime = $time;
|
||||
} elseif ($timestamp) {
|
||||
$dateTime = new DateTime();
|
||||
$dateTime->setTimestamp((int) $time);
|
||||
} else {
|
||||
$dateTime = new DateTime($time);
|
||||
}
|
||||
|
||||
return $dateTime->format($format);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取时间字段值
|
||||
* @access protected
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getTimestampValue($value)
|
||||
{
|
||||
$type = $this->checkTimeFieldType($this->autoWriteTimestamp);
|
||||
|
||||
if (is_string($type) && in_array(strtolower($type), [
|
||||
'datetime', 'date', 'timestamp',
|
||||
])) {
|
||||
$value = $this->formatDateTime($this->dateFormat, $value);
|
||||
} else {
|
||||
$value = $this->formatDateTime($this->dateFormat, $value, true);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
331
vendor/topthink/think-orm/src/model/relation/BelongsTo.php
vendored
Normal file
331
vendor/topthink/think-orm/src/model/relation/BelongsTo.php
vendored
Normal file
@@ -0,0 +1,331 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use Closure;
|
||||
use think\db\BaseQuery as Query;
|
||||
use think\helper\Str;
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* BelongsTo关联类
|
||||
*/
|
||||
class BelongsTo extends OneToOne
|
||||
{
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* @param string $relation 关联名
|
||||
*/
|
||||
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->query = (new $model)->db();
|
||||
$this->relation = $relation;
|
||||
|
||||
if (get_class($parent) == $model) {
|
||||
$this->selfRelation = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @access public
|
||||
* @param array $subRelation 子关联名
|
||||
* @param Closure $closure 闭包查询条件
|
||||
* @return Model
|
||||
*/
|
||||
public function getRelation(array $subRelation = [], Closure $closure = null)
|
||||
{
|
||||
if ($closure) {
|
||||
$closure($this->getClosureType($closure));
|
||||
}
|
||||
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$relationModel = $this->query
|
||||
->removeWhereField($this->localKey)
|
||||
->where($this->localKey, $this->parent->$foreignKey)
|
||||
->relation($subRelation)
|
||||
->find();
|
||||
|
||||
if ($relationModel) {
|
||||
if (!empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $this->parent);
|
||||
}
|
||||
|
||||
$relationModel->setParent(clone $this->parent);
|
||||
}
|
||||
|
||||
return $relationModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建关联统计子查询
|
||||
* @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 = '*', &$name = ''): string
|
||||
{
|
||||
if ($closure) {
|
||||
$closure($this->getClosureType($closure), $name);
|
||||
}
|
||||
|
||||
return $this->query
|
||||
->whereExp($this->localKey, '=' . $this->parent->getTable() . '.' . $this->foreignKey)
|
||||
->fetchSql()
|
||||
->$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)
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
if (!isset($result->$foreignKey)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($closure) {
|
||||
$closure($this->getClosureType($closure), $name);
|
||||
}
|
||||
|
||||
return $this->query
|
||||
->where($this->localKey, '=', $result->$foreignKey)
|
||||
->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @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): Query
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
$model = class_basename($this->parent);
|
||||
$relation = class_basename($this->model);
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
$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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @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): Query
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
$model = class_basename($this->parent);
|
||||
$relation = class_basename($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);
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
$query = $query ?: $this->parent->db()->alias($model);
|
||||
|
||||
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);
|
||||
})
|
||||
->where($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @access protected
|
||||
* @param array $resultSet 数据集
|
||||
* @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, array $cache = []): void
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$foreignKey)) {
|
||||
$range[] = $result->$foreignKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$this->query->removeWhereField($localKey);
|
||||
|
||||
$data = $this->eagerlyWhere([
|
||||
[$localKey, 'in', $range],
|
||||
], $localKey, $subRelation, $closure, $cache);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
// 关联模型
|
||||
if (!isset($data[$result->$foreignKey])) {
|
||||
$relationModel = null;
|
||||
} else {
|
||||
$relationModel = $data[$result->$foreignKey];
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->exists(true);
|
||||
}
|
||||
|
||||
if ($relationModel && !empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $result);
|
||||
} else {
|
||||
// 设置关联属性
|
||||
$result->setRelation($relation, $relationModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据)
|
||||
* @access protected
|
||||
* @param Model $result 数据对象
|
||||
* @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, array $cache = []): void
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$this->query->removeWhereField($localKey);
|
||||
|
||||
$data = $this->eagerlyWhere([
|
||||
[$localKey, '=', $result->$foreignKey],
|
||||
], $localKey, $subRelation, $closure, $cache);
|
||||
|
||||
// 关联模型
|
||||
if (!isset($data[$result->$foreignKey])) {
|
||||
$relationModel = null;
|
||||
} else {
|
||||
$relationModel = $data[$result->$foreignKey];
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->exists(true);
|
||||
}
|
||||
|
||||
if ($relationModel && !empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $result);
|
||||
} else {
|
||||
// 设置关联属性
|
||||
$result->setRelation($relation, $relationModel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加关联数据
|
||||
* @access public
|
||||
* @param Model $model关联模型对象
|
||||
* @return Model
|
||||
*/
|
||||
public function associate(Model $model): Model
|
||||
{
|
||||
$this->parent->setAttr($this->foreignKey, $model->getKey());
|
||||
$this->parent->save();
|
||||
|
||||
return $this->parent->setRelation($this->relation, $model);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销关联数据
|
||||
* @access public
|
||||
* @return Model
|
||||
*/
|
||||
public function dissociate(): Model
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$this->parent->setAttr($foreignKey, null);
|
||||
$this->parent->save();
|
||||
|
||||
return $this->parent->setRelation($this->relation, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(仅执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery(): void
|
||||
{
|
||||
if (empty($this->baseQuery)) {
|
||||
if (isset($this->parent->{$this->foreignKey})) {
|
||||
// 关联查询带入关联条件
|
||||
$this->query->where($this->localKey, '=', $this->parent->{$this->foreignKey});
|
||||
}
|
||||
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
707
vendor/topthink/think-orm/src/model/relation/BelongsToMany.php
vendored
Normal file
707
vendor/topthink/think-orm/src/model/relation/BelongsToMany.php
vendored
Normal file
@@ -0,0 +1,707 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use Closure;
|
||||
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;
|
||||
use think\Paginator;
|
||||
|
||||
/**
|
||||
* 多对多关联类
|
||||
*/
|
||||
class BelongsToMany extends Relation
|
||||
{
|
||||
/**
|
||||
* 中间表表名
|
||||
* @var string
|
||||
*/
|
||||
protected $middle;
|
||||
|
||||
/**
|
||||
* 中间表模型名称
|
||||
* @var string
|
||||
*/
|
||||
protected $pivotName;
|
||||
|
||||
/**
|
||||
* 中间表模型对象
|
||||
* @var Pivot
|
||||
*/
|
||||
protected $pivot;
|
||||
|
||||
/**
|
||||
* 中间表数据名称
|
||||
* @var string
|
||||
*/
|
||||
protected $pivotDataName = 'pivot';
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $middle 中间表/模型名
|
||||
* @param string $foreignKey 关联模型外键
|
||||
* @param 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($middle, '\\')) {
|
||||
$this->pivotName = $middle;
|
||||
$this->middle = class_basename($middle);
|
||||
} else {
|
||||
$this->middle = $middle;
|
||||
}
|
||||
|
||||
$this->query = (new $model)->db();
|
||||
$this->pivot = $this->newPivot();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置中间表模型
|
||||
* @access public
|
||||
* @param $pivot
|
||||
* @return $this
|
||||
*/
|
||||
public function pivot(string $pivot)
|
||||
{
|
||||
$this->pivotName = $pivot;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置中间表数据名称
|
||||
* @access public
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
public function name(string $name)
|
||||
{
|
||||
$this->pivotDataName = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化中间表模型
|
||||
* @access public
|
||||
* @param $data
|
||||
* @return Pivot
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function newPivot(array $data = []): Pivot
|
||||
{
|
||||
$class = $this->pivotName ?: Pivot::class;
|
||||
$pivot = new $class($data, $this->parent, $this->middle);
|
||||
|
||||
if ($pivot instanceof Pivot) {
|
||||
return $pivot;
|
||||
} else {
|
||||
throw new Exception('pivot model must extends: \think\model\Pivot');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合成中间表模型
|
||||
* @access protected
|
||||
* @param array|Collection|Paginator $models
|
||||
*/
|
||||
protected function hydratePivot(iterable $models)
|
||||
{
|
||||
foreach ($models as $model) {
|
||||
$pivot = [];
|
||||
|
||||
foreach ($model->getData() as $key => $val) {
|
||||
if (strpos($key, '__')) {
|
||||
[$name, $attr] = explode('__', $key, 2);
|
||||
|
||||
if ('pivot' == $name) {
|
||||
$pivot[$attr] = $val;
|
||||
unset($model->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$model->setRelation($this->pivotDataName, $this->newPivot($pivot));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建关联查询Query对象
|
||||
* @access protected
|
||||
* @return Query
|
||||
*/
|
||||
protected function buildQuery(): Query
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
$localKey = $this->localKey;
|
||||
|
||||
// 关联查询
|
||||
$pk = $this->parent->getPk();
|
||||
|
||||
$condition = ['pivot.' . $localKey, '=', $this->parent->$pk];
|
||||
|
||||
return $this->belongsToManyQuery($foreignKey, $localKey, [$condition]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @access public
|
||||
* @param array $subRelation 子关联名
|
||||
* @param Closure $closure 闭包查询条件
|
||||
* @return Collection
|
||||
*/
|
||||
public function getRelation(array $subRelation = [], Closure $closure = null): Collection
|
||||
{
|
||||
if ($closure) {
|
||||
$closure($this->getClosureType($closure));
|
||||
}
|
||||
|
||||
$result = $this->buildQuery()
|
||||
->relation($subRelation)
|
||||
->select()
|
||||
->setParent(clone $this->parent);
|
||||
|
||||
$this->hydratePivot($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载select方法
|
||||
* @access public
|
||||
* @param mixed $data
|
||||
* @return Collection
|
||||
*/
|
||||
public function select($data = null): Collection
|
||||
{
|
||||
$result = $this->buildQuery()->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
|
||||
{
|
||||
$result = $this->buildQuery()->paginate($listRows, $simple);
|
||||
$this->hydratePivot($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载find方法
|
||||
* @access public
|
||||
* @param mixed $data
|
||||
* @return Model
|
||||
*/
|
||||
public function find($data = null)
|
||||
{
|
||||
$result = $this->buildQuery()->find($data);
|
||||
|
||||
if (!$result->isEmpty()) {
|
||||
$this->hydratePivot([$result]);
|
||||
}
|
||||
|
||||
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
|
||||
* @param string $operator 比较操作符
|
||||
* @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', Query $query = null)
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @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 = '', Query $query = null)
|
||||
{
|
||||
throw new Exception('relation not support: hasWhere');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置中间表的查询条件
|
||||
* @access public
|
||||
* @param string $field
|
||||
* @param string $op
|
||||
* @param mixed $condition
|
||||
* @return $this
|
||||
*/
|
||||
public function wherePivot($field, $op = null, $condition = null)
|
||||
{
|
||||
$this->query->where('pivot.' . $field, $op, $condition);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @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
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$pk = $resultSet[0]->getPk();
|
||||
$range = [];
|
||||
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$pk)) {
|
||||
$range[] = $result->$pk;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
// 查询关联数据
|
||||
$data = $this->eagerlyManyToMany([
|
||||
['pivot.' . $localKey, 'in', $range],
|
||||
], $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->localKey, '=', $pk],
|
||||
], $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 integer
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
$pk = $result->$pk;
|
||||
|
||||
if ($closure) {
|
||||
$closure($this->getClosureType($closure), $name);
|
||||
}
|
||||
|
||||
return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
|
||||
['pivot.' . $this->localKey, '=', $pk],
|
||||
])->$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->belongsToManyQuery($this->foreignKey, $this->localKey, [
|
||||
[
|
||||
'pivot.' . $this->localKey, 'exp', new Raw('=' . $this->parent->db(false)->getTable() . '.' . $this->parent->getPk()),
|
||||
],
|
||||
])->fetchSql()->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多对多 关联模型预查询
|
||||
* @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->foreignKey, $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->localKey];
|
||||
|
||||
if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$set->setRelation($this->pivotDataName, $this->newPivot($pivot));
|
||||
|
||||
$data[$key][] = $set;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @return array|Pivot
|
||||
*/
|
||||
public function save($data, array $pivot = [])
|
||||
{
|
||||
// 保存关联表/中间表数据
|
||||
return $this->attach($data, $pivot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存当前关联数据对象
|
||||
* @access public
|
||||
* @param iterable $dataSet 数据集
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @param bool $samePivot 额外数据是否相同
|
||||
* @return array|false
|
||||
*/
|
||||
public function saveAll(iterable $dataSet, array $pivot = [], bool $samePivot = false)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($dataSet as $key => $data) {
|
||||
if (!$samePivot) {
|
||||
$pivotData = $pivot[$key] ?? [];
|
||||
} else {
|
||||
$pivotData = $pivot;
|
||||
}
|
||||
|
||||
$result[] = $this->attach($data, $pivotData);
|
||||
}
|
||||
|
||||
return empty($result) ? false : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 附加关联的一个中间表数据
|
||||
* @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->localKey] = $this->parent->$pk;
|
||||
$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->localKey, $this->parent->getKey())
|
||||
->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 = [];
|
||||
$pivot[] = [$this->localKey, '=', $this->parent->$pk];
|
||||
|
||||
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->localKey, $this->parent->$pk)
|
||||
->column($this->foreignKey);
|
||||
|
||||
$records = [];
|
||||
|
||||
foreach ($ids as $key => $value) {
|
||||
if (!is_array($value)) {
|
||||
$records[$value] = [];
|
||||
} else {
|
||||
$records[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$detach = array_diff($current, array_keys($records));
|
||||
|
||||
if ($detaching && count($detach) > 0) {
|
||||
$this->detach($detach);
|
||||
$changes['detached'] = $detach;
|
||||
}
|
||||
|
||||
foreach ($records as $id => $attributes) {
|
||||
if (!in_array($id, $current)) {
|
||||
$this->attach($id, $attributes);
|
||||
$changes['attached'][] = $id;
|
||||
} elseif (count($attributes) > 0 && $this->attach($id, $attributes)) {
|
||||
$changes['updated'][] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return $changes;
|
||||
}
|
||||
|
||||
}
|
||||
367
vendor/topthink/think-orm/src/model/relation/HasMany.php
vendored
Normal file
367
vendor/topthink/think-orm/src/model/relation/HasMany.php
vendored
Normal file
@@ -0,0 +1,367 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use Closure;
|
||||
use think\Collection;
|
||||
use think\db\BaseQuery as Query;
|
||||
use think\helper\Str;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
/**
|
||||
* 一对多关联类
|
||||
*/
|
||||
class HasMany extends Relation
|
||||
{
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 当前模型主键
|
||||
*/
|
||||
public function __construct(Model $parent, string $model, string $foreignKey, string $localKey)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->query = (new $model)->db();
|
||||
|
||||
if (get_class($parent) == $model) {
|
||||
$this->selfRelation = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @access public
|
||||
* @param array $subRelation 子关联名
|
||||
* @param Closure $closure 闭包查询条件
|
||||
* @return Collection
|
||||
*/
|
||||
public function getRelation(array $subRelation = [], Closure $closure = null): Collection
|
||||
{
|
||||
if ($closure) {
|
||||
$closure($this->getClosureType($closure));
|
||||
}
|
||||
|
||||
if ($this->withLimit) {
|
||||
$this->query->limit($this->withLimit);
|
||||
}
|
||||
|
||||
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 $cache 关联缓存
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation, Closure $closure = null, array $cache = []): void
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$range = [];
|
||||
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$localKey)) {
|
||||
$range[] = $result->$localKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$data = $this->eagerlyOneToMany([
|
||||
[$this->foreignKey, 'in', $range],
|
||||
], $subRelation, $closure, $cache);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
$pk = $result->$localKey;
|
||||
if (!isset($data[$pk])) {
|
||||
$data[$pk] = [];
|
||||
}
|
||||
|
||||
$result->setRelation($relation, $this->resultSetBuild($data[$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
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
|
||||
if (isset($result->$localKey)) {
|
||||
$pk = $result->$localKey;
|
||||
$data = $this->eagerlyOneToMany([
|
||||
[$this->foreignKey, '=', $pk],
|
||||
], $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 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) {
|
||||
$closure($this->getClosureType($closure), $name);
|
||||
}
|
||||
|
||||
return $this->query
|
||||
->where($this->foreignKey, '=', $result->$localKey)
|
||||
->$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->alias($aggregate . '_table')
|
||||
->whereExp($aggregate . '_table.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
|
||||
->fetchSql()
|
||||
->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 一对多 关联模型预查询
|
||||
* @access public
|
||||
* @param array $where 关联预查询条件
|
||||
* @param array $subRelation 子关联
|
||||
* @param Closure $closure
|
||||
* @param array $cache 关联缓存
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyOneToMany(array $where, array $subRelation = [], Closure $closure = null, array $cache = []): array
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$this->query->removeWhereField($this->foreignKey);
|
||||
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
if ($closure) {
|
||||
$this->baseQuery = true;
|
||||
$closure($this->getClosureType($closure));
|
||||
}
|
||||
|
||||
$list = $this->query
|
||||
->where($where)
|
||||
->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
|
||||
->with($subRelation)
|
||||
->select();
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
|
||||
foreach ($list as $set) {
|
||||
$key = $set->$foreignKey;
|
||||
|
||||
if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[$key][] = $set;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象
|
||||
* @param boolean $replace 是否自动识别更新和写入
|
||||
* @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();
|
||||
}
|
||||
|
||||
// 保存关联表数据
|
||||
$data[$this->foreignKey] = $this->parent->{$this->localKey};
|
||||
|
||||
return new $this->model($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存当前关联数据对象
|
||||
* @access public
|
||||
* @param iterable $dataSet 数据集
|
||||
* @param boolean $replace 是否自动识别更新和写入
|
||||
* @return array|false
|
||||
*/
|
||||
public function saveAll(iterable $dataSet, bool $replace = true)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($dataSet as $key => $data) {
|
||||
$result[] = $this->save($data, $replace);
|
||||
}
|
||||
|
||||
return empty($result) ? false : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @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 = 'INNER', Query $query = null): Query
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
|
||||
$model = class_basename($this->parent);
|
||||
$relation = class_basename($this->model);
|
||||
|
||||
if ('*' != $id) {
|
||||
$id = $relation . '.' . (new $this->model)->getPk();
|
||||
}
|
||||
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
$query = $query ?: $this->parent->db()->alias($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);
|
||||
})
|
||||
->group($relation . '.' . $this->foreignKey)
|
||||
->having('count(' . $id . ')' . $operator . $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @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): Query
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
$model = class_basename($this->parent);
|
||||
$relation = class_basename($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);
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
$query = $query ?: $this->parent->db()->alias($model);
|
||||
|
||||
return $query->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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(仅执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery(): void
|
||||
{
|
||||
if (empty($this->baseQuery)) {
|
||||
if (isset($this->parent->{$this->localKey})) {
|
||||
// 关联查询带入关联条件
|
||||
$this->query->where($this->foreignKey, '=', $this->parent->{$this->localKey});
|
||||
}
|
||||
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
382
vendor/topthink/think-orm/src/model/relation/HasManyThrough.php
vendored
Normal file
382
vendor/topthink/think-orm/src/model/relation/HasManyThrough.php
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use Closure;
|
||||
use think\Collection;
|
||||
use think\db\BaseQuery as Query;
|
||||
use think\helper\Str;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
/**
|
||||
* 远程一对多关联类
|
||||
*/
|
||||
class HasManyThrough extends Relation
|
||||
{
|
||||
/**
|
||||
* 中间关联表外键
|
||||
* @var string
|
||||
*/
|
||||
protected $throughKey;
|
||||
|
||||
/**
|
||||
* 中间主键
|
||||
* @var string
|
||||
*/
|
||||
protected $throughPk;
|
||||
|
||||
/**
|
||||
* 中间表查询对象
|
||||
* @var Query
|
||||
*/
|
||||
protected $through;
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 关联模型名
|
||||
* @param string $through 中间模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $throughKey 中间关联外键
|
||||
* @param string $localKey 当前模型主键
|
||||
* @param string $throughPk 中间模型主键
|
||||
*/
|
||||
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 = (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 Collection
|
||||
*/
|
||||
public function getRelation(array $subRelation = [], Closure $closure = null)
|
||||
{
|
||||
if ($closure) {
|
||||
$closure($this->getClosureType($closure));
|
||||
}
|
||||
|
||||
$this->baseQuery();
|
||||
|
||||
if ($this->withLimit) {
|
||||
$this->query->limit($this->withLimit);
|
||||
}
|
||||
|
||||
return $this->query->relation($subRelation)
|
||||
->select()
|
||||
->setParent(clone $this->parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @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): Query
|
||||
{
|
||||
$model = Str::snake(class_basename($this->parent));
|
||||
$throughTable = $this->through->getTable();
|
||||
$pk = $this->throughPk;
|
||||
$throughKey = $this->throughKey;
|
||||
$relation = new $this->model;
|
||||
$relationTable = $relation->getTable();
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
|
||||
if ('*' != $id) {
|
||||
$id = $relationTable . '.' . $relation->getPk();
|
||||
}
|
||||
$query = $query ?: $this->parent->db()->alias($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) {
|
||||
$query->where($relationTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
|
||||
})
|
||||
->group($relationTable . '.' . $this->throughKey)
|
||||
->having('count(' . $id . ')' . $operator . $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @param mixed $fields 字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @param Query $query Query对象
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($where = [], $fields = null, $joinType = '', Query $query = null): Query
|
||||
{
|
||||
$model = Str::snake(class_basename($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);
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
$query = $query ?: $this->parent->db()->alias($model);
|
||||
|
||||
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);
|
||||
})
|
||||
->group($modelTable . '.' . $this->throughKey)
|
||||
->where($where)
|
||||
->field($fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @access protected
|
||||
* @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
|
||||
{
|
||||
$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, $subRelation, $closure, $cache);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
$pk = $result->$localKey;
|
||||
if (!isset($data[$pk])) {
|
||||
$data[$pk] = [];
|
||||
}
|
||||
|
||||
// 设置关联属性
|
||||
$result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据)
|
||||
* @access protected
|
||||
* @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
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
$pk = $result->$localKey;
|
||||
|
||||
$this->query->removeWhereField($foreignKey);
|
||||
|
||||
$data = $this->eagerlyWhere([
|
||||
[$foreignKey, '=', $pk],
|
||||
], $foreignKey, $subRelation, $closure, $cache);
|
||||
|
||||
// 关联数据封装
|
||||
if (!isset($data[$pk])) {
|
||||
$data[$pk] = [];
|
||||
}
|
||||
|
||||
$result->setRelation($relation, $this->resultSetBuild($data[$pk], clone $this->parent));
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联模型预查询
|
||||
* @access public
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $key 关联键名
|
||||
* @param array $subRelation 子关联
|
||||
* @param Closure $closure
|
||||
* @param array $cache 关联缓存
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyWhere(array $where, string $key, array $subRelation = [], Closure $closure = null, array $cache = []): array
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
$throughList = $this->through->where($where)->select();
|
||||
$keys = $throughList->column($this->throughPk, $this->throughPk);
|
||||
|
||||
if ($closure) {
|
||||
$this->baseQuery = true;
|
||||
$closure($this->getClosureType($closure));
|
||||
}
|
||||
|
||||
$list = $this->query
|
||||
->where($this->throughKey, 'in', $keys)
|
||||
->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
|
||||
->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 字段
|
||||
* @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->getClosureType($closure), $name);
|
||||
}
|
||||
|
||||
$alias = Str::snake(class_basename($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 getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string
|
||||
{
|
||||
if ($closure) {
|
||||
$closure($this->getClosureType($closure), $name);
|
||||
}
|
||||
|
||||
$alias = Str::snake(class_basename($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(仅执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery(): void
|
||||
{
|
||||
if (empty($this->baseQuery) && $this->parent->getData()) {
|
||||
$alias = Str::snake(class_basename($this->model));
|
||||
$throughTable = $this->through->getTable();
|
||||
$pk = $this->throughPk;
|
||||
$throughKey = $this->throughKey;
|
||||
$modelTable = $this->parent->getTable();
|
||||
$fields = $this->getQueryFields($alias);
|
||||
|
||||
$this->query
|
||||
->field($fields)
|
||||
->alias($alias)
|
||||
->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
|
||||
->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
|
||||
->where($throughTable . '.' . $this->foreignKey, $this->parent->{$this->localKey});
|
||||
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
300
vendor/topthink/think-orm/src/model/relation/HasOne.php
vendored
Normal file
300
vendor/topthink/think-orm/src/model/relation/HasOne.php
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use Closure;
|
||||
use think\db\BaseQuery as Query;
|
||||
use think\helper\Str;
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* HasOne 关联类
|
||||
*/
|
||||
class HasOne extends OneToOne
|
||||
{
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $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->query = (new $model)->db();
|
||||
|
||||
if (get_class($parent) == $model) {
|
||||
$this->selfRelation = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @access public
|
||||
* @param array $subRelation 子关联名
|
||||
* @param Closure $closure 闭包查询条件
|
||||
* @return Model
|
||||
*/
|
||||
public function getRelation(array $subRelation = [], Closure $closure = null)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
|
||||
if ($closure) {
|
||||
$closure($this->getClosureType($closure));
|
||||
}
|
||||
|
||||
// 判断关联类型执行查询
|
||||
$relationModel = $this->query
|
||||
->removeWhereField($this->foreignKey)
|
||||
->where($this->foreignKey, $this->parent->$localKey)
|
||||
->relation($subRelation)
|
||||
->find();
|
||||
|
||||
if ($relationModel) {
|
||||
if (!empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $this->parent);
|
||||
}
|
||||
|
||||
$relationModel->setParent(clone $this->parent);
|
||||
}
|
||||
|
||||
return $relationModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建关联统计子查询
|
||||
* @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->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
|
||||
->fetchSql()
|
||||
->$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) {
|
||||
$closure($this->getClosureType($closure), $name);
|
||||
}
|
||||
|
||||
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类型
|
||||
* @param Query $query Query对象
|
||||
* @return 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);
|
||||
$relation = class_basename($this->model);
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
$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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @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): Query
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
$model = class_basename($this->parent);
|
||||
$relation = class_basename($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);
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
$query = $query ?: $this->parent->db()->alias($model);
|
||||
|
||||
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);
|
||||
})
|
||||
->where($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @access protected
|
||||
* @param array $resultSet 数据集
|
||||
* @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, array $cache = []): 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([
|
||||
[$foreignKey, 'in', $range],
|
||||
], $foreignKey, $subRelation, $closure, $cache);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
// 关联模型
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
$relationModel = null;
|
||||
} else {
|
||||
$relationModel = $data[$result->$localKey];
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->exists(true);
|
||||
}
|
||||
|
||||
if ($relationModel && !empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $result);
|
||||
} else {
|
||||
// 设置关联属性
|
||||
$result->setRelation($relation, $relationModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据)
|
||||
* @access protected
|
||||
* @param Model $result 数据对象
|
||||
* @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, array $cache = []): void
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$this->query->removeWhereField($foreignKey);
|
||||
|
||||
$data = $this->eagerlyWhere([
|
||||
[$foreignKey, '=', $result->$localKey],
|
||||
], $foreignKey, $subRelation, $closure, $cache);
|
||||
|
||||
// 关联模型
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
$relationModel = null;
|
||||
} else {
|
||||
$relationModel = $data[$result->$localKey];
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->exists(true);
|
||||
}
|
||||
|
||||
if ($relationModel && !empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $result);
|
||||
} else {
|
||||
$result->setRelation($relation, $relationModel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(仅执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery(): void
|
||||
{
|
||||
if (empty($this->baseQuery)) {
|
||||
if (isset($this->parent->{$this->localKey})) {
|
||||
// 关联查询带入关联条件
|
||||
$this->query->where($this->foreignKey, '=', $this->parent->{$this->localKey});
|
||||
}
|
||||
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
163
vendor/topthink/think-orm/src/model/relation/HasOneThrough.php
vendored
Normal file
163
vendor/topthink/think-orm/src/model/relation/HasOneThrough.php
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use Closure;
|
||||
use think\helper\Str;
|
||||
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->getClosureType($closure));
|
||||
}
|
||||
|
||||
$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 闭包
|
||||
* @param array $cache 关联缓存
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null, array $cache = []): 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, $subRelation, $closure, $cache);
|
||||
|
||||
// 关联数据封装
|
||||
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($relation, $relationModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据)
|
||||
* @access protected
|
||||
* @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
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$this->query->removeWhereField($foreignKey);
|
||||
|
||||
$data = $this->eagerlyWhere([
|
||||
[$foreignKey, '=', $result->$localKey],
|
||||
], $foreignKey, $subRelation, $closure, $cache);
|
||||
|
||||
// 关联模型
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
$relationModel = null;
|
||||
} else {
|
||||
$relationModel = $data[$result->$localKey];
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->exists(true);
|
||||
}
|
||||
|
||||
$result->setRelation($relation, $relationModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联模型预查询
|
||||
* @access public
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $key 关联键名
|
||||
* @param array $subRelation 子关联
|
||||
* @param Closure $closure
|
||||
* @param array $cache 关联缓存
|
||||
* @return 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);
|
||||
|
||||
if ($closure) {
|
||||
$closure($this->getClosureType($closure));
|
||||
}
|
||||
|
||||
$list = $this->query
|
||||
->where($this->throughKey, 'in', $keys)
|
||||
->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
|
||||
->select();
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
$keys = array_flip($keys);
|
||||
|
||||
foreach ($list as $set) {
|
||||
$data[$keys[$set->{$this->throughKey}]] = $set;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
353
vendor/topthink/think-orm/src/model/relation/MorphMany.php
vendored
Normal file
353
vendor/topthink/think-orm/src/model/relation/MorphMany.php
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use Closure;
|
||||
use think\Collection;
|
||||
use think\db\BaseQuery as Query;
|
||||
use think\db\exception\DbException as Exception;
|
||||
use think\helper\Str;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
/**
|
||||
* 多态一对多关联
|
||||
*/
|
||||
class MorphMany extends Relation
|
||||
{
|
||||
|
||||
/**
|
||||
* 多态关联外键
|
||||
* @var string
|
||||
*/
|
||||
protected $morphKey;
|
||||
/**
|
||||
* 多态字段名
|
||||
* @var string
|
||||
*/
|
||||
protected $morphType;
|
||||
|
||||
/**
|
||||
* 多态类型
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $morphKey 关联外键
|
||||
* @param string $morphType 多态字段名
|
||||
* @param string $type 多态类型
|
||||
*/
|
||||
public function __construct(Model $parent, string $model, string $morphKey, string $morphType, string $type)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->type = $type;
|
||||
$this->morphKey = $morphKey;
|
||||
$this->morphType = $morphType;
|
||||
$this->query = (new $model)->db();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @access public
|
||||
* @param array $subRelation 子关联名
|
||||
* @param Closure $closure 闭包查询条件
|
||||
* @return Collection
|
||||
*/
|
||||
public function getRelation(array $subRelation = [], Closure $closure = null): Collection
|
||||
{
|
||||
if ($closure) {
|
||||
$closure($this->getClosureType($closure));
|
||||
}
|
||||
|
||||
$this->baseQuery();
|
||||
|
||||
if ($this->withLimit) {
|
||||
$this->query->limit($this->withLimit);
|
||||
}
|
||||
|
||||
return $this->query->relation($subRelation)
|
||||
->select()
|
||||
->setParent(clone $this->parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @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)
|
||||
{
|
||||
throw new Exception('relation not support: has');
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @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 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
|
||||
{
|
||||
$morphType = $this->morphType;
|
||||
$morphKey = $this->morphKey;
|
||||
$type = $this->type;
|
||||
$range = [];
|
||||
|
||||
foreach ($resultSet as $result) {
|
||||
$pk = $result->getPk();
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$pk)) {
|
||||
$range[] = $result->$pk;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$where = [
|
||||
[$morphKey, 'in', $range],
|
||||
[$morphType, '=', $type],
|
||||
];
|
||||
$data = $this->eagerlyMorphToMany($where, $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)) {
|
||||
$key = $result->$pk;
|
||||
$data = $this->eagerlyMorphToMany([
|
||||
[$this->morphKey, '=', $key],
|
||||
[$this->morphType, '=', $this->type],
|
||||
], $subRelation, $closure, $cache);
|
||||
|
||||
if (!isset($data[$key])) {
|
||||
$data[$key] = [];
|
||||
}
|
||||
|
||||
$result->setRelation($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 mixed
|
||||
*/
|
||||
public function relationCount(Model $result, Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null)
|
||||
{
|
||||
$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->type],
|
||||
])
|
||||
->$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->type)
|
||||
->fetchSql()
|
||||
->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多态一对多 关联模型预查询
|
||||
* @access protected
|
||||
* @param array $where 关联预查询条件
|
||||
* @param array $subRelation 子关联
|
||||
* @param Closure $closure 闭包
|
||||
* @param array $cache 关联缓存
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyMorphToMany(array $where, array $subRelation = [], Closure $closure = null, array $cache = []): array
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
$this->query->removeOption('where');
|
||||
|
||||
if ($closure) {
|
||||
$this->baseQuery = true;
|
||||
$closure($this->getClosureType($closure));
|
||||
}
|
||||
|
||||
$list = $this->query
|
||||
->where($where)
|
||||
->with($subRelation)
|
||||
->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
|
||||
->select();
|
||||
$morphKey = $this->morphKey;
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
foreach ($list as $set) {
|
||||
$key = $set->$morphKey;
|
||||
|
||||
if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[$key][] = $set;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象
|
||||
* @param bool $replace 是否自动识别更新和写入
|
||||
* @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();
|
||||
}
|
||||
|
||||
// 保存关联表数据
|
||||
$pk = $this->parent->getPk();
|
||||
|
||||
$data[$this->morphKey] = $this->parent->$pk;
|
||||
$data[$this->morphType] = $this->type;
|
||||
|
||||
return new $this->model($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存当前关联数据对象
|
||||
* @access public
|
||||
* @param iterable $dataSet 数据集
|
||||
* @param boolean $replace 是否自动识别更新和写入
|
||||
* @return array|false
|
||||
*/
|
||||
public function saveAll(iterable $dataSet, bool $replace = true)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($dataSet as $key => $data) {
|
||||
$result[] = $this->save($data, $replace);
|
||||
}
|
||||
|
||||
return empty($result) ? false : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(仅执行一次)
|
||||
* @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->type],
|
||||
]);
|
||||
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
280
vendor/topthink/think-orm/src/model/relation/MorphOne.php
vendored
Normal file
280
vendor/topthink/think-orm/src/model/relation/MorphOne.php
vendored
Normal file
@@ -0,0 +1,280 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use Closure;
|
||||
use think\db\BaseQuery as Query;
|
||||
use think\db\exception\DbException as Exception;
|
||||
use think\helper\Str;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
/**
|
||||
* 多态一对一关联类
|
||||
*/
|
||||
class MorphOne extends Relation
|
||||
{
|
||||
/**
|
||||
* 多态关联外键
|
||||
* @var string
|
||||
*/
|
||||
protected $morphKey;
|
||||
|
||||
/**
|
||||
* 多态字段
|
||||
* @var string
|
||||
*/
|
||||
protected $morphType;
|
||||
|
||||
/**
|
||||
* 多态类型
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $morphKey 关联外键
|
||||
* @param string $morphType 多态字段名
|
||||
* @param string $type 多态类型
|
||||
*/
|
||||
public function __construct(Model $parent, string $model, string $morphKey, string $morphType, string $type)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->type = $type;
|
||||
$this->morphKey = $morphKey;
|
||||
$this->morphType = $morphType;
|
||||
$this->query = (new $model)->db();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @access public
|
||||
* @param array $subRelation 子关联名
|
||||
* @param Closure $closure 闭包查询条件
|
||||
* @return Model
|
||||
*/
|
||||
public function getRelation(array $subRelation = [], Closure $closure = null)
|
||||
{
|
||||
if ($closure) {
|
||||
$closure($this->getClosureType($closure));
|
||||
}
|
||||
|
||||
$this->baseQuery();
|
||||
|
||||
$relationModel = $this->query->relation($subRelation)->find();
|
||||
|
||||
if ($relationModel) {
|
||||
$relationModel->setParent(clone $this->parent);
|
||||
}
|
||||
|
||||
return $relationModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @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 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
|
||||
{
|
||||
$morphType = $this->morphType;
|
||||
$morphKey = $this->morphKey;
|
||||
$type = $this->type;
|
||||
$range = [];
|
||||
|
||||
foreach ($resultSet as $result) {
|
||||
$pk = $result->getPk();
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$pk)) {
|
||||
$range[] = $result->$pk;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$data = $this->eagerlyMorphToOne([
|
||||
[$morphKey, 'in', $range],
|
||||
[$morphType, '=', $type],
|
||||
], $subRelation, $closure, $cache);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$pk])) {
|
||||
$relationModel = null;
|
||||
} else {
|
||||
$relationModel = $data[$result->$pk];
|
||||
$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
|
||||
{
|
||||
$pk = $result->getPk();
|
||||
|
||||
if (isset($result->$pk)) {
|
||||
$pk = $result->$pk;
|
||||
$data = $this->eagerlyMorphToOne([
|
||||
[$this->morphKey, '=', $pk],
|
||||
[$this->morphType, '=', $this->type],
|
||||
], $subRelation, $closure, $cache);
|
||||
|
||||
if (isset($data[$pk])) {
|
||||
$relationModel = $data[$pk];
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->exists(true);
|
||||
} else {
|
||||
$relationModel = null;
|
||||
}
|
||||
|
||||
$result->setRelation($relation, $relationModel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多态一对一 关联模型预查询
|
||||
* @access protected
|
||||
* @param array $where 关联预查询条件
|
||||
* @param array $subRelation 子关联
|
||||
* @param Closure $closure 闭包
|
||||
* @param array $cache 关联缓存
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyMorphToOne(array $where, array $subRelation = [], $closure = null, array $cache = []): array
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
if ($closure) {
|
||||
$this->baseQuery = true;
|
||||
$closure($this->getClosureType($closure));
|
||||
}
|
||||
|
||||
$list = $this->query
|
||||
->where($where)
|
||||
->with($subRelation)
|
||||
->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
|
||||
->select();
|
||||
$morphKey = $this->morphKey;
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
|
||||
foreach ($list as $set) {
|
||||
$data[$set->$morphKey] = $set;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象
|
||||
* @param boolean $replace 是否自动识别更新和写入
|
||||
* @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();
|
||||
}
|
||||
|
||||
// 保存关联表数据
|
||||
$pk = $this->parent->getPk();
|
||||
|
||||
$data[$this->morphKey] = $this->parent->$pk;
|
||||
$data[$this->morphType] = $this->type;
|
||||
|
||||
return new $this->model($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* @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->type],
|
||||
]);
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
333
vendor/topthink/think-orm/src/model/relation/MorphTo.php
vendored
Normal file
333
vendor/topthink/think-orm/src/model/relation/MorphTo.php
vendored
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use 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);
|
||||
}
|
||||
|
||||
}
|
||||
332
vendor/topthink/think-orm/src/model/relation/OneToOne.php
vendored
Normal file
332
vendor/topthink/think-orm/src/model/relation/OneToOne.php
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use Closure;
|
||||
use think\db\BaseQuery as Query;
|
||||
use think\db\exception\DbException as Exception;
|
||||
use think\helper\Str;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
/**
|
||||
* 一对一关联基础类
|
||||
* @package think\model\relation
|
||||
*/
|
||||
abstract class OneToOne extends Relation
|
||||
{
|
||||
/**
|
||||
* JOIN类型
|
||||
* @var string
|
||||
*/
|
||||
protected $joinType = 'INNER';
|
||||
|
||||
/**
|
||||
* 绑定的关联属性
|
||||
* @var array
|
||||
*/
|
||||
protected $bindAttr = [];
|
||||
|
||||
/**
|
||||
* 关联名
|
||||
* @var string
|
||||
*/
|
||||
protected $relation;
|
||||
|
||||
/**
|
||||
* 设置join类型
|
||||
* @access public
|
||||
* @param string $type JOIN类型
|
||||
* @return $this
|
||||
*/
|
||||
public function joinType(string $type)
|
||||
{
|
||||
$this->joinType = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(JOIN方式)
|
||||
* @access public
|
||||
* @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 = Str::snake(class_basename($this->parent));
|
||||
|
||||
if ($first) {
|
||||
$table = $query->getTable();
|
||||
$query->table([$table => $name]);
|
||||
|
||||
if ($query->getOptions('field')) {
|
||||
$masterField = $query->getOptions('field');
|
||||
$query->removeOption('field');
|
||||
} else {
|
||||
$masterField = true;
|
||||
}
|
||||
|
||||
$query->tableField($masterField, $table, $name);
|
||||
}
|
||||
|
||||
// 预载入封装
|
||||
$joinTable = $this->query->getTable();
|
||||
$joinAlias = $relation;
|
||||
$joinType = $joinType ?: $this->joinType;
|
||||
|
||||
$query->via($joinAlias);
|
||||
|
||||
if ($this instanceof BelongsTo) {
|
||||
$joinOn = $name . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey;
|
||||
} else {
|
||||
$joinOn = $name . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey;
|
||||
}
|
||||
|
||||
if ($closure) {
|
||||
// 执行闭包查询
|
||||
$closure($this->getClosureType($closure));
|
||||
|
||||
// 使用withField指定获取关联的字段
|
||||
if ($this->withField) {
|
||||
$field = $this->withField;
|
||||
}
|
||||
}
|
||||
|
||||
$query->join([$joinTable => $joinAlias], $joinOn, $joinType)
|
||||
->tableField($field, $joinTable, $joinAlias, $relation . '__');
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @access protected
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据)
|
||||
* @access protected
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @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, array $cache = [], bool $join = false): void
|
||||
{
|
||||
if ($join) {
|
||||
// 模型JOIN关联组装
|
||||
foreach ($resultSet as $result) {
|
||||
$this->match($this->model, $relation, $result);
|
||||
}
|
||||
} else {
|
||||
// IN查询
|
||||
$this->eagerlySet($resultSet, $relation, $subRelation, $closure, $cache);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据)
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @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, array $cache = [], bool $join = false): void
|
||||
{
|
||||
if ($join) {
|
||||
// 模型JOIN关联组装
|
||||
$this->match($this->model, $relation, $result);
|
||||
} else {
|
||||
// IN查询
|
||||
$this->eagerlyOne($result, $relation, $subRelation, $closure, $cache);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象
|
||||
* @param boolean $replace 是否自动识别更新和写入
|
||||
* @return Model|false
|
||||
*/
|
||||
public function save($data, bool $replace = true)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定关联表的属性到父模型属性
|
||||
* @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 public
|
||||
* @param string $model 模型名称
|
||||
* @param string $relation 关联名
|
||||
* @param Model $result 模型对象实例
|
||||
* @return void
|
||||
*/
|
||||
protected function match(string $model, string $relation, Model $result): void
|
||||
{
|
||||
// 重新组装模型数据
|
||||
foreach ($result->getData() as $key => $val) {
|
||||
if (strpos($key, '__')) {
|
||||
[$name, $attr] = explode('__', $key, 2);
|
||||
if ($name == $relation) {
|
||||
$list[$name][$attr] = $val;
|
||||
unset($result->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($list[$relation])) {
|
||||
$array = array_unique($list[$relation]);
|
||||
|
||||
if (count($array) == 1 && null === current($array)) {
|
||||
$relationModel = null;
|
||||
} else {
|
||||
$relationModel = new $model($list[$relation]);
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->exists(true);
|
||||
}
|
||||
|
||||
if ($relationModel && !empty($this->bindAttr)) {
|
||||
$this->bindAttr($relationModel, $result);
|
||||
}
|
||||
} else {
|
||||
$relationModel = null;
|
||||
}
|
||||
|
||||
$result->setRelation($relation, $relationModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定关联属性到父模型
|
||||
* @access protected
|
||||
* @param Model $model 关联模型对象
|
||||
* @param Model $result 父模型对象
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function bindAttr(Model $model, Model $result): 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 一对一 关联模型预查询(IN方式)
|
||||
* @access public
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $key 关联键名
|
||||
* @param array $subRelation 子关联
|
||||
* @param Closure $closure
|
||||
* @param array $cache 关联缓存
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyWhere(array $where, string $key, array $subRelation = [], Closure $closure = null, array $cache = [])
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
if ($closure) {
|
||||
$this->baseQuery = true;
|
||||
$closure($this->getClosureType($closure));
|
||||
}
|
||||
|
||||
if ($this->withField) {
|
||||
$this->query->field($this->withField);
|
||||
}
|
||||
|
||||
if ($this->query->getOptions('order')) {
|
||||
$this->query->group($key);
|
||||
}
|
||||
|
||||
$list = $this->query
|
||||
->where($where)
|
||||
->with($subRelation)
|
||||
->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
|
||||
->select();
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
|
||||
foreach ($list as $set) {
|
||||
if (!isset($data[$set->$key])) {
|
||||
$data[$set->$key] = $set;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
209
vendor/topthink/think-orm/src/paginator/driver/Bootstrap.php
vendored
Normal file
209
vendor/topthink/think-orm/src/paginator/driver/Bootstrap.php
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: zhangyajun <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\paginator\driver;
|
||||
|
||||
use think\Paginator;
|
||||
|
||||
/**
|
||||
* Bootstrap 分页驱动
|
||||
*/
|
||||
class Bootstrap extends Paginator
|
||||
{
|
||||
|
||||
/**
|
||||
* 上一页按钮
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function getPreviousButton(string $text = "«"): string
|
||||
{
|
||||
|
||||
if ($this->currentPage() <= 1) {
|
||||
return $this->getDisabledTextWrapper($text);
|
||||
}
|
||||
|
||||
$url = $this->url(
|
||||
$this->currentPage() - 1
|
||||
);
|
||||
|
||||
return $this->getPageLinkWrapper($url, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一页按钮
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function getNextButton(string $text = '»'): string
|
||||
{
|
||||
if (!$this->hasMore) {
|
||||
return $this->getDisabledTextWrapper($text);
|
||||
}
|
||||
|
||||
$url = $this->url($this->currentPage() + 1);
|
||||
|
||||
return $this->getPageLinkWrapper($url, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 页码按钮
|
||||
* @return string
|
||||
*/
|
||||
protected function getLinks(): string
|
||||
{
|
||||
if ($this->simple) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$block = [
|
||||
'first' => null,
|
||||
'slider' => null,
|
||||
'last' => null,
|
||||
];
|
||||
|
||||
$side = 3;
|
||||
$window = $side * 2;
|
||||
|
||||
if ($this->lastPage < $window + 6) {
|
||||
$block['first'] = $this->getUrlRange(1, $this->lastPage);
|
||||
} elseif ($this->currentPage <= $window) {
|
||||
$block['first'] = $this->getUrlRange(1, $window + 2);
|
||||
$block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage);
|
||||
} elseif ($this->currentPage > ($this->lastPage - $window)) {
|
||||
$block['first'] = $this->getUrlRange(1, 2);
|
||||
$block['last'] = $this->getUrlRange($this->lastPage - ($window + 2), $this->lastPage);
|
||||
} else {
|
||||
$block['first'] = $this->getUrlRange(1, 2);
|
||||
$block['slider'] = $this->getUrlRange($this->currentPage - $side, $this->currentPage + $side);
|
||||
$block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage);
|
||||
}
|
||||
|
||||
$html = '';
|
||||
|
||||
if (is_array($block['first'])) {
|
||||
$html .= $this->getUrlLinks($block['first']);
|
||||
}
|
||||
|
||||
if (is_array($block['slider'])) {
|
||||
$html .= $this->getDots();
|
||||
$html .= $this->getUrlLinks($block['slider']);
|
||||
}
|
||||
|
||||
if (is_array($block['last'])) {
|
||||
$html .= $this->getDots();
|
||||
$html .= $this->getUrlLinks($block['last']);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染分页html
|
||||
* @return mixed
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
if ($this->hasPages()) {
|
||||
if ($this->simple) {
|
||||
return sprintf(
|
||||
'<ul class="pager">%s %s</ul>',
|
||||
$this->getPreviousButton(),
|
||||
$this->getNextButton()
|
||||
);
|
||||
} else {
|
||||
return sprintf(
|
||||
'<ul class="pagination">%s %s %s</ul>',
|
||||
$this->getPreviousButton(),
|
||||
$this->getLinks(),
|
||||
$this->getNextButton()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个可点击的按钮
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $page
|
||||
* @return string
|
||||
*/
|
||||
protected function getAvailablePageWrapper(string $url, string $page): string
|
||||
{
|
||||
return '<li><a href="' . htmlentities($url) . '">' . $page . '</a></li>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个禁用的按钮
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function getDisabledTextWrapper(string $text): string
|
||||
{
|
||||
return '<li class="disabled"><span>' . $text . '</span></li>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个激活的按钮
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function getActivePageWrapper(string $text): string
|
||||
{
|
||||
return '<li class="active"><span>' . $text . '</span></li>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成省略号按钮
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDots(): string
|
||||
{
|
||||
return $this->getDisabledTextWrapper('...');
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量生成页码按钮.
|
||||
*
|
||||
* @param array $urls
|
||||
* @return string
|
||||
*/
|
||||
protected function getUrlLinks(array $urls): string
|
||||
{
|
||||
$html = '';
|
||||
|
||||
foreach ($urls as $page => $url) {
|
||||
$html .= $this->getPageLinkWrapper($url, $page);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成普通页码按钮
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $page
|
||||
* @return string
|
||||
*/
|
||||
protected function getPageLinkWrapper(string $url, string $page): string
|
||||
{
|
||||
if ($this->currentPage() == $page) {
|
||||
return $this->getActivePageWrapper($page);
|
||||
}
|
||||
|
||||
return $this->getAvailablePageWrapper($url, $page);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user