no message
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2023 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
|
||||
{
|
||||
/**
|
||||
* 聚合查询.
|
||||
*
|
||||
* @param string $aggregate 聚合方法
|
||||
* @param string|Raw $field 字段名
|
||||
* @param bool $force 强制转为数字类型
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function aggregate(string $aggregate, string|Raw $field, bool $force = false, bool $one = false)
|
||||
{
|
||||
return $this->connection->aggregate($this, $aggregate, $field, $force, $one);
|
||||
}
|
||||
|
||||
/**
|
||||
* COUNT查询.
|
||||
*
|
||||
* @param string $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();
|
||||
if (isset($options['cache'])) {
|
||||
$cache = $options['cache'];
|
||||
unset($options['cache']);
|
||||
}
|
||||
|
||||
$subSql = $this->options($options)
|
||||
->field('count(' . $field . ') AS think_count')
|
||||
->bind($this->bind)
|
||||
->buildSql();
|
||||
|
||||
$query = $this->newQuery();
|
||||
if (isset($cache)) {
|
||||
$query->setOption('cache', $cache);
|
||||
}
|
||||
$query->table([$subSql => '_group_count_']);
|
||||
|
||||
$count = $query->aggregate('COUNT', '*');
|
||||
} else {
|
||||
$count = $this->aggregate('COUNT', $field);
|
||||
}
|
||||
|
||||
return (int) $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* SUM查询.
|
||||
*
|
||||
* @param string|Raw $field 字段名
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function sum(string|Raw $field): float
|
||||
{
|
||||
return $this->aggregate('SUM', $field, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* MIN查询.
|
||||
*
|
||||
* @param string|Raw $field 字段名
|
||||
* @param bool $force 强制转为数字类型
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function min(string|Raw $field, bool $force = true)
|
||||
{
|
||||
return $this->aggregate('MIN', $field, $force);
|
||||
}
|
||||
|
||||
/**
|
||||
* MAX查询.
|
||||
*
|
||||
* @param string|Raw $field 字段名
|
||||
* @param bool $force 强制转为数字类型
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function max(string|Raw $field, bool $force = true)
|
||||
{
|
||||
return $this->aggregate('MAX', $field, $force);
|
||||
}
|
||||
|
||||
/**
|
||||
* AVG查询.
|
||||
*
|
||||
* @param string|Raw $field 字段名
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function avg(string|Raw $field): float
|
||||
{
|
||||
return $this->aggregate('AVG', $field, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2023 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.
|
||||
*
|
||||
* @param array|string|Raw $join 关联的表名
|
||||
* @param mixed $condition 条件
|
||||
* @param string $type JOIN类型
|
||||
* @param array $bind 参数绑定
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function join(array|string|Raw $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.
|
||||
*
|
||||
* @param array|string|Raw $join 关联的表名
|
||||
* @param mixed $condition 条件
|
||||
* @param array $bind 参数绑定
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function leftJoin(array|string|Raw $join, string $condition = null, array $bind = [])
|
||||
{
|
||||
return $this->join($join, $condition, 'LEFT', $bind);
|
||||
}
|
||||
|
||||
/**
|
||||
* RIGHT JOIN.
|
||||
*
|
||||
* @param array|string|Raw $join 关联的表名
|
||||
* @param mixed $condition 条件
|
||||
* @param array $bind 参数绑定
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function rightJoin(array|string|Raw $join, string $condition = null, array $bind = [])
|
||||
{
|
||||
return $this->join($join, $condition, 'RIGHT', $bind);
|
||||
}
|
||||
|
||||
/**
|
||||
* FULL JOIN.
|
||||
*
|
||||
* @param array|string|Raw $join 关联的表名
|
||||
* @param mixed $condition 条件
|
||||
* @param array $bind 参数绑定
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function fullJoin(array|string|Raw $join, string $condition = null, array $bind = [])
|
||||
{
|
||||
return $this->join($join, $condition, 'FULL');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Join表名及别名 支持
|
||||
* ['prefix_table或者子查询'=>'alias'] 'table alias'.
|
||||
*
|
||||
* @param array|string|Raw $join JION表名
|
||||
* @param string $alias 别名
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
protected function getJoinTable(array|string|Raw $join, string &$alias = null)
|
||||
{
|
||||
if (is_array($join)) {
|
||||
$table = $join;
|
||||
$alias = array_shift($join);
|
||||
|
||||
return $table;
|
||||
} elseif ($join instanceof Raw) {
|
||||
return $join;
|
||||
}
|
||||
|
||||
$join = trim($join);
|
||||
|
||||
if (str_contains($join, '(')) {
|
||||
// 使用子查询
|
||||
$table = $join;
|
||||
} else {
|
||||
// 使用别名
|
||||
if (str_contains($join, ' ')) {
|
||||
// 使用别名
|
||||
[$table, $alias] = explode(' ', $join);
|
||||
} else {
|
||||
$table = $join;
|
||||
if (!str_contains($join, '.')) {
|
||||
$alias = $join;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->prefix && !str_contains($table, '.') && !str_starts_with($table, $this->prefix)) {
|
||||
$table = $this->getTable($table);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($alias) && $table != $alias) {
|
||||
$table = [$table => $alias];
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定JOIN查询字段.
|
||||
*
|
||||
* @param array|string|Raw $join 数据表
|
||||
* @param string|array|bool $field 查询字段
|
||||
* @param string $on JOIN条件
|
||||
* @param string $type JOIN类型
|
||||
* @param array $bind 参数绑定
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function view(array|string|Raw $join, string|array|bool $field = true, string $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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 视图查询处理.
|
||||
*
|
||||
* @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 (str_contains($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,641 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2023 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;
|
||||
|
||||
/**
|
||||
* 指定模型.
|
||||
*
|
||||
* @param Model $model 模型对象实例
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function model(Model $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的模型对象
|
||||
*
|
||||
* @return Model|null
|
||||
*/
|
||||
public function getModel()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要隐藏的输出属性.
|
||||
*
|
||||
* @param array $hidden 属性列表
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function hidden(array $hidden = [])
|
||||
{
|
||||
$this->options['hidden'] = $hidden;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要输出的属性.
|
||||
*
|
||||
* @param array $visible
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function visible(array $visible = [])
|
||||
{
|
||||
$this->options['visible'] = $visible;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要附加的输出属性.
|
||||
*
|
||||
* @param array $append 属性列表
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function append(array $append = [])
|
||||
{
|
||||
$this->options['append'] = $append;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加查询范围.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置关联查询.
|
||||
*
|
||||
* @param array $relation 关联名称
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function relation(array $relation)
|
||||
{
|
||||
if (empty($this->model) || empty($relation)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->options['relation'] = $relation;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用搜索器条件搜索字段.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 限制关联数据的字段 已废弃直接使用field或withoutfield替代.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @param array|string $field 关联字段限制
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withField($field)
|
||||
{
|
||||
return $this->field($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 限制关联数据的数量 已废弃直接使用limit替代.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @param int $limit 关联数量限制
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withLimit(int $limit)
|
||||
{
|
||||
return $this->limit($limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置关联数据不存在的时候默认值
|
||||
*
|
||||
* @param mixed $data 默认值
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withDefault($data = null)
|
||||
{
|
||||
$this->options['default_model'] = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置数据字段获取器.
|
||||
*
|
||||
* @param string|array $name 字段名
|
||||
* @param callable $callback 闭包获取器
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withAttr(string|array $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 (str_contains($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方式.
|
||||
*
|
||||
* @param array|string $with 关联方法名称
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function with(array|string $with)
|
||||
{
|
||||
if (empty($this->model) || empty($with)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->options['with'] = (array) $with;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联预载入 JOIN方式.
|
||||
*
|
||||
* @param array|string $with 关联方法名
|
||||
* @param string $joinType JOIN方式
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withJoin(array|string $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) && str_contains($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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
*
|
||||
* @param array|string $relations 关联方法名
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param bool $subQuery 是否使用子查询
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function withAggregate(string|array $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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联缓存.
|
||||
*
|
||||
* @param string|array|bool $relation 关联方法名
|
||||
* @param mixed $key 缓存key
|
||||
* @param int|\DateTime $expire 缓存有效期
|
||||
* @param string $tag 缓存标签
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withCache(string|array|bool $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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
*
|
||||
* @param string|array $relation 关联方法名
|
||||
* @param bool $subQuery 是否使用子查询
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withCount(string|array $relation, bool $subQuery = true)
|
||||
{
|
||||
return $this->withAggregate($relation, 'count', '*', $subQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计Sum.
|
||||
*
|
||||
* @param string|array $relation 关联方法名
|
||||
* @param string $field 字段
|
||||
* @param bool $subQuery 是否使用子查询
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withSum(string|array $relation, string $field, bool $subQuery = true)
|
||||
{
|
||||
return $this->withAggregate($relation, 'sum', $field, $subQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计Max.
|
||||
*
|
||||
* @param string|array $relation 关联方法名
|
||||
* @param string $field 字段
|
||||
* @param bool $subQuery 是否使用子查询
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withMax(string|array $relation, string $field, bool $subQuery = true)
|
||||
{
|
||||
return $this->withAggregate($relation, 'max', $field, $subQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计Min.
|
||||
*
|
||||
* @param string|array $relation 关联方法名
|
||||
* @param string $field 字段
|
||||
* @param bool $subQuery 是否使用子查询
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withMin(string|array $relation, string $field, bool $subQuery = true)
|
||||
{
|
||||
return $this->withAggregate($relation, 'min', $field, $subQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计Avg.
|
||||
*
|
||||
* @param string|array $relation 关联方法名
|
||||
* @param string $field 字段
|
||||
* @param bool $subQuery 是否使用子查询
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withAvg(string|array $relation, string $field, bool $subQuery = true)
|
||||
{
|
||||
return $this->withAggregate($relation, 'avg', $field, $subQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型.
|
||||
*
|
||||
* @param string $relation 关联方法名
|
||||
* @param mixed $operator 比较操作符
|
||||
* @param int $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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型.
|
||||
*
|
||||
* @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字段数据转换.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据转换为模型数据集对象
|
||||
*
|
||||
* @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,
|
||||
$this->options['with_cache'] ?? false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 模型数据集转换
|
||||
return $this->model->toCollection($resultSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据转换为模型对象
|
||||
*
|
||||
* @param array $result 查询数据
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function resultToModel(array &$result): void
|
||||
{
|
||||
// JSON数据处理
|
||||
if (!empty($this->options['json'])) {
|
||||
$this->jsonModelResult($result);
|
||||
}
|
||||
|
||||
// 实时读取延迟数据
|
||||
if (!empty($this->options['lazy_fields'])) {
|
||||
$id = $this->getKey($result);
|
||||
foreach ($this->options['lazy_fields'] as $field) {
|
||||
$result[$field] += $this->getLazyFieldValue($field, $id);
|
||||
}
|
||||
}
|
||||
|
||||
$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,
|
||||
$this->options['with_cache'] ?? false
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 关联统计查询
|
||||
if (!empty($this->options['with_aggregate'])) {
|
||||
foreach ($this->options['with_aggregate'] as $val) {
|
||||
$result->relationCount($this, $val[0], $val[1], $val[2], false);
|
||||
}
|
||||
}
|
||||
|
||||
// 动态获取器
|
||||
if (!empty($this->options['with_attr'])) {
|
||||
$result->withAttr($this->options['with_attr']);
|
||||
}
|
||||
|
||||
foreach (['hidden', 'visible', 'append'] as $name) {
|
||||
if (!empty($this->options[$name])) {
|
||||
$result->$name($this->options[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新原始数据
|
||||
$result->refreshOrigin();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2023 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\Connection;
|
||||
|
||||
/**
|
||||
* 参数绑定支持
|
||||
*/
|
||||
trait ParamsBind
|
||||
{
|
||||
/**
|
||||
* 当前参数绑定.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $bind = [];
|
||||
|
||||
/**
|
||||
* 批量参数绑定.
|
||||
*
|
||||
* @param array $value 绑定变量值
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function bind(array $value)
|
||||
{
|
||||
$this->bind = array_merge($this->bind, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个参数绑定.
|
||||
*
|
||||
* @param mixed $value 绑定变量值
|
||||
* @param int $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 ?: Connection::PARAM_STR];
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测参数是否已经绑定.
|
||||
*
|
||||
* @param string $key 参数名
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isBind(string $key)
|
||||
{
|
||||
return isset($this->bind[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置自动参数绑定.
|
||||
*
|
||||
* @param bool $bind 是否自动参数绑定
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function autoBind(bool $bind)
|
||||
{
|
||||
$this->options['auto_bind'] = $bind;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否开启自动参数绑定.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数绑定.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取绑定的参数 并清空.
|
||||
*
|
||||
* @param bool $clear 是否清空绑定数据
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBind(bool $clear = true): array
|
||||
{
|
||||
$bind = $this->bind;
|
||||
if ($clear) {
|
||||
$this->bind = [];
|
||||
}
|
||||
|
||||
return $bind;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2023 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
|
||||
{
|
||||
/**
|
||||
* 设置数据处理(支持模型).
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许返回空数据(或空模型).
|
||||
*
|
||||
* @param bool $allowEmpty 是否允许为空
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function allowEmpty(bool $allowEmpty = true)
|
||||
{
|
||||
$this->options['allow_empty'] = $allowEmpty;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置查询数据不存在是否抛出异常.
|
||||
*
|
||||
* @param bool $fail 数据不存在是否抛出异常
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function failException(bool $fail = true)
|
||||
{
|
||||
$this->options['fail'] = $fail;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理数据.
|
||||
*
|
||||
* @param array $result 查询数据
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function result(array &$result): void
|
||||
{
|
||||
// JSON数据处理
|
||||
if (!empty($this->options['json'])) {
|
||||
$this->jsonResult($result);
|
||||
}
|
||||
|
||||
// 实时读取延迟数据
|
||||
if (!empty($this->options['lazy_fields'])) {
|
||||
$id = $this->getKey($result);
|
||||
foreach ($this->options['lazy_fields'] as $field) {
|
||||
$result[$field] += $this->getLazyFieldValue($field, $id);
|
||||
}
|
||||
}
|
||||
|
||||
// 查询数据处理
|
||||
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']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理数据集.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用获取器处理数据.
|
||||
*
|
||||
* @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 (str_contains($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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理空数据.
|
||||
*
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws DataNotFoundException
|
||||
*
|
||||
* @return array|Model|null|static
|
||||
*/
|
||||
protected function resultToEmpty()
|
||||
{
|
||||
if (!empty($this->options['fail'])) {
|
||||
$this->throwNotFound();
|
||||
} elseif (!empty($this->options['allow_empty'])) {
|
||||
return !empty($this->model) ? $this->model->newInstance() : [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找单条记录 不存在返回空数据(或者空模型).
|
||||
*
|
||||
* @param mixed $data 数据
|
||||
*
|
||||
* @return array|Model|static|mixed
|
||||
*/
|
||||
public function findOrEmpty($data = null)
|
||||
{
|
||||
return $this->allowEmpty(true)->find($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON字段数据转换.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询失败 抛出异常.
|
||||
*
|
||||
* @throws ModelNotFoundException
|
||||
* @throws DataNotFoundException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找多条记录 如果不存在则抛出异常.
|
||||
*
|
||||
* @param array|string|Query|Closure $data 数据
|
||||
*
|
||||
* @throws ModelNotFoundException
|
||||
* @throws DataNotFoundException
|
||||
*
|
||||
* @return array|Collection|static[]
|
||||
*/
|
||||
public function selectOrFail($data = [])
|
||||
{
|
||||
return $this->failException(true)->select($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找单条记录 如果不存在则抛出异常.
|
||||
*
|
||||
* @param array|string|Query|Closure $data 数据
|
||||
*
|
||||
* @throws ModelNotFoundException
|
||||
* @throws DataNotFoundException
|
||||
*
|
||||
* @return array|Model|static|mixed
|
||||
*/
|
||||
public function findOrFail($data = null)
|
||||
{
|
||||
return $this->failException(true)->find($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2023 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
|
||||
{
|
||||
/**
|
||||
* 获取数据表字段信息.
|
||||
*
|
||||
* @param string $tableName 数据表名
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTableFields(string $tableName = ''): array
|
||||
{
|
||||
if ('' == $tableName) {
|
||||
$tableName = $this->getTable();
|
||||
}
|
||||
|
||||
return $this->connection->getTableFields($tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详细字段类型信息.
|
||||
*
|
||||
* @param string $tableName 数据表名称
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFields(string $tableName = ''): array
|
||||
{
|
||||
return $this->connection->getFields($tableName ?: $this->getTable());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段类型信息.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldsType(): array
|
||||
{
|
||||
if (!empty($this->options['field_type'])) {
|
||||
return $this->options['field_type'];
|
||||
}
|
||||
|
||||
return $this->connection->getFieldsType($this->getTable());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段类型信息.
|
||||
*
|
||||
* @param string $field 字段名
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFieldType(string $field)
|
||||
{
|
||||
$fieldType = $this->getFieldsType();
|
||||
|
||||
return $fieldType[$field] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段类型信息.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldsBindType(): array
|
||||
{
|
||||
$fieldType = $this->getFieldsType();
|
||||
|
||||
return array_map([$this->connection, 'getFieldBindType'], $fieldType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段类型信息.
|
||||
*
|
||||
* @param string $field 字段名
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getFieldBindType(string $field): int
|
||||
{
|
||||
$fieldType = $this->getFieldType($field);
|
||||
|
||||
return $this->connection->getFieldBindType($fieldType ?: '');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2023 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'],
|
||||
];
|
||||
|
||||
/**
|
||||
* 添加日期或者时间查询规则.
|
||||
*
|
||||
* @param array $rule 时间表达式
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function timeRule(array $rule)
|
||||
{
|
||||
$this->timeRule = array_merge($this->timeRule, $rule);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询日期或者时间.
|
||||
*
|
||||
* @param string $field 日期字段名
|
||||
* @param string $op 比较运算符或者表达式
|
||||
* @param mixed $range 比较范围
|
||||
* @param string $logic AND OR
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function whereTime(string $field, string $op, $range = null, string $logic = 'AND')
|
||||
{
|
||||
if (is_null($range)) {
|
||||
$range = $this->timeRule[$op] ?? $op;
|
||||
$op = is_array($range) ? 'between' : '>=';
|
||||
}
|
||||
|
||||
return $this->parseWhereExp($logic, $field, strtolower($op) . ' time', $range, [], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询某个时间间隔数据.
|
||||
*
|
||||
* @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').
|
||||
*
|
||||
* @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开始的一周数据.
|
||||
*
|
||||
* @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').
|
||||
*
|
||||
* @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').
|
||||
*
|
||||
* @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').
|
||||
*
|
||||
* @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').
|
||||
*
|
||||
* @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').
|
||||
*
|
||||
* @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').
|
||||
*
|
||||
* @param string $startField 开始时间字段
|
||||
* @param string $endField 结束时间字段
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function whereNotBetweenTimeField(string $startField, string $endField)
|
||||
{
|
||||
return $this->whereTime($startField, '>', time())
|
||||
->whereTime($endField, '<', time(), 'OR');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2023 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事务
|
||||
*
|
||||
* @param callable $callback 数据操作方法回调
|
||||
* @param array $dbs 多个查询对象或者连接对象
|
||||
*
|
||||
* @throws \PDOException
|
||||
* @throws \Exception
|
||||
* @throws \Throwable
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function transactionXa(callable $callback, array $dbs = [])
|
||||
{
|
||||
return $this->connection->transactionXa($callback, $dbs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行数据库事务
|
||||
*
|
||||
* @param callable $callback 数据操作方法回调
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function transaction(callable $callback)
|
||||
{
|
||||
return $this->connection->transaction($callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动事务
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startTrans(): void
|
||||
{
|
||||
$this->connection->startTrans();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于非自动提交状态下面的查询提交.
|
||||
*
|
||||
* @throws \PDOException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function commit(): void
|
||||
{
|
||||
$this->connection->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 事务回滚.
|
||||
*
|
||||
* @throws \PDOException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rollback(): void
|
||||
{
|
||||
$this->connection->rollback();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动XA事务
|
||||
*
|
||||
* @param string $xid XA事务id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startTransXa(string $xid): void
|
||||
{
|
||||
$this->connection->startTransXa($xid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预编译XA事务
|
||||
*
|
||||
* @param string $xid XA事务id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function prepareXa(string $xid): void
|
||||
{
|
||||
$this->connection->prepareXa($xid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交XA事务
|
||||
*
|
||||
* @param string $xid XA事务id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function commitXa(string $xid): void
|
||||
{
|
||||
$this->connection->commitXa($xid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回滚XA事务
|
||||
*
|
||||
* @param string $xid XA事务id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rollbackXa(string $xid): void
|
||||
{
|
||||
$this->connection->rollbackXa($xid);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,570 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2023 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查询条件.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
$pk = $this->getPk();
|
||||
if ((is_null($condition) || '=' == $op) && is_string($pk) && $pk == $field ) {
|
||||
$this->options['key'] = is_null($condition) ? $op : $condition;
|
||||
}
|
||||
|
||||
$param = func_get_args();
|
||||
array_shift($param);
|
||||
|
||||
return $this->parseWhereExp('AND', $field, $op, $condition, $param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析Query对象查询条件.
|
||||
*
|
||||
* @param BaseQuery $query 查询对象
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function parseQueryWhere(BaseQuery $query): void
|
||||
{
|
||||
$this->options['where'] = $query->getOptions('where') ?? [];
|
||||
$via = $query->getOptions('via');
|
||||
|
||||
if ($via) {
|
||||
foreach ($this->options['where'] as $logic => &$where) {
|
||||
foreach ($where as $key => &$val) {
|
||||
if (is_array($val) && !str_contains($val[0], '.')) {
|
||||
$val[0] = $via . '.' . $val[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->bind($query->getBind(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定OR查询条件.
|
||||
*
|
||||
* @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查询条件.
|
||||
*
|
||||
* @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查询条件.
|
||||
*
|
||||
* @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查询条件.
|
||||
*
|
||||
* @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查询条件.
|
||||
*
|
||||
* @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查询条件.
|
||||
*
|
||||
* @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查询条件.
|
||||
*
|
||||
* @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查询条件.
|
||||
*
|
||||
* @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查询条件.
|
||||
*
|
||||
* @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查询条件.
|
||||
*
|
||||
* @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查询条件.
|
||||
*
|
||||
* @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查询条件.
|
||||
*
|
||||
* @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查询条件.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个字段.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置软删除字段及条件.
|
||||
*
|
||||
* @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查询条件.
|
||||
*
|
||||
* @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查询.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定表达式查询条件.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @param string $where 查询条件
|
||||
* @param array $bind 参数绑定
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function whereOrRaw(string $where, array $bind = [])
|
||||
{
|
||||
return $this->whereRaw($where, $bind, 'OR');
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析查询表达式.
|
||||
*
|
||||
* @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']) && !str_contains($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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析查询表达式.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 相等查询的主键处理.
|
||||
*
|
||||
* @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];
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组批量查询.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除某个查询条件.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 条件查询.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user