DEV Community

Cover image for Laravel try-catch
Anas Hussain
Anas Hussain

Posted on

Laravel try-catch

1. Understand Laravel's Built-in Exception Handling

Laravel already provides a global exception handler in app/Exceptions/Handler.php. This is the first place exceptions are caught if not handled elsewhere. You can customize this to manage exceptions application-wide.

  • Register Custom Handlers:
  public function register()
  {
      $this->reportable(function (\Throwable $e) {
          \Log::error($e->getMessage());
      });
  }
Enter fullscreen mode Exit fullscreen mode

2. Types of Exceptions You May Encounter

Here are some common exceptions to handle in Laravel:

  1. Database Exceptions:
    • QueryException: For SQL errors like syntax or constraint violations.
    • ModelNotFoundException: When findOrFail fails to retrieve a record.
   try {
       $user = User::findOrFail($id);
   } catch (ModelNotFoundException $e) {
       \Log::error('User not found: ' . $e->getMessage());
       return response()->json(['error' => 'User not found'], 404);
   }
Enter fullscreen mode Exit fullscreen mode
  1. Validation Exceptions:
    • ValidationException: When validation fails.
   try {
       $validated = $request->validate([
           'name' => 'required|string|max:255',
       ]);
   } catch (ValidationException $e) {
       return back()->withErrors($e->validator)->withInput();
   }
Enter fullscreen mode Exit fullscreen mode
  1. File System Exceptions:
    • FileNotFoundException: When a file is missing.
    • FileException: For general file system errors.
   try {
       Storage::disk('local')->delete($filename);
   } catch (FileException $e) {
       \Log::error('File operation failed: ' . $e->getMessage());
   }
Enter fullscreen mode Exit fullscreen mode
  1. Authentication Exceptions:
    • AuthenticationException: When a user is not authenticated.
    • AuthorizationException: For unauthorized access.
   try {
       $this->authorize('update', $post);
   } catch (AuthorizationException $e) {
       abort(403, 'You are not authorized to update this post.');
   }
Enter fullscreen mode Exit fullscreen mode
  1. HTTP Client Exceptions:
    • RequestException: For errors in HTTP requests using Guzzle or Laravel's HTTP client.
   try {
       $response = Http::get('https://example.com/api');
   } catch (RequestException $e) {
       \Log::error('API call failed: ' . $e->getMessage());
   }
Enter fullscreen mode Exit fullscreen mode

3. Best Practices for Using try-catch

A. Catch Specific Exceptions

Always catch specific exceptions rather than the generic Exception or Throwable. This ensures you can handle different errors appropriately.

try {
    $data = SomeModel::findOrFail($id);
} catch (ModelNotFoundException $e) {
    return response()->json(['error' => 'Record not found'], 404);
} catch (\Exception $e) {
    return response()->json(['error' => 'An unexpected error occurred'], 500);
}
Enter fullscreen mode Exit fullscreen mode

B. Use Exception Messages and Codes

Include custom error messages and HTTP status codes for meaningful responses.

try {
    $response = Http::post('https://api.example.com/data', $data);
} catch (RequestException $e) {
    return response()->json(['error' => 'Failed to connect to API'], 503);
}
Enter fullscreen mode Exit fullscreen mode

C. Log Exceptions

Use Laravel's Log facade to record exceptions for debugging purposes. Always include context.

try {
    $user = User::findOrFail($id);
} catch (ModelNotFoundException $e) {
    \Log::error('User retrieval failed', ['id' => $id, 'error' => $e->getMessage()]);
}
Enter fullscreen mode Exit fullscreen mode

D. Fallbacks and Recovery

Design fallback mechanisms where applicable, such as retrying operations or defaulting to safe behavior.

try {
    $data = Cache::remember('data_key', 60, function () {
        return SomeModel::all();
    });
} catch (QueryException $e) {
    \Log::error('Database query failed: ' . $e->getMessage());
    $data = []; // Return an empty dataset as a fallback.
}
Enter fullscreen mode Exit fullscreen mode

E. Avoid Overuse

Not every piece of code needs a try-catch block. Use it only for code that is prone to failure, such as database queries, file operations, or external API calls.


4. Advanced Techniques for Robustness

A. Custom Exception Classes

Define your own exception classes for specific scenarios.

class UserNotActiveException extends \Exception
{
    public function __construct($message = "User account is not active.")
    {
        parent::__construct($message, 403);
    }
}
Enter fullscreen mode Exit fullscreen mode

B. Centralized Exception Handling

Leverage Handler.php for centralizing logic for common exceptions.

public function render($request, Throwable $exception)
{
    if ($exception instanceof ModelNotFoundException) {
        return response()->json(['error' => 'Resource not found'], 404);
    }

    return parent::render($request, $exception);
}
Enter fullscreen mode Exit fullscreen mode

C. Retry Mechanism

Laravel's retry() helper can attempt an operation multiple times before failing.

use Illuminate\Support\Facades\Retry;

$response = retry(3, function () {
    return Http::get('https://example.com');
}, 100);
Enter fullscreen mode Exit fullscreen mode

D. Third-Party Monitoring

Integrate tools like Sentry or Bugsnag for advanced error monitoring and reporting.


5. Testing Exception Scenarios

  • Write Unit Tests: Mock exceptions to ensure your try-catch blocks work as intended.
  public function testDeleteNonExistentRecord()
  {
      $this->expectException(ModelNotFoundException::class);

      $this->controller->delete(999); // Assuming 999 does not exist.
  }
Enter fullscreen mode Exit fullscreen mode
  • Simulate Failures: Test database, file, or API failures in a controlled environment.

6. Example: Comprehensive Use of try-catch

public function store(Request $request)
{
    try {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
        ]);

        $user = User::create($validated);

        return response()->json(['message' => 'User created successfully', 'user' => $user], 201);

    } catch (ValidationException $e) {
        return response()->json(['error' => 'Validation failed', 'details' => $e->errors()], 422);

    } catch (QueryException $e) {
        \Log::error('Database error: ' . $e->getMessage());
        return response()->json(['error' => 'Database error occurred'], 500);

    } catch (\Exception $e) {
        \Log::error('Unexpected error: ' . $e->getMessage());
        return response()->json(['error' => 'An unexpected error occurred'], 500);
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)