Ciao! Oggi facciamo un riepilogo delle relazioni che Eloquent mette a disposizione per gestire le associazioni di dati nel database in modo efficiente e “pigro”.
È importante distinguere due differenti modalità di recupero delle relazioni: proprietà e metodo.
- Proprietà: recupera direttamente il valore della relazione come se fosse una proprietà del modello.
- Metodo: permette di invocare il metodo della relazione e concatenare ulteriori metodi del query builder per filtrare o manipolare i dati.
# Proprietà
$profile = User::find($id)->profile;
# Metodo
$profile_ogg = User::find($id)->profile()->where('active', true)->get();
One to One
La relazione One to One collega un record di una tabella con un solo record di un’altra tabella.
Esempio: un utente ha un solo profilo.
# User model
public function profile() {
return $this->hasOne(Profile::class, 'user_id', 'id'); # chiave esterna Profile.user_id, chiave locale User.id
}
Nota: se non specifichi le chiavi, Eloquent assume id come chiave primaria del modello principale e modello_id come chiave esterna nella tabella secondaria.
Relazione inversa:
# Profile model
public function user() {
return $this->belongsTo(User::class, 'user_id', 'id'); # chiave locale Profile.user_id, chiave esterna User.id
}
One to Many
La relazione One to Many collega un record di una tabella a più record di un’altra.
Esempio: un autore ha molti post.
# Author model
public function posts() {
return $this->hasMany(Post::class, 'author_id', 'id'); # chiave esterna Post.author_id, chiave locale Author.id
}
Relazione inversa:
# Post model
public function author() {
return $this->belongsTo(User::class, 'author_id', 'id'); # chiave locale Post.author_id, chiave esterna User.id
}
Many to Many
La relazione Many to Many collega tanti record a tanti record, usando una tabella pivot.
Struttura tabella pivot:
# tabella pivot post_tag
# post_tag
# post_id
Esempio: post e tag.
# Post model
public function tags() {
return $this->belongsToMany(
Tag::class, # modello correlato
'post_tag', # tabella pivot
'post_id', # chiave locale sulla tabella pivot
'tag_id' # chiave esterna sulla tabella pivot
)->withTimestamps();
}
Se volessimo aggiungere I campi di timestamp sulla nostra tabella pivot, possiamo farlo utilizzando withTimestamps(), mentre se abbiamo la necessità di aggiungere altri campi useremo withPivot()
Relazione inversa nel modello Tag:
public function posts() {
return $this->belongsToMany(
Post::class,
'post_tag',
'tag_id',
'post_id'
)->withTimestamps();
}
Associare record alla pivot:
Per gestire l’associazione nel controller dei nostri dati su la tabella pivot, abbiamo a disposizione due metodi: attach() e sync().
Il secondo, in particolare, ci permette di sincronizzare gli id nella tabella pivot, possiamo passare anche degli argomenti per un solo id, oppure o per tutti grazie a syncWithPivotValues .
# Associare più id
$post->tags()->sync([1, 2, 3]);
# Con dati aggiuntivi
$post->tags()->sync([1 => ['expires' => true], 2, 3]);
# Con syncWithPivotValues
$post->tags()->syncWithPivotValues([1,2,3], ['active' => true]);
HasManyThrough
Permette di collegare due modelli indirettamente passando attraverso un terzo modello.
Esempio: Artist → Album → Song.
# Database
# artists: id, name
# albums: id, name, artist_id
# songs: id, name, album_id
Artist model
public function songs() {
return $this->hasManyThrough(
Song::class, # modello finale
Album::class, # modello intermedio
'artist_id', # chiave esterna su Album
'album_id', # chiave esterna su Song
'id', # chiave locale su Artist
'id' # chiave locale su Album
);
}
Recupero dati:
$songs = Artist::find(1)->songs; # collezione di tutte le canzoni dell'artista 1
Lazy Loading ed Eager Loading
Eloquent carica le relazioni in modo pigro (lazy loading) per impostazione predefinita. Questo significa che la relazione viene interrogata solo quando viene effettivamente utilizzata.
# Lazy loading
$users = User::all();
foreach ($users as $user) {
echo $user->profile->name; # query eseguita qui
}
Questo approccio è comodo ma può generare il classico problema N+1 query.
Eager Loading con with()
Per evitare query inutili possiamo caricare le relazioni in anticipo usando with().
# Eager loading
$users = User::with('profile')->get();
foreach ($users as $user) {
echo $user->profile->name; # nessuna query aggiuntiva
}
Possiamo caricare più relazioni contemporaneamente:
$users = User::with(['profile', 'posts'])->get();
E anche relazioni annidate:
$users = User::with('posts.comments')->get();
Caricamento dinamico con load()
Se abbiamo già recuperato il modello e vogliamo caricare la relazione in un secondo momento, possiamo usare load().
$user = User::find(1);
$user->load('profile');
Questo è utile quando la necessità della relazione dipende da una condizione runtime.
In breve:
- Lazy loading: comodo ma rischioso sulle performance
- Eager loading (
with): consigliato quando sappiamo già quali relazioni servono- load(): eager loading "posticipato"
Grazie per aver letto questo articoletto, alla prossima puntata!

