graficajam
Laravel, Traits e Modelli

Laravel, Traits e Modelli

Giamp28/03/2024

Ciao! Oggi parliamo dei trait in Laravel, una feature interessante ma spesso sottovalutata. A partire da Laravel 11, possiamo crearli rapidamente con Artisan:

php artisan make:trait NomeTrait

Cosa sono i trait?

I trait sono "frammenti di codice riutilizzabile" che possiamo inserire in più classi. Sono perfetti quando vogliamo condividere metodi tra diversi modelli senza usare l'ereditarietà classica.

In parole semplici: scrivi il codice una volta, lo usi ovunque serve.

Caso d'uso: sistema di History

Immaginiamo di voler tracciare tutte le modifiche ai nostri modelli in una tabella histories. Ogni volta che un record viene creato, aggiornato o cancellato, salviamo questa informazione.

Senza trait dovremmo riscrivere la stessa logica in ogni modello. Con i trait? Lo scriviamo una volta sola.

Creiamo il trait

php artisan make:trait WithHistory

Laravel crea il file in app/Traits/WithHistory.php. Apriamolo e scriviamo la nostra logica:

<?php

namespace App\Traits;

use App\Models\History;

trait WithHistory
{
    protected static function bootWithHistory()
    {
        // Quando viene creato un record
        static::created(function ($model) {
            History::create([
                'model_type' => get_class($model),
                'model_id' => $model->id,
                'action' => 'created',
                'user_id' => auth()->id(),
                'data' => json_encode($model->toArray()),
            ]);
        });

        // Quando viene aggiornato
        static::updated(function ($model) {
            History::create([
                'model_type' => get_class($model),
                'model_id' => $model->id,
                'action' => 'updated',
                'user_id' => auth()->id(),
                'data' => json_encode($model->getChanges()),
            ]);
        });

        // Quando viene cancellato
        static::deleted(function ($model) {
            History::create([
                'model_type' => get_class($model),
                'model_id' => $model->id,
                'action' => 'deleted',
                'user_id' => auth()->id(),
            ]);
        });
    }
}

Le funzioni static:: di Laravel sono una sorta di event listener che scattano a specifiche interazioni tra modelli e database e sono perfette per essere "incapsulate" in un trait.

ecco nel dettaglio:

metododesc
static::creating()Sta per essere creato
static::created()È stato appena creato
static::updating()Sta per essere aggiornato
static::updated()È stato appena aggiornato
static::deleting()Sta per essere cancellato
static::deleted()È stato appena cancellato
static::saving()Sta per essere salvato (create o update)
static::saved()È stato appena salvato

Il modello History

Prima di andare avanti, creiamo il modello e la migration per la tabella histories:

php artisan make:model History -m

Migration:

Schema::create('histories', function (Blueprint $table) {
    $table->id();
    $table->string('model_type'); // Tipo di modello (es. App\Models\Post)
    $table->unsignedBigInteger('model_id'); // ID del record
    $table->string('action'); // created, updated, deleted
    $table->unsignedBigInteger('user_id')->nullable();
    $table->json('data')->nullable(); // Dati del cambiamento
    $table->timestamps();
});

Usiamo il trait nei modelli

Ora possiamo aggiungere il nostro trait a qualsiasi modello vogliamo tracciare:

<?php
//Model/Post.php
class Post extends Model
{
    use WithHistory;
    // ...
}
//Model/News.php
class News extends Model
{
    use WithHistory;
    // ...
}

Fatto! Ora ogni creazione, modifica o cancellazione di Post e News viene automaticamente tracciata nella tabella histories.

Il vantaggio

La logica di tracking è scritta una sola volta nel trait. Se domani vogliamo aggiungere il tracking anche al modello Product, basta:

//Model/Product.php
class Product extends Model
{
    use WithHistory;
    // ...
}

Niente codice duplicato, tutto centralizzato e facile da gestire.

Visualizzare la history

Possiamo anche aggiungere una relazione nel trait per recuperare facilmente la storia di un modello:

trait WithHistory
{
    // ... metodi boot precedenti ...

    public function histories()
    {
        return $this->morphMany(History::class, 'model', 'model_type', 'model_id');
    }
}

E usarla così:

$post = Post::find(1);
$history = $post->histories; // Tutte le modifiche di questo post

// Oppure filtrare
$updates = $post->histories()->where('action', 'updated')->get();

Conclusione

I trait sono perfetti per condividere comportamenti comuni tra modelli diversi senza inquinare la gerarchia di classi. Il sistema di history che abbiamo creato è solo un esempio: potresti usare trait per gestire soft deletes custom, slug automatici, notifiche, e molto altro.

Per oggi è tutto, grazie per aver letto questo post. A presto!

Hai qualcosa da dire?

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

leggi anche...