<?php
declare(strict_types=1);
* +----------------------------------------------------------------------
* | ThinkAdmin Plugin for ThinkAdmin
* +----------------------------------------------------------------------
* | 版权所有 2014~2026 ThinkAdmin [ thinkadmin.top ]
* +----------------------------------------------------------------------
* | 官方网站: https://thinkadmin.top
* +----------------------------------------------------------------------
* | 开源协议 ( https://mit-license.org )
* | 免责声明 ( https://thinkadmin.top/disclaimer )
* | 会员特权 ( https://thinkadmin.top/vip-introduce )
* +----------------------------------------------------------------------
* | gitee 代码仓库:https://gitee.com/zoujingli/ThinkAdmin
* | github 代码仓库:https://github.com/zoujingli/ThinkAdmin
* +----------------------------------------------------------------------
*/
namespace think\admin\helper;
use think\admin\Helper;
use think\admin\service\SystemService;
use think\Container;
use think\db\BaseQuery;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\db\Query;
use think\Model;
* 搜索条件处理器.
* @see Query
* @mixin \think\db\Query
* @class QueryHelper
* @method bool mSave(array $data = [], string $field = '', mixed $where = []) 快捷更新
* @method bool mDelete(string $field = '', mixed $where = []) 快捷删除
* @method array|bool mForm(string $tpl = '', string $field = '', mixed $where = [], array $data = []) 快捷表单
* @method bool|int mUpdate(array $data = [], string $field = '', mixed $where = []) 快捷保存
*/
class QueryHelper extends Helper
{
* 分页助手工具.
* @var PageHelper
*/
protected $page;
* 当前数据操作.
* @var Query
*/
protected $query;
* 初始化默认数据.
* @var array
*/
protected $input;
* 克隆属性复制.
*/
public function __clone()
{
$this->page = clone $this->page;
$this->query = clone $this->query;
}
* QueryHelper call.
* @param string $name 调用方法名称
* @param array $args 调用参数内容
* @return $this|mixed
*/
public function __call(string $name, array $args)
{
return static::make($this->query, $name, $args, function ($name, $args) {
if (is_callable($callable = [$this->query, $name])) {
$value = call_user_func_array($callable, $args);
if ($name[0] === '_' || $value instanceof $this->query) {
return $this;
}
return $value;
}
return $this;
});
}
* 获取当前Db操作对象
*/
public function db(): Query
{
return $this->query;
}
* 逻辑器初始化.
* @param BaseQuery|Model|string $dbQuery
* @param null|array|string $input 输入数据
* @param null|callable $callable 初始回调
* @return $this
*/
public function init($dbQuery, $input = null, ?callable $callable = null): QueryHelper
{
$this->page = PageHelper::instance();
$this->input = $this->getInputData($input);
$this->query = $this->page->autoSortQuery($dbQuery);
is_callable($callable) && call_user_func($callable, $this, $this->query);
return $this;
}
* 设置 Like 查询条件.
* @param array|string $fields 查询字段
* @param string $split 前后分割符
* @param null|array|string $input 输入数据
* @param string $alias 别名分割符
* @return $this
*/
public function like($fields, string $split = '', $input = null, string $alias = '#'): QueryHelper
{
$data = $this->getInputData($input ?: $this->input);
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
[$dk, $qk] = [$field, $field];
if (stripos($field, $alias) !== false) {
[$dk, $qk] = explode($alias, $field);
}
if (isset($data[$qk]) && $data[$qk] !== '') {
$this->query->whereLike($dk, "%{$split}{$data[$qk]}{$split}%");
}
}
return $this;
}
* 设置 Equal 查询条件.
* @param array|string $fields 查询字段
* @param null|array|string $input 输入类型
* @param string $alias 别名分割符
* @return $this
*/
public function equal($fields, $input = null, string $alias = '#'): QueryHelper
{
$data = $this->getInputData($input ?: $this->input);
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
[$dk, $qk] = [$field, $field];
if (stripos($field, $alias) !== false) {
[$dk, $qk] = explode($alias, $field);
}
if (isset($data[$qk]) && $data[$qk] !== '') {
$this->query->where($dk, strval($data[$qk]));
}
}
return $this;
}
* 设置 IN 区间查询.
* @param array|string $fields 查询字段
* @param string $split 输入分隔符
* @param null|array|string $input 输入数据
* @param string $alias 别名分割符
* @return $this
*/
public function in($fields, string $split = ',', $input = null, string $alias = '#'): QueryHelper
{
$data = $this->getInputData($input ?: $this->input);
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
[$dk, $qk] = [$field, $field];
if (stripos($field, $alias) !== false) {
[$dk, $qk] = explode($alias, $field);
}
if (isset($data[$qk]) && $data[$qk] !== '') {
$this->query->whereIn($dk, explode($split, strval($data[$qk])));
}
}
return $this;
}
* 两字段范围查询.
* @example field1:field2#field,field11:field22#field00
* @param array|string $fields 查询字段
* @param null|array|string $input 输入数据
* @param string $alias 别名分割符
* @return $this
*/
public function valueRange($fields, $input = null, string $alias = '#'): QueryHelper
{
$data = $this->getInputData($input ?: $this->input);
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
if (strpos($field, ':') !== false) {
if (stripos($field, $alias) !== false) {
[$dk0, $qk0] = explode($alias, $field);
[$dk1, $dk2] = explode(':', $dk0);
} else {
[$qk0] = [$dk1, $dk2] = explode(':', $field, 2);
}
if (isset($data[$qk0]) && $data[$qk0] !== '') {
$this->query->where([[$dk1, '<=', $data[$qk0]], [$dk2, '>=', $data[$qk0]]]);
}
}
}
return $this;
}
* 设置内容区间查询.
* @param array|string $fields 查询字段
* @param string $split 输入分隔符
* @param null|array|string $input 输入数据
* @param string $alias 别名分割符
* @return $this
*/
public function valueBetween($fields, string $split = ' ', $input = null, string $alias = '#'): QueryHelper
{
return $this->setBetweenWhere($fields, $split, $input, $alias);
}
* 设置日期时间区间查询.
* @param array|string $fields 查询字段
* @param string $split 输入分隔符
* @param null|array|string $input 输入数据
* @param string $alias 别名分割符
* @return $this
*/
public function dateBetween($fields, string $split = ' - ', $input = null, string $alias = '#'): QueryHelper
{
return $this->setBetweenWhere($fields, $split, $input, $alias, static function ($value, $type) {
if (preg_match('#^\d{4}(-\d\d){2}\s+\d\d(:\d\d){2}$#', $value)) {
return $value;
}
return $type === 'after' ? "{$value} 23:59:59" : "{$value} 00:00:00";
});
}
* 设置时间戳区间查询.
* @param array|string $fields 查询字段
* @param string $split 输入分隔符
* @param null|array|string $input 输入数据
* @param string $alias 别名分割符
* @return $this
*/
public function timeBetween($fields, string $split = ' - ', $input = null, string $alias = '#'): QueryHelper
{
return $this->setBetweenWhere($fields, $split, $input, $alias, static function ($value, $type) {
if (preg_match('#^\d{4}(-\d\d){2}\s+\d\d(:\d\d){2}$#', $value)) {
return strtotime($value);
}
return $type === 'after' ? strtotime("{$value} 23:59:59") : strtotime("{$value} 00:00:00");
});
}
* 实例化分页管理器.
* @param bool|int $page 是否启用分页
* @param bool $display 是否渲染模板
* @param bool|int $total 集合分页记录数
* @param int $limit 集合每页记录数
* @param string $template 模板文件名称
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function page($page = true, bool $display = true, $total = false, int $limit = 0, string $template = ''): array
{
return $this->page->init($this->query, $page, $display, $total, $limit, $template);
}
* 清空数据并保留表结构.
* @return $this
*/
public function empty(): QueryHelper
{
$table = $this->query->getTable();
$ctype = strtolower($this->query->getConfig('type'));
if ($ctype === 'mysql') {
$this->query->getConnection()->execute("truncate table `{$table}`");
} elseif (in_array($ctype, ['sqlsrv', 'oracle', 'pgsql'])) {
$this->query->getConnection()->execute("truncate table {$table}");
} else {
try {
$this->query->newQuery()->whereRaw('1=1')->delete();
} catch (\Throwable $exception) {
trace_file($exception);
}
}
return $this;
}
* 中间回调处理.
* @return $this
*/
public function filter(callable $after): QueryHelper
{
call_user_func($after, $this, $this->query);
return $this;
}
* Layui.Table 组件数据.
* @param ?callable $befor 表单前置操作
* @param ?callable $after 表单后置操作
* @param string $template 视图模板文件
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function layTable(?callable $befor = null, ?callable $after = null, string $template = '')
{
if (in_array($this->output, ['get.json', 'get.layui.table'])) {
if (is_callable($after)) {
call_user_func($after, $this, $this->query);
}
$this->page->layTable($this->query, $template);
} else {
if (is_callable($befor)) {
call_user_func($befor, $this, $this->query);
}
$this->class->fetch($template);
}
}
/**
* 快捷助手调用勾子.
* @param Model|Query|string $model
* @return false|int|mixed|QueryHelper
*/
public static function make($model, string $method = 'init', array $args = [], ?callable $nohook = null)
{
$hooks = [
'mForm' => [FormHelper::class, 'init'],
'mSave' => [SaveHelper::class, 'init'],
'mQuery' => [QueryHelper::class, 'init'],
'mDelete' => [DeleteHelper::class, 'init'],
'mUpdate' => [SystemService::class, 'update'],
];
if (isset($hooks[$method])) {
[$class, $method] = $hooks[$method];
return Container::getInstance()->invokeClass($class)->{$method}($model, ...$args);
}
return is_callable($nohook) ? $nohook($method, $args) : false;
}
* 设置区域查询条件.
* @param array|string $fields 查询字段
* @param string $split 输入分隔符
* @param null|array|string $input 输入数据
* @param string $alias 别名分割符
* @param null|callable $callback 回调函数
* @return $this
*/
private function setBetweenWhere($fields, string $split = ' ', $input = null, string $alias = '#', ?callable $callback = null): QueryHelper
{
$data = $this->getInputData($input ?: $this->input);
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
[$dk, $qk] = [$field, $field];
if (stripos($field, $alias) !== false) {
[$dk, $qk] = explode($alias, $field);
}
if (isset($data[$qk]) && $data[$qk] !== '') {
[$begin, $after] = explode($split, strval($data[$qk]));
if (is_callable($callback)) {
$after = call_user_func($callback, $after, 'after');
$begin = call_user_func($callback, $begin, 'begin');
}
$this->query->whereBetween($dk, [$begin, $after]);
}
}
return $this;
}
* 获取输入数据.
* @param null|array|string $input
*/
private function getInputData($input): array
{
if (is_array($input)) {
return $input;
}
$input = $input ?: 'request';
return $this->app->request->{$input}();
}
}