Le tabelle di un database sono spesso correlate tra loro. Ad esempio, un post di blog ha molti commenti, oppure un ordine è collegato all’utente che l’ha effettuato.Eloquent offre un modo semplice per definire e manipolare queste relazioni tra tabelle. Le relazioni si definiscono come metodi sui modelli Eloquent.
In questa pagina usiamo come esempio modelli concreti come User, Post, Comment e Tag.
Diamo per scontato che modelli e tabelle siano già stati creati.
Le principali relazioni supportate da Eloquent sono le seguenti.
Relazione
Descrizione
hasOne
1-a-1 (il genitore ha un figlio)
belongsTo
Inverso di 1-a-1 (il figlio appartiene al genitore)
<?phpnamespace App\Models;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\HasOne;class User extends Model{ public function profile(): HasOne { return $this->hasOne(Profile::class); }}
Eloquent deduce automaticamente la chiave esterna dal nome del modello padre. Assume che la tabella profiles abbia una colonna user_id.
La relazione definita è accessibile come una proprietà.
$user = User::find(1);$profile = $user->profile;
Chiamando il metodo di relazione come una proprietà, Eloquent emette automaticamente la query e restituisce i dati correlati. Questa è la “dynamic relationship property”.
<?phpnamespace App\Models;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\HasMany;class Post extends Model{ public function comments(): HasMany { return $this->hasMany(Comment::class); }}
Eloquent assume che la tabella comments abbia una colonna post_id.
La relazione molti-a-molti si usa quando entrambi i modelli hanno molteplici associazioni tra loro. Ad esempio, un Post ha più Tag e un Tag appartiene a più Post.
<?phpnamespace App\Models;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\BelongsToMany;class Post extends Model{ public function tags(): BelongsToMany { return $this->belongsToMany(Tag::class); }}
Definendola in modo analogo anche sul lato Tag puoi navigare la relazione anche nel verso opposto.
<?phpnamespace App\Models;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\BelongsToMany;class Tag extends Model{ public function posts(): BelongsToMany { return $this->belongsToMany(Post::class); }}
Con attach aggiungi un record nella tabella pivot, con detach lo rimuovi.
$post = Post::find(1);// Aggiungi un tag$post->tags()->attach($tagId);// Rimuovi un tag$post->tags()->detach($tagId);// Sostituisce completamente le associazioni correnti$post->tags()->sync([$tagId1, $tagId2]);
Con sync i record della tabella pivot che non corrispondono agli ID passati vengono eliminati automaticamente, lasciando solo quelli indicati.
Accedendo a una relazione come proprietà, Eloquent emette una query ogni volta. Questo è il “lazy loading”. Se accedi alla relazione in un ciclo, incorri nel problema N+1.
// Una query per recuperare tutti i post$posts = Post::all();foreach ($posts as $post) { // Per ogni post viene emessa una query per ottenere l'utente (N volte) echo $post->user->name;}
Con 100 post fai 101 query in totale. Ha un forte impatto sulle prestazioni.
Con il metodo with() recuperi in blocco i dati correlati. Le query si riducono a due.
// Recupera post e utenti in due query$posts = Post::with('user')->get();foreach ($posts as $post) { // Non vengono emesse query aggiuntive echo $post->user->name;}
L’SQL eseguito è solo questo:
select * from postsselect * from users where id in (1, 2, 3, ...)
Con la notazione dot puoi fare eager load anche di relazioni annidate.
// Eager load dei commenti dei post e degli utenti di ciascun commento$posts = Post::with('comments.user')->get();
L’eager loading è fondamentale per prevenire il problema N+1. Prendi l’abitudine di usare with() ogni volta che accedi a relazioni all’interno di un ciclo.