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 acommentable_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.
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.
$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.
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()lanciaClassMorphViolationExceptionper i modelli non registrati. La classe genericaPivotintegrata 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 conmorphTo()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.
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 vecchiocommentable_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.- Analizza le colonne
*_typeinteressate 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. - 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.
- 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. - 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.
- 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.
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 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
- Documentazione ufficiale di Laravel: Custom Polymorphic Types
- Documentazione ufficiale di Laravel: Package Development
- Relation: unione della mappa e modalità rigorosa
- HasRelationships: recupero dei nomi dei tipi e risoluzione delle classi
- MorphOneOrMany: memorizzazione del nome del tipo, condizioni di query e salvataggio
- MorphTo: risoluzione dei tipi durante l’eager loading
- Test ufficiali di Laravel: registrazione e risoluzione delle morph map
- Test ufficiali di Laravel: lettura e scrittura in modalità rigorosa