> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Publicación y actualización de migraciones de paquetes

> A partir de la implementación de Laravel 13, explica la diferencia entre publishesMigrations y loadMigrationsFrom, el cambio de marcas de tiempo al publicar, los riesgos de volver a publicar y cómo hacer llegar los cambios de esquema a los usuarios existentes.

Para mantener a largo plazo un paquete que usa la base de datos, no basta con la instalación inicial: también necesitas un procedimiento para hacer llegar los cambios a los usuarios que ya tienen las tablas creadas. Diseña la publicación, la ejecución y el historial de ejecución de las migraciones como procesos separados.

Esta página parte de los [fundamentos del desarrollo de paquetes](/es/advanced/package-development) y analiza `ServiceProvider`, `VendorPublishCommand` y `Migrator` de Laravel 13. La implementación del framework que se toma como referencia es `v13.34.0`.

## ¿Copiar los archivos o cargarlos desde el paquete?

| Método | Proceso en el provider | Acción del usuario | Ubicación de los archivos |
| - | - | - | - |
| Publicar | `publishesMigrations()` | `migrate` después de `vendor:publish` | `database/migrations` de la aplicación |
| Cargar directamente | `loadMigrationsFrom()` | `migrate` | Dentro del paquete instalado |

`publishesMigrations()` solo registra el origen y el destino de la copia como recursos publicables. Arrancar el provider no copia archivos ni ejecuta SQL.

Por su parte, `loadMigrationsFrom()` registra una ruta de búsqueda en el Migrator. Con un `migrate` normal, los archivos de esa ruta también se incluyen, pero arrancar el provider por sí solo no los ejecuta.

```mermaid theme={null}
flowchart TD
    A["Service provider del paquete"] --> B["publishesMigrations()<br>Registra origen y destino de la copia"]
    B --> C["vendor:publish<br>Copia a la aplicación"]
    C --> E["migrate<br>Ejecuta los archivos pendientes"]
    A --> D["loadMigrationsFrom()<br>Añade a las rutas de búsqueda del Migrator"]
    D --> E
    E --> F["Registra los nombres de archivo ejecutados<br>en la tabla migrations"]
```

Si el diseño espera que el usuario ajuste nombres de tablas o columnas antes de ejecutar, la publicación es la opción candidata. Si el paquete gestiona el esquema y no se espera que el usuario edite los archivos, también puedes considerar la carga directa. Los dos ejemplos de provider siguientes son alternativas entre sí.

<Warning>
  Evita un diseño que publique una migración y al mismo tiempo la cargue directamente. Si la marca de tiempo cambia al publicar, el origen y el destino se tratan como entradas distintas del historial de ejecución, y el mismo proceso de creación de tablas podría ejecutarse dos veces.
</Warning>

## Implementar la publicación

Asigna una etiqueta propia del paquete para que el usuario pueda publicarlo por separado de otros recursos.

```php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->publishesMigrations([
            __DIR__.'/../database/migrations' => database_path('migrations'),
        ], 'courier-migrations');
    }
}
```

En la instalación inicial, copia los archivos indicando el provider y la etiqueta, revisa su contenido y después ejecútalos. Si indicas ambos, Laravel selecciona los recursos publicables de esa etiqueta que pertenecen a ese provider.

```bash theme={null}
php artisan vendor:publish --provider="Acme\Courier\CourierServiceProvider" --tag=courier-migrations
php artisan migrate:status
php artisan migrate
```

### El cambio de marca de tiempo depende de la configuración

La documentación oficial describe que, al publicar, la marca de tiempo de la migración se actualiza a la fecha y hora actuales. Sin embargo, la implementación de `ServiceProvider::publishesMigrations()` solo añade el origen a los archivos cuya marca de tiempo se actualizará cuando `database.migrations.update_date_on_publish` está activado. El valor por defecto al leer esta configuración es `false`.

En la aplicación estándar de Laravel 13, `config/database.php` incluye la siguiente configuración. En aplicaciones que heredan una estructura antigua, comprueba también que esta configuración exista.

```php theme={null}
'migrations' => [
    'table' => 'migrations',
    'update_date_on_publish' => true,
],
```

Además, `VendorPublishCommand` reescribe la fecha cuando el archivo coincide con la ruta real de un origen registrado y el nombre de destino tiene el formato `YYYY_MM_DD_HHMMSS_`. Toma como base la hora de inicio del comando y suma un segundo por cada archivo afectado. Si el nombre no tiene este formato, ese proceso no le añade ninguna fecha.

```text theme={null}
Origen:
2026_09_01_000000_create_courier_deliveries_table.php

Ejemplo tras publicar:
2026_10_02_120001_create_courier_deliveries_table.php
```

La fecha posterior a la publicación del ejemplo es ilustrativa. El nombre real del archivo depende del momento en que se publique.

<Info>
  La actualización de la marca de tiempo depende tanto del registro en el paquete como de la configuración de la aplicación que lo usa. No cambies esta configuración de forma generalizada desde el provider del paquete; documenta el requisito en las instrucciones de instalación. Si se usa la caché de configuración, también hay que reconstruirla después de cambiar la configuración.
</Info>

## Volver a publicar no significa "añadir solo lo pendiente"

`vendor:publish` no consulta el historial de ejecución de la base de datos. Además, en el proceso de copia de `v13.34.0`, la comprobación de archivos existentes se hace sobre el destino **antes del cambio de marca de tiempo**. Incluso al publicar un directorio, primero comprueba si en el destino existe la misma ruta relativa que en el origen, y solo después reescribe la fecha.

Por eso, si la fecha cambió en la primera publicación y en la aplicación no existe un archivo con el mismo nombre que el origen, volver a publicar la misma etiqueta puede añadir otro archivo con una fecha distinta. No des por hecho que, sin `--force`, siempre se evitan los duplicados.

| Condición | Qué vigilar al volver a publicar |
| - | - |
| La actualización de fecha está activada y no existe el destino previo al cambio | Puede añadirse otra copia con una fecha distinta |
| La actualización de fecha está desactivada y el destino tiene el mismo nombre | Normalmente se omite el archivo existente |
| Se indica `--force` | Si se cumplen las condiciones de copia y la actualización de fecha está activada, puede generarse un nombre distinto |
| Se indica `--existing` | Como comprueba la existencia del destino previo al cambio, no siempre se incluye aunque exista el archivo con la fecha ya cambiada |

### Si una migración ya se ejecutó se decide por el nombre de archivo

`Migrator::getMigrationName()` devuelve el nombre base del archivo sin `.php`. Para determinar qué está pendiente, compara este nombre con el historial de ejecución. No decide en función de si el contenido PHP o el nombre de la tabla son iguales.

```text theme={null}
2026_10_02_120001_create_courier_deliveries_table
2026_10_03_090001_create_courier_deliveries_table
```

Son dos nombres de migración distintos. Aunque el primero ya se haya ejecutado, ese historial por sí solo no marca el segundo como ejecutado.

<Warning>
  No vuelvas a ejecutar sin condiciones el comando de publicación de la instalación inicial en cada actualización del paquete, ni conviertas `--force` en el procedimiento estándar. Además de sobrescribir las ediciones de los archivos publicados, el cambio de fecha puede añadir procesos duplicados.
</Warning>

## Implementar la carga directa

Si quieres que las migraciones del paquete se ejecuten tal cual, registra la ruta de búsqueda. Con este método no se añade ningún proceso que publique los mismos archivos.

```php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
    }
}
```

`loadMigrationsFrom()` llama a `path()` cuando se resuelve el Migrator. `Migrator::path()` elimina las rutas de búsqueda duplicadas, y `getMigrationFiles()` indexa los archivos encontrados por nombre de migración y los ordena por ese nombre.

Cuando el usuario actualiza el paquete, los archivos nuevos se incluyen en el siguiente `migrate`. No cambies el nombre de los archivos existentes y añade archivos nuevos para los nuevos cambios de esquema. Para evitar colisiones con otros paquetes, incluye también el nombre de la funcionalidad, como en `create_courier_deliveries_table`. Los archivos con el mismo nombre comparten la misma clave, así que no se ejecutan ambos de forma independiente.

<Warning>
  Pasar de la publicación a la carga directa no es simplemente reescribir el provider. Si el historial de ejecución del usuario está registrado con los nombres asignados al publicar, no coincidirá con los nombres originales del paquete. Necesitas un procedimiento de transición que tenga en cuenta el historial de los usuarios existentes, los archivos publicados y los rollbacks.
</Warning>

## Hacer llegar los cambios de esquema a los usuarios existentes

Por ejemplo, para añadir un número de seguimiento a la tabla de envíos, no edites el `create_courier_deliveries_table` ya publicado: añade un archivo nuevo para el cambio. Si editas la migración de creación existente, ese cambio no se ejecutará para los usuarios que ya la ejecutaron.

```php database/migrations/2026_10_02_000000_add_tracking_code_to_courier_deliveries_table.php theme={null}
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::table('courier_deliveries', function (Blueprint $table) {
            $table->string('tracking_code')->nullable();
        });
    }

    public function down(): void
    {
        Schema::table('courier_deliveries', function (Blueprint $table) {
            $table->dropColumn('tracking_code');
        });
    }
};
```

Como el ejemplo añade una columna a una tabla que ya tiene filas, aquí se define como nullable. Si necesitas hacerla obligatoria o rellenar datos, diseña por separado ese procedimiento y su orden de ejecución.

Con la publicación, prepara un procedimiento de actualización que compare con los archivos ya publicados del usuario y entregue **solo los archivos añadidos en esta versión**. También puedes crear una etiqueta de publicación exclusiva para los archivos nuevos, pero si la actualización de fecha está activada, ejecutar esa etiqueta repetidamente requiere la misma precaución. No conviertas el procedimiento de actualización en volver a ejecutar la etiqueta de la instalación inicial.

Con la carga directa, el código actualizado detecta los archivos nuevos. En ambos métodos, la existencia de los archivos no modifica la base de datos por sí sola, así que indica claramente en las notas de la versión que es necesario ejecutar las migraciones.

## Qué comprobar antes de publicar una versión

Además de las pruebas de base de datos del paquete, comprueba los procedimientos de publicación y actualización en una aplicación que lo use. Cargar las migraciones directamente en las pruebas no equivale a verificar la publicación, en la que cambian los nombres de archivo.

* Una instalación inicial en una base de datos vacía crea las tablas necesarias.
* Al actualizar desde la base de datos y el historial de ejecución de una versión anterior, solo se aplican los cambios nuevos.
* Revisa la lista de archivos tras repetir el mismo comando de publicación y comprueba que el procedimiento de actualización no genera duplicados.
* El procedimiento tiene en cuenta si la actualización de fecha está activada o desactivada y si se editaron los archivos publicados.
* Comprueba el rollback de las nuevas migraciones y su orden de ejecución respecto a las demás migraciones de la aplicación.

## Páginas relacionadas

<Columns cols={2}>
  <Card title="Migraciones" icon="database" href="/es/migrations">
    Repasa los fundamentos de la definición de esquemas, el historial de ejecución y los rollbacks.
  </Card>

  <Card title="Pruebas de paquetes" icon="flask" href="/es/advanced/package-testing">
    Prueba el service provider y la base de datos del paquete.
  </Card>

  <Card title="Fusión y caché de la configuración de paquetes" icon="sliders" href="/es/advanced/package-config-merging">
    Revisa procedimientos de actualización que tienen en cuenta la configuración del usuario y la caché de configuración.
  </Card>

  <Card title="Gestión de la compatibilidad de versiones" icon="code-branch" href="/es/advanced/package-versioning">
    Vincula los procedimientos de actualización y los cambios de compatibilidad con la política de versiones.
  </Card>
</Columns>

## Fuentes primarias consultadas

* [Documentación oficial de Laravel: migraciones de paquetes](https://github.com/laravel/docs/blob/156fc7fde114548640e13c39aa79b991291c3f91/packages.md#migrations)
* [ServiceProvider: registro de recursos publicables y rutas de búsqueda](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Support/ServiceProvider.php)
* [VendorPublishCommand: copia y cambio de marcas de tiempo](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/VendorPublishCommand.php)
* [Migrator: detección de archivos y determinación de pendientes](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Database/Migrations/Migrator.php)
* [Aplicación estándar de Laravel 13: configuración de base de datos](https://github.com/laravel/laravel/blob/06d016a364a37430eec9cbc52209adce3d7667ce/config/database.php)


## Related topics

- [Desarrollo de paquetes para Laravel](/es/advanced/package-development.md)
- [Temas avanzados](/es/advanced/index.md)
- [Actualización de Laravel 10 a 11](/es/blog/upgrade-10-to-11.md)
- [Actualización de Laravel — Junio de 2026](/es/blog/changelog/202606.md)
- [Actualización de Laravel — Abril de 2026](/es/blog/changelog/202604.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.