Introduction
Error handling is critical for building robust and user-friendly applications. In this post, we’ll explore three approaches to exception handling in Laravel and focus on configuring centralized exception handling in the bootstrap/app.php
file using withExceptions
.
Why Centralized Error Handling?
- Keeps the code DRY by avoiding repetitive
try-catch
blocks. - Enhances maintainability by consolidating error handling logic in one place.
- Improves user experience with consistent error responses.
Exploring Approaches to Exception Handling
1. Exception Handling per Method
- Handle exceptions locally within each method.
- Drawback: Repetitive code and harder to maintain.
Example:
public function store(Request $request) {
try {
// Code logic here
} catch (QueryException $e) {
return back()->with('error', 'Database error occurred.');
}
}
2. Exception Handling in bootstrap/app.php
- Centralized error handling at the application level.
- Flexible, DRY, and maintainable.
- Focus of this tutorial.
3. Custom Middleware for Exception Handling
- Ideal for handling specific exceptions for routes or groups.
- Adds complexity when handling application-wide errors.
Configuring Exception Handling in bootstrap/app.php
Step 1: Update the bootstrap/app.php
File
Use Laravel's Application
class to set up centralized exception handling with withExceptions
.
Here’s the complete code snippet:
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use App\Http\Middleware\EnsureAdminIsAuthenticated;
use Illuminate\Http\Request;
use Illuminate\Database\QueryException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__ . '/../routes/web.php',
api: __DIR__ . '/../routes/api.php',
commands: __DIR__ . '/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
"admin_auth" => EnsureAdminIsAuthenticated::class,
]);
})
->withExceptions(function (Exceptions $exceptions) {
// Handling database query exceptions
$exceptions->render(function (QueryException $e, Request $request) {
Log::error('Database error: ' . $e->getMessage(), [
'error' => $e->getMessage(),
'url' => $request->fullUrl(),
'user_id' => auth()->check() ? auth()->id() : 'guest',
]);
return redirect()->back()->with('warning', 'A database error occurred. Please try again.');
});
// Handling not found exceptions
$exceptions->render(function (NotFoundHttpException $e, Request $request) {
if ($e->getPrevious() instanceof ModelNotFoundException) {
Log::error('Model not found: ' . $e->getMessage(), [
'error' => $e->getMessage(),
'url' => $request->fullUrl(),
'user_id' => auth()->check() ? auth()->id() : 'guest',
]);
return redirect()->back()->with('warning', 'Requested resource not found.');
}
});
// Handling validation exceptions
$exceptions->render(function (ValidationException $e, Request $request) {
Log::error('Validation error: ' . $e->getMessage(), [
'user_id' => auth()->id(),
'url' => $request->fullUrl(),
'input' => $request->all(),
]);
return redirect()->back()->withErrors($e->validator)->withInput();
});
// Handling generic exceptions
$exceptions->render(function (Exception $e, Request $request) {
Log::error('An unexpected error occurred.', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'url' => $request->fullUrl(),
'user_id' => auth()->check() ? auth()->id() : 'guest',
]);
return redirect()->back()->with('warning', 'An unexpected error occurred. Please try again.');
});
})->create();
Step 2: Explanation of Each Exception
QueryException
- Catches database-related errors.
- Logs the error details and redirects users back with a warning.
ModelNotFoundException
- Handles cases when a model cannot be found.
- Logs the error and displays a user-friendly message.
ValidationException
- Captures validation errors and sends them back to the form.
- Logs the input data and errors for debugging.
Generic Exception
- Catches all other unhandled exceptions.
- Logs the error message and stack trace for further investigation.
Step 3: Testing the Configuration
- Trigger each type of exception (e.g., invalid form data, nonexistent routes).
- Verify the logs contain detailed error information.
- Ensure the user sees friendly and consistent error messages.
Advantages of This Approach
- Centralized Control: All exception logic in one place.
- Improved User Experience: Users are redirected with helpful messages.
- Debugging Made Easy: Logs provide detailed context for errors.
Conclusion
Centralized exception handling in bootstrap/app.php
is an efficient way to manage errors in a Laravel application. By following this guide, you can handle exceptions systematically, maintain clean controllers, and enhance user experience.
Top comments (0)