ThinkPHP的MVC是依赖文件位置、类名规则、方法签名和配置项严格约定的协作机制。控制器仅作请求分发,模型封装数据与业务逻辑,视图专注纯展示,三者通过目录结构、命名规范和配置项强制协同,不容偏差。

ThinkPHP的MVC不是教科书里的抽象概念,而是一套靠文件位置、类名规则、方法签名和配置项共同约束落地的协作机制。写错一个控制器后缀、漏配一条路由、或在控制器里直接echo输出,页面就可能空白或报错——它不讲道理,只认约定。
控制器:请求调度中枢,不掺业务逻辑
控制器本质是“请求分发员”:接收URL路由映射过来的请求(如/user/detail→app/controller/User.php中的detail()方法),校验参数,调用模型处理,再把结果交给视图渲染。它不做SQL拼接、不写密码加密逻辑、不判断权限是否足够——这些必须下沉到模型层。
- 推荐用model('User')或Db::name('user')调模型,避免硬编码路径
- 参数获取优先走路由绑定(如Route::get('user/:id', 'user/read')),$id会自动注入read()方法,无需input('id')
- 返回值必须是字符串、View对象或Response实例,不能直接echo或die
模型:数据与业务规则的唯一出口
模型不只是数据库操作封装,更是业务逻辑的守门人。继承\think\Model只是起点,真正价值在于把校验、关联、状态转换、敏感操作(如密码重置)全部收束其中。比如用户登录,控制器只传账号密码,模型负责查库、比对哈希、更新登录时间、触发事件,甚至调用短信服务——视图和控制器完全感知不到这些细节。
- 字段验证、唯一性检查、自动完成(如create_time)应在模型的validate或autoWrite中定义
- 关联查询(如hasOne、hasMany)统一在模型内声明,控制器调用时只需$user->orders
- 业务强相关方法(如UserModel::resetPassword())必须放在模型里,禁止散落在控制器中
视图:纯展示层,拒绝逻辑渗入
视图是模板引擎渲染的最终HTML片段,只做数据填充和结构组织。ThinkPHP默认使用fetch()按命名规则加载模板(如控制器User::profile()对应view/user/profile.html),所有变量都由控制器显式传入,模板内不允许执行数据库查询、不调用模型方法、不写if-else嵌套超过两层的业务判断。
立即学习“PHP免费学习笔记(深入)”;
- 用{$user.name}取值,不用
- 复杂展示逻辑可抽成模板函数或组件,但不得引入模型类或DB操作
- 静态资源路径、SEO标题等应通过控制器传参控制,而非在模板里硬编码
三者协同靠约定,不靠文档解释
MVC在ThinkPHP里不是松散耦合的模块,而是被目录结构、命名规范和配置项死死绑在一起的系统。比如controller_layer设为ctrl,那控制器就必须放在app/ctrl/下;controller_suffix开启后,类名必须以Controller结尾;fetch()找不到对应模板就报错,不会自动fallback——它靠的是机械执行,不是智能推导。
- 文件位置即契约:app/model/User.php → User模型类 → 对应user数据表
- 方法签名即接口:index()必须返回可响应内容,initialize()会在每次实例化时自动调用
- 配置项即开关:empty_controller决定404时走哪个兜底类,default_controller指定首页入口



















