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

0 阅读4分钟

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 安装:

composer require topthink/think-orm ^3.0

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

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
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 门面类提供查询构造器,链式调用风格保持简洁:

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 复杂条件示例

$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 等接口,因此可以像数组一样使用:

$list = Db::name('user')->select();

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

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

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

// 取出某一列
$names = $list->column('name');

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

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

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

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

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

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

// 内部生成: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 配置,开启后当检测到连接失效时会自动重连并重试当前操作:

'connections' => [
    'mysql' => [
        // ...
        'break_reconnect' => true,
    ],
],

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

8. 小结

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

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

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