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

Laravel Migration ve Seeder: Şema ve Test Verisi

Bir Laravel migration, veritabanı şemanı tıpkı koduna olduğu gibi versiyonlamanın yoludur. Tabloları SQL panelinden elle açmak yerine, her şema değişikliğini PHP ile yazılmış bir sınıfta tarif edersin; bu sınıf ekibindeki herkesin ve sunucunun aynı yapıyı tek komutla kurmasını sağlar. Seeder ise bu şemaya gerçekçi test verisi doldurmanın resmi yoludur. İkisini birlikte kullanınca, projeyi sıfırdan ayağa kaldırmak tek satıra iner ve "bende çalışıyordu" sorunlarının büyük kısmı ortadan kalkar.

Migration nedir, neden elle SQL'den iyidir?

Şemayı elle yazmanın iki büyük sorunu var: değişiklik geçmişi tutulmaz ve herkesin veritabanı farklı sonuçlanır. Migration bu ikisini de çözer. Her dosya timestamp ile adlandırılır, böylece sıra nettir; up() metodu değişikliği uygular, down() metodu ise geri alır. Migration'lar Git'e girdiği için şema geçmişin commit geçmişinle birlikte ilerler.

  • Tekrarlanabilir: aynı komut her makinede aynı yapıyı kurar.
  • Geri alınabilir: hatalı bir değişikliği down() ile geri sarabilirsin.
  • Takip edilebilir: migrations tablosu hangi dosyanın çalıştığını kaydeder, ikinci kez çalışmaz.

İlk migration'ını oluşturmak

Artisan komutu hem dosyayı hem iskeletini üretir. Yeni bir tablo için --create, var olan tabloyu değiştirmek için --table bayrağını kullan:

php artisan make:migration create_posts_table

# Var olan tabloya kolon eklemek için:
php artisan make:migration add_status_to_posts_table --table=posts

Üretilen dosya database/migrations/ altına düşer. create_posts_table içeriği şöyle görünür:

<?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() otomatik artan birincil anahtar, timestamps() ise created_at ve updated_at kolonlarını ekler. Çoğu sütun tipi için akıcı bir API vardır: string, text, integer, boolean, json, decimal ve daha fazlası.

Migration'ları çalıştırmak ve geri almak

Bekleyen tüm migration'ları uygulamak için:

php artisan migrate

Geliştirme sırasında en çok işine yarayacak komutlar şunlardır:

  • php artisan migrate:rollback — son grup migration'ı geri alır.
  • php artisan migrate:rollback --step=2 — son iki adımı geri sarar.
  • php artisan migrate:fresh — tüm tabloları düşürüp baştan kurar.
  • php artisan migrate:refresh — her şeyi geri alıp yeniden çalıştırır.
  • php artisan migrate:status — hangi migration'ların çalıştığını listeler.

Üretim ortamında dikkat: migrate:fresh ve refresh verileri siler. Canlı sunucuda sadece php artisan migrate --force kullan; --force bayrağı production'da onay sormadan çalışmasını sağlar.

İlişkiler ve indeksler

Tablolar arası ilişkileri yabancı anahtarla kurarsın. Laravel bunun için kısa bir yardımcı sunar:

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

foreignId('post_id')->constrained() otomatik olarak posts tablosunun id kolonuna referans veren bir yabancı anahtar oluşturur. cascadeOnDelete() ise bir gönderi silindiğinde ona bağlı yorumların da silinmesini sağlar. Sık sorgulanan kolonlara $table->index('slug') ile indeks eklemeyi unutma; bu, büyük tablolarda okuma hızını ciddi biçimde artırır.

Seeder ile test verisi üretmek

Şema hazır olunca, onu doldurmak için seeder yazarsın. Seeder sınıfını da Artisan üretir:

php artisan make:seeder PostSeeder

Bu sınıf database/seeders/ altına gelir ve run() metodu içinde kayıt ekler:

<?php

namespace Database\Seeders;

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

class PostSeeder extends Seeder
{
    public function run(): void
    {
        Post::create([
            'title'     => 'Merhaba dünya',
            'slug'      => 'merhaba-dunya',
            'body'      => 'İlk yazının gövdesi.',
            'published' => true,
        ]);
    }
}

Bir seeder'ı tek başına çalıştırmak için sınıf adını verirsin:

php artisan db:seed --class=PostSeeder

Tüm seeder'ları zincirlemek için DatabaseSeeder içinde call() kullanırsın; php artisan db:seed bu ana sınıfı çalıştırır:

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

Factory ile gerçekçi ve toplu veri

Birkaç satırı elle yazmak küçük projeler için yeterli olsa da, gerçek testler yüzlerce kayıt ister. Burada factory devreye girer: tek bir kaydın nasıl üretileceğini tarif eder, gerisini Faker kütüphanesi halleder.

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),
    ];
}

Ardından seeder içinde tek satırla 50 gerçekçi gönderi üretirsin:

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

İlişkili veriyi de factory'ler üzerinden zincirleyebilirsin: Post::factory()->count(10)->hasComments(5)->create() her gönderi için beş yorum oluşturur. Bu yaklaşım, demoları ve otomatik testleri tutarlı ama çeşitli verilerle besler.

Pratik iş akışı ve sık yapılan hatalar

Günlük geliştirmede en temiz başlangıç, şema ve veriyi tek komutta sıfırlamaktır:

php artisan migrate:fresh --seed

Birkaç pratik kural işini kolaylaştırır:

  • Bir migration kez çalışıp commit'lendikten sonra onu düzenleme — yeni bir migration yaz. Çünkü düzenleme yalnızca henüz çalıştırmamış makinelerde etkili olur.
  • down() metodunu her zaman up()'ın tam tersi olacak şekilde doldur; geri almanın çalıştığını gerçekten test et.
  • Seeder'larda updateOrCreate kullanmak, aynı seeder'ı ikinci kez çalıştırdığında çift kayıt oluşmasını önler.
  • Üretim verisini asla seeder'da hardcode etme; sabit referans verisi (ülkeler, roller gibi) dışında seeder'lar test/geliştirme içindir.

Sık Sorulan Sorular

Migration ile seeder arasındaki fark nedir?

Migration veritabanının yapısını (tablolar, kolonlar, indeksler, ilişkiler) tanımlar ve versiyonlar. Seeder ise bu yapının içine veri ekler. Biri iskeleti kurar, diğeri onu test/örnek kayıtlarla doldurur. İkisi farklı amaçlara hizmet eder ama birlikte çalışır.

Canlı sunucuda migration çalıştırmak veriyi siler mi?

php artisan migrate yalnızca bekleyen yeni migration'ları uygular ve mevcut veriye dokunmaz. Veriyi silen komutlar migrate:fresh ve migrate:refresh'tir; bunları üretimde kullanma. Canlıda her zaman migrate --force tercih et.

Bir kolonu sonradan eklemek için ne yapmalıyım?

Eski migration'ı düzenleme. make:migration add_x_to_y_table --table=y ile yeni bir migration oluştur, up() içinde $table->string('x')->nullable() gibi kolonu ekle, down() içinde $table->dropColumn('x') ile geri al ve php artisan migrate çalıştır.

Projende temiz ve test edilebilir bir veritabanı katmanı mı kurmak istiyorsun? Laravel migration, seeder ve factory yapısını projenin ihtiyacına göre kurgulayıp uygularım. Detayları konuşmak için benimle iletişime geç.

Devamı için