Cria exceções customizadas renderable e reportable para HTTP e logging em Laravel. Use quando precisar criar exception handlers, exceções de domínio, ou melhorar o tratamento de erros.
From laravel-toolkitnpx claudepluginhub aronpc/ai --plugin laravel-toolkitThis skill is limited to using the following tools:
Executes pre-written implementation plans: critically reviews, follows bite-sized steps exactly, runs verifications, tracks progress with checkpoints, uses git worktrees, stops on blockers.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Guides idea refinement into designs: explores context, asks questions one-by-one, proposes approaches, presents sections for approval, writes/review specs before coding.
Cria exceções customizadas com interfaces renderable/reportable para HTTP responses e logging.
| Skill | Quando usar junto |
|---|---|
architecture | Para exceções de domínio na arquitetura |
actions | Para tratamento em Actions |
testing | Para testar exceções |
qa | Para verificar tratamento de erros |
Use esta skill sempre que:
CRÍTICO: Use exceções especializadas para erros de domínio específicos - NUNCA use Exception genérico.
render() para respostas HTTP customizadasreport() para logging/monitoramento__() para mensagens de exceção (EN, ES, PT-BR)app/
├── Exceptions/
│ ├── Handler.php # Global exception handler
│ ├── Business/
│ │ ├── BusinessLimitExceededException.php
│ │ ├── BusinessNotFoundException.php
│ │ └── InvalidBusinessTypeException.php
│ ├── Tenant/
│ │ ├── TenantInactiveException.php
│ │ ├── TenantSuspendedException.php
│ │ └── UnauthorizedTenantAccessException.php
│ ├── Menu/
│ │ ├── MenuItemNotFoundException.php
│ │ ├── InvalidPriceException.php
│ │ └── MenuItemUnavailableException.php
│ └── Billing/
│ ├── InsufficientCreditsException.php
│ ├── PlanLimitExceededException.php
│ └── PaymentFailedException.php
php artisan make:exception Business/BusinessLimitExceededException
<?php
declare(strict_types=1);
namespace App\Exceptions\Business;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
final class BusinessLimitExceededException extends Exception
{
/**
* Create a new exception instance.
*/
public function __construct(
public readonly int $currentCount,
public readonly int $maxAllowed,
public readonly string $planName,
) {
parent::__construct(
message: __('exceptions.business_limit_exceeded', [
'current' => $this->currentCount,
'max' => $this->maxAllowed,
'plan' => $this->planName,
]),
code: 403
);
}
/**
* Render exception as an HTTP response.
*/
public function render(Request $request): JsonResponse|RedirectResponse
{
if ($request->expectsJson()) {
return response()->json([
'message' => $this->getMessage(),
'current_count' => $this->currentCount,
'max_allowed' => $this->maxAllowed,
'plan_name' => $this->planName,
], 403);
}
return back()->with('error', $this->getMessage());
}
/**
* Report exception (logging/monitoramento).
*/
public function report(): void
{
// Log or send to monitoring service
\Log::warning('Business limit exceeded', [
'current' => $this->currentCount,
'max' => $this->maxAllowed,
'plan' => $this->planName,
]);
}
}
<?php
declare(strict_types=1);
namespace App\Actions\Business;
use App\DataObjects\Business\CreateBusinessData;
use App\Exceptions\Business\BusinessLimitExceededException;
use App\Models\Business;
use App\Models\Tenant;
use Lorisleiva\Actions\Concerns\AsAction;
final class CreateBusinessAction
{
use AsAction;
public function handle(Tenant $tenant, CreateBusinessData $data): Business
{
// Check business limit
$currentCount = $tenant->businesses()->count();
$maxAllowed = $tenant->plan->limits['businesses'] ?? 0;
if ($currentCount >= $maxAllowed) {
throw new BusinessLimitExceededException(
currentCount: $currentCount,
maxAllowed: $maxAllowed,
planName: $tenant->plan->name,
);
}
// Create business
return $tenant->businesses()->create($data->toArray());
}
}
// When resource not found
throw new BusinessNotFoundException($id);
// When validation fails
throw new InvalidBusinessTypeException($type);
// When limit exceeded
throw new BusinessLimitExceededException($current, $max, $plan);
// When tenant access denied
throw new UnauthorizedTenantAccessException($tenantId);
// When tenant is inactive
throw new TenantInactiveException($tenant);
// When tenant is suspended
throw new TenantSuspendedException($tenant, $reason);
// When credits insufficient
throw new InsufficientCreditsException($required, $available);
// When plan limit exceeded
throw new PlanLimitExceededException($resource, $limit);
// When payment fails
throw new PaymentFailedException($transactionId, $reason);
| Tipo | Convenção | Exemplo |
|---|---|---|
| Not Found | *NotFoundException | BusinessNotFoundException |
| Invalid Input | Invalid*Exception | InvalidBusinessTypeException |
| Limit Exceeded | *LimitExceededException | BusinessLimitExceededException |
| Unauthorized | Unauthorized*Exception | UnauthorizedTenantAccessException |
| Payment | Payment*Exception | PaymentFailedException |
| Resource State | *InactiveException | TenantInactiveException |
| Business Rule | Nome descritivo | MenuItemUnavailableException |
// lang/en/exceptions.php
return [
'business_limit_exceeded' => 'Business limit exceeded. You have :current businesses, but your :plan plan allows only :max.',
'business_not_found' => 'Business not found.',
'invalid_business_type' => 'Invalid business type: :type',
'tenant_inactive' => 'Your account is inactive. Please contact support.',
'tenant_suspended' => 'Your account has been suspended. Reason: :reason',
'insufficient_credits' => 'Insufficient credits. Required: :required, Available: :available',
];
// lang/es/exceptions.php
return [
'business_limit_exceeded' => 'Límite de negocios excedido. Tienes :current negocios, pero tu plan :plan permite solo :max.',
// ...
];
// lang/pt_BR/exceptions.php
return [
'business_limit_exceeded' => 'Limite de negócios excedido. Você tem :current negócios, mas seu plano :plan permite apenas :max.',
// ...
];
| Exception Type | HTTP Status | Descrição |
|---|---|---|
| NotFoundException | 404 | Resource not found |
| Unauthorized | 403 | Access denied |
| ValidationException | 422 | Validation failed |
| LimitExceeded | 403 | Resource limit exceeded |
| PaymentFailed | 402 | Payment required |
| InactiveException | 403 | Account/resource inactive |
| SuspendedException | 403 | Account suspended |
<?php
use App\Actions\Business\CreateBusinessAction;
use App\Exceptions\Business\BusinessLimitExceededException;
it('throws exception when business limit exceeded', function () {
$tenant = Tenant::factory()->create(['plan_id' => 1]);
$tenant->plan->update(['limits' => ['businesses' => 2]]);
// Create 2 businesses (at limit)
Business::factory()->count(2)->create(['tenant_id' => $tenant->id]);
// Try to create 3rd business
$data = CreateBusinessData::fromArray([
'name' => 'New Business',
'type' => 'restaurant',
]);
expect(fn() => CreateBusinessAction::run($tenant, $data))
->toThrow(BusinessLimitExceededException::class, 'Business limit exceeded');
});
it('includes exception context in response', function () {
$tenant = Tenant::factory()->create(['plan_id' => 1]);
Business::factory()->count(2)->create(['tenant_id' => $tenant->id]);
try {
CreateBusinessAction::run($tenant, $data);
} catch (BusinessLimitExceededException $e) {
expect($e->currentCount)->toBe(2)
->and($e->maxAllowed)->toBe(2)
->and($e->planName)->toBe('Basic');
}
});
<?php
declare(strict_types=1);
namespace App\Exceptions;
use App\Exceptions\Business\BusinessLimitExceededException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
final class Handler extends ExceptionHandler
{
/**
* A list of exception types with their corresponding custom log levels.
*/
protected $levels = [
BusinessLimitExceededException::class => 'warning',
];
/**
* A list of exception types that are not reported.
*/
protected $dontReport = [
// Exceptions that shouldn't be logged
];
/**
* Register exception handling callbacks.
*/
public function register(): void
{
$this->reportable(function (Throwable $e) {
// Send to monitoring service (Sentry, Bugsnag, etc.)
});
}
}
render() para respostas HTTP customizadasreport() para logging/monitoramentoException genérico sem motivo claro✅ Violações de regras de negócio (limits, constraints)
✅ Erros de domínio específicos (invalid state transitions)
✅ Falhas de autorização (tenant access, ownership)
✅ Erros de pagamento/billing
✅ Resource not found (quando 404 genérico não é suficiente)
❌ Erros de validação (use Form Requests)
❌ Erros de conexão de banco (framework handles)
❌ Erros genéricos (use built-in exceptions)
laravel-i18n para traduções de mensagens de erro em EN, ES, PT-BRlaravel-architecture e laravel-actions-events)laravel-testing-pest para testar exceções sendo lançadas