Relationships: la gestione delle relazioni con Laravel

Le tabelle di un database sono spesso collegate tra di loro e per questo serve gestire le relazioni tra queste tabelle, Laravel utilizza Eloquent. Le principali relazioni gestite da laravel sono:

  • One to One: relazione uno a uno;
  • One to Many: relazione uno a molti;
  • Many to Many: relazioni molti a molti;
  • HasManyThrough: permette collegare due tabelle che non hanno una relazione diretta tra di loro ma sono legate da un’altra tabella
  • Polimorfiche: mettono in relazione un modello con oggetti di natura diversa

Ora per cercare di capirle proviamo ad implementare il classico esempio di un blog stile WordPress, con tanto di utenti, post, commenti e tag.

One to One

Chiaramente è la relazione più semplice, un elemento della prima tabella può essere relazionato con un solo elemento di della seconda tabella, immaginiamo di collegare il modello User a quello Profile, ad un utente corrisponderà un solo e unico profilo. Sono delle informazioni ulteriori sul singolo utente. Per collegarle possiamo all’interno del modello User andare ad implementare la seguente funzione:

public function profile(){

    return $this->hasOne(App\Profile::class):

}

Questo significa che Laravel, in realtà Eloquent, andrà a cercare all’interno della tabella profiles la chiave esterna user_id, se invece abbiamo un nome diverso per la chiave esterna possiamo definire il nome della chiave in questo modo:

return $this->hasOne(App\Profile::class, 'nome_id_user_tabella_profile', 'id_primario_tabella_user');

Questo perchè la relazione hasOne come anche le successive hanno tre parametri che sono il modello con cui ci si deve relazionalel’id della chiave esterna e l’id locale. Se non passiamo gli ultimi due parametri Eloquent dà per scontato che la chiave esterna sia composta dal nome del modello seguito da un ‘_id’, a questo punto chiamandosi il modello User, la chiave predefinita risulta user_id, mentre la chiave interna alla tabella è automaticamente ‘id’.

Il modo per trovare il modello connesso è molto semplice:

$info = User::find(1)->profile;

Per avere invece la relazione inversa, quindi accedere all’utente associato a quel profilo possiamo impostare dentro al modello Profile la seguente funzione:

public function user(){

	return $this->belongsTo(App\User::class);

}

in questo modo tramite il modello Info possiamo accedere all’utente collegato.  Anche in questo caso Eloquent darà per scontato di trovare una colonna user_id nella tabella profiles e nel caso avessimo scelto un nome diverso abbiamo la possibilità di passargli i parametri per l’id e la chiave esterna.

One to Many

Altra relazione che si trova frequente è quella uno a molti, la possiamo trovare ad esempio nella relazione tra utenti e post, un utente ha più post, mentre un post appartiene solamente ad un utente. Quindi all’interno del modello User, possiamo creare la seguente funzione:

public function posts(){

	return $this->hasMany(App\Post::class);

}

Ancora una volta in questo caso Eloquent dà per scontato di trovare il campo user_id nella tabella posts, se così non fosse possiamo tranquillamente definire il nome delle chiavi esterne proprio come abbiamo visto prima con la relazione One to One.

Possiamo recuperare il dato in modo simile, in questo caso chiaramente avremo una collezione e quindi dovremo poi scorrerla con il foreach:

$posts = User::find(1)->posts;

Il caso inverso è identico al caso di relazione One to One che abbiamo visto prima. Se dal modello Post vogliamo andare a trovare l’utente, si tratterà dell’unico utente a cui può essere associato quel post e quindi aggiungeremo al modello Post la seguente funzione:

public function user(){

    return $this->belongsTo(App\User::class);

}

Il post appartiene solo all’utente che lo ha scritto e quindi andiamo a recuperarlo così semplicemente.

Many to Many

Andiamo a vedere ora la relazione molti a molti, rimanendo sul funzionamento di un blog WordPress possiamo andare ad analizzare la relazione tra post e tag. In questo caso c’è bisogno di quella che si chiama una tabella di pivot, che contiene post_id e tag_id. Ciò ci permetterà di mettere in relazione le due tabelle posts e tags. La tabella di pivot chiaramente non ha bisogno di un suo modello. Quindi un post potrà avere più tag e un tag può essere associato a più post.

Ma andiamo a vedere quanto Eloquent renda semplice questa relazione, nel model Post aggiungiamo la seguente funzione:

public function tags(){

    return $this->belongsToMany(App\Tag::class);

}

Ecco che ora Eloquent si aspetta di trovare una tabella post_tag e qui all’interno si aspetta di trovare post_id e tag_id, questo perchè la convenzione per le chiavi esterne è sempre ‘{nomeModello}_id’. Anche in questo caso ci verrà ritornata una collection e quindi ci sarà bisogno poi di un foreach. Interessante è adesso andare a vedere i parametri che riceve la funzione belongsToMany, in questo caso non sono più tre ma quattro parametri, il primo rimane il modello associato, il secondo è il nome della tabella di pivot, in questo modo se abbiamo un nome diverso da post_tag possiamo impostarlo, il terzo parametro è il nome della colonna che nella tabella pivot identifica il modello, il quarto è il nome della colonna che invece identifica l’altro modello collegato. in questo caso sarebbe quindi:

return $this->belongsToMany(App\Tag::class, 'nome della tabella pivot', 'id post nella tabella pivot', 'id tag nella tabella pivot');

La relazione inversa messa all’interno del modello Tag è praticamente identica:

public function posts(){

    return $this->belongsToMany(App\Post::class);

}

Chiaramente nel caso dobbiamo specificare gli altri parametri dovremmo invertire l’ordine in questo modo:

return $this->belongsToMany(App\Post::class, 'nome della tabella pivot', 'id tag nella tabella pivot', 'id post nella tabella pivot');

La lettura la riscriviamo ma è praticamente identica agli esempi precedenti:

$tags = Post::find(1)->tags;

HasManyThrough

Rendiamo tutto un po’ più complicato ogni utente ha un paese diverso, quindi esiste un modello Country e una tabella countries. In questo caso è utilissima questo tipo di relazione.

All’interno del modello Country andiamo a creare un metodo che ritorna i post, in questo modo possiamo ottenere tutti i post degli utenti di quel paese. Attenzione quindi la tabella countries e questa posts non sono collegate tra loro direttamente, lo sono attraverso la tabella users. Così dobbiamo definire il modello da cui prendere i dati ma anche quello che collega i due:

public function posts(){

    return $this->hasManyThrough(App\Post::class, App\User::class);

}

Di default Eloquent andrà a cercare nella tabella posts il campo user_id e in quella users il campo country_id. I parametri che prende questa funzione sono ben 6: i primi due sono sempre da valorizzare e sono il modello da cui prendere i dati (in questo caso ‘App\Post’), il modello che fa da relazione (in questo caso ‘App\User’), terzo parametro la chiave esterna della tabella che fa da relazione con il modello attuale (in questo caso di default ‘country_id’ nella tabella users), quarto parametro la chiave esterna che collega la tabella che fa da relazione con quella finale (in questo caso di default user_id nella tabela posts), quinto parametro la chiave primaria del modello (in questo caso di default sarà la colonna id nella tabella countries), sesto elemento è la chiave primaria del modello finale (in questo caso sarà di default la colonna id nella tabella users).

return $this->hasManyThrough(App\Post::class, App\User::class, 'country_id', 'user_id', 'id', 'id');

La lettura a questo punto è, come al solito, molto facile:

$posts = Country::find(1)->posts;

Polimorfiche

Le relazioni polimorfiche permettono che un modello abbia relazioni con oggetti di natura diversa e un’unica entità correlata. Permettono cioè a un modello di avere più relazioni molti a uno con diversi modelli. Immaginiamo ad esempio di dover gestire i commenti nel nostro sito, questi commenti possono essere ad un post, e quindi essere una semplice relazione uno a molti, infatti ad ogni commento corrisponde un unico post, immaginiamo di dover gestire i commenti anche su video e inoltre dobbiamo permettere di rispondere al commento stesso, così come avviene proprio con wordpress (che abbiamo preso come esempio).

Seguendo l’esempio riportato nella documentazione di Laravel andiamo a prendere queste tre tabelle:
posts
id – integer
title – string
body – text

videos
id – integer
title – string
url – string

comments
id – integer
body – text
commentable_id – integer
commentable_type – string

Come potete notare la tabella commenti ha una struttura un po’ particolare. In commentable_id andiamo a mettere l’id dell’instanza che è commentata, che questa sia un post, un video o un commento stesso, andiamo a mettere il suo id. In commentable_type andiamo invece a mettere la classe dell’istanza commentata, riportata proprio come stringa nel nostro esempio potremmo trovare ad esempio ‘App\Post’ oppure ‘App\Video’ o ancora ‘App\Comment’.

Vediamo come impostare i modelli, andiamo nel primo Comment e qui andiamo a definire la relazione polimorfica creando la seguente funzione:

public function commentable(){

    return $this->morphTo();

}

mentre in Post, Video e Comment andremo a inserire questa funzione:

public function comments(){

    return $this->morphMany('App\Comment', 'commentable')

}

Questa funzione morphMany prende due parametri, il primo è il modello, il secondo è il tipo di morph.

Ancora una volta la lettura è sempre uguale:

$comments = Video::find(1)->comments;

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *