
在 laravel 中,重复的控制器逻辑(如通用 store 方法)应通过抽象基类、依赖注入与策略化请求验证来消除冗余,而非复制粘贴;本文提供可落地的 dry 实现方案,兼顾类型安全、请求验证隔离与扩展性。
为真正践行 DRY(Don’t Repeat Yourself)原则,控制器中高度相似的 CRUD 逻辑不应简单复制,而应通过泛型化基类 + 运行时绑定模型 + 动态请求解析实现复用。你提出的 MainController 思路方向正确,但原始实现存在关键缺陷:无法静态指定请求类(导致 IDE 无提示、类型检查失效)、$this->model 赋值时机不安全(构造函数中实例化模型违反依赖倒置),且缺乏对请求验证契约的统一支持。
✅ 推荐方案:使用 抽象资源控制器 + 模型绑定 + 请求类反射解析
首先,定义一个泛型基类,强制子类声明模型类名与对应表单请求类:
requestClass();
$validated = app($requestClass)->validated();
$model = app($this->modelClass())->create($validated);
return response()->json(['message' => 'Successfully created'], 201);
}
}然后,在具体控制器中精准声明契约:
? 关键优势说明:
- ✅ 类型安全 & IDE 友好:modelClass() 和 requestClass() 返回 string,配合 PHPStan/PHPStorm 可精准推导模型与请求类;
- ✅ 请求验证解耦:每个控制器仍使用专属 FormRequest,保留独立规则、消息与授权逻辑(如 authorize() 方法);
- ✅ 符合 Laravel 习惯:不破坏路由模型绑定、中间件、资源控制器等生态;
- ✅ 可扩展性强:后续可轻松添加 index()、update() 等通用方法,并支持 withTrashed()、withoutEvents() 等策略钩子。
⚠️ 注意事项:
- 避免在基类中硬编码响应结构。如需统一响应格式(如 {"data": {}, "meta": {}}),建议封装为 ApiResourceResponse 类(如答案中提到的 YourCustomResponse::success()),并在基类中调用,而非直接写死 response()->json(...);
- 若模型需特殊创建逻辑(如关联赋值、状态机流转),应在模型自身 create() 或 fill() 后置钩子中处理,而非污染控制器;
- 不推荐用 __construct() 注入模型实例(如 $this->model = new First()),这会阻碍测试 Mock 和依赖注入容器管理——始终优先使用 app($class) 或构造器注入。
总结:DRY 不等于“写一次、到处继承”,而是在抽象层级上识别稳定契约(模型+请求),将变化点(业务规则、权限、响应形态)保留在具体子类或独立服务中。上述方案既消除了 90% 的模板代码,又为未来演进(如增加审计日志、事件广播)预留了清晰的扩展入口。










