Le relazioni polimorfiche sono un tipo di relazione tra entità di dato, che consente di associare un modello di dato a più modelli di dato su una singola associazione.

Ciò è particolarmente utile quando una tabella contiene collegamenti a più tabelle.

Vediamo come gestire le relazioni polimorfiche con Eloquent di Laravel.

Tempo di lettura stimato: 7 minuti

Problema

Nella progettazione e gestione dei database, a volte incontriamo situazioni al di fuori delle solite convenzioni dei database relazionali. Ad esempio, quando dobbiamo associare una singola tabella con più tabelle, possiamo incontrare delle relazioni polimorfiche. In questo articolo approfondiremo il concetto di relazione polimorfica, come implementarla elegantemente e in particolare come Eloquent di Laravel ci aiuta semplificandone lo sviluppo.

Scenario di Esempio

Supponiamo di dover implementare un sistema di gestione dei contenuti, comunemente detto Content Management System. Questa sistema deve gestire i commenti per vari tipi di contenuti: articoli , video e immagini . Ciascuno di questi tipi di contenuto viene archiviato nella rispettiva tabella nel database, ci troveremo quindi ad affrontare la seguente problematica:

Tabelle di “commento” ridondanti

Senza relazioni polimorfiche, probabilmente ricorreresti alla creazione di tabelle separate per i commenti associati a ciascun tipo di contenuto. Ciò comporterebbe una proliferazione di tabelle come article_comments, video_comments e image_comments.

Gestire e mantenere queste tabelle potrebbe diventare complicato, man mano che il CMS si popola. Per non parlare della complessità delle query e del codice.

Per risolvere questo problema, possiamo avere due potenziali soluzioni:

  • o ripetere in ciascuna delle tabelle l’attributo condiviso più volte. Il che sarà un incubo ogni volta che dovremmo aggiungere dei commenti;
  • Oppure creiamo una super tabella chiamata Comments in cui memorizzare tutti gli attributi correlati e quindi creare sottotabelle per ciascun tipo.

Come le relazioni polimorfiche risolvono il problema

Fondamentalmente, una relazione polimorfica consente di associare una singola tabella a più tabelle. Ciò garantisce flessibilità e consente di evitare la creazione di tabelle separate per ogni tipo di associazione. È invece possibile utilizzare una singola tabella per gestire le associazioni in modo dinamico.

Tornando all’esempio del CMS: con una relazione polimorfica, possiamo creare una tabella di commenti collegabile ad articoli, video e immagini, dipende dal contenuto commentato. Quindi le relazioni polimorfiche ci consentono di eliminare le tabelle ridondanti.

Vediamo ora quali sono gli strumenti che Eloquent ci mette a disposizione per implementare le relazioni polimorfiche.

Come funzionano le relazioni polimorfiche

Il polimorfismo consente a una tabella di associarsi a più tabelle utilizzando due attributi specifici, il tipo e l’identificativo: type e id.

Questi campi vengono utilizzati per archiviare le informazioni necessarie per identificare il tipo di record associato e l’identificatore univoco (solitamente la chiave primaria). Nell’esempio che andremo ad approfondire:

  • commentable_type: memorizza il tipo del modello associato (“Articolo” o “Video”);
  • commentable_id contiene il suo ID corrispondente.

La letture dei dati dalle associazioni polimorfiche implica in genere l’uso di JOIN. Nel nostro caso la JOIN unisce la tabella comments con la corrispondente tabella dei contenuti in base a commentable_type e commentable_id . Ciò consente di accedere in modo efficiente ai dati su diversi modelli.

La query SQL che ci consente di recuperare i commenti e gli articoli associati:

SELECT c.*, a.title AS article_title
FROM comments c
JOIN articles a ON c.commentable_type = 'Article' AND c.commentable_id = a.id;

La query seguente per leggere i commenti associati ai video:

SELECT c.*, v.title AS video_title
FROM comments c
JOIN videos v ON c.commentable_type = 'Video' AND c.commentable_id = v.id;

Implementazione con Laravel Eloquent

In Laravel, Eloquent mette a disposizione strumenti ideali per lavorare con relazioni polimorfiche. Ecco una guida passo passo per implementarli:

Torniamo al modello Comment che può essere associato a vari tipi di contenuto come ArticleVideo e Image.

Andiamo a vedere passo passo la configurazione della associazione polimorfica:

Creazione migration delle tabelle

Innanzitutto, abbiamo bisogno dei file di migrazione per creare le tabelle del database necessarie. Ecco come creare le migration per le tre tabelle:

php artisan make:migration create_articles_table
php artisan make:migration create_videos_table
php artisan make:migration create_comments_table

Il file migration per la tabella comments deve avere i campi definiti come segue

public function up()
{
    Schema::create('comments', function (Blueprint $table) {
        $table->id();
        $table->text('body');
        $table->unsignedBigInteger('commentable_id');
        $table->string('commentable_type');
        $table->timestamps();
    });
}

Dopo aver creato le tabelle con il seguente comando:

php artisan migrate

Andiamo a creare i modelli Comment, Article, Video e Image

php artisan make:model Article
php artisan make:model Video
php artisan make:model Image
php artisan make:model Comment

Il metodo morphTo() viene utilizzato nel modello che verrà associato agli altri modelli in modo polimorfico. Consente di recuperare il modello associato senza specificare esplicitamente il tipo esatto. Semplifica il processo di lavoro con le relazioni polimorfiche.

Articoli correlati
Newsletter sull’Innovazione
Non perderti le notizie più importanti sull'Innovazione. Iscriviti per riceverle via e-mail.
class Comment extends Model
{
    use HasFactory;
    
    public function commentable(){
        return $this->morphTo();
    }
}

Di seguito il model Article

class Article extends Model
{
    use HasFactory;

    public function comments(){
        return $this->morphMany(Comment::class, 'commentable');
    }
}

Di seguito il model Video

class Video extends Model
{
    use HasFactory;

    public function comments(){
        return $this->morphMany(Comment::class, 'commentable');
    }
}

Di seguito il model Image

class Image extends Model
{
    use HasFactory;

    public function comments(){
        return $this->morphMany(Comment::class, 'commentable');
    }
}

Il metodo morphMany() viene utilizzato per definire una relazione polimorfica uno-a-molti su un modello. Ti consente di recuperare più record associati da vari modelli in modo pulito ed efficiente. Viene spesso utilizzato quando un modello può essere collegato a diversi tipi di modelli correlati.

Relazione Polimorfica Uno a Uno

Creazione di modelli

Creiamo due model per rappresentare una relazione polimorfica uno a uno, lavoriamo con un modello Comment e un modello Image.

php artisan make:model Comment
php artisan make:model Image
Definire la relazione

Nel modello Comment , stabiliamo la relazione polimorfica uno-a-uno con il modello Image creando un metodo image. Questo metodo utilizza la relazione eloquent morphOne

// app/models/Comment.php
public function image()
{
    return $this->morphOne(Image::class, 'imageable');
}

Nel modello Image, definiamo l’ inverso della relazione creando un metodo imageable. Questo metodo utilizza la relazione morphTo di Eloquent:

// app/models/Image.php
public function imageable()
{
    return $this->morphTo();
}
Creazione e lettura

È ora possibile creare e leggere informazioni utilizzando la relazione polimorfica uno a uno. Ecco come possiamo associare un’immagine a un commento:

// Create a comment
$comment = new Comment(['text' => 'A beautiful realistic image.']);
$comment->save();

// Create an image for the comment
$image = new Image(['url' => 'https://bloginnovazione.it/virtual-reality.jpg']);
$comment->image()->save($image);

Per leggere un’immagine associata a un commento:

$comment = Comment::find(1);
$image = $comment->image;
Aggiornamento ed eliminazione dei record

L’aggiornamento e l’eliminazione di record correlati tramite relazione polimorfica è semplice. Per aggiornare l’URL di un’immagine:

$image = Image::find(1);
$image->update(['url' => 'https://bloginnovazione.it/new-virtual-reality.jpg']);

Per eliminare un’immagine:

$image = Image::find(1);
$image->delete();

Relazione Polimorfica Uno a Molti

Una relazione polimorfica uno a molti viene utilizzata quando un modello di dati appartiene a più di un altro modello su un singolo modello di associazione. Ad esempio, se disponiamo di tabelle di post e video, entrambi devono aggiungere un sistema di commenti.

Creazione di modelli

Creiamo tre model per rappresentare una relazione polimorfica uno a molti, lavoriamo con un modello Comment, un modello Post e un modello Image.

php artisan make:model Comment
php artisan make:model Image
php artisan make:model Post
Definire la relazione

Nel modello Comment , stabiliamo la relazione polimorfica uno-a-molti con il modello Image e il modello Post. Questo metodo utilizza la relazione eloquent morphTo, e si sviluppa in funzione del tipo di entità a cui il commento è riferito: Image o Post.

// app/models/Comment.php
class Comment extends Model
{
    /**
     * Get all of the owning commentable models.
     */
    public function commentable()
    {
        return $this->morphTo();
    }
}

Nel modello Image, definiamo l’ inverso della relazione creando un metodo comments. Questo metodo utilizza la relazione morphMany di Eloquent:

// app/models/Image.php
class Image extends Model
{
    /**
     * Get all of the image's comments.
     */
    public function comments()
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}

Anche nel modello Post, definiamo l’inverso della relazione creando un metodo comments. Questo metodo utilizza la relazione morphMany di Eloquent:

// app/models/Post.php
class Post extends Model
{
    /**
     * Get all of the post's comments.
     */
    public function comments()
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}
Creazione e lettura

È ora possibile creare e leggere informazioni utilizzando la relazione polimorfica uno a uno. Ecco come possiamo associare un’immagine a un commento:

// Find a Post and Create a comment
$post = Post::find(1);	
 
$comment = new Comment;
$comment->body = "Hi, Here we are bloginnovazione.it";
 
$post->comments()->save($comment);

// Find an Image and Create a comment
$image = Image::find(1);	
 
$comment = new Comment;
$comment->body = "Hi, Here we are bloginnovazione.it";
 
$image->comments()->save($comment);	

Se invece volessimo scrivere più commenti contemporaneamente:

// Create an image
$image = new Image(['url' => 'https://bloginnovazione.it/image.jpg']);
$image->save();

// Create a post
$post = new Post(['url' => 'https://bloginnovazione.it/post']);
$post->save();

// Add comments to the image
$image->comments()->createMany([
    ['text' => 'Beautiful image!'],
    ['text' => 'I love this picture.'],
]);

// Add comments to the post
$post->comments()->createMany([
    ['text' => 'Great post!'],
    ['text' => 'Well done.'],
]);	

Per leggere tutti i commenti associati a una immagine o a un post:

// find a post and print comments
$post = Post::find(1);	 
dd($post->comments);

// find an image and print comments
$image = Image::find(1);	 
dd($image->comments);
Aggiornamento ed eliminazione dei record

L’aggiornamento e l’eliminazione di record correlati tramite relazione polimorfica è semplice. Per aggiornare l’URL di un’immagine:

$comment = Comment::find(1);
$comment->update(['text' => 'Absolutely amazing!']);

Per eliminare un’commento:

$comment = Comment::find(1);
$comment->delete();

Letture Correlate

Ercole Palmeri

Newsletter sull’Innovazione
Non perderti le notizie più importanti sull'Innovazione. Iscriviti per riceverle via e-mail.