ThinkPHP 的“视图”实为 ViewModel,是用于模拟多表 JOIN 的 PHP 封装,须继承 Think\Model\ViewModel、用 D() 实例化、正确配置 $viewFields 和 _on 关联,TP6 已废弃该机制。

ThinkPHP 里所谓“视图”不是数据库视图,也不是前端模板页面,而是 ViewModel —— 一种用 PHP 模拟多表 JOIN 查询的模型封装方式。直接用错,90% 是当成数据库视图或普通 Model 在用。
ViewModel 必须继承 Think\Model\ViewModel
写错最常见原因:复制普通模型文件后只改类名,没改父类。TP3.2 的视图模型不能 extends Model,必须是 ViewModel。
- 正确写法:
class BlogViewModel extends ViewModel - 错误写法:
class BlogViewModel extends Model(会报方法不存在或字段丢失) - 别漏掉命名空间声明:
use Think\Model\ViewModel,否则类找不到 - 文件名必须匹配类名且首字母大写,如
BlogViewModel.class.php;含下划线(Blog_View_Model.class.php)会被自动忽略
D() 实例化,不能用 M()
M() 只支持单表操作,D() 才能加载自定义模型类(包括 ViewModel)。控制器里写 M('BlogView') 看似能跑通,但实际走的是默认 Model,$viewFields 完全不生效。
- 正确调用:
D('BlogView')->select() - 错误调用:
M('BlogView')->select()(查不到关联字段,SQL 里没有 JOIN) - 调试时加一句:
echo D('BlogView')->getLastSql();,把生成的 SQL 拿到 MySQL 里执行,能快速定位_on条件写错、字段名冲突等问题
$viewFields 里表名不带前缀,_on 关联也别带
TP 会自动读取配置里的 DB_PREFIX,你在 $viewFields 里写的表名只是“别名”,不是真实表名。写成 'n_blog' => array(...) 或在 _on 里写 n_blog.id = n_user.blog_id,会导致 SQL 报错或关联失效。
立即学习“PHP免费学习笔记(深入)”;
- 假设数据库真实表是
n_blog和n_user,配置了'DB_PREFIX' => 'n_' - 正确写法:
'blog'=>array('id','title'), 'user'=>array('name'=>'username','_on'=>'blog.user_id=user.id') - 错误写法:
'n_blog'=>...或_on'=>'n_blog.user_id=n_user.id' - 字段别名冲突要手动处理:两个表都有
title字段?必须至少一个重命名,比如'title'=>'category_title'
TP6 已废弃 ViewModel,别混用版本特性
TP6 完全移除了 ViewModel 类,也不再支持 $viewFields。如果你在 TP6 项目里照搬 TP3.2 的写法,会直接报 Class 'Think\Model\ViewModel' not found。
- TP6 正确做法:用关联模型(
hasOne/belongsTo)+with()预载,或原生查询Db::table()->alias()->join() - 别试图在 TP6 的
config/view.php里配视图相关项 —— 那是模板引擎配置,和数据库无关 - 看到
Template not found或Call to undefined method think\View::fetch(),基本是模板引擎配置错版本,和 ViewModel 无关
真正容易被忽略的点是:ViewModel 本质是语法糖,它生成的 SQL 每次都重新执行,没缓存、不复用执行计划。线上高并发场景下,JOIN 多、数据量大时,性能瓶颈比你想象中来得快。别把它当数据库视图用,更别在循环里反复调用 D('XxxView')->select()。



















