取消https验证,并添加vendor文件夹。
This commit is contained in:
+1313
File diff suppressed because it is too large
Load Diff
+1322
File diff suppressed because it is too large
Load Diff
+209
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+347
@@ -0,0 +1,347 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* 数据库连接基础类
|
||||
*/
|
||||
abstract class Connection implements ConnectionInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* 当前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 DbManager
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* 是否读取主库
|
||||
* @var bool
|
||||
*/
|
||||
protected $readMaster = false;
|
||||
|
||||
/**
|
||||
* 数据库连接参数配置
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* 缓存对象
|
||||
* @var CacheInterface
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* 架构函数 读取数据库配置信息
|
||||
* @access public
|
||||
* @param array $config 数据库配置数组
|
||||
*/
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
if (!empty($config)) {
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
|
||||
// 创建Builder对象
|
||||
$class = $this->getBuilderClass();
|
||||
|
||||
$this->builder = new $class($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的builder实例对象
|
||||
* @access public
|
||||
* @return Builder
|
||||
*/
|
||||
public function getBuilder()
|
||||
{
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建查询对象
|
||||
*/
|
||||
public function newQuery()
|
||||
{
|
||||
$class = $this->getQueryClass();
|
||||
|
||||
/** @var BaseQuery $query */
|
||||
$query = new $class($this);
|
||||
|
||||
$timeRule = $this->db->getConfig('time_query_rule');
|
||||
if (!empty($timeRule)) {
|
||||
$query->timeRule($timeRule);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定表名开始查询
|
||||
* @param $table
|
||||
* @return BaseQuery
|
||||
*/
|
||||
public function table($table)
|
||||
{
|
||||
return $this->newQuery()->table($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定表名开始查询(不带前缀)
|
||||
* @param $name
|
||||
* @return BaseQuery
|
||||
*/
|
||||
public function name($name)
|
||||
{
|
||||
return $this->newQuery()->name($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前的数据库Db对象
|
||||
* @access public
|
||||
* @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)) {
|
||||
$listen[] = function ($sql, $time, $master) {
|
||||
if (0 === strpos($sql, 'CONNECT:')) {
|
||||
$this->db->log($sql);
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录SQL
|
||||
if (is_bool($master)) {
|
||||
// 分布式记录当前操作的主从
|
||||
$master = $master ? 'master|' : 'slave|';
|
||||
} else {
|
||||
$master = '';
|
||||
}
|
||||
|
||||
$this->db->log($sql . ' [ ' . $master . 'RunTime:' . $time . 's ]');
|
||||
};
|
||||
}
|
||||
|
||||
$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 查询对象
|
||||
* @param string $method 查询方法
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey(BaseQuery $query, string $method = ''): string
|
||||
{
|
||||
if (!empty($query->getOptions('key')) && empty($method)) {
|
||||
$key = 'think_' . $this->getConfig('database') . '.' . $query->getTable() . '|' . $query->getOptions('key');
|
||||
} else {
|
||||
$key = $query->getQueryGuid();
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析缓存
|
||||
* @access protected
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @param array $cache 缓存信息
|
||||
* @param string $method 查询方法
|
||||
* @return CacheItem
|
||||
*/
|
||||
protected function parseCache(BaseQuery $query, array $cache, string $method = ''): CacheItem
|
||||
{
|
||||
[$key, $expire, $tag] = $cache;
|
||||
|
||||
if ($key instanceof CacheItem) {
|
||||
$cacheItem = $key;
|
||||
} else {
|
||||
if (true === $key) {
|
||||
$key = $this->getCacheKey($query, $method);
|
||||
}
|
||||
|
||||
$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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* 指定表名开始查询
|
||||
* @param $table
|
||||
* @return BaseQuery
|
||||
*/
|
||||
public function table($table);
|
||||
|
||||
/**
|
||||
* 指定表名开始查询(不带前缀)
|
||||
* @param $name
|
||||
* @return BaseQuery
|
||||
*/
|
||||
public function name($name);
|
||||
|
||||
/**
|
||||
* 连接数据库方法
|
||||
* @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
|
||||
*/
|
||||
public function find(BaseQuery $query): array;
|
||||
|
||||
/**
|
||||
* 查找记录
|
||||
* @access public
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @return array
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public function insertAll(BaseQuery $query, array $dataSet = []): int;
|
||||
|
||||
/**
|
||||
* 更新记录
|
||||
* @access public
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @return integer
|
||||
*/
|
||||
public function update(BaseQuery $query): int;
|
||||
|
||||
/**
|
||||
* 删除记录
|
||||
* @access public
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @return int
|
||||
*/
|
||||
public function delete(BaseQuery $query): int;
|
||||
|
||||
/**
|
||||
* 得到某个字段的值
|
||||
* @access public
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @param string $field 字段名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function value(BaseQuery $query, string $field, $default = null);
|
||||
|
||||
/**
|
||||
* 得到某个列的数组
|
||||
* @access public
|
||||
* @param BaseQuery $query 查询对象
|
||||
* @param string|array $column 字段名 多个字段用逗号分隔
|
||||
* @param string $key 索引
|
||||
* @return array
|
||||
*/
|
||||
public function column(BaseQuery $query, $column, string $key = ''): array;
|
||||
|
||||
/**
|
||||
* 执行数据库事务
|
||||
* @access public
|
||||
* @param callable $callback 数据操作方法回调
|
||||
* @return mixed
|
||||
*/
|
||||
public function transaction(callable $callback);
|
||||
|
||||
/**
|
||||
* 启动事务
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function startTrans();
|
||||
|
||||
/**
|
||||
* 用于非自动提交状态下面的查询提交
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function commit();
|
||||
|
||||
/**
|
||||
* 事务回滚
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function rollback();
|
||||
|
||||
/**
|
||||
* 获取最近一次查询的sql语句
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getLastSql(): string;
|
||||
|
||||
}
|
||||
+492
@@ -0,0 +1,492 @@
|
||||
<?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 默认值
|
||||
* @param bool $one
|
||||
* @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
|
||||
$subSql = $this->query->field('count(' . $field . ') AS think_count')->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;
|
||||
}
|
||||
}
|
||||
+708
@@ -0,0 +1,708 @@
|
||||
<?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\Command;
|
||||
use MongoDB\Driver\Cursor;
|
||||
use MongoDB\Driver\Exception\AuthenticationException;
|
||||
use MongoDB\Driver\Exception\ConnectionException;
|
||||
use MongoDB\Driver\Exception\InvalidArgumentException;
|
||||
use MongoDB\Driver\Exception\RuntimeException;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use think\db\exception\DbException as Exception;
|
||||
use think\Paginator;
|
||||
|
||||
class Mongo extends BaseQuery
|
||||
{
|
||||
/**
|
||||
* 当前数据库连接对象
|
||||
* @var \think\db\connector\Mongo
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* 执行指令 返回数据集
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行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', 'projection', 'filter', 'json', 'with_attr', 'with_relation_attr'] 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['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
|
||||
[$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;
|
||||
}
|
||||
}
|
||||
+1866
File diff suppressed because it is too large
Load Diff
+450
@@ -0,0 +1,450 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* 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 = [])
|
||||
{
|
||||
$this->options['order'][] = new Raw($field, $bind);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取执行的SQL语句而不进行实际的查询
|
||||
* @access public
|
||||
* @param bool $fetch 是否返回sql
|
||||
* @return $this|Fetch
|
||||
*/
|
||||
public function fetchSql(bool $fetch = true)
|
||||
{
|
||||
$this->options['fetch_sql'] = $fetch;
|
||||
|
||||
if ($fetch) {
|
||||
return new Fetch($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批处理执行SQL语句
|
||||
* 批处理的指令都认为是execute操作
|
||||
* @access public
|
||||
* @param array $sql SQL批处理指令
|
||||
* @return bool
|
||||
*/
|
||||
public function batchQuery(array $sql = []): bool
|
||||
{
|
||||
return $this->connection->batchQuery($this, $sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* USING支持 用于多表删除
|
||||
* @access public
|
||||
* @param mixed $using USING
|
||||
* @return $this
|
||||
*/
|
||||
public function using($using)
|
||||
{
|
||||
$this->options['using'] = $using;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储过程调用
|
||||
* @access public
|
||||
* @param bool $procedure 是否为存储过程查询
|
||||
* @return $this
|
||||
*/
|
||||
public function procedure(bool $procedure = true)
|
||||
{
|
||||
$this->options['procedure'] = $procedure;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定group查询
|
||||
* @access public
|
||||
* @param string|array $group GROUP
|
||||
* @return $this
|
||||
*/
|
||||
public function group($group)
|
||||
{
|
||||
$this->options['group'] = $group;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定having查询
|
||||
* @access public
|
||||
* @param string $having having
|
||||
* @return $this
|
||||
*/
|
||||
public function having(string $having)
|
||||
{
|
||||
$this->options['having'] = $having;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定distinct查询
|
||||
* @access public
|
||||
* @param bool $distinct 是否唯一
|
||||
* @return $this
|
||||
*/
|
||||
public function distinct(bool $distinct = true)
|
||||
{
|
||||
$this->options['distinct'] = $distinct;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定强制索引
|
||||
* @access public
|
||||
* @param string $force 索引名称
|
||||
* @return $this
|
||||
*/
|
||||
public function force(string $force)
|
||||
{
|
||||
$this->options['force'] = $force;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询注释
|
||||
* @access public
|
||||
* @param string $comment 注释
|
||||
* @return $this
|
||||
*/
|
||||
public function comment(string $comment)
|
||||
{
|
||||
$this->options['comment'] = $comment;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否REPLACE
|
||||
* @access public
|
||||
* @param bool $replace 是否使用REPLACE写入数据
|
||||
* @return $this
|
||||
*/
|
||||
public function replace(bool $replace = true)
|
||||
{
|
||||
$this->options['replace'] = $replace;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前查询所在的分区
|
||||
* @access public
|
||||
* @param string|array $partition 分区名称
|
||||
* @return $this
|
||||
*/
|
||||
public function partition($partition)
|
||||
{
|
||||
$this->options['partition'] = $partition;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置DUPLICATE
|
||||
* @access public
|
||||
* @param array|string|Raw $duplicate DUPLICATE信息
|
||||
* @return $this
|
||||
*/
|
||||
public function duplicate($duplicate)
|
||||
{
|
||||
$this->options['duplicate'] = $duplicate;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置查询的额外参数
|
||||
* @access public
|
||||
* @param string $extra 额外信息
|
||||
* @return $this
|
||||
*/
|
||||
public function extra(string $extra)
|
||||
{
|
||||
$this->options['extra'] = $extra;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建子查询SQL
|
||||
* @access public
|
||||
* @param bool $sub 是否添加括号
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function buildSql(bool $sub = true): string
|
||||
{
|
||||
return $sub ? '( ' . $this->fetchSql()->select() . ' )' : $this->fetchSql()->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前数据表的主键
|
||||
* @access public
|
||||
* @return string|array
|
||||
*/
|
||||
public function getPk()
|
||||
{
|
||||
if (empty($this->pk)) {
|
||||
$this->pk = $this->connection->getPk($this->getTable());
|
||||
}
|
||||
|
||||
return $this->pk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定数据表自增主键
|
||||
* @access public
|
||||
* @param string $autoinc 自增键
|
||||
* @return $this
|
||||
*/
|
||||
public function autoinc(string $autoinc)
|
||||
{
|
||||
$this->autoinc = $autoinc;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前数据表的自增主键
|
||||
* @access public
|
||||
* @return string|null
|
||||
*/
|
||||
public function getAutoInc()
|
||||
{
|
||||
$tableName = $this->getTable();
|
||||
|
||||
if (empty($this->autoinc) && $tableName) {
|
||||
$this->autoinc = $this->connection->getAutoInc($tableName);
|
||||
}
|
||||
|
||||
return $this->autoinc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段值增长
|
||||
* @access public
|
||||
* @param string $field 字段名
|
||||
* @param float $step 增长值
|
||||
* @return $this
|
||||
*/
|
||||
public function inc(string $field, float $step = 1)
|
||||
{
|
||||
$this->options['data'][$field] = ['INC', $step];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段值减少
|
||||
* @access public
|
||||
* @param string $field 字段名
|
||||
* @param float $step 增长值
|
||||
* @return $this
|
||||
*/
|
||||
public function dec(string $field, float $step = 1)
|
||||
{
|
||||
$this->options['data'][$field] = ['DEC', $step];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的查询标识
|
||||
* @access public
|
||||
* @param mixed $data 要序列化的数据
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryGuid($data = null): string
|
||||
{
|
||||
return md5($this->getConfig('database') . serialize(var_export($data ?: $this->options, true)) . serialize($this->getBind(false)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询但只返回PDOStatement对象
|
||||
* @access public
|
||||
* @return PDOStatement
|
||||
*/
|
||||
public function getPdo(): PDOStatement
|
||||
{
|
||||
return $this->connection->pdo($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用游标查找记录
|
||||
* @access public
|
||||
* @param mixed $data 数据
|
||||
* @return \Generator
|
||||
*/
|
||||
public function cursor($data = null)
|
||||
{
|
||||
if (!is_null($data)) {
|
||||
// 主键条件分析
|
||||
$this->parsePkWhere($data);
|
||||
}
|
||||
|
||||
$this->options['data'] = $data;
|
||||
|
||||
$connection = clone $this->connection;
|
||||
|
||||
return $connection->cursor($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分批数据返回处理
|
||||
* @access public
|
||||
* @param integer $count 每次处理的数据数量
|
||||
* @param callable $callback 处理回调方法
|
||||
* @param string|array $column 分批处理的字段名
|
||||
* @param string $order 字段排序
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool
|
||||
{
|
||||
$options = $this->getOptions();
|
||||
$column = $column ?: $this->getPk();
|
||||
|
||||
if (isset($options['order'])) {
|
||||
unset($options['order']);
|
||||
}
|
||||
|
||||
$bind = $this->bind;
|
||||
|
||||
if (is_array($column)) {
|
||||
$times = 1;
|
||||
$query = $this->options($options)->page($times, $count);
|
||||
} else {
|
||||
$query = $this->options($options)->limit($count);
|
||||
|
||||
if (strpos($column, '.')) {
|
||||
[$alias, $key] = explode('.', $column);
|
||||
} else {
|
||||
$key = $column;
|
||||
}
|
||||
}
|
||||
|
||||
$resultSet = $query->order($column, $order)->select();
|
||||
|
||||
while (count($resultSet) > 0) {
|
||||
if (false === call_user_func($callback, $resultSet)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($times)) {
|
||||
$times++;
|
||||
$query = $this->options($options)->page($times, $count);
|
||||
} else {
|
||||
$end = $resultSet->pop();
|
||||
$lastId = is_array($end) ? $end[$key] : $end->getData($key);
|
||||
|
||||
$query = $this->options($options)
|
||||
->limit($count)
|
||||
->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId);
|
||||
}
|
||||
|
||||
$resultSet = $query->bind($bind)->order($column, $order)->select();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* 参数绑定
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $bind = [];
|
||||
|
||||
/**
|
||||
* 创建一个查询表达式
|
||||
*
|
||||
* @param string $value
|
||||
* @param array $bind
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $value, array $bind = [])
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->bind = $bind;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表达式
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数绑定
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBind(): array
|
||||
{
|
||||
return $this->bind;
|
||||
}
|
||||
|
||||
}
|
||||
+182
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
+432
@@ -0,0 +1,432 @@
|
||||
<?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 (empty($options['field']) || '*' == $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[] = (strpos($key, '->') ? strstr($key, '->', true) : $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 = $this->parseRaw($query, $value);
|
||||
}
|
||||
|
||||
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 = $this->parseRaw($query, $value);
|
||||
}
|
||||
|
||||
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 $this->parseRaw($query, $key);
|
||||
}
|
||||
|
||||
$key = trim($key);
|
||||
|
||||
if (strpos($key, '->>') && false === strpos($key, '(')) {
|
||||
// JSON字段支持
|
||||
[$field, $name] = explode('->>', $key, 2);
|
||||
|
||||
return $this->parseKey($query, $field, true) . '->>\'$' . (strpos($name, '[') === 0 ? '' : '.') . str_replace('->>', '.', $name) . '\'';
|
||||
} elseif (strpos($key, '->') && false === strpos($key, '(')) {
|
||||
// JSON字段支持
|
||||
[$field, $name] = explode('->', $key, 2);
|
||||
return 'json_extract(' . $this->parseKey($query, $field, true) . ', \'$' . (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 ' . $this->parseRaw($query, $duplicate) . ' ';
|
||||
}
|
||||
|
||||
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) . " = " . $this->parseRaw($query, $val);
|
||||
} else {
|
||||
$name = $query->bindValue($val, $query->getConnection()->getFieldBindType($key));
|
||||
$updates[] = $this->parseKey($query, $key) . " = :" . $name;
|
||||
}
|
||||
}
|
||||
|
||||
return ' ON DUPLICATE KEY UPDATE ' . implode(' , ', $updates) . ' ';
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
<?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;
|
||||
use think\db\exception\DbException as Exception;
|
||||
use think\db\Raw;
|
||||
|
||||
/**
|
||||
* 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 bool $strict
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function parseKey(Query $query, $key, bool $strict = false): string
|
||||
{
|
||||
if (is_int($key)) {
|
||||
return (string) $key;
|
||||
} elseif ($key instanceof Raw) {
|
||||
return $this->parseRaw($query, $key);
|
||||
}
|
||||
|
||||
$key = trim($key);
|
||||
|
||||
if (strpos($key, '->') && false === strpos($key, '(')) {
|
||||
// JSON字段支持
|
||||
[$field, $name] = explode($key, '->');
|
||||
$key = $field . '."' . $name . '"';
|
||||
} elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) {
|
||||
[$table, $key] = explode('.', $key, 2);
|
||||
|
||||
$alias = $query->getOptions('alias');
|
||||
|
||||
if ('__TABLE__' == $table) {
|
||||
$table = $query->getOptions('table');
|
||||
$table = is_array($table) ? array_shift($table) : $table;
|
||||
}
|
||||
|
||||
if (isset($alias[$table])) {
|
||||
$table = $alias[$table];
|
||||
}
|
||||
}
|
||||
|
||||
if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
|
||||
throw new Exception('not support data:' . $key);
|
||||
}
|
||||
|
||||
if ('*' != $key && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) {
|
||||
$key = '"' . $key . '"';
|
||||
}
|
||||
|
||||
if (isset($table)) {
|
||||
$key = '"' . $table . '".' . $key;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand(Query $query): string
|
||||
{
|
||||
return 'DBMS_RANDOM.value';
|
||||
}
|
||||
}
|
||||
+118
@@ -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 $this->parseRaw($query, $key);
|
||||
}
|
||||
|
||||
$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()';
|
||||
}
|
||||
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
<?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 $this->parseRaw($query, $key);
|
||||
}
|
||||
|
||||
$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 ('*' != $key && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
|
||||
$key = '`' . $key . '`';
|
||||
}
|
||||
|
||||
if (isset($table)) {
|
||||
$key = '`' . $table . '`.' . $key;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置锁机制
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @param bool|string $lock
|
||||
* @return string
|
||||
*/
|
||||
protected function parseLock(Query $query, $lock = false): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
+184
@@ -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[] = $this->parseRaw($query, $val);
|
||||
} 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 $this->parseRaw($query, $key);
|
||||
}
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?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\exception\DbException;
|
||||
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
|
||||
|
||||
if (!preg_match('/^[\w\.\*]+$/', $field)) {
|
||||
throw new DbException('not support data:' . $field);
|
||||
}
|
||||
|
||||
$options = $this->getOptions();
|
||||
$subSql = $this->options($options)
|
||||
->field('count(' . $field . ') AS think_count')
|
||||
->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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,562 @@
|
||||
<?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($this->model) || empty($relation)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->options['relation'] = $relation;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用搜索器条件搜索字段
|
||||
* @access public
|
||||
* @param string|array $fields 搜索字段
|
||||
* @param mixed $data 搜索数据
|
||||
* @param string $prefix 字段前缀标识
|
||||
* @return $this
|
||||
*/
|
||||
public function withSearch($fields, $data = [], string $prefix = '')
|
||||
{
|
||||
if (is_string($fields)) {
|
||||
$fields = explode(',', $fields);
|
||||
}
|
||||
|
||||
$likeFields = $this->getConfig('match_like_fields') ?: [];
|
||||
|
||||
foreach ($fields as $key => $field) {
|
||||
if ($field instanceof Closure) {
|
||||
$field($this, $data[$key] ?? null, $data, $prefix);
|
||||
} 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);
|
||||
} elseif (isset($data[$field])) {
|
||||
$this->where($fieldName, in_array($fieldName, $likeFields) ? 'like' : '=', in_array($fieldName, $likeFields) ? '%' . $data[$field] . '%' : $data[$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置数据字段获取器
|
||||
* @access public
|
||||
* @param string|array $name 字段名
|
||||
* @param callable $callback 闭包获取器
|
||||
* @return $this
|
||||
*/
|
||||
public function withAttr($name, callable $callback = null)
|
||||
{
|
||||
if (is_array($name)) {
|
||||
foreach ($name as $key => $val) {
|
||||
$this->withAttr($key, $val);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->options['with_attr'][$name] = $callback;
|
||||
|
||||
if (strpos($name, '.')) {
|
||||
[$relation, $field] = explode('.', $name);
|
||||
|
||||
if (!empty($this->options['json']) && in_array($relation, $this->options['json'])) {
|
||||
|
||||
} else {
|
||||
$this->options['with_relation_attr'][$relation][$field] = $callback;
|
||||
unset($this->options['with_attr'][$name]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联预载入 In方式
|
||||
* @access public
|
||||
* @param array|string $with 关联方法名称
|
||||
* @return $this
|
||||
*/
|
||||
public function with($with)
|
||||
{
|
||||
if (empty($this->model) || empty($with)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$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($this->model) || 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 (empty($this->model)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (!$subQuery) {
|
||||
$this->options['with_aggregate'][] = [(array) $relations, $aggregate, $field];
|
||||
return $this;
|
||||
}
|
||||
|
||||
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 (empty($this->model)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字段数据转换
|
||||
* @access protected
|
||||
* @param array $result 查询数据
|
||||
* @return void
|
||||
*/
|
||||
protected function jsonModelResult(array &$result): void
|
||||
{
|
||||
$withAttr = $this->options['with_attr'];
|
||||
foreach ($this->options['json'] as $name) {
|
||||
if (!isset($result[$name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$jsonData = json_decode($result[$name], true);
|
||||
|
||||
if (isset($withAttr[$name])) {
|
||||
foreach ($withAttr[$name] as $key => $closure) {
|
||||
$jsonData[$key] = $closure($jsonData[$key] ?? null, $jsonData);
|
||||
}
|
||||
}
|
||||
|
||||
$result[$name] = !$this->options['json_assoc'] ? (object) $jsonData : $jsonData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据转换为模型数据集对象
|
||||
* @access protected
|
||||
* @param array $resultSet 数据集
|
||||
* @return ModelCollection
|
||||
*/
|
||||
protected function resultSetToModelCollection(array $resultSet): ModelCollection
|
||||
{
|
||||
if (empty($resultSet)) {
|
||||
return $this->model->toCollection();
|
||||
}
|
||||
|
||||
$this->options['is_resultSet'] = true;
|
||||
|
||||
foreach ($resultSet as $key => &$result) {
|
||||
// 数据转换为模型对象
|
||||
$this->resultToModel($result);
|
||||
}
|
||||
|
||||
foreach (['with', 'with_join'] as $with) {
|
||||
// 关联预载入
|
||||
if (!empty($this->options[$with])) {
|
||||
$result->eagerlyResultSet(
|
||||
$resultSet,
|
||||
$this->options[$with],
|
||||
$this->options['with_relation_attr'],
|
||||
'with_join' == $with ? true : false,
|
||||
$this->options['with_cache'] ?? false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 模型数据集转换
|
||||
return $this->model->toCollection($resultSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据转换为模型对象
|
||||
* @access protected
|
||||
* @param array $result 查询数据
|
||||
* @return void
|
||||
*/
|
||||
protected function resultToModel(array &$result): void
|
||||
{
|
||||
// JSON数据处理
|
||||
if (!empty($this->options['json'])) {
|
||||
$this->jsonModelResult($result);
|
||||
}
|
||||
|
||||
$result = $this->model->newInstance(
|
||||
$result,
|
||||
!empty($this->options['is_resultSet']) ? null : $this->getModelUpdateCondition($this->options),
|
||||
$this->options
|
||||
);
|
||||
|
||||
// 模型数据处理
|
||||
foreach ($this->options['filter'] as $filter) {
|
||||
call_user_func_array($filter, [$result, $this->options]);
|
||||
}
|
||||
|
||||
// 关联查询
|
||||
if (!empty($this->options['relation'])) {
|
||||
$result->relationQuery($this->options['relation'], $this->options['with_relation_attr']);
|
||||
}
|
||||
|
||||
// 关联预载入查询
|
||||
if (empty($this->options['is_resultSet'])) {
|
||||
foreach (['with', 'with_join'] as $with) {
|
||||
if (!empty($this->options[$with])) {
|
||||
$result->eagerlyResult(
|
||||
$this->options[$with],
|
||||
$this->options['with_relation_attr'],
|
||||
'with_join' == $with ? true : false,
|
||||
$this->options['with_cache'] ?? false
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 关联统计查询
|
||||
if (!empty($this->options['with_aggregate'])) {
|
||||
foreach ($this->options['with_aggregate'] as $val) {
|
||||
$result->relationCount($this, $val[0], $val[1], $val[2], false);
|
||||
}
|
||||
}
|
||||
|
||||
// 动态获取器
|
||||
if (!empty($this->options['with_attr'])) {
|
||||
$result->withAttr($this->options['with_attr']);
|
||||
}
|
||||
|
||||
foreach (['hidden', 'visible', 'append'] as $name) {
|
||||
if (!empty($this->options[$name])) {
|
||||
$result->$name($this->options[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新原始数据
|
||||
$result->refreshOrigin();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?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 bool $bind 是否自动参数绑定
|
||||
* @return $this
|
||||
*/
|
||||
public function autoBind(bool $bind)
|
||||
{
|
||||
$this->options['auto_bind'] = $bind;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否开启自动参数绑定
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isAutoBind(): bool
|
||||
{
|
||||
$autoBind = $this->getConfig('auto_param_bind');
|
||||
if (null !== $this->getOptions('auto_bind')) {
|
||||
$autoBind = $this->getOptions('auto_bind');
|
||||
}
|
||||
|
||||
return (bool) $autoBind;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数绑定
|
||||
* @access public
|
||||
* @param string $sql 绑定的sql表达式
|
||||
* @param array $bind 参数绑定
|
||||
* @return void
|
||||
*/
|
||||
public 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
<?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\Collection;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\db\Query;
|
||||
use think\helper\Str;
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 查询数据处理
|
||||
*/
|
||||
trait ResultOperation
|
||||
{
|
||||
/**
|
||||
* 设置数据处理(支持模型)
|
||||
* @access public
|
||||
* @param callable $filter 数据处理Callable
|
||||
* @param string $index 索引(唯一)
|
||||
* @return $this
|
||||
*/
|
||||
public function filter(callable $filter, string $index = null)
|
||||
{
|
||||
if ($index) {
|
||||
$this->options['filter'][$index] = $filter;
|
||||
} else {
|
||||
$this->options['filter'][] = $filter;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许返回空数据(或空模型)
|
||||
* @access public
|
||||
* @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
|
||||
{
|
||||
// JSON数据处理
|
||||
if (!empty($this->options['json'])) {
|
||||
$this->jsonResult($result);
|
||||
}
|
||||
|
||||
// 查询数据处理
|
||||
foreach ($this->options['filter'] as $filter) {
|
||||
$result = call_user_func_array($filter, [$result, $this->options]);
|
||||
}
|
||||
|
||||
// 获取器
|
||||
if (!empty($this->options['with_attr'])) {
|
||||
$this->getResultAttr($result, $this->options['with_attr']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理数据集
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param bool $toCollection 是否转为对象
|
||||
* @return void
|
||||
*/
|
||||
protected function resultSet(array &$resultSet, bool $toCollection = true): void
|
||||
{
|
||||
foreach ($resultSet as &$result) {
|
||||
$this->result($result);
|
||||
}
|
||||
|
||||
// 返回Collection对象
|
||||
if ($toCollection) {
|
||||
$resultSet = new Collection($resultSet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用获取器处理数据
|
||||
* @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|static
|
||||
* @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|static|mixed
|
||||
*/
|
||||
public function findOrEmpty($data = null)
|
||||
{
|
||||
return $this->allowEmpty(true)->find($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字段数据转换
|
||||
* @access protected
|
||||
* @param array $result 查询数据
|
||||
* @return void
|
||||
*/
|
||||
protected function jsonResult(array &$result): void
|
||||
{
|
||||
foreach ($this->options['json'] as $name) {
|
||||
if (!isset($result[$name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[$name] = json_decode($result[$name], true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询失败 抛出异常
|
||||
* @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|Collection|static[]
|
||||
* @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|static|mixed
|
||||
* @throws ModelNotFoundException
|
||||
* @throws DataNotFoundException
|
||||
*/
|
||||
public function findOrFail($data = null)
|
||||
{
|
||||
return $this->failException(true)->find($data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 ?: '');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
<?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, 'OR');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前时间在两个时间字段范围 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');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?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 Transaction
|
||||
{
|
||||
|
||||
/**
|
||||
* 执行数据库Xa事务
|
||||
* @access public
|
||||
* @param callable $callback 数据操作方法回调
|
||||
* @param array $dbs 多个查询对象或者连接对象
|
||||
* @return mixed
|
||||
* @throws PDOException
|
||||
* @throws \Exception
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function transactionXa(callable $callback, array $dbs = [])
|
||||
{
|
||||
return $this->connection->transactionXa($callback, $dbs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行数据库事务
|
||||
* @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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动XA事务
|
||||
* @access public
|
||||
* @param string $xid XA事务id
|
||||
* @return void
|
||||
*/
|
||||
public function startTransXa(string $xid): void
|
||||
{
|
||||
$this->connection->startTransXa($xid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预编译XA事务
|
||||
* @access public
|
||||
* @param string $xid XA事务id
|
||||
* @return void
|
||||
*/
|
||||
public function prepareXa(string $xid): void
|
||||
{
|
||||
$this->connection->prepareXa($xid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交XA事务
|
||||
* @access public
|
||||
* @param string $xid XA事务id
|
||||
* @return void
|
||||
*/
|
||||
public function commitXa(string $xid): void
|
||||
{
|
||||
$this->connection->commitXa($xid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回滚XA事务
|
||||
* @access public
|
||||
* @param string $xid XA事务id
|
||||
* @return void
|
||||
*/
|
||||
public function rollbackXa(string $xid): void
|
||||
{
|
||||
$this->connection->rollbackXa($xid);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,529 @@
|
||||
<?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')
|
||||
{
|
||||
$this->options['where'][$logic][] = [$field, 'EXP', new Raw($where, $bind)];
|
||||
|
||||
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')
|
||||
{
|
||||
$this->options['where'][$logic][] = new Raw($where, $bind);
|
||||
|
||||
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 ($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 ($condition instanceof Raw) {
|
||||
|
||||
} elseif (preg_match('/[,=\<\'\"\(\s]/', $field)) {
|
||||
return $this->whereRaw($field, is_array($op) ? $op : [], $logic);
|
||||
} elseif (is_string($op) && strtolower($op) == 'exp' && !is_null($condition)) {
|
||||
$bind = isset($param[2]) && is_array($param[2]) ? $param[2] : [];
|
||||
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)
|
||||
{
|
||||
$where = [];
|
||||
foreach ($field as $key => $val) {
|
||||
if (is_int($key)) {
|
||||
$where[] = $val;
|
||||
} elseif ($val instanceof Raw) {
|
||||
$where[] = [$key, 'exp', $val];
|
||||
} else {
|
||||
$where[] = is_null($val) ? [$key, 'NULL', ''] : [$key, is_array($val) ? 'IN' : '=', $val];
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+1175
File diff suppressed because it is too large
Load Diff
@@ -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' => 'NO' == $val['null'],
|
||||
'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): void
|
||||
{
|
||||
$this->initConnect(true);
|
||||
$this->linkID->exec("XA START '$xid'");
|
||||
}
|
||||
|
||||
/**
|
||||
* 预编译XA事务
|
||||
* @access public
|
||||
* @param string $xid XA事务id
|
||||
* @return void
|
||||
*/
|
||||
public function prepareXa(string $xid): void
|
||||
{
|
||||
$this->initConnect(true);
|
||||
$this->linkID->exec("XA END '$xid'");
|
||||
$this->linkID->exec("XA PREPARE '$xid'");
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交XA事务
|
||||
* @access public
|
||||
* @param string $xid XA事务id
|
||||
* @return void
|
||||
*/
|
||||
public function commitXa(string $xid): void
|
||||
{
|
||||
$this->initConnect(true);
|
||||
$this->linkID->exec("XA COMMIT '$xid'");
|
||||
}
|
||||
|
||||
/**
|
||||
* 回滚XA事务
|
||||
* @access public
|
||||
* @param string $xid XA事务id
|
||||
* @return void
|
||||
*/
|
||||
public function rollbackXa(string $xid): void
|
||||
{
|
||||
$this->initConnect(true);
|
||||
$this->linkID->exec("XA ROLLBACK '$xid'");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?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 = '" . $tableName . "' ) b where table_name = '" . $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|null $sequence 自增序列名
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLastInsID(BaseQuery $query, string $sequence = null)
|
||||
{
|
||||
if(!is_null($sequence)) {
|
||||
$pdo = $this->linkID->query("select {$sequence}.currval as id from dual");
|
||||
$result = $pdo->fetchColumn();
|
||||
}
|
||||
|
||||
return $result ?? null;
|
||||
}
|
||||
|
||||
protected function supportSavepoint(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?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'];
|
||||
}
|
||||
|
||||
if (!empty($config['trust_server_certificate'])) {
|
||||
$dsn .= ';TrustServerCertificate=' . $config['trust_server_certificate'];
|
||||
}
|
||||
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getFields(string $tableName): array
|
||||
{
|
||||
[$tableName] = explode(' ', $tableName);
|
||||
strpos($tableName, '.') && $tableName = substr($tableName, strpos($tableName, '.') + 1);
|
||||
$sql = "SELECT column_name, data_type, column_default, is_nullable
|
||||
FROM information_schema.tables AS t
|
||||
JOIN information_schema.columns AS c
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 '获得表信息';
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* Db事件异常
|
||||
*/
|
||||
class DbEventException extends DbException
|
||||
{
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
* Database相关异常处理类
|
||||
*/
|
||||
class DbException extends Exception
|
||||
{
|
||||
/**
|
||||
* DbException constructor.
|
||||
* @access public
|
||||
* @param string $message
|
||||
* @param array $config
|
||||
* @param string $sql
|
||||
* @param int $code
|
||||
*/
|
||||
public function __construct(string $message, array $config = [], string $sql = '', int $code = 10500)
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->code = $code;
|
||||
|
||||
$this->setData('Database Status', [
|
||||
'Error Code' => $code,
|
||||
'Error Message' => $message,
|
||||
'Error SQL' => $sql,
|
||||
]);
|
||||
|
||||
unset($config['username'], $config['password']);
|
||||
$this->setData('Database Config', $config);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
$message = $exception->getMessage();
|
||||
|
||||
if (!empty($error)) {
|
||||
$this->setData('PDO Error Info', [
|
||||
'SQLSTATE' => $error[0],
|
||||
'Driver Error Code' => isset($error[1]) ? $error[1] : 0,
|
||||
'Driver Error Message' => isset($error[2]) ? $error[2] : '',
|
||||
]);
|
||||
}
|
||||
|
||||
parent::__construct($message, $config, $sql, $code);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user