thinkphp的mvc需主动遵守三层边界:控制器仅接收参数、调用模型、传值给视图;视图禁止数据库操作和php逻辑;模型专注数据处理,不输出html或响应。越界将导致隐性耦合。

ThinkPHP 的 MVC 不是“配好了就能自动分离”的魔法,它依赖你主动遵守三层边界。框架只提供目录约定和基础类支持,真正在哪写逻辑、数据怎么传、模板里能干什么,全靠你卡住那几条线——越界一步,后面就全是隐性耦合。
控制器里别碰数据库查询语句
常见错误是直接在 IndexController.php 里写 $pdo->query() 或 Db::table()->select()。这看似快,但模型层就废了,后续改表结构、加缓存、换数据库驱动时,你得翻遍所有控制器。
正确做法是:控制器只做三件事——接收参数、调用模型方法、传递数据给视图。比如:
class UserController extends Controller
{
public function profile()
{
$id = input('id');
$user = User::find($id); // 调用模型,不是自己查
return view('profile', ['user' => $user]);
}
}
-
User必须是继承think\Model的类,放在app/model/User.php - 所有字段校验、关联查询、软删除逻辑都写在模型里,控制器不掺和
- 如果模型方法返回
null,控制器应统一处理(如抛HttpException或跳 404),而不是在视图里判空
视图文件里禁止 new Model() 或 Db::
很多新手在 app/view/user/profile.html 里直接写 {:Db::name('log')->where('uid',$user['id'])->select()},以为“只是查点小数据”。结果是:页面逻辑散落、无法复用、模板变慢、安全风险(SQL 注入可能绕过模型层防护)。
视图只做一件事:把控制器传来的变量,按需展示。它不该有“能力”去拿新数据。
- 必须由控制器提前查好所有需要的数据,用
compact()或数组方式传入,例如return view('profile', compact('user', 'posts', 'permissions')) - 模板中允许用
{$user.name}、{volist name="posts" id="post"}{$post.title}{/volist}这类渲染语法,但不能出现Db::、new、include外部 PHP 文件 - 如果某页需要动态加载评论,应走 AJAX 请求独立接口(返回 JSON),而不是在模板里嵌 SQL
模型类不能 echo 或 return HTML
模型的职责非常窄:封装数据操作。一旦你在 User.php 里写了 echo 'success'; 或 return view('xxx'),它就不再是模型,而是一个混血怪物。
ThinkPHP 的模型设计默认面向数据对象,不是面向响应。
- 所有数据库交互方法(
getById()、search()、saveWithLog())必须返回数组、对象、Collection或布尔值 - 事务控制写在模型方法内部(
$this->startTrans()),但事务成败的响应逻辑(如提示用户、跳转)仍归控制器管 - 模型可定义
scope、accessor、mutator,但这些都不能触发输出或重定向
路由配置别绕过控制器直接指向视图
有人为了“快速出页面”,在路由配置里写 Route::get('about', 'view://about') 或直接 return view('about') 在闭包里。这等于把控制器层抽掉,MVC 变成 MV——视图直连请求,模型彻底闲置。
哪怕是最简单的静态页,也应走控制器中转,保持调用链完整。
- 静态页控制器可极简:
public function about() { return view(); },不传参也行,但必须存在 - 所有带参数的 URL(如
/user/123)必须绑定到控制器方法,不能靠正则匹配后直接include模板 - 使用资源路由(
Route::resource('user', 'User'))时,确保对应控制器已实现全部标准方法(index、show等),别只写一半
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











