4、控制器(Controller)深入:控制器的责任、请求处理、路由分发、模型与视图的协调以及常见的控制器设计模式。

好,咱们今天来聊聊控制器。说实话,很多初学者觉得控制器就是“写逻辑的地方”,结果把控制器搞得又胖又乱。我见过一个项目,控制器里塞了两千行代码,连数据库查询都写在里面。嗯,那场面,简直是一场灾难。

控制器的核心责任,说白了就三件事:接住请求、调度资源、返回响应。它不负责算账,不负责查数据库,更不负责画页面。它就是个“交通指挥员”。

4.1 控制器的核心责任

我个人习惯把控制器的职责拆成五个点,你记一下:

  • 接收输入:从 HTTP 请求中提取参数、表单数据、请求头。
  • 参数校验:检查数据格式对不对,比如邮箱是不是真的邮箱。
  • 调用模型:把处理好的数据交给模型层去干活。
  • 选择视图:根据结果决定返回哪个页面或 JSON。
  • 错误处理:出错了别崩,给用户一个友好的提示。

重要原则:控制器里不要出现 SQL 语句,不要出现 HTML 标签。一旦你发现控制器里写了 echo "

",那你就该重构了。

4.2 请求处理流程

一个请求进来,到底是怎么走到控制器里的?我画了一张图,你看一眼就明白了。

控制器请求处理流程 用户 HTTP 请求 路由分发器 控制器 模型层 视图层 返回响应

你看,请求先到路由,路由根据 URL 找到对应的控制器方法,然后控制器去协调模型和视图。整个过程就像流水线,每个环节各司其职。

4.3 路由分发机制

路由分发,说白了就是“谁负责这件事”。我建议你使用显式路由,而不是隐式路由。为什么?

我曾经在一个遗留项目里看到过隐式路由,控制器和方法名全靠约定,结果找了一个 bug 找了三天——原来是一个方法名拼写错了,路由死活匹配不上。从那以后,我坚持用显式路由。

来看一个简单的路由配置示例:

// 显式路由配置
$router->get('/users', 'UserController@index');
$router->get('/users/{id}', 'UserController@show');
$router->post('/users', 'UserController@store');
$router->put('/users/{id}', 'UserController@update');
$router->delete('/users/{id}', 'UserController@destroy');

小技巧:路由命名要遵循 RESTful 风格。你想想看,如果每个接口都叫 getUserDatadeleteUserRecord,那维护起来得多痛苦。

4.4 模型与视图的协调

控制器怎么协调模型和视图?我总结了一个“三步法”:

  1. 从模型拿数据:调用模型的方法,获取业务数据。
  2. 处理数据:如果需要对数据做格式化、筛选,在这里做。
  3. 传给视图:把处理好的数据塞给视图,让视图去渲染。

看个例子:

class UserController {
    public function show($id) {
        // 1. 从模型拿数据
        $user = UserModel::find($id);

        // 2. 检查数据是否存在
        if (!$user) {
            return $this->notFound('用户不存在');
        }

        // 3. 传给视图
        return $this->view('user.profile', ['user' => $user]);
    }
}

注意:不要在控制器里直接操作数据库!我曾经见过有人直接在控制器里写 DB::query("SELECT * FROM users"),结果后来数据库改了字段名,满世界找哪里引用了这个字段。那叫一个酸爽。

4.5 常见的控制器设计模式

在实际项目中,我常用的控制器模式有这么几种:

模式名称 适用场景 优点 缺点
瘦控制器 业务逻辑简单的 CRUD 代码清晰,容易测试 模型层会变胖
资源控制器 标准的 RESTful 接口 结构统一,团队协作方便 灵活性稍差
动作控制器 每个动作一个类 单一职责,易于扩展 文件数量多
服务控制器 复杂业务逻辑 业务逻辑抽离到服务层 需要额外维护服务层

我个人最推荐的是瘦控制器 + 服务层的组合。控制器只负责“接和传”,真正的业务逻辑放到服务层里。这样测试起来也方便,你想想看,测试一个控制器方法,只需要 mock 服务层就行了,多省事。

4.6 避坑指南

最后,我分享几个我踩过的坑:

  • 控制器里不要写事务:事务应该放在服务层或模型层。控制器里写事务,一旦方法多了,事务嵌套会让你崩溃。
  • 不要直接返回模型对象:我曾经直接返回了一个 Eloquent 模型,结果前端拿到了所有字段,包括密码哈希。嗯,那是一次紧急修复。
  • 参数校验别偷懒:每个接口都要做参数校验。你以为前端做了校验就万事大吉了?别忘了,有人可以直接调你的 API。

核心总结:控制器就是个“二传手”。它不生产数据,它只是数据的搬运工。把这句话刻在脑子里,你的控制器就不会跑偏。

公众号:蓝海资料掘金营,微信 deep3321