Support the ongoing development of Laravel.io →
Blade Laravel
Last updated 3 days ago.
1

Excellent question and you’re absolutely right to want to keep firstOrFail(), because it’s idiomatic and clean in Laravel. The “hack” using first() works but gives up the elegant exception handling built into the framework.

Let’s solve this properly, and also in a global way so that every theme’s custom 404.blade.php is automatically used for missing models, missing pages, etc.

When firstOrFail() (or any findOrFail(), ModelNotFoundException, etc.) fails, Laravel throws a ModelNotFoundException. You can catch that globally in your app/Exceptions/Handler.php.

So instead of manually redirecting to /404, let’s intercept that exception and render the correct 404 view based on the currently active theme.

Step 1: Edit app/Exceptions/Handler.php Open app/Exceptions/Handler.php and add a custom handler for ModelNotFoundException (and optionally for NotFoundHttpException too).

use Illuminate\Database\Eloquent\ModelNotFoundException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use App\Models\Settings;

public function render($request, Throwable $exception) { // Handle model not found if ($exception instanceof ModelNotFoundException || $exception instanceof NotFoundHttpException) {

    // Get the active theme
    $settings = Settings::first();
    $theme_directory = $settings->theme_directory ?? 'default';

    // Build the path to the custom 404 view
    $theme_404_view = 'themes.' . $theme_directory . '.templates.404';

    // Check if that view exists
    if (view()->exists($theme_404_view)) {
        return response()->view($theme_404_view, [
            'theme_directory' => $theme_directory,
            'site_name' => $settings->site_name ?? config('app.name'),
        ], 404);
    }

    // Fallback to Laravel's default 404 page
    return response()->view('errors.404', [], 404);
}

return parent::render($request, $exception);

}

Step 2: Keep your controller clean Now you can safely write:

$article = Article::visible()->where('slug', $slug)->firstOrFail();

return view( 'themes/' . $this->theme_directory . '/templates/article', array_merge($this->data, [ 'article' => $article ]) );

If the article doesn’t exist, the ModelNotFoundException is thrown, automatically caught by your global handler, and the correct theme-specific 404.blade.php is rendered.

No redirect('/404') needed.

Step 3 (Optional): Make it even more flexible If you want to handle all 404s (including missing routes, pages, etc.), also register your theme 404 logic in a fallback route in routes/web.php:

Route::fallback(function () { $settings = App\Models\Settings::first(); $theme_directory = $settings->theme_directory ?? 'default'; $theme_404_view = 'themes.' . $theme_directory . '.templates.404';

if (view()->exists($theme_404_view)) {
    return response()->view($theme_404_view, [], 404);
}

return response()->view('errors.404', [], 404);

});

This ensures even unhandled routes will show your theme’s 404.

Last updated by @muhalifanhar 1 week ago.
1
Solution selected by @ajax30

@muhalifanhar Thanks for the answer, I will test it. But please notice that there is some code displayed as text. Please fix that. Thanks!

0

Sign in to participate in this thread!

Native PHP

Your banner here too?

Razvan ajax30 Joined 2 Oct 2021

Moderators

We'd like to thank these amazing companies for supporting us

Your logo here?

Laravel.io

The Laravel portal for problem solving, knowledge sharing and community building.

© 2025 Laravel.io - All rights reserved.