I am using route model binding for my API routes.
Route::middleware('client')->group(function(){
Route::get('/documents/{document}', [DocumentController::class, 'get']);
Route::apiResource('documents', DocumentController::class)->only('store', 'destroy');
Route::post('filter', [DocumentController::class, 'filter']);
});
DocumentController:
public function get(Document $document): JsonResponse|string|null
{
// do some stuff
return response()->json([
'code' => 404,
"message" => "Some Message"
], 404);
}
When I access my API route (GET - /api/documents/$uuid) with valid $uuid that exist in DB everything works fine. But If I call it with invalid $uuid which doesn't exist in DB it throws exception:
"message": "No query results for model [App\\Models\\Document] some_uuid_here",
"exception": "Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException",
Problem is thrown exception is NotFoundHttpException while it should be ModelNotFoundException.
Shouldn't NotFoundHttpException need to get thrown if route completely undefined?
I don't want to show user Route not defined while there is actual route but not record with given primary key.
I surrounded Document::findOrFail($value); in try catch block just to debug exception and even findOrFail throws NotFoundHttpException
try {
Document::findOrFail($value);
}catch (\Exception $exception){
throw $exception;
}

try {
Document::findOrFail($value);
}catch (\Exception $exception){
echo get_class($exception);
}

Laravel automatically converts ModelNotFoundException into NotFoundHttpException during route model binding to standardize 404 responses. This behavior is controlled by the prepareException method in the App\Exceptions\Handler class.
To customize the response for missing models, you can override the render method in App\Exceptions\Handler to check if a NotFoundHttpException wraps a ModelNotFoundException:
public function render($request, Throwable $exception)
{
if ($exception instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) {
if ($exception->getPrevious() instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
return response()->json(['code' => 404, 'message' => 'Record not found'], 404);
}
return response()->json(['code' => 404, 'message' => 'Route not defined'], 404);
}
return parent::render($request, $exception);
}
ModelNotFoundException and NotFoundHttpException differentlyIn your comment under your question, I saw you asking how to identify whether the problem is 'Route not defined' or 'Record not found', given that both are thrown by the same class. You could do something like this:
prepareException method to prevent converting ModelNotFoundException into NotFoundHttpException:protected function prepareException(Throwable $e)
{
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
return $e; // Do not convert it to NotFoundHttpException
}
return parent::prepareException($e);
}
render Method:public function render($request, Throwable $exception)
{
if ($exception instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
return response()->json(['code' => 404, 'message' => 'Record not found'], 404);
}
if ($exception instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) {
return response()->json(['code' => 404, 'message' => 'Route not defined'], 404);
}
return parent::render($request, $exception);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With