> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Gestione degli errori

> Il meccanismo di gestione delle eccezioni di Laravel. Reporting, rendering, pagine di errore personalizzate e generazione di risposte HTTP di errore.

## Panoramica

Quando crei un progetto Laravel, la gestione di errori ed eccezioni è già preconfigurata.
Personalizzi con il metodo `withExceptions` in `bootstrap/app.php`.

### Flusso di gestione delle eccezioni

Dal momento in cui viene sollevata un'eccezione fino alla risposta al client.

```mermaid theme={null}
flowchart TD
    A["Eccezione sollevata"] --> B["ExceptionHandler::report"]
    B --> C{"Da loggare?"}
    C -->|"Sì"| D["Log"]
    C -->|"No"| E["Salta"]
    D --> F["ExceptionHandler::render"]
    E --> F
    F --> G{"Tipo di richiesta"}
    G -->|"Web"| H["Pagina HTML di errore"]
    G -->|"API/JSON"| I["Response JSON di errore"]
    H --> J["Invio al client"]
    I --> J
```

```php theme={null}
// bootstrap/app.php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;

return Application::configure(basePath: dirname(__DIR__))
    ->withExceptions(function (Exceptions $exceptions): void {
        // Configura qui report e rendering delle eccezioni
    })->create();
```

L'oggetto `$exceptions` è un'istanza di `Illuminate\Foundation\Configuration\Exceptions` che gestisce l'handler dell'applicazione.

### Debug

L'opzione `debug` di `config/app.php` controlla quanto viene mostrato.
Di default segue `APP_DEBUG` in `.env`.

```ini theme={null}
# Locale
APP_DEBUG=true

# Produzione
APP_DEBUG=false
```

<Warning>
  In produzione imposta sempre `APP_DEBUG` a `false`. Lasciarlo su `true` espone dati sensibili agli utenti finali.
</Warning>

## Reporting delle eccezioni

Il reporting è la registrazione delle eccezioni in log o su servizi esterni come [Sentry](https://github.com/getsentry/sentry-laravel) o [Flare](https://flareapp.io).
Di default vanno nel log secondo `config/logging.php`.

### Callback di report personalizzate

Per gestire in modo diverso i vari tipi di eccezione, passa una closure a `report`. Laravel deduce il tipo dal type hint.

```php theme={null}
use App\Exceptions\InvalidOrderException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->report(function (InvalidOrderException $e) {
        // Notifica a servizi esterni, ecc.
    });
})
```

Anche con callback personalizzate, il log di default continua. Per fermare la propagazione chiama `stop()` o restituisci `false`.

```php theme={null}
->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->report(function (InvalidOrderException $e) {
        // ...
    })->stop();
})
```

### Helper `report()`

Per segnalare un'eccezione senza mostrare una pagina d'errore, usa `report()`.

```php theme={null}
public function isValid(string $value): bool
{
    try {
        // Logica di validazione...
    } catch (Throwable $e) {
        report($e);

        return false;
    }
}
```

<Tip>
  `report()` registra l'errore senza interrompere la risposta all'utente. Utile per background job e operazioni non critiche.
</Tip>

### Prevenire duplicati

Se la stessa istanza viene passata più volte a `report()` compaiono log duplicati. Con `dontReportDuplicates()` viene registrata solo la prima volta.

```php theme={null}
->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontReportDuplicates();
})
```

```php theme={null}
$original = new RuntimeException('Whoops!');

report($original); // Registrato

try {
    throw $original;
} catch (Throwable $caught) {
    report($caught); // Ignorato (stessa istanza)
}
```

### Contesto globale del log

Per aggiungere informazioni comuni a tutti i log di eccezioni usa `context`.
L'ID utente corrente viene aggiunto automaticamente se disponibile.

```php theme={null}
->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->context(fn () => [
        'app_version' => config('app.version'),
    ]);
})
```

### Metodo `context()` sulla classe eccezione

Definendo `context()` sulla classe eccezione includi informazioni specifiche di quella eccezione.

```php theme={null}
<?php

namespace App\Exceptions;

use Exception;

class InvalidOrderException extends Exception
{
    public function __construct(
        private readonly int $orderId,
        string $message = '',
    ) {
        parent::__construct($message);
    }

    /**
     * @return array<string, mixed>
     */
    public function context(): array
    {
        return ['order_id' => $this->orderId];
    }
}
```

### Livello di log

Per registrare certe eccezioni a un livello specifico usa `level`.

```php theme={null}
use PDOException;
use Psr\Log\LogLevel;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->level(PDOException::class, LogLevel::CRITICAL);
})
```

### Throttling delle segnalazioni

In caso di molte eccezioni puoi limitare le segnalazioni con `throttle`.

```php theme={null}
use Illuminate\Support\Lottery;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    // 1 segnalazione ogni 1000 casuali
    $exceptions->throttle(function (Throwable $e) {
        return Lottery::odds(1, 1000);
    });
})
```

Per limite al minuto usa `Limit`.

```php theme={null}
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->throttle(function (Throwable $e) {
        if ($e instanceof BroadcastException) {
            return Limit::perMinute(300);
        }
    });
})
```

## Rendering delle eccezioni

Il rendering trasforma l'eccezione in una risposta HTTP. Laravel lo fa in automatico, ma puoi personalizzare.

### Callback di rendering personalizzate

Con una closure passata a `render` trasformi l'eccezione in risposta.

```php theme={null}
use App\Exceptions\InvalidOrderException;
use Illuminate\Http\Request;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->render(function (InvalidOrderException $e, Request $request) {
        return response()->view('errors.invalid-order', status: 500);
    });
})
```

Puoi sovrascrivere anche il rendering di eccezioni integrate (come `NotFoundHttpException`).
Se la closure non ritorna nulla, viene usato il rendering di default.

```php theme={null}
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->render(function (NotFoundHttpException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Record not found.',
            ], 404);
        }
        // null → pagina 404 di default
    });
})
```

### JSON o HTML in automatico

Laravel decide in base all'header `Accept`. Per personalizzare la logica usa `shouldRenderJsonWhen`.

```php theme={null}
use Illuminate\Http\Request;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) {
        if ($request->is('admin/*')) {
            return true;
        }

        return $request->expectsJson();
    });
})
```

### Manipolare la risposta finale

Con `respond` modifichi la risposta generata.

```php theme={null}
use Symfony\Component\HttpFoundation\Response;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->respond(function (Response $response) {
        if ($response->getStatusCode() === 419) {
            return back()->with([
                'message' => 'La pagina è scaduta. Riprova.',
            ]);
        }

        return $response;
    });
})
```

## Classi eccezione personalizzate

Puoi creare eccezioni personalizzate in `app/Exceptions/`.
Se definisci `report()` e `render()` vengono chiamati automaticamente senza dover configurare `bootstrap/app.php`.

### Creare la classe eccezione

<Steps>
  <Step title="Crea la classe">
    ```shell theme={null}
    php artisan make:exception InvalidOrderException
    ```
  </Step>

  <Step title="Implementa report() e render()">
    ```php theme={null}
    <?php

    namespace App\Exceptions;

    use Exception;
    use Illuminate\Http\Request;
    use Illuminate\Http\Response;

    class InvalidOrderException extends Exception
    {
        public function __construct(
            private readonly int $orderId,
            string $message = 'Invalid order.',
        ) {
            parent::__construct($message);
        }

        public function report(): void
        {
            // Notifica a servizi esterni
        }

        public function render(Request $request): Response
        {
            return response()->view('errors.invalid-order', [
                'orderId' => $this->orderId,
            ], 422);
        }
    }
    ```
  </Step>
</Steps>

<Info>
  In `report()` puoi usare la dependency injection tramite type hint. Il service container risolve automaticamente.
</Info>

### Interfaccia `ShouldntReport`

Per eccezioni che non vanno mai segnalate implementa `ShouldntReport`.

```php theme={null}
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Contracts\Debug\ShouldntReport;

class PodcastProcessingException extends Exception implements ShouldntReport
{
    //
}
```

## Lanciare eccezioni

### Helper `abort()`

Ovunque nell'applicazione puoi generare una risposta di errore HTTP.

```mermaid theme={null}
flowchart LR
    A["abort(404)"] --> B["NotFoundHttpException"]
    C["abort(403)"] --> D["AccessDeniedHttpException"]
    E["abort(500)"] --> F["HttpException 500"]
    G["abort(422)"] --> H["UnprocessableEntityHttpException"]
    B --> I["resources/views/errors/404.blade.php"]
    D --> J["resources/views/errors/403.blade.php"]
    F --> K["resources/views/errors/500.blade.php"]
    H --> L["resources/views/errors/422.blade.php"]
```

```php theme={null}
// 404
abort(404);

// Con messaggio
abort(403, 'Non hai i permessi per questa operazione.');
```

### `abort_if()` / `abort_unless()`

Helper per lanciare eccezioni condizionali.

```php theme={null}
// Abort se la condizione è vera
abort_if(! $user->isAdmin(), 403);

// Abort se la condizione è falsa
abort_unless($user->hasPermission('edit'), 403, 'Permesso negato.');
```

<Tip>
  Comodi per i controlli di permesso in controller e middleware, spesso combinati con gate e policy.
</Tip>

## Controllo globale delle eccezioni

### Ignorare eccezioni

Escludi eccezioni dal reporting con `dontReport`. La logica di rendering personalizzata continua a funzionare.

```php theme={null}
use App\Exceptions\InvalidOrderException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontReport([
        InvalidOrderException::class,
    ]);
})
```

Condizionalmente con `dontReportWhen`.

```php theme={null}
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontReportWhen(function (Throwable $e) {
        return $e instanceof PodcastProcessingException &&
               $e->reason() === 'Subscription expired';
    });
})
```

<Info>
  Laravel ignora già di default alcune eccezioni (404, CSRF 419, mismatch origin 403, ecc.).
</Info>

### Riportare eccezioni ignorate

Con `stopIgnoring` riporti eccezioni ignorate.

```php theme={null}
use Symfony\Component\HttpKernel\Exception\HttpException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->stopIgnoring(HttpException::class);
})
```

## Pagine di errore HTTP

Puoi definire viste personalizzate per ogni codice HTTP.

### Creare viste di errore

Crea in `resources/views/errors/` template Blade con il codice HTTP come nome.

```
resources/
└── views/
    └── errors/
        ├── 404.blade.php
        ├── 403.blade.php
        └── 500.blade.php
```

Nella vista usa `$exception`.

```blade theme={null}
{{-- resources/views/errors/404.blade.php --}}
<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Pagina non trovata</title>
</head>
<body>
    <h1>404 - Pagina non trovata</h1>
    <p>{{ $exception->getMessage() }}</p>
    <a href="{{ url('/') }}">Torna alla home</a>
</body>
</html>
```

### Pubblicare i template di default

Per personalizzarli usa `vendor:publish`.

```shell theme={null}
php artisan vendor:publish --tag=laravel-errors
```

### Pagine di fallback

Crea `4xx.blade.php` e `5xx.blade.php` per fare da fallback.

```
resources/
└── views/
    └── errors/
        ├── 4xx.blade.php
        └── 5xx.blade.php
```

<Warning>
  Per `404`, `500`, `503` Laravel ha pagine di default. Per personalizzarli crea i file specifici, non il fallback.
</Warning>

## Esempio pratico: handler API

Nelle applicazioni API le eccezioni vanno restituite sempre in JSON. Esempio in `bootstrap/app.php`.

```php theme={null}
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->render(function (NotFoundHttpException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Risorsa non trovata.',
            ], 404);
        }
    });

    $exceptions->render(function (AuthenticationException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Autenticazione richiesta.',
            ], 401);
        }
    });

    $exceptions->render(function (ValidationException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Errore di validazione.',
                'errors'  => $e->errors(),
            ], 422);
        }
    });
})
```

### Eccezione API personalizzata

```php theme={null}
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class ApiException extends Exception
{
    public function __construct(
        string $message = 'An error occurred.',
        private readonly int $statusCode = 500,
        private readonly array $errors = [],
    ) {
        parent::__construct($message);
    }

    public function render(Request $request): JsonResponse
    {
        $data = ['message' => $this->getMessage()];

        if (! empty($this->errors)) {
            $data['errors'] = $this->errors;
        }

        return response()->json($data, $this->statusCode);
    }
}
```

Uso nel controller:

```php theme={null}
<?php

namespace App\Http\Controllers\Api;

use App\Exceptions\ApiException;
use App\Models\Order;

class OrderController extends Controller
{
    public function show(int $id): JsonResponse
    {
        $order = Order::find($id);

        if (! $order) {
            throw new ApiException('Ordine non trovato.', 404);
        }

        if ($order->isCancelled()) {
            throw new ApiException('Ordine già annullato.', 422);
        }

        return response()->json($order);
    }
}
```

## Riepilogo

<AccordionGroup>
  <Accordion title="Report delle eccezioni">
    | Modalità                 | Uso                                    |
    | ------------------------ | -------------------------------------- |
    | `$exceptions->report()`  | Callback per tipo di eccezione         |
    | `$exceptions->context()` | Info comuni a tutti i log              |
    | Metodo `context()`       | Info specifiche della classe eccezione |
    | Helper `report()`        | Segnala senza interrompere             |
    | `dontReportDuplicates()` | Evita duplicati                        |
    | `ShouldntReport`         | Classi mai segnalate                   |
  </Accordion>

  <Accordion title="Rendering delle eccezioni">
    | Modalità                 | Uso                            |
    | ------------------------ | ------------------------------ |
    | `$exceptions->render()`  | Response per tipo di eccezione |
    | Metodo `render()`        | Response definita nella classe |
    | `shouldRenderJsonWhen()` | Personalizza JSON/HTML         |
    | `respond()`              | Manipola la response           |
  </Accordion>

  <Accordion title="Pagine di errore">
    * Crea `resources/views/errors/404.blade.php` ecc. per attivarle
    * Usa `$exception` per i dettagli
    * `vendor:publish --tag=laravel-errors` per i template di default
    * `4xx.blade.php` / `5xx.blade.php` come fallback
  </Accordion>

  <Accordion title="Best practice in produzione">
    * `APP_DEBUG=false` sempre, per non esporre lo stack trace
    * Integra Sentry, Flare o strumenti simili
    * `throttle()` per non intasare i log
    * Mantieni un formato JSON coerente per gli errori delle API
  </Accordion>
</AccordionGroup>


## Related topics

- [Autenticazione OAuth 2.0 - Google Sheets API for Laravel](/it/packages/laravel-google-sheets/oauth.md)
- [Laravel Fetch Metadata](/it/packages/laravel-fetch-metadata.md)
- [Costruire una SPA con Inertia.js](/it/blog/inertia-introduction.md)
- [Hook di sessione](/it/packages/laravel-copilot-sdk/hooks.md)
- [Ciclo di vita della richiesta](/it/lifecycle.md)
