Categories: Laravel

Laravel Başlangıç Rehberi – 8

Selam,

Bugün veritabanı üzerinde çalışıyorum. Konularım:

  • Migrasyon
  • SchemaBuilder
  • Veri Ekleme (Seeding)
  • Model
  • Eloquent
  • İlişkisel veritabanı tasarımları ve Laravel

Veritabanı üzerinde çalışacağımız için halihazırda bir veritabanımıza bağlantımızın olduğunu varsayıyorum ve vakit kaybetmeden migrasyon (migration) ile başlıyorum.

Migrasyon

Migrasyonlar veritabanı için bir sürüm kontrol türüdür. Bir ekibin veritabanı şemasını değiştirmesine ve son şema durumuna güncel kalmalarına imkan verir.

Migrasyon oluşturmak için komut satırında “Artistan“ı kullanmamız gerecek. Migrasyon dosyaları app/database/migrations altında toplanıyor. Komut satırımı açıp Laravel’i kurduğum dizine geliyorum ve şu kodu yazıyorum:

php artisan migrate:make tarifler_tablosu_olustur

Komutu çalıştırdığımız zaman app/database/migrations altında bizim için bir dosya oluşturulduğunu görebiliriz. Bu dosyanın içi hemen hemen şöyle bir şey:

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

class TariflerTablosuOlustur extends Migration {

	/**
	 * Run the migrations.
	 *
	 * @return void
	 */
	public function up()
	{
		//
	}

	/**
	 * Reverse the migrations.
	 *
	 * @return void
	 */
	public function down()
	{
		//
	}

}

up() metodunda veritabanı üzerinde çalıştırmak istediğimiz kodları yazacağız. down() metodu ise up() metodunda yaptığımız işlemleri geri almak için. Yani up() metodundaki sorguların tersini down() metoduna yazabiliriz.

up() metodu içinde tarifler tablomu oluşturmak için gerekli kodları yazıyorum:

public function up()
	{
		DB::statement("
			CREATE TABLE tarifler (
				id INTEGER PRIMARY KEY AUTO_INCREMENT,
				name VARCHAR(255) UNIQUE NOT NULL,
				text TEXT NOT NULL
			)
		");
	}

down() metodu içine ise bu kodun tersini yapacak kodlarımı yazıyorum. Yani tarifler tablosunu silmek için:

public function down()
	{
		DB::statement("
			DROP TABLE tarifler
		");
	}

Şimdi bu migrasyon dosyamızı çalıştıralım. Bunun için komut satırına şu kodu yazıyoruz:

php artisan migrate

Böylece veritabanında tarifler tablosunu oluşturduk. Ayrıca veritabanımıza "migrations" adında bir tablo daha ekleniyor. Bu tabloya şimdilik dokunmamız daha iyi.

Eğer son sorgumuzu geri almak istersek, yani tarifler tablosunu silmek için, komut satırına şu kodu yazmamız yeterli:

php artisan migrate:rollback

Bazen bu tür geri alma işlemlerinde hatayla karşılaşabilirsiniz. Böyle bir durumda aşağıdaki komutu çalıştırıp yeniden denemelisiniz:

php composer dump-autoload

SchemaBuilder

Yukarıda migrasyon ile çalışırken SQL kodlarımızı uzunca yazdık. Bu pek kullanıcı dostu olan bir işlem değil açıkcası. Laravel’in SchemaBuilder‘ı ile pratik bir şekilde bu işlemleri yapabiliriz. Ayrıca veritabanı tiplerine göre sorgularımızı düzenlememize gerek kalmaz.

Bu örnekte kitaplar tablosu üzerinden gideceğiz. Yeni bir migrasyon ile başlıyorum:

php artisan migrate:make kitaplar_tablosu_olustur

Komut satırına kodu yazdıktan sonra app/database/migrations altında oluşturulan dosyamı düzenlemeye başlıyorum. Yeni bir tablo oluşturacağım için up() metodu içinde Schema classını kullanarak şu kodları yazmam gerek:

public function up()
	{
		Schema::create("kitaplar", function($table)
		{
			$table->increments("id");
			$table->string("baslik");
			$table->string("yazar");
			$table->string("ozet")->nullable();
		});
	}

Yeni bir tablo için yapmamız gereken Schema::create() metodunu kullanmak. İlk parametre olarak tablo adını, 2. parametre olarak anonim bir fonksiyon gönderiyoruz. Bu fonksiyon $table parametresi alıyor ve bu $table ı kullanarak tablomuzun sütunlarını oluşturuyoruz. increments("id") ile id adında, otomatik artan, primary key özelliğinde bir sütun oluşturduk. Tüm bu fonksiyonların listesi dökümantasyonda mevcut.

Eğer oluşturduğumuz tabloyu silmek istersek down() metoduna şu kodu eklememiz yeterli:

public function down()
	{
		Schema::drop("kitaplar");
	}

Var olan tablomuza yeni bir alan eklemek isteyebiliriz. kitaplar tablomuz için "tur" alanı ekleyelim. Önce yeni bir migrasyon dosyası oluşturmakla başlıyorum:

php artisan migrate:make tur_alani_ekle

app/database/migrations altında yeni oluşturulan dosyamda up() metodunu düzenliyorum:

public function up()
	{
		Schema::table("kitaplar", function($table)
		{
			$table->string("tur");
		});
	}

down() metodunda ise bu işlemi geriye alacak kodlarım:

public function down()
	{
		Schema::table("kitaplar", function($table)
		{
			$table->dropColumn("tur");
		});
	}

Aynı anda birden fazla alanı silmek istersek dropColumn() metoduna dizi içinde sütunların adını göndermemiz yeterli:

public function down()
	{
		Schema::table("kitaplar", function($table)
		{
			$table->dropColumn( array("tur", "ozet", "yazar"));
		});
	}

Veri Ekleme (Seeding)

Veri Ekme (seeding), migrasyon ile oluşturulacak veritabanı tablosunda gerekli olacak ilk veri kayıtlarının (seed data) oluşturulması işlemidir.

Veritabanımız için oluşturacağımız deneme verilerini  app/database/seeds içinde ekleyebiliriz. Hazırda DatabaseSeeder.php isminde bir dosya bizim için bekliyor. Şimdi DatabaseSeeder sınıfını kullanarak öylesine veri eklemeyi öğreneceğim.

DatabaseSeeder.php içinde kendime bir sınıf oluşturup, bu sınıf içinde run() metodu hazırlacağım. Hazırladığımız sınıflar Seeder sınıfını extend etmeli. Şimdi kitaplar tablomu örnek verilerle doldurmak için gerekli kodları yazıyorum:

class KitaplarTablosunaVeriEkle extends Seeder {
	public function run(){
		for($i=0; $i<100; $i++) {
			DB::table("books")->insert( array(
				"baslik" => "Kitap ".$i,
				"yazar" => "Yazar ".$i,
				"ozet" => "Kitap ".$i."e ait özet...",
				"tur" => "Tur ".$i
			));
		}
	}
}

Hazırladığım bu örnek verileri eklemek için DatabaseSeeder sınıfındaki run() metodunu düzenlemem lazım. run() metodu içinde call() metoduna parametre olarak  hazırladığım sınıfın adını göndereceğim: "KitaplarTablosunaVeriEkle"

class DatabaseSeeder extends Seeder {

	/**
	 * Run the database seeds.
	 *
	 * @return void
	 */
	public function run()
	{
		Eloquent::unguard();

		$this->call('KitaplarTablosunaVeriEkle');
	}

}

Son haliyle DatabaseSeeder.php dosyam şu şekilde:

class DatabaseSeeder extends Seeder {
	public function run()
	{
		Eloquent::unguard();

		$this->call('KitaplarTablosunaVeriEkle');
	}

}

class KitaplarTablosunaVeriEkle extends Seeder {
	public function run(){
		for($i=0; $i<100; $i++) {
			DB::table("kitaplar")->insert( array(
				"baslik" => "Kitap ".$i,
				"yazar" => "Yazar ".$i,
				"ozet" => "Kitap ".$i."e ait özet...",
				"tur" => "Tur ".$i
			));
		}
	}
}

Son olarak komut satırından verilerin eklenmesi için komutu vermem gerek:

php artisan db:seed

Böylelikle kitaplar tablomuza 100 adet veri eklenmiş oldu. Yalnız bu veriler pek gerçekçi değil. Daha gerçekçi veriler eklemek istersek “Faker” isimli php sınıfını kullanabiliriz. Bunu sonraki derslerde öğreneceğim. Şimdilik Seeder‘ın mantığını kavramaya çalıştığım için bu kadarıyla yetiniyorum.

Modeller

Üzerinde çalışmak için makaleler adında örnek bir tablo oluşturarak başlıyorum. Önce komut satırından migrasyon komutunu yazıyorum:

php artisan migrate:make makaleler_tablosu_olustur

app/database/migrations altında oluşan dosyamda Schema sınıfını kullanarak tablomun alanlarını belirtiyorum:

class MakalelerTablosuOlustur extends Migration {
	public function up()
	{
		Schema::create("makaleler", function($table) {
			$table->increments("id");
			$table->string("baslik")->unique();
			$table->text("icerik");
		});
	}

	public function down()
	{
		Schema::drop("makaleler");
	}

}

Ve migrasyonu çalıştırmak için tekrar komut satırından gerekli kodu yazıyorum:

php artisan migrate

Böylelikle makaleler tablom hazırlanmış oldu. Bu tablom üzerinde model dosyalarını kullanarak çalışacağım.

Model dosyaları app/models altında toplanıyor. app/models altında User.php adında hazır bir model dosyası zaten mevcut. Ben kendime ait  Makale.php isminde yeni bir model dosyası oluşturuyorum. Bu model dosyamda makaleler tablosuna ait veritabanı işlemlerini gerçekleştireceğim.

Eloquent

MVC yapısının temelini öğrendiğim derste, her model dosyasının veritabanımızdaki tablolarla ilişkilendirdiğimizi öğrenmiştim. Makale.php model dosyam ile makaleler tablosu arasında bir bağ olursa, Makale.php model dosyam üzerinden makaleler tablosuna rahatça erişebilir ve müdahale edebilirim. Bunun için Laravel’in Eloquenti kullanacağım. Eloquent hakkında detaylı bilgi dökümantasyonda mevcut.

Model dosyalarımız üzerinde Eloquent ile çalışmak için Eloquent’i kalıtım yapmamız lazım:

class Makale extends Eloquent{

}

Burada bilmemiz gereken önemli bir nokta var. Eloquent kalıtım yapıldığı sınıfın veritabanında var olan bir tablo olduğunu düşünüyor. Ama varsayılan olarak bu tablonun adını, sınıfın adının çoğulu olarak düşünüyor. Tabii ki İngilizce dil bilgisi kurallarına göre. Bizim durumda Eloquent, veritabanında makales isminde bir tablo olduğunu varsayacaktır. Eloquent’e “kardeşim aslında o tablonun adı makales değil de makaleler olacak” demek istersek $table özelliğini değiştirmemiz gerek. Şu şekilde:

class Makale extends Eloquent{
    protected $table = "makaleler";
}

Ayrıca Eloquent tablolarımızda timestamps() alanlarının (created_at ve updated_at) olduğunu düşünüyor. Eloquent’e “kardeşim ben de o alanlar yok” demek için de $timestamps değerini false olarak belirtirebiliriz:

class Makale extends Eloquent{
    public $timestamps = false;
    protected $table = "makaleler";
}

Ya da yeni bir migrasyon ile tablonuza timestamps alanlarını ekleyebilirsiniz. Tercih sizin.

Veri Ekleme

Bunlar ilk olarak aklımızda tutmamız gereken noktalardı. Şimdi gelelim makaleler tablomuza nasıl veri ekleyeceğimize. routes.php dosyam üzerinde bunu gerçekleştireceğim:

Route::get('/', function()
{
	$makale = Makale::create( array(
		"baslik" => "Ahirette seni kurtaracak...",
		"icerik" => "Âhirette seni kurtaracak bir eserin olmadığı takdirde, fâni dünyada bıraktığın eserlere de kıymet verme"
	));
	return $makale->id;
});

Oluşturduğumuz Makale sınıfı Eloquent sınıfını kalıtım yaptığı için onun fonksiyonlarını kullanabiliyor. create() ile veritabanımıza yeni veriler ekliyoruz ve bu metod geriye bir nesne döndürüyor. Bu nesneyi $makale değişkeni ile alıyorum. Böylece özelliklerini yani tablomdaki alanları kullanabilirim. Bu örnekte veri kaydı gerçekleştikten sonra geriye “1” değeri dönmeli çünkü makaleler tablomuza ilk kez veri girişi yaptık.

Yalnız burada bilmemiz gereken önemli bir nokta daha var. create() metodu içinde birden fazla alana veri eklemek istersem, varsayılan olarak Eloquent buna izin vermeyecektir. Bunun için Makale.php içinde $fillable özelliğine dizi olarak aynı anda verileri girebileceğim alanları belirtmem gerek:

class Makale extends Eloquent{
    public $timestamps = false;
    protected $table = "makaleler";
    protected $fillable = array("baslik", "icerik");
}

Böylece makaleler tablomdaki baslik ve icerik alanlarına aynı anda veri kaydı yapılabilir.

Veri eklemenin diğer bir yolu Makale sınıfımızdan nesneler türetip bunlar üzerinden veri kaydı gerçekleştirmektir. Bu durumda $fillable özelliğini düzenlememize de gerek kalmaz.

$baskaMakale = new Makale();
	$baskaMakale->baslik = "En büyük kusur";
	$baskaMakale->icerik = "kusurunu görmemek, o kusurdan daha büyük bir kusurdur.";
	$baskaMakale->save();
	return $baskaMakale->id;

Verileri Listeleme

Tablomuzdaki tüm verileri çekmek için all() metodunu kullanabiliriz:

$makaleler = Makale::all();
	foreach ($makaleler as $makale) {
		echo $makale->baslik."<br/>";
	}

WHERE koşulu ile kullanmak istersek:

$makaleler = Makale::where("id", ">", "1")->get();

Sadece belirli bir veriyi getirmek istersek:

$makaleler = Makale::find(2);
	echo $makaleler->icerik;

Böylece "id" si “2” olan makaleyi çektik ve "icerik" alanını geriye döndürdük.

Güncelleme

Tek bir veriyi güncellemek istersek aşağıdaki yöntemi kullanabiliriz:

$makale = Makale::find(1);
	$makale->baslik = "Güncellenmiş başlık";
	$makale->save();

Çok sayıda veriyi aynı anda güncellemek istersek update() metoduna dizi göndermemiz gerek:

$makale = Makale::where("id", "<", "2");
	$makale -> update( array(
		"baslik" => "Yeni güncel başlıklar"
	));

Böylelikle "id" alanı “2” den küçük olan verilerin "baslik" alanlarını güncellemiş olduk.

Silme

Article::destroy(2);

"id" si 2 olan verimizi destroy() metodu ile sildik. Çok sayıda veri silmek için destroy() metoduna dizi gönderebiliriz ya da where() metodunu kullanabiliriz:

$silinecekler = Article::where("id", "<", "2");
	$silinecekler->delete();

İlişkisel Veritabanı ve Laravel

Gerçek hayatta web uygulamalarının veritabanları tasarlanırken genelde ilişkisel veritabanı tasarımı kullanılır. Bunu zaten biliyorsunuz. Bizim örneğimizde makaleler ve yorumlar tablosu olacak. Bir makaleye ait birçok yorum olabilir.

makaleler tablom yukarıda kullandığım örneklerdekinin aynısı olacak. İhtiyacımız olan yorumlar tablosu. Komut satırına migrasyon kodumu yazıyorum:

php artisan migrate:make yorumlar_tablosu_olustur

app/database/migrations altında hazırlanan dosyama yorumlar tablosunu oluşturmak ve silmek için kodlarımı yazıyorum:

class YorumlarTablosuOlustur extends Migration {

	public function up()
	{
		Schema::create("yorumlar", function($table){
			$table->increments("id");
			$table->text("yorum");
			$table->integer("makale_id")->unsigned();
			$table->timestamps();

			$table->foreign("makale_id")->references("id")->on("makaleler");
		});
	}
	
	public function down()
	{
		Schema::drop("yorumlar");
	}

}

Burada dikkat etmemiz gerekenler 8. ve 11. satır.

8. satırda unsigned() metodunu da kullandık. Otomatik artan bir tam sayıya başvuran bir foreign key oluşturulurken, foreign key sütunu her zaman için unsigned olmalı.*

11. satırda ise, makale_id sütununun makaleler tablosundaki id sütununu referans ettiğini belirtiyoruz.

NOT: MySQL de foreign key oluşturmak için veritabanı motoru InnoDB olmalı. Tabloyu oluştururken varsayılan olarak veritabanı motorunuz InnoDB değilse $table->engine = "InnoDB"; ile veritabanı motorunu InnoDB yapabilirsiniz.

Şimdi komut satırından oluşturduğum migrasyon dosyamı çalıştırıyorum:

php artisan migrate

yorumlar tablomuz da hazır olduğuna göre, model dosyamı oluşturup tablo ile bağlantısını kurabilirim. app/models altında Yorum.php isimli bir dosya oluşturup düzenlemelerimi yapıyorum:

class Yorum extends Eloquent
{
    protected $table = "yorumlar";
    protected $fillable = array("yorum", "makale_id");
}

3. satırda Eloquent için tablomun varsayılan adı olan “yorums“u değiştirerek “yorumlar” yaptım.

4.satırda ise yorum ve makale_id alanlarına aynı anda veri giribileceğini belirttim.

Konunun başında bir makaleye ait birçok yorum olabileceğini belirtmiştim. Şimdi aynı şeyi Eloquent’e de bildirmek için Makale model dosyamı açıyorum ve düzenlememi yapıyorum:

public function yorum() {
        return $this->hasMany("Yorum");
    }

yorum() isminde bir metod oluşturdum ve bu metodun içinde hasMany() metodunu kullandım. hasMany() metoduna Yorum model dosyamın adını yazdım çünkü makaleler tablosu yorumlar tablosuyla ilişkili. Ve yorumlar tablosunu da Yorum model dosyamla ilişkilendirmiştim.

Aynı şekilde Yorum model dosyamda da ilişkiyi bildirmem lazım. Ama bir yorum bir makaleye ait olabilir. Bunun için Yorum model dosyamda makale() isminde bir metod oluşturup belongsTo() metodunu kullanacağım. belongsTo() metoduna da Makale model dosyamın adını parametre olarak gireceğim çünkü makaleler tablosu Makale model dosyasıyla bağlantılı.

public function makale(){
        return $this->belongsTo("Makale");
    }

Son halleriyle Makale.php ve Yorum.php dosyamı şu şekilde oldu:

Makale.php

class Makale extends Eloquent{

    public function yorum() {
        return $this->hasMany("Yorum");
    }

    public $timestamps = false;
    protected $table = "makaleler";
    protected $fillable = array("baslik", "icerik");

}

Yorum.php:

class Yorum extends Eloquent
{
    public function makale(){
        return $this->belongsTo("Makale");
    }

    protected $table = "yorumlar";
    protected $fillable = array("yorum", "makale_id");
}

Şimdi bu yazdığımız kodlar ile belirli bir makaleye ait yorumları nasıl kolaylıkça çekeceğimizi görelim. routes.php dosyamı açıp şu kodu yazıyorum:

Route::get('/', function()
{
$yorumlar = Makale::find(1)->yorum()->get();
	foreach ($yorumlar as $yorum) {
		echo $yorum->yorum."<br/>";
	}
});

Böylelikle “id”si 1 olan makaleye ait tüm yorumları listelemiş olduk. Hatta yukarıdaki kodu biraz daha kısaltabiliriz:

Route::get('/', function()
{
	$yorumlar = Makale::find(1)->yorum;
	foreach ($yorumlar as $yorum) {
		echo $yorum->yorum."<br/>";
	}
});

Ve tabii ki bu işlemin tersini de yapabiliriz. Yani belirli bir yorumun hangi makale altında yapıldığını bulabiliriz:

$yorum = Yorum::find(3)->makale;
	return  $yorum->id;

“id” si 3 olan yorum hangi makaleye aitse o makalenin “id” sini geriye döndürdük.

Bu şekilde veritabanı üzerinde farklı ilişkilendirmeleri de düzenleyebiliriz. Makaleler ve yazarlar gibi. Bir makale bir yazara ait olabilir ama bir yazarın birçok makalesi olabilir. Bugün öğrendiğim bu tarz ilişkileri düzenlemekti. Yani birden çoğa (1-n) ilişkiler. Ama bildiğiniz gibi veritabanı ilişkileri birden bire ya da çoktan çoğa olabilir. Laravel ve veritabanı ilişkileri konusunda daha fazla bilgi için her zaman ki gibi dökümantasyona başvurabilirsiniz.

 Sırada Ne Var?

Laravel’in temel yapısı üzerinde epey yol katettim. Özellikle bugünkü konuyla beraber MVC’nin altyapısını tamamen atmış oldum. Sırada çeşitli konulardan oluşan karma bir bölüm var:

  • Filtreleme (Filters)
  • Geçerlilik denetimi (Validation)
  • Artisan dosyası ve 404 durumları
  • Generator
  • Faker (Veritabanını daha gerçekçi örnek verilerle doldurmak için)

Hatırlatma1: Laravel öğrenmek için bu linkteki eğitimi takip ediyorum ve öğrendiklerimi burada paylaşıyorum.Buraya tıklayarak eğitimin anasayfasına ulaşabilir ve kaynak kodlarını indirebilirsiniz.

Hatırlatma2: Lütfen eksik,hatalı ya da düzeltilmesi gereken bir şey farkederseniz bana bildirin.

Recent Posts

Celery Checklist [Python]

Celery ile alakalı "best practice"leri ve faydalı araçları bir araya getiren güzel bir checklist'e denk…

5 yıl ago

JavaScript Dizileri için 13 İpucu

Diziler en temel ve sık kullandığımız araçlardan... Kod yazarken işimizi kolaylaştıracak, daha temiz kod yazmamızı…

5 yıl ago

List & Tuple Mini Test (Python)

listve tuple bilginizi test etmek ister misiniz? realpython.com da keşfettiğim ve Türkçe'ye çevirdiğim mini teste…

5 yıl ago

Bilmeniz Gereken 11 Python Mülakat Sorusu

Rehberlik sağlaması ve bilgi tazelemesi açısından faydalı olduğunu düşündüğüm bir Toptal blog paylaşımınıTürkçe'ye çevirdim.Devamını okuyunBilmeniz…

5 yıl ago

Angular Componentlere Konsol Üzerinden Hızlı Erişim

Angular componentlerine console üzerinden hızlıca erişmek için kullanılan bir teknik. Unutmamak için kendime not düşüyorum.Devamını…

5 yıl ago

Birkaç Güzel JavaScript Sorusu İster Miydiniz?

Geçtiğimiz günlerde keşfettiğim ve oldukça da hoşuma giden repoyu paylaşmak istiyorum: lydiahallie/javascript-questions Genel olarak temel…

5 yıl ago