Laravel 9:如何在父控制器捕获Repository中findOrFail的异常?
Laravel 9中处理多次findOrFail的异常捕获方案
你的代码里控制器的try/catch其实已经能捕获两次findOrFail抛出的ModelNotFoundException,但如果想要更精准的错误提示或者更灵活的处理,有以下几种可行方案:
方案1:直接复用现有捕获,利用Laravel默认异常信息
Laravel的findOrFail抛出的ModelNotFoundException本身就包含具体的模型类和ID信息,比如找不到Article时,异常消息是No query results for model [App\Models\Article] {id},找不到Vote时是No query results for model [App\Models\Vote] {manyItemId}。
只需要保持控制器现有代码即可,返回的响应会自动带上明确的错误信息,前端能直接识别是哪个资源不存在:
public function articleManyVotesStore(Request $request, int $articleId, int $voteId) { try { $data = $request->only('article_id', 'active', 'expired_at', 'supervisor_id', 'supervisor_notes'); return $repository->store(id: $articleId, manyItemId: $voteId, data: $data); } catch (ModelNotFoundException $e) { return response()->json(['message' => $e->getMessage()], 404); } catch (CustomManyToManyItemException $e) { return response()->json(['message' => $e->getMessage()], 400); // 这里建议用400更符合语义,重复关联属于客户端错误 } }
方案2:自定义模型不存在异常,精准区分资源类型
如果想要更友好的业务化提示(比如把"App\Models\Article"换成"文章"),可以在Repository里手动检查模型存在性,抛出自定义异常:
- 先创建两个自定义异常类:
// app/Exceptions/ArticleNotFoundException.php namespace App\Exceptions; use Exception; class ArticleNotFoundException extends Exception { public function __construct(int $id) { parent::__construct("ID为{$id}的文章不存在"); } } // app/Exceptions/VoteNotFoundException.php namespace App\Exceptions; use Exception; class VoteNotFoundException extends Exception { public function __construct(int $id) { parent::__construct("ID为{$id}的投票不存在"); } }
- 修改Repository的
store方法,替换findOrFail为手动检查:
public function store(int $id, int $manyItemId, array $data): JsonResponse|MessageBag { $article = Article::find($id); if (!$article) { throw new \App\Exceptions\ArticleNotFoundException($id); } $vote = Vote::find($manyItemId); if (!$vote) { throw new \App\Exceptions\VoteNotFoundException($manyItemId); } // 后续关联检查、事务逻辑不变... }
- 在控制器里分别捕获这两个自定义异常:
use App\Exceptions\ArticleNotFoundException; use App\Exceptions\VoteNotFoundException; public function articleManyVotesStore(Request $request, int $articleId, int $voteId) { try { $data = $request->only('article_id', 'active', 'expired_at', 'supervisor_id', 'supervisor_notes'); return $repository->store(id: $articleId, manyItemId: $voteId, data: $data); } catch (ArticleNotFoundException|VoteNotFoundException $e) { return response()->json(['message' => $e->getMessage()], 404); } catch (CustomManyToManyItemException $e) { return response()->json(['message' => $e->getMessage()], 400); } }
方案3:在Repository里统一捕获并包装异常
如果不想创建多个自定义异常,也可以在Repository里用try/catch包裹两次findOrFail,然后抛出带明确标识的异常:
public function store(int $id, int $manyItemId, array $data): JsonResponse|MessageBag { try { $article = Article::findOrFail($id); } catch (ModelNotFoundException $e) { throw new ModelNotFoundException("文章ID {$id} 不存在", 0, $e); } try { $vote = Vote::findOrFail($manyItemId); } catch (ModelNotFoundException $e) { throw new ModelNotFoundException("投票ID {$manyItemId} 不存在", 0, $e); } // 后续关联检查、事务逻辑不变... }
这样控制器里还是用原来的ModelNotFoundException捕获,返回的消息就是自定义的友好提示。
内容的提问来源于stack exchange,提问作者mstdmstd
相关产品推荐
相关产品推荐

