notiziephp.it

Eloquent Refreshes: caricare le colonne generate dopo il salvataggio

Traduzione da Laravel News
23 Settembre 2026 Tempo di lettura: 4 minuti
Eloquent Refreshes: caricare le colonne generate dopo il salvataggio

Se create un model Eloquent e leggete subito una colonna generata, ottenete null. Quando aggiornate un model, la colonna conserva il vecchio valore finché non chiamate refresh().

Laravel 13.33 ha introdotto l'attributo #[Refreshes] per risolvere questo potenziale problema: elencate le colonne che il database calcola e Eloquent le rilegge dopo ogni insert e update.

Vediamo come usare questo trait per caricare automaticamente le colonne generate dopo il salvataggio, senza dovervi ricordare di fare refresh.

Colonne generate e model obsoleti

Prendiamo una tabella orders in cui il database calcola total a partire da altre due colonne:

Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->decimal('subtotal', 10, 2);
$table->decimal('tax', 10, 2);
$table->decimal('total', 10, 2)->storedAs('subtotal + tax');
$table->timestamps();
});

Potete interrogare e indicizzare total come qualsiasi altra colonna, ma il model che ottenete da create() non la contiene:

$order = Order::create(['subtotal' => 100, 'tax' => 8.25]);

$order->total; // null

$order->update(['tax' => 9.00]);

$order->total; // still null

Prima di questa release dovevate chiamare manualmente $order->refresh() dopo ogni scrittura, il che ricarica ogni colonna e ogni relazione già caricata.

Aggiungere l'attributo Refreshes

Caleb White ha aggiunto #[Refreshes] nella PR #61523. Mettetelo sul model ed elencate le colonne che il database imposta:

use Illuminate\Database\Eloquent\Attributes\Refreshes;
use Illuminate\Database\Eloquent\Model;

#[Refreshes('total')]
class Order extends Model
{
protected $fillable = ['subtotal', 'tax'];
}

Dopo ogni insert o update, Eloquent seleziona quelle colonne per la riga salvata e le copia sul model:

$order = Order::create(['subtotal' => 100, 'tax' => 8.25]);

$order->total; // "108.25"

$order->update(['tax' => 9.00]);

$order->total; // "109.00"

Per aggiornare più di una colonna, passate un array o più argomenti. #[Refreshes(['total', 'slug'])] e #[Refreshes('total', 'slug')] fanno la stessa cosa.

Se configurate i model con proprietà invece che con attributi, impostate $refreshes:

class Order extends Model
{
protected array $refreshes = ['total'];
}

Quando un model ha entrambi, viene usata la proprietà e l'attributo viene ignorato. Un model che non ha né l'una né l'altro non esegue nessuna query aggiuntiva.

Valori generati negli eventi del model

Il refresh viene eseguito dopo l'INSERT o l'UPDATE e prima che Eloquent scateni created o updated, quindi i listener di entrambi gli eventi vedono il nuovo valore. Un observer che invia una ricevuta può leggere il totale calcolato dal database:

class OrderObserver
{
public function created(Order $order): void
{
Mail::to($order->customer)->send(new OrderReceipt($order));
}

public function updated(Order $order): void
{
if ($order->wasChanged('total')) {
$order->customer->notify(new OrderTotalChanged($order));
}
}
}

Il controllo wasChanged('total') funziona perché Eloquent registra le modifiche dopo il refresh. Il vostro codice non imposta mai total, ma la colonna compare in getChanges() quando il database calcola un nuovo valore.

Quali scritture attivano un refresh

Il refresh viene eseguito su un'istanza del model in questi scenari:

  • Insert da create(), save(), saveQuietly() e saveOrIgnore()
  • Update da update(), save() e saveQuietly()
  • Chiamate di increment/decrement: increment(), decrement(), incrementEach() e decrementEach()

Come funziona la query di refresh

Il refresh è una sola SELECT per le colonne elencate, limitata alla primary key del model:

select `total` from `orders` where `id` = ? limit 1

La query viene eseguita sulla connessione di scrittura. Con le read replica, una replica potrebbe non avere ancora la nuova riga.

Inoltre salta i global scope, il che significa che uno scope di tenant o di soft delete non può nascondere la riga appena salvata da Eloquent. E usa firstOrFail(): se la riga è sparita nel momento in cui viene eseguita la query, il salvataggio lancia una ModelNotFoundException.

Refreshes o refresh()

#[Refreshes] è adatto alle colonne che leggete subito dopo una scrittura: colonne storedAs e virtualAs, valori impostati da un trigger e default del database che Eloquent non imposta. L'attributo aggiunge una query a ogni scrittura, quindi elencate solo le colonne che rileggete.

Chiamate $model->refresh() quando vi serve rileggere l'intera riga o le relazioni caricate. Potete anche chiamarlo per una scrittura isolata, dove aggiungere l'attributo al model metterebbe una query extra su ogni altro salvataggio.

La documentazione di Eloquent tratta l'attributo nella sezione "Refreshing Attributes After Writes".

Per approfondire

Condividi su: LinkedIn

Altri articoli recenti