Relazioni

Introduzione

Le tabelle del database spesso sono correlate tra loro. Ad esempio, un post del blog può avere molti commenti oppure un ordine potrebbe essere legato all’utente che lo ha effettuato. Eloquent rende semplice gestire e lavorare con queste relazioni e supporta una varietà di relazioni comuni:

Definizione delle Relazioni

Le relazioni in Eloquent sono definite come metodi nelle tue classi modello Eloquent. Poiché le relazioni fungono anche da potenti query builder, definire le relazioni come metodi offre potenti capacità di concatenazione di metodi e di esecuzione delle query. Ad esempio, possiamo aggiungere ulteriori vincoli alla query su questa relazione posts:

    $user->posts()->where('active', 1)->get();

Ma, prima di approfondire l’uso delle relazioni, impariamo come definire ogni tipo di relazione supportata da Eloquent.

One to One / Has One

Una relazione uno a uno è un tipo molto semplice di relazione nel database. Ad esempio, un modello User potrebbe essere associato a un modello Phone. Per definire questa relazione, posizioneremo un metodo phone sul modello User. Il metodo phone deve chiamare il metodo hasOne e restituire il suo risultato. Il metodo hasOne è disponibile per il tuo modello tramite la classe base Illuminate\Database\Eloquent\Model del modello:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;

class User extends Model
{
    /**
     * Get the phone associated with the user.
     */
    public function phone(): HasOne
    {
        return $this->hasOne(Phone::class);
    }
}

Il primo argomento passato al metodo hasOne è il nome della classe del modello correlato. Una volta definita la relazione, possiamo recuperare il record correlato utilizzando le proprietà dinamiche di Eloquent. Le proprietà dinamiche ti permettono di accedere ai metodi di relazione come se fossero proprietà definite sul modello:

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

Eloquent determina la chiave esterna della relazione basandosi sul nome del modello padre. In questo caso, si presume automaticamente che il modello Phone abbia una chiave esterna user_id. Se desideri sovrascrivere questa convenzione, puoi passare un secondo argomento al metodo hasOne:

return $this->hasOne(Phone::class, 'foreign_key');

Inoltre, Eloquent presume che la chiave esterna debba avere un valore corrispondente alla colonna della chiave primaria del modello padre. In altre parole, Eloquent cercherà il valore della colonna id dell’utente nella colonna user_id del record Phone. Se desideri che la relazione utilizzi un valore di chiave primaria diverso da id o dalla proprietà $primaryKey del tuo modello, puoi passare un terzo argomento al metodo hasOne:

return $this->hasOne(Phone::class, 'foreign_key', 'local_key');

Definire l’inverso della relazione

Quindi, possiamo accedere al modello Phone dal nostro modello User. Successivamente, definiamo una relazione sul modello Phone che ci permetterà di accedere all’utente che possiede il telefono. Possiamo definire l’inverso di una relazione hasOne usando il metodo belongsTo:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Phone extends Model
{
    /**
     * Get the user that owns the phone.
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}

Invocando il metodo user, Eloquent cercherà di trovare un modello User il cui id corrisponde alla colonna user_id nel modello Phone.

Eloquent determina il nome della chiave esterna esaminando il nome del metodo di relazione e aggiungendo il suffisso _id. Quindi, in questo caso, Eloquent presuppone che il modello Phone abbia una colonna user_id. Tuttavia, se la chiave esterna nel modello Phone non è user_id, puoi passare un nome di chiave personalizzato come secondo argomento al metodo belongsTo:

/**
 * Get the user that owns the phone.
 */
public function user(): BelongsTo
{
    return $this->belongsTo(User::class, 'foreign_key');
}

Se il modello padre non utilizza id come chiave primaria, o desideri trovare il modello associato utilizzando una colonna diversa, puoi passare un terzo argomento al metodo belongsTo specificando la chiave personalizzata della tabella padre:

/**
 * Get the user that owns the phone.
 */
public function user(): BelongsTo
{
    return $this->belongsTo(User::class, 'foreign_key', 'owner_key');
}

Uno a molti / Has Many

Una relazione uno a molti viene utilizzata per definire relazioni in cui un singolo modello è il genitore di uno o più modelli figli. Per esempio, un post del blog può avere un numero infinito di commenti. Come tutte le altre relazioni di Eloquent, le relazioni uno a molti sono definite creando un metodo sul tuo modello Eloquent:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Post extends Model
{
    /**
     * Get the comments for the blog post.
     */
    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }
}

Ricorda che Eloquent determinerà automaticamente la colonna della chiave esterna corretta per il modello Comment. Per convenzione, Eloquent prenderà il nome del modello genitore in "snake case" e lo aggiungerà con il suffisso _id. Quindi, in questo esempio, Eloquent presumerà che la colonna della chiave esterna nel modello Comment sia post_id.

Una volta definito il metodo della relazione, possiamo accedere alla collezione di commenti correlati accedendo alla proprietà comments. Ricorda che, poiché Eloquent fornisce "proprietà di relazione dinamiche", possiamo accedere ai metodi delle relazioni come se fossero proprietà definite sul modello:

use App\Models\Post;

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

foreach ($comments as $comment) {
    // ...
}

Poiché tutte le relazioni fungono anche da builder di query, puoi aggiungere ulteriori vincoli alla query della relazione chiamando il metodo comments e continuando a concatenare condizioni sulla query:

$comment = Post::find(1)->comments()
                    ->where('title', 'foo')
                    ->first();

Come il metodo hasOne, puoi anche sovrascrivere le chiavi esterne e locali passando argomenti aggiuntivi al metodo hasMany:

return $this->hasMany(Comment::class, 'foreign_key');

return $this->hasMany(Comment::class, 'foreign_key', 'local_key');

Idratare automaticamente i modelli genitore sui figli

Anche quando si utilizza l’eager loading di Eloquent, possono sorgere problemi di query "N + 1" se tenti di accedere al modello genitore da un modello figlio mentre cicli attraverso i modelli figli:

$posts = Post::with('comments')->get();

foreach ($posts as $post) {
    foreach ($post->comments as $comment) {
        echo $comment->post->title;
    }
}

Nell’esempio sopra, si è introdotto un problema di query "N + 1" perché, anche se i commenti sono stati eager loaded per ogni modello Post, Eloquent non idrata automaticamente il genitore Post su ogni modello figlio Comment.

Se desideri che Eloquent idrati automaticamente i modelli genitore nei loro figli, puoi invocare il metodo chaperone quando definisci una relazione hasMany:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Post extends Model
{
    /**
     * Ottieni i commenti per il post del blog.
     */
    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class)->chaperone();
    }
}

Oppure, se preferisci abilitare l’idratazione automatica dei genitori a runtime, puoi invocare il metodo chaperone quando carichi eager la relazione:

use App\Models\Post;

$posts = Post::with([
    'comments' => fn ($comments) => $comments->chaperone(),
])->get();

One to Many (Inverso) / Belongs To

Ora che possiamo accedere a tutti i commenti di un post, definiamo una relazione che permetta a un commento di accedere al suo post padre. Per definire l’inverso di una relazione hasMany, definisci un metodo di relazione nel modello figlio che chiama il metodo belongsTo:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Comment extends Model
{
    /**
     * Ottieni il post che possiede il commento.
     */
    public function post(): BelongsTo
    {
        return $this->belongsTo(Post::class);
    }
}

Una volta definita la relazione, possiamo recuperare il post padre di un commento accedendo alla proprietà dinamica di relazione post:

use App\Models\Comment;

$comment = Comment::find(1);

return $comment->post->title;

Nell’esempio sopra, Eloquent tenterà di trovare un modello Post che ha un id che corrisponde alla colonna post_id nel modello Comment.

Eloquent determina il nome della chiave esterna predefinita esaminando il nome del metodo di relazione e aggiungendo un _ seguito dal nome della colonna della chiave primaria del modello padre. Quindi, in questo esempio, Eloquent assumerà che la chiave esterna del modello Post nella tabella comments sia post_id.

Tuttavia, se la chiave esterna per la tua relazione non segue queste convenzioni, puoi passare un nome di chiave esterna personalizzato come secondo argomento al metodo belongsTo:

/**
 * Ottieni il post che "possiede" il commento.
 */
public function post(): BelongsTo
{
    return $this->belongsTo(Post::class, 'foreign_key');
}

Se il tuo modello padre non usa id come chiave primaria, o desideri trovare il modello associato utilizzando una colonna diversa, puoi passare un terzo argomento al metodo belongsTo specificando la chiave personalizzata della tua tabella padre:

/**
 * Ottieni il post che "possiede" il commento.
 */
public function post(): BelongsTo
{
    return $this->belongsTo(Post::class, 'foreign_key', 'owner_key');
}

Modelli Predefiniti

Le relazioni belongsTo, hasOne, hasOneThrough e morphOne ti permettono di definire un modello predefinito che verrà restituito se la relazione specificata è null. Questo pattern è spesso chiamato Null Object pattern e può aiutare a rimuovere controlli condizionali nel tuo codice. Nell’esempio seguente, la relazione user restituirà un modello vuoto App\Models\User se nessun utente è associato al modello Post:

    /**
     * Ottieni l'autore del post.
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class)->withDefault();
    }

Per popolare il modello predefinito con attributi, puoi passare un array o una closure al metodo withDefault:

    /**
     * Ottieni l'autore del post.
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class)->withDefault([
            'name' => 'Autore Ospite',
        ]);
    }

    /**
     * Ottieni l'autore del post.
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class)->withDefault(function (User $user, Post $post) {
            $user->name = 'Autore Ospite';
        });
    }

Interrogare le Relazioni "Belongs To"

Quando interroghi i figli di una relazione "belongs to", puoi costruire manualmente la clausola where per recuperare i modelli Eloquent corrispondenti:

use App\Models\Post;

$posts = Post::where('user_id', $user->id)->get();

Tuttavia, potrebbe essere più comodo usare il metodo whereBelongsTo, che determinerà automaticamente la relazione corretta e la chiave esterna per il modello fornito:

$posts = Post::whereBelongsTo($user)->get();

Puoi anche fornire un’istanza di collection al metodo whereBelongsTo. In questo modo, Laravel recupererà i modelli che appartengono a uno qualsiasi dei modelli genitori nella collezione:

$users = User::where('vip', true)->get();

$posts = Post::whereBelongsTo($users)->get();

Per impostazione predefinita, Laravel determinerà la relazione associata al modello fornito basandosi sul nome della classe del modello; tuttavia, puoi specificare manualmente il nome della relazione fornendolo come secondo argomento al metodo whereBelongsTo:

$posts = Post::whereBelongsTo($user, 'author')->get();

Has One di Molti

A volte un modello può avere molti modelli correlati, ma desideri recuperare facilmente il modello "più recente" o "più vecchio" della relazione. Ad esempio, un modello User può essere correlato a molti modelli Order, ma vuoi definire un modo comodo per interagire con l’ordine più recente che l’utente ha effettuato. Puoi ottenere questo usando il tipo di relazione hasOne combinato con i metodi ofMany:

/**
 * Ottieni l'ordine più recente dell'utente.
 */
public function latestOrder(): HasOne
{
    return $this->hasOne(Order::class)->latestOfMany();
}

Allo stesso modo, puoi definire un metodo per recuperare il modello correlato "più vecchio", o primo, di una relazione:

/**
 * Ottieni l'ordine più vecchio dell'utente.
 */
public function oldestOrder(): HasOne
{
    return $this->hasOne(Order::class)->oldestOfMany();
}

Per impostazione predefinita, i metodi latestOfMany e oldestOfMany recuperano il modello correlato più recente o più vecchio basato sulla chiave primaria del modello, che deve essere ordinabile. Tuttavia, a volte potresti voler recuperare un singolo modello da una relazione più ampia utilizzando un criterio di ordinamento diverso.

Ad esempio, usando il metodo ofMany, puoi recuperare l’ordine più costoso dell’utente. Il metodo ofMany accetta la colonna ordinabile come primo argomento e quale funzione di aggregazione (min o max) applicare quando si interroga il modello correlato:

/**
 * Ottieni l'ordine più grande dell'utente.
 */
public function largestOrder(): HasOne
{
    return $this->hasOne(Order::class)->ofMany('price', 'max');
}

Poiché PostgreSQL non supporta l’esecuzione della funzione MAX sulle colonne UUID, attualmente non è possibile utilizzare relazioni one-of-many in combinazione con colonne UUID di PostgreSQL.

Convertire le relazioni "Many" in relazioni "Has One"

Spesso, quando si recupera un singolo modello utilizzando i metodi latestOfMany, oldestOfMany o ofMany, hai già definita una relazione "has many" per lo stesso modello. Per comodità, Laravel permette di convertire facilmente questa relazione in una relazione "has one" invocando il metodo one sulla relazione:

/**
 * Ottieni gli ordini dell'utente.
 */
public function orders(): HasMany
{
    return $this->hasMany(Order::class);
}

/**
 * Ottieni l'ordine più grande dell'utente.
 */
public function largestOrder(): HasOne
{
    return $this->orders()->one()->ofMany('price', 'max');
}

Relazioni Avanzate "Has One of Many"

È possibile costruire relazioni "has one of many" più avanzate. Ad esempio, un modello Product può avere molti modelli Price associati che vengono mantenuti nel sistema anche dopo che sono state pubblicate nuove tariffe. Inoltre, i nuovi dati di prezzo per il prodotto possono essere pubblicati in anticipo per entrare in vigore in una data futura tramite una colonna published_at.

In sintesi, dobbiamo recuperare l’ultima tariffa pubblicata la cui data di pubblicazione non è nel futuro. Inoltre, se due prezzi hanno la stessa data di pubblicazione, preferiremo il prezzo con l’ID maggiore. Per fare ciò, dobbiamo passare un array al metodo ofMany che contiene le colonne ordinabili che determinano l’ultimo prezzo. Inoltre, verrà fornita una closure come secondo argomento al metodo ofMany. Questa closure sarà responsabile di aggiungere ulteriori vincoli sulla data di pubblicazione alla query della relazione:

/**
 * Ottieni il prezzo attuale per il prodotto.
 */
public function currentPricing(): HasOne
{
    return $this->hasOne(Price::class)->ofMany([
        'published_at' => 'max',
        'id' => 'max',
    ], function (Builder $query) {
        $query->where('published_at', '<', now());
    });
}

Has One Through

La relazione "has-one-through" definisce una relazione uno-a-uno con un altro modello. Tuttavia, questa relazione indica che il modello dichiarante può essere associato a un’istanza di un altro modello procedendo attraverso un terzo modello.

Per esempio, in un’applicazione di un’officina meccanica, ogni modello Mechanic può essere associato a un modello Car, e ogni modello Car può essere associato a un modello Owner. Sebbene il meccanico e il proprietario non abbiano una relazione diretta nel database, il meccanico può accedere al proprietario attraverso il modello Car. Vediamo le tabelle necessarie per definire questa relazione:

    mechanics
        id - integer
        name - string

    cars
        id - integer
        model - string
        mechanic_id - integer

    owners
        id - integer
        name - string
        car_id - integer

Ora che abbiamo esaminato la struttura delle tabelle per la relazione, definiamo la relazione nel modello Mechanic:

    <?php

    namespace App\Models;

    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\Relations\HasOneThrough;

    class Mechanic extends Model
    {
        /**
         * Ottieni il proprietario dell'auto.
         */
        public function carOwner(): HasOneThrough
        {
            return $this->hasOneThrough(Owner::class, Car::class);
        }
    }

Il primo argomento passato al metodo hasOneThrough è il nome del modello finale a cui desideriamo accedere, mentre il secondo argomento è il nome del modello intermedio.

Oppure, se le relazioni rilevanti sono già state definite su tutti i modelli coinvolti nella relazione, puoi definire fluentemente una relazione "has-one-through" invocando il metodo through e fornendo i nomi di quelle relazioni. Per esempio, se il modello Mechanic ha una relazione cars e il modello Car ha una relazione owner, puoi definire una relazione "has-one-through" che collega il meccanico e il proprietario in questo modo:

// Sintassi basata su stringhe...
return $this->through('cars')->has('owner');

// Sintassi dinamica...
return $this->throughCars()->hasOwner();

Convenzioni delle Chiavi

Verranno utilizzate le convenzioni standard delle chiavi esterne di Eloquent quando si eseguono le query delle relazioni. Se desideri personalizzare le chiavi della relazione, puoi passarle come terzo e quarto argomento al metodo hasOneThrough. Il terzo argomento è il nome della chiave esterna sul modello intermedio. Il quarto argomento è il nome della chiave esterna sul modello finale. Il quinto argomento è la chiave locale, mentre il sesto argomento è la chiave locale del modello intermedio:

class Mechanic extends Model
{
    /**
     * Ottieni il proprietario dell'auto.
     */
    public function carOwner(): HasOneThrough
    {
        return $this->hasOneThrough(
            Owner::class,
            Car::class,
            'mechanic_id', // Chiave esterna sulla tabella delle auto...
            'car_id', // Chiave esterna sulla tabella dei proprietari...
            'id', // Chiave locale sulla tabella dei meccanici...
            'id' // Chiave locale sulla tabella delle auto...
        );
    }
}

Oppure, come discusso in precedenza, se le relazioni rilevanti sono già state definite su tutti i modelli coinvolti nella relazione, puoi definire fluentemente una relazione "has-one-through" invocando il metodo through e fornendo i nomi di quelle relazioni. Questo approccio offre il vantaggio di riutilizzare le convenzioni delle chiavi già definite sulle relazioni esistenti:

// Sintassi basata su stringhe...
return $this->through('cars')->has('owner');

// Sintassi dinamica...
return $this->throughCars()->hasOwner();

Has Many Through

La relazione "has-many-through" offre un modo comodo per accedere a relazioni lontane tramite una relazione intermedia. Ad esempio, supponiamo di costruire una piattaforma di deployment come Laravel Vapor. Un modello Project potrebbe accedere a molti modelli Deployment attraverso un modello intermedio Environment. Usando questo esempio, potresti facilmente raccogliere tutti i deployments per un determinato progetto. Vediamo le tabelle necessarie per definire questa relazione:

projects
    id - integer
    name - string

environments
    id - integer
    project_id - integer
    name - string

deployments
    id - integer
    environment_id - integer
    commit_hash - string

Ora che abbiamo esaminato la struttura delle tabelle per la relazione, definiamo la relazione sul modello Project:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;

class Project extends Model
{
    /**
     * Ottieni tutti i deployments per il progetto.
     */
    public function deployments(): HasManyThrough
    {
        return $this->hasManyThrough(Deployment::class, Environment::class);
    }
}

Il primo argomento passato al metodo hasManyThrough è il nome del modello finale a cui vogliamo accedere, mentre il secondo argomento è il nome del modello intermedio.

Oppure, se le relazioni rilevanti sono già state definite su tutti i modelli coinvolti nella relazione, puoi definire fluentemente una relazione "has-many-through" invocando il metodo through e fornendo i nomi di quelle relazioni. Ad esempio, se il modello Project ha una relazione environments e il modello Environment ha una relazione deployments, puoi definire una relazione "has-many-through" che collega il progetto e i deployments in questo modo:

// Sintassi basata su stringhe...
return $this->through('environments')->has('deployments');

// Sintassi dinamica...
return $this->throughEnvironments()->hasDeployments();

Anche se la tabella del modello Deployment non contiene una colonna project_id, la relazione hasManyThrough fornisce accesso ai deployments di un progetto tramite $project->deployments. Per recuperare questi modelli, Eloquent esamina la colonna project_id nella tabella del modello intermedio Environment. Dopo aver trovato gli ID degli environment rilevanti, vengono utilizzati per interrogare la tabella del modello Deployment.

Convenzioni Chiave

Le convenzioni tipiche delle chiavi esterne Eloquent saranno utilizzate quando si eseguono le query delle relazioni. Se desideri personalizzare le chiavi della relazione, puoi passarle come terzo e quarto argomento al metodo hasManyThrough. Il terzo argomento è il nome della chiave esterna sul modello intermedio. Il quarto argomento è il nome della chiave esterna sul modello finale. Il quinto argomento è la chiave locale, mentre il sesto argomento è la chiave locale del modello intermedio:

class Project extends Model
{
    public function deployments(): HasManyThrough
    {
        return $this->hasManyThrough(
            Deployment::class,
            Environment::class,
            'project_id', // Chiave esterna sulla tabella environments...
            'environment_id', // Chiave esterna sulla tabella deployments...
            'id', // Chiave locale sulla tabella projects...
            'id' // Chiave locale sulla tabella environments...
        );
    }
}

Oppure, come discusso in precedenza, se le relazioni rilevanti sono già state definite su tutti i modelli coinvolti nella relazione, puoi definire fluentemente una relazione "has-many-through" invocando il metodo through e fornendo i nomi di quelle relazioni. Questo approccio offre il vantaggio di riutilizzare le convenzioni delle chiavi già definite sulle relazioni esistenti:

// Sintassi basata su stringa...
return $this->through('environments')->has('deployments');

// Sintassi dinamica...
return $this->throughEnvironments()->hasDeployments();

Relazioni Molti-a-Molti

Le relazioni molti-a-molti sono leggermente più complesse rispetto alle relazioni hasOne e hasMany. Un esempio di relazione molti-a-molti è un utente che ha molti ruoli e questi ruoli sono condivisi anche da altri utenti nell’applicazione. Ad esempio, a un utente può essere assegnato il ruolo di "Author" e "Editor"; tuttavia, questi ruoli possono essere assegnati anche ad altri utenti. Quindi, un utente ha molti ruoli e un ruolo ha molti utenti.

Struttura delle tabelle

Per definire questa relazione, sono necessarie tre tabelle nel database: users, roles e role_user. La tabella role_user deriva dall’ordine alfabetico dei nomi dei modelli correlati e contiene le colonne user_id e role_id. Questa tabella funge da collegamento tra utenti e ruoli.

Ricorda, poiché un ruolo può appartenere a molti utenti, non possiamo semplicemente aggiungere una colonna user_id nella tabella roles. Ciò significherebbe che un ruolo potrebbe appartenerne solo a un singolo utente. Per permettere che i ruoli siano assegnati a più utenti, è necessaria la tabella role_user. Possiamo riassumere la struttura delle tabelle della relazione nel seguente modo:

    users
        id - integer
        name - string

    roles
        id - integer
        name - string

    role_user
        user_id - integer
        role_id - integer

Struttura del Modello

Le relazioni molti-a-molti si definiscono creando un metodo che restituisce il risultato del metodo belongsToMany. Il metodo belongsToMany è fornito dalla classe base Illuminate\Database\Eloquent\Model usata da tutti i modelli Eloquent della tua applicazione. Ad esempio, definiamo un metodo roles nel nostro modello User. Il primo argomento passato a questo metodo è il nome della classe del modello correlato:

    <?php

    namespace App\Models;

    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\Relations\BelongsToMany;

    class User extends Model
    {
        /**
         * I ruoli appartenenti all'utente.
         */
        public function roles(): BelongsToMany
        {
            return $this->belongsToMany(Role::class);
        }
    }

Una volta definita la relazione, puoi accedere ai ruoli dell’utente usando la proprietà dinamica roles:

    use App\Models\User;

    $user = User::find(1);

    foreach ($user->roles as $role) {
        // ...
    }

Poiché tutte le relazioni funzionano anche come costruttori di query, puoi aggiungere ulteriori vincoli alla query della relazione chiamando il metodo roles e concatenando condizioni alla query:

    $roles = User::find(1)->roles()->orderBy('name')->get();

Per determinare il nome della tabella intermedia della relazione, Eloquent unirà i nomi dei due modelli correlati in ordine alfabetico. Tuttavia, puoi sovrascrivere questa convenzione passando un secondo argomento al metodo belongsToMany:

    return $this->belongsToMany(Role::class, 'role_user');

Oltre a personalizzare il nome della tabella intermedia, puoi anche personalizzare i nomi delle colonne delle chiavi nella tabella passando ulteriori argomenti al metodo belongsToMany. Il terzo argomento è il nome della chiave esterna del modello su cui stai definendo la relazione, mentre il quarto argomento è il nome della chiave esterna del modello a cui ti stai collegando:

    return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id');

Definire l’Inverso della Relazione

Per definire l’"inverso" di una relazione molti-a-molti, dovresti definire un metodo sul modello correlato che restituisce anche il risultato del metodo belongsToMany. Per completare il nostro esempio utente / ruolo, definiamo il metodo users sul modello Role:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Role extends Model
{
    /**
     * The users that belong to the role.
     */
    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class);
    }
}

Come puoi vedere, la relazione è definita esattamente allo stesso modo del suo omologo nel modello User, ad eccezione del riferimento al modello App\Models\User. Poiché stiamo riutilizzando il metodo belongsToMany, tutte le opzioni di personalizzazione per tabelle e chiavi sono disponibili quando si definisce l’"inverso" di relazioni molti-a-molti.

Recupero delle colonne della tabella intermedia

Come hai già imparato, lavorare con relazioni molti-a-molti richiede la presenza di una tabella intermedia. Eloquent fornisce alcuni modi molto utili per interagire con questa tabella. Ad esempio, supponiamo che il nostro modello User abbia molti modelli Role a cui è correlato. Dopo aver accesso a questa relazione, possiamo accedere alla tabella intermedia usando l’attributo pivot sui modelli:

use App\Models\User;

$user = User::find(1);

foreach ($user->roles as $role) {
    echo $role->pivot->created_at;
}

Nota che ogni modello Role che recuperiamo ha automaticamente assegnato un attributo pivot. Questo attributo contiene un modello che rappresenta la tabella intermedia.

Per impostazione predefinita, solo le chiavi del modello saranno presenti sul modello pivot. Se la tua tabella intermedia contiene attributi extra, devi specificarli quando definisci la relazione:

return $this->belongsToMany(Role::class)->withPivot('active', 'created_by');

Se desideri che la tua tabella intermedia abbia i timestamp created_at e updated_at che vengono mantenuti automaticamente da Eloquent, chiama il metodo withTimestamps quando definisci la relazione:

return $this->belongsToMany(Role::class)->withTimestamps();

Le tabelle intermedie che utilizzano i timestamp mantenuti automaticamente da Eloquent devono avere entrambe le colonne created_at e updated_at.

Personalizzare il nome dell’attributo pivot

Come già menzionato, gli attributi della tabella intermedia possono essere acceduti nei modelli tramite l’attributo pivot. Tuttavia, puoi personalizzare il nome di questo attributo per riflettere meglio il suo scopo nella tua applicazione.

Per esempio, se la tua applicazione ha utenti che possono iscriversi ai podcast, probabilmente hai una relazione molti-a-molti tra utenti e podcast. In questo caso, potresti voler rinominare l’attributo della tabella intermedia in subscription invece di pivot. Questo si può fare usando il metodo as quando definisci la relazione:

    return $this->belongsToMany(Podcast::class)
                    ->as('subscription')
                    ->withTimestamps();

Una volta specificato l’attributo personalizzato della tabella intermedia, puoi accedere ai dati della tabella intermedia usando il nome personalizzato:

    $users = User::with('podcasts')->get();

    foreach ($users->flatMap->podcasts as $podcast) {
        echo $podcast->subscription->created_at;
    }

Filtrare le Query tramite le Colonne della Tabella Intermedia

Puoi anche filtrare i risultati restituiti dalle query delle relazioni belongsToMany utilizzando i metodi wherePivot, wherePivotIn, wherePivotNotIn, wherePivotBetween, wherePivotNotBetween, wherePivotNull e wherePivotNotNull quando definisci la relazione:

return $this->belongsToMany(Role::class)
            ->wherePivot('approved', 1);
return $this->belongsToMany(Role::class)
            ->wherePivotIn('priority', [1, 2]);
return $this->belongsToMany(Role::class)
            ->wherePivotNotIn('priority', [1, 2]);
return $this->belongsToMany(Podcast::class)
            ->as('subscriptions')
            ->wherePivotBetween('created_at', ['2020-01-01 00:00:00', '2020-12-31 00:00:00']);
return $this->belongsToMany(Podcast::class)
            ->as('subscriptions')
            ->wherePivotNotBetween('created_at', ['2020-01-01 00:00:00', '2020-12-31 00:00:00']);
return $this->belongsToMany(Podcast::class)
            ->as('subscriptions')
            ->wherePivotNull('expired_at');
return $this->belongsToMany(Podcast::class)
            ->as('subscriptions')
            ->wherePivotNotNull('expired_at');

Ordinare le Query tramite Colonne della Tabella Intermedia

Puoi ordinare i risultati restituiti dalle query di relazione belongsToMany utilizzando il metodo orderByPivot. Nell’esempio seguente, recupereremo tutti i badge più recenti per l’utente:

return $this->belongsToMany(Badge::class)
                ->where('rank', 'gold')
                ->orderByPivot('created_at', 'desc');

Definire Modelli Personalizzati per Tabelle Intermedie

Se desideri definire un modello personalizzato per rappresentare la tabella intermedia della tua relazione molti-a-molti, puoi utilizzare il metodo using quando definisci la relazione. I modelli pivot personalizzati ti permettono di aggiungere comportamenti aggiuntivi al modello pivot, come metodi e cast.

I modelli pivot personalizzati per relazioni molti-a-molti dovrebbero estendere la classe Illuminate\Database\Eloquent\Relations\Pivot, mentre i modelli pivot polimorfici personalizzati per relazioni molti-a-molti dovrebbero estendere la classe Illuminate\Database\Eloquent\Relations\MorphPivot. Ad esempio, possiamo definire un modello Role che utilizza un modello pivot personalizzato RoleUser:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Role extends Model
{
    /**
     * Gli utenti che appartengono al ruolo.
     */
    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class)->using(RoleUser::class);
    }
}

Quando definisci il modello RoleUser, dovresti estendere la classe Illuminate\Database\Eloquent\Relations\Pivot:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Relations\Pivot;

class RoleUser extends Pivot
{
    // ...
}

I modelli pivot non possono utilizzare il trait SoftDeletes. Se hai bisogno di eliminare soft le record pivot, considera di convertire il tuo modello pivot in un vero modello Eloquent.

Modelli Pivot Personalizzati e ID Auto-incrementanti

Se hai definito una relazione molti-a-molti che utilizza un modello pivot personalizzato e quel modello pivot ha una chiave primaria auto-incrementante, devi assicurarti che la tua classe del modello pivot personalizzato definisca una proprietà incrementing impostata su true.

    /**
     * Indicates if the IDs are auto-incrementing.
     *
     * @var bool
     */
    public $incrementing = true;

Relazioni Polimorfiche

Una relazione polimorfica permette al modello figlio di appartenere a più di un tipo di modello usando un’unica associazione. Ad esempio, immagina di creare un’applicazione che permette agli utenti di condividere post del blog e video. In un’applicazione del genere, un modello Comment potrebbe appartenere sia ai modelli Post che Video.

One to One (Polimorfica)

Struttura della Tabella

Una relazione polimorfica uno a uno è simile a una tipica relazione uno a uno; tuttavia, il modello figlio può appartenere a più tipi di modelli utilizzando una singola associazione. Ad esempio, un Post di un blog e un User possono condividere una relazione polimorfica con un modello Image. Utilizzando una relazione polimorfica uno a uno, puoi avere una singola tabella di immagini uniche che possono essere associate sia a post che a utenti. Prima di tutto, esaminiamo la struttura delle tabelle:

    posts
        id - integer
        name - string

    users
        id - integer
        name - string

    images
        id - integer
        url - string
        imageable_id - integer
        imageable_type - string

Nota le colonne imageable_id e imageable_type nella tabella images. La colonna imageable_id conterrà il valore dell’ID del post o dell’utente, mentre la colonna imageable_type conterrà il nome della classe del modello genitore. La colonna imageable_type viene utilizzata da Eloquent per determinare quale "tipo" di modello genitore restituire quando accedi alla relazione imageable. In questo caso, la colonna conterrà App\Models\Post o App\Models\User.

Struttura del Modello

Vediamo ora le definizioni dei modelli necessarie per creare questa relazione:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;

class Image extends Model
{
    /**
     * Recupera il modello imageable genitore (user o post).
     */
    public function imageable(): MorphTo
    {
        return $this->morphTo();
    }
}

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;

class Post extends Model
{
    /**
     * Recupera l'immagine del post.
     */
    public function image(): MorphOne
    {
        return $this->morphOne(Image::class, 'imageable');
    }
}

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;

class User extends Model
{
    /**
     * Recupera l'immagine dell'utente.
     */
    public function image(): MorphOne
    {
        return $this->morphOne(Image::class, 'imageable');
    }
}

Recuperare la Relazione

Una volta che la tua tabella del database e i modelli sono definiti, puoi accedere alle relazioni tramite i tuoi modelli. Ad esempio, per recuperare l’immagine di un post, possiamo accedere alla proprietà dinamica della relazione image:

    use App\Models\Post;

    $post = Post::find(1);

    $image = $post->image;

Puoi recuperare il genitore del modello polimorfico accedendo al nome del metodo che esegue la chiamata a morphTo. In questo caso, si tratta del metodo imageable sul modello Image. Quindi, accederemo a quel metodo come una proprietà dinamica della relazione:

    use App\Models\Image;

    $image = Image::find(1);

    $imageable = $image->imageable;

La relazione imageable sul modello Image restituirà un’istanza di Post o User, a seconda del tipo di modello che possiede l’immagine.

Convenzioni Chiave

Se necessario, puoi specificare il nome delle colonne "id" e "type" utilizzate dal tuo modello polimorfico figlio. Se lo fai, assicurati di passare sempre il nome della relazione come primo argomento al metodo morphTo. Solitamente, questo valore dovrebbe corrispondere al nome del metodo, quindi puoi usare la costante __FUNCTION__ di PHP:

/**
 * Ottieni il modello a cui appartiene l'immagine.
 */
public function imageable(): MorphTo
{
    return $this->morphTo(__FUNCTION__, 'imageable_type', 'imageable_id');
}

One to Many (Polimorfica)

Struttura delle Tabelle

Una relazione polimorfica uno-a-molti è simile a una relazione uno-a-molti tradizionale; però, il modello figlio può appartenere a più tipi di modelli usando un’unica associazione. Per esempio, immagina che gli utenti della tua applicazione possano "commentare" post e video. Usando relazioni polimorfiche, puoi utilizzare una singola tabella comments per i commenti sia dei post che dei video. Prima di tutto, vediamo la struttura delle tabelle necessaria per creare questa relazione:

    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

Struttura del Modello

Successivamente, esaminiamo le definizioni dei modelli necessarie per costruire questa relazione:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;

class Comment extends Model
{
    /**
     * Ottieni il modello commentable padre (post o video).
     */
    public function commentable(): MorphTo
    {
        return $this->morphTo();
    }
}

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;

class Post extends Model
{
    /**
     * Ottieni tutti i commenti del post.
     */
    public function comments(): MorphMany
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;

class Video extends Model
{
    /**
     * Ottieni tutti i commenti del video.
     */
    public function comments(): MorphMany
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}

Recuperare la Relazione

Una volta che la tua tabella del database e i modelli sono definiti, puoi accedere alle relazioni tramite le proprietà di relazione dinamiche del tuo modello. Ad esempio, per accedere a tutti i commenti di un post, possiamo usare la proprietà dinamica comments:

    use App\Models\Post;

    $post = Post::find(1);

    foreach ($post->comments as $comment) {
        // ...
    }

Puoi inoltre recuperare il genitore di un modello figlio polimorfico accedendo al nome del metodo che effettua la chiamata a morphTo. In questo caso, si tratta del metodo commentable sul modello Comment. Quindi, accederemo a quel metodo come proprietà di relazione dinamica per accedere al modello genitore del commento:

    use App\Models\Comment;

    $comment = Comment::find(1);

    $commentable = $comment->commentable;

La relazione commentable nel modello Comment restituirà un’istanza di Post o Video, a seconda del tipo di modello che è il genitore del commento.

Popolazione Automatica dei Modelli Genitori sui Figli

Anche utilizzando il caricamento eager di Eloquent, possono sorgere problemi di query "N + 1" se tenti di accedere al modello padre da un modello figlio mentre cicli attraverso i modelli figli:

$posts = Post::with('comments')->get();

foreach ($posts as $post) {
    foreach ($post->comments as $comment) {
        echo $comment->commentable->title;
    }
}

Nell’esempio sopra, è stato introdotto un problema di query "N + 1" perché, anche se i commenti sono stati caricati eager per ogni modello Post, Eloquent non popola automaticamente il modello padre Post su ogni modello figlio Comment.

Se desideri che Eloquent popoli automaticamente i modelli genitori sui loro figli, puoi invocare il metodo chaperone durante la definizione di una relazione morphMany:

    class Post extends Model
    {
        /**
         * Ottieni tutti i commenti del post.
         */
        public function comments(): MorphMany
        {
            return $this->morphMany(Comment::class, 'commentable')->chaperone();
        }
    }

Oppure, se preferisci attivare la popolazione automatica dei genitori al momento dell’esecuzione, puoi invocare il metodo chaperone durante il caricamento eager della relazione:

use App\Models\Post;

$posts = Post::with([
    'comments' => fn ($comments) => $comments->chaperone(),
])->get();

One to Many (Polimorfica)

A volte un modello può avere molti modelli correlati, ma potresti voler recuperare facilmente il modello correlato "più recente" o "più vecchio" della relazione. Ad esempio, un modello User può essere collegato a molti modelli Image, ma vuoi definire un modo comodo per interagire con l’immagine più recente che l’utente ha caricato. Puoi ottenere questo utilizzando il tipo di relazione morphOne combinato con i metodi ofMany:

/**
 * Ottieni l'immagine più recente dell'utente.
 */
public function latestImage(): MorphOne
{
    return $this->morphOne(Image::class, 'imageable')->latestOfMany();
}

Allo stesso modo, puoi definire un metodo per recuperare il modello correlato "più vecchio", o il primo, di una relazione:

/**
 * Ottieni l'immagine più vecchia dell'utente.
 */
public function oldestImage(): MorphOne
{
    return $this->morphOne(Image::class, 'imageable')->oldestOfMany();
}

Per impostazione predefinita, i metodi latestOfMany e oldestOfMany recuperano il modello correlato più recente o più vecchio basandosi sulla chiave primaria del modello, che deve essere ordinabile. Tuttavia, a volte potresti voler recuperare un singolo modello da una relazione più ampia utilizzando un criterio di ordinamento diverso.

Ad esempio, usando il metodo ofMany, puoi recuperare l’immagine più "piaciuta" dell’utente. Il metodo ofMany accetta la colonna da ordinare come primo argomento e quale funzione di aggregazione (min o max) applicare durante la query per il modello correlato:

/**
 * Ottieni l'immagine più popolare dell'utente.
 */
public function bestImage(): MorphOne
{
    return $this->morphOne(Image::class, 'imageable')->ofMany('likes', 'max');
}

[!NOTA]
È possibile costruire relazioni "uno tra molti" più avanzate. Per maggiori informazioni, consulta la documentazione has one of many.

Many to Many (Polimorfica)

Struttura delle Tabelle

Le relazioni polimorfiche molti-a-molti sono leggermente più complesse rispetto alle relazioni "morph one" e "morph many". Ad esempio, un modello Post e un modello Video potrebbero condividere una relazione polimorfica con un modello Tag. Utilizzare una relazione polimorfica molti-a-molti in questa situazione permetterebbe alla tua applicazione di avere una singola tabella di tag unici che possono essere associati a post o video. Prima, esaminiamo la struttura delle tabelle necessaria per costruire questa relazione:

    posts
        id - integer
        name - string

    videos
        id - integer
        name - string

    tags
        id - integer
        name - string

    taggables
        tag_id - integer
        taggable_id - integer
        taggable_type - string

Prima di approfondire le relazioni polimorfiche molti-a-molti, potrebbe esserti utile leggere la documentazione sulle relazioni molti-a-molti.

Struttura del Modello

Ora siamo pronti a definire le relazioni nei modelli. I modelli Post e Video conterranno entrambi un metodo tags che chiama il metodo morphToMany fornito dalla classe base del modello Eloquent.

Il metodo morphToMany accetta il nome del modello correlato e il "nome della relazione". In base al nome che abbiamo assegnato alla nostra tabella intermedia e alle chiavi che contiene, ci riferiremo alla relazione come "taggable":

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;

class Post extends Model
{
    /**
     * Ottieni tutti i tag per il post.
     */
    public function tags(): MorphToMany
    {
        return $this->morphToMany(Tag::class, 'taggable');
    }
}

Definire l’Inverso della Relazione

Successivamente, nel modello Tag, dovresti definire un metodo per ciascuno dei suoi possibili modelli genitori. In questo esempio, definiremo un metodo posts e un metodo videos. Entrambi i metodi devono restituire il risultato del metodo morphedByMany.

Il metodo morphedByMany accetta il nome del modello correlato e il "nome della relazione". Sulla base del nome che abbiamo assegnato alla nostra tabella intermedia e delle chiavi che contiene, ci riferiremo alla relazione come "taggable":

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;

class Tag extends Model
{
    /**
     * Ottieni tutti i post assegnati a questo tag.
     */
    public function posts(): MorphToMany
    {
        return $this->morphedByMany(Post::class, 'taggable');
    }

    /**
     * Ottieni tutti i video assegnati a questo tag.
     */
    public function videos(): MorphToMany
    {
        return $this->morphedByMany(Video::class, 'taggable');
    }
}

Recuperare la relazione

Una volta definite le tabelle del database e i modelli, puoi accedere alle relazioni tramite i tuoi modelli. Ad esempio, per accedere a tutti i tag di un post, puoi utilizzare la proprietà di relazione dinamica tags:

use App\Models\Post;

$post = Post::find(1);

foreach ($post->tags as $tag) {
    // ...
}

Puoi recuperare il genitore di una relazione polimorfica dal modello figlio polimorfico accedendo al nome del metodo che esegue la chiamata a morphedByMany. In questo caso, si tratta dei metodi posts o videos sul modello Tag:

use App\Models\Tag;

$tag = Tag::find(1);

foreach ($tag->posts as $post) {
    // ...
}

foreach ($tag->videos as $video) {
    // ...
}

Tipi Polimorfici Personalizzati

Per impostazione predefinita, Laravel utilizzerà il nome completo della classe per memorizzare il "tipo" del modello correlato. Ad esempio, dato l’esempio di relazione uno-a-molti sopra in cui un modello Comment può appartenere a un modello Post o Video, il valore predefinito per commentable_type sarà rispettivamente App\Models\Post o App\Models\Video. Tuttavia, potresti voler disaccoppiare questi valori dalla struttura interna della tua applicazione.

Ad esempio, invece di utilizzare i nomi dei modelli come "tipo", possiamo usare stringhe semplici come post e video. In questo modo, i valori della colonna "type" polimorfica nel nostro database rimarranno validi anche se i modelli vengono rinominati:

    use Illuminate\Database\Eloquent\Relations\Relation;

    Relation::enforceMorphMap([
        'post' => 'App\Models\Post',
        'video' => 'App\Models\Video',
    ]);

Puoi chiamare il metodo enforceMorphMap nel metodo boot della tua classe App\Providers\AppServiceProvider o creare un provider di servizio separato se lo desideri.

Puoi determinare l’alias morph di un determinato modello in fase di esecuzione utilizzando il metodo getMorphClass del modello. Al contrario, puoi determinare il nome completo della classe associata a un alias morph utilizzando il metodo Relation::getMorphedModel:

    use Illuminate\Database\Eloquent\Relations\Relation;

    $alias = $post->getMorphClass();

    $class = Relation::getMorphedModel($alias);

Quando aggiungi una "morph map" alla tua applicazione esistente, ogni valore della colonna *_type morphable nel tuo database che contiene ancora una classe completamente qualificata dovrà essere convertito nel suo nome mappato.

Relazioni Dinamiche

Puoi usare il metodo resolveRelationUsing per definire relazioni tra modelli Eloquent durante l’esecuzione. Anche se generalmente non è raccomandato per lo sviluppo normale di applicazioni, può essere utile quando sviluppi pacchetti Laravel.

Il metodo resolveRelationUsing accetta il nome della relazione desiderata come primo argomento. Il secondo argomento deve essere una closure che riceve l’istanza del modello e restituisce una definizione valida di relazione Eloquent. Di solito, dovresti configurare le relazioni dinamiche all’interno del metodo boot di un service provider:

use App\Models\Order;
use App\Models\Customer;

Order::resolveRelationUsing('customer', function (Order $orderModel) {
    return $orderModel->belongsTo(Customer::class, 'customer_id');
});

Quando definisci relazioni dinamiche, fornisci sempre argomenti di nome chiave espliciti ai metodi di relazione Eloquent.

Interrogare le Relazioni

Poiché tutte le relazioni di Eloquent sono definite tramite metodi, puoi chiamare questi metodi per ottenere un’istanza della relazione senza eseguire effettivamente una query per caricare i modelli correlati. Inoltre, tutti i tipi di relazioni Eloquent fungono anche da costruttori di query, permettendoti di aggiungere ulteriori vincoli alla query della relazione prima di eseguire finalmente la query SQL sul tuo database.

Ad esempio, immagina un’applicazione blog in cui un modello User ha molti modelli Post associati:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class User extends Model
{
    /**
     * Ottieni tutti i post per l'utente.
     */
    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }
}

Puoi interrogare la relazione posts e aggiungere ulteriori vincoli alla relazione in questo modo:

use App\Models\User;

$user = User::find(1);

$user->posts()->where('active', 1)->get();

Puoi usare tutti i metodi del costruttore di query di Laravel sulla relazione, quindi assicurati di esplorare la documentazione del costruttore di query per conoscere tutti i metodi disponibili.

Concatenare clausole orWhere dopo le relazioni

Come dimostrato nell’esempio sopra, puoi aggiungere ulteriori vincoli alle relazioni quando le interroghi. Tuttavia, fai attenzione quando concatenare clausole orWhere a una relazione, poiché le clausole orWhere saranno raggruppate logicamente allo stesso livello del vincolo della relazione:

    $user->posts()
            ->where('active', 1)
            ->orWhere('votes', '>=', 100)
            ->get();

L’esempio sopra genererà il seguente SQL. Come puoi vedere, la clausola or fa sì che la query restituisca qualsiasi post con più di 100 voti. La query non è più limitata a un utente specifico:

select *
from posts
where user_id = ? and active = 1 or votes >= 100

Nella maggior parte delle situazioni, dovresti usare gruppi logici per raggruppare i controlli condizionali all’interno delle parentesi:

    use Illuminate\Database\Eloquent\Builder;

    $user->posts()
            ->where(function (Builder $query) {
                return $query->where('active', 1)
                             ->orWhere('votes', '>=', 100);
            })
            ->get();

L’esempio sopra produrrà il seguente SQL. Nota che il raggruppamento logico ha correttamente raggruppato i vincoli e la query rimane limitata a un utente specifico:

select *
from posts
where user_id = ? and (active = 1 or votes >= 100)

Relationship Methods vs. Dynamic Properties

Se non hai bisogno di aggiungere vincoli aggiuntivi a una query di relazione Eloquent, puoi accedere alla relazione come se fosse una proprietà. Ad esempio, continuando a usare i nostri modelli di esempio User e Post, possiamo accedere a tutti i post di un utente in questo modo:

use App\Models\User;

$user = User::find(1);

foreach ($user->posts as $post) {
    // ...
}

Le proprietà dinamiche delle relazioni eseguono "lazy loading", il che significa che caricheranno i dati della relazione solo quando le accedi effettivamente. Per questo motivo, gli sviluppatori utilizzano spesso eager loading per pre-caricare le relazioni di cui sanno che saranno accessibili dopo il caricamento del modello. L’eager loading consente una riduzione significativa delle query SQL necessarie per caricare le relazioni di un modello.

Verifica dell’Esistenza delle Relazioni

Quando recuperi i record dei modelli, potresti voler limitare i risultati in base all’esistenza di una relazione. Ad esempio, immagina di voler recuperare tutti i post del blog che hanno almeno un commento. Per farlo, puoi passare il nome della relazione ai metodi has e orHas:

    use App\Models\Post;

    // Recupera tutti i post che hanno almeno un commento...
    $posts = Post::has('comments')->get();

Puoi anche specificare un operatore e un valore di conteggio per personalizzare ulteriormente la query:

    // Recupera tutti i post che hanno tre o più commenti...
    $posts = Post::has('comments', '>=', 3)->get();

Le istruzioni has annidate possono essere costruite usando la notazione "punto". Ad esempio, puoi recuperare tutti i post che hanno almeno un commento che ha almeno un’immagine:

    // Recupera i post che hanno almeno un commento con immagini...
    $posts = Post::has('comments.images')->get();

Se hai bisogno di ancora più potenza, puoi usare i metodi whereHas e orWhereHas per definire ulteriori vincoli alla tua query has, come ispezionare il contenuto di un commento:

    use Illuminate\Database\Eloquent\Builder;

    // Recupera i post con almeno un commento che contiene parole come code%...
    $posts = Post::whereHas('comments', function (Builder $query) {
        $query->where('content', 'like', 'code%');
    })->get();

    // Recupera i post con almeno dieci commenti che contengono parole come code%...
    $posts = Post::whereHas('comments', function (Builder $query) {
        $query->where('content', 'like', 'code%');
    }, '>=', 10)->get();

Eloquent attualmente non supporta la verifica dell’esistenza delle relazioni tra database. Le relazioni devono esistere nello stesso database.

Query sull’Esistenza delle Relazioni Inline

Se vuoi verificare l’esistenza di una relazione con una singola condizione where semplice collegata alla query della relazione, potrebbe essere più comodo usare i metodi whereRelation, orWhereRelation, whereMorphRelation e orWhereMorphRelation. Ad esempio, possiamo cercare tutti i post che hanno commenti non approvati:

use App\Models\Post;

$posts = Post::whereRelation('comments', 'is_approved', false)->get();

Naturalmente, come le chiamate al metodo where del query builder, puoi anche specificare un operatore:

$posts = Post::whereRelation(
    'comments', 'created_at', '>=', now()->subHour()
)->get();

Interrogare l’assenza di una relazione

Durante il recupero dei record del modello, potresti voler limitare i risultati in base all’assenza di una relazione. Ad esempio, immagina di voler recuperare tutti i post del blog che non hanno commenti. Per fare ciò, puoi passare il nome della relazione ai metodi doesntHave e orDoesntHave:

use App\Models\Post;

$posts = Post::doesntHave('comments')->get();

Se hai bisogno di maggiore flessibilità, puoi usare i metodi whereDoesntHave e orWhereDoesntHave per aggiungere ulteriori vincoli alla tua query doesntHave, come esaminare il contenuto di un commento:

use Illuminate\Database\Eloquent\Builder;

$posts = Post::whereDoesntHave('comments', function (Builder $query) {
    $query->where('content', 'like', 'code%');
})->get();

Puoi usare la notazione "dot" per eseguire una query su una relazione annidata. Ad esempio, la seguente query recupererà tutti i post che non hanno commenti; tuttavia, i post che hanno commenti da autori non bannati saranno inclusi nei risultati:

use Illuminate\Database\Eloquent\Builder;

$posts = Post::whereDoesntHave('comments.author', function (Builder $query) {
    $query->where('banned', 0);
})->get();

Interrogare le Relazioni Morph To

Per verificare l’esistenza delle relazioni "morph to", puoi utilizzare i metodi whereHasMorph e whereDoesntHaveMorph. Questi metodi accettano il nome della relazione come primo argomento. Successivamente, accettano i nomi dei modelli correlati che desideri includere nella query. Infine, puoi fornire una closure che personalizza la query della relazione:

use App\Models\Comment;
use App\Models\Post;
use App\Models\Video;
use Illuminate\Database\Eloquent\Builder;

// Recupera i commenti associati a post o video con un titolo che inizia con code%...
$comments = Comment::whereHasMorph(
    'commentable',
    [Post::class, Video::class],
    function (Builder $query) {
        $query->where('title', 'like', 'code%');
    }
)->get();

// Recupera i commenti associati a post con un titolo non simile a code%...
$comments = Comment::whereDoesntHaveMorph(
    'commentable',
    Post::class,
    function (Builder $query) {
        $query->where('title', 'like', 'code%');
    }
)->get();

Occasionalmente potresti aver bisogno di aggiungere vincoli alla query basati sul "tipo" del modello polimorfico correlato. La closure passata al metodo whereHasMorph può ricevere un valore $type come secondo argomento. Questo argomento ti permette di esaminare il "tipo" della query che viene costruita:

use Illuminate\Database\Eloquent\Builder;

$comments = Comment::whereHasMorph(
    'commentable',
    [Post::class, Video::class],
    function (Builder $query, string $type) {
        $column = $type === Post::class ? 'content' : 'title';

        $query->where($column, 'like', 'code%');
    }
)->get();

A volte potresti voler interrogare i figli del genitore di una relazione "morph to". Puoi farlo utilizzando i metodi whereMorphedTo e whereNotMorphedTo, che determinano automaticamente la mappatura del tipo morph corretta per il modello dato. Questi metodi accettano il nome della relazione morphTo come primo argomento e il modello genitore correlato come secondo argomento:

$comments = Comment::whereMorphedTo('commentable', $post)
                      ->orWhereMorphedTo('commentable', $video)
                      ->get();

Interrogare Tutti i Modelli Correlati

Invece di passare un array di possibili modelli polimorfici, puoi fornire * come valore jolly. Questo istruisce Laravel a recuperare tutti i tipi polimorfici possibili dal database. Laravel eseguirà una query aggiuntiva per effettuare questa operazione:

use Illuminate\Database\Eloquent\Builder;

$comments = Comment::whereHasMorph('commentable', '*', function (Builder $query) {
    $query->where('title', 'like', 'foo%');
})->get();

Aggregazione dei Modelli Relazionati

Conteggio dei Modelli Relazionati

A volte potresti voler contare il numero di modelli correlati per una determinata relazione senza caricare effettivamente i modelli. Per fare questo, puoi usare il metodo withCount. Il metodo withCount aggiungerà un attributo {relation}_count ai modelli risultanti:

use App\Models\Post;

$posts = Post::withCount('comments')->get();

foreach ($posts as $post) {
    echo $post->comments_count;
}

Passando un array al metodo withCount, puoi aggiungere i "conteggi" per più relazioni e aggiungere ulteriori vincoli alle query:

use Illuminate\Database\Eloquent\Builder;

$posts = Post::withCount(['votes', 'comments' => function (Builder $query) {
    $query->where('content', 'like', 'code%');
}])->get();

echo $posts[0]->votes_count;
echo $posts[0]->comments_count;

Puoi anche assegnare un alias al risultato del conteggio della relazione, permettendo più conteggi sulla stessa relazione:

use Illuminate\Database\Eloquent\Builder;

$posts = Post::withCount([
    'comments',
    'comments as pending_comments_count' => function (Builder $query) {
        $query->where('approved', false);
    },
])->get();

echo $posts[0]->comments_count;
echo $posts[0]->pending_comments_count;

Caricamento del Conteggio Differito

Usando il metodo loadCount, puoi caricare il conteggio di una relazione dopo che il modello principale è già stato recuperato:

$book = Book::first();

$book->loadCount('genres');

Se hai bisogno di aggiungere ulteriori vincoli alla query di conteggio, puoi passare un array con le relazioni che vuoi contare. I valori dell’array dovrebbero essere delle closure che ricevono l’istanza del query builder:

$book->loadCount(['reviews' => function (Builder $query) {
    $query->where('rating', 5);
}])

Conteggio delle Relazioni e Dichiarazioni Select Personalizzate

Se stai combinando withCount con una dichiarazione select, assicurati di chiamare withCount dopo il metodo select:

$posts = Post::select(['title', 'body'])
                ->withCount('comments')
                ->get();

Altre Funzioni Aggregate

Oltre al metodo withCount, Eloquent fornisce i metodi withMin, withMax, withAvg, withSum e withExists. Questi metodi aggiungeranno un attributo {relation}_{function}_{column} ai modelli risultanti:

    use App\Models\Post;

    $posts = Post::withSum('comments', 'votes')->get();

    foreach ($posts as $post) {
        echo $post->comments_sum_votes;
    }

Se desideri accedere al risultato della funzione aggregate con un altro nome, puoi specificare un alias:

    $posts = Post::withSum('comments as total_comments', 'votes')->get();

    foreach ($posts as $post) {
        echo $post->total_comments;
    }

Come il metodo loadCount, sono disponibili anche versioni differite di questi metodi. Queste operazioni aggregate aggiuntive possono essere eseguite su modelli Eloquent già recuperati:

    $post = Post::first();

    $post->loadSum('comments', 'votes');

Se stai combinando questi metodi aggregate con una dichiarazione select, assicurati di chiamare i metodi aggregate dopo il metodo select:

    $posts = Post::select(['title', 'body'])
                    ->withExists('comments')
                    ->get();

Conteggio dei Modelli Relazionati nelle Relazioni Morph To

Se vuoi caricare in eager una relazione "morph to", così come il conteggio dei modelli correlati per le diverse entità che possono essere restituite da quella relazione, puoi utilizzare il metodo with insieme al metodo morphWithCount della relazione morphTo.

In questo esempio, supponiamo che i modelli Photo e Post possano creare modelli ActivityFeed. Assumeremo che il modello ActivityFeed definisca una relazione "morph to" chiamata parentable che ci permette di recuperare il modello padre Photo o Post per una determinata istanza di ActivityFeed. Inoltre, supponiamo che i modelli Photo "abbiano molti" modelli Tag e i modelli Post "abbiano molti" modelli Comment.

Ora, immaginiamo di voler recuperare le istanze di ActivityFeed e caricare in eager i modelli padre parentable per ogni istanza di ActivityFeed. Inoltre, vogliamo recuperare il numero di tag associati a ogni photo padre e il numero di commenti associati a ogni post padre:

use Illuminate\Database\Eloquent\Relations\MorphTo;

$activities = ActivityFeed::with([
    'parentable' => function (MorphTo $morphTo) {
        $morphTo->morphWithCount([
            Photo::class => ['tags'],
            Post::class => ['comments'],
        ]);
    }])->get();

Caricamento Differito dei Conteggi

Supponiamo di aver già recuperato un insieme di modelli ActivityFeed e ora vogliamo caricare i conteggi delle relazioni annidate per i vari modelli parentable associati ai feed di attività. Puoi utilizzare il metodo loadMorphCount per ottenere questo risultato:

$activities = ActivityFeed::with('parentable')->get();

$activities->loadMorphCount('parentable', [
    Photo::class => ['tags'],
    Post::class => ['comments'],
]);

Eager Loading

Quando accedi alle relazioni Eloquent come proprietà, i modelli correlati vengono "caricati pigramente". Questo significa che i dati della relazione non vengono effettivamente caricati fino al primo accesso alla proprietà. Tuttavia, Eloquent può "caricare anticipatamente" le relazioni al momento della query sul modello principale. Il caricamento anticipato allevia il problema della query "N + 1". Per illustrare il problema della query N + 1, considera un modello Book che "appartiene" a un modello Author:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Book extends Model
{
    /**
     * Ottieni l'autore che ha scritto il libro.
     */
    public function author(): BelongsTo
    {
        return $this->belongsTo(Author::class);
    }
}

Ora, recuperiamo tutti i libri e i loro autori:

use App\Models\Book;

$books = Book::all();

foreach ($books as $book) {
    echo $book->author->name;
}

Questo ciclo eseguirà una query per recuperare tutti i libri nella tabella del database, poi un’altra query per ogni libro per recuperare l’autore del libro. Quindi, se abbiamo 25 libri, il codice sopra eseguirebbe 26 query: una per i libri originali e 25 query aggiuntive per recuperare l’autore di ciascun libro.

Fortunatamente, possiamo usare il caricamento anticipato (Eager Loading) per ridurre questa operazione a sole due query. Quando costruisci una query, puoi specificare quali relazioni devono essere caricate anticipatamente usando il metodo with:

$books = Book::with('author')->get();

foreach ($books as $book) {
    echo $book->author->name;
}

Per questa operazione, verranno eseguite solo due query: una per recuperare tutti i libri e una per recuperare tutti gli autori di tutti i libri:

select * from books

select * from authors where id in (1, 2, 3, 4, 5, ...)

Eager Loading di più Relazioni

A volte potresti aver bisogno di caricare anticipatamente diverse relazioni. Per farlo, passa semplicemente un array di relazioni al metodo with:

$books = Book::with(['author', 'publisher'])->get();

Eager Loading Annidato

Per caricare le relazioni delle relazioni, puoi usare la sintassi con il punto. Ad esempio, carichiamo tutti gli autori del libro e tutti i contatti personali degli autori:

$books = Book::with('author.contacts')->get();

In alternativa, puoi specificare relazioni pre-caricate in modo annidato fornendo un array annidato al metodo with, il che può essere comodo quando carichi in eager loading più relazioni annidate:

$books = Book::with([
    'author' => [
        'contacts',
        'publisher',
    ],
])->get();

Caricamento Eager Annidato di Relazioni morphTo

Se desideri effettuare un eager load di una relazione morphTo, così come delle relazioni annidate sulle varie entità che possono essere restituite da quella relazione, puoi usare il metodo with in combinazione con il metodo morphWith della relazione morphTo. Per illustrare questo metodo, consideriamo il seguente modello:

<?php

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;

class ActivityFeed extends Model
{
    /**
     * Ottieni il genitore del record activity feed.
     */
    public function parentable(): MorphTo
    {
        return $this->morphTo();
    }
}

In questo esempio, supponiamo che i modelli Event, Photo e Post possano creare modelli ActivityFeed. Inoltre, supponiamo che i modelli Event appartengano a un modello Calendar, i modelli Photo siano associati a modelli Tag e i modelli Post appartengano a un modello Author.

Usando queste definizioni di modelli e relazioni, possiamo recuperare istanze del modello ActivityFeed e effettuare un eager load di tutti i modelli parentable e delle rispettive relazioni annidate:

use Illuminate\Database\Eloquent\Relations\MorphTo;

$activities = ActivityFeed::query()
    ->with(['parentable' => function (MorphTo $morphTo) {
        $morphTo->morphWith([
            Event::class => ['calendar'],
            Photo::class => ['tags'],
            Post::class => ['author'],
        ]);
    }])->get();

Caricamento Preventivo di Colonne Specifiche

Potresti non aver sempre bisogno di ogni colonna dalle relazioni che stai recuperando. Per questo motivo, Eloquent ti permette di specificare quali colonne della relazione desideri recuperare:

$books = Book::with('author:id,name,book_id')->get();

Quando usi questa funzionalità, dovresti sempre includere la colonna id e qualsiasi colonna di chiave esterna rilevante nell’elenco delle colonne che desideri recuperare.

Eager Loading di default

A volte potresti voler caricare sempre alcune relazioni quando recuperi un modello. Per fare questo, puoi definire una proprietà $with nel modello:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Book extends Model
{
    /**
     * Le relazioni che dovrebbero essere sempre caricate.
     *
     * @var array
     */
    protected $with = ['author'];

    /**
     * Ottieni l'autore che ha scritto il libro.
     */
    public function author(): BelongsTo
    {
        return $this->belongsTo(Author::class);
    }

    /**
     * Ottieni il genere del libro.
     */
    public function genre(): BelongsTo
    {
        return $this->belongsTo(Genre::class);
    }
}

Se vuoi rimuovere un elemento dalla proprietà $with per una singola query, puoi usare il metodo without:

$books = Book::without('author')->get();

Se vuoi sovrascrivere tutti gli elementi nella proprietà $with per una singola query, puoi usare il metodo withOnly:

$books = Book::withOnly('genre')->get();

Limitare il caricamento anticipato

A volte potresti voler caricare anticipatamente una relazione ma anche specificare condizioni aggiuntive per la query di caricamento anticipato. Puoi fare questo passando un array di relazioni al metodo with, dove la chiave dell’array è il nome della relazione e il valore dell’array è una closure che aggiunge vincoli aggiuntivi alla query di caricamento anticipato:

use App\Models\User;
use Illuminate\Contracts\Database\Eloquent\Builder;

$users = User::with(['posts' => function (Builder $query) {
    $query->where('title', 'like', '%code%');
}])->get();

In questo esempio, Eloquent caricherà anticipatamente solo i post dove la colonna title del post contiene la parola code. Puoi chiamare altri metodi del query builder per personalizzare ulteriormente l’operazione di caricamento anticipato:

$users = User::with(['posts' => function (Builder $query) {
    $query->orderBy('created_at', 'desc');
}])->get();

Limitare l’Eager Loading delle Relazioni morphTo

Se stai effettuando il caricamento anticipato di una relazione morphTo, Eloquent eseguirà più query per recuperare ogni tipo di modello correlato. Puoi aggiungere ulteriori vincoli a ciascuna di queste query utilizzando il metodo constrain della relazione MorphTo:

use Illuminate\Database\Eloquent\Relations\MorphTo;

$comments = Comment::with(['commentable' => function (MorphTo $morphTo) {
    $morphTo->constrain([
        Post::class => function ($query) {
            $query->whereNull('hidden_at');
        },
        Video::class => function ($query) {
            $query->where('type', 'educational');
        },
    ]);
}])->get();

In questo esempio, Eloquent caricherà anticipatamente solo i post che non sono stati nascosti e i video che hanno un valore type di "educational".

Limitare i Caricamenti Eager con l’Esistenza di Relazioni

A volte potresti dover verificare l’esistenza di una relazione mentre carichi simultaneamente la relazione stessa basandoti sulle stesse condizioni. Ad esempio, potresti voler recuperare solo i modelli User che hanno modelli figli Post che soddisfano una determinata condizione di query, caricando inoltre i post corrispondenti. Puoi ottenere questo utilizzando il metodo withWhereHas:

use App\Models\User;

$users = User::withWhereHas('posts', function ($query) {
    $query->where('featured', true);
})->get();

Lazy Eager Loading

A volte potresti aver bisogno di eager loadare una relationship dopo che il parent model è già stato recuperato. Ad esempio, questo può essere utile se devi decidere dinamicamente se caricare i modelli correlati:

use App\Models\Book;

$books = Book::all();

if ($someCondition) {
    $books->load('author', 'publisher');
}

Se hai bisogno di impostare ulteriori vincoli di query sulla query di eager loading, puoi passare un array indicizzato dalle relationships che desideri caricare. I valori dell’array dovrebbero essere istanze di closure che ricevono l’istanza della query:

$author->load(['books' => function (Builder $query) {
    $query->orderBy('published_date', 'asc');
}]);

Per caricare una relationship solo quando non è già stata caricata, usa il metodo loadMissing:

$book->loadMissing('author');

Lazy Eager Loading Annidato e morphTo

Se desideri caricare eager una relazione morphTo, oltre alle relazioni annidate sulle varie entità che possono essere restituite da quella relazione, puoi usare il metodo loadMorph.

Questo metodo accetta il nome della relazione morphTo come primo argomento e un array di coppie modello/relazione come secondo argomento. Per illustrare questo metodo, consideriamo il seguente modello:

<?php

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;

class ActivityFeed extends Model
{
    /**
     * Ottieni il genitore del record del feed di attività.
     */
    public function parentable(): MorphTo
    {
        return $this->morphTo();
    }
}

In questo esempio, supponiamo che i modelli Event, Photo e Post possano creare modelli ActivityFeed. Inoltre, supponiamo che i modelli Event appartengano a un modello Calendar, i modelli Photo siano associati a modelli Tag, e i modelli Post appartengano a un modello Author.

Usando queste definizioni di modello e relazioni, possiamo recuperare istanze del modello ActivityFeed e caricare eager tutti i modelli parentable e le rispettive relazioni annidate:

$activities = ActivityFeed::with('parentable')
    ->get()
    ->loadMorph('parentable', [
        Event::class => ['calendar'],
        Photo::class => ['tags'],
        Post::class => ['author'],
    ]);

Prevenire il Lazy Loading

Come discusso in precedenza, il caricamento anticipato delle relazioni può spesso offrire significativi vantaggi in termini di prestazioni alla tua applicazione. Pertanto, se lo desideri, puoi istruire Laravel a prevenire sempre il lazy loading delle relazioni. Per fare ciò, puoi invocare il metodo preventLazyLoading offerto dalla classe base del modello Eloquent. In genere, dovresti chiamare questo metodo all’interno del metodo boot della classe AppServiceProvider della tua applicazione.

Il metodo preventLazyLoading accetta un argomento booleano opzionale che indica se il lazy loading deve essere prevenuto. Ad esempio, potresti voler disabilitare il lazy loading solo negli ambienti non di produzione, in modo che l’ambiente di produzione continui a funzionare normalmente anche se una relazione lazy loaded è presente accidentalmente nel codice di produzione:

use Illuminate\Database\Eloquent\Model;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Model::preventLazyLoading(! $this->app->isProduction());
}

Dopo aver prevenuto il lazy loading, Eloquent lancerà un’eccezione Illuminate\Database\LazyLoadingViolationException quando la tua applicazione tenterà di caricare in modo lazy qualsiasi relazione Eloquent.

Puoi personalizzare il comportamento delle violazioni del lazy loading utilizzando il metodo handleLazyLoadingViolationsUsing. Ad esempio, usando questo metodo, puoi istruirle a registrare solamente le violazioni del lazy loading invece di interrompere l’esecuzione dell’applicazione con delle eccezioni:

Model::handleLazyLoadingViolationUsing(function (Model $model, string $relation) {
    $class = $model::class;

    info("Attempted to lazy load [{$relation}] on model [{$class}].");
});

Inserimento e Aggiornamento dei Model Correlati

Il metodo save

Eloquent offre metodi comodi per aggiungere nuovi modelli alle relazioni. Ad esempio, potresti dover aggiungere un nuovo commento a un post. Invece di impostare manualmente l’attributo post_id sul modello Comment, puoi inserire il commento usando il metodo save della relazione:

use App\Models\Comment;
use App\Models\Post;

$comment = new Comment(['message' => 'A new comment.']);

$post = Post::find(1);

$post->comments()->save($comment);

Nota che non abbiamo accesso alla relazione comments come proprietà dinamica. Invece, abbiamo chiamato il metodo comments per ottenere un’istanza della relazione. Il metodo save aggiungerà automaticamente il valore post_id appropriato al nuovo modello Comment.

Se hai bisogno di salvare più modelli correlati, puoi usare il metodo saveMany:

$post = Post::find(1);

$post->comments()->saveMany([
    new Comment(['message' => 'A new comment.']),
    new Comment(['message' => 'Another new comment.']),
]);

I metodi save e saveMany salveranno le istanze dei modelli forniti, ma non aggiungeranno i modelli appena salvati a eventuali relazioni in memoria già caricate sul modello padre. Se prevedi di accedere alla relazione dopo aver usato i metodi save o saveMany, potresti voler usare il metodo refresh per ricaricare il modello e le sue relazioni:

$post->comments()->save($comment);

$post->refresh();

// Tutti i commenti, incluso il commento appena salvato...
$post->comments;

Salvataggio Ricorsivo di Modelli e Relazioni

Se vuoi save il tuo modello e tutte le relazioni associate, puoi usare il metodo push. In questo esempio, il modello Post verrà salvato insieme ai suoi commenti e agli autori dei commenti:

$post = Post::find(1);

$post->comments[0]->message = 'Message';
$post->comments[0]->author->name = 'Author Name';

$post->push();

Il metodo pushQuietly può essere usato per salvare un modello e le sue relazioni associate senza generare eventi:

$post->pushQuietly();

Il Metodo create

Oltre ai metodi save e saveMany, puoi anche usare il metodo create, che accetta un array di attributi, crea un modello e lo inserisce nel database. La differenza tra save e create è che save accetta un’istanza completa di modello Eloquent mentre create accetta un semplice array PHP. Il modello appena creato verrà restituito dal metodo create:

use App\Models\Post;

$post = Post::find(1);

$comment = $post->comments()->create([
    'message' => 'A new comment.',
]);

Puoi usare il metodo createMany per creare più modelli correlati:

$post = Post::find(1);

$post->comments()->createMany([
    ['message' => 'A new comment.'],
    ['message' => 'Another new comment.'],
]);

I metodi createQuietly e createManyQuietly possono essere usati per creare un modello/i senza inviare eventi:

$user = User::find(1);

$user->posts()->createQuietly([
    'title' => 'Post title.',
]);

$user->posts()->createManyQuietly([
    ['title' => 'First post.'],
    ['title' => 'Second post.'],
]);

Puoi anche usare i metodi findOrNew, firstOrNew, firstOrCreate e updateOrCreate per creare e aggiornare modelli nelle relazioni.

Prima di usare il metodo create, assicurati di rivedere la documentazione su mass assignment.

Relazioni belongsTo

Se desideri assegnare un modello figlio a un nuovo modello padre, puoi usare il metodo associate. In questo esempio, il modello User definisce una relazione belongsTo con il modello Account. Questo metodo associate imposterà la foreign key sul modello figlio:

    use App\Models\Account;

    $account = Account::find(10);

    $user->account()->associate($account);

    $user->save();

Per rimuovere un modello padre da un modello figlio, puoi usare il metodo dissociate. Questo metodo imposterà la foreign key della relazione su null:

    $user->account()->dissociate();

    $user->save();

Relazioni Molti a Molti

Aggiungere / Rimuovere

Eloquent fornisce anche metodi per rendere più comodo lavorare con relazioni molti-a-molti. Ad esempio, immaginiamo che un utente possa avere molti ruoli e un ruolo possa avere molti utenti. Puoi usare il metodo attach per aggiungere un ruolo a un utente inserendo un record nella tabella intermedia della relazione:

    use App\Models\User;

    $user = User::find(1);

    $user->roles()->attach($roleId);

Quando aggiungi una relazione a un modello, puoi anche passare un array di dati aggiuntivi da inserire nella tabella intermedia:

    $user->roles()->attach($roleId, ['expires' => $expires]);

A volte può essere necessario rimuovere un ruolo da un utente. Per rimuovere un record di una relazione molti-a-molti, usa il metodo detach. Il metodo detach eliminerà il record appropriato dalla tabella intermedia; tuttavia, entrambi i modelli rimarranno nel database:

    // Scollega un singolo ruolo dall'utente...
    $user->roles()->detach($roleId);

    // Scollega tutti i ruoli dall'utente...
    $user->roles()->detach();

Per comodità, attach e detach accettano anche array di ID come input:

    $user = User::find(1);

    $user->roles()->detach([1, 2, 3]);

    $user->roles()->attach([
        1 => ['expires' => $expires],
        2 => ['expires' => $expires],
    ]);

Sincronizzare le Associazioni

Puoi anche usare il metodo sync per costruire associazioni molti-a-molti. Il metodo sync accetta un array di ID da inserire nella tabella intermedia. Qualsiasi ID non presente nell’array verrà rimosso dalla tabella intermedia. Quindi, dopo che questa operazione è completata, solo gli ID presenti nell’array esisteranno nella tabella intermedia:

$user->roles()->sync([1, 2, 3]);

Puoi anche passare valori aggiuntivi per la tabella intermedia insieme agli ID:

$user->roles()->sync([1 => ['expires' => true], 2, 3]);

Se desideri inserire gli stessi valori nella tabella intermedia con ciascuno degli ID sincronizzati, puoi usare il metodo syncWithPivotValues:

$user->roles()->syncWithPivotValues([1, 2, 3], ['active' => true]);

Se non vuoi scollegare gli ID esistenti che mancano nell’array fornito, puoi usare il metodo syncWithoutDetaching:

$user->roles()->syncWithoutDetaching([1, 2, 3]);

Commutare le Associazioni

Il rapporto molti-a-molti offre anche un metodo toggle che "commuta" lo stato di collegamento degli ID dei modelli correlati forniti. Se l’ID dato è attualmente collegato, verrà scollegato. Allo stesso modo, se è attualmente scollegato, verrà collegato:

$user->roles()->toggle([1, 2, 3]);

Puoi anche passare valori aggiuntivi per la tabella intermedia insieme agli ID:

$user->roles()->toggle([
    1 => ['expires' => true],
    2 => ['expires' => true],
]);

Aggiornare un Record nella Tabella Intermedia

Se hai bisogno di aggiornare una riga esistente nella tabella intermedia della tua relazione, puoi usare il metodo updateExistingPivot. Questo metodo accetta la chiave esterna del record intermedio e un array di attributi da aggiornare:

$user = User::find(1);

$user->roles()->updateExistingPivot($roleId, [
    'active' => false,
]);

Toccare i Timestamp del Genitore

Quando un modello definisce una relazione belongsTo o belongsToMany con un altro modello, come un Comment che appartiene a un Post, a volte è utile aggiornare il timestamp del genitore quando il modello figlio viene aggiornato.

Ad esempio, quando un modello Comment viene aggiornato, potresti voler "toccare" automaticamente il timestamp updated_at del Post proprietario in modo che venga impostato sulla data e ora correnti. Per fare ciò, puoi aggiungere una proprietà touches al tuo modello figlio contenente i nomi delle relazioni il cui updated_at deve essere aggiornato quando il modello figlio viene aggiornato:

    <?php

    namespace App\Models;

    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\Relations\BelongsTo;

    class Comment extends Model
    {
        /**
         * Tutte le relazioni da toccare.
         *
         * @var array
         */
        protected $touches = ['post'];

        /**
         * Ottieni il post a cui il commento appartiene.
         */
        public function post(): BelongsTo
        {
            return $this->belongsTo(Post::class);
        }
    }

I timestamp del modello genitore verranno aggiornati solo se il modello figlio viene aggiornato usando il metodo save di Eloquent.

Lascia un commento

Lascia un commento

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