ThinkPHP模型是业务逻辑守门人和数据操作统一出口,封装验证规则、自动赋值、关联查询等,避免控制器重复代码;TP6与TP5在路径、验证、关联语法上存在关键差异。

ThinkPHP 模型到底干啥用?
它不是数据库的“快捷方式”,而是业务逻辑的守门人和数据操作的统一出口。没模型,你得在控制器里拼 SQL、验规则、填时间戳;有模型,这些事集中封装、一次定义、多处复用——而且改表结构或验证逻辑时,只动模型,控制器几乎不用碰。
什么时候必须写模型?
只要涉及以下任一场景,绕开模型就等于主动给自己埋坑:
- 表单提交需要校验(比如
email格式、password长度),用模型的validate规则比在控制器里if (!is_email($data['email']))干净十倍 - 字段要自动补值(如
create_time写入当前时间、status默认设为1),模型的auto或filter机制能避免每处insert()都手动赋值 - 同一张表被多个控制器调用(比如
UserModel被登录、后台用户管理、API 接口共用),不抽成模型,后期加个字段或改个默认值就得全局搜Db::name('user')改七八处 - 需要关联查询(如查用户同时带出他最新的 3 篇文章),模型里的
hasMany+with写法清晰稳定,硬写 JOIN 容易漏别名、错条件、难复用
常见错误:把模型当“SQL 封装类”来用
很多人建了 UserModel,却只在里面写 selectUserById() 这种纯查询方法,然后还在控制器里手动组装 where 条件、自己做分页计算——这等于白建模型。模型的核心价值不在“封装 SQL”,而在“封装规则”和“封装意图”。
正确做法是让模型表达业务语义:
立即学习“PHP免费学习笔记(深入)”;
- 用
protected $validate = [...]声明“注册时邮箱必填且唯一”,而不是在控制器里查一遍再报错 - 用
protected $auto = ['status' => 1, 'create_time' => 'time']表达“新用户默认启用、创建时间取当前秒级时间戳”,而不是每次save()前手动$data['create_time'] = time() - 用
scopeRecent()定义“最近发布的文章”这种可复用查询范围,而不是在每个控制器里重复写where('publish_time', '>', time() - 86400 * 7)
TP6 和 TP5 模型写法差异要注意
TP6 把模型彻底独立为 app\model\User 类,默认继承 think\Model;TP5 则常放在 application/common/model/User.php,继承 think\Model 或 think\model\Base。路径和命名空间错了,new UserModel() 直接报 Class 'app\model\UserModel' not found。
更隐蔽的坑是验证行为:TP6 的 validate(true) 默认走模型验证规则,而 TP5 很多老项目仍依赖控制器里手动调 $model->validate()->save($data),混用容易漏校验。
还有就是关联预载入语法:TP6 用 UserModel::with('posts')->find(1),TP5 是 UserModel::with(['posts'])->find(1)——少个方括号,运行时不报错但关联数据为空,极难排查。
模型不是可选项,它是 ThinkPHP 区别于裸写 PHP 的关键分水岭。越早把它当成“业务契约”来写,而不是“数据库代理”来凑合,后面加功能、改需求、查 bug 才真能省下大把时间。



















