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

# Paket-Migrationen veröffentlichen und aktualisieren

> Anhand der Implementierung von Laravel 13 erläutert: der Unterschied zwischen publishesMigrations und loadMigrationsFrom, die Änderung von Zeitstempeln beim Veröffentlichen, die Risiken erneuten Veröffentlichens und wie Sie Schemaänderungen an bestehende Nutzer ausliefern.

Wer ein Paket mit Datenbankzugriff dauerhaft pflegen will, braucht nicht nur einen Ablauf für die Erstinstallation, sondern auch einen Weg, Änderungen an Nutzer auszuliefern, bei denen die Tabellen bereits existieren. Planen Sie das Veröffentlichen, das Ausführen und den Ausführungsverlauf von Migrationen als getrennte Vorgänge.

Diese Seite setzt die [Grundlagen der Paketentwicklung](/de/advanced/package-development) voraus und analysiert `ServiceProvider`, `VendorPublishCommand` und `Migrator` aus Laravel 13. Als Referenz für die Framework-Implementierung dient `v13.34.0`.

## Kopieren und übergeben oder aus dem Paket laden?

| Methode | Verarbeitung im Provider | Aktion der Nutzer | Speicherort der Dateien |
| - | - | - | - |
| Veröffentlichen | `publishesMigrations()` | `migrate` nach `vendor:publish` | `database/migrations` der Anwendung |
| Direkt laden | `loadMigrationsFrom()` | `migrate` | Im installierten Paket |

`publishesMigrations()` registriert lediglich Quelle und Ziel als Veröffentlichungsziel. Beim Booten des Providers werden weder Dateien kopiert noch SQL-Anweisungen ausgeführt.

`loadMigrationsFrom()` hingegen registriert einen Suchpfad beim Migrator. Ein normales `migrate` berücksichtigt dann auch die Dateien in diesem Pfad, doch allein durch das Booten des Providers wird nichts ausgeführt.

```mermaid theme={null}
flowchart TD
    A["Service Provider des Pakets"] --> B["publishesMigrations()<br>Quelle und Ziel registrieren"]
    B --> C["vendor:publish<br>In die Anwendung kopieren"]
    C --> E["migrate<br>Nicht ausgeführte Dateien ausführen"]
    A --> D["loadMigrationsFrom()<br>Zum Suchpfad des Migrators hinzufügen"]
    D --> E
    E --> F["Namen ausgeführter Dateien<br>in der Tabelle migrations speichern"]
```

Sollen Nutzer Tabellennamen oder Spalten vor dem Ausführen anpassen können, kommt das Veröffentlichen infrage. Verwaltet das Paket das Schema selbst und ist keine Bearbeitung der Dateien durch die Nutzer vorgesehen, können Sie auch das direkte Laden in Betracht ziehen. Die beiden folgenden Provider-Beispiele sind Alternativen zueinander.

<Warning>
  Vermeiden Sie ein Design, bei dem dieselben Migrationen sowohl veröffentlicht als auch direkt geladen werden. Ändert sich der Zeitstempel beim Veröffentlichen, werden Quelle und Kopie als getrennte Einträge im Ausführungsverlauf behandelt, und dieselbe Tabellenerstellung kann doppelt ausgeführt werden.
</Warning>

## Das Veröffentlichen implementieren

Vergeben Sie einen paketspezifischen Tag, damit Nutzer die Migrationen getrennt von anderen Ressourcen veröffentlichen können.

```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');
    }
}
```

Bei der Erstinstallation kopieren Nutzer die Dateien unter Angabe des Providers und des Tags, prüfen den Inhalt und führen sie dann aus. Werden beide angegeben, wählt Laravel die Veröffentlichungsziele dieses Tags aus, die zu diesem Provider gehören.

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

### Die Änderung des Zeitstempels hängt von der Konfiguration ab

Die offizielle Dokumentation beschreibt, dass der Zeitstempel einer Migration beim Veröffentlichen auf das aktuelle Datum und die aktuelle Uhrzeit gesetzt wird. Die Implementierung von `ServiceProvider::publishesMigrations()` fügt die Quelle jedoch nur dann den Kandidaten für die Zeitstempelaktualisierung hinzu, wenn `database.migrations.update_date_on_publish` aktiviert ist. Der Fallback beim Auslesen dieser Einstellung ist `false`.

Die `config/database.php` der Standardanwendung von Laravel 13 enthält die folgende Einstellung. Bei Anwendungen, die eine ältere Struktur übernommen haben, prüfen Sie auch, ob die Einstellung überhaupt vorhanden ist.

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

Außerdem schreibt `VendorPublishCommand` das Datum nur um, wenn der reale Pfad mit einer registrierten Quelle übereinstimmt und der Zielname das Format `YYYY_MM_DD_HHMMSS_` enthält. Ausgehend vom Startzeitpunkt des Befehls wird für jede betroffene Datei eine Sekunde addiert. Fehlt dieses Format im Namen, fügt dieser Vorgang kein Datum hinzu.

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

Beispiel nach dem Veröffentlichen:
2026_10_02_120001_create_courier_deliveries_table.php
```

Das Datum nach dem Veröffentlichen ist oben nur zur Veranschaulichung angegeben. Der tatsächliche Dateiname hängt vom Zeitpunkt des Veröffentlichens ab.

<Info>
  Die Zeitstempelaktualisierung hängt sowohl von der Registrierung im Paket als auch von der Konfiguration der nutzenden Anwendung ab. Ändern Sie diese Einstellung nicht pauschal aus dem Provider des Pakets heraus, sondern dokumentieren Sie die Voraussetzung in der Installationsanleitung. Wird ein Konfigurations-Cache verwendet, muss dieser nach der Änderung zusätzlich neu erstellt werden.
</Info>

## Erneutes Veröffentlichen heißt nicht „nur nicht ausgeführte hinzufügen“

`vendor:publish` prüft den Ausführungsverlauf in der Datenbank nicht. Zudem prüft der Kopiervorgang in `v13.34.0` die Existenz vorhandener Dateien anhand des Ziels **vor der Änderung des Zeitstempels**. Auch beim Veröffentlichen eines Verzeichnisses wird zuerst geprüft, ob im Ziel derselbe relative Pfad wie in der Quelle existiert, und erst danach wird das Datum umgeschrieben.

Wurde das Datum also beim ersten Veröffentlichen geändert und gibt es in der Anwendung keine Datei mit demselben Namen wie in der Quelle, kann ein erneutes Veröffentlichen desselben Tags eine weitere Datei mit anderem Datum hinzufügen. Gehen Sie nicht davon aus, dass Duplikate immer vermieden werden, solange Sie `--force` nicht angeben.

| Bedingung | Worauf beim erneuten Veröffentlichen zu achten ist |
| - | - |
| Datumsaktualisierung aktiviert und Ziel vor der Änderung existiert nicht | Eine Kopie mit anderem Datum kann hinzugefügt werden |
| Datumsaktualisierung deaktiviert und Ziel mit gleichem Namen vorhanden | Die vorhandene Datei wird normalerweise übersprungen |
| `--force` angegeben | Sind die Kopierbedingungen erfüllt und ist die Datumsaktualisierung aktiviert, kann ein anderer Name entstehen |
| `--existing` angegeben | Da die Existenz des Ziels vor der Änderung geprüft wird, ist eine Datei nicht unbedingt betroffen, wenn nur die Datei mit geändertem Datum existiert |

### Ob eine Migration ausgeführt wurde, wird am Dateinamen entschieden

`Migrator::getMigrationName()` gibt den Basisnamen der Datei ohne `.php` zurück. Bei der Prüfung auf nicht ausgeführte Migrationen wird dieser Name mit dem Ausführungsverlauf verglichen. Es wird nicht geprüft, ob der PHP-Inhalt oder der Tabellenname übereinstimmt.

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

Dies sind zwei unterschiedliche Migrationsnamen. Auch wenn die erste ausgeführt wurde, gilt die zweite allein aufgrund dieses Verlaufs nicht als ausgeführt.

<Warning>
  Führen Sie den Veröffentlichungsbefehl für die Erstinstallation nicht bei jedem Paket-Update bedingungslos erneut aus, und machen Sie `--force` nicht zum Standardablauf. Dadurch werden nicht nur Änderungen an bereits veröffentlichten Dateien überschrieben, sondern durch die Datumsänderung können auch doppelte Vorgänge hinzukommen.
</Warning>

## Das direkte Laden implementieren

Wenn die Migrationen im Paket unverändert ausgeführt werden sollen, registrieren Sie einen Suchpfad. Bei diesem Ansatz fügen Sie keine Veröffentlichung derselben Dateien hinzu.

```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()` ruft `path()` auf, sobald der Migrator aufgelöst wird. `Migrator::path()` entfernt doppelte Suchpfade, und `getMigrationFiles()` indiziert die gefundenen Dateien nach Migrationsnamen und sortiert sie in dieser Namensreihenfolge.

Aktualisieren Nutzer das Paket, werden neue Dateien beim nächsten `migrate` berücksichtigt. Benennen Sie vorhandene Dateien nicht um, sondern fügen Sie für neue Schemaänderungen neue Dateien hinzu. Um Konflikte mit anderen Paketen zu vermeiden, nehmen Sie auch den Funktionsnamen auf, etwa `create_courier_deliveries_table`. Dateien mit gleichem Namen erhalten denselben Schlüssel und werden nicht beide unabhängig voneinander ausgeführt.

<Warning>
  Der Wechsel vom Veröffentlichen zum direkten Laden ist kein bloßes Umschreiben des Providers. Wurde der Ausführungsverlauf der Nutzer unter dem Namen zum Zeitpunkt des Veröffentlichens gespeichert, stimmt er nicht mit dem ursprünglichen Namen im Paket überein. Sie benötigen eine Migrationsanleitung, die den Verlauf bestehender Nutzer, die veröffentlichten Dateien und Rollbacks berücksichtigt.
</Warning>

## Schemaänderungen an bestehende Nutzer ausliefern

Wenn Sie beispielsweise der Liefertabelle eine Sendungsnummer hinzufügen, bearbeiten Sie nicht die bereits veröffentlichte `create_courier_deliveries_table`, sondern fügen eine neue Datei für die Änderung hinzu. Wenn Sie die bestehende Erstellungsmigration bearbeiten, wird die Änderung bei Nutzern, die sie bereits ausgeführt haben, nicht ausgeführt.

```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');
        });
    }
};
```

Da es sich um ein Beispiel für eine Tabelle mit vorhandenen Zeilen handelt, ist die Spalte hier nullable. Soll sie zur Pflichtspalte werden oder müssen Daten nachträglich befüllt werden, planen Sie diese Schritte und ihre Ausführungsreihenfolge gesondert.

Beim Veröffentlichen stellen Sie einen Update-Ablauf bereit, der mit den bereits veröffentlichten Dateien der Nutzer abgeglichen wird und **nur die neu hinzugefügten Dateien** ausliefert. Sie können auch einen eigenen Veröffentlichungs-Tag nur für neue Dateien einrichten, doch bei aktivierter Datumsaktualisierung gilt für das wiederholte Ausführen dieses Tags dieselbe Vorsicht. Machen Sie das bloße erneute Ausführen des Tags für die Erstinstallation nicht zum Update-Ablauf.

Beim direkten Laden kann der aktualisierte Code neue Dateien erkennen. Bei beiden Ansätzen ändert sich die Datenbank nicht allein dadurch, dass eine Datei vorhanden ist. Weisen Sie daher in den Release Notes ausdrücklich darauf hin, dass Migrationen ausgeführt werden müssen.

## Vor dem Release prüfen

Prüfen Sie zusätzlich zu den Datenbanktests des Pakets die Abläufe für Veröffentlichung und Update in einer nutzenden Anwendung. Wenn Sie in Tests die Migrationen nur direkt laden, haben Sie das Veröffentlichen, bei dem sich Dateinamen ändern, nicht überprüft.

* Bei einer Erstinstallation auf einer leeren Datenbank lassen sich die benötigten Tabellen erstellen.
* Bei einem Update ausgehend von Datenbank und Ausführungsverlauf eines älteren Releases werden nur die neuen Änderungen angewendet.
* Die Dateiliste nach wiederholtem Ausführen desselben Veröffentlichungsbefehls ist geprüft, und der Update-Ablauf erzeugt keine Duplikate.
* Der Ablauf berücksichtigt aktivierte und deaktivierte Datumsaktualisierung sowie Änderungen an bereits veröffentlichten Dateien.
* Das Rollback neuer Migrationen und die Ausführungsreihenfolge im Verhältnis zu den anderen Migrationen der Anwendung sind geprüft.

## Verwandte Seiten

<Columns cols={2}>
  <Card title="Migrationen" icon="database" href="/de/migrations">
    Grundlagen zu Schemadefinition, Ausführungsverlauf und Rollback.
  </Card>

  <Card title="Pakete testen" icon="flask" href="/de/advanced/package-testing">
    Service Provider und Datenbank eines Pakets testen.
  </Card>

  <Card title="Zusammenführen und Cachen von Paketkonfigurationen" icon="sliders" href="/de/advanced/package-config-merging">
    Update-Abläufe unter Berücksichtigung der Nutzerkonfiguration und des Konfigurations-Caches.
  </Card>

  <Card title="Verwaltung der Versionskompatibilität" icon="code-branch" href="/de/advanced/package-versioning">
    Update-Abläufe und Kompatibilitätsänderungen mit der Release-Strategie verknüpfen.
  </Card>
</Columns>

## Verwendete Primärquellen

* [Offizielle Laravel-Dokumentation: Paket-Migrationen](https://github.com/laravel/docs/blob/156fc7fde114548640e13c39aa79b991291c3f91/packages.md#migrations)
* [ServiceProvider: Registrierung von Veröffentlichungszielen und Suchpfaden](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Support/ServiceProvider.php)
* [VendorPublishCommand: Kopieren und Ändern von Zeitstempeln](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/VendorPublishCommand.php)
* [Migrator: Erkennen von Dateien und Prüfung auf nicht ausgeführte Migrationen](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Database/Migrations/Migrator.php)
* [Standardanwendung von Laravel 13: Datenbankkonfiguration](https://github.com/laravel/laravel/blob/06d016a364a37430eec9cbc52209adce3d7667ce/config/database.php)


## Related topics

- [Laravel-Paketentwicklung](/de/advanced/package-development.md)
- [Weiterführende Themen](/de/advanced/index.md)
- [Laravel Pennant](/de/pennant.md)
- [Upgrade von Laravel 10 auf 11](/de/blog/upgrade-10-to-11.md)
- [Laravel AI SDK](/de/ai-sdk.md)


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