Saltar al contenido principal
Si quieres seguir leyendo los datos guardados de relaciones polimórficas después de mover o renombrar un modelo de tu paquete, separa el nombre de clase del modelo del nombre de tipo que se guarda en la base de datos. Un alias del morph map no es tanto una abreviatura como un contrato sobre el formato de almacenamiento que mantienes entre versiones. Esta página se basa en la implementación oficial de Laravel Framework v13.42.0. Para los fundamentos de las relaciones, consulta la Introducción a las relaciones de Eloquent. Para añadir relaciones desde fuera a los modelos de la aplicación que usa el paquete, consulta el Registro dinámico de relaciones en paquetes.

Separar los nombres de tipo de los nombres de relación

Una relación polimórfica combina la clave primaria del modelo relacionado con un nombre de tipo para referenciar varios tipos de modelos. Por ejemplo, si los comentarios pueden pertenecer a artículos o vídeos, necesitas commentable_type además de commentable_id. Si no hay registro, normalmente se usa como tipo un nombre de clase completamente cualificado como Acme\Publishing\Models\Article. Al registrar un alias, se usa acme-publishing.article, por ejemplo al crear nuevas relaciones. Registrarlo no reescribe los valores de las filas existentes. Un morph map no crea métodos de relación ni tablas. Prepara los modelos, las columnas y los índices como de costumbre.

Registrar solo alias propios del paquete

En el ejemplo, Acme\Publishing\ corresponde a src/ mediante PSR-4 de Composer. Primero, define src/Models/Article.php.
En src/Models/Comment.php defines la relación inversa.
acme_articles tiene id, title y marcas de tiempo, y acme_comments tiene id, body, commentable_type, commentable_id y marcas de tiempo. Las claves primarias de este ejemplo son enteros. Puedes usar las columnas y el índice compuesto que crea $table->morphs('commentable'), pero si usas UUID u otras configuraciones, haz coincidir los tipos de columna con el modelo relacionado. A continuación, registra el alias en boot() de un service provider normal, no diferido, src/PublishingServiceProvider.php.
La carga del provider se configura con la detección automática de paquetes. Durante el registro no necesitas guardar modelos ni actualizar los nombres de tipo en la base de datos. La migración de los datos existentes es una tarea aparte.
$alias es acme-publishing.article y $class es Article::class. Salvo que Comment pase a ser el modelo relacionado de otra relación polimórfica, no necesitas registrar un alias para Comment en este ejemplo.

Entender las reglas de fusión y colisión de nombres

Relation::morphMap() opera sobre una tabla de correspondencias estática. La fusión predeterminada combina el array nuevo y el existente con $map + static::$morphMap. Si un paquete pasa false como segundo argumento, borra los registros de otros paquetes y de la aplicación. En un registro normal, conserva la tabla existente y elige un alias que identifique al proveedor y al paquete, en lugar de un nombre genérico como article. No reutilices el mismo alias con otro significado. Si pasas solo una lista de nombres de clase, Laravel instancia cada modelo y usa el nombre de su tabla como clave. Los paquetes que quieran desacoplar el formato de almacenamiento también de los cambios de nombre de tabla deben declarar explícitamente la correspondencia entre alias y clase, como en el ejemplo anterior.
Si la aplicación añade otro alias para la misma clase, el orden de registro puede cambiar los valores que se guarden a partir de entonces y las condiciones de las consultas de relación. Registrar ambos nombres no garantiza que se mantenga la compatibilidad. Incluso si permites sustituir la clase, comprueba como parte del contrato público que los datos guardados se pueden leer con el mismo alias.
El getMorphClass() estándar busca la clase concreta del modelo. Usar una subclase de Article no hereda automáticamente el alias de la clase padre. Los paquetes que admiten sustituir modelos necesitan un diseño que registre en la tabla las clases que realmente se usan.

Convertir el modo estricto en una política de toda la aplicación

enforceMorphMap(), de la documentación oficial, registra los alias y llama a requireMorphMap(true) en un solo paso. No es una obligación limitada al paquete. En la implementación estándar de v13.42.0, el modo estricto afecta en ambos sentidos.
  • Del modelo al nombre de tipo: getMorphClass() lanza ClassMorphViolationException para modelos no registrados. La clase genérica Pivot incorporada tiene una excepción, pero los modelos normales y los pivots propios no tienen esa exención.
  • Del nombre de tipo guardado a la clase: getActualClassNameForMorph() lanza la misma excepción para valores que no son claves de la tabla. También se usa en la obtención normal con morphTo() y en la carga anticipada. Aunque la clase figure como “valor” en la tabla, un nombre de clase completamente cualificado antiguo que no sea “clave” no se puede cargar.
Si el modo estricto está desactivado, los nombres de tipo que no están en la tabla se tratan como nombres de clase. Sin embargo, eso no garantiza que la clase exista ni que sea el modelo relacionado esperado. Normalmente, el paquete registra solo sus propios modelos con morphMap() y deja a la aplicación la decisión de imponerlo en toda la aplicación. Si la aplicación ya lo impone, no lo desactives desde el paquete con requireMorphMap(false).

Migrar los datos existentes antes de evaluar la compatibilidad

Introducir el registro de alias tiene condiciones distintas para instalaciones nuevas y para actualizaciones de aplicaciones existentes. La documentación oficial también advierte que, al introducirlo, hay que convertir los nombres de clase completamente cualificados existentes en alias.

Comprobar solo la consulta inversa no basta

Con el modo estricto desactivado, es posible que puedas seguir obteniendo el artículo desde un comentario con morphTo() mientras exista la clase del commentable_type antiguo. Sin embargo, el morphMany() del lado del artículo filtra por el valor de getMorphClass(). Tras registrar el alias busca acme-publishing.article, así que las filas con el nombre de clase antiguo desaparecen de la lista de comentarios del artículo. MorphOneOrMany conserva el nombre de tipo al crearse y lo usa tanto en las condiciones de consulta como al crear registros nuevos. Aunque convivan nombres de tipo antiguos y nuevos, no busca automáticamente todos los valores con OR.

Incluir el procedimiento de actualización en la versión

El siguiente fragmento convierte el nombre de clase antiguo en el alias. Impleméntalo como una migración de datos de la aplicación destino y no lo ejecutes automáticamente desde un service provider.
Este paso por sí solo no completa el despliegue.
  1. Revisa las columnas *_type afectadas y los valores guardados, y prepara una copia de seguridad, un recuento de filas y un procedimiento de restauración antes de actualizar. Si otras tablas referencian el mismo modelo, inclúyelas también.
  2. En la primera implantación, define un procedimiento de cambio, incluida la detención de escrituras o el mantenimiento, para que no convivan procesos que escriben el nombre de clase antiguo y procesos que escriben el alias. En tablas grandes, considera también los bloqueos de actualización y el procesamiento por lotes.
  3. Cambia la tabla y los datos, convirtiendo los nombres de tipo sin modificar los valores de las claves primarias. Fija el nombre de clase antiguo en el código de migración como la cadena que realmente se guardó en el pasado, no como Article::class, cuyo significado cambia con futuros renombrados.
  4. Comprueba el número de valores antiguos restantes, los nuevos guardados, las consultas de relación en ambos sentidos y la carga anticipada, y actualiza los workers de larga duración antes de reanudar las escrituras.
  5. Para revertir, comprueba que el código al que vuelves puede resolver el alias guardado. Volver a la versión anterior de Composer no revierte los nombres de tipo de la base de datos.
Si mantienes un alias ya fijado y solo cambias el namespace del modelo, cambia el valor de la tabla a la nueva clase y los nombres de tipo siguen funcionando tal cual. En cambio, modificar el propio alias se trata como un cambio que requiere migrar datos. Alinea también el procedimiento de actualización con la Gestión de la compatibilidad de versiones de paquetes y la Publicación y actualización de migraciones de paquetes.

Probar los valores guardados y las consultas en ambos sentidos

Con Orchestra Testbench, verifica no solo la tabla, sino también los valores guardados en la base de datos y las consultas en ambos sentidos. El siguiente ejemplo supone que el paquete se carga automáticamente con Composer y que el driver PDO de SQLite está disponible. Registra el provider explícitamente en la prueba, separado de la verificación de la detección automática.
La tabla y el indicador de obligación son estados estáticos independientes, así que no supongas que reconstruir la aplicación los reinicia. Este ejemplo guarda y restaura ambos. En producción tampoco los reescribas por petición ni por tenant, y limítate a un registro estable durante el arranque. Las relaciones ya creadas conservan su nombre de tipo, por lo que un cambio de registro a mitad de camino tampoco se refleja de forma uniforme en las instancias existentes. Añade además los siguientes casos de regresión a la CI del paquete.
  • Registrar el provider no desactiva el modo estricto cuando la aplicación ya lo ha activado.
  • El formato de almacenamiento publicado no cambia con conflictos del mismo alias, varios alias para la misma clase o un modelo concreto sustituido.
  • Con fixtures guardados con el nombre de clase anterior a la actualización, tras la migración no quedan valores antiguos y las consultas en ambos sentidos y los recuentos coinciden.
  • El procedimiento real de actualización, incluidos los workers de colas, se puede restaurar con la combinación de código y datos revertidos.

Fuentes primarias consultadas

Última modificación el 11 de noviembre de 2026