路由中不应直接写条件判断,应将逻辑下移到控制器或服务层,路由仅负责分发;固定类型推荐拆分为独立路由,where()仅用于参数格式校验而非业务分支。

路由里直接写条件判断会破坏可维护性
很多人想在 routes/api.php 里用 if 判断请求参数来返回不同结果,比如根据 type=users 或 type=posts 走不同逻辑。这看似省事,但实际会让路由文件越来越臃肿,测试难、复用差、中间件无法精准控制。
正确做法是把判断下移到控制器或服务层,路由只负责分发。例如定义统一入口 GET /api/resources,由控制器处理 type 参数分支:
public function index(Request $request)
{
$type = $request->input('type');
return match($type) {
'users' => $this->userService->list(),
'posts' => $this->postService->list(),
default => response()->json(['error' => 'invalid type'], 400),
};
}
- 避免在路由闭包里写业务逻辑,尤其是涉及数据库查询或外部调用时
- 如果类型固定且差异大,更推荐拆成独立路由(
/api/users、/api/posts),语义清晰、缓存友好、OpenAPI 文档也更准 -
match表达式从 PHP 8.0 开始支持,低于该版本请用switch
Laravel 的 where() 和 whereIn() 不适合动态条件路由
有人尝试用 where('type', ['users', 'posts']) 或 where('type', 'users|posts') 让一个路由匹配多个值——这只能做字符串格式校验,不是“根据值执行不同逻辑”的方案。它不改变响应内容,只过滤非法输入。
真正需要的是运行时决策,而不是路由正则约束。比如下面这种写法毫无意义:
Route::get('/api/{type}', [ResourceController::class, 'index'])
->where('type', 'users|posts');
-
where()只影响路由是否匹配成功,不参与后续逻辑分支 - 若想限制参数范围,应放在控制器里验证,配合
$request->validate()或自定义规则,便于返回一致错误格式 - 过度依赖
where()容易掩盖真实业务意图,后期加新类型还得改路由定义
用 API Resource 做响应差异化比手动拼数组更安全
当同一接口需返回不同结构(如用户带头像、文章带摘要),别在控制器里写一堆 if 拼关联字段。Laravel 的 ApiResource 天然支持按模型类型定制输出,且自动处理空关系、隐藏字段、序列化上下文。
例如统一返回 ResourceCollection,但让 UserResource 和 PostResource 各自决定字段:
return new UserResource($user); // 返回 name/email/avatar return new PostResource($post); // 返回 title/content/excerpt
- 不要在控制器里用
toArray()手动构造响应,丢失了资源的封装性和可扩展性 - 如果必须混排(如首页 feed),用
whenLoaded()控制关联加载,再交由对应 Resource 渲染 - 注意
ResourceCollection默认不支持多模型混合,需自定义 collection 类或用Collection::map()分流
Query 参数太多时,别硬塞进一个路由
当接口要支持 ?type=xxx&status=yyy&sort=zzz&include=aaa 这类组合参数,还要求每个组合返回不同数据结构,说明这个接口职责已经超载。这不是路由设计问题,而是领域边界模糊。
- 优先按资源划分路由(
/api/users、/api/posts),再用标准查询参数过滤/排序/包含关系 - 如果确实存在强耦合场景(如搜索聚合页),用专用控制器方法(
SearchController@aggregate)并明确文档其输入输出契约 - 警惕把所有参数都扔给一个
index()方法处理,容易导致缓存失效、SQL 注入风险上升、Eloquent 加载失控
复杂条件路由的本质,是业务规则没沉淀到领域层。路由越简单,越说明逻辑被正确隔离了。











