ThinkORM 3.0 入门:强类型模型定义与查询构造器

1. 引言:ThinkORM 3.0 为什么值得重新认识

ThinkORM 是 ThinkPHP 生态中的独立 ORM 组件,可以脱离框架单独使用。到了 3.0 版本,它对 Model 层做了一次彻底重构:不再依赖魔术方法堆砌属性,而是把模型定义建立在 PHP 强类型与 PDO 之上,让 IDE 补全、静态分析和运行时校验都能真正生效。

本文从零开始,带你走一遍 ThinkORM 3.0 的核心用法:强类型模型定义、查询构造器链式操作、数据集对象、参数绑定与断点重连。

下图展示了 ThinkORM 3.0 的整体分层结构,以及本文将要依次介绍的核心模块:

flowchart TD A[应用代码] --> B[Db门面类与Model模型] B --> C[查询构造器链式API] C --> D[参数绑定与PDO预处理] D --> E[数据库连接断点重连] E --> F[MySQL等数据库] C --> G[thinkCollection数据集对象] G --> A

2. 安装与基础配置

通过 Composer 安装:

bash 复制代码
composer require topthink/think-orm ^3.0

ThinkORM 3.0 要求 PHP 8.0 及以上版本,并依赖 PDO 扩展。初始化数据库连接:

php 复制代码
use think\facade\Db;

Db::setConfig([
    'default'     => 'mysql',
    'connections' => [
        'mysql' => [
            'type'            => 'mysql',
            'hostname'        => '127.0.0.1',
            'database'        => 'demo',
            'username'        => 'root',
            'password'        => '',
            'hostport'        => '3306',
            'charset'         => 'utf8mb4',
            // 断点重连,见第 7 节
            'break_reconnect' => true,
        ],
    ],
]);

3. 强类型模型定义

3.1 属性声明移入构造函数

3.0 最大的变化之一,是模型字段不再散落在类属性上,而是统一在构造函数中声明,并启用 PHP 的属性提升(Constructor Property Promotion)语法。这样字段的类型、默认值、可空性都集中在一处,一目了然。

php 复制代码
<?php
namespace app\model;

use think\Model;

class User extends Model
{
    protected $name = 'user';

    public function __construct(
        protected int $id = 0,
        protected string $name = '',
        protected int $age = 0,
        protected string $email = '',
    ) {
        parent::__construct();
    }
}

3.2 强类型带来的好处

  • IDE 友好 :$user->age 能被识别为 int,补全与跳转不再靠猜。
  • 静态分析可用:PHPStan、Psalm 能对模型字段做类型检查。
  • 运行时校验:写入时类型不匹配会尽早暴露,而不是等到数据库报错。

4. 查询构造器链式操作

4.1 Db 门面类

ThinkORM 3.0 通过 Db 门面类提供查询构造器,链式调用风格保持简洁:

php 复制代码
use think\facade\Db;

$list = Db::name('user')
    ->where('age', 14)
    ->field('id,name,age')
    ->order('id', 'desc')
    ->limit(10)
    ->select();

常用链式方法一览:

方法 说明
where() 条件筛选,支持数组、闭包、表达式
field() 指定查询字段
order() 排序
limit() 限制条数
page() 分页
join() 关联查询
group() 分组

链式调用的执行顺序可以理解为「逐步构建 SQL → 一次性执行 → 返回数据集」,流程如下:

flowchart LR A[Dbnameuser] --> B[whereage14] B --> C[fieldidnameage] C --> D[orderiddesc] D --> E[limit10] E --> F[select] F --> G[生成SQL并参数绑定] G --> H[PDO预处理执行] H --> I[返回thinkCollection]

4.2 复杂条件示例

php 复制代码
$list = Db::name('user')
    ->where('age', '>=', 18)
    ->where(function ($query) {
        $query->where('status', 1)
              ->whereOr('vip', 1);
    })
    ->select();

5. 数据集对象 think\Collection

select() 返回的不是裸数组,而是 think\Collection 数据集对象。它实现了 ArrayAccess、Countable、IteratorAggregate 等接口,因此可以像数组一样使用:

php 复制代码
$list = Db::name('user')->select();

// 数组式访问
echo $list[0]['name'];
echo count($list);

// foreach 遍历
foreach ($list as $item) {
    echo $item['name'];
}

同时它封装了大量便捷方法,避免手写循环:

php 复制代码
// 取出某一列
$names = $list->column('name');

// 过滤
$adults = $list->where('age', '>=', 18);

// 映射
$emails = $list->column('email');

// 转数组
$array = $list->toArray();

这种「数组无差别用法 + 额外封装方法」的设计,让迁移成本极低,又能享受集合操作的便利。

6. 自动参数绑定与预查询

ThinkORM 3.0 的查询构造器默认使用 PDO 预处理语句,所有条件值都会自动参数绑定,而不是拼接进 SQL 字符串:

php 复制代码
// 内部生成:SELECT * FROM `user` WHERE `name` = ?
$user = Db::name('user')->where('name', $name)->find();

这意味着即使用户输入包含 ' OR 1=1 -- 这类内容,也只会被当作普通字符串参数处理,从机制上杜绝 SQL 注入。开发者无需手动转义,也不应再使用字符串拼接构造条件。

参数绑定与预查询的防注入机制,可以用下面的流程来理解:

flowchart TD A[用户输入name] --> B[wherename参数] B --> C{是否拼接进SQL字符串} C -->|否| D[生成占位符SQL] D --> E[PDO预处理prepare] E --> F[绑定参数execute] F --> G[输入被当作纯字符串处理] G --> H[SQL注入失效] C -->|是| I[存在注入风险]

7. 断点重连:应对数据库抖动

线上环境偶尔会遇到数据库连接被中间件或超时机制断开的情况。ThinkORM 3.0 提供 break_reconnect 配置,开启后当检测到连接失效时会自动重连并重试当前操作:

php 复制代码
'connections' => [
    'mysql' => [
        // ...
        'break_reconnect' => true,
    ],
],

建议在生产环境开启该选项,避免因短暂的数据库抖动导致请求直接失败。需要注意的是,重连只对幂等或可安全重试的查询友好,涉及事务的场景仍需自行控制边界。

8. 小结

ThinkORM 3.0 的核心变化可以概括为三点:

  1. 模型层强类型化:属性声明移入构造函数,配合属性提升语法,类型清晰、工具友好。
  2. 查询构造器更安全:链式 API 保持简洁,自动参数绑定从机制上防注入。
  3. 运行更稳健 :think\Collection 提供数组无差别用法,break_reconnect 应对连接抖动。

掌握这些基础后,可以进一步探索关联模型、事务与事件等进阶能力。

相关推荐
vipxieliang10 分钟前
虚拟线程与 GraalVM:Spring Boot 3.x 云原生性能跃迁
java·spring boot·后端
Rain的Java大神之路11 分钟前
面试官最烦的答案:“我用 Redis + Token”!接口幂等 8 种方案 + 四层防御,把重复扣款工单干到 0
java·后端·面试
vipxieliang24 分钟前
PHP 密码哈希与 password_hash 最佳实践
后端·php
量化分析码农40 分钟前
【Python量化波动率实战 03】波动大就少拿点:波动率目标仓位的回测实证
后端
驭渊的小故事1 小时前
Spring AOP 入门:从 AOP 概念到第一个耗时统计切面
java·后端·spring
系统集成-鹰翔1 小时前
ERP单据已作废为什么商品仍不能删除,引用检查与软删除怎么查
javascript·经验分享·后端
Moment1 小时前
Harness Engineering 为什么是 AI Coding 的工程底座 ❓❓❓
前端·后端·面试
mldong2 小时前
主办先挂起,收齐意见再拍板:jeeflow 的动态会签
后端·架构