4.1 控制器形态与派工规范


4.1 控制器形态与派工规范

本节摘要:控制器有三种编制:普通控制器管一组相关动作,资源控制器固定承担 RESTful 七件套,单动作控制器一个类只干一件事加一个 __invoke。本节给出三者的选型标准与代码骨架,再讲派工规范:动作怎么保持短小、依赖怎么注入、公共逻辑往哪放。读完你能把一个 CRUD 模块的组织方式说得清楚、写得干净。

接单的人先立规矩

前三章让请求顺利走到了工位,本章开始,写的主要就是控制器代码了。动手之前先把形态选对:控制器不是越多越好,也不是越全越好,形态对不上场景,后面每一行都在别扭。

三种形态的选型

普通控制器:默认形态,一组相关动作住在一起。

<?php namespace App\Http\Controllers; use App\Models\WorkOrder; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\View\View; class WorkOrderController extends Controller { public function index(): View { return view('orders.index', [ 'orders' => WorkOrder::latest()->paginate(15), ]); } public function store(Request $request): RedirectResponse { $order = WorkOrder::create($request->validated()); return redirect()->route('orders.show', $order) ->with('status', '工单已登记'); } }

资源控制器:动作集合固定,第三章的资源路由已经把 URI 和方法名都约定死了,直接按表填代码:

php artisan make:controller OrderController --resource --model=Order

单动作控制器:一类只留一个 __invoke,适合"这个动作很重、值得独占一类"的场景,比如导出报表、发起支付:

<?php namespace App\Http\Controllers; use App\Services\MonthlyReportExporter; use Illuminate\Http\Response; class ExportMonthlyReportController extends Controller { public function __invoke(MonthlyReportExporter $exporter): Response { return $exporter->download(now()->subMonth()); } }
<?php // 路由侧不用写方法名,容器直接调 __invoke Route::get('/reports/monthly/export', ExportMonthlyReportController::class);

选型经验谈:CRUD 主体用资源控制器;一个动作复杂到带专属表单验证、专属服务类时,升级成单动作;普通控制器收纳那些不成套的杂项动作。判断标准不是数量,是"这组动作共享多少上下文"。

图 4-1:三种形态的派工规范对比

图 4-1:三种形态的派工规范对比

派工规范:让动作保持薄

控制器膨胀几乎都从同一个姿势开始:验证、查库、业务计算、发通知、组织响应全堆在一个动作里。规范只有三条,守住了就薄:

  • 验证前置:用第三章的路由约束挡格式,用下一节的 Form Request 挡业务规则,控制器拿到的数据默认可信;
  • 业务下沉:跨模型、跨服务的流程抽成服务类,控制器只负责"调用并把结果变成响应";
  • 响应规范:页面走 view,跳转带闪存,JSON 交给 API 资源(4.3 节),不在动作里手拼数组。
<?php // 一个"薄"字到什么程度:动作只剩调度与表达 public function accept(AcceptOrderRequest $request, Order $order): RedirectResponse { // 授权与验证已由 Form Request 完成(下一节) $this->orderService->accept($order, $request->user()); return redirect()->route('orders.show', $order) ->with('status', '工单已接单,施工队进场'); }

动作什么时候该拆服务类:一个判断练习

拿工单系统里的"接单"动作举例。第一版它只有一行模型调用,留在控制器里正合适;两週后需求加了"接单要校验队组余额、发进场通知、记操作流水",动作涨到十几行——这时拆的收益就出现了:接单流程被三个入口共用(页面、API、 artisan 修复命令),散在控制器里就是三份拷贝。

<?php // 拆分后的服务类:流程的唯一真相源 namespace App\Services; use App\Models\Order; use App\Models\User; class OrderAcceptService { public function accept(Order $order, User $actor): void { // 校验余额 扣减 建进场记录 发通知——全部步骤集中于此 $order->markAccepted($actor); } }

控制器退回一行调用加一个响应。判断标准可以总结成一句:两个以上入口要走的流程,或者超过一屏的动作,拆服务类;其余留给控制器,别为拆而拆。

三个高频追问

构造器注入与方法注入怎么选?
本类处处要用的依赖放构造器(声明一次),单个动作才用的放方法参数(按需声明)。中间件式的控制器构造器注入还能配合框架的授权与限流方法,做类级别的统一配置。

控制器里可以 echo 或 dd 调试吗?
开发期临时用没问题,但要清楚它们会截断响应流程——dd 直接吐出内容并终止。长期手段是 logger() 加日志查看,或第九章的测试断言;生产环境代码里出现 dd 是事故级失误。

为什么动作方法都要写返回类型?
返回类型是给读代码的人与 IDE 的契约:view、RedirectResponse、JsonResponse 一眼可辨,重构时类型检查器还能替你抓漏网的地方。框架不强制,但这是低成本高回报的习惯。

本节要点回顾

  • 三种编制:普通管杂项、资源管成套 CRUD、单动作管重流程,按共享上下文选型。
  • 薄控制器三条:验证前置、业务下沉、响应规范,动作一屏读完。
  • 用户上下文:由控制器显式下传,不让模型偷偷依赖请求态。

形态立好了,接下来处理最容易被写脏的一环:材料进场前的质检。下一节用 Form Request 把验证彻底规范化。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U