通用查询系统使用手册(ThinkPHP / PHP 7.4)

适用于:BaseModel::getList() + 控制器层 searchPage()
目标:单表 / 多表联查 / 自动分页 / 分组统计 / 统一输出 一套搞定
版本特性:兼容 PHP 7.4(无命名参数),自动从 request() 读取分页参数


目录


一、核心理念与总体结构

框架提供两层通用查询接口:

层级方法作用
模型层getList()通用数据查询(字段、联表、分组、关联、分页)
控制器层searchPage()自动构造查询条件并分页输出

理念:

让所有查询接口实现“一行搞定”,
参数驱动、结果统一、0 重复 SQL。


二、BaseModel::getList

方法签名

public function getList(array $where = [], array $expand = []): array

expand 参数表

字段说明
page / limit分页参数(自动从 request 获取)
field查询字段,默认 *
order排序,如 id desc
group分组字段
with模型关联加载
hidden隐藏字段
join联表配置
alias主表别名(默认 t)

where 条件写法

[
  ['status', '=', 1],
  ['nickname', 'like', '%王%'],
  ['create_time', 'between', [1700000000,1710000000]],
]

支持操作符:

=, like, between, in, notnull, null, noIn

join 写法

推荐写法(结构化)

'join' => [
  ['tp_user u', 'u.id = t.uid', 'LEFT'],
  ['tp_order o', 'o.user_id = u.id', 'INNER'],
]

⚠️ 不推荐写法(字符串)

'join' => ['tp_user u on u.id = t.uid']

使用示例

1️⃣ 单表查询

$this->getList([['status', '=', 1]], ['order' => 'id desc']);

2️⃣ 多表联查

$this->getList(
  [['u.nickname', 'like', '%张%']],
  [
    'alias' => 'm',
    'field' => 'm.*,u.nickname,u.phone',
    'join'  => [['tp_user u', 'u.id = m.uid', 'LEFT']],
    'order' => 'm.id desc',
    'page'  => 1,
    'limit' => 10
  ]
);

3️⃣ 聚合统计

$this->getList(
  [],
  [
    'alias' => 'm',
    'field' => 'u.nickname, SUM(m.amount) AS total_amount',
    'join'  => [['tp_user u', 'u.id = m.uid', 'LEFT']],
    'group' => 'm.uid',
    'order' => 'total_amount desc',
    'limit' => 20
  ]
);

三、searchPage(控制器层)

方法签名

protected function searchPage(string $modelClass, array $directive = []): array

directive 参数表

字段说明
spec快捷搜索映射(自动取 request 参数)
where手动附加条件
join联表配置
alias主表别名
field查询字段
with模型关联
hider隐藏字段
order排序
group分组字段

spec 快捷搜索说明

例:

'spec' => ['nickname' => 'like', 'phone' => '=', 'status' => '=']

当请求:

?page=1&limit=10&nickname=王&status=1

生成:

[
  ['nickname','like','%王%'],
  ['status','=','1']
]

使用示例

1️⃣ 单表搜索

return $this->searchPage(UserModel::class, [
  'spec'  => ['nickname' => 'like', 'phone' => '='],
  'where' => [['status', '=', 1]],
  'order' => 'id desc'
]);

2️⃣ 联表搜索

return $this->searchPage(UserMoneyRecordModel::class, [
  'alias' => 'm',
  'join'  => [['tp_user u', 'u.id = m.uid', 'LEFT']],
  'field' => 'm.*,u.nickname,u.phone',
  'spec'  => ['order_sn' => 'like', 'u.nickname' => 'like'],
  'order' => 'm.id desc'
]);

3️⃣ 分组统计

return $this->searchPage(UserMoneyRecordModel::class, [
  'alias' => 'm',
  'join'  => [['tp_user u', 'u.id = m.uid', 'LEFT']],
  'field' => 'u.nickname, SUM(m.amount) as total_amount',
  'group' => 'm.uid',
  'order' => 'total_amount desc'
]);

四、统一返回格式

{
  "total": 120,
  "list": [ ... ]
}

五、最佳实践与常见坑

✅ 0 值条件

if ($value !== '' && $value !== null)

保留 0,排除空字符串/null。

✅ -1 表示“全部”

-1 时忽略该字段条件。

✅ 分页逻辑

page>0 && limit>0 → 分页
否则 → 全量。

✅ group + paginate

group 查询自动切换 simple 模式,避免 count 报错。

✅ join 别名

确保 ON 条件别名与 alias 一致(例:u.id=m.uid)。


六、联表模板(复制即用)

'join' => [
  ['tp_user u', 'u.id = t.uid', 'LEFT'],
  ['tp_order o', 'o.user_id = u.id', 'LEFT'],
]

七、控制器模板(复制即用)

public function index()
{
  $data = $this->searchPage(\app\model\UserMoneyRecordModel::class, [
    'alias' => 'm',
    'join'  => [['tp_user u', 'u.id = m.uid', 'LEFT']],
    'field' => 'm.*,u.nickname,u.phone',
    'spec'  => ['order_sn' => 'like', 'u.nickname' => 'like', 'status' => '='],
    'order' => 'm.id desc'
  ]);

  return $this->success($data);
}

八、推荐 buildWhereConditions 逻辑

foreach ($conditions as $condition) {
  if ($value === '' || $value === null) continue;
  switch ($operator) {
    case 'like':
      $query->where($field, 'like', $value);
      break;
    case 'between':
      $range = is_array($value) ? $value : explode(',', $value);
      if (count($range) === 2)
        $query->whereBetween($field, $range);
      break;
    case 'in':
      is_array($value) && count($value)
        ? $query->whereIn($field, $value)
        : $query->whereRaw('1=0');
      break;
    default:
      $query->where($field, $operator, $value);
  }
}

九、术语速查

名称说明
spec快捷搜索配置
where自定义条件
join联表配置 [表名 别名, on 条件, 类型]
alias主表别名
with关联模型
hidden隐藏字段
group分组字段
自动分页page、limit 同时存在即分页

一句话总结:

searchPage() 说“查什么”,
getList() 控制“怎么查”。

统一结果,低重复,结构优雅,就是这套框架的灵魂。

BaseModel的封装

 /**
     * 查询一条数据如果不存在则抛出异常
     * @param $where //条件数组或主键id
     * @param $msg //抛出的异常信息
     * @param $lock //是否行锁
     * @return array|mixed|Model
     * @throws ApiException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public static function FindTips($where, $msg, $lock = false)
    {
        $Model = self::getModel();
        if (is_array($where)) {
            $Model = $Model->where($where);
        } else {
            $Model = $Model->where($Model->getPk(), $where);
        }
        $Info = $Model->lock($lock)->find();
        if (!$Info) throw new ApiException($msg);
        return $Info;
    }

    /**
     * 通用查询列表(Clean版)
     * 支持:单表 / 联表 / 自动分页 / 分组 / 统一输出
     *
     * @param array $where 查询条件
     * @param array $expand 附加配置:
     * [
     *     'page'   => 1,                         // 页码(可选,自动从 request 获取)
     *     'limit'  => 20,                        // 每页条数(可选,自动从 request 获取)
     *     'field'  => 't.*,u.nickname',          // 查询字段
     *     'with'   => [],                        // 关联模型
     *     'hidden' => [],                        // 隐藏字段
     *     'order'  => 't.id desc',               // 排序
     *     'group'  => '',                        // 分组字段
     *     'join'   => [['tp_user u','u.id=t.uid','LEFT']], // 链表
     *     'alias'  => 't',                       // 主表别名
     * ]
     * @return array ['total'=>x,'list'=>[]]
     */
    public function getList(array $where = [], array $expand = []): array
    {
        // ====== 自动获取分页参数 ======
        $page = isset($expand['page']) ? (int)$expand['page'] : (int)request()->get('page', 0);
        $limit = isset($expand['limit']) ? (int)$expand['limit'] : (int)request()->get('limit', 0);

        // ====== 提取其他参数 ======
        $field = $expand['field'] ?? '*';
        $with = $expand['with'] ?? [];
        $hidden = $expand['hidden'] ?? [];
        $order = $expand['order'] ?? '';
        $group = $expand['group'] ?? '';
        $join = $expand['join'] ?? [];
        $alias = $expand['alias'] ?? null;

        // ====== 初始化模型 ======
        $model = $this->getModel();
        $pk = $model->getPk();
        if (empty($order)) $order = "{$pk} desc";

        $query = $model;

        // ====== 设置别名 ======
        if ($alias || $join) {
            $query = $query->alias($alias ?: 't');
        }

        // ====== 处理联表 ======
        foreach ($join as $j) {
            if (is_array($j) && count($j) >= 2) {
                [$table, $on, $type] = array_pad(array_values($j), 3, 'LEFT');
                $query->join($table, $on, strtoupper($type));
            } elseif (is_string($j)) {
                $query->join($j);
            }
        }

        // ====== where 条件 ======
        $query = $this->buildWhereConditions($where);

        // ====== 字段 / 排序 / 分组 / 关联 ======
        $query->field($field)
            ->with($with)
            ->hidden($hidden)
            ->orderRaw($order);

        if (!empty($group)) {
            $query->group($group);
        }

        // ====== 是否分页(自然逻辑) ======
        $isGroup = !empty($group);
        $paginate = ($page > 0 && $limit > 0);

        // ====== 执行查询 ======
        if ($paginate) {
            $res = $query->paginate([
                'list_rows' => max(1, $limit),
                'page' => max(1, $page),
                'simple' => $isGroup, // group 模式禁用 count
            ])->toArray();

            return [
                'total' => $res['total'] ?? 0,
                'list' => $res['data'] ?? [],
            ];
        } else {
            $list = $query->select()->toArray();
            return [
                'total' => count($list),
                'list' => $list,
            ];
        }
    }
    
 /**
     * 查询条件重构
     */
    public function buildWhereConditions($conditions)
    {
        return $this->getModel()->where(function ($query) use ($conditions) {
            if (count($conditions) <= 0) return $query;
            foreach ($conditions as $condition) {
                if (count($condition) == 3) {
                    [$field, $operator, $value] = $condition;
                } else {
                    $operator = '=';
                    [$field, $value] = $condition;
                }
                if ($value !== '' && $value !== null) {
                    switch ($operator) {
                        case 'like':
                            $query->where($field, 'like', $value);
                            break;
                        case 'between':
                            $query->whereBetween($field, $value);
                            break;
                        case 'in':
                            $query->whereIn($field, $value);
                            break;
                        case '=':
                            $query->where($field, '=', $value);
                            break;
                        case 'notnull':
                            $query->whereNotNull($field);
                            break;
                        case 'null':
                            $query->whereNull($field);
                            break;
                        case 'noIn':
                            $query->wherenotIn($field, $value);
                            break;
                        // 不存在上面特殊声明的查询类型
                        // 默认使用传递的参数
                        default:
                            $query->where($field, $operator, $value);
                            break;
                    }
                }
            }
        });
    }

ApiController的封装适用于后端BaseAdminConteroller

/**
 * 通用分页搜索(加强版)
 * 支持 spec 快捷搜索、自定义 where、关联模型、join、多表、分组、排序等
 *
 * @param string $modelClass 模型类名
 * @param array  $directive  查询指令参数
 * [
 *     'spec'   => ['nickname' => 'like', 'phone' => '='], // 快捷搜索字段(自动从 request 取值)
 *     'where'  => [['status', '=', 1]],                   // 自定义条件
 *     'join'   => [['tp_user u', 'u.id = m.uid', 'LEFT']], // 链表查询
 *     'alias'  => 'm',                                   // 主表别名
 *     'field'  => 'm.*,u.nickname,u.phone',              // 字段筛选
 *     'with'   => ['relation'],                          // 模型关联
 *     'hider'  => ['password'],                          // 隐藏字段
 *     'order'  => 'm.id desc',                           // 排序
 *     'group'  => 'm.uid',                               // 分组
 * ]
 * @return array ['total'=>x,'list'=>[]]
 */
protected function searchPage(string $modelClass, array $directive = []): array
{
    // ==== 初始化模型 ====
    $model = new $modelClass;

    // ==== 拆解指令参数 ====
    $spec   = $directive['spec']   ?? [];
    $where  = $directive['where']  ?? [];
    $join   = $directive['join']   ?? [];
    $alias  = $directive['alias']  ?? null;
    $field  = $directive['field']  ?? '*';
    $with   = $directive['with']   ?? [];
    $hidden = $directive['hider']  ?? [];
    $order  = $directive['order']  ?? '';
    $group  = $directive['group']  ?? '';

    // ==== 自动构造 where 条件 ====
    $params = request()->param();
    foreach ($spec as $key => $op) {
        if (isset($params[$key]) && $params[$key] !== '') {
            switch (strtolower($op)) {
                case 'like':
                    $where[] = [$key, 'like', "%{$params[$key]}%"];
                    break;
                case 'between':
                    $range = is_array($params[$key])
                        ? $params[$key]
                        : explode(',', $params[$key]);
                    if (count($range) === 2) {
                        $where[] = [$key, 'between', $range];
                    }
                    break;
                default:
                    $where[] = [$key, $op, $params[$key]];
                    break;
            }
        }
    }

    // ==== 统一构造 expand 参数 ====
    $expand = [
        'field'  => $field,
        'with'   => $with,
        'hidden' => $hidden,
        'order'  => $order,
        'group'  => $group,
        'join'   => $join,
        'alias'  => $alias,
    ];

    // ==== 调用核心 getList ====
    return $model->getList($where, $expand);
}
Last modification:October 23, 2025
反正也没人会打赏