While developing backend part in my current project I facing a problem with customizing “missing model” logic in Laravel Implicit Binding. Of course, we’ve Missing Model Behavior from documentaion, but I’ve lots of routes with groups – so it’ll not very suitable method.
I spent hours digging the solution and finally want to share it with you.
Option 1. Simplifying method from documentation
As I sad above – it is not very suitable to pass closure on every “missing” method, but it is still a solution, especially when you assign anonymous function to variable and make your route file more pretty like this
$notFound = function (Request $request) { return response('',404, ['content-type' => 'application/json']); }; Route::middleware('auth:sanctum')->group( function () use ($notFound){ Route::get('/user', [UserController::class, 'info']); Route::middleware('can:admin')->group(function () use ($notFound) { Route::get('/users', [UserController::class, 'index'])->name('users.index'); Route::get('/user/{user}', [UserController::class, 'view'])->name('user.view')->missing($notFound); Route::patch('/user/{user}', [UserController::class, 'update'])->name('user.update')->missing($notFound); }); } );
Sounds like solution, but it is not very suitable when you’ve lots of routes, so second option is …
Option 2. Catch ModelNotFoundException in Laravel 9.
If you look into documentaion – you’ll see, that you can easily catch any exception from global handler, but you’ll facing a problem
/* this works */ $this->renderable(function (NotFoundHttpException $e, $request) { if ($request->is('api/*')) { return response()->json([ 'message' => 'Page not found.' ], 404); } }); /* this don't work */ $this->renderable(function (ModelNotFoundException $e, $request) { if ($request->is('api/*')) { return response()->json([ 'message' => 'Record not found.' ], 404); } });
that ModelNotFoundException is not handled (deer)
First of all – we can do a workaround in Laravel 7 style using render method in global handler like this
/* this works */ public function render($request, Throwable $e) { if ($e instanceof ModelNotFoundException) { return response('',404,['content-type' => 'application/json']); } }
and this’ll work. But in Laravel 9 method render is not blank and contains logic, so I dig again and find this code
/* this works in Laravel 9 */ public function register() { $this->renderable(function (NotFoundHttpException $e, $request) { if ($e->getPrevious() instanceof ModelNotFoundException) { return response('',404,['content-type' => 'application/json']); } }); }
on stackoverflow.
As you can see – there is a confusing condition – we match against NotFoundHttpException and than do a $e->getPrevious(), here is the answer – ModelNotFoundException automatically call NotFoundHttpException.
Option 3. Call ModelNotFoundException on one Model
This is not my case – but I think to add it to complete the answer.
If you need customize ModelNotFoundException only on one model, you need to add an explicit binding for model in RouteServiceProvider and pass a callback as a third parameter that’s called when the model is not found
public function boot() { parent::boot(); Route::model('order', \App\Order::class, function ($order) { throw new OrderNotFoundException($order); }); }
That’s all. Have a nice ModelNotFoundException Laravel handling