aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Webentwicklung

Laravel Migration und Seeder: Schema und Testdaten

Eine Laravel-Migration ist die Art und Weise, dein Datenbankschema genauso zu versionieren, wie du deinen Code versionierst. Statt Tabellen von Hand in einem SQL-Client anzulegen, beschreibst du jede Schemaänderung in einer PHP-Klasse; diese Klasse erlaubt es jedem in deinem Team und deinem Server, dieselbe Struktur mit einem einzigen Befehl aufzubauen. Ein Seeder wiederum ist der offizielle Weg, dieses Schema mit realistischen Testdaten zu füllen. Setzt du beide zusammen ein, schrumpft das Aufsetzen des Projekts von Grund auf auf eine Zeile, und die meisten „Bei mir lief es doch"-Probleme verschwinden einfach.

Was eine Migration ist und warum sie handgeschriebenem SQL überlegen ist

Das Schema von Hand zu schreiben hat zwei große Probleme: Es wird keine Änderungshistorie geführt, und jedermanns Datenbank fällt am Ende leicht unterschiedlich aus. Migrations lösen beides. Jede Datei wird mit einem timestamp benannt, sodass die Reihenfolge eindeutig ist; die Methode up() wendet die Änderung an, die Methode down() macht sie rückgängig. Da Migrations in Git liegen, schreitet deine Schemahistorie gemeinsam mit deiner Commit-Historie voran.

  • Wiederholbar: derselbe Befehl baut auf jeder Maschine dieselbe Struktur auf.
  • Umkehrbar: eine fehlerhafte Änderung kannst du mit down() zurückrollen.
  • Nachvollziehbar: die Tabelle migrations hält fest, welche Datei lief, sodass sie nie zweimal ausgeführt wird.

Deine erste Migration erstellen

Der Artisan-Befehl erzeugt sowohl die Datei als auch ihr Grundgerüst. Nutze --create für eine brandneue Tabelle und --table, um eine bestehende zu ändern:

php artisan make:migration create_posts_table

# Um einer bestehenden Tabelle eine Spalte hinzuzufügen:
php artisan make:migration add_status_to_posts_table --table=posts

Die erzeugte Datei landet unter database/migrations/. Eine create_posts_table sieht so aus:

<?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::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('slug')->unique();
            $table->text('body');
            $table->boolean('published')->default(false);
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

$table->id() erstellt einen automatisch hochzählenden Primärschlüssel, und timestamps() fügt die Spalten created_at und updated_at hinzu. Für die meisten Spaltentypen gibt es eine fließende API: string, text, integer, boolean, json, decimal und viele mehr.

Migrations ausführen und zurückrollen

Um alle ausstehenden Migrations anzuwenden:

php artisan migrate

Das sind die Befehle, die du während der Entwicklung am häufigsten brauchst:

  • php artisan migrate:rollback — macht die letzte Charge Migrations rückgängig.
  • php artisan migrate:rollback --step=2 — spult die letzten zwei Schritte zurück.
  • php artisan migrate:fresh — löscht alle Tabellen und baut von Grund auf neu auf.
  • php artisan migrate:refresh — rollt alles zurück und führt es erneut aus.
  • php artisan migrate:status — listet auf, welche Migrations gelaufen sind.

Vorsicht in der Produktion: migrate:fresh und refresh zerstören Daten. Verwende auf einem Live-Server nur php artisan migrate --force; das Flag --force lässt sie in der Produktion ohne Bestätigungsabfrage laufen.

Beziehungen und Indizes

Tabellen verbindest du mit Fremdschlüsseln, und Laravel bietet dafür eine kurze Hilfsfunktion:

Schema::create('comments', function (Blueprint $table) {
    $table->id();
    $table->foreignId('post_id')
          ->constrained()
          ->cascadeOnDelete();
    $table->text('body');
    $table->timestamps();
});

foreignId('post_id')->constrained() erstellt automatisch einen Fremdschlüssel, der auf die Spalte id der Tabelle posts verweist. cascadeOnDelete() stellt sicher, dass beim Löschen eines Beitrags auch dessen Kommentare verschwinden. Vergiss nicht, häufig abgefragten Spalten mit $table->index('slug') einen Index hinzuzufügen; bei großen Tabellen verbessert das die Lesegeschwindigkeit erheblich.

Testdaten mit einem Seeder erzeugen

Sobald das Schema steht, schreibst du einen Seeder, um es zu füllen. Artisan erzeugt auch die Seeder-Klasse:

php artisan make:seeder PostSeeder

Die Klasse landet unter database/seeders/ und fügt Datensätze in ihrer run()-Methode ein:

<?php

namespace Database\Seeders;

use App\Models\Post;
use Illuminate\Database\Seeder;

class PostSeeder extends Seeder
{
    public function run(): void
    {
        Post::create([
            'title'     => 'Hallo Welt',
            'slug'      => 'hallo-welt',
            'body'      => 'Der Inhalt des ersten Beitrags.',
            'published' => true,
        ]);
    }
}

Um einen einzelnen Seeder auszuführen, übergibst du seinen Klassennamen:

php artisan db:seed --class=PostSeeder

Um alle Seeder zu verketten, nutzt du call() innerhalb von DatabaseSeeder; php artisan db:seed führt diese Hauptklasse aus:

public function run(): void
{
    $this->call([
        UserSeeder::class,
        PostSeeder::class,
        CommentSeeder::class,
    ]);
}

Realistische Massendaten mit Factories

Ein paar Zeilen von Hand zu schreiben reicht für kleine Projekte, aber echte Tests verlangen Hunderte Datensätze. Hier kommt die Factory ins Spiel: Sie beschreibt, wie ein einzelner Datensatz erzeugt wird, und überlässt den Rest der Faker-Bibliothek.

php artisan make:factory PostFactory --model=Post
public function definition(): array
{
    return [
        'title'     => fake()->sentence(),
        'slug'      => fake()->unique()->slug(),
        'body'      => fake()->paragraphs(3, true),
        'published' => fake()->boolean(80),
    ];
}

Anschließend erzeugt eine einzige Zeile im Seeder 50 realistische Beiträge:

Post::factory()->count(50)->create();

Du kannst auch verknüpfte Daten über Factories verketten: Post::factory()->count(10)->hasComments(5)->create() erstellt für jeden Beitrag fünf Kommentare. Dieser Ansatz versorgt Demos und automatisierte Tests mit konsistenten und zugleich abwechslungsreichen Daten.

Ein praktischer Arbeitsablauf und häufige Fehler

Im Alltag baut der sauberste Reset Schema und Daten in einem einzigen Befehl neu auf:

php artisan migrate:fresh --seed

Ein paar praktische Regeln ersparen dir Ärger:

  • Sobald eine Migration gelaufen und committet ist, bearbeite sie nicht mehr — schreibe eine neue. Eine Bearbeitung wirkt nur auf Maschinen, die sie noch nicht ausgeführt haben.
  • Fülle die down()-Methode stets so, dass sie das genaue Gegenteil von up() ist, und teste tatsächlich, ob das Zurückrollen funktioniert.
  • updateOrCreate in Seedern verhindert doppelte Datensätze, wenn du denselben Seeder ein zweites Mal ausführst.
  • Setze niemals Produktionsdaten fest in einen Seeder; abgesehen von festen Referenzdaten (Länder, Rollen) sind Seeder für Test und Entwicklung gedacht.

Häufige Fragen

Was ist der Unterschied zwischen einer Migration und einem Seeder?

Eine Migration definiert und versioniert die Struktur der Datenbank (Tabellen, Spalten, Indizes, Beziehungen). Ein Seeder fügt Daten in diese Struktur ein. Das eine baut das Gerüst, das andere füllt es mit Test- oder Beispieldatensätzen. Sie dienen verschiedenen Zwecken, arbeiten aber zusammen.

Löscht das Ausführen von Migrations auf einem Live-Server Daten?

php artisan migrate wendet nur ausstehende neue Migrations an und rührt bestehende Daten nie an. Die Befehle, die Daten zerstören, sind migrate:fresh und migrate:refresh; nutze sie nicht in der Produktion. Auf einem Live-Server bevorzuge immer migrate --force.

Wie füge ich später eine Spalte hinzu?

Bearbeite nicht die alte Migration. Erstelle eine neue mit make:migration add_x_to_y_table --table=y, füge die Spalte in up() mit etwa $table->string('x')->nullable() hinzu, mache sie in down() mit $table->dropColumn('x') rückgängig und führe dann php artisan migrate aus.

Möchtest du eine saubere, testbare Datenbankschicht in deinem Projekt? Ich entwerfe und implementiere Laravel-Migration-, Seeder- und Factory-Strukturen, zugeschnitten auf den Bedarf deines Projekts. Um die Details zu besprechen, nimm Kontakt mit mir auf.

Devamı için