Prologo
Primi Passi
Architettura
Le Basi
- Routing
- Middleware
- Protezione da CSRF
- Controller
- Richieste
- Risposte
- Views
- Blade
- Vite
- URL
- Sessioni
- Validazione
- Errori
- Logging
Approfondimenti
- Artisan
- Broadcasting
- Cache
- Collezioni
- Concorrenza
- Contesto
- Contratti
- Eventi
- File System
- Helpers
- Client HTTP
- Localizzazione
- Notifiche
- Sviluppo di Package
- Processi
- Code
- Rate-limiting
- Stringhe
- Scheduling di Task
Sicurezza
Database
Eloquent ORM
Testing
Package
Primi Passi con Eloquent
- Introduzione
- Generare classi modello
- Convenzioni del Modello Eloquent
- Recuperare i Modelli
- Ottenere Modelli Singoli / Aggregati
- Inserimento e aggiornamento dei Model
- Eliminare i modelli
- Pruning dei Modelli
- Replicare i Modelli
- Scope delle Query
- Confrontare i Modelli
- Eventi
Introduzione
Laravel include Eloquent, un object-relational mapper (ORM) che rende piacevole interagire con il tuo database. Quando usi Eloquent, ogni tabella del database ha un "Model" corrispondente che viene utilizzato per interagire con quella tabella. Oltre a recuperare record dalla tabella del database, i modelli Eloquent ti permettono anche di inserire, aggiornare e cancellare record dalla tabella.
Prima di iniziare, assicurati di configurare una connessione al database nel file di configurazione
config/database.php
della tua applicazione. Per ulteriori informazioni sulla configurazione del database, consulta la documentazione sulla configurazione del database.
Laravel Bootcamp
Se sei nuovo a Laravel, puoi iniziare con il Laravel Bootcamp. Il Laravel Bootcamp ti guiderà nella creazione della tua prima applicazione Laravel usando Eloquent. È un ottimo modo per scoprire tutto ciò che Laravel ed Eloquent offrono.
Generare classi modello
Per iniziare, creiamo un modello Eloquent. I modelli solitamente si trovano nella directory app\Models
e estendono la classe Illuminate\Database\Eloquent\Model
. Puoi usare il comando make:model
di Artisan per generare un nuovo modello:
php artisan make:model Flight
Se vuoi generare una migrazione del database quando crei il modello, puoi usare l’opzione --migration
o -m
:
php artisan make:model Flight --migration
Puoi generare diversi altri tipi di classi quando crei un modello, come factory, seeders, policies, controller e form request. Inoltre, queste opzioni possono essere combinate per creare più classi contemporaneamente:
# Crea un modello e una classe FlightFactory...
php artisan make:model Flight --factory
php artisan make:model Flight -f
# Genera un modello e una classe FlightSeeder...
php artisan make:model Flight --seed
php artisan make:model Flight -s
# Genera un modello e una classe FlightController...
php artisan make:model Flight --controller
php artisan make:model Flight -c
# Genera un modello, la classe risorsa FlightController e le classi form request...
php artisan make:model Flight --controller --resource --requests
php artisan make:model Flight -crR
# Genera un modello e una classe FlightPolicy...
php artisan make:model Flight --policy
# Genera un modello e una migrazione, factory, seeder e controller...
php artisan make:model Flight -mfsc
# Scorciatoia per generare un modello, migrazione, factory, seeder, policy, controller e richieste di form...
php artisan make:model Flight --all
php artisan make:model Flight -a
# Genera un modello pivot...
php artisan make:model Member --pivot
php artisan make:model Member -p
Ispezionare i Modelli
A volte può essere difficile determinare tutti gli attributi e le relazioni disponibili di un modello semplicemente scorrendone il codice. Invece, prova il comando Artisan model:show
, che fornisce una panoramica comoda di tutti gli attributi e le relazioni del modello:
php artisan model:show Flight
Convenzioni del Modello Eloquent
I modelli creati con il comando make:model
vengono inseriti nella cartella app/Models
. Vediamo una classe modello di base e parliamo di alcune delle principali convenzioni di Eloquent:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
// ...
}
Nomi delle Tabelle
Dopo aver visto l’esempio sopra, potresti aver notato che non abbiamo detto a Eloquent quale tabella del database corrisponde al nostro modello Flight
. Per convenzione, il nome della classe al plurale e in "snake case" viene usato come nome della tabella, a meno che non venga specificato un altro nome. Quindi, in questo caso, Eloquent presumerà che il modello Flight
memorizzi i record nella tabella flights
, mentre un modello AirTrafficController
memorizzerebbe i record nella tabella air_traffic_controllers
.
Se la tabella del database corrispondente al tuo modello non segue questa convenzione, puoi specificare manualmente il nome della tabella definendo una proprietà table
nel modello:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'my_flights';
}
Chiavi Primarie
Eloquent presumerà anche che ogni tabella del database corrispondente al modello abbia una colonna chiave primaria chiamata id
. Se necessario, puoi definire una proprietà protetta $primaryKey
nel tuo modello per specificare una colonna diversa che funge da chiave primaria del tuo modello:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* La chiave primaria associata alla tabella.
*
* @var string
*/
protected $primaryKey = 'flight_id';
}
Inoltre, Eloquent presume che la chiave primaria sia un valore intero incrementale, il che significa che Eloquent convertirà automaticamente la chiave primaria in un intero. Se desideri utilizzare una chiave primaria non incrementale o non numerica, devi definire una proprietà pubblica $incrementing
nel tuo modello impostata su false
:
<?php
class Flight extends Model
{
/**
* Indica se l'ID del modello è auto-incrementante.
*
* @var bool
*/
public $incrementing = false;
}
Se la chiave primaria del tuo modello non è un intero, dovresti definire una proprietà protetta $keyType
nel tuo modello. Questa proprietà dovrebbe avere il valore string
:
<?php
class Flight extends Model
{
/**
* Il tipo di dato dell'ID della chiave primaria.
*
* @var string
*/
protected $keyType = 'string';
}
Chiavi Primarie "Composite"
Eloquent richiede che ogni modello abbia almeno un "ID" che identifichi univocamente e possa fungere da chiave primaria. Le chiavi primarie "composite" non sono supportate dai modelli Eloquent. Tuttavia, puoi aggiungere indici unici su più colonne alle tue tabelle del database oltre alla chiave primaria unica della tabella.
UUID e chiavi ULID
Invece di utilizzare interi autoincrementanti come chiavi primarie del tuo modello Eloquent, puoi scegliere di usare UUID. Gli UUID sono identificatori alfanumerici univoci a livello globale lunghi 36 caratteri.
Se desideri che un modello utilizzi una chiave UUID invece di una chiave intera autoincrementante, puoi usare il trait Illuminate\Database\Eloquent\Concerns\HasUuids
nel modello. Naturalmente, dovresti assicurarti che il modello abbia una colonna chiave primaria equivalente a UUID:
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use HasUuids;
// ...
}
$article = Article::create(['title' => 'Traveling to Europe']);
$article->id; // "8f8e8478-9035-4d23-b9a7-62f4d2612ce5"
Per impostazione predefinita, il trait HasUuids
genererà "UUID ordinati" per i tuoi modelli. Questi UUID sono più efficienti per l’archiviazione indicizzata nel database perché possono essere ordinati lessicograficamente.
Puoi sovrascrivere il processo di generazione UUID per un determinato modello definendo un metodo newUniqueId
nel modello. Inoltre, puoi specificare quali colonne devono ricevere UUID definendo un metodo uniqueIds
nel modello:
use Ramsey\Uuid\Uuid;
/**
* Generate a new UUID for the model.
*/
public function newUniqueId(): string
{
return (string) Uuid::uuid4();
}
/**
* Get the columns that should receive a unique identifier.
*
* @return array<int, string>
*/
public function uniqueIds(): array
{
return ['id', 'discount_code'];
}
Se lo desideri, puoi scegliere di utilizzare "ULID" invece degli UUID. Gli ULID sono simili agli UUID; tuttavia, sono lunghi solo 26 caratteri. Come gli UUID ordinati, gli ULID possono essere ordinati lessicograficamente per un’efficiente indicizzazione nel database. Per utilizzare gli ULID, dovresti usare il trait Illuminate\Database\Eloquent\Concerns\HasUlids
nel tuo modello. Dovresti inoltre assicurarti che il modello abbia una colonna chiave primaria equivalente a ULID:
use Illuminate\Database\Eloquent\Concerns\HasUlids;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use HasUlids;
// ...
}
$article = Article::create(['title' => 'Traveling to Asia']);
$article->id; // "01gd4d3tgrrfqeda94gdbtdk5c"
Timestamp
Per impostazione predefinita, Eloquent si aspetta che esistano le colonne created_at
e updated_at
nella tabella del database corrispondente al tuo modello. Eloquent imposterà automaticamente i valori di queste colonne quando i modelli vengono creati o aggiornati. Se non desideri che queste colonne vengano gestite automaticamente da Eloquent, dovresti definire una proprietà $timestamps
nel tuo modello con valore false
:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* Indica se il modello deve avere timestamp.
*
* @var bool
*/
public $timestamps = false;
}
Se hai bisogno di personalizzare il formato dei timestamp del tuo modello, imposta la proprietà $dateFormat
nel tuo modello. Questa proprietà determina come gli attributi di data vengono memorizzati nel database e il loro formato quando il modello viene serializzato in un array o JSON:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* Il formato di memorizzazione delle colonne di data del modello.
*
* @var string
*/
protected $dateFormat = 'U';
}
Se hai bisogno di personalizzare i nomi delle colonne usate per memorizzare i timestamp, puoi definire le costanti CREATED_AT
e UPDATED_AT
nel tuo modello:
<?php
class Flight extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';
}
Se desideri eseguire operazioni sul modello senza che il timestamp updated_at
venga modificato, puoi operare sul modello all’interno di una closure fornita al metodo withoutTimestamps
:
Model::withoutTimestamps(fn () => $post->increment('reads'));
Connessioni al Database
Per impostazione predefinita, tutti i modelli Eloquent utilizzeranno la connessione al database predefinita configurata per la tua applicazione. Se desideri specificare una connessione diversa da utilizzare quando interagisci con un determinato modello, dovresti definire una proprietà $connection
nel modello:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* La connessione al database che deve essere utilizzata dal modello.
*
* @var string
*/
protected $connection = 'mysql';
}
Valori Predefiniti degli Attributi
Di default, una nuova istanza di un modello non conterrà valori per gli attributi. Se desideri definire i valori predefiniti per alcuni attributi del tuo modello, puoi definire una proprietà $attributes
nel tuo modello. I valori degli attributi inseriti nell’array $attributes
dovrebbero essere nel loro formato grezzo, "memorizzabile", come se fossero appena letti dal database:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* I valori predefiniti degli attributi del modello.
*
* @var array
*/
protected $attributes = [
'options' => '[]',
'delayed' => false,
];
}
Configurare la Rigorosità di Eloquent
Laravel offre diversi metodi che ti permettono di configurare il comportamento e la "rigorosità" di Eloquent in varie situazioni.
Innanzitutto, il metodo preventLazyLoading
accetta un argomento booleano opzionale che indica se si dovrebbe prevenire il lazy loading. Ad esempio, potresti voler disabilitare il lazy loading solo in ambienti non di produzione in modo che il tuo ambiente di produzione continui a funzionare normalmente anche se accidentalmente è presente una relazione caricata in lazy nel codice di produzione. Tipicamente, questo metodo dovrebbe essere invocato nel metodo boot
del AppServiceProvider
della tua applicazione:
use Illuminate\Database\Eloquent\Model;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Model::preventLazyLoading(! $this->app->isProduction());
}
Inoltre, puoi istruire Laravel a lanciare un’eccezione quando tenti di compilare un attributo non compilabile invocando il metodo preventSilentlyDiscardingAttributes
. Questo può aiutare a prevenire errori inaspettati durante lo sviluppo locale quando si tenta di impostare un attributo che non è stato aggiunto all’array fillable
del modello:
Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction());
Recuperare i Modelli
Dopo aver creato un modello e la sua tabella di database associata, puoi iniziare a recuperare dati dal database. Ogni modello Eloquent può essere visto come un potente costruttore di query che permette di interrogare facilmente la tabella collegata al modello. Il metodo all
del modello recupera tutti i record dalla tabella di database associata:
use App\Models\Flight;
foreach (Flight::all() as $flight) {
echo $flight->name;
}
Costruire Query
Il metodo all
di Eloquent restituirà tutti i risultati nella tabella del modello. Tuttavia, poiché ogni modello Eloquent funge da query builder, puoi aggiungere ulteriori restrizioni alle query e poi chiamare il metodo get
per recuperare i risultati:
$flights = Flight::where('active', 1)
->orderBy('name')
->take(10)
->get();
Poiché i modelli Eloquent sono dei query builder, dovresti esaminare tutti i metodi offerti dal query builder di Laravel. Puoi usare qualsiasi di questi metodi quando scrivi le tue query Eloquent.
Aggiornamento dei Modelli
Se hai già un’istanza di un modello Eloquent recuperata dal database, puoi "aggiornare" il modello utilizzando i metodi fresh
e refresh
. Il metodo fresh
riottiene il modello dal database. L’istanza del modello esistente non sarà influenzata:
$flight = Flight::where('number', 'FR 900')->first();
$freshFlight = $flight->fresh();
Il metodo refresh
riidrata il modello esistente utilizzando dati aggiornati dal database. Inoltre, tutte le relazioni caricate verranno aggiornate:
$flight = Flight::where('number', 'FR 900')->first();
$flight->number = 'FR 456';
$flight->refresh();
$flight->number; // "FR 900"
Collezioni
Come abbiamo visto, i metodi Eloquent come all
e get
recuperano più record dal database. Tuttavia, questi metodi non restituiscono un semplice array PHP. Invece, viene restituita un’istanza di Illuminate\Database\Eloquent\Collection
.
La classe Collection
di Eloquent estende la classe base di Laravel Illuminate\Support\Collection
, che fornisce una varietà di metodi utili per interagire con le collezioni di dati. Ad esempio, il metodo reject
può essere usato per rimuovere modelli da una collezione basandosi sul risultato di una closure invocata:
$flights = Flight::where('destination', 'Paris')->get();
$flights = $flights->reject(function (Flight $flight) {
return $flight->cancelled;
});
Oltre ai metodi forniti dalla classe base delle collezioni di Laravel, la classe delle collezioni Eloquent offre alcuni metodi extra pensati specificamente per interagire con collezioni di modelli Eloquent.
Poiché tutte le collezioni di Laravel implementano le interfacce iterable di PHP, puoi iterare sulle collezioni come se fossero un array:
foreach ($flights as $flight) {
echo $flight->name;
}
Suddivisione dei Risultati
La tua applicazione potrebbe esaurire la memoria se tenti di caricare decine di migliaia di record Eloquent utilizzando i metodi all
o get
. Invece di usare questi metodi, puoi utilizzare il metodo chunk
per elaborare un gran numero di modelli in modo più efficiente.
Il metodo chunk
recupera un sottoinsieme di modelli Eloquent, passando loro una closure per l’elaborazione. Poiché viene recuperato solo il blocco corrente di modelli Eloquent alla volta, il metodo chunk
consente di ridurre significativamente l’uso della memoria quando si lavora con un gran numero di modelli:
use App\Models\Flight;
use Illuminate\Database\Eloquent\Collection;
Flight::chunk(200, function (Collection $flights) {
foreach ($flights as $flight) {
// ...
}
});
Il primo argomento passato al metodo chunk
è il numero di record che desideri ricevere per "blocco". La closure passata come secondo argomento verrà invocata per ogni blocco recuperato dal database. Verrà eseguita una query al database per recuperare ogni blocco di record passato alla closure.
Se stai filtrando i risultati del metodo chunk
basandoti su una colonna che aggiornerai anche durante l’iterazione, dovresti usare il metodo chunkById
. Utilizzare il metodo chunk
in questi casi potrebbe portare a risultati inaspettati e incoerenti. Internamente, il metodo chunkById
recupera sempre i modelli con una colonna id
maggiore dell’ultimo modello nel blocco precedente:
Flight::where('departed', true)
->chunkById(200, function (Collection $flights) {
$flights->each->update(['departed' => false]);
}, column: 'id');
Poiché i metodi chunkById
e lazyById
aggiungono le proprie condizioni "where" alla query in esecuzione, dovresti solitamente raggruppare logicamente le tue condizioni all’interno di una closure:
Flight::where(function ($query) {
$query->where('delayed', true)->orWhere('cancelled', true);
})->chunkById(200, function (Collection $flights) {
$flights->each->update([
'departed' => false,
'cancelled' => true
]);
}, column: 'id');
Suddivisione in blocchi usando Lazy Collections
Il metodo lazy
funziona in modo simile al metodo chunk
in quanto esegue la query a blocchi. Tuttavia, invece di passare ogni blocco direttamente a una callback, il metodo lazy
restituisce una LazyCollection
appiattita di modelli Eloquent, permettendoti di interagire con i risultati come un flusso unico:
use App\Models\Flight;
foreach (Flight::lazy() as $flight) {
// ...
}
Se stai filtrando i risultati del metodo lazy
basandoti su una colonna che aggiornerai anche durante l’iterazione sui risultati, dovresti usare il metodo lazyById
. Internamente, il metodo lazyById
recupererà sempre modelli con una colonna id
maggiore dell’ultimo modello nel blocco precedente:
Flight::where('departed', true)
->lazyById(200, column: 'id')
->each->update(['departed' => false]);
Puoi filtrare i risultati in base all’ordinamento decrescente della colonna id
utilizzando il metodo lazyByIdDesc
.
Cursori
Simile al metodo lazy
, il metodo cursor
può essere utilizzato per ridurre significativamente il consumo di memoria della tua applicazione quando si iterano decine di migliaia di record di modelli Eloquent.
Il metodo cursor
esegue una sola query al database; tuttavia, i singoli modelli Eloquent non vengono caricati fino a quando non vengono effettivamente iterati. Pertanto, solo un modello Eloquent è mantenuto in memoria in qualsiasi momento durante l’iterazione sul cursore.
Poiché il metodo
cursor
mantiene in memoria solo un singolo modello Eloquent alla volta, non può caricare preventivamente le relazioni. Se hai bisogno di caricare preventivamente le relazioni, considera l’utilizzo di metodolazy
invece.
Internamente, il metodo cursor
utilizza i generatori di PHP per implementare questa funzionalità:
use App\Models\Flight;
foreach (Flight::where('destination', 'Zurich')->cursor() as $flight) {
// ...
}
Il metodo cursor
restituisce un’istanza di Illuminate\Support\LazyCollection
. Le collezioni lazy ti permettono di utilizzare molti dei metodi delle collezioni disponibili sulle collezioni Laravel tipiche, caricando solo un singolo modello in memoria alla volta:
use App\Models\User;
$users = User::cursor()->filter(function (User $user) {
return $user->id > 500;
});
foreach ($users as $user) {
echo $user->id;
}
Anche se il metodo cursor
utilizza molta meno memoria rispetto a una query normale (mantenendo solo un singolo modello Eloquent in memoria alla volta), alla fine esaurirà comunque la memoria. Ciò è dovuto al fatto che il driver PDO di PHP memorizza internamente nella cache tutti i risultati grezzi della query nel suo buffer. Se stai gestendo un numero molto grande di record Eloquent, considera di utilizzare invece il metodo lazy
.
Sottoquery Avanzate
Selezioni con Sottoquery
Eloquent offre anche un supporto avanzato per le sottoquery, che permette di ottenere informazioni da tabelle correlate in un’unica query. Per esempio, immaginiamo di avere una tabella destinations
e una tabella flights
per le destinazioni. La tabella flights
contiene una colonna arrived_at
che indica quando il volo è arrivato a destinazione.
Usando la funzionalità di sottoquery disponibile nei metodi select
e addSelect
del query builder, possiamo selezionare tutte le destinations
e il nome del volo che è arrivato più recentemente a quella destinazione utilizzando una singola query:
use App\Models\Destination;
use App\Models\Flight;
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderByDesc('arrived_at')
->limit(1)
])->get();
Ordinamento con Subquery
Inoltre, la funzione orderBy
del query builder supporta le subquery. Continuando con il nostro esempio dei voli, possiamo usare questa funzionalità per ordinare tutte le destinazioni in base a quando l’ultimo volo è arrivato in quella destinazione. Anche questa operazione può essere eseguita con una singola query al database:
return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderByDesc('arrived_at')
->limit(1)
)->get();
Ottenere Modelli Singoli / Aggregati
Oltre a recuperare tutti i record che corrispondono a una determinata query, puoi anche ottenere singoli record usando i metodi find
, first
o firstWhere
. Invece di restituire una collezione di modelli, questi metodi restituiscono una singola istanza di modello:
use App\Models\Flight;
// Recupera un modello tramite la chiave primaria...
$flight = Flight::find(1);
// Recupera il primo modello che soddisfa i vincoli della query...
$flight = Flight::where('active', 1)->first();
// Alternativa per recuperare il primo modello che soddisfa i vincoli della query...
$flight = Flight::firstWhere('active', 1);
A volte potresti voler eseguire un’altra azione se non vengono trovati risultati. I metodi findOr
e firstOr
restituiranno una singola istanza di modello oppure, se non vengono trovati risultati, eseguiranno la closure fornita. Il valore restituito dalla closure sarà considerato il risultato del metodo:
$flight = Flight::findOr(1, function () {
// ...
});
$flight = Flight::where('legs', '>', 3)->firstOr(function () {
// ...
});
Eccezioni per Risorse Non Trovate
A volte potresti voler lanciare un’eccezione se un modello non viene trovato. Questo è particolarmente utile nelle rotte o nei controller. I metodi findOrFail
e firstOrFail
recupereranno il primo risultato della query; tuttavia, se non viene trovato alcun risultato, verrà lanciata un’Illuminate\Database\Eloquent\ModelNotFoundException
:
$flight = Flight::findOrFail(1);
$flight = Flight::where('legs', '>', 3)->firstOrFail();
Se l’ModelNotFoundException
non viene gestita, una risposta HTTP 404 viene automaticamente inviata al client:
use App\Models\Flight;
Route::get('/api/flights/{id}', function (string $id) {
return Flight::findOrFail($id);
});
Recuperare o Creare Modelli
Il metodo firstOrCreate
tenterà di trovare un record nel database utilizzando le coppie colonna/valore fornite. Se il modello non viene trovato nel database, verrà inserito un record con gli attributi risultanti dalla fusione del primo array con l’eventuale secondo array passato come argomento:
Il metodo firstOrNew
, come firstOrCreate
, tenterà di trovare un record nel database che corrisponda agli attributi forniti. Tuttavia, se non viene trovato un modello, verrà restituita una nuova istanza del modello. Nota che il modello restituito da firstOrNew
non è ancora stato salvato nel database. Dovrai chiamare manualmente il metodo save
per salvarlo:
use App\Models\Flight;
// Recupera il volo per nome o crealo se non esiste...
$flight = Flight::firstOrCreate([
'name' => 'London to Paris'
]);
// Recupera il volo per nome o crealo con gli attributi name, delayed e arrival_time...
$flight = Flight::firstOrCreate(
['name' => 'London to Paris'],
['delayed' => 1, 'arrival_time' => '11:30']
);
// Recupera il volo per nome o istanzia una nuova istanza di Flight...
$flight = Flight::firstOrNew([
'name' => 'London to Paris'
]);
// Recupera il volo per nome o istanzia con gli attributi name, delayed e arrival_time...
$flight = Flight::firstOrNew(
['name' => 'Tokyo to Sydney'],
['delayed' => 1, 'arrival_time' => '11:30']
);
Recupero degli Aggregati
Quando interagisci con i modelli Eloquent, puoi anche utilizzare i metodi count
, sum
, max
e altri metodi aggregati forniti dal query builder di Laravel. Come potresti aspettarti, questi metodi restituiscono un valore scalare invece di un’istanza del modello Eloquent:
$count = Flight::where('active', 1)->count();
$max = Flight::where('active', 1)->max('price');
Inserimento e aggiornamento dei Model
Inserimenti
Naturalmente, quando usiamo Eloquent, non dobbiamo solo recuperare modelli dal database. Dobbiamo anche inserire nuovi record. Fortunatamente, Eloquent rende questo semplice. Per inserire un nuovo record nel database, devi creare un’istanza di un nuovo modello e impostare gli attributi sul modello. Poi, chiama il metodo save
sull’istanza del modello:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Flight;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class FlightController extends Controller
{
/**
* Memorizza un nuovo volo nel database.
*/
public function store(Request $request): RedirectResponse
{
// Valida la richiesta...
$flight = new Flight;
$flight->name = $request->name;
$flight->save();
return redirect('/flights');
}
}
In questo esempio, assegnamo il campo name
dalla richiesta HTTP in arrivo all’attributo name
dell’istanza del modello App\Models\Flight
. Quando chiamiamo il metodo save
, un record viene inserito nel database. I timestamp created_at
e updated_at
del modello verranno impostati automaticamente quando viene chiamato il metodo save
, quindi non c’è bisogno di impostarli manualmente.
In alternativa, puoi usare il metodo create
per "salvare" un nuovo modello usando un’unica istruzione PHP. L’istanza del modello inserito ti verrà restituita dal metodo create
:
use App\Models\Flight;
$flight = Flight::create([
'name' => 'London to Paris',
]);
Tuttavia, prima di usare il metodo create
, dovrai specificare una proprietà fillable
o guarded
nella tua classe modello. Queste proprietà sono necessarie perché tutti i modelli Eloquent sono protetti contro le vulnerabilità di assegnazione di massa per impostazione predefinita. Per saperne di più sull’assegnazione di massa, consulta la documentazione sull’assegnazione di massa.
Aggiornamenti
Il metodo save
può essere utilizzato anche per aggiornare modelli già presenti nel database. Per aggiornare un modello, devi recuperarlo e impostare gli attributi che desideri modificare. Poi, chiama il metodo save
del modello. Anche in questo caso, il timestamp updated_at
verrà aggiornato automaticamente, quindi non è necessario impostarlo manualmente:
use App\Models\Flight;
$flight = Flight::find(1);
$flight->name = 'Paris to London';
$flight->save();
A volte, potresti aver bisogno di aggiornare un modello esistente o crearne uno nuovo se non esiste un modello corrispondente. Come il metodo firstOrCreate
, il metodo updateOrCreate
salva il modello, quindi non è necessario chiamare manualmente il metodo save
.
Nell’esempio seguente, se esiste un volo con una departure
da Oakland
e una destination
a San Diego
, le colonne price
e discounted
verranno aggiornate. Se non esiste un simile volo, verrà creato un nuovo volo con gli attributi risultanti dalla fusione del primo array di argomenti con il secondo array di argomenti:
$flight = Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
Aggiornamenti di Massa
È possibile eseguire aggiornamenti anche sui modelli che corrispondono a una determinata query. In questo esempio, tutti i voli che sono active
e hanno una destination
di San Diego
saranno contrassegnati come ritardati:
Flight::where('active', 1)
->where('destination', 'San Diego')
->update(['delayed' => 1]);
Il metodo update
si aspetta un array di coppie colonna-valore che rappresentano le colonne da aggiornare. Il metodo update
restituisce il numero di righe interessate.
Quando si esegue un aggiornamento di massa tramite Eloquent, gli eventi del modello
saving
,saved
,updating
eupdated
non verranno attivati per i modelli aggiornati. Ciò accade perché i modelli non vengono mai effettivamente recuperati durante un aggiornamento di massa.
Verificare le Modifiche degli Attributi
Eloquent fornisce i metodi isDirty
, isClean
e wasChanged
per controllare lo stato interno del tuo modello e determinare come i suoi attributi siano cambiati da quando il modello è stato originariamente recuperato.
Il metodo isDirty
verifica se uno qualsiasi degli attributi del modello è stato modificato da quando è stato recuperato. Puoi passare un nome di attributo specifico o un array di attributi al metodo isDirty
per determinare se uno di questi è "sporco". Il metodo isClean
determina se un attributo è rimasto invariato da quando il modello è stato recuperato. Anche questo metodo accetta un argomento opzionale per l’attributo:
use App\Models\User;
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->isDirty(); // true
$user->isDirty('title'); // true
$user->isDirty('first_name'); // false
$user->isDirty(['first_name', 'title']); // true
$user->isClean(); // false
$user->isClean('title'); // false
$user->isClean('first_name'); // true
$user->isClean(['first_name', 'title']); // false
$user->save();
$user->isDirty(); // false
$user->isClean(); // true
Il metodo wasChanged
determina se uno qualsiasi degli attributi è stato modificato quando il modello è stato salvato l’ultima volta nel ciclo di richiesta corrente. Se necessario, puoi passare un nome di attributo per vedere se un particolare attributo è stato cambiato:
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->save();
$user->wasChanged(); // true
$user->wasChanged('title'); // true
$user->wasChanged(['title', 'slug']); // true
$user->wasChanged('first_name'); // false
$user->wasChanged(['first_name', 'title']); // true
Il metodo getOriginal
restituisce un array contenente gli attributi originali del modello indipendentemente da eventuali modifiche apportate al modello da quando è stato recuperato. Se necessario, puoi passare un nome di attributo specifico per ottenere il valore originale di un particolare attributo:
$user = User::find(1);
$user->name; // John
$user->email; // john@example.com
$user->name = "Jack";
$user->name; // Jack
$user->getOriginal('name'); // John
$user->getOriginal(); // Array of original attributes...
Assegnazione di massa
Puoi usare il metodo create
per "salvare" un nuovo modello usando una singola istruzione PHP. L’istanza del modello inserito ti sarà restituita dal metodo:
use App\Models\Flight;
$flight = Flight::create([
'name' => 'London to Paris',
]);
Tuttavia, prima di usare il metodo create
, dovrai specificare una proprietà fillable
o guarded
nella tua classe modello. Queste proprietà sono necessarie perché tutti i modelli Eloquent sono protetti da vulnerabilità di mass assignment per impostazione predefinita.
Una vulnerabilità di mass assignment si verifica quando un utente invia un campo della richiesta HTTP inaspettato e quel campo modifica una colonna nel tuo database che non ti aspettavi. Ad esempio, un utente malintenzionato potrebbe inviare un parametro is_admin
tramite una richiesta HTTP, che viene poi passato al metodo create
del tuo modello, permettendo all’utente di elevarsi a amministratore.
Quindi, per iniziare, dovresti definire quali attributi del modello vuoi rendere assegnabili in massa. Puoi farlo usando la proprietà $fillable
nel modello. Ad esempio, rendiamo l’attributo name
del nostro modello Flight
assegnabile in massa:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = ['name'];
}
Una volta specificati gli attributi assegnabili in massa, puoi usare il metodo create
per inserire un nuovo record nel database. Il metodo create
restituisce l’istanza del modello appena creata:
$flight = Flight::create(['name' => 'London to Paris']);
Se hai già un’istanza di modello, puoi usare il metodo fill
per popolarla con un array di attributi:
$flight->fill(['name' => 'Amsterdam to Frankfurt']);
Assegnazione di massa e colonne JSON
Quando assegni colonne JSON, ogni chiave assegnabile di massa della colonna deve essere specificata nell’array $fillable
del tuo modello. Per sicurezza, Laravel non supporta l’aggiornamento di attributi JSON annidati quando si usa la proprietà guarded
:
/**
* Gli attributi che possono essere assegnati massivamente.
*
* @var array<int, string>
*/
protected $fillable = [
'options->enabled',
];
Consentire la Mass Assignation
Per rendere tutti gli attributi mass assignable, puoi impostare la proprietà $guarded
del tuo modello come un array vuoto. Se decidi di disabilitare la protezione del modello, fai attenzione a creare manualmente gli array che passi ai metodi fill
, create
e update
di Eloquent:
/**
* The attributes that aren't mass assignable.
*
* @var array<string>|bool
*/
protected $guarded = [];
Eccezioni di Assegnazione di Massa
Per impostazione predefinita, gli attributi non inclusi nell’array $fillable
vengono silenziosamente scartati durante le operazioni di assegnazione di massa. In produzione, questo è il comportamento previsto; tuttavia, durante lo sviluppo locale può causare confusione sul motivo per cui le modifiche al modello non hanno effetto.
Se lo desideri, puoi istruire Laravel a lanciare un’eccezione quando tenti di riempire un attributo non riempibile invocando il metodo preventSilentlyDiscardingAttributes
. Tipicamente, questo metodo dovrebbe essere chiamato nel metodo boot
della classe AppServiceProvider
della tua applicazione:
use Illuminate\Database\Eloquent\Model;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Model::preventSilentlyDiscardingAttributes($this->app->isLocal());
}
Upserts
Il metodo upsert
di Eloquent può essere utilizzato per aggiornare o creare record in un’unica operazione atomica. Il primo argomento del metodo contiene i valori da inserire o aggiornare, mentre il secondo argomento specifica la/e colonna/e che identificano univocamente i record nella tabella associata. Il terzo e ultimo argomento è un array delle colonne che devono essere aggiornate se esiste già un record corrispondente nel database. Il metodo upsert
imposterà automaticamente i timestamp created_at
e updated_at
se i timestamp sono abilitati sul modello:
Flight::upsert([
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], uniqueBy: ['departure', 'destination'], update: ['price']);
Tutti i database tranne SQL Server richiedono che le colonne nel secondo argomento del metodo
upsert
abbiano un indice "primary" o "unique". Inoltre, i driver dei database MariaDB e MySQL ignorano il secondo argomento del metodoupsert
e utilizzano sempre gli indici "primary" e "unique" della tabella per rilevare i record esistenti.
Eliminare i modelli
Per eliminare un modello, puoi chiamare il metodo delete
sull’istanza del modello:
use App\Models\Flight;
$flight = Flight::find(1);
$flight->delete();
Puoi chiamare il metodo truncate
per eliminare tutti i record del database associati al modello. L’operazione truncate
resetterà anche eventuali ID auto-incrementanti sulla tabella associata al modello:
Flight::truncate();
Eliminare un Modello Esistente tramite la Sua Chiave Primaria
Nell’esempio sopra, stiamo recuperando il modello dal database prima di chiamare il metodo delete
. Tuttavia, se conosci la chiave primaria del modello, puoi eliminare il modello senza recuperarlo esplicitamente usando il metodo destroy
. Oltre ad accettare una singola chiave primaria, il metodo destroy
accetta più chiavi primarie, un array di chiavi primarie o una collection di chiavi primarie:
Flight::destroy(1);
Flight::destroy(1, 2, 3);
Flight::destroy([1, 2, 3]);
Flight::destroy(collect([1, 2, 3]));
Se stai utilizzando soft deleting models, puoi eliminare definitivamente i modelli tramite il metodo forceDestroy
:
Flight::forceDestroy(1);
Il metodo
destroy
carica ogni modello singolarmente e chiama il metododelete
in modo che gli eventideleting
edeleted
vengano correttamente inviati per ogni modello.
Eliminare Modelli Usando Query
Naturalmente, puoi creare una query Eloquent per eliminare tutti i modelli che corrispondono ai criteri della tua query. In questo esempio, elimineremo tutti i voli contrassegnati come inattivi. Come per gli aggiornamenti di massa, anche le eliminazioni di massa non dispatcheranno eventi modello per i modelli eliminati:
$deleted = Flight::where('active', 0)->delete();
Quando esegui una dichiarazione di eliminazione di massa tramite Eloquent, gli eventi modello
deleting
edeleted
non verranno dispatcherati per i modelli eliminati. Questo perché i modelli non vengono mai effettivamente recuperati durante l’esecuzione della dichiarazione di eliminazione.
Cancellazione Soft
Oltre a rimuovere effettivamente i record dal tuo database, Eloquent può anche "cancellare soft" i modelli. Quando i modelli vengono cancellati soft, non vengono rimossi dal database. Invece, viene impostato un attributo deleted_at
sul modello che indica la data e l’ora in cui il modello è stato "cancellato". Per abilitare le cancellazioni soft per un modello, aggiungi il trait Illuminate\Database\Eloquent\SoftDeletes
al modello:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Flight extends Model
{
use SoftDeletes;
}
Il trait
SoftDeletes
convertirà automaticamente l’attributodeleted_at
in un’istanzaDateTime
/Carbon
per te.
Dovresti anche aggiungere la colonna deleted_at
alla tua tabella nel database. Il schema builder di Laravel contiene un metodo di supporto per creare questa colonna:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::table('flights', function (Blueprint $table) {
$table->softDeletes();
});
Schema::table('flights', function (Blueprint $table) {
$table->dropSoftDeletes();
});
Ora, quando chiami il metodo delete
sul modello, la colonna deleted_at
verrà impostata sulla data e l’ora correnti. Tuttavia, il record del modello nel database rimarrà nella tabella. Quando esegui una query su un modello che utilizza le cancellazioni soft, i modelli cancellati soft saranno automaticamente esclusi dai risultati della query.
Per determinare se un’istanza di modello è stata cancellata soft, puoi usare il metodo trashed
:
if ($flight->trashed()) {
// ...
}
Ripristinare i Modelli Soft Deleted
A volte potresti voler "annullare l’eliminazione" di un modello soft deleted. Per ripristinare un modello soft deleted, puoi chiamare il metodo restore
su un’istanza del modello. Il metodo restore
imposterà la colonna deleted_at
del modello a null
:
$flight->restore();
Puoi anche utilizzare il metodo restore
in una query per ripristinare più modelli. Anche in questo caso, come per altre operazioni di massa, non verranno dispatchati eventi modello per i modelli che vengono ripristinati:
Flight::withTrashed()
->where('airline_id', 1)
->restore();
Il metodo restore
può essere utilizzato anche quando costruisci query di relazioni:
$flight->history()->restore();
Eliminazione definitiva dei modelli
A volte potresti aver bisogno di rimuovere completamente un modello dal tuo database. Puoi utilizzare il metodo forceDelete
per eliminare definitivamente un modello soft deleted dalla tabella del database:
$flight->forceDelete();
Puoi anche usare il metodo forceDelete
quando costruisci query di relazioni Eloquent:
$flight->history()->forceDelete();
Interrogare i Modelli Soft Deleted
Inclusione dei modelli Soft Deleted
Come detto sopra, i modelli soft deleted saranno automaticamente esclusi dai risultati delle query. Tuttavia, puoi forzare l’inclusione dei modelli soft deleted nei risultati di una query chiamando il metodo withTrashed
sulla query:
use App\Models\Flight;
$flights = Flight::withTrashed()
->where('account_id', 1)
->get();
Il metodo withTrashed
può essere chiamato anche durante la costruzione di una relazione query:
$flight->history()->withTrashed()->get();
Recuperare Solo Modelli Soft Deleted
Il metodo onlyTrashed
recupera solo i modelli soft deleted:
$flights = Flight::onlyTrashed()
->where('airline_id', 1)
->get();
Pruning dei Modelli
A volte potresti voler eliminare periodicamente i modelli che non sono più necessari. Per fare ciò, puoi aggiungere il trait Illuminate\Database\Eloquent\Prunable
o Illuminate\Database\Eloquent\MassPrunable
ai modelli che desideri potare periodicamente. Dopo aver aggiunto uno dei trait al modello, implementa un metodo prunable
che restituisce un builder di query Eloquent che seleziona i modelli che non sono più necessari:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Prunable;
class Flight extends Model
{
use Prunable;
/**
* Ottieni la query del modello potabile.
*/
public function prunable(): Builder
{
return static::where('created_at', '<=', now()->subMonth());
}
}
Quando contrassegni i modelli come Prunable
, puoi anche definire un metodo pruning
nel modello. Questo metodo verrà chiamato prima che il modello venga eliminato. Questo metodo può essere utile per eliminare eventuali risorse aggiuntive associate al modello, come file archiviati, prima che il modello venga rimosso definitivamente dal database:
/**
* Prepara il modello per il pruning.
*/
protected function pruning(): void
{
// ...
}
Dopo aver configurato il tuo modello potabile, dovresti programmare il comando Artisan model:prune
nel file routes/console.php
della tua applicazione. Puoi scegliere l’intervallo appropriato per eseguire questo comando:
use Illuminate\Support\Facades\Schedule;
Schedule::command('model:prune')->daily();
Dietro le quinte, il comando model:prune
rileverà automaticamente i modelli "Prunable" nella directory app/Models
della tua applicazione. Se i tuoi modelli si trovano in una posizione diversa, puoi usare l’opzione --model
per specificare i nomi delle classi dei modelli:
Schedule::command('model:prune', [
'--model' => [Address::class, Flight::class],
])->daily();
Se desideri escludere determinati modelli dal pruning mentre sistemi tutti gli altri modelli rilevati, puoi usare l’opzione --except
:
Schedule::command('model:prune', [
'--except' => [Address::class, Flight::class],
])->daily();
Puoi testare la tua query prunable
eseguendo il comando model:prune
con l’opzione --pretend
. In modalità simulazione, il comando model:prune
riporterà semplicemente quante registrazioni verrebbero potate se il comando venisse effettivamente eseguito:
php artisan model:prune --pretend
I modelli eliminati soft saranno eliminati definitivamente (
forceDelete
) se corrispondono alla query prunable.
Mass Pruning
Quando i modelli sono contrassegnati con il trait Illuminate\Database\Eloquent\MassPrunable
, vengono eliminati dal database usando query di cancellazione di massa. Pertanto, il metodo pruning
non verrà invocato, né saranno dispatchati gli eventi del modello deleting
e deleted
. Questo perché i modelli non vengono mai effettivamente recuperati prima dell’eliminazione, rendendo il processo di pruning molto più efficiente:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\MassPrunable;
class Flight extends Model
{
use MassPrunable;
/**
* Get the prunable model query.
*/
public function prunable(): Builder
{
return static::where('created_at', '<=', now()->subMonth());
}
}
Replicare i Modelli
Puoi creare una copia non salvata di un’istanza di modello esistente usando il metodo replicate
. Questo metodo è particolarmente utile quando hai istanze di modelli che condividono molti degli stessi attributi:
use App\Models\Address;
$shipping = Address::create([
'type' => 'shipping',
'line_1' => '123 Example Street',
'city' => 'Victorville',
'state' => 'CA',
'postcode' => '90001',
]);
$billing = $shipping->replicate()->fill([
'type' => 'billing'
]);
$billing->save();
Per escludere uno o più attributi dalla replica nel nuovo modello, puoi passare un array al metodo replicate
:
$flight = Flight::create([
'destination' => 'LAX',
'origin' => 'LHR',
'last_flown' => '2020-03-04 11:00:00',
'last_pilot_id' => 747,
]);
$flight = $flight->replicate([
'last_flown',
'last_pilot_id'
]);
Scope delle Query
Scope Globali
Gli scope globali ti permettono di aggiungere restrizioni a tutte le query per un determinato modello. La funzionalità soft delete di Laravel utilizza gli scope globali per recuperare solo i modelli "non eliminati" dal database. Creare i tuoi scope globali può offrire un modo comodo e semplice per assicurarti che ogni query per un modello specifico abbia certe restrizioni.
Generazione degli Scope
Per generare un nuovo global scope, puoi utilizzare il comando Artisan make:scope
, che posizionerà lo scope generato nella directory app/Models/Scopes
della tua applicazione:
php artisan make:scope AncientScope
Scrivere Scope Globali
Scrivere uno scope globale è semplice. Innanzitutto, usa il comando make:scope
per generare una classe che implementa l’interfaccia Illuminate\Database\Eloquent\Scope
. L’interfaccia Scope
richiede di implementare un metodo: apply
. Il metodo apply
può aggiungere vincoli where
o altri tipi di clausole alla query secondo necessità:
<?php
namespace App\Models\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class AncientScope implements Scope
{
/**
* Applica lo scope a un query builder di Eloquent.
*/
public function apply(Builder $builder, Model $model): void
{
$builder->where('created_at', '<', now()->subYears(2000));
}
}
Se il tuo scope globale aggiunge colonne alla clausola select della query, dovresti usare il metodo
addSelect
invece diselect
. Questo eviterà la sostituzione involontaria della clausola select esistente nella query.
Applicare Scope Globali
Per assegnare uno scope globale a un modello, puoi semplicemente aggiungere l’attributo ScopedBy
al modello:
<?php
namespace App\Models;
use App\Models\Scopes\AncientScope;
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
#[ScopedBy([AncientScope::class])]
class User extends Model
{
//
}
Oppure, puoi registrare manualmente lo scope globale sovrascrivendo il metodo `booted` del modello e invocando il metodo `addGlobalScope` del modello. Il metodo `addGlobalScope` accetta un'istanza del tuo scope come unico argomento:
```php
<?php
namespace App\Models;
use App\Models\Scopes\AncientScope;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Il metodo "booted" del modello.
*/
protected static function booted(): void
{
static::addGlobalScope(new AncientScope);
}
}
Dopo aver aggiunto lo scope nell’esempio sopra al modello App\Models\User
, una chiamata al metodo User::all()
eseguirà la seguente query SQL:
select * from `users` where `created_at` < 0021-02-18 00:00:00
Global Scopes Anonimi
Eloquent consente anche di definire global scopes utilizzando closure, il che è particolarmente utile per scope semplici che non richiedono una classe separata. Quando definisci un global scope con una closure, devi fornire un nome per lo scope a tua scelta come primo argomento del metodo addGlobalScope
:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Il metodo "booted" del modello.
*/
protected static function booted(): void
{
static::addGlobalScope('ancient', function (Builder $builder) {
$builder->where('created_at', '<', now()->subYears(2000));
});
}
}
Rimuovere gli Scope Globali
Se desideri rimuovere uno scope globale per una determinata query, puoi usare il metodo withoutGlobalScope
. Questo metodo accetta il nome della classe dello scope globale come unico argomento:
User::withoutGlobalScope(AncientScope::class)->get();
Oppure, se hai definito lo scope globale usando una closure, dovresti passare il nome stringa che hai assegnato allo scope globale:
User::withoutGlobalScope('ancient')->get();
Se vuoi rimuovere diversi o anche tutti gli scope globali della query, puoi usare il metodo withoutGlobalScopes
:
// Rimuovi tutti gli scope globali...
User::withoutGlobalScopes()->get();
// Rimuovi alcuni degli scope globali...
User::withoutGlobalScopes([
FirstScope::class, SecondScope::class
])->get();
Scope Locali
Gli scope locali ti permettono di definire set comuni di vincoli alle query che puoi riutilizzare facilmente in tutta la tua applicazione. Per esempio, potresti aver bisogno di recuperare frequentemente tutti gli utenti considerati "popolari". Per definire uno scope, aggiungi il prefisso scope
al metodo del modello Eloquent.
Gli scope devono sempre restituire la stessa istanza di query builder oppure void
:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Definisci uno scope per includere solo utenti popolari.
*/
public function scopePopular(Builder $query): void
{
$query->where('votes', '>', 100);
}
/**
* Definisci uno scope per includere solo utenti attivi.
*/
public function scopeActive(Builder $query): void
{
$query->where('active', 1);
}
}
Utilizzo di uno Scope Locale
Una volta definito lo scope, puoi chiamare i metodi dello scope quando interroghi il modello. Tuttavia, non dovresti includere il prefisso scope
quando chiami il metodo. Puoi anche concatenare chiamate a vari scope:
use App\Models\User;
$users = User::popular()->active()->orderBy('created_at')->get();
Combinare più scope di modelli Eloquent tramite un operatore di query or
potrebbe richiedere l’uso di closure per ottenere il corretto raggruppamento logico:
$users = User::popular()->orWhere(function (Builder $query) {
$query->active();
})->get();
Tuttavia, poiché questo può essere scomodo, Laravel fornisce un metodo orWhere
di "ordine superiore" che ti permette di concatenare fluentemente gli scope senza l’uso di closure:
$users = User::popular()->orWhere->active()->get();
Dynamic Scopes
A volte potresti voler definire uno scope che accetta parametri. Per iniziare, aggiungi semplicemente i tuoi parametri aggiuntivi alla firma del metodo scope. I parametri dello scope dovrebbero essere definiti dopo il parametro $query
:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Scope a query to only include users of a given type.
*/
public function scopeOfType(Builder $query, string $type): void
{
$query->where('type', $type);
}
}
Una volta che gli argomenti previsti sono stati aggiunti alla firma del metodo scope, puoi passare gli argomenti quando chiami lo scope:
$users = User::ofType('admin')->get();
Confrontare i Modelli
A volte potrebbe essere necessario determinare se due modelli sono "uguali" o meno. I metodi is
e isNot
possono essere usati per verificare rapidamente se due modelli hanno la stessa chiave primaria, tabella e connessione al database:
if ($post->is($anotherPost)) {
// ...
}
if ($post->isNot($anotherPost)) {
// ...
}
I metodi is
e isNot
sono disponibili anche quando si utilizzano le relazioni belongsTo
, hasOne
, morphTo
e morphOne
relationships. Questo metodo è particolarmente utile quando si desidera confrontare un modello correlato senza eseguire una query per recuperare quel modello:
if ($post->author()->is($user)) {
// ...
}
Eventi
Vuoi trasmettere i tuoi eventi Eloquent direttamente alla tua applicazione lato client? Dai un’occhiata a model event broadcasting di Laravel.
I modelli Eloquent emettono diversi eventi, permettendoti di agganciarti ai seguenti momenti nel ciclo di vita di un modello: retrieved
, creating
, created
, updating
, updated
, saving
, saved
, deleting
, deleted
, trashed
, forceDeleting
, forceDeleted
, restoring
, restored
e replicating
.
L’evento retrieved
viene emesso quando un modello esistente viene recuperato dal database. Quando un nuovo modello viene salvato per la prima volta, vengono emessi gli eventi creating
e created
. Gli eventi updating
/ updated
vengono emessi quando un modello esistente viene modificato e viene chiamato il metodo save
. Gli eventi saving
/ saved
vengono emessi quando un modello viene creato o aggiornato, anche se gli attributi del modello non sono stati cambiati. I nomi degli eventi che terminano con -ing
vengono emessi prima che qualsiasi modifica al modello venga salvata, mentre gli eventi che terminano con -ed
vengono emessi dopo che le modifiche al modello sono state salvate.
Per iniziare ad ascoltare gli eventi del modello, definisci una proprietà $dispatchesEvents
nel tuo modello Eloquent. Questa proprietà mappa vari punti del ciclo di vita del modello Eloquent alle tue classi di eventi. Ogni classe di evento del modello deve aspettarsi di ricevere un’istanza del modello interessato tramite il suo costruttore:
<?php
namespace App\Models;
use App\Events\UserDeleted;
use App\Events\UserSaved;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
/**
* The event map for the model.
*
* @var array<string, string>
*/
protected $dispatchesEvents = [
'saved' => UserSaved::class,
'deleted' => UserDeleted::class,
];
}
Dopo aver definito e mappato i tuoi eventi Eloquent, puoi utilizzare listener di eventi per gestire gli eventi.
Quando esegui una query di aggiornamento o cancellazione di massa tramite Eloquent, gli eventi di modello
saved
,updated
,deleting
edeleted
non verranno emessi per i modelli interessati. Questo perché i modelli non vengono mai effettivamente recuperati quando si eseguono aggiornamenti o cancellazioni di massa.
Utilizzo delle Closure
Invece di usare classi di eventi personalizzate, puoi registrare closure che si eseguono quando vengono emessi vari eventi del modello. Di solito, dovresti registrare queste closure nel metodo booted
del tuo modello:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Il metodo "booted" del modello.
*/
protected static function booted(): void
{
static::created(function (User $user) {
// ...
});
}
}
Se necessario, puoi utilizzare listener di eventi anonimi queueable durante la registrazione degli eventi del modello. Questo indicherà a Laravel di eseguire il listener dell’evento del modello in background utilizzando la coda della tua applicazione:
use function Illuminate\Events\queueable;
static::created(queueable(function (User $user) {
// ...
}));
Observer
Definire gli Observer
Se stai ascoltando molti eventi su un determinato modello, puoi usare gli observers per raggruppare tutti i tuoi ascoltatori in un’unica classe. Le classi Observer hanno nomi di metodi che riflettono gli eventi Eloquent che desideri ascoltare. Ognuno di questi metodi riceve il modello interessato come unico argomento. Il comando Artisan make:observer
è il modo più semplice per creare una nuova classe observer:
php artisan make:observer UserObserver --model=User
Questo comando posizionerà il nuovo observer nella directory app/Observers
. Se questa directory non esiste, Artisan la creerà per te. Il tuo nuovo observer avrà il seguente aspetto:
<?php
namespace App\Observers;
use App\Models\User;
class UserObserver
{
/**
* Handle the User "created" event.
*/
public function created(User $user): void
{
// ...
}
/**
* Handle the User "updated" event.
*/
public function updated(User $user): void
{
// ...
}
/**
* Handle the User "deleted" event.
*/
public function deleted(User $user): void
{
// ...
}
/**
* Handle the User "restored" event.
*/
public function restored(User $user): void
{
// ...
}
/**
* Handle the User "forceDeleted" event.
*/
public function forceDeleted(User $user): void
{
// ...
}
}
Per registrare un observer, puoi posizionare l’attributo ObservedBy
sul modello corrispondente:
use App\Observers\UserObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
#[ObservedBy([UserObserver::class])]
class User extends Authenticatable
{
//
}
Oppure, puoi registrare manualmente un osservatore invocando il metodo observe
sul modello che desideri osservare. Puoi registrare gli osservatori nel metodo boot
della classe AppServiceProvider
della tua applicazione:
use App\Models\User;
use App\Observers\UserObserver;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
User::observe(UserObserver::class);
}
Ci sono eventi aggiuntivi a cui un osservatore può ascoltare, come
saving
eretrieved
. Questi eventi sono descritti nella documentazione degli eventi.
Observer e Transazioni nel Database
Quando i modelli vengono creati all’interno di una transazione nel database, potresti voler istruire un observer a eseguire i suoi gestori di eventi solo dopo che la transazione del database è stata confermata. Questo puoi farlo implementando l’interfaccia ShouldHandleEventsAfterCommit
nel tuo observer. Se non è in corso una transazione nel database, i gestori di eventi verranno eseguiti immediatamente:
<?php
namespace App\Observers;
use App\Models\User;
use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit;
class UserObserver implements ShouldHandleEventsAfterCommit
{
/**
* Gestisci l'evento "created" di User.
*/
public function created(User $user): void
{
// ...
}
}
Disattivare gli Eventi
A volte potresti aver bisogno di "disattivare" temporaneamente tutti gli eventi emessi da un modello. Puoi farlo usando il metodo withoutEvents
. Il metodo withoutEvents
accetta una closure come unico argomento. Qualsiasi codice eseguito all’interno di questa closure non dispatcherà eventi del modello e qualsiasi valore restituito dalla closure sarà restituito dal metodo withoutEvents
:
use App\Models\User;
$user = User::withoutEvents(function () {
User::findOrFail(1)->delete();
return User::find(2);
});
Salvataggio di un singolo modello senza eventi
A volte potresti voler "salvare" un modello senza emettere eventi. Puoi farlo utilizzando il metodo saveQuietly
:
$user = User::findOrFail(1);
$user->name = 'Victoria Faith';
$user->saveQuietly();
Puoi anche "aggiornare", "delete", "soft delete", "restore" e "replicate" un modello senza emettere eventi:
$user->deleteQuietly();
$user->forceDeleteQuietly();
$user->restoreQuietly();