PHP项目ThinkPHP查询构造器高级用法

wen PHP项目 3

本文目录导读:

PHP项目ThinkPHP查询构造器高级用法

  1. 链式操作的灵魂:常用高级方法
  2. 高阶数据操作(增删改)
  3. 性能与安全优化(重点)
  4. 高级场景实战案例
  5. 版本差异提醒(TP6 变化)

ThinkPHP 的查询构造器(Query Builder)功能非常强大,它提供了面向对象的方式构建 SQL 查询,避免手写 SQL 的繁琐且更安全(自动防注入),以下是 ThinkPHP 5.x/6.x 中查询构造器的高级用法

链式操作的灵魂:常用高级方法

1 条件分支查询(where 进阶)

  • 数组条件(批量)

    // 等效于 WHERE status=1 AND name='thinkphp' AND id>10
    $map = [
        'status' => 1,
        'name'   => 'thinkphp',
        'id'     => ['>', 10],
        // 支持模糊、区间等
        'create_time' => ['between', ['2023-01-01', '2023-12-31']],
        'title'       => ['like', '%php%'],
        'id'          => ['not in', [1, 2, 3]],
    ];
    Db::name('user')->where($map)->select();
  • whereRaw(原生复杂条件)

    // 处理复杂的OR逻辑组合
    Db::name('user')
        ->where('status', 1)
        ->whereRaw('(score > 80 OR level > 3)')
        ->select();
  • whereOr / whereX

    // 闭包构建分组查询
    Db::name('user')->where(function ($query) {
        $query->where('name', 'like', '%a%')->whereOr('nickname', 'like', '%a%');
    })->where('status', 1)->select();
    // 生成: WHERE (name LIKE '%a%' OR nickname LIKE '%a%') AND status = 1

2 字段与数据操作

  • 原生字段(fieldRaw

    // 支持聚合函数和别名
    Db::name('order')
        ->fieldRaw('SUM(total_amount) as total, COUNT(*) as num')
        ->select();
    // 字段排除
    Db::name('user')->withoutField('password, salt')->select();
  • value / column 获取单值/列

    // 返回第一条记录的某个字段值
    $name = Db::name('user')->where('id', 1)->value('name');
    // 返回某一列的值集合(可以指定索引字段)
    $names = Db::name('user')->column('name', 'id'); // 返回 [id => name]

3 排序与分组

  • orderRaw

    // 自定义排序,按指定id顺序排序 FIELD()
    Db::name('user')
        ->where('id', 'in', [5, 1, 3])
        ->orderRaw("FIELD(id, 5, 1, 3)")
        ->select();
  • group + having

    Db::name('order')
        ->field('user_id, COUNT(*) as order_count, SUM(money) as total_money')
        ->group('user_id')
        ->having('order_count > 5 AND total_money > 1000')
        ->select();

4 聚合查询(高级统计)

// 平均值
$avg = Db::name('score')->where('course_id', 1)->avg('score');
// 分组统计 count 支持指定字段去重
$count = Db::name('order')->distinct(true)->count('user_id');

高阶数据操作(增删改)

1 批量更新(saveAll / 循环)

$data = [
    ['id' => 1, 'name' => 'a'],
    ['id' => 2, 'name' => 'b'],
];
Db::name('user')->saveAll($data);

注意:saveAll 实际是逐条更新,并非MySQL原生批量更新。

2 字段自增/自减(inc / dec

// 浏览量 +1,且只在帖子状态正常时更新
Db::name('article')
    ->where('id', 10)
    ->where('status', 1)
    ->inc('views')   // 可以指定步长 ->inc('views', 5)
    ->update();
// 库存 -1
Db::name('product')->where('id', 1)->dec('stock', 1)->update();

3 表达式更新(updateRaw / exp

// 将 price 设置成 price * 1.1(涨价10%)
Db::name('product')
    ->where('id', 1)
    ->updateRaw('price = price * 1.1');
// 或者使用 exp 表达式(TP5风格)
Db::name('product')->where('id', 1)->update([
    'price' => Db::raw('price * 1.1')
]);

4 查找并更新/删除(findOrFail 等)

// 查不到则抛异常
$user = Db::name('user')->where('id', 999)->findOrFail();
// 查找并更新(不存在则不操作)
Db::name('user')->where('id', 1)->updateOrFail(['name' => 'x']);

性能与安全优化(重点)

1 使用 fetchSql 调试

// 打印SQL,不真正执行
$sql = Db::name('user')->where('id', 1)->fetchSql(true)->find();
dump($sql);

2 findInSet (处理逗号分隔字段)

// 假设表里有 tags 字段,值为 '1,2,3'
// 查找包含标签ID为 2 的记录(比 like 更精确,避免 12 被匹配)
Db::name('article')->whereFindInSet('tags', 2)->select();
// 或者
Db::name('article')->where('tags', 'find in set', 2)->select();

3 JSON 字段查询(MySQL 5.7+)

// 假设 info 字段是JSON类型 {"age": 25, "name": "tp"}
Db::name('user')
    ->where('info->age', '>', 20)
    ->select();
// JSON 字段更新
Db::name('user')->where('id', 1)->update([
    'info->age' => 30
]);

4 子查询(闭包子查询)

// 查出一批有订单的用户ID,再查这些用户的信息
$subQuery = Db::name('order')->field('user_id')->where('status', 1)->group('user_id');
$users = Db::name('user')
    ->where('id', 'in', $subQuery)
    ->select();
// 注意:TP会将闭包对象自动解析为子查询字符串

高级场景实战案例

连表查询 + 条件复杂 + 分页

list($page, $limit) = [1, 10];
$list = Db::name('order')
    ->alias('o') // 主表别名
    ->join('user u', 'u.id = o.user_id') // 关联用户
    ->join('product p', 'p.id = o.product_id', 'LEFT') // 左连接商品
    ->where('o.status', 1)
    ->where(function ($query) {
        // 关键字搜索,多字段模糊
        $keyword = input('keyword');
        if ($keyword) {
            $query->where('u.name', 'like', "%$keyword%")
                  ->whereOr('p.title', 'like', "%$keyword%");
        }
    })
    ->field('o.*, u.name as uname, p.title as ptitle')
    ->order('o.create_time DESC')
    ->page($page, $limit)
    ->select();

事务+锁

Db::transaction(function () {
    // 悲观锁:锁定该行,防止并发超卖
    $product = Db::name('product')
                ->where('id', 1)
                ->lock(true) // 加 FOR UPDATE
                ->find();
    if ($product['stock'] <= 0) {
        throw new \Exception('库存不足');
    }
    Db::name('product')->where('id', 1)->dec('stock')->update();
    Db::name('log')->insert(['msg' => '扣减库存']);
});

版本差异提醒(TP6 变化)

  • 命名空间:TP6 使用 Db::name() 静态调用需引入 use think\facade\Db;
  • whereOr 闭包:在 TP6 中,where 闭包内需要用 $query->whereOr(...),写法基本一致。
  • 软删除:TP6 引入了 SoftDelete 特性,但查询构造器本身不包含该功能,需结合模型使用。

总结建议: 尽量使用数组条件闭包来构建复杂查询,避免字符串拼接导致的SQL注入,在大量数据更新时,优先使用 inc/decwhereRaw 做原子操作,能有效避免并发问题。

抱歉,评论功能暂时关闭!