取消https验证,并添加vendor文件夹。

This commit is contained in:
2025-04-18 12:58:39 +08:00
parent 552909d82b
commit 2d3f7d8511
727 changed files with 132015 additions and 19 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+209
View File
@@ -0,0 +1,209 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace think\db;
use DateInterval;
use DateTime;
use DateTimeInterface;
use think\db\exception\InvalidArgumentException;
/**
* CacheItem实现类
*/
class CacheItem
{
/**
* 缓存Key
* @var string
*/
protected $key;
/**
* 缓存内容
* @var mixed
*/
protected $value;
/**
* 过期时间
* @var int|DateTimeInterface
*/
protected $expire;
/**
* 缓存tag
* @var string
*/
protected $tag;
/**
* 缓存是否命中
* @var bool
*/
protected $isHit = false;
public function __construct(string $key = null)
{
$this->key = $key;
}
/**
* 为此缓存项设置「键」
* @access public
* @param string $key
* @return $this
*/
public function setKey(string $key)
{
$this->key = $key;
return $this;
}
/**
* 返回当前缓存项的「键」
* @access public
* @return string
*/
public function getKey()
{
return $this->key;
}
/**
* 返回当前缓存项的有效期
* @access public
* @return DateTimeInterface|int|null
*/
public function getExpire()
{
if ($this->expire instanceof DateTimeInterface) {
return $this->expire;
}
return $this->expire ? $this->expire - time() : null;
}
/**
* 获取缓存Tag
* @access public
* @return string|array
*/
public function getTag()
{
return $this->tag;
}
/**
* 凭借此缓存项的「键」从缓存系统里面取出缓存项
* @access public
* @return mixed
*/
public function get()
{
return $this->value;
}
/**
* 确认缓存项的检查是否命中
* @access public
* @return bool
*/
public function isHit(): bool
{
return $this->isHit;
}
/**
* 为此缓存项设置「值」
* @access public
* @param mixed $value
* @return $this
*/
public function set($value)
{
$this->value = $value;
$this->isHit = true;
return $this;
}
/**
* 为此缓存项设置所属标签
* @access public
* @param string|array $tag
* @return $this
*/
public function tag($tag = null)
{
$this->tag = $tag;
return $this;
}
/**
* 设置缓存项的有效期
* @access public
* @param mixed $expire
* @return $this
*/
public function expire($expire)
{
if (is_null($expire)) {
$this->expire = null;
} elseif (is_numeric($expire) || $expire instanceof DateInterval) {
$this->expiresAfter($expire);
} elseif ($expire instanceof DateTimeInterface) {
$this->expire = $expire;
} else {
throw new InvalidArgumentException('not support datetime');
}
return $this;
}
/**
* 设置缓存项的准确过期时间点
* @access public
* @param DateTimeInterface $expiration
* @return $this
*/
public function expiresAt($expiration)
{
if ($expiration instanceof DateTimeInterface) {
$this->expire = $expiration;
} else {
throw new InvalidArgumentException('not support datetime');
}
return $this;
}
/**
* 设置缓存项的过期时间
* @access public
* @param int|DateInterval $timeInterval
* @return $this
* @throws InvalidArgumentException
*/
public function expiresAfter($timeInterval)
{
if ($timeInterval instanceof DateInterval) {
$this->expire = (int) DateTime::createFromFormat('U', (string) time())->add($timeInterval)->format('U');
} elseif (is_numeric($timeInterval)) {
$this->expire = $timeInterval + time();
} else {
throw new InvalidArgumentException('not support datetime');
}
return $this;
}
}
+347
View File
@@ -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();
}
}
+190
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
}
File diff suppressed because it is too large Load Diff
+450
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,182 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace think\db;
use ArrayAccess;
/**
* 数组查询对象
*/
class Where implements ArrayAccess
{
/**
* 查询表达式
* @var array
*/
protected $where = [];
/**
* 是否需要把查询条件两边增加括号
* @var bool
*/
protected $enclose = false;
/**
* 创建一个查询表达式
*
* @param array $where 查询条件数组
* @param bool $enclose 是否增加括号
*/
public function __construct(array $where = [], bool $enclose = false)
{
$this->where = $where;
$this->enclose = $enclose;
}
/**
* 设置是否添加括号
* @access public
* @param bool $enclose
* @return $this
*/
public function enclose(bool $enclose = true)
{
$this->enclose = $enclose;
return $this;
}
/**
* 解析为Query对象可识别的查询条件数组
* @access public
* @return array
*/
public function parse(): array
{
$where = [];
foreach ($this->where as $key => $val) {
if ($val instanceof Raw) {
$where[] = [$key, 'exp', $val];
} elseif (is_null($val)) {
$where[] = [$key, 'NULL', ''];
} elseif (is_array($val)) {
$where[] = $this->parseItem($key, $val);
} else {
$where[] = [$key, '=', $val];
}
}
return $this->enclose ? [$where] : $where;
}
/**
* 分析查询表达式
* @access protected
* @param string $field 查询字段
* @param array $where 查询条件
* @return array
*/
protected function parseItem(string $field, array $where = []): array
{
$op = $where[0];
$condition = $where[1] ?? null;
if (is_array($op)) {
// 同一字段多条件查询
array_unshift($where, $field);
} elseif (is_null($condition)) {
if (is_string($op) && in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) {
// null查询
$where = [$field, $op, ''];
} elseif (is_null($op) || '=' == $op) {
$where = [$field, 'NULL', ''];
} elseif ('<>' == $op) {
$where = [$field, 'NOTNULL', ''];
} else {
// 字段相等查询
$where = [$field, '=', $op];
}
} else {
$where = [$field, $op, $condition];
}
return $where;
}
/**
* 修改器 设置数据对象的值
* @access public
* @param string $name 名称
* @param mixed $value 值
* @return void
*/
public function __set($name, $value)
{
$this->where[$name] = $value;
}
/**
* 获取器 获取数据对象的值
* @access public
* @param string $name 名称
* @return mixed
*/
public function __get($name)
{
return $this->where[$name] ?? null;
}
/**
* 检测数据对象的值
* @access public
* @param string $name 名称
* @return bool
*/
public function __isset($name)
{
return isset($this->where[$name]);
}
/**
* 销毁数据对象的值
* @access public
* @param string $name 名称
* @return void
*/
public function __unset($name)
{
unset($this->where[$name]);
}
// ArrayAccess
public function offsetSet($name, $value)
{
$this->__set($name, $value);
}
public function offsetExists($name)
{
return $this->__isset($name);
}
public function offsetUnset($name)
{
$this->__unset($name);
}
public function offsetGet($name)
{
return $this->__get($name);
}
}
+675
View File
@@ -0,0 +1,675 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace think\db\builder;
use MongoDB\BSON\Javascript;
use MongoDB\BSON\ObjectID;
use MongoDB\BSON\Regex;
use MongoDB\Driver\BulkWrite;
use MongoDB\Driver\Command;
use MongoDB\Driver\Exception\InvalidArgumentException;
use MongoDB\Driver\Query as MongoQuery;
use think\db\connector\Mongo as Connection;
use think\db\exception\DbException as Exception;
use think\db\Mongo as Query;
class Mongo
{
// connection对象实例
protected $connection;
// 最后插入ID
protected $insertId = [];
// 查询表达式
protected $exp = ['<>' => 'ne', '=' => 'eq', '>' => 'gt', '>=' => 'gte', '<' => 'lt', '<=' => 'lte', 'in' => 'in', 'not in' => 'nin', 'nin' => 'nin', 'mod' => 'mod', 'exists' => 'exists', 'null' => 'null', 'notnull' => 'not null', 'not null' => 'not null', 'regex' => 'regex', 'type' => 'type', 'all' => 'all', '> time' => '> time', '< time' => '< time', 'between' => 'between', 'not between' => 'not between', 'between time' => 'between time', 'not between time' => 'not between time', 'notbetween time' => 'not between time', 'like' => 'like', 'near' => 'near', 'size' => 'size'];
/**
* 架构函数
* @access public
* @param Connection $connection 数据库连接对象实例
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
/**
* 获取当前的连接对象实例
* @access public
* @return Connection
*/
public function getConnection(): Connection
{
return $this->connection;
}
/**
* key分析
* @access protected
* @param string $key
* @return string
*/
protected function parseKey(Query $query, string $key): string
{
if (0 === strpos($key, '__TABLE__.')) {
[$collection, $key] = explode('.', $key, 2);
}
if ('id' == $key && $this->connection->getConfig('pk_convert_id')) {
$key = '_id';
}
return trim($key);
}
/**
* value分析
* @access protected
* @param Query $query 查询对象
* @param mixed $value
* @param string $field
* @return string
*/
protected function parseValue(Query $query, $value, $field = '')
{
if ('_id' == $field && 'ObjectID' == $this->connection->getConfig('pk_type') && is_string($value)) {
try {
return new ObjectID($value);
} catch (InvalidArgumentException $e) {
return new ObjectID();
}
}
return $value;
}
/**
* insert数据分析
* @access protected
* @param Query $query 查询对象
* @param array $data 数据
* @return array
*/
protected function parseData(Query $query, array $data): array
{
if (empty($data)) {
return [];
}
$result = [];
foreach ($data as $key => $val) {
$item = $this->parseKey($query, $key);
if (is_object($val)) {
$result[$item] = $val;
} elseif (isset($val[0]) && 'exp' == $val[0]) {
$result[$item] = $val[1];
} elseif (is_null($val)) {
$result[$item] = 'NULL';
} else {
$result[$item] = $this->parseValue($query, $val, $key);
}
}
return $result;
}
/**
* Set数据分析
* @access protected
* @param Query $query 查询对象
* @param array $data 数据
* @return array
*/
protected function parseSet(Query $query, array $data): array
{
if (empty($data)) {
return [];
}
$result = [];
foreach ($data as $key => $val) {
$item = $this->parseKey($query, $key);
if (is_array($val) && isset($val[0]) && is_string($val[0]) && 0 === strpos($val[0], '$')) {
$result[$val[0]][$item] = $this->parseValue($query, $val[1], $key);
} else {
$result['$set'][$item] = $this->parseValue($query, $val, $key);
}
}
return $result;
}
/**
* 生成查询过滤条件
* @access public
* @param Query $query 查询对象
* @param mixed $where
* @return array
*/
public function parseWhere(Query $query, array $where): array
{
if (empty($where)) {
$where = [];
}
$filter = [];
foreach ($where as $logic => $val) {
$logic = '$' . strtolower($logic);
foreach ($val as $field => $value) {
if (is_array($value)) {
if (key($value) !== 0) {
throw new Exception('where express error:' . var_export($value, true));
}
$field = array_shift($value);
} elseif (!($value instanceof \Closure)) {
throw new Exception('where express error:' . var_export($value, true));
}
if ($value instanceof \Closure) {
// 使用闭包查询
$query = new Query($this->connection);
call_user_func_array($value, [ & $query]);
$filter[$logic][] = $this->parseWhere($query, $query->getOptions('where'));
} else {
if (strpos($field, '|')) {
// 不同字段使用相同查询条件(OR)
$array = explode('|', $field);
foreach ($array as $k) {
$filter['$or'][] = $this->parseWhereItem($query, $k, $value);
}
} elseif (strpos($field, '&')) {
// 不同字段使用相同查询条件(AND)
$array = explode('&', $field);
foreach ($array as $k) {
$filter['$and'][] = $this->parseWhereItem($query, $k, $value);
}
} else {
// 对字段使用表达式查询
$field = is_string($field) ? $field : '';
$filter[$logic][] = $this->parseWhereItem($query, $field, $value);
}
}
}
}
$options = $query->getOptions();
if (!empty($options['soft_delete'])) {
// 附加软删除条件
[$field, $condition] = $options['soft_delete'];
$filter['$and'][] = $this->parseWhereItem($query, $field, $condition);
}
return $filter;
}
// where子单元分析
protected function parseWhereItem(Query $query, $field, $val): array
{
$key = $field ? $this->parseKey($query, $field) : '';
// 查询规则和条件
if (!is_array($val)) {
$val = ['=', $val];
}
[$exp, $value] = $val;
// 对一个字段使用多个查询条件
if (is_array($exp)) {
$data = [];
foreach ($val as $value) {
$exp = $value[0];
$value = $value[1];
if (!in_array($exp, $this->exp)) {
$exp = strtolower($exp);
if (isset($this->exp[$exp])) {
$exp = $this->exp[$exp];
}
}
$k = '$' . $exp;
$data[$k] = $value;
}
$result[$key] = $data;
return $result;
} elseif (!in_array($exp, $this->exp)) {
$exp = strtolower($exp);
if (isset($this->exp[$exp])) {
$exp = $this->exp[$exp];
} else {
throw new Exception('where express error:' . $exp);
}
}
$result = [];
if ('=' == $exp) {
// 普通查询
$result[$key] = $this->parseValue($query, $value, $key);
} elseif (in_array($exp, ['neq', 'ne', 'gt', 'egt', 'gte', 'lt', 'lte', 'elt', 'mod'])) {
// 比较运算
$k = '$' . $exp;
$result[$key] = [$k => $this->parseValue($query, $value, $key)];
} elseif ('null' == $exp) {
// NULL 查询
$result[$key] = null;
} elseif ('not null' == $exp) {
$result[$key] = ['$ne' => null];
} elseif ('all' == $exp) {
// 满足所有指定条件
$result[$key] = ['$all', $this->parseValue($query, $value, $key)];
} elseif ('between' == $exp) {
// 区间查询
$value = is_array($value) ? $value : explode(',', $value);
$result[$key] = ['$gte' => $this->parseValue($query, $value[0], $key), '$lte' => $this->parseValue($query, $value[1], $key)];
} elseif ('not between' == $exp) {
// 范围查询
$value = is_array($value) ? $value : explode(',', $value);
$result[$key] = ['$lt' => $this->parseValue($query, $value[0], $key), '$gt' => $this->parseValue($query, $value[1], $key)];
} elseif ('exists' == $exp) {
// 字段是否存在
$result[$key] = ['$exists' => (bool) $value];
} elseif ('type' == $exp) {
// 类型查询
$result[$key] = ['$type' => intval($value)];
} elseif ('exp' == $exp) {
// 表达式查询
$result['$where'] = $value instanceof Javascript ? $value : new Javascript($value);
} elseif ('like' == $exp) {
// 模糊查询 采用正则方式
$result[$key] = $value instanceof Regex ? $value : new Regex($value, 'i');
} elseif (in_array($exp, ['nin', 'in'])) {
// IN 查询
$value = is_array($value) ? $value : explode(',', $value);
foreach ($value as $k => $val) {
$value[$k] = $this->parseValue($query, $val, $key);
}
$result[$key] = ['$' . $exp => $value];
} elseif ('regex' == $exp) {
$result[$key] = $value instanceof Regex ? $value : new Regex($value, 'i');
} elseif ('< time' == $exp) {
$result[$key] = ['$lt' => $this->parseDateTime($query, $value, $field)];
} elseif ('> time' == $exp) {
$result[$key] = ['$gt' => $this->parseDateTime($query, $value, $field)];
} elseif ('between time' == $exp) {
// 区间查询
$value = is_array($value) ? $value : explode(',', $value);
$result[$key] = ['$gte' => $this->parseDateTime($query, $value[0], $field), '$lte' => $this->parseDateTime($query, $value[1], $field)];
} elseif ('not between time' == $exp) {
// 范围查询
$value = is_array($value) ? $value : explode(',', $value);
$result[$key] = ['$lt' => $this->parseDateTime($query, $value[0], $field), '$gt' => $this->parseDateTime($query, $value[1], $field)];
} elseif ('near' == $exp) {
// 经纬度查询
$result[$key] = ['$near' => $this->parseValue($query, $value, $key)];
} elseif ('size' == $exp) {
// 元素长度查询
$result[$key] = ['$size' => intval($value)];
} else {
// 普通查询
$result[$key] = $this->parseValue($query, $value, $key);
}
return $result;
}
/**
* 日期时间条件解析
* @access protected
* @param Query $query 查询对象
* @param string $value
* @param string $key
* @return string
*/
protected function parseDateTime(Query $query, $value, $key)
{
// 获取时间字段类型
$type = $query->getFieldType($key);
if ($type) {
if (is_string($value)) {
$value = strtotime($value) ?: $value;
}
if (is_int($value)) {
if (preg_match('/(datetime|timestamp)/is', $type)) {
// 日期及时间戳类型
$value = date('Y-m-d H:i:s', $value);
} elseif (preg_match('/(date)/is', $type)) {
// 日期及时间戳类型
$value = date('Y-m-d', $value);
}
}
}
return $value;
}
/**
* 获取最后写入的ID 如果是insertAll方法的话 返回所有写入的ID
* @access public
* @return mixed
*/
public function getLastInsID()
{
return $this->insertId;
}
/**
* 生成insert BulkWrite对象
* @access public
* @param Query $query 查询对象
* @return BulkWrite
*/
public function insert(Query $query): BulkWrite
{
// 分析并处理数据
$options = $query->getOptions();
$data = $this->parseData($query, $options['data']);
$bulk = new BulkWrite;
if ($insertId = $bulk->insert($data)) {
$this->insertId = $insertId;
}
$this->log('insert', $data, $options);
return $bulk;
}
/**
* 生成insertall BulkWrite对象
* @access public
* @param Query $query 查询对象
* @param array $dataSet 数据集
* @return BulkWrite
*/
public function insertAll(Query $query, array $dataSet): BulkWrite
{
$bulk = new BulkWrite;
$options = $query->getOptions();
$this->insertId = [];
foreach ($dataSet as $data) {
// 分析并处理数据
$data = $this->parseData($query, $data);
if ($insertId = $bulk->insert($data)) {
$this->insertId[] = $insertId;
}
}
$this->log('insert', $dataSet, $options);
return $bulk;
}
/**
* 生成update BulkWrite对象
* @access public
* @param Query $query 查询对象
* @return BulkWrite
*/
public function update(Query $query): BulkWrite
{
$options = $query->getOptions();
$data = $this->parseSet($query, $options['data']);
$where = $this->parseWhere($query, $options['where']);
if (1 == $options['limit']) {
$updateOptions = ['multi' => false];
} else {
$updateOptions = ['multi' => true];
}
$bulk = new BulkWrite;
$bulk->update($where, $data, $updateOptions);
$this->log('update', $data, $where);
return $bulk;
}
/**
* 生成delete BulkWrite对象
* @access public
* @param Query $query 查询对象
* @return BulkWrite
*/
public function delete(Query $query): BulkWrite
{
$options = $query->getOptions();
$where = $this->parseWhere($query, $options['where']);
$bulk = new BulkWrite;
if (1 == $options['limit']) {
$deleteOptions = ['limit' => 1];
} else {
$deleteOptions = ['limit' => 0];
}
$bulk->delete($where, $deleteOptions);
$this->log('remove', $where, $deleteOptions);
return $bulk;
}
/**
* 生成Mongo查询对象
* @access public
* @param Query $query 查询对象
* @param bool $one 是否仅获取一个记录
* @return MongoQuery
*/
public function select(Query $query, bool $one = false): MongoQuery
{
$options = $query->getOptions();
$where = $this->parseWhere($query, $options['where']);
if ($one) {
$options['limit'] = 1;
}
$query = new MongoQuery($where, $options);
$this->log('find', $where, $options);
return $query;
}
/**
* 生成Count命令
* @access public
* @param Query $query 查询对象
* @return Command
*/
public function count(Query $query): Command
{
$options = $query->getOptions();
$cmd['count'] = $options['table'];
$cmd['query'] = (object) $this->parseWhere($query, $options['where']);
foreach (['hint', 'limit', 'maxTimeMS', 'skip'] as $option) {
if (isset($options[$option])) {
$cmd[$option] = $options[$option];
}
}
$command = new Command($cmd);
$this->log('cmd', 'count', $cmd);
return $command;
}
/**
* 聚合查询命令
* @access public
* @param Query $query 查询对象
* @param array $extra 指令和字段
* @return Command
*/
public function aggregate(Query $query, array $extra): Command
{
$options = $query->getOptions();
[$fun, $field] = $extra;
if ('id' == $field && $this->connection->getConfig('pk_convert_id')) {
$field = '_id';
}
$group = isset($options['group']) ? '$' . $options['group'] : null;
$pipeline = [
['$match' => (object) $this->parseWhere($query, $options['where'])],
['$group' => ['_id' => $group, 'aggregate' => ['$' . $fun => '$' . $field]]],
];
$cmd = [
'aggregate' => $options['table'],
'allowDiskUse' => true,
'pipeline' => $pipeline,
'cursor' => new \stdClass,
];
foreach (['explain', 'collation', 'bypassDocumentValidation', 'readConcern'] as $option) {
if (isset($options[$option])) {
$cmd[$option] = $options[$option];
}
}
$command = new Command($cmd);
$this->log('aggregate', $cmd);
return $command;
}
/**
* 多聚合查询命令, 可以对多个字段进行 group by 操作
*
* @param Query $query 查询对象
* @param array $extra 指令和字段
* @return Command
*/
public function multiAggregate(Query $query, $extra): Command
{
$options = $query->getOptions();
[$aggregate, $groupBy] = $extra;
$groups = ['_id' => []];
foreach ($groupBy as $field) {
$groups['_id'][$field] = '$' . $field;
}
foreach ($aggregate as $fun => $field) {
$groups[$field . '_' . $fun] = ['$' . $fun => '$' . $field];
}
$pipeline = [
['$match' => (object) $this->parseWhere($query, $options['where'])],
['$group' => $groups],
];
$cmd = [
'aggregate' => $options['table'],
'allowDiskUse' => true,
'pipeline' => $pipeline,
'cursor' => new \stdClass,
];
foreach (['explain', 'collation', 'bypassDocumentValidation', 'readConcern'] as $option) {
if (isset($options[$option])) {
$cmd[$option] = $options[$option];
}
}
$command = new Command($cmd);
$this->log('group', $cmd);
return $command;
}
/**
* 生成distinct命令
* @access public
* @param Query $query 查询对象
* @param string $field 字段名
* @return Command
*/
public function distinct(Query $query, $field): Command
{
$options = $query->getOptions();
$cmd = [
'distinct' => $options['table'],
'key' => $field,
];
if (!empty($options['where'])) {
$cmd['query'] = (object) $this->parseWhere($query, $options['where']);
}
if (isset($options['maxTimeMS'])) {
$cmd['maxTimeMS'] = $options['maxTimeMS'];
}
$command = new Command($cmd);
$this->log('cmd', 'distinct', $cmd);
return $command;
}
/**
* 查询所有的collection
* @access public
* @return Command
*/
public function listcollections(): Command
{
$cmd = ['listCollections' => 1];
$command = new Command($cmd);
$this->log('cmd', 'listCollections', $cmd);
return $command;
}
/**
* 查询数据表的状态信息
* @access public
* @param Query $query 查询对象
* @return Command
*/
public function collStats(Query $query): Command
{
$options = $query->getOptions();
$cmd = ['collStats' => $options['table']];
$command = new Command($cmd);
$this->log('cmd', 'collStats', $cmd);
return $command;
}
protected function log($type, $data, $options = [])
{
$this->connection->mongoLog($type, $data, $options);
}
}
+432
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,118 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace think\db\builder;
use think\db\Builder;
use think\db\Query;
use think\db\Raw;
/**
* Pgsql数据库驱动
*/
class Pgsql extends Builder
{
/**
* INSERT SQL表达式
* @var string
*/
protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
/**
* INSERT ALL SQL表达式
* @var string
*/
protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
/**
* limit分析
* @access protected
* @param Query $query 查询对象
* @param mixed $limit
* @return string
*/
public function parseLimit(Query $query, string $limit): string
{
$limitStr = '';
if (!empty($limit)) {
$limit = explode(',', $limit);
if (count($limit) > 1) {
$limitStr .= ' LIMIT ' . $limit[1] . ' OFFSET ' . $limit[0] . ' ';
} else {
$limitStr .= ' LIMIT ' . $limit[0] . ' ';
}
}
return $limitStr;
}
/**
* 字段和表名处理
* @access public
* @param Query $query 查询对象
* @param mixed $key 字段名
* @param bool $strict 严格检测
* @return string
*/
public function parseKey(Query $query, $key, bool $strict = false): string
{
if (is_int($key)) {
return (string) $key;
} elseif ($key instanceof Raw) {
return $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
View File
@@ -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
View File
@@ -0,0 +1,184 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\builder;
use think\db\Builder;
use think\db\exception\DbException as Exception;
use think\db\Query;
use think\db\Raw;
/**
* Sqlsrv数据库驱动
*/
class Sqlsrv extends Builder
{
/**
* SELECT SQL表达式
* @var string
*/
protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%';
/**
* SELECT INSERT SQL表达式
* @var string
*/
protected $selectInsertSql = 'SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%';
/**
* UPDATE SQL表达式
* @var string
*/
protected $updateSql = 'UPDATE %TABLE% SET %SET% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
/**
* DELETE SQL表达式
* @var string
*/
protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
/**
* INSERT SQL表达式
* @var string
*/
protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
/**
* INSERT ALL SQL表达式
* @var string
*/
protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
/**
* order分析
* @access protected
* @param Query $query 查询对象
* @param mixed $order
* @return string
*/
protected function parseOrder(Query $query, array $order): string
{
if (empty($order)) {
return ' ORDER BY rand()';
}
$array = [];
foreach ($order as $key => $val) {
if ($val instanceof Raw) {
$array[] = $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();
}
}
+133
View File
@@ -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');
}
}
+122
View File
@@ -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);
}
}
+529
View File
@@ -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;
}
}
File diff suppressed because it is too large Load Diff
+162
View File
@@ -0,0 +1,162 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace think\db\connector;
use PDO;
use think\db\PDOConnection;
/**
* mysql数据库驱动
*/
class Mysql extends PDOConnection
{
/**
* 解析pdo连接的dsn信息
* @access protected
* @param array $config 连接信息
* @return string
*/
protected function parseDsn(array $config): string
{
if (!empty($config['socket'])) {
$dsn = 'mysql:unix_socket=' . $config['socket'];
} elseif (!empty($config['hostport'])) {
$dsn = 'mysql:host=' . $config['hostname'] . ';port=' . $config['hostport'];
} else {
$dsn = 'mysql:host=' . $config['hostname'];
}
$dsn .= ';dbname=' . $config['database'];
if (!empty($config['charset'])) {
$dsn .= ';charset=' . $config['charset'];
}
return $dsn;
}
/**
* 取得数据表的字段信息
* @access public
* @param string $tableName
* @return array
*/
public function getFields(string $tableName): array
{
[$tableName] = explode(' ', $tableName);
if (false === strpos($tableName, '`')) {
if (strpos($tableName, '.')) {
$tableName = str_replace('.', '`.`', $tableName);
}
$tableName = '`' . $tableName . '`';
}
$sql = 'SHOW FULL COLUMNS FROM ' . $tableName;
$pdo = $this->getPDOStatement($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
if (!empty($result)) {
foreach ($result as $key => $val) {
$val = array_change_key_case($val);
$info[$val['field']] = [
'name' => $val['field'],
'type' => $val['type'],
'notnull' => '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'");
}
}
+119
View File
@@ -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;
}
}
+108
View File
@@ -0,0 +1,108 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\connector;
use PDO;
use think\db\PDOConnection;
/**
* Pgsql数据库驱动
*/
class Pgsql extends PDOConnection
{
/**
* 默认PDO连接参数
* @var array
*/
protected $params = [
PDO::ATTR_CASE => PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
];
/**
* 解析pdo连接的dsn信息
* @access protected
* @param array $config 连接信息
* @return string
*/
protected function parseDsn(array $config): string
{
$dsn = 'pgsql:dbname=' . $config['database'] . ';host=' . $config['hostname'];
if (!empty($config['hostport'])) {
$dsn .= ';port=' . $config['hostport'];
}
return $dsn;
}
/**
* 取得数据表的字段信息
* @access public
* @param string $tableName
* @return array
*/
public function getFields(string $tableName): array
{
[$tableName] = explode(' ', $tableName);
$sql = 'select fields_name as "field",fields_type as "type",fields_not_null as "null",fields_key_name as "key",fields_default as "default",fields_default as "extra" from table_msg(\'' . $tableName . '\');';
$pdo = $this->getPDOStatement($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
if (!empty($result)) {
foreach ($result as $key => $val) {
$val = array_change_key_case($val);
$info[$val['field']] = [
'name' => $val['field'],
'type' => $val['type'],
'notnull' => (bool) ('' !== $val['null']),
'default' => $val['default'],
'primary' => !empty($val['key']),
'autoinc' => (0 === strpos($val['extra'], 'nextval(')),
];
}
}
return $this->fieldCase($info);
}
/**
* 取得数据库的表信息
* @access public
* @param string $dbName
* @return array
*/
public function getTables(string $dbName = ''): array
{
$sql = "select tablename as Tables_in_test from pg_tables where schemaname ='public'";
$pdo = $this->getPDOStatement($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
foreach ($result as $key => $val) {
$info[$key] = current($val);
}
return $info;
}
protected function supportSavepoint(): bool
{
return true;
}
}
+96
View File
@@ -0,0 +1,96 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\connector;
use PDO;
use think\db\PDOConnection;
/**
* Sqlite数据库驱动
*/
class Sqlite extends PDOConnection
{
/**
* 解析pdo连接的dsn信息
* @access protected
* @param array $config 连接信息
* @return string
*/
protected function parseDsn(array $config): string
{
$dsn = 'sqlite:' . $config['database'];
return $dsn;
}
/**
* 取得数据表的字段信息
* @access public
* @param string $tableName
* @return array
*/
public function getFields(string $tableName): array
{
[$tableName] = explode(' ', $tableName);
$sql = 'PRAGMA table_info( \'' . $tableName . '\' )';
$pdo = $this->getPDOStatement($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
if (!empty($result)) {
foreach ($result as $key => $val) {
$val = array_change_key_case($val);
$info[$val['name']] = [
'name' => $val['name'],
'type' => $val['type'],
'notnull' => 1 === $val['notnull'],
'default' => $val['dflt_value'],
'primary' => '1' == $val['pk'],
'autoinc' => '1' == $val['pk'],
];
}
}
return $this->fieldCase($info);
}
/**
* 取得数据库的表信息
* @access public
* @param string $dbName
* @return array
*/
public function getTables(string $dbName = ''): array
{
$sql = "SELECT name FROM sqlite_master WHERE type='table' "
. "UNION ALL SELECT name FROM sqlite_temp_master "
. "WHERE type='table' ORDER BY name";
$pdo = $this->getPDOStatement($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
$info = [];
foreach ($result as $key => $val) {
$info[$key] = current($val);
}
return $info;
}
protected function supportSavepoint(): bool
{
return true;
}
}
+126
View File
@@ -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;
}
}
+117
View File
@@ -0,0 +1,117 @@
CREATE OR REPLACE FUNCTION pgsql_type(a_type varchar) RETURNS varchar AS
$BODY$
DECLARE
v_type varchar;
BEGIN
IF a_type='int8' THEN
v_type:='bigint';
ELSIF a_type='int4' THEN
v_type:='integer';
ELSIF a_type='int2' THEN
v_type:='smallint';
ELSIF a_type='bpchar' THEN
v_type:='char';
ELSE
v_type:=a_type;
END IF;
RETURN v_type;
END;
$BODY$
LANGUAGE PLPGSQL;
CREATE TYPE "public"."tablestruct" AS (
"fields_key_name" varchar(100),
"fields_name" VARCHAR(200),
"fields_type" VARCHAR(20),
"fields_length" BIGINT,
"fields_not_null" VARCHAR(10),
"fields_default" VARCHAR(500),
"fields_comment" VARCHAR(1000)
);
CREATE OR REPLACE FUNCTION "public"."table_msg" (a_schema_name varchar, a_table_name varchar) RETURNS SETOF "public"."tablestruct" AS
$body$
DECLARE
v_ret tablestruct;
v_oid oid;
v_sql varchar;
v_rec RECORD;
v_key varchar;
BEGIN
SELECT
pg_class.oid INTO v_oid
FROM
pg_class
INNER JOIN pg_namespace ON (pg_class.relnamespace = pg_namespace.oid AND lower(pg_namespace.nspname) = a_schema_name)
WHERE
pg_class.relname=a_table_name;
IF NOT FOUND THEN
RETURN;
END IF;
v_sql='
SELECT
pg_attribute.attname AS fields_name,
pg_attribute.attnum AS fields_index,
pgsql_type(pg_type.typname::varchar) AS fields_type,
pg_attribute.atttypmod-4 as fields_length,
CASE WHEN pg_attribute.attnotnull THEN ''not null''
ELSE ''''
END AS fields_not_null,
pg_attrdef.adsrc AS fields_default,
pg_description.description AS fields_comment
FROM
pg_attribute
INNER JOIN pg_class ON pg_attribute.attrelid = pg_class.oid
INNER JOIN pg_type ON pg_attribute.atttypid = pg_type.oid
LEFT OUTER JOIN pg_attrdef ON pg_attrdef.adrelid = pg_class.oid AND pg_attrdef.adnum = pg_attribute.attnum
LEFT OUTER JOIN pg_description ON pg_description.objoid = pg_class.oid AND pg_description.objsubid = pg_attribute.attnum
WHERE
pg_attribute.attnum > 0
AND attisdropped <> ''t''
AND pg_class.oid = ' || v_oid || '
ORDER BY pg_attribute.attnum' ;
FOR v_rec IN EXECUTE v_sql LOOP
v_ret.fields_name=v_rec.fields_name;
v_ret.fields_type=v_rec.fields_type;
IF v_rec.fields_length > 0 THEN
v_ret.fields_length:=v_rec.fields_length;
ELSE
v_ret.fields_length:=NULL;
END IF;
v_ret.fields_not_null=v_rec.fields_not_null;
v_ret.fields_default=v_rec.fields_default;
v_ret.fields_comment=v_rec.fields_comment;
SELECT constraint_name INTO v_key FROM information_schema.key_column_usage WHERE table_schema=a_schema_name AND table_name=a_table_name AND column_name=v_rec.fields_name;
IF FOUND THEN
v_ret.fields_key_name=v_key;
ELSE
v_ret.fields_key_name='';
END IF;
RETURN NEXT v_ret;
END LOOP;
RETURN ;
END;
$body$
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;
COMMENT ON FUNCTION "public"."table_msg"(a_schema_name varchar, a_table_name varchar)
IS '获得表信息';
---重载一个函数
CREATE OR REPLACE FUNCTION "public"."table_msg" (a_table_name varchar) RETURNS SETOF "public"."tablestruct" AS
$body$
DECLARE
v_ret tablestruct;
BEGIN
FOR v_ret IN SELECT * FROM table_msg('public',a_table_name) LOOP
RETURN NEXT v_ret;
END LOOP;
RETURN;
END;
$body$
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;
COMMENT ON FUNCTION "public"."table_msg"(a_table_name varchar)
IS '获得表信息';
@@ -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);
}
}