amontreuil/exception-handler-bundle
Installation
composer require amontreuil/exception-handler-bundle
No additional configuration is required—exceptions are auto-loaded via PSR-4 autoloading.
First Use Case
Throw a BadRequestException for missing/invalid data:
use Amontreuil\ExceptionHandlerBundle\Exception\BadRequestException;
if (empty($request->input('required_field'))) {
throw new BadRequestException('The required data is missing.');
}
Where to Look First
src/Exception for available exceptions.400, 404, 503).Validation Errors
Use BadRequestException or BadFormPostException for form/data validation:
if (!$validator->passes()) {
throw new BadRequestException('Validation failed: ' . implode(', ', $validator->errors()));
}
API-Specific Errors
UnsupportedMediaTypeApiException
if (!$request->isJson()) {
throw new UnsupportedMediaTypeApiException('Content-Type must be application/json.');
}
BadUuidException
if (!Uuid::isValid($uuid)) {
throw new BadUuidException("Invalid UUID: {$uuid}");
}
Forbidden/Unauthorized
UnauthorizedRequestException
if (!$user->hasRole('admin')) {
throw new UnauthorizedRequestException('Access denied.');
}
ForbiddenRequestException
if ($resource->isPrivate() && !$user->owns($resource)) {
throw new ForbiddenRequestException('You do not have permission to access this resource.');
}
Duplicate Data
Use DuplicateDataException with context:
if ($existingRecord = User::where('email', $email)->first()) {
throw new DuplicateDataException("Email {$email} is already in use.");
}
Custom Rules Violations
ViolationRuleRequestException for business logic breaches:
if ($user->isSuspended()) {
throw new ViolationRuleRequestException('Account is suspended.');
}
ExceptionListener to customize responses (e.g., add metadata to JSON errors).use Symfony\Component\HttpFoundation\JsonResponse;
use Amontreuil\ExceptionHandlerBundle\Exception\ApiException;
class BaseController extends Controller {
public function handleException(ApiException $e): JsonResponse {
return new JsonResponse([
'error' => $e->getMessage(),
'status' => $e->getStatusCode(),
], $e->getStatusCode());
}
}
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage('The required data is missing.');
Namespace Conflicts
Amontreuil\ExceptionHandlerBundle\Exception namespace. Ensure no naming collisions with other packages (e.g., custom Exception classes).Status Code Deviations
452 (BadFormPostException) or 453 (BadUuidException) are non-standard. Document these in your API specs to avoid client confusion.Optional Parameters in ApiException
// Older versions might need:
new BadRequestException('Message', [], 400);
Symfony Version Lock
dd($exception->getStatusCode()) to verify the correct code is thrown.try-catch to log context:
try {
// Risky operation
} catch (BadUuidException $e) {
\Log::error("Invalid UUID {$uuid}: " . $e->getMessage());
throw $e;
}
Custom Exceptions
Extend ApiException to create project-specific exceptions:
namespace App\Exception;
use Amontreuil\ExceptionHandlerBundle\Exception\ApiException;
class InvalidPaymentException extends ApiException {
public function __construct(string $message) {
parent::__construct($message, [], 400);
}
}
Override Exception Listener
Replace the bundle’s ExceptionListener in config/services.yaml:
services:
App\EventListener\CustomExceptionListener:
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
Add Metadata to Responses
Extend the ApiException constructor to include extra data:
new BadRequestException('Invalid input.', ['field' => 'email', 'rule' => 'required'], 400);
Then customize the listener to serialize this into the response.
Localization Use Symfony’s translation system to localize messages:
throw new BadRequestException($this->translator->trans('validation.required', ['%field%' => 'email']));
How can I help you explore Laravel packages today?