Vai al contenuto principale
Se vuoi continuare a leggere i dati salvati delle relazioni polimorfiche dopo aver spostato o rinominato un modello del pacchetto, separa il nome della classe del modello dal nome del tipo salvato nel database. Un alias della morph map non è tanto un’abbreviazione quanto un contratto sul formato di salvataggio da mantenere tra una release e l’altra. Questa pagina fa riferimento all’implementazione ufficiale di Laravel Framework v13.42.0. Per le basi delle relazioni consulta l’Introduzione alle relazioni Eloquent. Per aggiungere relazioni dall’esterno ai modelli dell’app che usa il pacchetto, consulta la Registrazione di relazioni dinamiche nei pacchetti.

Separare i nomi dei tipi dai nomi delle relazioni

Una relazione polimorfica combina la chiave primaria del modello collegato e un nome di tipo per riferirsi a più tipi di modelli. Ad esempio, se i commenti possono appartenere ad articoli o video, oltre a commentable_id serve anche commentable_type. Senza registrazione, di norma si usa come tipo un nome di classe completamente qualificato come Acme\Publishing\Models\Article. Dopo aver registrato un alias, si usa acme-publishing.article, ad esempio quando si creano nuove relazioni. La sola registrazione non riscrive i valori delle righe esistenti. Una morph map non crea metodi di relazione né tabelle. Prepara modelli, colonne e indici come al solito.

Registrare solo alias specifici del pacchetto

Nell’esempio, Acme\Publishing\ corrisponde a src/ tramite il PSR-4 di Composer. Per prima cosa definisci src/Models/Article.php.
In src/Models/Comment.php definisci la relazione inversa.
acme_articles ha id, title e i timestamp, mentre acme_comments ha id, body, commentable_type, commentable_id e i timestamp. Le chiavi primarie di questo esempio sono interi. Puoi usare le colonne e l’indice composto creati da $table->morphs('commentable'), ma con UUID o configurazioni simili allinea i tipi di colonna al modello collegato. Poi registra l’alias in boot() di un normale service provider non differito, src/PublishingServiceProvider.php.
Il caricamento del provider si configura con il package auto-discovery. Durante la registrazione non devi salvare modelli né aggiornare i nomi dei tipi nel database. La migrazione dei dati esistenti è un’attività separata.
$alias è acme-publishing.article e $class è Article::class. A meno che Comment stesso non diventi il modello collegato di un’altra relazione polimorfica, per questo esempio non serve registrare un alias anche per Comment.

Capire le regole di unione e di conflitto dei nomi

Relation::morphMap() opera su una mappa statica. L’unione predefinita combina il nuovo array e quello esistente con $map + static::$morphMap. Se un pacchetto passa false come secondo argomento, cancella le registrazioni degli altri pacchetti e dell’app. Nella registrazione normale mantieni la mappa esistente e scegli un alias che identifichi il vendor e il pacchetto, invece di un nome generico come article. Non riutilizzare lo stesso alias con un significato diverso. Se passi solo un elenco di nomi di classe, Laravel istanzia ogni modello e usa il nome della tabella come chiave. I pacchetti che vogliono rendere il formato di salvataggio indipendente anche dai cambi di nome della tabella devono dichiarare esplicitamente la corrispondenza tra alias e classe, come nell’esempio sopra.
Se l’app aggiunge un altro alias per la stessa classe, l’ordine di registrazione può cambiare i valori salvati da quel momento in poi e le condizioni delle query di relazione. Registrare entrambi i nomi non garantisce di per sé la compatibilità. Anche quando consenti di sostituire la classe, verifica come parte del contratto pubblico che i dati salvati restino leggibili con lo stesso alias.
Il getMorphClass() standard cerca la classe concreta del modello. Usare una sottoclasse di Article non eredita automaticamente l’alias della classe padre. I pacchetti che supportano la sostituzione dei modelli devono prevedere la registrazione nella mappa delle classi effettivamente usate.

Rendere la modalità rigorosa una scelta dell’intera app

enforceMorphMap() della documentazione ufficiale registra gli alias e chiama requireMorphMap(true) in un solo passaggio. Non è un vincolo limitato al pacchetto. Nell’implementazione standard di v13.42.0, la modalità rigorosa agisce in entrambe le direzioni.
  • Dal modello al nome del tipo: getMorphClass() lancia ClassMorphViolationException per i modelli non registrati. La classe generica Pivot integrata ha un’eccezione, ma i modelli normali e i pivot personalizzati non godono della stessa esenzione.
  • Dal nome del tipo salvato alla classe: getActualClassNameForMorph() lancia la stessa eccezione per i valori che non sono chiavi della mappa. Viene usato anche nel normale recupero con morphTo() e nell’eager loading. Anche se la classe compare come “valore” nella mappa, un vecchio nome di classe completamente qualificato che non è una “chiave” non può essere caricato.
Se la modalità rigorosa è disattivata, i nomi di tipo non presenti nella mappa vengono trattati come nomi di classe. Questo però non garantisce che la classe esista né che sia il modello collegato atteso. Di solito il pacchetto registra solo i propri modelli con morphMap() e lascia all’app la scelta di imporre la mappa a livello globale. Se l’app la impone già, non riportarla a requireMorphMap(false) dal pacchetto.

Migrare i dati esistenti prima di valutare la compatibilità

Introdurre la registrazione degli alias comporta condizioni diverse per le nuove installazioni e per l’aggiornamento di app esistenti. Anche la documentazione ufficiale avverte che, al momento dell’introduzione, occorre convertire in alias i nomi di classe completamente qualificati esistenti.

Verificare solo il recupero inverso non basta

Con la modalità rigorosa disattivata, finché la classe del vecchio commentable_type esiste potresti ancora recuperare l’articolo da un commento con morphTo(). Il morphMany() lato articolo, però, filtra in base al valore di getMorphClass(). Dopo la registrazione dell’alias cerca acme-publishing.article, quindi le righe con il vecchio nome di classe spariscono dall’elenco dei commenti dell’articolo. MorphOneOrMany memorizza il nome del tipo alla creazione e lo usa sia nelle condizioni di query sia nella creazione di nuovi record. Anche se nomi di tipo vecchi e nuovi convivono, non cerca automaticamente tutti i valori in OR.

Includere la procedura di aggiornamento nella release

Il seguente frammento converte il vecchio nome di classe nell’alias. Implementalo come migrazione dei dati dell’app di destinazione e non eseguirlo automaticamente da un service provider.
Questo passaggio da solo non completa il deploy.
  1. Analizza le colonne *_type interessate e i valori salvati, e prepara backup, conteggio delle righe e procedura di ripristino prima dell’aggiornamento. Se altre tabelle fanno riferimento allo stesso modello, includi anche quelle.
  2. Per la prima introduzione definisci una procedura di passaggio, inclusi lo stop delle scritture o la manutenzione, così che non convivano processi che scrivono il vecchio nome di classe e processi che scrivono l’alias. Per tabelle grandi valuta anche lock di aggiornamento ed elaborazione a batch.
  3. Cambia mappa e dati, convertendo i nomi dei tipi senza modificare i valori delle chiavi primarie. Nel codice di migrazione fissa il vecchio nome di classe come la stringa effettivamente salvata in passato, non come Article::class, il cui significato cambia con le future rinomine.
  4. Verifica il numero di vecchi valori rimasti, i nuovi salvataggi, i recuperi in entrambe le direzioni e l’eager loading, poi aggiorna i worker a lunga esecuzione prima di riprendere le scritture.
  5. Per un rollback, verifica che il codice ripristinato sappia risolvere l’alias salvato. Tornare alla versione Composer precedente non ripristina da solo i nomi dei tipi nel database.
Se mantieni un alias già fissato e cambi solo il namespace del modello, aggiorna il valore della mappa con la nuova classe e i nomi dei tipi continuano a funzionare così come sono. Cambiare l’alias stesso, invece, va trattato come una modifica che richiede una migrazione dei dati. Allinea la procedura di aggiornamento anche a Gestire la compatibilità tra versioni dei pacchetti e Pubblicazione e aggiornamento delle migrazioni di un pacchetto.

Testare i valori salvati e i recuperi in entrambe le direzioni

Con Orchestra Testbench verifica non solo la mappa, ma anche i valori salvati nel database e i recuperi in entrambe le direzioni. L’esempio seguente presuppone che il pacchetto sia caricato automaticamente tramite Composer e che sia disponibile il driver PDO SQLite. Registra il provider esplicitamente nel test, separandolo dalla verifica dell’auto-discovery.
La mappa e il flag di obbligo sono stati statici separati, quindi non dare per scontato che ricreare l’applicazione li reimposti. Questo esempio salva e ripristina entrambi. Anche in produzione non riscriverli per singola richiesta o tenant, e limitati a una registrazione stabile all’avvio. Le relazioni già create mantengono il nome del tipo, quindi una modifica della registrazione a metà strada non si riflette in modo uniforme sulle istanze esistenti. Aggiungi inoltre alla CI del pacchetto i seguenti casi di regressione.
  • La registrazione del provider non disattiva la modalità rigorosa quando l’app l’ha già attivata.
  • Il formato di salvataggio pubblicato non cambia con conflitti sullo stesso alias, più alias per la stessa classe e un modello concreto sostituito.
  • Con fixture salvate con il nome di classe precedente all’aggiornamento, dopo la migrazione non restano vecchi valori e i recuperi in entrambe le direzioni e i conteggi coincidono.
  • La procedura di aggiornamento reale, inclusi i queue worker, può essere ripristinata con la combinazione di codice e dati riportati indietro.

Fonti primarie consultate

Ultima modifica il 11 novembre 2026