---
title: "Laravel: 4+1 elementi che non devono mai mancare in AppServiceProvider"
date: 2025-02-09
lang: it
tags: [Laravel, PHP]
description: "In ogni applicazione Laravel ci sono alcuni elementi indispensabili"
---

In ogni applicazione **Laravel**, ci sono alcuni elementi che non devono mai mancare.

Solitamente vanno definiti nel metodo `boot()` di `AppServiceProvider` (o di un altro service provider, a seconda di come strutturi le tue applicazioni) che si presenta quindi molto simile a questo esempio:

```php showLineNumbers
<?php
    
declare(strict_types=1);

namespace App\Providers;

use Carbon\CarbonImmutable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Vite;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $isProduction = app()->isProduction();

        // Evita comandi distruttivi in produzione
        DB::prohibitDestructiveCommands($isProduction);

        // Abilita la modalità "strict" per i modelli in ambienti non di produzione
        Model::shouldBeStrict(!$isProduction);

        // Imposta lo schema degli URL su HTTPS
        URL::forceScheme('https');

        // Utilizza CarbonImmutable per la gestione delle date
        Date::use(CarbonImmutable::class);
        
        // Abilita il prefetching degli asset generati da Vite
        Vite::prefetch();
    }
}
```

Analizziamo gli elementi uno per uno, in modo da capire perché è opportuno usarli.

## 1. Niente comandi distruttivi per il DB

La sicurezza prima di tutto! A chi non è mai capitato, per distrazione o stanchezza, di eseguire una query o un comando convinto di essere in un ambiente locale o di test, per poi scoprire di aver eliminato dati di produzione? A me è capitato ✋🏻

`DB::prohibitDestructiveCommands($isProduction)` impedisce che vengano eseguiti comandi **distruttivi** sul **database**.  
Ok, ma quali sono questi _comandi distruttivi_, per i quali è importante proibire l'esecuzione in determinate circostanze? Quelli che fanno uso del trait `Illuminate\Console\Prohibitable`. Laravel definisce alcuni comandi come _distruttivi_: ad esempio, `db:wipe`, `migrate:fresh`, `migrate:refresh`, `migrate:reset`.

È possibile aggiungere il trait `Prohibitable` anche ai propri comandi:

```php showLineNumbers
use Illuminate\Console\Command;
use Illuminate\Console\Prohibitable;
 
class DeleteDocumentCommand extends Command
{
    use Prohibitable;

    // ...
}
```

In un service provider, è sufficiente indicare a Laravel se l'esecuzione di quel comando debba essere proibita o meno, tipicamente con una condizione che risulta vera sull'ambiente di produzione o sugli ambienti considerati "critici":

```php showLineNumbers
public function boot(): void
{
    // Impedisce l'esecuzione di questo comando in produzione
    DeleteDocumentCommand::prohibit($this->app->isProduction());
}
```

## 2. Modalità strict per i model

```php showLineNumbers
public function boot(): void
{
    // ...
    Model::shouldBeStrict(! $this->app->isProduction());    
}
```

Questa riga di codice abilita la modalità _strict_ per i model se l'applicazione non è in esecuzione in produzione. In ambienti non critici, è opportuno mantenerla disattivata, in modo che Laravel sollevi le opportune eccezioni, consentendo di correggere il codice; viceversa, sugli ambienti critici l'esecuzione del codice non si deve interrompere e non devono comparire errori.
In cosa consiste esattamente la modalità _strict_? Eseguire `Model::shouldBeStrict(...)` corrisponde a richiamare queste tre metodi:

```php showLineNumbers

Model::preventLazyLoading(); // 1.
Model::preventAccessingMissingAttributes(); // 2.
Model::preventSilentlyDiscardingAttributes(); // 3.
```

1. consente di identificare i problemi legati alle query **N+1** e al **lazy loading**: se un model dichiara una relazione, ma questa viene usata senza che sia stata caricata tramite `with()` (metodo che consente di sfruttare l'**eager loading**), nel momento in cui utilizzi tale relazione (ad esempio in un ciclo), si verifica il _lazy loading_. Laravel, per accedere ai valori di cui hai bisogno tramite la relazione, è costretto ad eseguire una query aggiuntiva per ogni record presente nella relazione: se la relazione ha ritornato N record, si traduce in N query che, sommate alla query principale sul model, fanno appunto N+1 query.  
   Disabilitando il lazy loading, su ambienti non critici (es. in locale) verrà sollevata un'eccezione: ad esempio, `Attempted to lazy load [user] on model [App\Models\Post] but lazy loading is disabled.`; su ambienti critici, invece, è opportuno lasciare abilitato il lazy loading, in modo che il codice venga comunque eseguito senza ritornare errori. Se la scelta è fra "l'applicazione in produzione si rompe e non ritorna risultati" e "l'applicazione ritorna risultati ma lentamente ed eseguendo un sacco di query", meglio il secondo caso
2. previene comportamenti indesiderati quando si aggiorna un model: ad esempio, se il model `Customer` non riporta il campo `code` nell'array `$fillable` e proviamo ad aggiornare un'istanza di quel model, referenziando `code`, verrà sollevata un'eccezione tramite la quale Laravel si lamenta e suggerisce di aggiungere `$code` all'array `$fillable`
3. evita che possano essere richiamati attributi di un model che non esistono. Ad esempio, se richiamo `$user->las_name` e l'attributo non esiste, verrà sollevata un'eccezione che ci avvisa che l'attributo non esiste o non è stato caricato per quel model. In questo esempio, si tratta di un errore di battitura; in altri casi, l'attributo potrebbe essere previsto (sul db, nel model) ma potrebbe essere stato escluso dalla query.

## 3. Forzare HTTPS

HTTPS è il protocollo standard _de facto_ sul web. Inizialmente era considerata "una sicurezza in più"; da qualche anno, ormai, è obbligatorio, tant'è che i moderni browser addirittura impediscono o rendono difficoltoso l'accesso a contenuti serviti su _HTTP_.

```php showLineNumbers
<?php

use Illuminate\Support\Facades\URL;

// nel metodo boot() di un service provider
URL::forceScheme('https');

```

Questa semplice riga di codice indica a Laravel di utilizzare **HTTPS** in tutte le rotte del vostro applicativo.

È comunque buona pratica configurare il web server che eroga il tuo applicativo Laravel affinché venga effettuato _redirect_ (permanente) sulla versione HTTPS di un URL, nel caso in cui questo venga richiesto su HTTP.

## 4. CarbonImmutable di default

**Carbon** è un package PHP molto diffuso e consente di gestire e manipolare date. La classe `Carbon`, purtroppo, nasce come mutabile (_mutable_): questo significa che, se richiamiamo un metodo su un'istanza di Carbon, il risultato modifica l'istanza iniziale. Questo può dar luogo a errori "inaspettati", dovuti al fatto che è facile scordarsi che l'istanza è _mutable_. Esempio:

```php showLineNumbers

// L'helper now() di Laravel, di default, restituisce un'istanza di Carbon
$start = now();

// ...

$end = $start->addHour();

dump($start, $end);
```

Potresti essere portato a pensare che il codice sia corretto, ovvero che `$start` contenga una specifica data/ora e `$end` sia di un'ora successiva a `$start`. In realtà, `$start` e `$end` conterranno la stessa data/ora. Provare per credere:

```php
$start->equalTo($end); // true
```

Questo si verifica proprio perché `$start` contiene un'istanza mutabile: `$start->addHour()` aggiunge sì un'ora a `$start` e ritorna il risultato al chiamante, ma causa anche l'_effetto collaterale_ di modificare il contenuto stesso di `$start`.

Vediamola sotto questa luce: Carbon può essere considerato un enorme **value object** e questi, per loro natura, dovrebbero essere **immutabili**. Se vogliamo, aver creato Carbon come oggetto mutabile può essere considerato un errore di progettazione. Per correre al riparo, è stato quindi introdotta la sua versione immutabile: **CarbonImmutable**. Ti consiglio vivamente di usarla al posto di Carbon, per evitare gli errori di cui sopra.

Per fare in modo che Laravel di default utilizzi `CarbonImmutable` per le date e, ad esempio, per evitare di doverti ricordare di usare `now()->toImmutable()`, è sufficiente fare in questo modo:

```php showLineNumbers
<?php

use Carbon\CarbonImmutable;
use Illuminate\Support\Facades\Date;

// nel metodo boot() di un service provider
Date::use(CarbonImmutable::class);
```

## 4+1. Prefetch degli asset tramite Vite

Se la tua applicazione Laravel fornisce solo endpoint che ritornano JSON, ha poco senso parlare di **Vite**: la gestione degli asset di frontend è sicuramente gestita da un'altra applicazione che invoca le API fornite dall'applicazione Laravel.

Se, invece, gestisci anche la parte di frontend generando HTML, è sicuramente utile istruire Vite affinché esegua il _prefetch_ degli asset:

```php showLineNumbers

use Illuminate\Support\Facades\Vite;

// nel metodo boot() di un service provider
Vite::prefetch();

```

Cos'è questo prefetch? **Vite** può essere configurato per suddividere gli _asset_ JavaScript e CSS in piccoli _chunk_ (porzioni), in modo che in ogni pagina venga caricato solo ciò che serve, senza portarsi dietro enormi _bundle_ di classi o funzioni che in quel contesto non servono, ma che verrebbero comunque caricati dal browser, sprecando banda e potenzialmente aumentando i tempi di attesa.

Il _prefetch_ consente di precaricare i _chunk_ Javascript e CSS generati da Vite, in modo da ridurre i tempi di attesa durante la navigazione di una Single Page Application.

Consulta la [guida di Laravel](https://laravel.com/docs/11.x/vite#asset-prefetching) e la relativa [Pull Request](https://github.com/laravel/framework/pull/52462) per ulteriori dettagli e opzioni che è possibile utilizzare. Ad esempio, è possibile specificare quale strategia usare:

```php showLineNumbers
<?php

use Illuminate\Support\Facades\Vite;

Vite::usePrefetchStrategy('aggressive');
```
