You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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里手动检查模型存在性,抛出自定义异常:

  1. 先创建两个自定义异常类:
// 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}的投票不存在");
    }
}
  1. 修改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);
    }

    // 后续关联检查、事务逻辑不变...
}
  1. 在控制器里分别捕获这两个自定义异常:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 11:20:36