+ Yüklə - Kitabsiz.az

Transkript

+ Yüklə - Kitabsiz.az
Laravel: Code Bright (TR) Türkçe Çevirisi
Yeni Başlayanlar İçin Laravel Framework Versiyon 4 İle
Web Uygulama Geliştirme
Dayle Rees ve Sinan Eldem
Bu kitap şu adreste satılmaktadır http://leanpub.com/codebright-tr
Bu versiyon şu tarihte yayımlandı 2013-12-24
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
©2013 Dayle Rees
Kitabı tweetleyin!
Dayle Rees ve Sinan Eldem’a kitabını şu adresten Twitter tanıtarak yardımcı olun!
Kitap için önerilen tweet:
Laravel: Code Bright kitabının Türkçe Çevirisi http://leanpub.com/codebright-tr #codebright-tr
#laravel @laraveltr
Kitap için önerilen hashtag #codebright-tr.
Kitap için diğerleri ne demiş merak ediyorsanız bağlantıya tıklayarak hashtagları arayabilirsiniz:
https://twitter.com/search?q =#codebright-tr
İçindekiler
Teşekkürler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
i
Yazım Hataları . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
iii
Geribildirim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
iv
Çeviriler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
v
Giriş . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
vi
Ön Bilgiler . . . . . . . . .
Aduzayları (Namespaces)
JSON . . . . . . . . . . .
Composer . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
7
13
Mimari . . . . . . . . . .
Container (Konteyner)
Facades . . . . . . . .
Esneklik (Flexibility) .
Sağlamlık (Robustness)
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
33
33
35
37
38
Başlarken . . . . . . . . . . .
Gereksinimler . . . . . . .
Yükleme . . . . . . . . . .
Web Server Yapılandırması
Proje Yapısı . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
39
39
40
42
46
Basit Rotalama . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Basit Rotalama . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Rota Parametreleri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
51
51
56
Cevaplar (Responses)
Görünümler . . . .
Görünüm Verisi . .
Redirekt . . . . . .
Özel Cevaplar . . .
59
60
61
63
64
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
İÇINDEKILER
Cevap Kısayolları . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Filtreler . . . . . . . .
Basit Filtreler . . .
Çoklu Filtreler . . .
Filtre Parametreleri
Filtre Sınıfları . . .
Evrensel Filtreler .
Default Filtreler . .
Desen Filtreleri . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
68
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
71
71
75
76
79
81
82
83
Denetçiler (Controllers) . .
Denetçilerin Oluşturulması
Controller Rotaları . . . .
RESTful Denetçiler . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
84
84
86
88
Blade . . . . . . . . . . . . .
Şablonların Oluşturulması
PHP Çıktısı . . . . . . . .
Kontrol Yapıları . . . . . .
Şablonlar . . . . . . . . . .
Şablon Kalıtımı . . . . . .
Yorumlar . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 90
. 90
. 91
. 93
. 96
. 97
. 104
Gelişmiş Rotalama . . . .
İsimli Rotalar . . . . .
Güvenli Rotalar . . . .
Parametre Sınırlamaları
Rota Grupları . . . . .
Rotalara Ön Ek Koyma
Domain Rotalama . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
105
105
107
108
109
110
111
URL Üretimi . . . . . . . . . .
Şimdiki URL . . . . . . . . .
Framework URL’leri Üretimi
Varlık URL’leri . . . . . . .
Üretim Kısayolları . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
114
114
116
120
122
İstek Verisi . . . . . . . . . . . . . .
Verileri Alma . . . . . . . . . . .
Önceki Input . . . . . . . . . . . .
Gönderilmiş (Uploaded) Dosyalar
Çerezler . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
125
126
132
138
145
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Formlar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
İÇINDEKILER
Formların Açılması
Form Alanları . . .
Form Düğmeleri . .
Form Makroları . .
Form Güvenliği . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
150
155
166
168
170
Geçerlilik Denetimi (Validation)
Basit Geçerlilik Denetimi . . .
Geçerlilik Kuralları . . . . . .
Hata Mesajları . . . . . . . . .
Özel Geçerlilik Kuralları . . .
Özel Geçerlilik Mesajları . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
173
175
184
193
201
204
Veritabanları . .
Soyutlama . .
Yapılandırma
Hazırlama . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
208
208
209
214
Şema Oluşturucusu . . . .
Tabloların Oluşturulması
Sütun Türleri . . . . . .
Özellikli Sütun Türleri .
Sütun Niteleyicileri . . .
Tabloların Güncellenmesi
Tabloların Düşürülmesi .
Şema Püf Noktaları . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
216
216
218
229
230
236
242
243
Migrasyonlar . . . . . . . . . . .
Temel Kavram . . . . . . . . .
Migrasyonların Oluşturulması
Migrasyonların Çalıştırılması .
Geri Alma (Rolling Back) . . .
Migrasyon Püf Noktaları . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
247
247
248
252
258
258
Eloquent ORM . . . . . . . . . . . .
Yeni Modellerin Oluşturulması . .
Mevcut Modellerin Okunması . .
Mevcut Modellerin Güncellenmesi
Mevcut Modellerin Silinmesi . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
260
263
271
271
274
Eloquent Sorguları . .
Hazırlık . . . . . . .
Eloquent’ten String’e
Sorgu Yapısı . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
277
277
281
286
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
İÇINDEKILER
Fetch Metodları . . . .
Sorgu Sınırlamaları . .
Sihirli Where Sorguları
Sorgu Scope’ları . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
288
299
318
320
Eloquent Koleksiyonları
Collection Sınıfı . . . .
Collection Metodları .
En İyi Uygulamalar . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
323
323
323
345
Eloquent İlişkileri (Relationships)
İlişkilere Giriş . . . . . . . . . .
İlişkilerin Uygulanması . . . . .
İlişkilendirme ve Sorgulama . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
347
348
353
358
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu .
Konu üzerinde düşünmeye başlayalım. . . . . . . . . . . . .
Şimdi hacking zamanı! . . . . . . . . . . . . . . . . . . . . .
Veritabanı . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Rotalar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Görünümler . . . . . . . . . . . . . . . . . . . . . . . . . . .
Uygulama Mantığı . . . . . . . . . . . . . . . . . . . . . . .
Rahatlayın . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Ev Ödevi . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
363
363
364
366
369
370
372
379
384
385
Kimlik Doğrulama (Authentication) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
O nerede olacak? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
Prosedürel Kod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
Nesne Yönelimli Kod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406
Pek Yakında . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412
Teşekkürler
Her şeyden önce kız arkadaşım Emma’ya teşekkür etmek istiyorum, sadece benim tüm asosyal
girişimlerime tahammül ettiği için değil, aynı zamanda her iki kitabım için müthiş kırmızı panda
resimleri çektiği için! Seni seviyorum Emma!
Taylor Otwell, geçen yıl inanılmaz oldu, bana ekibin bir parçası olma fırsatı verdiğin için ve
dostluğun için teşekkür ederim. Kullanması gerçekten zevk veren bir framework yaptığın için,
kodlarımızı şiir okunur gibi yaptığın için ve onun geliştirilmesine bu kadar zaman ve tutku koyduğun
için teşekkür ederim. Laravel’in bu yeni versiyonunda seninle çalışmaktan gerçekten zevk aldım ve
gelecekteki projelerde tekrar seninle çalışmayı umuyorum!
Eric Barnes, Phill Sparks, Shawn McCool, Jason Lewis, Ian Landsman, çatıyla ilgili tüm destekleriniz
ve iyi dostlar olduğunuz için teşekkürler.
Anne ve babama teşekkür ediyorum, yirmi sekiz yıldır benim asosyal çabalarımı destekliyorlar! Ve
yine aile üyeleri için benim ilk kitabımdan bir milyar kopya kadar aldıkları için teşekkürler!
İlk kitabım Code Happy almış olan herkese ve Laravel topluluğunun hepsine teşekkür ederim. Sizin
desteğiniz olmadan ikinci kitabım asla gerçekleşemezdi.
Çevirenin Notu
Bu kitap, ilk çeviri tecrübem olarak bana son derece keyif verdi. Çeviriyi yaparken bir yandan da
öğrendim, bu da işi daha zevkli hale getirdi.
Dayle Rees’in samimi anlatımı ve hemen her konuyu örneklendirmesi öğrenme sürecinde her bilgi
seviyesindeki kullanıcıya son derece yardımcı olacak bir kaynağa dönüştürdü bu kitabı.
Öncelikle sevgili eşim Bilge ve gözümün ışığı kızım Tuana Şeyma’ya teşekkürler. İyi ki varsınız!
Laravel Türkiye Forumları¹‘nda oluşturduğumuz dokümantasyon çeviri ekibine, kısa zamanda
belgelerin tamamlanmasını sağladığınız ve Laravel’in kapılarını Türkçe dilini kullanan tüm kullanıcılara açtığınız için teşekkürler.
Gerek dokümantasyon, gerekse bu kitabın çevirisinde tüm süreç boyunca yanımda olan ve çok katkı
sağlayan değerli Sergin Arı’ya, kattıklarından dolayı minnettarım. Sen olmadan olmazdı!
Çeviri sürecinde ince eleyip sık dokudum ancak yine de hatalar yapmış olabilirim, bu sebeple
karşılaşmanız muhtemel hataları bana aşağıdaki kanallardan bildirirseniz sevinirim.
E-posta: [email protected]
¹http://www.laravel.gen.tr
Teşekkürler
Web: www.sinaneldem.com.tr²
Twitter: twitter.com/sineld³
Diğer Laravel Türkçe Kitapları: leanpub.com/u/sineld⁴
²http://www.sinaneldem.com.tr/
³http://twitter.com/sineld/
⁴https://leanpub.com/u/sineld/
ii
Yazım Hataları
Bu benim ikinci kitabım ve öncekinden bu yana yazılarımı iyileştirdim, ama sizi temin ederim, çok, pek çok hata olacak. Bulduğun hataları kesim başlığıyla birlikte bir e-posta olarak
[email protected]⁵ adresine göndermek suretiyle destek verebilirsiniz.
Hatalar keşfedildikçe düzeltilecektir. Düzeltmeler kitabın sonraki baskılarında yer alacaktır.
⁵mailto:[email protected]
Geribildirim
Aynı şekilde, kitap içeriği konusunda veya başka nedenle [email protected]⁶ adresine e-posta
göndererek veya @daylerees’e bir tweet göndererek bir geribildirimde bulunabilirsiniz. Ben, aldığım
tüm postaları cevaplamaya çalışacağım.
⁶mailto:[email protected]
Çeviriler
Code Bright’ı kendi dilinize çevirmek isterseniz, lütfen planlarınızla birlikte [email protected]⁷
adresine bir email gönderin. Tercüme kopyadan gelecek kazancı 50/50 bölüşmeyi teklif ediyorum ve
fiyatı İngilizce kopyasıyla aynı olacaktır.
Lütfen kitabın markdown formatıyla yazılacağını unutmayın.
⁷mailto:[email protected]
Giriş
Evet, bir kitap bölümü yazmayalı çok zaman oldu. Code Happy 12 ay kadar önce yayınlandı ve üç
bin satış rakamını aştı. Yazı nasıl yazılır hatırlayabilecek miyim bakalım.
O kitabı okuduysanız benim öncelikle bir geliştirici, ikinci olarak bir yazar olduğumu zaten
biliyorsunuzdur. Bu nedenle, bu kitapta uzun kelimeler göremeyeceksiniz. Shakespeare’i hiçbir
şey etkilemeyecektir nasıl olsa (yazım hataları dışında). Laravel çatısını öğrenmek için, basit, düz
konuşmalar alacaksınız. Ayrıca tutku alacaksınız! Terli yatak çarşafları türünde bir tutku değil,
rakipsiz Laravel framework coşkusu. Ben kitaplarımı karşınızda durmuş, sizinle karşılıklı konuşur
gibi yazmayı seviyorum. Aslında, eğer gerçekten benimle konuşmak istiyorsanız, o zaman Laravel
IRC kanalına gelin ve beni görün!
Şimdi, ‘Yazar hakkında bilgi’ paragrafına geldik. Burayı kimse okumak istemez, fakat bir miktar
egonun kimseye zararı olmaz, öyle değil mi?
Benim adım Dayle Rees (kapakta öyle diyor!) ve ben bir web geliştiricisi ve bir tasarım tutkunuyum.
Galler kıyısında küçük bir kasaba olan Aberystwyth’liyim. Son kitabım ‘Code Happy’yi yazdığım
sırada Aberystwyth’de Galler Milli Kütüphanesinde çalışıyordum, burası Birleşik Krallıktaki üç telif
kütüphanesinden biridir.
Galler başkenti Cardiff’e taşındığımdan bu yana BoxUK ile çalışıyorum. BoxUK bir internet
danışmanlık ve geliştirme örgütüdür, orada web geliştirme dünyasına meraklı bir geliştiriciler ekibi
ile birlikteyim.
Web geliştirme benim sadece işim değil, aynı zamanda hobim. Yararlı ve ilginç kod parçaları ya da
güzel tasarımlar bulmak hoşuma gidiyor. Yeteneklerimizin harika şeyler üreteceğine inanıyorum ve
hayata geçmiş fikirler görmeyi seviyorum.
Bir yıldan biraz daha önce Laravel topluluğuna kod demetleri, web tasarımları ve yapabildiğim başka
yollarla yardımcı olmaya başladım. O zamandan bu yana ilişkim arttı. Laravel artık benim esas açık
kaynak projem ve ben şimdi çatının çekirdek geliştirme ekibinin bir üyesiyim.
Laravel 4 (kod adı Illuminate) ile birlikte benim katılımım çok yükseklere çıktı. Bu sürümü,
şimdiye dek kullanılabilecek en iyi çatı yapmak için Taylor Otwell ile birlikte çalışıyorum. Laravel
4 ile ilgili bir şey söylemeyin! Onu kullanmaya başlayın ve kod yazarken gülümsemelerinizi
durduramadığınızda bize teşekkür edersiniz.
Laravel bir geliştirme aracının ne kadar üretken olabileceğini gösteren bir örnektir. Laravel’in
güzelim sözdizimi Taylor Otwell’in rakipsiz dehasından geliyor. O bize şiir gibi okunacak kodlar
yazma imkanı vermektedir ve kodlama görevlerimizden zevk almamızı sağlayacaktır.
Peki Dayle, çatının son sürümüyle ne değişti?
vii
Giriş
Basit ama kafa karıştırıcı cevap, her şey ve hiçbir şey!
Laravel 4, bir milyar (tam rakam değil, saymadım) yeni özellikler ile birlikte esneklik ve test
edilebilirliği artırmak üzere sıfırdan tekrar yazılmıştır. Laravel 3’te kodunuzu yapılandırmak için
size bir miktar özgürlük verilmişti, Laravel 4 hackerların vahşi doğaya çıkmalarına ve çatıyı kendi
gereksinimlerine uygun şekilde değiştirmelerine olanak sağlayacaktır.
Bir şeyin iyileştirildiğini duyduğumda her zaman bir bityeniği ararım fakat Laravel 4 öyle değil. O
hala sevdiğiniz güzel ve ifade edici sözdizimine sahip; belki de onu daha çok sevdiğinizi göreceksiniz!
Dostum, niye yeni bir kitap yazdın?
Code Happy 3.0 ile 3.2.x arasında dar bir sürümü kapsıyordu ve bir şeyleri doğru yapmış olmalıyım
ki üç binden fazla kopya satıldı. Emin olun, Laravel 4 ile çalışması için çok büyük ihtimalle bütün
bir kitabı yeniden işleyecektim. Bununla birlikte, çatının bu versiyonu yeni bir frameworktür. Eğer
kitabı güncellemiş olsaydım, hala büyük bir çatı olduğuna inandığım sürüm 3 hakkındaki tüm
bilgileri kaybedecektiniz. Birçok insanın Laravel 3’e dayalı projeleri olacaktır ve bu kişiler ihtiyaç
duyduklarında Code Happy’deki bilgilere erişebilmelidir diye düşünüyorum.
Ayrıca kendi tecrübelerim var. Code Happy’yi bitirdikten sonra bir kitap yazma konusunda çok
şeyler öğrendim. Şimdi kaçınabileceğim, sık yaptığım yanlışları öğrendim. Zaten yaptığım bir şeyi
iyiye götürebilirim ve umarım öyle olur.
Code Happy’yi okumamıştım! Önce onu mu okumalıyım?
İstiyorsanız okuyun, oraya bazı komik şakalar koymuştum. Ancak bu kitap da yeni başlayanlar
içindir ve bu nedenle çok temel bilgilerden başlayacağız. Şayet zaten Laravel kullanıyorsanız devam
edin ve ne değiştiğini görmek için ilginç parçalara geçin. Çatı için yeniyseniz, bana sadık kalmanızı
ve sayfa sayfa okumanızı önereceğim. Merak etmeyin! İlginç tutmaya çalışacağım. Yakında, Laravel
ile harika, etkileyici PHP uygulamaları oluşturmuş olacaksınız.
Kitap ne zaman tamamlanacak?
Önceki kitabımda olduğu gibi, bu kitap da ilerledikçe yayınlanacak. Yani siz her bölümü ben
yazdıkça alacaksınız. Kitabın şimdiki durumu tam olmayabilir ancak ek bölümleri ekledikçe bir
e-posta alacak, güncellemeleri ücretsiz indirebileceksiniz.
Böyle yazma yönteminin büyük bir esneklik sağladığını düşünüyorum. Yanlışlarım varsa kolayca değiştirebileceğimi bilerek, yazdıklarım hakkında rahat olabiliyorum. Belli bir tarihe kadar
yetiştirme telaşı olmadığında, yazacağım kitabın daha büyük kalitede olacağını hissediyorum.
Gelecekteki sürümler için ya da ek bilgileri vurgulamak için bu kitabı güncelleyebilirim. Siz içeriğe
daha hızlı erişebileceksiniz. Ayrıca, çatının yeni sürümünün piyasaya çıkmasıyla birlikte kitap
yayınlayabilmemi de sağlamaktadır.
viii
Giriş
Sorulardan yoruldum..
İyi! Öyleyse, öğrenme sürecine başlamaya çok hevesli olmalısınız. Hemen atlayın ve Laravel’in
keyfini çıkarmaya başlayın. Benimle sohbet etmek isterseniz bir tweet veya IRC’den mesaj göndermekten çekinmeyin!
Ön Bilgiler
Hey! Code Happy’de bu bölüm yoktu!
Gerçek bir Code Happy hayranı olarak geçmişte böyle bir şey koyamadım! Aferin sadık okuyucu!
Gördüğünüz gibi Laravel 4 çok sayıda yeni teknoloji kullanıyor. Bu teknolojiler kendi konularında
ve frameworke paralel olarak kolayca öğretilebilir. Bu düşünceyle, öğrenme deneyiminizi beslemek
için bu yeni teknolojiler üzerine bir bölümle başlamanın iyi olabileceğini düşündüm.
Deneyimli web geliştiricileri bu teknolojilerle zaten karşılaşmışlardır veya onları zaten kullanmaktadırlar. Eğer istiyorsanız bu ön bilgi bölümünü atlamanızı hoş karşılarım. Bu beni üzmez. Yoo,
gerçekten… devam edin. Artık bana ihtiyacınız yok.
Şayet hala okuyorsanız sizi arkadaşım olarak kabul ediyorum. Doğrudan rotalama bölümüne atlayan
hainlerden değilsiniz!
Haydi ilk ön bilgimize geçelim ve PHP aduzaylarını (namespaces) konuşalım.
Aduzayları (Namespaces)
PHP 5.3 sürümünde dile namespace denen yeni bir özellik eklendi. Birçok modern dil bu özelliği bir
süredir zaten kullanıyordu ancak PHP sahneye biraz geç çıktı. Az ya da çok, her yeni özelliğin bir
maksadı vardır. Bakalım PHP namespace bizim uygulamalarımıza ne gibi faydalar sağlıyor.
PHP’de aynı adı paylaşan iki sınıfınız olamaz. Onlar benzersiz olmalıdır. Bu kısıtlamayla ilgili sorun,
User adında bir sınıfı olan bir üçüncü parti kitaplık kullanıyorken, kendinize ait User adlı bir sınıf
oluşturamayacak olmanızdır. Oldukça uygun bir sınıf adını veremiyor olmak gerçekten utanılacak
bir durum, değil mi?
PHP namespace’leri bu sorunu aşmamızı mümkün kılar, gerçekten de istediğimiz kadar çok sayıda
User adlı sınıfımız olabilir. Tek yararı bu değil, namespace’leri benzer kodlarımızı düzgün küçük
paketlere koymak, hatta sahipliği göstermek için de kullanabiliriz.
Şimdi normal bir sınıfa bakalım. Evet… Onları daha önce kullandığınızı biliyorum. Güvenin bana,
tamam mı?
Global Namespace
İşte gerçekten basit bir sınıf.
Ön Bilgiler
1
2
<?php
2
3
// app/models/Eddard.php
4
5
6
class Eddard
{
7
8
}
Ona özel bir şey yok, kullanmak istediğimizde şöyle yapabiliriz.
1
<?php
2
3
// app/routes.php
4
5
$eddard = new Eddard();
Dayle, ben PHP biliyorum…
Tamam, tamam, pardon. Temel olarak, biz bu sınıfın ‘global’ aduzayında olduğunu düşünebiliriz. Bu
terimin onun için doğru olup olmadığını bilmiyorum ancak bana oldukça uygun geliyor. Bu aslında,
bir namespace olmadan var olan bir sınıf anlamına gelir. Sadece normal bir sınıftır.
Basit Namespace
Şimdi orijinal, global Eddard yanında başka bir sınıf oluşturalım.
1
<?php
2
3
namespace Stark;
4
5
// app/models/another.php
6
7
8
class Eddard
{
9
10
}
Burada küçük bir değişiklik, namespace direktifinin eklenmesiyle, başka bir Eddard sınıfımız var.
Buradaki namespace Stark; satırı PHP’ye yapacağımız her şeyin Stark aduzayına göreli olduğunu
bildirir. Ayrıca, bu dosya içinde oluşturulan tüm sınıfların Stark aduzayı içerisinde yaşayacağı
anlamına gelir.
Şimdi, ‘Eddard’ sınıfını bir kez daha kullanmayı denediğimizde.
Ön Bilgiler
1
3
<?php
2
3
// app/routes.php
4
5
$eddard = new Eddard();
Yine son kesimde oluşturduğumuz ilk sınıfın bir olgusunu elde ederiz, ‘Stark’ namespace’i içindekini
değil. Hadi ‘Stark’ aduzayı içindeki ‘Eddard’ sınıfının bir olgusunu oluşturmaya çalışalım.
1
<?php
2
3
// app/routes.php
4
5
$eddard = new Stark\Eddard();
Bir namespace içindeki bir sınıfı, önüne namespace’in adını getirerek ve ikisini bir ters bölü (\) ile
ayırarak başlatabiliyoruz. Artık ‘Stark’ aduzayı içindeki ‘Eddard’ sınıfından bir olgumuz var. Büyü
gibi değil mi?
Namespace’lerin gerektiği kadar çok hiyerarşi düzeyinde olabileceğini bilin. Örneğin:
1
Bu\Aduzay\Ve\Class\Kombinasyonu\Aptalca\Gibidir\Ama\Uygundur
Rölativite Teorisi
PHP’nin her zaman için mevcut aduzayına göreli reaksiyon vereceğini söylediğimi hatırlayın.
Bunun nasıl olduğunu görelim:
1
<?php
2
3
namespace Stark;
4
5
// app/routes.php
6
7
$eddard = new Eddard();
Başlatma örneğine namespace direktifi eklemekle, PHP skriptinin çalışmasını ‘Stark’ aduzayına
taşımış oluyoruz. Biz şimdi içine ‘Eddard’ koyduğumuzla aynı namespace içinde olduğumuz için,
bu sefer bu aduzayındaki ‘Eddard’ sınıfını alacağız. Göreliliğin nasıl olduğunu gördünüz mü?
Şimdi namespace değiştirince, küçük bir problem oluşturduk. Ne olduğunu tahmin edebilecek
misiniz? Orijinal ‘Eddard’ sınıfını nasıl başlatacağız şimdi? O bir namespace’de değil ki.
Neyse ki, PHP’de global aduzayında bulunan sınıfları ifade etmek için bir püf noktası vardır, biz
basitçe başına bir ters bölü (\) koyarız.
Ön Bilgiler
1
4
<?php
2
3
// app/routes.php
4
5
$eddard = new \Eddard();
Başında ters bölü (\) olunca, PHP global aduzayındaki ‘Eddard’ı söylediğimizi bilir ve onu başlatır.
Biraz hayal gücünüzü kullanın ve Barney’nin sizi nasıl gösterdiğini düşünün. Diyelim ki, Tully\Edmure
adındaki başka bir aduzayı sınıfımız var. ‘Stark’ çerçevesi içerisinde bu sınıfı kullanmak istiyoruz.
Nasıl yapacağız?
1
<?php
2
3
namespace Stark;
4
5
// app/routes.php
6
7
$edmure = new \Tully\Edmure();
‘Tully’ namespace’inden bir sınıfın başlatılabilmesi için, aynı şekilde başına ters bölü koyarak
öncelikle global aduzayına geri getirmemiz gerekiyor.
Diğer aduzaylarındaki sınıflara atıfta bulunurken her seferinde tam hiyerarşilerini kullanmak
yorucu olabilir. Neyse ki kullanabileceğimiz güzel bir kısayol var: use. Bunu iş yaparken görelim.
1
<?php
2
3
namespace Stark;
4
5
use Tully\Edmure;
6
7
// app/routes.php
8
9
$edmure = new Edmure();
use cümleciğini kullanarak, başka bir aduzayındaki bir sınıfı mevcut aduzayına getirebiliyoruz.
Bize onu sadece adıyla başlatma imkanı veriyor. Şimdi bana niçin ters bölü ön eki gerekmediğini
sormayın, zira bilmiyorum. Bildiğim tek istisna bu değil. Bunun için üzgünüm. İsterseniz başına
bölü getirebilirseniz de, buna gerek olmadığını bilin.
O korkunç tutarsızlığı telafi etmek için, başka bir düzgün bir numara göstereyim. İthal ettiğimiz
sınıflara, PHP’de kullandığımıza benzer şekilde takma isimler verebiliyoruz. Göstereyim:
Ön Bilgiler
1
5
<?php
2
3
namespace Stark;
4
5
use Tully\Brynden as Blackfish;
6
7
// app/routes.php
8
9
$edmure = new Blackfish();
‘as‘ anahtarını kullanmak suretiyle, ‘Tully/Brynden’ sınıfımıza ‘Blackfish’ takma adını verdik,
bu bize onu mevcut namespace içerisinde tanımlamak için yeni takma adını kullanma imkanı
sağlayacak. Düzgün bir numara değil mi? Aynı namespace içinde aynı isimli iki sınıf kullanmamız
gerektiğinde bu gerçekten kullanışlıdır, örneğin:
1
<?php
2
3
namespace Targaryen;
4
5
use Dothraki\Daenerys as Khaleesi;
6
7
// app/routes.php
8
9
10
class Daenerys
{
11
12
}
13
14
15
// Targaryen\Daenerys
$daenerys = new Daenerys();
16
17
18
// Dothraki\Daenerys
$khaleesi = new Khaleesi();
‘Dothraki’ aduzayı içindeki ‘Daenerys’ sınıfına ‘Khaleesi’ takma adını vermekle, iki ‘Daenerys’
sınıfını sadece isimleriyle kullanabiliyoruz. İşe yarıyor değil mi? İşin özü çatışmaları önlemek ve
bir şeyleri amacına göre gruplamaktır.
Ne kadar gerekiyorsa o kadar sınıfı use edebilirsiniz.
Ön Bilgiler
1
6
<?php
2
3
namespace Targaryen;
4
5
6
7
8
use
use
use
use
Dothraki\Daenerys;
Stark\Eddard;
Lannister\Tyrion;
Snow\Jon as Bastard;
Yapı
Aduzayları sadece çatışmaları önlemekle ilgili değildir, onları organizasyonlar için ve sahipliği
belirtmek için de kullanabiliriz. Başka bir örnekle de bunu açıklayayım.
Diyelim ki bir açık kaynak kitaplığı oluşturmak istiyorum. Başkalarının benim kodumu kullanmasını
severim, bu harika bir şey! Sorun şu ki, benim kodumu kullanan kişinin herhangi bir sınıf adı
çatışması problemine yol açmayı istemem. Bu son derece rahatsız edici olurdu. Burada, belirli bir
açık kaynak için güçlüklere yol açmayı nasıl önleyeceğimizi gösteriyorum.
1
2
3
Dayle\Blog\Content\Post
Dayle\Blog\Content\Page
Dayle\Blog\Tag
Orijinal kodu benim oluşturduğumu göstermek ve benim kodumu benim kitaplığımı kullanan
kişininkinden ayırmak için burada kendi adımı kullandım. Benim uygulamamı kendi iç yapısına
göre organize etmek için de temel namespace içerisinde, birtakım alt aduzayları oluşturdum.
Composer bölümünde, sınıf tanımlarını yükleme eylemini kolaylaştırmak için aduzaylarını nasıl
kullanacağımızı öğreneceğiz. Bu yararlı mekanizmaya mutlaka göz atmanızı öneriyorum.
Sınırlamalar
Doğrusunu isterseniz, bu alt başlığa ‘Sınırlamalar’ dediğim için bir miktar suçluluk hissediyorum.
Hakkında konuşacağım şey aslında bir kusur değildir.
Bildiğiniz gibi, diğer dillerde de aduzayları benzer bir yolla uygulanır ve bunlardan bir kısmı
aduzaylarıyla etkileşimde ek özellikler sağlar.
Mesela Java’da, import cümleciğini bir jokerle birlikte kullanarak çok sayıda sınıfı mevcut namespace içine ithal edebiliyorsunuz. Java’daki ‘import’ bizim ‘use’ ile eşdeğerdir ve iç içe aduzaylarını
(veya paketleri) ayırmak için nokta kullanır. İşte bir örnek.
Ön Bilgiler
1
7
import dayle.blog.*;
Bu satır ‘dayle.blog’ paketi içinde bulunan sınıfların hepsini ithal edecektir.
PHP’de bunu yapamazsınız. Her sınıfı tek tek ithal etmek zorundasınız. Üzgünüm. Aslında, özrü
niçin ben diliyorum ki? Git ve PHP iç ekibine şikayet edin. Sadece nazik olun. Onlar bize son
zamanlarda bir sürü güzel şeyler verdiler.
Bununla birlikte, ben size kullanabileceğiniz uygun bir numara vereyim. Önceki örnektekiyle aynı
şu namespace ve sınıf yapımız olduğunu düşünün.
1
2
3
Dayle\Blog\Content\Post
Dayle\Blog\Content\Page
Dayle\Blog\Tag
Çocuk sınıflarını kullanmak için bir alt aduzayına bir takma ad verebiliriz. Örnek şöyle:
1
<?php
2
3
namespace Baratheon;
4
5
use Dayle\Blog as Cms;
6
7
// app/routes.php
8
9
10
11
$post = new Cms\Content\Post;
$page = new Cms\Content\Page;
$tag = new Cms\Tag;
Eğer aynı namespace içindeki birçok sınıfı kullanmanız gerekirse, bu çok yararlıdır. Zevkini çıkarın!
Sonraki konuda Jason öğreneceğiz. Hayır, Avustralyalı Jason Lewis değil, JSON stringleri. Sadece
sayfayı çevirin ve ne demek istediğimi görün!
JSON
JSON Nedir?
JSON JavaScript Nesne Gösterimi (Object Notation) anlamına gelir. Bu formatın avantajını ilk alan
dil JavaScript olduğu için böyle adlandırılmıştır.
Esasında, JSON dizileri ve nesneleri stringler halindeki değerleriyle birlikte insanların okuyabileceği
şekilde depolama yöntemidir. Öncelikle veri transferi için kullanılmaktadır ve XML gibi diğer
seçeneklerin bir kısmına göre gereksiz ayrıntılar çok daha azdır.
Ön Bilgiler
8
Çoğunlukla, uygulamanızın ön tarafında yeni bir sayfa yüklemesi olmadan arka taraftan bazı veriler
gerektirdiğinde kullanılmaktadır. Bu normalde, JavaScriptle bir AJAX isteği kullanılarak elde edilir.
Birçok yazılım API’si içeriklerini bu dosya formatını kullanarak sunmaktadır. Bu tür API’lerin güzel
bir örneği Twitter’dir.
PHP 5.2.0 sürümünden itibaren nesneleri ve dizileri JSON’a serileştirebilmeye başladı. Bu benim
kişisel olarak bir milyar, belki daha fazla istismar ettiğim bir şeydir ve PHP diline büyük bir katkı
olmuştur.
Bir süredir PHP ile çalışıyorsanız, bir PHP nesnesini bir string olarak temsil etmek için onun
serialize() metodunu zaten kullanmışsınızdır. Daha sonra bu stringi orijinal değeri içeren yeni
bir olguya dönüştürmek için de unserialize() kullanabiliyorsunuz.
JSON kullanarak ne olacağı kabaca budur. Ancak, avantajı JSON’un birtakım farklı diller tarafından
ayrıştırılabilmesi, oysa serialize() edilmiş stringlerin sadece PHP tarafından parse edilebilmesidir.
Ek avantajı bizim (insanlar ve pandalar olarak) JSON stringlerini okuyabilmemiz, serileştirilmiş PHP
stringlerinin ise çöp yığını gibi gözükmesidir.
Bu kadar hikaye yeter, haydi içeri dalalım ve JSON’a biraz göz atalım.
JSON Sözdizimi
1
2
{"ismi":"Lushui","cinsi":"Panda","diyeti":"Yeşil Şeyler","yas":7,"renkleri":["kır\
mızı","kahverengi","beyaz"]}
Yaşasın JSON! Tamam, insanların onu okuyabildiğini söyledim ama bir şeyi söylemeyi unutmuş
olabilirim. JSON ön tanımlı olarak, değerleri arasında hiçbir beyaz boşluk olmadan depolanır ve bu
da okunmasını biraz zorlaştırır.
Bu normalde veri transfer ederken bant genişliğinden tasarruf sağlar. Fazladan beyaz boşluklar
olmadığında, JSON stringi çok daha kısa olacak ve böylece nakledilecek byte daha az olacaktır.
İyi bir haber JSON’un anahtarları ve değerleri arasındaki boşluk veya satır sonlarını umursamamasıdır. Haydi rastgele! Daha okunabilir yapmak için istediğiniz kadar beyaz boşluk koyun.
Şüphesiz, biz bunu elimizle yapabiliriz (ancak yapmayalım), fakat web’te JSON’u güzelleştirmek
için bol miktarda araç var. Ben sizin için birini seçmek istemiyorum. Kendiniz arayın, bulun! Web
sunucularından gelen JSON cevabını daha kolay okumanıza imkan veren web tarayıcı uzantıları bile
bulabilirsiniz. Bunlardan birini bulmanızı kuvvetle tavsiye ederim!
Haydi okumasını daha kolay yapmak için, JSON’a beyaz boşluklar ekleyelim. (Dürüst olmak
gerekirse, bunu elle yaptım. Hey millet, bunu evde sakın denemeyin.)
9
Ön Bilgiler
1
{
"ismi":
"cinsi":
"diyeti":
"yas":
"renkleri":
2
3
4
5
6
7
"Lushui",
"Panda",
"Yeşil Şeyler",
7,
["kırmızı", "kahverengi", "beyaz"]
}
Aha! Başladık. Şimdi, kitaplarımın kapaklarında yaşayan kırmızı pandayı temsil eden bir JSON
stringimiz oldu. Lushui sizin Laravel bilginizi meraklı gözlerden güvenle korur.
Örnekten görebileceğiniz gibi, birkaç anahtar-değer çiftimiz var. Anahtar-değer çiftlerimizden
birinin içinde bir dizi var. Açıkçası, daha önce JavaScript kullanmışsanız burada neyin değiştiğini
merak edebilirsiniz. Gerçekten de, bu JavaScriptte nasıl temsil edilecekti bir görelim.
1
2
3
4
5
6
7
var lushui = {
ismi:
cinsi:
diyeti:
yas:
renkleri:
};
'Lushui',
'Panda',
'Yeşil Şeyler',
7,
['kırmızı', 'kahverengi', 'beyaz']
Ümit ediyorum ki, JavaScript ile JSON kod parçacıkları arasındaki bazı benzerlikleri fark edeceksiniz.
JavaScript kod parçası bir nesneyi bir değişkene atamaktadır, şöyle:
1
var lushui = { .. };
Tabii, JSON bir veri transfer biçimidir ve bir dil değildir. Değişken kavramına sahip değildir. İşte bu
yüzden JSON kod parçasında atama yapmamıza gerek yoktur.
Bir nesne değişmezini temsil etme yöntemi çok benzerdir. Bu bir rastlantı değil! Önce de söylediğim
gibi, JSON orijinal olarak JavaScript ile kullanım için ortaya çıkarılmıştır.
JSON ve JavaScript her ikisinde de, nesneler { iki küme parantezi } içinde barındırılır ve anahtardeğer veri çiftlerinden oluşur. JavaScript biçiminde anahtarlar değişkenleri temsil ettikleri için tırnak
gerektirmezler ancak az önce JSON’unda değişkenler olmadığını duydunuz. Bu gayet iyi, çünkü
stringleri anahtar olarak kullanabiliyoruz ve JSON tam olarak bu problemle ilgileniyor.
JavaScript değerleri etrafında tek tırnak kullandığım da dikkatinizi çekmiş olabilir. Bu bir tuzak!
Bu davranış JavaScript’te kusursuz olarak kabul edilir, JSON stringleri çift tırnaklar içinde olmak
zorundadır. Bunu hiç unutmayın genç adam!
JavaScript ve JSON, her ikisinde de anahtar-değer çiftleri iki nokta üst üste (:) ile ayrılmalıdır ve
anahtar-değer takımları birbirinden virgülle (,) ayrılmış olmalıdır.
Ön Bilgiler
10
JSON stringleri ve sayısal tipleri destekleyecektir. Lushui’nin yaş değerini yedi tam sayısı olarak
ayarladığımızı görmüştünüz.
1
yas: 7,
JSON aşağıdaki değer tiplerine izin verecektir.
•
•
•
•
•
•
•
Double
Float
String
Boolean
Array
Object
Null
Numerik değerler tırnak olmadan gösterilir. Bir değerin tırnaklı olup olmamasını seçerken dikkatli
olun. Örnek olarak Türkiye posta kodları beş rakamdan oluşur. Bununla birlikte, eğer posta kodu
için tırnak koymayı ihmal ederseniz, bu durumda 07700 bir tamsayı olarak davranacak ve 7700‘ye
budanmış olacaktır. Bu, bir web yolculuğuna çıkmış olanların başına çok gelmiş bir yanlıştır.
Boolean’lar true ve false kelimeleri ile temsil edilir ve her ikisi de PHP’nin kendisindeki gibi
tırnaksızdır. Daha önce söylediğim gibi, stringler tek tırnak içinde değil çift tırnak içine alınır.
Null değer ise aynı PHP’dekine benzer davranır ve tırnak olmaksızın null kelimesi ile temsil edilir.
Bunu hatırlamak kolay olmalı!
Nesneleri gördük. Ana JSON nesnesinin kendine çok benzer olarak, bunlar küme parantezi içine
alınırlar ve tüm değer tiplerini içerebilirler.
Diziler ise JavaScript dizilerine çok benzer.
1
2
// JavaScript
['kırmızı', 'kahverengi', 'beyaz']
3
4
5
["kırmızı", "kahverengi", "beyaz"]
*Not Yukardaki örnekte JSON kod parçasında satır içi yorum eklemediğimi fark edeceksiniz. Bunun
nedeni JSON’un veri transferi için kullanılmasından dolayı yorumları desteklememesidir. Bunu
aklınızda tutun!
Görebileceğiniz gibi, her iki tipteki dizi [ köşeli parantez ] içine alınır ve anahtarlı olmayan, virgül
(,) ile ayrılmış bir değerler listesi içerir. Tekrar hatırlatmakta fayda var, tek fark JSON içinde stringler
için çift tırnak kullanımının şart olmasıdır. Söylediklerimden hala sıkılmadınız mı?
Yukarıda bahsettiğim gibi, JSON içinde taşınabilen değerler hem nesne, hem de dizi içerebilirler.
Okuyucularım arasındaki zeki adamlar (yani hepiniz) JSON’un iç içe nesneler ve dizileri desteklediğini anlamıştır. İş üstünde bir görelim!
Ön Bilgiler
1
{
"bir_nesne": {
"bir_nesneler_dizisi": [
{ "Bizim": "sırrımız" },
{ "o": "ki" },
{ "ben": "hala" },
{ "ayakkabıları": "seviyorum!" }
]
}
2
3
4
5
6
7
8
9
10
11
}
Pekiyi. Derin bir nefes alın. Burada, bir nesneler dizisi içeren bir nesne içeren bir JSON nesnesi
var. Bu gayet güzel bir şeydir ve JSON’un karmaşık veri koleksiyonlarını ifade etmesine imkan
vermektedir.
JSON ve PHP
Daha önce belirtildiği gibi PHP 5.2.0 sürümünden beri JSON formatına serileştirme ve JSON
formatından veri çözme için destek vermiştir. Haydi gidip bu işe bir göz atalım.
PHP dizisini JSON’a serileştirme
Bir PHP değerini serileştirmek için ihtiyacımız olan tek şey json_encode() metodunu kullanmak.
Şöyle mesela:
1
<?php
2
3
4
$gercek = array('panda' => 'Müthiş!');
echo json_encode($gercek);
Bu kod parçasının sonucu aşağıdaki değeri içeren bir JSON stringi olacaktır.
1
{"panda":"Müthiş!"}
Mükemmel! Daha fazlası da olabilir mi? Bu veriyi gerisin geriye, PHP’nin anlayabileceği bir formata
çevirebileceğimizden emin olalım.
Bir JSON stringinden bir PHP dizisine seri çözme
Bunun için de json_decode() metodunu kullanacağız. Ne geleceğini görmediğinizden eminim.
Ön Bilgiler
1
12
<?php
2
3
4
$gercek = json_decode('{"panda":"Müthiş!"}');
echo $gercek['panda'];
Müthiş! Gidiyoruz… bekliyoruz, ne?
1
Fatal error: Cannot use object of type stdClass as array in ...
Gördünüz mü? json_decode() metodu bizim JSON’u bir PHP dizisi olarak döndürmedi; verimizi
temsil etmek için bir stdClass nesnesi kullanıyor. O zaman biz de nesne anahtarımıza bir nesne
niteliği şeklinde erişeceğiz.
1
<?php
2
3
4
$gercek = json_decode('{"panda":"Müthiş!"}');
echo $gercek->panda;
5
6
// Müthiş!
Harika! İstediğimiz işte bu. Tabi bir dizi isteseydik PHP bu diziyi ona çevirecek çeşitli yollar sağlıyor,
ama neyseki json_decode() kolunun altında başka bir hile taşıyor!
Eğer biz bu fonksiyona ikinci bir parametre olarak true girersek, bizim PHP dizimizi tam beklediğimiz şekilde alabileceğiz. Teşekkürler json_decode()!
1
<?php
2
3
4
$gercek = json_decode('{"panda":"Müthiş!”}', true);
echo $gercek['panda'];
5
6
// Müthiş!
Yaşasın!
Bir Laravel kitabı içinde sadece JSON üzerine neden dev bir bölüm yazdığımı merak ediyor
olabilirsiniz. Üstelik, büyük ihtimalle bu soruya neden bölümün en sonunda cevap vermeyi tercih
ettiğimi de sorguluyorsunuzdur!
Sadece bu daha eğlenceli olduğu için.
Sonraki kesimde PHP için yeni bir paket yöneticisi Composer’a bakacağız. Composer’ı incelemeye
başladığımızda JSON bilgisinin ne kadar önemli olduğunu anlayacaksın.
Ön Bilgiler
13
Composer
Composer PHP dünyasında özel bir şey. O bizim uygulama bağımlılıklarını yoluna koyma şeklimizi
değiştirdi ve birçok PHP geliştiricisinin göz yaşlarını dindirdi.
Bilirsiniz, eski günlerde, üçüncü parti bağımlılıklarına dayanan bir uygulama inşa etmek istediğimizde, onları PEAR veya PECL ile yüklemek zorundaydık. Bu iki bağımlılık yöneticisinde de günü
geçmiş, çok sınırlı sayıda bağımlılık vardı ve uzun bir süredir PHP geliştiricileri açısından bir bela
olmuşlardı.
Bir paket, sonunda kullanılabilir olduğunda belirli bir versiyonunu indirebilir ve sisteminize yükleyebilirdiniz. Ancak, bağımlılık sizin uygulamanızın kendine değil, PHP’ye bağlanırdı. Yani, aynı
bağımlılıkların farklı versiyonlarını gerektiren iki uygulamanız olduğunda… evet, kötü bir zaman
yaşardınız.
Composer geldi, paket yöneticilerinin kıralı. Öncelikle paketler hakkında konuşalım, onlar nedir?
Her şeyden önce, şimdilik ‘uygulamalar’ ve ‘projeler’ kavramlarını unutalım. İnşa etmekte olduğunuz araç bir paket olarak adlandırılır. Uygulamanızın çalışması için gereken her şeyi taşıyan küçük
bir kutu hayal edin ve onu tarif edin.
Onu bir paket olarak kayda geçirmek için bu kutunun içinde sadece bir parça kağıt (dosya)
bulunmasını gerektirir.
Yapılandırma
Önceki bölümde JSON öğrenmiştiniz, değil mi? Yani artık bunun için hazırsınız! Size JSON’un web
uygulamaları arasında veri transferi için kullanıldığını söylediğimi hatırlıyor musunuz? Tamam,
yalan söyledim. Kötü birisi olduğumdan değil, sadece, konuyu onun yapabildiklerinin küçük bir
kısmıyla öğretmek daha kolay olduğu için. Ben bunu çok yaparım, çok yalanlar bekleyin benden!
Hatırlıyor musunuz, JSON karmaşık bir veri parçasını nasıl gösteriyordu? Peki, o zaman niçin biz
onu yapılandırma sağlayacak düz dosyalar içinde kullanmayalım ki? İşte Composer adamlarının
düşündüğü de tam olarak bu. Onlarla mı tartışacağız?
JSON dosyaları .json uzantısı kullanır. Composer, paket yapılandırmasının paketinizin kök dizininde bulunan adı composer.json olan bir dosya olmasını bekler. Bunu unutmayın! Laravel bu dosyayı
çok sık kullanacaktır.
Haydi dosyayı açalım ve paketimiz hakkında bazı bilgiler girmeye başlayalım.
14
Ön Bilgiler
1
{
"name":
"description":
"keywords":
"homepage":
"time":
"license":
"authors": [
{
"name":
"email":
"homepage":
"role":
}
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"marvel/xmen",
"Mutants saving the world for people who hate them.",
["mutant", "superhero", "bald", "guy"],
"http://marvel.com/xmen",
"1963-09-01",
"MIT",
"Stan Lee",
"[email protected]",
"http://marvel.com",
"Genius"
}
Tamam, X-Men paketinin kök dizininde bir composer.json dosyamız oldu. Neden X-Men? Onlar
harika, o yüzden.
Gerçeği söylemek gerekirse, bu dosyadaki opsiyonların (anahtarların) hepsi opsiyoneldir. Normalde
yukarıdaki bilgileri paketi dağıtmayı veya vahşi doğaya salmayı düşünürseniz verirsiniz.
Size karşı dürüst olacağım, ben normalde girerim ve bu bilgileri bir şekilde doldururum. Bu herhangi
bir zarar vermez. Yukarıdaki yapılandırma, paketi tanımlamak için kullanılır. Bazı özel durumlar için
ayrılmış olduğunu düşündüğüm birkaç anahtarı dahil etmedim. Ek yaplandırma maddelerini merak
ediyorsanız, zengin bilgi ve belgeler içeren Composer websitesine⁸ bakmanızı öneririm.
Ayrıca Composer’a yeni gelenler için paket oluşturacakları zaman yararlı olabilecek bu yararlı cheat
sayfasını⁹ da buldum. Yapılandırma maddelerini daha fazla keşfetmek için fareyi her satırın üstüne
getirin.
Her neyse, en iyisi biz X-Men paketi için oluşturduğunuz yapılandırmaya daha yakından bakalım..
1
"name": "marvel/xmen",
Bu paketin adıdır. Şayet Github¹⁰ kullanıyorsanız, isim formatı size tanıdık gelecektir, ama ben yine
de açıklayacağım.
Paket adı normal bölü (/) ile ayrılmış iki kelimeden ibarettir. Bölü işaretinden önceki kısım paketin
sahibini temsil eder. Çoğu durumda geliştiriciler sahip olarak kendi Github kullanıcı adlarını
⁸http://getcomposer.org/
⁹http://composer.json.jolicode.com/
¹⁰http://github.com
Ön Bilgiler
15
kullanırlar ve ben de bu görüşe tamamen katılıyorum. Ama siz istediğiniz ismi kullanabilirsiniz.
Size ait tüm paketlerde bu tutarlı olsun.
name stringinin ikinci kısmı paketin adıdır. Bunu basit ve açıklayıcı tutun. Aynı şekilde, birçok
geliştirici paket için, Github’ta barındırıldığındaki ambar ismini kullanmayı tercih eder ve ben yine
aynı şekilde bu sisteme tam olarak katılıyorum.
1
"description": "Mutants saving the world for people who hate them.",
Paketin işlevselliğinin kısa bir açıklamasıdır. Bunu da basit tutmayı unutmayın. Eğer paket açık
kaynak için düşünülmüşse, ayrıntıları sizin ambardaki README dosyasında verebilirsiniz. Eğer bazı
kişisel belgeleri tutmak istiyorsanız burası onun konacağı yer değildir. Sırtınıza dövme yaptırın ve
bir ayna bulundurun. Bana en mantıklı geleni bu. Yapışkan notlar da aynı işi görecektir.
1
"keywords": ["mutant", "superhero", "bald", "guy"],
Bu kelimeler paketinizi göstermekte kullanılan bir string dizisidir. Bunlar bloglardaki etiketlere
benzer ve esasında aynı amaca hizmet eder. Paketiniz bir ambarda listelendiği zaman bu etiketler
yararlı arama meta verileri olacaktır.
1
"homepage": "http://marvel.com/xmen",
homepage yapılandırması açık kaynak olacak paketler için yararlıdır. Projeniz için homepage
kullanabilirsiniz veya Github ambarının URL’si olabilir. Daha bilgilendirici olacağını düşündüğünüz
herhangi biri.
Tekrar söylüyorum, bu yapılandırma seçeneklerinin tümü opsiyoneldir. Paketiniz için mantıklı
değilse, atlamaktan çekinmeyin.
1
"time": "1963-09-01",
Bu çok sık görmediğim seçeneklerden biridir. Yukarıda sözünü ettiğim cheat sayfasına göre,
uygulamanızın veya kitaplığın salınım tarihini temsil eder. Çoğu paket Github’da veya diğer sürüm
kontrol sitelerinde barındırıldığı için, çoğu durumda bunun gerekli olmadığını düşünüyorum. Bu
siteler normalde her gönderi, her etiket ve diğer yararlı olaylarda tarih atmaktadırlar.
time yapılandırması için kabul edilen biçimler YYYY-MM-DD ve YYYY-MM-DD HH:MM:SS‘dir. Hoşunuza
gittiyse, gidin verin bu değerleri!
1
"license": "MIT",
16
Ön Bilgiler
Eğer paketiniz yeniden dağıtılabilir olacaksa, bu durumda ona bir lisans vermek isteyeceksiniz. Bir
lisans olmadığında, birçok kullanıcı yasal sınırlamalar yüzünden paketinizi hiç kullanamayacaklardır. İhtiyaçlarınıza uygun ancak kodunuzu kullanmayı umanlar için çok kısıtlayıcı olmayan bir
lisans seçiniz. Laravel projesi, büyük bir özgürlük sunan MIT lisansı kullanmaktadır.
Çoğu lisans lisansın bir kopyasının kaynak ambarında tutulmasını gerektirir, fakat siz composer.json
dosyasında bu yapılandırma girişini de verirseniz, bu durumda paket ambarı paketi kendi lisansı ile
listeleyebilecektir.
Yapılandırmanın authors bölümü paketi oluşturanlar hakkında bilgi verir ve iletişim kurmak isteyen
paket kullanıcıları için yararlı olabilir.
authors bölümünün, işbirliğiyle yapılan paketler için bir yazarlar dizisine izin verdiğine dikkat edin.
Verilen seçenektekine bakalım tekrar.
1
2
3
4
5
6
7
8
"authors": [
{
"name":
"email":
"homepage":
"role":
}
]
"Stan Lee",
"[email protected]",
"http://marvel.com",
"Genius"
Her bir yazarı göstermek için bir nesne kullanın. Bizim örneğimizde sadece bir yazar var. Stan Lee’ye
bakalım. O sadece her Marvel filminde görünen biri değil, benim kitabımda da aynı şeyi yapıyor.
Ne arsız eski sosis!
1
"name": "Stan Lee",
Bu satırı nasıl basitleştireceğimi gerçekten bilemiyorum. Onu anlamakta sorun yaşıyorsanız, bu
kitabı kapatmayı düşünebilirsiniz ve çorap kuklacılığı sanatına devam edebilirsiniz.
1
"email": "[email protected]",
Paket patlarsa sizinle irtibat kurulabilmesi için geçerli bir eposta adresi verdiğinizden emin olun.
1
"homepage": "http://marvel.com",
Bu sefer kişisel web sayfası verilebilir, devam edin ve biraz hit alın!
17
Ön Bilgiler
1
"role": "Genius"
role seçeneği yazarın proje içindeki rolünü tanımlar. Örneğin, geliştirici, tasarımcı, hatta çorap
kuklası sanatçısı. Eğer doğru bir şey düşünemiyorsanız, o zaman komik bir şey koyun.
Paketinizi tanımlamak için gereken şeylerin hepsi bu kadar. Şimdi daha ilginç bir şeye bakalım:
Bağımlılık yönetimi!
Bağımlılık Yönetimi
X-Men’i taşıyacak bir kutunuz oldu. Ancak bu kutuda henüz mutantlar yok. Büyük bir süper
kahramanlar ekibi (uygulama) oluşturmak için diğer mutantların desteğinin alınması gerekiyor
(3üncü parti bağımlılıkları). Composer’in bunu gerçekleştirmemize nasıl yardımcı olacağını görelim.
1
{
"name":
"description":
"keywords":
"homepage":
"time":
"license":
"authors": [
{
"name":
"email":
"homepage":
"role":
}
],
"require": {
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"marvel/xmen",
"Mutants saving the world for people who hate them."
["mutant", "superhero", "bald", "guy"],
"http://marvel.com/xmen",
"1963-09-01",
"MIT",
"Stan Lee",
"[email protected]",
"http://marvel.com",
"Genius"
17
}
18
19
}
Şimdi bizim composer.json içinde “require” adında yeni bir kesimimiz oldu. Bu bizim bağıml…
mutantlarımızı listelemekte kullanılacak. Şimdi örnekleri kısaltmak için yapılanmanın geri kalanını
bir tarafa bırakıp sadece require bloğunu göstereceğim. Orada olduğundan emin olalım!
X-Men’in bağımlı olduğu şeyleri biliyoruz:
• Wolverine
• Cyclops
18
Ön Bilgiler
• Storm
• Gambit
Başkaları da var ama bu adamlar oldukça uygun. Şimdilik onlara dayanacağız. Tabii şöyle yapabilirdik. Bunların kaynak dosyalarını doğrudan bizim uygulamamızın içine kopyalar, daha sonra
her değişiklikte onları kendimiz güncellerdik. Bu gerçekten çok bunaltıcı olurdu. Biz onları require
kesimine ekleyelim, ondan sonra Composer bunları bizim için yönetecektir.
1
2
3
4
5
6
"require": {
"xmen/wolverine":
"xmen/cyclops":
"xmen/storm":
"xmen/gambit":
}
"1.0.0",
"1.0.1",
"1.2.0",
"1.0.0"
Biz burada, mutant bağımlılıklarımızı ve kullanmak istediğimiz sürümlerini listeliyoruz. Bu örnekte,
onların hepsi aynı sahibe, X-Men paketine aittirler ancak başka bir kişiye ait de olabilirlerdi.
Yeniden dağıtılabilir paketlerin çoğu bir sürüm kontrol web sitesinde, mesela Github¹¹ veya Bitbucket¹² sitesinde barındırılmaktadır. Versiyon kontrol ambarlarında, uygulamamızın stabil sürümlerini
tanımlayabildiğimiz bir etiketleme sistemi vardır. Örneğin git’te şu komutu kullanabiliriz:
1
git tag -a 1.0.0 -m 'İlk sürüm.'
Bu komutla uygulamamızın 1.0.0 sürümünü oluşturmuş oluyoruz. Bu, insanların bağımlı olabileceği stabil bir sürümdür.
Şimdi Gambit bağımlılığına biraz yakından bakalım.
1
"xmen/gambit": "1.0.0"
Artık Composer paket isimlerinin bir bölü (/) karakteri ile ayrılmış bir sahip ve bir paket takma
adından oluştuğunu biliyorsunuz. Bu bilgiyle biliyoruz ki, bu gambit paketi xmen kullanıcısı
tarafından yazılmıştır.
require kesimi içindeki her maddenin key kısmı paketin adıdır ve değer kısmı gereken sürümü
temsil eder.
Gambit örneğimizde, sürüm numarası Github’ta kodun o sürümünün olduğu etiketle karşılaştırılır.
Bağımlılık sürümlerimizin nasıl tüm sisteme değil de uygulamamıza özgü olabildiğini gördünüz
mü?
¹¹http://github.com
¹²http://bitbucket.org
Ön Bilgiler
19
Projenize istediğiniz kadar çok bağımlılık ekleyebilirsiniz. Gidin, bir milyar ekleyin! Yanlışım varsa
kanıtlayın.
Dinleyin, size bir sır vereyim mi? Kimseye anlatmacağınıza söz veriyor musunuz? Vayy, hııı.
Yaklaşın, kulağınıza fısıldayacağım. Duymak istediğiniz kelimeler…
Bağımlılıklarınızın da kendi bağımlılıkları olabilir.
Bu doğru! Bağımlılıklarınız da Composer paketleridir. Onların da kendi composer.json dosyaları
vardır. Bu demektir ki, onların da kendi bağımlılıklarını listeleyen kendi require bölümleri vardır
ve hatta bu bağımlılıkların da bağımlılıkları olabilecektir.
Daha iyisi ise, Composer’in bu içi içe bağımlılıkları sizin için yönetecek ve yükleyecek olması. Ne
kadar harika! Wolverine tools/claws, tools/yellow-mask ve power/regeneration gerektirebilir
ama bu konuda endişelenmenize gerek yok. Siz kendi require kısmınıza xmen/wolverine paketini
koyduğunuz sürece Composer gerisini halledecektir.
Bağımlılık sürümlerine gelince, bunlar çeşitli biçimlerde olabilmektedir. Örneğin bir bileşen için
minör güncellemeleri dikkate almayabilirsiniz. Bu durumda sürüm içinde joker kullanabilirsiniz,
şöyle:
1
"xmen/gambit": "1.0.*"
Bu durumda Composer 1.0 ile başlayan son sürümü yükleyecek. Örneğin Gambit 1.0.0 ve 1.0.1
sürümleri varsa, 1.0.1 yüklenecektir.
Paketinizde paket sürümleri için bir en alt veya en üst sınır olabilir. Bu tanımlama büyüktür ve
küçüktür işaretleri kullanılarak yapılabilmektedir.
1
"xmen/gambit": ">1.0.0"
Yukarıdaki örnek, 1.0.0‘den daha büyük bir sürüm numarasına sahip tüm xmen/gambit paket
sürümleri ile karşılanabilecektir.
1
"xmen/gambit": "<1.0.0"
Benzer şekilde, küçüktür işareti versiyon 1.0.0‘dan küçük paketlerle karşılanabilecektir. Paketinizin
maksimum bir sürüm bağımlılığı belirlemesine imkan verecektir.
1
2
"xmen/gambit": "=>1.0.0"
"xmen/gambit": "=<1.0.0"
Karşılatırma operatörüne eşittir = işareti de eklemek, sürüm sınırlamasını karşılayan sürüm listesine
karşılaştırılacak sürümün de eklenmesine yol açacaktır.
Kimi zaman, birden çok sürüm girmek veya bir paket versiyon aralığı vermek isteyebilirsiniz. Birden
çok sürüm sınırlaması, her sınırlamayı bir virgülle (,) ayırarak eklenebilmektedir. Örnek olarak:
Ön Bilgiler
1
20
"xmen/gambit": ">1.0.0,<1.0.2"
Yukarıdaki örnek 1.0.1 sürümü ile karşılanabilecektir.
Eğer stabil bağımlılıkları yüklemek istemiyorsanız, mesela bungee jumping veya yamaç paraşütünden hoşlanan bir tip olabilirsiniz, dolayısıyla oranızı buranızı kanatacak sürümler kullanmak
isteyebilirsiniz. Composer aşağıdaki sözdizimi kullanılarak bir ambarın dallarını (branches) nişanlayabilmektedir.
1
"xmen/gambit": "dev-dalismi"
Örneğin, Github’daki Gambit projesinin develop dalındaki güncel kodu kullanmak istiyorsanız, bu
durumda dev-develop sürüm sınırlaması kullanacaksınız.
1
"xmen/gambit": "dev-develop"
Paketiniz için doğru bir en düşük stabilite ayarınız olmadığı sürece bu geliştirme sürümü çalışmayacaktır. Ön tanımlı olarak, Composer stable minimum uyumluluk flag’ını kullanır ki, bağımlılık
sürümlerini stabil, etiketlenmiş sürümlere sınırlayacaktır.
Bu seçeneği geçersiz kılmak isterseniz, tek yapacağınız composer.json dosyanızdaki minumum-stability
yapılandırma seçeneğini değiştirin.
1
2
3
4
"require": {
"xmen/gambit": "dev-master"
},
"minimum-stability": "dev"
Minimum stabilite ayarı için kullanılabilecek başka değerler de var ancak bunların açıklanması
sürüm stabilite etiketlerinin derinliklerine dalmak demek. Ben bunlara girip de bu bölümü daha
da karıştırmak istemiyorum. Bu bölüme ileride geri dönüp konuyu anlatacağım ama konuyla ilgili
ek bilgiler bulmak için şimdilik Paket sürümleri için Composer belgelerine¹³ bakmanızı önereceğim.
Bazen, uygulamanızın sadece geliştirilmesiyle ilgili bağımlılıkları kullanma gereği duyabilirsiniz. Bu
bağımlılıkların bir üretim ortamında uygulamanızın her günkü kullanımı için gerekli olmayabilir.
Composer require-dev kesimi sayesinde bu yükü de sırtınızdan alır. Bir an için uygulamamızın
kabul testleri sağlamak için Codeception test framework¹⁴ gerektirdiğini düşünelim. Bu testler
bizim üretim ortamımızda hiç kullanılmayacaklardır, bu nedenle bunları composer.json dosyamızın
require-dev kesimine ekleyeceğiz.
¹³http://getcomposer.org/doc/01-basic-usage.md#package-versions
¹⁴http://codeception.com/
Ön Bilgiler
1
2
3
4
5
6
21
"require": {
"xmen/gambit": "dev-master"
},
"require-dev": {
"codeception/codeception": "1.6.0.3"
}
Bu codeception/codeception paketi yalnızca biz Composer’i --dev anahtarıyla kullanırsak yüklenecektir. Yükleme ve kullanım kesimlerinde bu konuda daha fazla bilgi olacaktır.
Yukarıda görmüş olduğunuz gibi, require-dev kesimi require kesimiyle tam aynı formatı kullanmaktadır. Aslında, aynı formatı kullanan başka kesimler de vardır. Neler varmış bir bakalım mı?
1
2
3
"conflict": {
"marvel/spiderman": "1.0.0"
}
conflict kesimi bizim paketimizle bir arada mutlu çalışamayacak paketlerin bir listesini taşır.
Composer bu paketlerin yan yana yüklenmesine izin vermeyecektir.
1
2
3
"replace": {
"xmen/gambit": "1.0.0"
}
replace kesimi size bu paketin başka bir paket yerine kulllanılabileceğini bildirir. Bu, başka bir
paketten dallandırılmış (forked) ama aynı işlevselliğe sahip paketler için işe yarar.
1
2
3
"provide": {
"xmen/gambit": "1.0.0"
}
Bu kesim paketlerin sizin paketinizin kod tabanı içerisinde sağlandığını gösterir. Şayet Gambit
paketlerinin kaynağı sizin ana paketiniz içinde yer almışsa, bu durumda onu yeniden yüklemek
anlamsız olacaktı. Bu kesimi Composer’in sizin ana paketiniz içine gömülmüş hangi paketler
olduğunu bilmesi için kullanın. Unutmayın, burada paket bağımlılıklarınızı listelemeniz gerekmiyor.
require‘de bulunan bir şey sayılmayacaktır.
1
2
3
"suggest": {
"xmen/gambit": "1.0.0"
}
Ön Bilgiler
22
Paketinizde onun işlevselliğini artıran, ama kesin gereklilikte olmayan ekstra paketler olabilir. Neden
onları suggest kesimine eklemiyorsunuz? Composer install komutu çalıştırıldığında, Composer bu
kesimdeki paketleri “yüklenmesi önerilenler” olarak anacaktır.
Bağımlılıklar üzerine söyleyeceklerim bu kadar. Şimdi Composer’in başka bir sihirli parçasına
gideceğiz. Otomatik yüklemeler!
Otomatik Yüklemeler
Şu ana kadar Composer’in paket bağımlılıklarımızı bizim için alıp getirebildiğini öğrendik, fakat
bunları nasıl kullanacağımızı biliyor muyuz? Biz kendimiz PHP içinde bu kaynak dosyalarını
require() edebilirdik ancak bunu yapabilmek için onların tam olarak nerede olduklarını da
bilmemiz gerekiyor.
Bunu yapacak zamanı olan yoktur. Composer bunu bizim için halledecektir. Composer’a sınıflarımızın oldukları yerleri ve onları yüklemek için kullanılabilecek metodların ne olduğunu söylersek,
uygulamamız tarafından kullanılabilecek sınıf tariflerini yüklemek için otomatik yüklemesini
üretecektir.
Eylemler kelimelerden daha iyi anlaşılır, öyleyse hemen bir örnek verelim.
1
"autoload": {
2
3
}
Burası bizim otomatik yükleme yapılandırmamızın tümünü kapsayacak olan kesimdir. Basit, değil
mi? Süper! Sizin için çorap kuklacılığı değil.
En basit yükleme mekanizması olan files yöntemine bakalım.
1
2
3
4
5
6
"autoload": {
"files": [
"path/to/my/firstfile.php",
"path/to/my/secondfile.php"
]
}
Bu files yükleme mekanizması, uygulamanız içinde Composer otomatik yükleyicisi bileşeni
yüklendiği zaman yüklenecek dosyaları bir dizi olarak verir. Dosya yolları uygulamanızın kök
dizinine göreli kabul edilir. Bu yükleme yöntemi etkilidir ancak çok uygun değil. Büyük bir proje
için her dosyayı elle tek tek eklemek istemezsiniz. Daha büyük miktarlarda dosya yüklemenin biraz
daha iyi yöntemlerine bakalım şimdi de.
Ön Bilgiler
1
2
3
4
5
6
23
"autoload": {
"classmap": [
"src/Models",
"src/Controllers"
]
}
classmap bir dizi alan başka bir yükleme mekanizmasıdır. Bu seferki dizi değişik sayıdaki dizinlerden
ibarettir ve yine aynı şekilde projenin köküne görelidirler.
Composer, otomatik yükleyicisinin kodunu üretirken, PHP sınıflarını içeren dosyaları bulmak için
bu dizinleri dolaşacaktır. Bu dosyalar bir dosya yolunun bir sınıf adına eşleştirildiği bir koleksiyona
eklenecektir. Bir uygulama Composer otomatik yükleyicisi kullandığı ve mevcut olmayan bir sınıf
başlatmak istediği zaman, Composer devreye girecek ve bu eşleştirmede saklanan bilgiyi kullanarak,
gerekli sınıf tarifini yükleyecektir.
Ancak, bu yükleme mekanizmasını kullanmanın bir dezavantajı var. Yeni bir dosya eklediğiniz her
zaman, sınıf eşleştirmesini yeniden inşa etmek için composer dump-autoload komutunu kullanmanız gerekecektir. Neyse ki, hepsinin en iyisi olan son bir yükleme mekanizması bulunmaktadır ve
bir eşleştirme istemeyecek kadar zekidir. Öncelikle PSR-0 sınıf yükleme konusunda bilgi verelim.
PSR-0 sınıf yüklemesi ilk kez PSR-0 PHP standardında tanımlanmıştır ve PHP aduzaylı sınıfları,
içinde bulundukları dosyalara eşleştirmede basit bir yol sağlamaktadır.
Sınıfınızı içeren dosyaya bir namespace deklarasyonu eklemeyi biliyor olmalısınız, bunun gibi:
1
<?php
2
3
namespace Xmen;
4
5
6
7
8
class Wolverine
{
// ...
}
Ondan sonra, bu sınıf Xmen\Wolverine haline gelir ve PHP artık Wolverine sınıfına tamamen farklı
bir hayvan muamelesi yapacaktır.
PSR-0 otomatik yüklemesi kullanılınca, Xmen\Wolverine sınıfı Xmen/Wolverine.php dosyası içinde
olmalıdır.
Namespace’in sınıfın içinde olduğu dizin ile eşleştiğini görebildin mi? Xmen aduzayının Wolverine
sınıfı Xmen dizininde yer almaktadır.
Ayrıca, dosya adının da sınıf adıyla (büyük harfler de dahil) eşleştiğini farketmişsinizdir. PSR-0
otoyüklemenin düzgün çalışması için sınıf adıyla dosya adının aynı olması şarttır.
Aduzayları birkaç seviyeli olabilir, örneğin, aşağıdaki sınıfı ele alalım.
Ön Bilgiler
1
24
<?php
2
3
namespace Super\Happy\Fun;
4
5
6
7
8
class Time
{
// ...
}
Time sınıfı Super\Happy\Fun aduzayı içinde bulunmaktadır. Dolayıyla PHP onu Time olarak değil
Super\Happy\Fun\Time olarak tanıyacaktır.
Bu sınıf aşağıdaki dosya yolunda yer alıyor olacaktır.
1
Super/Happy/Fun/Time.php
Aduzayı ile dizin yapısının nasıl uyduğunu bir daha gördünüz mü? Ayrıca, dosya adının da sınıfla
tam olarak aynı olduğu dikkatinizi çekmiştir.
PSR-0 otoyüklemesi bu kadar. Gerçekten oldukça basit! Şimdi sınıf yüklememizi basitleştirmek için
bunu Composer ile nasıl kullanabileceğimizi görelim.
1
2
3
4
5
"autoload": {
"psr-0": {
"Super\\Happy\\Fun\\Time": "src/"
}
}
Bu sefer, bizim psr-0 otoyükleme bloğumuz bir dizi olmak yerine bir nesnedir. Bunun sebebi hem
bir anahtar, hem de bir değer gerekmesidir.
Bu nesnedeki her anahtar bir aduzayını temsil eder. Çift ters bölüler konusunda endişe etmeyin. Tek
ters bölü JSON’da escape karakterini temsil ettiği için bu şekilde kullanıyoruz. JSON dosyalarında
aduzaylarını eşleştirirken bu kuralı unutmayın!
İkinci değer aduzayının eşleşeceği dizindir. Ben sondaki bölünün aslında gerekli olmadığını keşfettim, ama birçok örnek bir dizini belirtmek için bölüyü de eklemeyi seviyor.
Bundan sonrası çok önemlidir ve birçok insanın yakalandığı ciddi bir tuzaktır. Lütfen dikkatli
okuyun.
Bu ikinci parametre, aduzayı sınıflarının yerleşik olduğu dizin değildir. Onun yerine, aduzayının
dizin eşleştirmeye başladığı dizindir. Bunu daha iyi açıklamak için önceki örneği inceleyelim.
Super happy fun time sınıfını hatırlayın. Bir daha bakalım.
25
Ön Bilgiler
1
<?php
2
3
namespace Super\Happy\Fun;
4
5
6
7
8
class Time
{
// ...
}
Tamam, biz şimdi biliyoruz ki, bu sınıf Super/Happy/Fun/Time.php dosyasında yerleştirilmiş
olacaktır. Bunu aklımızda tutarak, aşağıdaki otomatik yükleme kod parçacığını ele alalım.
1
2
3
4
5
"autoload": {
"psr-0": {
"Super\\Happy\\Fun\\Time": "src/"
}
}
Sen Composer’in sınıfı src/Time.php içinde arayacağını bekleyebilirsin. Bu yanlış olacaktır ve sınıf
bulunamayacaktır.
Bunun yerine, dizin yapısı şu biçimde mevcut olmalıdır.
1
src/Super/Happy/Fun/Time.php
Bu, birçok insanın Composer’i ilk kullandığında yakalandığı bir şeydir. Bu gerçeği akılda tutmanın
ne kadar önemli olduğunu yeterince vurguladığımı sanıyorum.
Eğer biz Commposer’ın bir yüklemesini şu anda çalıştırmış olsak ve daha sonra aynı aduzayına yeni
bir sınıf, Life.php eklersek, otomatik yükleyiciyi yeniden üretmek zorunda olmayacağız. Composer,
aduzayının sınıflarının olduğu yeri ve onları nasıl yükleyeceğini tam olarak bilir. Harika!
Aduzayı dosyalarımı niçin bir src klasörüne koyduğumu merak etmiş olabilirsiniz. Composer
tabanlı kitaplık yazarken bu sık kullanılan bir gelenektir. Aslında, bir Composer paketi için sık
kullanılan bir dizin/dosya yapısı şöyledir.
1
2
3
4
src/
tests/
docs/
composer.json
(Sınıflar.)
(Unit/Kabul testleri.)
(Belgeler.)
Bu standarda bağlı kalmakta ya da sizi ne mutlu edecekse onu yapmakta özgürsünüz. Laravel kendi
sınıfları için kendi konumlarını vermektedir ve ilerideki bir bölümde bunları açıklayacağım.
Otomatik yükleme mekanizmalarımızı nasıl tanımlayacağımızı artık öğrendik, Composeri nasıl
yükleyeceğimizi ve kullanacağımızı öğrenmenin vakti geldi, otomatik yükleyicinin avantajını
kullanmaya başlayabiliriz.
Ön Bilgiler
26
Yükleme
Yükleme ve kullanım konusunu neden bu bölümün en sonuna bıraktığımı merak ediyorsunuzdur?
Yapılandırma konusunu iyi bilmenin, Composer’in biz onu kullanırken sahne arkasında neler
yapıyor olduğunu anlamamıza yardım edeceğini düşündüm. Bırakın bileyim!
Aşağıdaki yükleme yöntemleri Linux veya Mac OSX gibi unix tabanlı geliştirme çevrelerine özgüdür.
Umarım Taylor bu bölüme düzenleme yapabilir de bir Windows ortamında Composer yükleme
konusunda bilgi verir, çünkü ben belirli işletim sistemlerinden vebadan kaçar gibi kaçıyorum.
Composer PHP tabanlı bir uygulamadır, dolayısıyla onu kullanabilmeniz için PHP CLI istemcisinin
yüklü olması gerekiyor. Aşağıdaki komutu çalıştırarak bunu iki kez kontrol edin.
1
php -v
Şayet PHP düzgünce yüklenmiş ise, şuna benzer bir şeyler göreceksiniz.
1
2
3
4
5
$ php -v
PHP 5.4.4 (cli) (built: Jul 4 2012 17:28:56)
Copyright (c) 1997-2012 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2012 Zend Technologies
with XCache v2.0.0, Copyright (c) 2005-2012, by mOo
Eğer çıktı sizin PHP sürüm 5.3.2’den daha küçüğünü kullandığınızı söylüyorsa, PHP sürümünüzü
güncellemediğiniz sürece Composer kullanamayacaksınız demektir. Aslında, eğer PHP 5.3.7’ten
daha düşük bir sürüm kullanıyorsanız, bu durumda Laravel kullanmanız hiç mümkün olmayacaktır.
Composer’i indirmek için CURL kullanabilirsiniz. Mac OSX ön tanımlı olarak böyle gelir. Birçok
Linux dağıtımları kendi yazılım ambarları içinde CURL bulundurur, tabii bir standart olarak zaten
yüklenmiş değilse. Composer’ın uygulamasını indirmek için CURL kullanalım.
1
curl -sS https://getcomposer.org/installer | php
Deneyimli linux kullanıcıları CURL’un yükleme skriptini PHP’ye bağlamasından endişe duyabilirler. Bu haklı bir endişedir, fakat Composer yüklemesi binlerce geliştirici tarafından kullanılmıştır ve
güvenli olduğu kanıtlanmıştır. Bu güzelim yazılım parçasını kullanmaktan uzak durmayalım!
Yükleme sürecinin başarıyla tamamlandığını (o size söyleyecektir) kabul ederek, şimdi uygulama
dizininizde bir composer.phar dosyanız olacak. Composer’i çalıştırmak için kullanacağınız dosya
işte budur, örneğin
1
php composer.phar
27
Ön Bilgiler
mevcut komutların bir listesini size gösterecektir.
Artık bu şekilde Composer kullanmaya devam edebilirsiniz ancak ben onu global olarak yüklemenizi önereceğim. Böyle yaptığınızda Composer projelerinizin tümünde kullanabileceksiniz ve onu
çalıştırmak için daha kısa bir komut yeterli olacaktır.
Composer’i global tarzda yüklemek için, onu kendi PATH ortam değişkeninin içindeki bir konuma
taşıyınız. Bu yerleşimleri aşağıdaki komutu kullanarak görebilirsiniz.
1
echo $PATH
Bununla birlikte çoğu sistemde kabul edilebilir bir konum /usr/local/bin‘dir. Bu dosyayı taşıdığımız zaman, aynı zamanda onu daha kolay çalıştırmak için adını da composer olarak değiştirmiş
oluyoruz. Bunun için gerekli komut şudur:
1
sudo mv composer.phar /usr/local/bin/composer
Composer’i global tarzda yüklemekle, artık aynı komutlar listesini görmek için aşağıdaki kısa
sözdizimini kullanabileceğiz. Bu komut aynı zamanda sisteminizdeki her yerden çalıştırılabilecektir.
1
composer
Hey, bu çok daha temiz, değil mi? Ee, kullanalım o zaman.
Kullanım
Paket dizinimizde aşağıdaki composer.json dosyasını oluşturmuş olduğumuzu varsayalım.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"name":
"description":
"keywords":
"homepage":
"time":
"license":
"authors": [
{
"name":
"email":
"homepage":
"role":
}
"marvel/xmen",
"Mutants saving the world for people who hate them."
["mutant", "superhero", "bald", "guy"],
"http://marvel.com/xmen",
"1963-09-01",
"MIT",
"Stan Lee",
"[email protected]",
"http://marvel.com",
"Genius"
28
Ön Bilgiler
],
"require": {
"xmen/wolverine":
"xmen/cyclops":
"xmen/storm":
"xmen/gambit":
},
"autoload": {
"classmap": [
"src/Xmen"
]
}
15
16
17
18
19
20
21
22
23
24
25
26
27
"1.0.0",
"1.0.1",
"1.2.0",
"1.0.0"
}
İlerleyelim ve paket bağımlılıklarımızı yüklemek ve otomatik yükleyicimizi kurmak için install
komutunu kullanalım.
1
composer install
Composer’den alacağımız çıktı şöyle bir şey olacaktır:
1
2
Loading composer repositories with package information
Installing dependencies
3
4
5
- Installing tools/claws (1.1.0)
Cloning bc0e1f0cc285127a38c232132132121a2fd53e94
6
7
8
- Installing tools/yellow-mask (1.1.0)
Cloning bc0e1f0cc285127a38c6c12312325dba2fd53e95
9
10
11
- Installing power/regeneration (1.0.0)
Cloning bc0e1f0cc2851213313128ea88bc5dba2fd53e94
12
13
14
- Installing xmen/wolverine (1.0.0)
Cloning bc0e1f0cc285127a38c6c8ea88bc523523523535
15
16
17
- Installing xmen/cyclops (1.0.1)
Cloning bc0e1f0cc2851272343248ea88bc5dba2fd54353
18
19
20
21
- Installing xmen/storm (1.2.0)
Cloning bc0e1f0cc285127a38c6c8ea88bc5dba2fd53343
Ön Bilgiler
22
23
29
- Installing xmen/gambit (1.0.0)
Cloning bc0e1f0cc285127a38c6c8ea88bc5dba2fd56642
24
25
26
Writing lock file
Generating autoload files
Bunların bir örnek olarak kullanılan uyduruk paketler olduğunu unutmayın. Bunları indirmek işe
yaramaz! Bununla birlikte, X-men olmaları nedeniyle daha eğlenceli olabilir! Yaa!
Peki listede sadece dört tane varken neden yedi paket yüklendi? İyi de, Composer’ın bağımlılıkların bağımlılıklarını otomatik olarak yönettiğini unutuyorsunuz! Fazladan gördüğünüz üç paket
xmen/wolverine paketinin bağımlılıklarıdır.
Ben sizin şimdi büyük bir ihtimalle bu paketlerin nereye yüklendiğini merak ettiğinizi düşünüyorum.
Composer paketinizin kaynak dosyalarını içermek üzere projenizin kök dizininde bir vendor dizini
oluşturur.
xmen/wolverine paketi vendor/xmen/wolverine‘de bulunabilir, orada onun kaynak dosyalarını ve
kendi composer.json dosyasını bulacaksınız.
Composer ayrıca otomatik yükleme sistemiyle ilgili kendi dosyalarından bir kısmını da vendor/composer
dizininde depolar. Dert etmeyin. Bunu hiç bir zaman doğrudan düzenlemek zorunda olmayacaksınız.
Peki biz bu harika otomatik yükleme yetilerinin avantajını nasıl elde edeceğiz? Bunun cevabı
otoyüklemenin kendisini kurmaktan bile basit. Sadece uygulamanız içinde vendor/autoload.php
doyasını require() veya include() ediniz. Örneğin:
1
<?php
2
3
require 'vendor/autoload.php';
4
5
// Harika uygulama bootstrap'ı işte burada!
Müthiş! Artık, bağımlılıklarınızdan birine ait bir sınıfı başlatabilirsiniz, örneğin.
1
<?php
2
3
$gambit = new \Xmen\Gambit;
Composer bütün sihri yapacak ve sınıf tarifini sizin için otomatik yükleyecektir. Ne kadar harika
değil mi? Artık kaynak dosyalarınızı binlerce include() cümlesiyle karıştırmayacaksınız.
Eğer bir sınıf-eşleştirme dizinine bir dosya eklemişseniz, Composer’in onu yükleyebilmesi için
öncelikle bir komut çalıştırmanız gerekecektir.
Ön Bilgiler
1
30
composer dump-autoload
Yukarıdaki komut tüm eşleştirmeleri yeniden inşa edecek ve sizin için yeni bir autoload.php
oluşturacaktır.
Projemize yeni bir bağımlılık eklemek istersek ne yapacağız? Şimdi composer.json dosyamıza
xmen/beast‘i ekleyelim.
1
{
"name":
"marvel/xmen",
"description":
"Mutants saving the world for people who hate them.",
"keywords":
["mutant", "superhero", "bald", "guy"],
"homepage":
"http://marvel.com/xmen",
"time":
"1963-09-01",
"license":
"MIT",
"authors": [
{
"name":
"Stan Lee",
"email":
"[email protected]",
"homepage":
"http://marvel.com",
"role":
"Genius"
}
],
"require": {
"xmen/wolverine":
"1.0.0",
"xmen/cyclops":
"1.0.1",
"xmen/storm":
"1.2.0",
"xmen/gambit":
"1.0.0",
"xmen/beast":
"1.0.0"
},
"autoload": {
"classmap": [
"src/Xmen"
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
}
Şimdi, Composer’in yeni eklediğimiz paketi yükleyebilmesi için composer install‘i tekrar çalıştırmamız gerekiyor.
Ön Bilgiler
1
2
31
Loading composer repositories with package information
Installing dependencies
3
4
5
- Installing xmen/beast (1.1.0)
Cloning bc0e1f0c34343347a38c232132132121a2fd53e94
6
7
8
Writing lock file
Generating autoload files
Artık xmen/beast yüklendi ve onu hemen kullanabiliriz. Olağanüstü!
composer install komutunun çıktısındaki aşağıdaki satır dikkatinizi çekmiş olabilir.
1
Writing lock file
Aynı zamanda, Composer’ın uygulamanızın kök dizininde composer.lock adlı bir dosya oluşturduğunu da fark etmiş olabilirsiniz. Ağladığınızı mı duyuyorum?
Bu composer.lock dosyası paketiniz hakkında composer install veya composer update komutunun en son gerçekleştirilme zamanı hakkında bilgiler içerir. Ayrıca, yüklenmiş olan her bağımlılığın
tam sürümünden oluşan bir liste de içerir.
Neden ki? Çok basit. Dizin içinde bir composer.lock dosyası bulunduğunda, composer install
komutunu kullandığınız zaman, her bağımlığın taze sürümlerini çekmek yerine bu dosya içinde
bulunan sürümleri kullanacaktır.
Bunun anlamı şudur: Eğer sürümünüz uygulama kaynağınızla birlikte composer.lock dosyasını
içeriyorsa (bunu kuvvetle öneririm), üretim ortamını dağıttığınız zaman, lokal geliştirme ortamında
denediğiniz ve test ettiğinizle tam aynı sürümler kullanılacaktır. Yani, Composer’in uygulamanızı
kırabilecek herhangi bir bağımlılık sürümünü yüklemeyeceğinden emin olabileceksiniz.
composer.lock dosyasının hiç bir zaman elle düzenlenmemesi gerektiğini unutmayın.
Bağımlılık sürümleri konusundayken, bunların güncellenmesi hususu niye yoktu? Örneğin composer.json
dosyamızda aşağıdaki gereksinim olsaydı.
1
"xmen/gambit": "1.0.*"
Composer bizim için 1.0.0 sürümünü yükleyebilecektir. Ancak, birkaç gün sonra bu paket 1.0.1
sürümüne güncellenmişse ne olacak?
Peki o zaman, tüm bağımlılıklarımızı son sürümlerine güncellemek için composer update komutunu
kullanabiliriz. Çıktıya bir bakalım.
Ön Bilgiler
1
2
3
32
$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
4
5
6
- Installing xmen/gambit (1.0.1)
Cloning bc0e1f0cc285127a38c6c8ea88bc5dba2fd56642
7
8
Generating autoload files
Harika! xmen/gambit paketi en son sürümüne güncellendi ve bizim composer.lock dosyası güncellendi.
Tümünü değil de sadece bir bağımlılığı güncellemek istersek, update komutunu kullanırken paket
adını belirtebiliriz. Örneğin:
1
composer update xmen/gambit
Durun, oradaki (eklenen require-dev) ne demek oluyor? Hatırlarsanız composer.json dosyasındaki require-dev kesiminde biz sadece geliştirmeye özgü bağımlılıkları listeliyorduk. Composer,
update komutunun sadece güvenli bir geliştirme veya test ortamında çalıştırılmasını bekler. Bu
sebeple, geliştirme bağımlılıklarınızı isteyeceğinizi varsayacak ve onları indirecektir.
Eğer geliştirme bağımlılıklarınızı yüklemek istemiyorsanız, aşağıdaki anahtarı kullanabilirsiniz.
1
composer update --no-dev
Ayrıca, install komutunu kullanırken bağımlılıkları yüklemek istiyorsanız, basitçe şu anahtarı
kullanın.
1
composer install --dev
Bilmeniz gereken son şey, Composer binarisinin kendini güncellemek için composer self-update
komutunu kullanabilecek olmanızdır. Global olarak yüklemek istiyorsanız, sudo kullanmayı unutmayın.
1
sudo composer self-update
Tamam, Laravel’le çalışırken ihtiyacınız olacak tüm Composer bilgisi bu kadar. Alınması gereken
çok fazla bilgi ve bu yorgun parmaklar için uzun bir bölüm oldu. Umarım ondan bir şeyler
kazanmışsınızdır.
Belirli bir konunun genişletilmesi gerektiğini düşünüyorsanız, bana bildirmeyi unutmayın!
Mimari
Code Bright’ı daha kapsamlı bir öğrenme deneyimi yapmak istiyorum ve bu sebeple bu bölümü de
dahil etmeye karar verdim. Kodlamaya bir an önce başlamak istiyorsanız, bu bölümü atlayabilirsiniz.
Yine de ben, Laravel’in nasıl inşa edildiğini öğrenmek için aşağıdaki satırların gerçekten yararlı
olacağını düşünüyorum.
Laravel 3’te, IoC konteyneri çoğu insanın kafasını karıştıran bir bileşen idi. Laravel 4’te ise çatının her
şeyi bir arada tutan kısmıdır. Bunun önemini ifade edemem. Evet, aslında anlamını kavrayabilmek
için bu bölüme gerek duydum. Bir giriş yapalım bakalım.
Container (Konteyner)
Laravel 4’te, konteyner bootstrap sürecinde erkenden oluşturulur. $app adı verilen bir nesnedir. Peki
bir konteyner’den söz ettiğimizde aklınıza ne geliyor?
Hadi sözlük tanımına bakalım. Niçin? Çünkü emin değilim… Diğer yazarların da böyle yaptıklarını
gördüm. Beni küçük görmelerini istemem. İşte burada.
İsim - Container. Özellikle nakliye veya depolama işlerinde, muhafaza için kullanılan
veya muhafaza kapasitesindeki bir nesne, örneğin kutu, karton kutu vb.
Ben bu tarifin Laravel container bileşenine tam uyduğunu düşünüyorum. Kesinlikle bir şeyler
depolamakta kullanılıyor. Dürüst olalım… Bir şeyleri depolamasaydı, o zaman ‘Konteyner’ berbat
bir isim olurdu. Taşımacılık bakımından da, diğer bileşenlere erişmemiz gerektiğinde onları çatı
çevresinde “taşımayı” kolaylaştırıyor.
Esasen bir karton ya da kutu gibi değildir. Daha ziyade, anahtarlı bir depolama sistemidir.
Bu $app nesnesi oluşturulduktan sonra, app() helper metodunu kullanarak ona her yerden erişebilirsiniz. Tüm samimiyetimle söylüyorum, büyük ihtimalle bunu yapmak zorunda olmayacaksınız.
Bu, bu bölümde daha ilerde açıklayacağım bir özellik nedeniyledir.
app nesnesinin tuttuklarını alalım ve ne olduklarına bir bakalım. Bunu app/routes.php dosyanızda
yapabilirsiniz.
Mimari
1
34
<?php
2
3
// app/routes.php
4
5
var_dump($app);
6
7
die();
app/routes.php dosyası içerisinde app() helperini kullanmak zorunda olmadığımız dikkatinizi
çekmiştir. O, global kapsamdadır, dolayısıyla doğrudan erişebiliriz.
Vuuaahh, bu ne büyük bir nesne böyle! Hııı, sanırım bu… framework’ün özü olmalı. Onun
Illuminate\Foundation\Application sınıfının bir olgusu olduğunu göreceksiniz. Laravel 4 bileşenlerinin tamamı Illuminate aduzayı içerisinde yaşar. Bu, çatının erken tasarım evrelerinde kullanılan
bir kod adı idi ve henüz çıkarmaya yüreğimiz el vermedi. Laravel’i PHP dünyasını “illuminating”
(aydınlatan) olarak düşünün!
Application sınıfının kaynaklarına bakarsak, Container‘i genişlettiğini göreceğiz. Buyrun, o kay-
nak şurada:
https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Application.php¹⁵
Container sınıfı bütün sihrin gerçekleştiği yerdir. Ayrıca, ArrayAccess arayüzüne de bir uygulama
getirir. ArrayAccess interface, bir nesnenin niteliklerine JavaScript nesne değişmezlerine benzer
şekilde erişilebilmesine imkan veren özel bir arayüzdür. Örnek olarak, aşağıda verilen yöntemlerin
her ikisiyle de nesne niteliklerine erişmek mümkün olacaktır:
1
<?php
2
3
4
$nesne->nitelik = 'falan';
$nesne['nitelik'] = 'falan';
Frameworkün bootstrapı sırasında çok sayıda hizmet sağlayıcı sınıfı çalışır. Bu sınıflar, çatının belirli
bileşenlerinin uygulama konteyneri içinde kayıt edilmesi amacını yerine getirirler.
Pekiyi, bileşen deyince ne anlayacağım? Şöyle ki, bir framework birçok farklı parçadan yapılmıştır.
Örnek olarak, bizim bir rotalama katmanımız, bir geçerlilik denetimi sistemimiz, bir kimlik doğrulama katmanımız ve spesifik bir işlevi yerine getiren daha birçok paketimiz vardır. Biz bu framework
bileşenlerimizi çağırırız ve onlar tam frameworkü oluşturmak üzere konteyner içerisinde bir araya
gelirler.
Laravel’i ilk defa kullanıyorsanız, bir sonraki örnek size pek bir his vermeyecek. Üzerinde durmayın!
O sadece Laravel 3 kullanıcılarının düşüneceği bir şey. Şuna bir göz atın.
¹⁵https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Application.php
Mimari
1
35
<?php
2
3
// app/routes.php
4
5
6
7
8
$app['router']->get('/', function()
{
return 'Code Bright\'ı satın aldığınız için teşekkürler!';
});
Burada biz bir ‘GET’ HTTP isteğine cevap verecek bir rota oluşturmak için konteyner içindeki
rotalama katmanına erişiyoruz. Bu, Laravel 3 kullanıcılarına tanıdık gelecektir, gerçi sözdizimi biraz
garip görünebilir.
Gördüğünüz gibi, bir dizi sözdizimi kullanarak, konteynerimizdeki rotalama bileşenine erişebiliyoruz. Bu, ArrayAccess interface tarafından sağlanan bir sihirdir. İstersek, rotalama katmanına şu
şekilde de erişebiliriz:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
$app->router->get('/', function()
{
return 'Cidden, Code Bright\'ı satın aldığınız için teşekkürler!';
});
İlgili bileşene konteynerimizin bir niteliği gibi erişilmesi de önceki metodla tam olarak aynı işi görür.
Çok da güzel değilmiş, öyle değil mi? Yani framework’te yeni iseniz, Laravel’in güzel sözdizimini
duymuşsundur. Daha önce Laravel 3 kullanmışsanız, onun zevkini zaten yaşıyorsunuzdur.
Umarım bu şöhret berbat olmaz, değil mi? Tabiki olmayacak! Bak daha sihirli eylemler de var.
Facades
Sevgili dostum Kenny Meyers, ilk kitabım ‘Code Happy’ için The Nerdary¹⁶‘de güzel bir yorum
yazmıştı. Bu iyiliğini ödemek isterim. Kendisi bize son Laravel konferansında belirli kelimeleri
telaffuz etmekte zorlandığını söyledi ve bu kelimenin de telaffuzu zor bir kelime olduğundan eminim. Bu yüzden, Kenny ve ilk dil olarak ingilizce konuşmayanlar için Facade’ın nasıl okunduğunu
gösteriyorum.
“fah-sahd”
Laravel 3’te, çoğu bileşene statik bir metod kullanılarak erişilirdi. Örneğin, son bölümdeki rotalama
örneği şu şekilde olabilirdi:
¹⁶http://www.thenerdary.net/
Mimari
1
36
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return 'Sağol Kenny, seni seviyoruz!';
});
Bu güzel görünüyor… Bileşen için tanımlayıcı bir isme ve sıklıkla bu bileşende yapılan eylemi tarif
eden bir fiile sahibiz.
Ne yazık ki, deneyimli geliştiriciler statik bir metod görünce korkacaktır. Gördüğünüz gibi, statik
metodları test etmek oldukça zor olabilir. Bunun neden böyle olduğunu açıklayarak daha kitabın
başlarında bir şeyleri karıştırmak istemiyorum, o yüzden, bana güvenmek durumundasınız.
Laravel 4 tasarımı sırasında bu büyük bir problem oluşturdu. Biz güzel ve basit sözdizimimizi
seviyorduk, ama aynı zamanda en iyi kodlama uygulamalarını benimsemek istiyorduk ve bu da
kodumuzun sağlamlığından emin olmak için pek çok test yazmamızı da içerir.
Neyseki, Taylor harika bir fikirle, adını ‘Facade’ tasarım deseninden alan Facade sınıfı fikriyle geldi.
Facade’ları kullanarak her iki dünyanın en iyisine sahip olduk. Hoş statik metodlar, ama publik
metodlarla başlatılan bileşenler. Bu, kodumuzun hem güzel, hem de son derece test edilebilir olduğu
anlamına gelir. Nasıl çalıştığını bir görelim.
Laravel 4’te kök aduzayına takma ad verilmiş çok sayıda sınıf bulunmaktadır. Bunlar bizim Facade
sınıflarımızdır ve bu sınıfların hepsi de Illuminate\Support\Facades\Facade sınıfını genişletirler.
Bu sınıf çok zekidir. Onun zekasını bazı kod örnekleriyle açıklayacağım. Gördüğünüz gibi, tıpkı eski
Laravel 3’teki gibi statik bir rotalama metodu kullanabiliyoruz, şöyle mesela:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return 'Facade\'lar için sağol Taylor!';
});
Bu örnekteki Facade ‘Route’ sınıfıdır. Her bir Facade konteynerde bir bileşenin bir olgusuna
bağlanmıştır. Bir Facade’ın statik metodları kısayollardır ve çağrıldıkları zaman, konteynerde temsil
ettikleri nesnenin ilgili publik metodunu çağırırlar. Yani, buradaki Route::get() metodu aslında
aşağıdaki metodu çağıracaktır:
Mimari
1
37
<?php
2
3
// app/routes.php
4
5
6
7
8
$app['router']->get('/', function()
{
return 'Facade\'lar için sağol Taylor!';
});
Bu neden önemlidir? Şöyle ki, gördüğünüz gibi bu büyük bir esneklik sağlar.
Esneklik (Flexibility)
Esneklik ne demek? Bu sefer sözlüğe bakmayacağım. Değişebilirlik anlamına geldiğini biliyorum.
Konteynerin faydalarını oldukça iyi açıklayan özelliklerden biridir. Göreceğiniz gibi, biz konteyner
içinde kayda geçirilmiş nesne ve bileşenleri değiştirebiliriz veya yerine başkasını koyabiliriz. Bu bize
büyük bir güç sunar.
Bir an için rotalama katmanının çalışma şeklini gerçekten sevmediğimizi düşünelim (merak etmeyin,
çok seveceksiniz, sadece hayal edin).
Biz hepimiz epik kodlayıcılar olduğumuza göre, doğruca gidip, SuperRouter adında ana sınıfı olan
kendi rotalama katmanımızı yazalım. Konteynerin esnekliğine bağlı olarak, rotalama katmanının
yerine kendi yazdığımızı koyabiliriz. Tüm samimiyetimle söylüyorum, bu düşünebildiğiniz kadar
kolaydır. Biz sadece mevcut rotanın indeksine kendi rotamızı atayacağız. Ooo, lütfen dikkat edin,
özellikle acemiyseniz bunu şimdi yapmanızı önermiyorum. Sadece büyük bir örnek olduğu için
gösteriyorum.
1
<?php
2
3
// app/routes.php
4
5
$app['router'] = new SuperRouter();
Şimdi, kendi rotamızı sadece konteyner içinde doğrudan erişmek suretiyle kullanmıyoruz, aynı
zamanda, bizim Route Facade’mız da bu bileşene erişmeye devam edecek.
Aslında olanlar daha etkileyicidir. Laravel’in bileşenlerini kendinizinkiyle değiştirdiğiniz zaman, çatı
kendi görevleri için sizin koyduğunuz bileşeni kullanmaya çalışacaktır, tıpkı kendi bileşenleri gibi.
Çekirdek çatı bileşenlerine bu şekilde erişilebilmesi, size büyük bir güç bahşetmektedir. Eminim
şımardınız, değil mi!
Mimari
38
Sağlamlık (Robustness)
Daha önce birkaç kez bahsettiğim gibi, Laravel tek tek bileşenler kümesinden meydana gelir. Her
bileşen kendi küçük işlevselliğinden sorumludur. Bunun anlamı, bunların tek sorumluluk ilkesine
çok saygılı olduklarıdır.
Gerçekten de, Laravel bileşenlerinin birçoğu, çatıdan tamamen ayrılmış olarak, kendi başlarına
hareket edebilirler. Bu sebeple, github’taki Illuminate organizasyonu¹⁷ altında bileşenlerin kopyaları
bulunabilir. Bu bileşenlerden bir takım aynı zamanda Packagist’de¹⁸ bulunmaktadır, dolayısıyla
Composeri kullanarak kendi projelerinizde bunların avantajından yararlanabilirsiniz.
Peki buradaki Sağlamlık nedir? Çok iyi gördüğünüz gibi, Laravel tarafından kullanılan her bileşen
iyi test edilir. Tüm framework 900 test ve 1700 iddianın üzerinde bir paket barındırır.
Bu testler sayesinde, bir şey kırılacak mı ya da mevcut işlevsellik bozulacak mı gibi bir endişe
duymadan çatı için verimli bir gelecek sağlayacak katkıları kabul edebilir ve değişiklik yapabiliriz.
Tamam, mimari hakkında bu kadar yeter. Bence geliştirmeye geçmeye artık hazırsınız. Hadi
başlayalım!
¹⁷https://github.com/illuminate
¹⁸https://packagist.org/
Başlarken
Laravel PHP programlama dili için bir çatıdır. PHP sözdizimi pek çekici değilken, kullanımı
kolaydır, dağıtımı kolaydır ve her gün kullandığımız modern web sitelerinin bir çoğuna güç verdiği
görülebilir. Laravel sizin web tabanlı projelerinizin tümünde başarı kazanmanıza yardımcı olmak
için sadece yararlı kestirme yollar, aletler ve bileşenler sağlamakla kalmayıp, aynı zamanda bazı
PHP kusurlarını düzeltmeyi de hedeflemektedir.
Laravel çok sayıda PHP frameworkleri arasında kendini öne çıkarmasını sağlayan güzel, anlamlı ve
üretken bir sözdizimine sahiptir. Güç ve verimlilikten ödün vermeden, PHP’yi kullanımı keyifli bir
hale getirir. Laravel hem amatör projeler hem de kurumsal çözümler için mükemmel bir seçimdir
ve bu dilin tecrübeli bir profesyoneli de olsanız, yenisi de olsanız, Code Bright fikirlerinizi tam
fonksiyonlu web uygulamaları haline çevirmenize yardım edecektir.
Hem çatı hem de bu kitap için gereksinimlere hızlıca bir göz atalım.
Gereksinimler
• Bir Bilgisayar Okumak harikadır ancak kitapta bulacağınız örneklerle oynamakla daha çok
öğreneceksiniz.
• Bir Web Sunucu Laravel bir web sunucusu gerektirir. Hangisini kullandığınızın bir önemi
yoktur ancak topluluğun büyük çoğunluğunun Apache ya da Nginx kullandığını gördüm ve
aynısını yaparsak ihtiyacımız olduğunda daha kolay destek bulabiliriz.
• PHP: Hypertext Preprocessor 5.3 veya üstü Laravel bir PHP çatısıdır, PHP programlama
dilini gerektirir. Bunun için bana güvenin. Laravel bu dilin bazı modern özelliklerini de
kullandığı için, aynı zamanda 5.3.7 veya üstü sürüm gerekecektir. Çoğu web sunucuda ya
konsolda php -v yazarak ya da phpinfo() metodunu kullanarak kullanmakta olduğunuz PHP
versiyonunu öğrenebilirsiniz.
• Bir Veritabanı Sürücüsü Çatının bir gereksinimi olmamakla birlikte, kitaptaki örneklerin
birçoğu bir veritabanı ile etkileşmektedir. Bu sebeple, PDO bağlayıcıyla desteklenen bir
veritabanı sunucusu kurmanızı önereceğim. Benim önerim Su.. Oracle’ın esnek ve parasız
MySQL‘ini kullanmanızdır. Diğer popüler veritabanı sunucuları arasında SQL Server, Postgres
ve SQLite sayılabilir.
• Bir Metin Editörü Kitapta bulunan örneklerle oynamak için gereklidir. Ben Sublime Text 2
kullanmanızı öneriyorum, parasız değildir ama son derece seksidir. Bununla birlikte piyasada
milyonlarca editör ve IDE vardır, çalışma şeklinize uyan birini bulun.
Laravel projelerimiz üzerinde çalışmaya başlamadan önce, frameworkün bir kopyasını indirmemiz
gerekiyor.
Başlarken
40
Yükleme
Yükleme kelimesinin bu kesim için doğru bir başlık olduğundan emin değilim. Ancak daha iyisini
gerçekten düşünemiyorum.
Görüyorsunuz, Laravel yeni Laravel uygulamanız için bir çeşit ‘şablon’ gibi davranan bir Github
ambarına sahiptir. Lokal makinemize bir kopya indirelim.
Geliştirme web sunucumuzdaki bir klasöre indirilecek ambarı ‘clone’lamak için git kullanacağız.
Komut şudur.
1
git clone git://github.com/laravel/laravel.git projem
Şimdi projem klasörü içinde Laravel için bir şablon uygulamanız olacak. Diğerlerinin bu şablona
‘app package’ dediğini duyabilirsiniz. Bu paket sizin bütün uygulamanızı taşıyacaktır ve tüm dizin
muhtemelen versiyonlanacaktır.
Bazı tecrübeli Laravel kullanııları çatıyı güçlendiren dosyaları taşımakta kullanılan laravel adındaki bir dizini hatırlayabilirler. Biz bazen bunu çatının çekirdek (‘core’) dosyaları olarak ifade edeceğiz.
Evet, bu dizin artık bulunmuyor. Composer’ın gücünden yararlanarak, çatının çekirdek dosyaları
artık ayrı bir paket olarak mevcuttur, yani bizim şablon paketimizin bir bağımlığıdır.
Nelerin yeni olduğunu görmek için projem dizinimizin içindeki composer.json dosyasına bakalım.
1
{
"require": {
"laravel/framework": "4.0.*"
},
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php"
]
},
"scripts": {
"post-update-cmd": "php artisan optimize"
},
"minimum-stability": "dev"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
}
Başlarken
41
Bu kesim son güncellendiğinden bu yana bağımlılık sürümlerinin değişmiş olabileceğini unutmayın.
Ancak, sonuçlar yine de aynı kalacaktır.
Bizim uygulama paketimiz sadece laravel/framework paketine bağımlıdır, bu paket sizin uygulamanızı güçlendirmek için gerekli tüm bileşenleri içermektedir. Bu composer.json dosyası bizimdir.
Uygulamamız içindir ve istersek onu düzenleyebiliriz. Ancak, sizin için bazı makul ön tanımlı
değerler verilmiştir. Ayrıca, laravel/framework paketini çıkarmanızı hiç önermem. Çok kötü şeyler
olacaktır.
Şu anda elimizde sadece bir şablon var. Çatının çekirdeğini yüklemek için composer install
komutunu çalıştıralım.
1
2
3
4
Loading composer repositories with package information
Installing dependencies
- Installing doctrine/lexer (dev-master bc0e1f0)
Cloning bc0e1f0cc285127a38c6c8ea88bc5dba2fd53e94
5
6
7
- Installing doctrine/annotations (v1.1)
Loading from cache
8
9
... Burada daha birçok paket olacaktır. ...
10
11
12
- Installing ircmaxell/password-compat (1.0.x-dev v1.0.0)
Cloning v1.0.0
13
14
15
- Installing swiftmailer/swiftmailer (dev-master e77eb35)
Cloning e77eb358a1aa7157afb922f33e2afc22f6a7bef2
16
17
18
- Installing laravel/framework (dev-master 227f5b8)
Cloning 227f5b85cc2201b6330a8f7ea75f0093a311fe3b
19
20
21
22
23
24
25
26
27
28
29
30
31
32
predis/predis suggests installing ext-phpiredis (Allows faster serialization and \
deserialization of the Redis protocol)
symfony/translation suggests installing symfony/config (2.2.*)
symfony/translation suggests installing symfony/yaml (2.2.*)
symfony/routing suggests installing symfony/config (2.2.*)
symfony/routing suggests installing symfony/yaml (2.2.*)
symfony/event-dispatcher suggests installing symfony/dependency-injection (2.2.*)
symfony/http-kernel suggests installing symfony/class-loader (2.2.*)
symfony/http-kernel suggests installing symfony/config (2.2.*)
symfony/http-kernel suggests installing symfony/dependency-injection (2.2.*)
symfony/debug suggests installing symfony/class-loader (~2.1)
monolog/monolog suggests installing mlehner/gelf-php (Allow sending log messages \
to a GrayLog2 server)
42
Başlarken
33
34
35
36
37
38
39
40
41
42
monolog/monolog suggests installing
QP server (1.0+ required))
monolog/monolog suggests installing
ngoDB server)
monolog/monolog suggests installing
to a CouchDB server)
monolog/monolog suggests installing
Sentry server)
Writing lock file
Generating autoload files
ext-amqp (Allow sending log messages to an AM\
ext-mongo (Allow sending log messages to a Mo\
doctrine/couchdb (Allow sending log messages \
raven/raven (Allow sending log messages to a \
Tekrar söylüyorum, paket sürümleri değişmiş olabilir ancak sonuçlar yine aynısı olacaktır.
Bu paket listesi ne kadar uzun böyle! Onlar ne için? Peki şöyle ifade edeyim, Laravel açık kaynağın
gücünü, Composer’de bulunan zengin paketleri özümser. Bu paketler çatının kendi bağımlılıklarıdır.
Laravel gibi yapıp, Packagist websitesinde¹⁹ listelenen paketlere bakabilirsiniz. Klişe kodlar geldikten
sonra, tekerleği yeniden keşfetmenin alemi yok.
Composer bölümünü okuduğunuz için, Laravel’in çekirdek dosyalarının vendor klasörüne yüklenmiş olacağını biliyorsunuz. Kodunuzun yanında bağımlılıklarınızın versiyonlanmasını gerçekten
istemiyorsanız, Laravel diğer birkaç makul ön tanımlar yanında vendorun klasörünü göz ardı edecek
örnek bir .gitignore dosyası sağlamıştır.
Laravel geliştirme ortamımızı hemen hemen kurmuş olduk. Sadece bir konu kaldı. Web sunucunun
nasıl kurulacağı.
Web Server Yapılandırması
Bu kesim daima yazması zor olanlardan biri oluyor. Gördüğünüz gibi, her şey hafif farklı şekilde
kuruluyor ve piyasada birçok farklı web sunucusu mevcut.
Burada ne yapacağımı söyliyeyim. Web sunucusu için belirtilmesi gereken temellerden bahsedeceğim. Ayrıca, sık kullanılan web sunucuları için örnek bazı yapılandırmalar da vereceğim. Ancak,
bunlar çok genel olacak ve her duruma uyması için biraz oynamak gerekecek. Yine de, benim çaba
göstermediğimi söyleyemezsiniz!
Burada amacımızın ne olduğuna bir göz atalım. Laravel’de bootstrap (önceden yüklenecek) kodunu
içeren public denen bir dizin vardır. Bu kod frameworkun başlatılması için ve uygulamanıza yapılan
tüm istekleri işlemek için kullanılır.
public klasörü aynı zamanda sizin kamuya açık varlıklarınızı, örneğin JavaScript, CSS ve resimleri
de içerir. Esasında, doğrudan bir link ile erişilebilecek her şey public dizini içinde olmalıdır.
¹⁹http://packagist.org
Başlarken
43
Bunlar web sunucu yapılandırması açısından ne anlama geliyor? Tabii ki bizim ilk görevimiz web
sunucusunun doğru yere bakmasını temin etmek. Web sunucusunun yapılandırmasını, projemizin
kök dizinine değil public dizinine bakacak şekilde düzenlememiz gerekiyor.
Sonraki görev, web sunucusunun zarif URL’leri nasıl işleyeceğini bilmesini sağlamak.
Ben aslında uygun bir domain adı seçtim, benim zaten zarif bir URL’em yok mu?
Üzgünüm, çalışma şekli bu değil. Hayatın hepsi kek ve pasta değil, biliyorsunuz değil mi? Laravel’in
bootstrap’ı (önceden yüklenmesi gereken kodları) public klasörü içindeki index.php denen bir
dosyanın içinde bulunmaktadır. Framework’e yapılan tüm istekler bu dosya üzerinden gidiyor. Yani,
ön tanımlı durumda bizim ziyaretcidefteri sayfamızın URL’si aşağıdaki gibi görünecektir:
1
http://hayatkekvepastadegil.com/index.php/ziyaretcidefteri
Web sayfamızın ziyaretçilerinin her şeyin index.php aracılığıyla yönlendirildiğini bilmesine gerek
yok. Ayrıca, arama motoru optimizasyonu için de harika bir özellik değildir. Bu sebeple, web sunucumuzu index.php‘yi URL’den çıkartıp, sadece ‘zarif’ bölümü kalacak şekilde yapılandırmalıyız.
Bu normalde bir düzenli ifade sihirbazı tarafından hazırlanmış bir yapılandırma kodu ile elde edilir.
Şimdi bizim yukarıda bahsedilen hedeflere varmamızı sağlayacak bazı örnek web sunucusu yapılandırmalarına geçelim. Tekrar belirtiyorum, bu yapılandırmalar sadece kaba kılavuz olarak
kullanılabilir. Sunucu yapılandırması konusunda daha ayrıntılı bilgiler için, seçmiş olduğunuz web
sunucusu belgelerini ziyaret etmenizi önereceğim.
Önce nginx ile başlayalım.
Nginx
Nginx, ‘encin-İKS’ diye okunur, yakınlarda kullanmaya başladığım harika bir web sunucusudur.
Benim için seçim basit oldu. Apache’den çok daha hızlı çalışıyor ve XML(emsi) yapılandırması
gerektirmiyor. Tam kafama göre.
Ubuntu gibi Debian tabanlı bir linux dağıtımında, aşağıdaki komutu çalıştırarak nginx ve PHP
yükleyebilirsiniz.
1
sudo apt-get install nginx php5-fpm
İkinci paket PHP-FPM, nginx’in PHP kodunu çalıştırmasına imkan verecek bir FastCGI modülüdür.
Mac’da bu paketler Macports’da²⁰ bulunmaktadır. Gerekli paketler aşağıdaki komut kullanılarak
yüklenebilir.
²⁰http://www.macports.org/
44
Başlarken
1
sudo port install php54-fpm nginx
Nginx site yapılandırmanız normalde /etc/nginx/sites-enabled‘de konumlandırılır. Burada yeni
bir site kurmak için kullanabileceğiniz bir şablon verilmiştir.
1
server {
2
3
4
# Web sunucusunun dinleyeceği Port.
listen
80
5
6
7
# Bu projeyi sunacak Host.
server_name
app.dev
8
9
10
11
12
# Debug için yararlı günlükler.
access_log
/path/to/access.log;
error_log
/path/to/error.log;
rewrite_log
on;
13
14
15
# Projemizin publik dizininin konumu.
root
/path/to/our/public;
16
17
18
# Laravel ön kontrolöre index noktası.
index
index.php;
19
20
location / {
21
# Denenecek URL'ler, zarif URL'ler dahil.
try_files
$uri $uri/ /index.php?$query_string;
22
23
24
25
}
26
27
28
29
30
# Lütfen rota sistemi için son bölüyü çıkartınız.
if (!-d $request_filename) {
rewrite
^/(.+)/$ /$1 permanent;
}
31
32
33
34
35
36
37
# PHP FPM yapılandırması.
location ~* \.php$ {
fastcgi_pass
fastcgi_index
fastcgi_split_path_info
include
unix:/var/run/php5-fpm.sock;
index.php;
^(.+\.php)(.*)$;
/etc/nginx/fastcgi_params;
45
Başlarken
38
39
40
fastcgi_param
i_script_name;
}
SCRIPT_FILENAME $document_root$fastcg\
41
# nginx'te .ht dosyalarına ihtiyacımız yok.
location ~ /\.ht {
deny all;
}
42
43
44
45
46
47
}
Tabii bütün web sunucuları aynı şekilde gitmiyor. Tüm durumlara uygun genel bir yapılandırma
sağlamak imkansız bir görev olacaktır. Buna rağmen verdiğim örnek başlangıç için yeterlidir.
Herkes katkıda bulunabilsin diye bu bölümde kullandığım yapılandırmaları Github’ta paylaştım.
Onları daylerees/laravel-website-configs²¹ ambarında bulabilirsiniz.
Nginx harika bir web sunucu seçimi olsa da, Apache web sunucusu da yaygın kullanılmaktadır. Bir
de onun nasıl yapılandırılacağına bakalım.
Apache
Apache web sunucusu Debian tabanlı sistemlerde aşağıdaki komut kullanılarak yüklenebilir.
1
sudo apt-get install apache2 php5
Burada verilen Apache VirtualHost yapılandırması birçok duruma uyacaktır, herhangi bir değişiklik
gerekirse github’daki ambara²² katkıda bulunabilirsiniz.
1
<VirtualHost *:80>
2
3
4
# Bu projeyi sunacak Host.
ServerName
app.dev
5
6
7
# Projelerin publik dizininin konumu.
DocumentRoot
/path/to/our/public
8
9
10
11
# Debug için kullanılabilir günceler.
CustomLog
/path/to/access.log common
ErrorLog
/path/to/error.log
²¹http://github.com/daylerees/laravel-website-configs
²²https://github.com/sineld/laravel-website-configs
Başlarken
46
12
# Zarif URL'ler için Rewrite yapar, .htaccess'e güvenmemek daha iyi.
<Directory /path/to/our/public>
<IfModule mod_rewrite.c>
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
</Directory>
13
14
15
16
17
18
19
20
21
22
23
</VirtualHost>
Proje Yapısı
Önceki kitapta paketin dizin yapısını anlatan bir bölüm vardı ve oradaki herşey duruyor gibi. Bu
bölümde çok fazla değer olduğunu düşünüyorum ve bu nedenle Laravel 3’ten bu yana oluşan dizin
yapısı değişiklikleriyle birlikte tekrarlamak istiyorum.
Başlarken’in bu kesimi taze bir Laravel 4 projesinde composer install çalıştırmış olduğunuzu
varsayacaktır.
Proje Kök Dizini
Kök klasör yapısına bir göz atarak başlayalım.
•
•
•
•
•
•
•
•
•
•
•
app/
bootstrap/
vendor/
public/
.gitattributes
.gitignore
artisan
composer.json
composer.lock
phpunit.xml
server.php
Bu kök öğeler hakkında sırayla geçemez miyiz? Bana harika bir fikir gibi geliyor!
app
Başlarken
47
En başında app dizinimiz var. App, projenizin özel kodlarının tümü için ön tanımlı yer sağlamakta
kullanılır. Burada uygulamanın işlevselliğini sağlayan sınıflar, yapılandırma dosyaları ve daha başka
şeyler içerir. Bu app klasörü oldukça önemlidir, o yüzden tek bir paragrafta kötü bir özet vermek
yerine bu kesimin sonunda ayrıntılı olarak değineceğim. Şimdilik burasının proje dosyalarınızın
yaşadığı yer olduğunu bilmeniz yeterlidir.
bootstrap
• autoload.php
• paths.php
• start.php
Bootstrap dizini çatının başlangıç prosedürleriyle ilgili birkaç dosya içerir. Bu prosedürlerin çoğunu
autoload.php dosyası içerir ve bu dosya sadece deneyimli Laravel kullanıcıları tarafından düzenlenmelidir.
paths.php dosyası çatı tarafından kullanılan ortak dosya sistemi yollarından oluşan bir dizi
meydana getirir. Eğer bir sebeple çatı paketlerinin dizin yapılarını değiştirmeye karar verirseniz, bu
dosyanın içeriğini sizin yaptığınız değişiklikleri yansıtacak şekilde değiştirmeniz gerekebilecektir.
start.php çatının diğer başlangıç prosedürlerini içerir. Gereksiz kafa karışıklığına yol açabileceği
nedeniyle şimdilik bunun ayrıntılarına girmek istemiyorum. Bunun yerine çatının ortamlarının
(environments) ayarlandığı yer olarak aklınızda bulunsun. Eğer ortamların ne için kullanıldığını
bilmiyorsanız, endişelenmeyin. İleride onu da göreceğiz!
Basitçe söyleyecek olursak, bootstrap dizininin içeriği sadece dosya sistemlerinde çatının biçimini
değiştirmesi gereken deneyimli Laravel kullanıcıları tarafından düzenlenmelidir. Laravel’de yeni
iseniz, şimdilik onu görmezden gelin ancak silmeyin! Laravel’in iş görmesi için bu dizin gereklidir.
vendor
vendor dizini uygulamanız tarafından kullanılan composer paketlerinin tümünü içermektedir. Tabii
ki, Laravel framework paketini de içerir. Bu dizin hakkında daha fazla bilgi için lütfen Ön Bilgiler
bölümündeki Composer kesimine bakınız.
public
•
•
•
•
•
packages/
.htaccess
favicon.ico
index.php
robots.txt
public dizini bir Laravel uygulamasının webe dönük tek dizinidir. Normalde burası CSS, Javascript
dosyaları ve resimler gibi varlıklarınızın yaşadığı yerdir. Bunun içeriğine biraz yakından bakalım.
Başlarken
48
packages dizini üçüncü parti paketleri tarafından yüklenmesi gerekecek varlıkları kapsamak için
kullanılacaktır. Bunlar ayrı dizinlerde tutulurlar, dolayısıyla bizim uygulamamızın kendi varlıkları
ile çatışmazlar.
Laravel 4 Apache web sunucusu için standart bir .htaccess dosyasıyla gelir. Bu dosya, çatı
kullanıcılarının çoğu için uygun olacak bazı standart yapılanma direktiflerini içerir. Şayet alternatif
bir web sunucu kullanıyorsanız, bu dosyayı ya görmezden gelebilir veya silebilirsiniz.
Ön tanımlı olarak, web tarayıcıları bir sitenin dizin indeksinde bir favicon.ico dosyası bulmaya
çalışır. Bu dosya küçük 16 x 16px bir görüntü olup, tarayıcı sekmesinde gösterilir. Sorun şu ki, web
sunucusunda bu dosya bulunamadığı zaman bundan yakınmayı sever. Gereksiz günlük girişlerine
yol açar. Bu problemi ortadan kaldırmak için Laravel ön tanımlı olarak boş bir favicon.ico dosyası
sağlamış olup, sonradan istendiği şekilde değiştirilebilir.
index.php dosyası Laravel framework ön denetleyicisidir. Bu dosya bir tarayıcıdan bir istek
alındığında web sunucusunun ulaştığı ilk dosyadır. Çatının bootstrap’ını başlatacak ve tabiri caizse
topu çevirmeye başlayacak dosya budur. Bu dosyayı silmeyin, bu iyi bir fikir değildir!
Laravel ön tanımlı olarak tüm ana bilgisayarlara izin veren standart robots.txt dosyası içerir. Bu
size kolaylık sağlamak içindir, bu dosyayı gerektiği kadar değiştirebilirsiniz.
.gitattributes
Laravel, kullanmanız için bazı ön tanımlı Git sürüm kontrol yapılandırması vermiştir. Git günümüzde en popüler sürüm kontrol seçimidir. Projenizi Git ile sürümlendirmek istemiyorsanız bu dosyaları
çıkartmakta serbestsiniz.
.gitignore
.gitignore dosyası Git sürüm kontrolünü hangi klasörlerin sürümlenmeyeceği konusunda bilgilendirdiği ön tanımlı bazı değerleri içerir. Bu dosyanın aynı zamanda hem vendor dizinini hem de
uygulama storage dizinini de içerdiğine dikkat ediniz. Üçüncü parti paketlerinin versiyonlanmasını
önlemek için vendor dizini dahil edilmiştir.
composer.lock da dahil edilmiştir, ancak bu girişi çıkarmak ve kilit dosyanızı versiyonlamak
isteyebilirsiniz, böylece üretim ortamınızın geliştirme ortamınızla tam aynı bağımlılık sürümlerini
yükleyebilirsiniz. Ön Bilgiler bölümünün Composer kesiminde bu konuda daha fazla bilgi bulunmaktadır.
artisan
artisan dosyası Laravel için Artisan komut satırı arayüzünü çalıştırmak için kullanılan çalış-
tırılabilir bir dosyadır. Artisan, çatıya kısayollar ve ek işlevsellik sağlayan çok sayıda komutlar
içermektedir. İlerdeki bir bölümde bu komutlar ayrıntılı olarak anlatılacaktır.
composer.json ve composer.lock
Hem composer.json hem de composer.lock dosyası bu proje tarafından kullanılan composer
paketleri hakkında bilgi içerir. Tekrar ifade ediyorum, Ön Bilgiler bölümünün Composer kesiminde
bu konularla ilgili daha fazla bilgi bulabilirsiniz.
Başlarken
49
phpunit.xml
phpunit.xml dosyası PHP unit testi çatısı için varsayılan bir yapılandırma sağlar. Bu, composer
bağımlılıklarının yüklenmesini halledecek ve klasöründe bulunan testleri çalıştıracaktır. Laravel’de
testlerle ilgili bilgiler ilerdeki bir bölümde karşımıza çıkacaktır, bizden ayrılmayın!
server.php
@todo: Bu biraz daha araştırma yapmayı gerektirecek.
Uygulama Dizini (‘app’)
Uygulamanızın şeklini alacağı yer burasıdır. Zamanınızın çoğunu alacak olan dizin bu dizindir. O
zaman neden daha fazla bilgi sahibi olmuyoruz?
•
•
•
•
•
•
•
•
•
•
•
•
commands/
config/
controllers/
database/
lang/
models/
start/
storage/
tests/
views/
filters.php
routes.php
commands
Commands dizini uygulamanızın gerektirdiği özel artisan komut satırı arayüzü komutlarını içerir.
Gördüğünüz gibi Artisan CLI sadece projenizi inşa etmenize yardım edecek varsayılan işlevselliği
sağlamakla kalmayıp, aynı zamanda sizin yapmak istediğiniz özel komutları da oluşturabilmektedir.
config
Hem çatının hem de uygulamanızın yapılandırması bu dizinde tutulur. Laravel’in konfigürasyonu
anahtar-değer dizileri içeren bir takım PHP dosyaları şeklinde bulunmaktadır. Bu dizin aynı
zamanda, farklı ortamlarda yüklenebilecek farklı yapılandırmalara imkan veren alt dizinler de
içerecektir.
controllers
Adından da anlaşılacağı gibi, bu dizin controller’larınızı (denetçilerinizi) barındırır. Denetçiler
uygulama mantığı sağlamak ve uygulamanın ayrı parçalarını bir arada tutmak için kullanılabilir.
Başlarken
50
Size kolaylık olması bakımından bu dizin composer.json‘da ön tanımlı bir classmap otomatik
yüklenen konumu olarak eklenmiştir.
database
Uzun vadeli bir depolama yöntemi olarak bir veritabanı kullanmayı seçerseniz, bu durumda bu dizin
oluşturacağınız veritabanı şeması dosyalarını ve ona örnek veriler ekme (seeding) metodlarınızı
barındırmak için kullanılacaktır. Bu dizinde, ön tanımlı SQLite veritabanı da bulunmaktadır.
lang
lang dizini uygulamanıza yerelleştirme desteği vermekte kullanılabilecek string dizileri olan PHP
dosyalarını içerir. Bölgelere göre isimlendirilen alt klasörler birden çok dil için string dosyaları
olmasına imkan verir.
models
Models dizini modellerinizi taşır. Şaşırdınız mı? Modeller iş modelinizi temsil etmek için veya
depoyla (storage) etkileşim için kullanılırlar. Aklınız mı karıştı? Dert etmeyin. İlerideki bir bölümde
modelleri ayrıntılarıyla göreceğiz. Uygulama kimlik doğrulaması için hazır olarak bir User modelinin sunulduğunu bilin. controllers dizini gibi bu dizin de ön tanımlı composer.json‘un classmap
otomatik yüklenenler kesimine eklenmiştir.
start
bootstrap dizini çatıya ait başlangıç prosedürlerini içerirken, start dizini sizin uygulamanıza ait
başlangıç prosedürlerini içerir. Her zaman olduğu gibi, sizin için bazı makul ön tanımlar sağlanmıştır.
storage
Laravel’in diske bir şey yazması gerektiğinde, bunu storage dizini içinde yapar. Bu sebeple web
sunucunuzun bu konuma yazabilir olması gereklidir.
tests
tests dizini uygulanızın tüm unit ve kabul testlerini içerecektir. Ön tanımlı olarak bu dizindeki
testlere bakacak ön tanımlı bir PHP Unit yapılandırması Laravel’e dahil edilmiştir.
views
Views dizini uygulamanızın görsel şablonlarını taşımak için kullanılır. Kolaylık olsun diye ön tanımlı
bir hello görünümü sağlanmıştır.
filters.php
filters.php dosyası uygulamanızın rota filtrelerini içerir. İlerdeki bir bölümde filtreler hakkında
daha fazla bilgi öğreneceksiniz.
routes.php
Routes dosyası uygulama rotalarınızın tümünü içerir. Rotaların ne olduğunu bilmiyor musunuz? O
zaman, niye daha fazla vakit kaybedelim ki. Hemen sonraki bölüme geçelim!
Basit Rotalama
Sen …‘nin vahşi doğasında uzun, düz, çamurlu bir yoldasın. Hangi ülkede vahşi doğa var? Avustralya! Yapacağız.
Heyecanlanmayın! Bu bölüm biraz hayal gücü gerektiriyor, bu yüzden öyle yapmaya çalışacağım.
Ana karakterimizin adının kitaplardaki gibi olmasını öneriyorum. Hmm… adı Browsy Mc’Request
olsun.
Browsy Mc’Request kanguru ülkesinde bir yerlerde düz, çamurlu bir yol boyunca yürüyor. Browsy
buranın yerlisi değil ve kaybolduğunu düşünüyor, nereye gittiği konusunda hiçbir fikri yok. Ancak,
nerede olmak istediğini biliyor. Daha önce bir arkadaşı ona “Jason Lewis’in Bıyıklı Keseliler
Barınağından” bahsetmişti. Movember’in yaklaştığını ve Kanguru stilistlerinden yardım alması
gerekeceğini biliyor.
Aniden, Browsy uzaklarda yolun ikiye ayrıldığını fark etti. Eski bildik düz, çamurlu yoldan başka
bir şey görünce heyecanladı, bir yön bulurum umuduyla yol ayrımına koşturdu.
Yol ayrımına vardığında, barınağa götürebilecek bir gösterge aradı ancak hiçbir şey bulamadı.
Browsy’nin şansına, tam o anda bir vahşi web geliştiricisi ortaya çıktı. Bu güçlü ve yakışıklı web
geliştiricisi göz açıp kapayıncaya kadarlık bir sürede bir tabelayı sert toprağa çarparak gözden
kayboldu.
Browsy web geliştiricisinin hızı ve yakışıklılığı karşısında şaşakaldı. Onun yakışıklı olduğunu
söylemiş miydim? Browsy’nin değil, web geliştiricisinin… Browsy BU KİTAP’tan gelmiş ‘çok
değerli’ ADAMLAR gibi görünüyor. En azından ben öyle görüyorum.
Nefesini biraz tuttuktan sonra, Browsy tabelayı çalışmaya başlıyor.
Sola dönüp “Bob’un Bobcat Binicilik Ahırlarını” bulacaksınız ve sonra sağa… ah! Devam edin. Sağ
tarafta “Jason Lewis’in Bıyıklı Keseliler Barınağını” bulacaksınız.
Browsy sonunda Keseliler Barınağı yolunu buldu ve kangurulara seyislik etmeyi öğrenerek uzun ve
mutlu bir yaşam sürdü.
Gördüğünüz gibi bu öykünün ana fikri, ona yol gösteren (YAKIŞIKLI) bir geliştirici olmasaydı,
Browsy Mc’Request bizim uygulam.. pardon Keseliler Barınağının yolunu bulamayacaktı.
Tıpkı bu öyküde olduğu gibi, bir web isteği de bir rotalama olmadan uygulamamız mantığına
yönlenemeyecektir.
Basit Rotalama
Laravel framework’e yapılan bir isteğe hep birlikte bakalım.
Basit Rotalama
1
52
http://domain.com/benim/sayfam
Bu örnekte domain.com‘da barındırılan Laravel uygulamanıza erişmek için http protokolu (çoğu
web tarayıcısı tarafından kullanılan) kullanıyoruz. URL’deki benim/sayfam kısmı web isteklerini
uygun mantığa yönlendirmekte kullanacağımız şeydir.
Devam edip, sizi en sona atayım. Rotalar app/routes.php dosyasında tanımlanmıştır, bu yüzden
oraya gidelim ve yukarıda bahsettiğimiz isteği dinleyecek bir rota oluşturalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
Route::get('benim/sayfam', function() {
return 'Merhaba dünya!';
});
Şimdi web tarayıcısına http://domain.com/benim/sayfam girin, domain.com yerine Laravel uygulamanızın adresi yazılacak. Bu muhtemelen localhost‘dır.
Eğer her şey düzgünce yapılandırılmış ise, harika Times New Roman fontuyla yazılmış ‘Merhaba
dünya!’ kelimelerini göreceksin! Bunun nasıl çalıştığını görmek için neden rota deklarasyonuna
yakından bakmayalım.
Rotalar her zaman için Route sınıfı kullanılarak deklare edilir. Yani başlangıçtaki :: öncesindeki
kısımdır. get parçası ise, belirli bir URL’ye HTTP ‘GET’ fiili kullanılarak yapılmış olan istekleri
‘yakalamak’ için kullanılan, rota sınıfındaki bir metodtur.
Gördüğünüz gibi, bir web tarayıcısı tarafından yapılan bütün istekler bir fiil içerir. Çoğu defa bu fiil,
bir web sayfası istemekte kullanılan GET olacaktır. Web tarayıcınıza yeni bir adres yazdığınız her
seferinde bir GET isteği gönderilir.
Tek istek bu değildir. Ayrıca, bir istek yapmak ve onunla birlikte bir parça veri de sağlamakta
kullanılan POST da vardır. Bunlar normalde bir form göndermenin sonucudur, buradaki veriler
URL’de gösterilmeksizin web sunucusuna gönderilmesi gereken verilerdir.
Başka HTTP fiilleri de vardır. Rotalama sınıfında kullanabileceğiniz metodların bazıları şunlardır:
Basit Rotalama
1
53
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get();
Route::post();
Route::put();
Route::delete();
Route::any();
Bu metodların hepsi de aynı parametreleri kabul eder, bu yüzden, verilen bir durumda hangi HTTP
fiili kullanmanın doğru olacağı konusunda rahat olun. Bu, RESTful rotalama olarak bilinmektedir.
İlerde bu konuya daha ayrıntılı değineceğiz. Şimdilik bilmeniz gerekenler GETin istek yapmak için
kullanıldığı ve POSTun istekle birlikte ek bilgiler göndermeniz gerektiğinde kullanılacağıdır.
Route::any() metodu herhangi bir HTTP filine eşleştirme için kullanılır. Yine de ben uygulamanızı
daha transparan yapmak için, kullandığınız durum için doğru olan fiili kullanmanızı öneririm.
Elimizdeki örneğe tekrar bakalım ve hafızamızı tazeleyelim:
1
<?php
2
3
// app/routes.php
4
5
6
7
Route::get('benim/sayfam', function() {
return 'Merhaba dünya!';
});
Kod parçasının sonraki kısmı get() (veya başka bir fiil) metoduna ilk parametredir. Bu parametre,
istediğimiz URL ile eşleşen URI’yi tanımlar. Buradaki örnekte eşleştirilen benim/sayfam‘dir.
Son parametre ise, isteği işleyecek uygulama mantığını vermektedir. Biz burada bir Closure
kullanıyoruz (bu aynı zamanda anonim fonksiyon olarak da bilinmektedir). Closure’lar diğer basit
tiplerde olduğu gibi değişkenlere atanabilen bir ismi olmayan basit fonksiyonlardır.
Örnek olarak, yukarıdaki kod parçacığı şöyle de yazılabilirdi:
Basit Rotalama
1
54
<?php
2
3
// app/routes.php
4
5
6
7
$mantik = function() {
return 'Merhaba dünya!';
}
8
9
Route::get('benim/sayfam', $mantik);
Burada Closureu $mantik değişkeninde saklıyoruz ve sonra da onu Route::get() metoduna
geçiriyoruz.
Bu örneğimizde, Laravel Closure fonksiyonunu sadece güncel istek HTTP GET fiilini kullandığı
ve URI benim/sayfam olduğu zaman çalıştıracaktır. Bu şartlar altında return cümlesi işlenecek ve
tarayıcıya “Merhaba dünya!” metni sunulacaktır.
İstediğiniz kadar çok sayıda rota tanımlayabilirsiniz, örneğin:
1
<?php
2
3
// app/routes.php
4
5
6
7
Route::get('birinci/sayfa', function() {
return 'Birinci!';
});
8
9
10
11
Route::get('ikinci/sayfa', function() {
return 'İkinci!';
});
12
13
14
15
Route::get('ucuncu/sayfa', function() {
return 'Patates!';
});
Uygulamanızın nasıl davrandığını görmek için aşağıdaki URL’leri gezmeyi deneyin.
1
2
3
http://domain.com/birinci/sayfa
http://domain.com/ikinci/sayfa
http://domain.com/ucuncu/sayfa
Web uygulamanızın kökünü eşleştirmek isteyebilirsiniz belki. Örneğin…
Basit Rotalama
1
55
http://domain.com
Normalde bu, uygulamanızın ana sayfasını barındırmak için kullanılır. Ona uyan bir rota oluşturalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
Route::get('/', function() {
return 'Sovyet Rusya\'da, fonksiyon sizi tanımlar.';
});
Hey, bekle bir dakika! Bizim URI’de bir bölü işareti olmayacaktı?
SAKİN OL OKUYUCU! Giderek çıldırıyorsun.
Görmüş olduğunuz gibi, sadece bir bölü işareti taşıyan bir rota, websitesinin URL’si ile eşleşecektir,
sonunda ister bölü olsun, ister olmasın. Yukarıdaki rota aşağıdaki URL’lerin ikisine de cevap
verecektir.
1
2
http://domain.com
http://domain.com/
URL’ler istediğiniz kadar çok segmentten (bölüler arasındaki parçalar) oluşabilir. Bunu bir site
hiyerarşisi oluşturmak için kullanabilirsiniz.
Şöyle bir site yapısını ele alalım:
1
2
3
4
5
6
7
8
/
/kitaplar
/kurgu
/bilim
/romantizm
/dergiler
/sohretler
/teknoloji
Evet, bu oldukça küçük bir site, ama web’de sıklıkla bulacağın bir yapı için büyük bir örnek. Şimdi
Laravel rotalarını kullanarak bunu yeniden oluşturalım.
Anlaşılır olması bakımından, her bir rotadaki Closure işleme kodları budanmıştır.
Basit Rotalama
1
56
<?php
2
3
// app/routes.php
4
5
6
// ana sayfa
Route::get('/', function() {});
7
8
9
10
11
12
13
// kitaplar kesimi için rotalar
Route::get('/kitaplar', function() {});
Route::get('/kitaplar/kurgu', function() {});
Route::get('/kitaplar/bilim', function() {});
Route::get('/kitaplar/roman', function() {});
14
15
16
17
18
// dergiler kesimi için rotalar
Route::get('/dergiler', function() {});
Route::get('/dergiler/sohretler', function() {});
Route::get('/dergiler/teknoloji', function() {});
Bu rota koleksiyonuyla kolaylıkla bir site hiyerarşisi oluşturduk. Ancak, belirli miktarda tekrar
olduğunu fark etmiş olabilirsin. Bu tekrarlamaları en aza indirecek bir yol bulalım ve KTE (Kendinizi
Tekrar Etmeyin) ilkelerine yapışalım.
Rota Parametreleri
Rota parametreleri rota tanımlarınıza yer tutucular eklemek için kullanılabilir. Bunlar hangi URI
segmentlerinin toplanacağı ve uygulamanın mantık işleyicisine geçirilebileceğini tanımlayan bir
deseni etkili bir şekilde oluşturacaklardır.
Bu biraz kafanızı karıştırmış olabilir, ama işin yapılışını gördüğünüz zaman her şey yerine oturacak.
İşte başlıyoruz…
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
// kitaplar kesimi için rotalar
Route::get('/kitaplar', function()
{
return 'Kitaplar indeksi.';
});
Basit Rotalama
11
12
13
14
57
Route::get('/kitaplar/{tarz}', function($tarz)
{
return "{$tarz} kategorisindeki kitaplar.";
});
Bu örnekte, bir rota yer tutucusu dahil etmekle kitap türlerinin hepsine rota yazma gereğini ortadan
kaldırdık. {tarz} yer tutucusu /kitaplar/ URI’den sonra gelen her şeyle eşleşecektir. Bu, onun
değerini Closure’un $tarz parametresine geçirecektir, böylece bu bilginin bizim mantık kısmımız
içinde kullanılması mümkün olacaktır.
Örneğin, şu URL’yi ziyaret edecek olsaydınız:
1
http://domain.com/kitaplar/cinayet
Bu metin cevabı ile karşılaşacaktınız:
1
cinayet kategorisindeki kitaplar.
Opsiyonel bir parametre kullanmak suretiyle kitapların index rotası için ihtiyacı da ortadan
kaldırabiliriz. Adının sonuna bir soru işareti (?) eklemekle bir parametre opsiyonel yapılabilmektedir.
Örneğin:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
// kitaplar kesimi için rotalar
Route::get('/kitaplar/{tarz?}', function($tarz = null)
{
if ($tarz == null) return 'Kitaplar indeksi.';
return "{$tarz} kategorisindeki kitaplar.";
});
Şayet URL’de bir tarz verilmezse, bu durumda $tarz değişkeninin değeri null‘a eşit olacak ve
Kitaplar indeksi. mesajı gösterilecektir.
Eğer bir rota parametresi değerinin ön tanımlı olarak null olmasını istemiyorsak, atama kullanarak
bir alternatif belirleyebiliriz. Örneğin:
Basit Rotalama
1
58
<?php
2
3
// app/routes.php
4
5
6
7
8
9
// kitaplar kesimi için rotalar
Route::get('/kitaplar/{tarz?}', function($tarz = 'Cinayet')
{
return "{$tarz} kategorisindeki kitaplar.";
});
Şimdi eğer aşağıdaki URL’yi ziyaret edersek:
1
http://domain.com/kitaplar
Bu cevabı alacağız:
1
Cinayet kategorisindeki kitaplar.
Umarım, rotaların sitenize yapılan istekleri yönlendirmek için ve uygulamanızı birbirine bağlamakta
kullanılan ‘kod yapıştırıcısı’ olarak nasıl kullanıldığını görmeye başladınız.
Rota konusunda daha çok şeyler var. Ona geri döneceğiz ama temeller üzerinde biraz daha kalalım.
Sonraki bölümde Laravel’in sunduğu cevap tiplerine bakacağız.
Cevaplar (Responses)
Birisi bir şey sorduğunda, soru anlamsız değilse ve ruh haliniz yerindeyse büyük ihtimalle bir cevap
verirsiniz. Ben diğer bir istisnanın soru benzeri karşılamalar olabileceğini düşünüyorum, birinin
‘İyisin?’ demesi gibi. Böyle olduğunda bile, bir cevap çeşidi olarak en azından kafanızı sallarsınız.
Bir web sunucusuna yapılan istekler ‘İyisin?’ diyen birinden farklı değildir. Bir şeylerin geri
gelmesini beklerler. Önceki bölümde bizim cevaplarımız rota closure’larından döndürülen stringler
biçimini alıyordu. Şöyle bir şey:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return 'İyi miyim be patron.';
});
Yani burada web tarayıcısına “İyi miyim be patron.” stringini gönderdik. Bu string bizim cevabımızdır ve daima bir rota closure’u veya daha sonra anlatacağımız bir Kontrolör eylemi tarafından
döndürülür (return edilir).
Cevap olarak bir miktar HTML göndermek isteyeceğinizi düşünmek yanlış olmasa gerek. Web
uygulamaları geliştirirken genelde böyle yapılır.
Cevap stringine HTML koyabileceğimizi sanıyorum?
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
12
Route::get('/', function()
{
return '<!doctype html>
<html lang ="en">
<head>
<meta charset ="UTF-8">
<title>Pekala!</title>
</head>
Cevaplar (Responses)
<body>
<p>Bu mükemmel bir cevap!</p>
</body>
</html>';
13
14
15
16
17
60
});
Müthiş! Laravel’in gücünü ve zerafetini gördün mü… şaka, şaka. Biz HTML’i bu şekilde sunmak
istemeyiz. Mantık gömme can sıkıcı olacaktır ve daha da önemlisi düz HTML olması da yanlış!
Neyse ki, Laravel çok daha kolay anlamlı bir cevap döndürmemizi sağlayan çeşitli Response
nesnelerine sahiptir. En heyecan verenlerden biri olan View (Görünüm) cevabına bakalım!
Görünümler
Görünümler uygulamanızın görsel kısmıdır. Model View Controller deseni içerisinde bunlar View
kısmını oluştururlar. Bunlara onun için view deniyor. Bu roket bilimi değil sonuçta. Roket bilimi
ilerideki bir bölümde anlatılacaktır.
Bir görünüm web tarayıcısına döndürülebilecek düz bir metin şablonundan başka bir şey değildir,
tabi HTML de içerebilir. Görünümler .php uzantısını kullanırlar ve normalde app/views dizinine
konurlar. Demek ki, görünümleriniz içinde PHP kodu da parse edilebilecektir. Şimdilik çok basit bir
görünüm oluşturarak işe başlayalım.
1
<!-- app/views/basit.php -->
2
3
4
5
6
7
8
9
10
11
12
<!doctype html>
<html lang ="en">
<head>
<meta charset ="UTF-8">
<title>Görünümler!</title>
</head>
<body>
<p>Aa evet! GÖRÜNÜMLER!</p>
</body>
</html>
Harika! app/views/basit.php dosyasında saklanan küçük bir HTML. Şimdi bir görünüm yapalım.
Daha demin yaptığımız neydi?
Haha! Evet önceki yaptığın görünümdü, ama ben ‘Başka bir görünüm dosyası yapalım.’ demek
istemedim. Oluşturduğumuz o görünüm dosyasına dayanan yeni bir view cevabı nesnesi yapalım
(make()) demek istedim. Görünümleri temsil eden dosyalara şablon denilmektedir. Bu açıklama View
nesneleri ile HTML şablonlarını ayırt etmenize yardımcı olabilir.
Cevaplar (Responses)
1
61
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('basit');
});
O, Görüyorum! make() metoduna gelmişsin.
Biraz açıklayayım. View::make() metodunu kullanmakla yeni bir View cevap nesnesi olgusu
oluşturabiliyoruz. make() metoduna verdiğimiz birinci parametre kullanmak istediğimiz görünüm
şablonudur. Tam dosya yolu app/views/basit.php kullanmadığımıza dikkat edin. Bu Laravel’in
zeki olmasından geliyor. Çünkü o ön tanımlı olarak görünüm dosyalarının app/views dizininde
olduğunu varsayacak ve uygun bir görünüm uzantısı olan bir dosya arayacaktır.
Closure‘a daha yakından bakarsanız, oluşturduğumuz View nesnesini döndürdüğümüzü göreceksin.
Bu çok önemlidir, çünkü Laravel web tarayıcına bizim Closure’un sonucunu sunacaktır.
İlerleyelim ve uygulamamızda / URI’ye gitmeye çalışalım. Muhteşem, işte yazdığımız şablon!
Bu kitabın ilerdeki bölümlerinde hayatınızı kolaylaştırmak için View cevabı ile çalışan farklı şablon
tiplerinin nasıl oluşturulacağını öğreneceksiniz. Temel Laravel kavralarının iyice özümsenmesi
adına şimdilik ben temellere bağlı kalacağım.
Görünüm Verisi
Şablonları gösterebiliyor olmak harika. Gerçekten. Peki, Closure’umuzdan bazı veriler kullanmasını
istersek ne olacak? Önceki bölümde Rota parametreleri kullanmayı öğrenmiştik. Görünüm içinde
bu parametrelere başvurmak mümkün olabilir mi? Bunun nasıl yapılabileceğine bakalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
Route::get('/{sincap}', function($sincap)
{
$veri['sincap'] = $sincap;
8
return View::make('basit', $veri);
9
10
});
62
Cevaplar (Responses)
Yukarıdaki örnekte $sincap parametresini aldık ve görünümümüzün veri dizisine ekledik. Gördüğünüz gibi, make() metodunun ikinci parametresi görünüm şablonuna geçilecek bir dizi kabul
etmektedir. Onun görünümümüzün veri dizisi olduğunu işaret etmek için bu diziye normalde $veri
dizisi diyorum, ama siz istediğiniz herhangi bir isim kullanabilirsiniz!
Bu veriyi kullanmaya başlamadan önce, gelin görünüm veri dizisinden biraz daha söz edelim.
Görünüme bu diziyi geçtiğiniz zaman, dizi anahtarları değişkenlere dönüştürülür; dizi anahtarları
bu değişkenlerin adı, değeri de değeri olur. Basitleştirmek için bir örnek vermeden kafanız biraz
karışabilir.
View::make()‘e görünüm verisi olarak tutturulan dizi şöyle olsun.
1
2
<?php
array('isim' => 'Taylor Otwell', 'durum' => 'Kod Gurusu')
Görünüm şablonunun oluşturulmasında bu değerlere şu şekilde erişebileceğiz:
1
<?php echo $isim;
// 'Taylor Otwell' verir ?>
<?php echo $durum;
// 'Kod Gurusu' verir ?>
2
3
Yani bizim isim dizi anahtarımız şablon içinde $isim değişkeni haline gelmiştir. Görünüm veri
dizisinde istediğiniz derinlikte çok boyutlu diziler saklayabilirsiniz. Denemesi bedava!
Daha önce oluşturduğumuz basit şablonundaki $sincap değişkenini kullanalım.
1
<!-- app/views/basit.php -->
2
3
4
5
6
7
8
9
10
11
12
<!doctype html>
<html lang ="en">
<head>
<meta charset ="UTF-8">
<title>Sincap</title>
</head>
<body>
<p><?php echo $sincap; ?> bir sincap olmak isterdim!</p>
</body>
</html>
Şimdi eğer /Gri URI’yi ziyaret edersek ‘Gri bir sincap olmak isterdim!’ diyen bir sayfa alırız.
Çok basit değil mi? Görünümleri kullanmak sayesinde artık Closure’den string döndürmek zorunda
olmayacağız!
Farklı tiplerde cevap nesneleri olduğunu daha önce söylemiştim. Bazı şartlarda uygulamanızın
akışını başka bir rotaya veya mantık kısmına yön değiştirmek isteyebilirsiniz. Böylesi durumlarda
Redirect cevap nesnesi işe yarayacaktır. Bak, Laravel arkanızda!
Cevaplar (Responses)
63
Redirekt
Bir Redirect, uygulamanın akışını başka bir rotaya yönlendiren özel bir cevap nesnesi tipidir. Daha
ayrıntılı açıklayabilmek için birkaç örnek rota oluşturalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('birinci', function()
{
return 'Birinci rota.';
});
9
10
11
12
13
Route::get('ikinci', function()
{
return 'İkinci rota.';
});
Yukarıdaki rotaları eklediğimizde, /birinci URI ziyaretinde ‘Birinci rota.’ ve /ikinci URI ziyaretinde ‘İkinci rota.’ alacaksınız.
Mükemmel, ne beklediysek o oldu. Şimdi ilk rotamızın Closure’una bir redirekt ekleyerek uygulamamızın akışını tamamen kaydıralım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('birinci', function()
{
return Redirect::to('ikinci');
});
9
10
11
12
13
Route::get('ikinci', function()
{
return 'İkinci rota.';
});
İlk rotamızda şimdi Redirect::to() metodunun sonucunu döndürüyoruz ve hedef konum URI’sini
geçiyoruz. Şimdiki örneğimizde konum olarak ikinci rotanın URI’si olan ikinci‘yi veriyoruz.
Cevaplar (Responses)
64
Eğer şimdi /birinci URI’yi ziyaret ederseniz, ‘İkinci rota.’ metni ile karşılaşacaksınız. Bunun sebebi
döndürülen Redirect nesnesini almanızdır; Laravel, uygulamanızın akışını verilen hedefe gidecek
şekilde kaydırmıştır. Bu durumda akış ikinci rotanın closure’una kaydırılmıştır.
Bu özellik, bir şey başarısız olduğunda kullanıcıyı daha yararlı bir yere yönlendirme gereği
duyduğunuz zaman gerçekten yararlı olabilir. Başka bir kullanım alanı olan kimlik doğrulama
sistemi (ilerde anlatılacaktır) örneğini gösterelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
Route::get('kitaplar', function()
{
if (Auth::guest()) return Redirect::to('login');
8
// Kitapları sadece giriş yapmış kullanıcılara göster.
9
10
});
Bu örnekte, sisteme henüz giriş yapmamış bir kullanıcı /kitaplar URI’yi ziyaret ederse, misafir
olarak kabul edilir ve login sayfasına yönlendirilecektir.
İlerde rota filtrelerini tartışacağımız zaman, erişim sınırlamak için daha iyi bir yol bulacağız,
bu yüzden yukarıdaki örneğe çok takılıp kalmayın. Sadece şunu anlamak yeterli, şartlarımız
karşılanmazsa kullanıcıyı daha uygun bir yere yönlendirebiliyoruz.
Özel Cevaplar
View ve Redirect, her ikisi de Laravel’in Response nesnesinden türemişlerdir. Cevap nesnesi,
framework’ün tarayıcıya doğru cevap türü sunabilmesini sağlamak için bir rota closure’u veya bir
kontrolör eyleminin bir sonucu olarak Laravel’e geri sunulan bir sınıfın olgusudur.
Response nesneleri genel olarak bir gövde (body), bir durum kodu (status code), HTTP başlıkları
(header) ve diğer yararlı bilgiler taşır. Örnek olarak, Görünüm’ün gövde kısmı HTML içeriği
olacaktır. Bir Redirect için durum kodu 301 olacaktır. Laravel bu bilgiyi tarayıcıya döndürülebilecek
makul bir sonuç oluşturmak için kullanır.
Biz sadece View ve Redirect kullanmakla sınırlı değiliz. Kendi ihtiyaçlarımıza uygun cevap
nesnelerimizi de oluşturabiliriz. Bunun nasıl yapılacağına bir göz atalım.
Cevaplar (Responses)
1
65
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('ozel/cevap', function()
{
return Response::make('Merhaba dünya!', 200);
});
Yukarıdaki örnekte yeni bir cevap nesnesi oluşturmak için Response::make() metodunu kullanıyoruz. Birinci parametresi cevabın içeriği veya gövdesidir ve ikinci parametresi cevapla birlikte
sunulacak HTTP durum kodudur.
HTTP durum kodunu daha önce görmediyseniz, bunu sayfanızı alan web tarayıcısı için bir durum
bildirimleri olarak düşünebilirsiniz. Örneğin, her şey yolunda giderse, standart bir cevap bir 200
durum kodunu içerebilir ki, web sunucusu ‘A-OK’ demiş olmaktadır. Bir 302 durum kodu da bir
redirekt yapıldığını gösterir.
Aslında, meşhur 404 sayfa bulunamadı’yı bildiğinizi sanıyorum. 404 kısmı istenen kaynak bulunamadığında tarayıcı tarafından alınan durum kodudur.
Basitçe ifade edilecek olursa, yukarıdaki cevap tarayıcıya ‘Merhaba dünya!’ içeriği yanında isteğin
başarılı olduğunu bildiren bir HTTP 200 durum kodu da sunacaktır.
HTTP başlıkları, cevabı alan web tarayıcısına yararlı bilgiler sunan bir anahtar-değer çifti koleksiyonudur. Normalde bunlar sonucun formatını veya içeriğin hangi uzunlukta önbelleklendiğini
göstermek için kullanılır. Bununla birlikte, istediğiniz kadar özel başlık tanımlayabilirsiniz. Şimdi
bazı header değerlerini nasıl ayarlayabileceğimizi görelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('ozel/cevap', function()
{
$cevap = Response::make('Merhaba dünya!', 200);
$cevap->headers->set('anahtar', 'değer');
return $cevap;
});
Önceki örnekte yaptığımız gibi örnek bir cevap nesnesi oluşturduk. Ancak bu sefer özel bir başlık
da ayarladık.
HTTP başlıkları koleksiyonuna cevap nesnesinin headers özelliği ile erişebiliriz.
Cevaplar (Responses)
1
66
<?php
2
3
var_dump($cevap->headers);
Koleksiyondaki set() metodunu kullanarak, koleksiyona kendi başlığımızı ekleyebiliyoruz, birinci
parametresi bir anahtarı, ikinci parametresi de bunun değerini temsil ediyor.
Başlığımızı ekledikten sonra cevap nesnesini daha önce yaptığımız gibi döndürüyoruz. Tarayıcı
cevapla birlikte başlıkları da alacak, ancak bunu istediği gibi kullanacaktır.
Daha yararlı olacak bir örnek düşünelim. Hrrm… Şöyle yapalım, uygulamamızın HTML yerine
markdown cevapları sunmasını istediğimizi hayal edelim. Bir web tarayıcısının bir markdown
cevabını parse edemeyeceğini biliyoruz, fakat bunu yapabilen başka bir masaüstü uygulamamız
olabilir.
İçeriğin HTML değil de, markdown olduğunu göstermek için, Content-Type başlığını değiştireceğiz.
Bu Content-Type web tarayıcıları tarafından kendilerine gönderilen çeşitli formatlardaki içerikleri
ayırt etmekte kullandıkları yaygın bir başlık anahtarıdır. Kafanız karışmasın! Bakın bir örnek
vereyim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('markdown/cevap', function()
{
$cevap = Response::make('***Bu metin koyu yazılmış***', 200);
$cevap->headers->set('Content-Type', 'text/x-markdown');
return $cevap;
});
Cevap nesnesinin gövde kısmını örnek bir markdown metnine (bu örnek için Bu metin koyu
yazılmış) ve Content-Type başlığını Markdown düz metin formatının mime tipine ayarlayarak,
Markdown olarak tanınacak bir cevap sunmuş olduk.
Bizim masaüstü ugulamamız şimdi /markdown/cevap URI’sine bir istek yapabilir, Content-Type
başlığının ne olduğuna bakar ve text/x-markdown değerini elde edince, gövdeyi işlemek için bir
markdown dönüştürücüsü kullanacağını bilir.
Şimdi, biz hepimiz arkadaş olduğumuz için, sizinle bir sırrı paylaşacağım. Yaklaşın. Buraya gelin.
Toplanalım. Cevap nesnesi aslında Laravel’e ait değildir.
İHANET? BU NE ÇILGINLIK?
Cevaplar (Responses)
67
Heyecan yapmayın. Gördüğünüz gibi, Laravel çok sayıda ‘tekerleği yeniden keşfetmek’ten kaçınmak
için, Symfony 2 projesine ait çok sağlam bileşenlerin bir kısmını kullanmaktadır. Laravel’in response
nesnesi içeriğinin çoğunu gerçekte Symfony HTTPFoundation bileşenine ait Response nesnesinden
kalıtımla almıştır.
Şunu demek istiyorum, eğer Symfony response nesnesinin API’lerine bakarsanız, Laravel belgelerinde olmayan ekstra metodlara erişirsiniz! Kutsal duman! Bir sır daha vereyim, orada sizi bir Laravel
ustası olmaktan alıkoyacak bir şey yok!
Symfony Response nesnesi için API belgeleri burada bulunabilir²³ “The Symfony Response Object”.
Eğer sayfaya bakarsanız bu sınıfın $headers adlı bir niteliğe sahip olduğunu fark edersiniz. Bu doğru!
Yani sadece bir dakika önce kullanıyor olduğumuz headers koleksiyonu.
Laravel response nesnesi bundan türetildiği için, Symfony API belgelerinde gördüğünüz her türlü
metodu kullanabilirsiniz. Örnek olarak setTtl() metoduna bakalım. API ne diyor?
public Response setTtl(**integer $seconds**)
Sets the response’s time-to-live for shared caches. This method adjusts the Cache-Control/s-maxage
directive.
Parameters:
integer $seconds Number of seconds
Return Value:
Response
Tamam, yani bu metod paylaşılan önbellek için yaşam süresini ayarlıyor. Ben bu konuda uzman
değilim ancak bir bilgi parçasının atılmadan önce yararlı kabul edildiği süreyi ifade eder diyebilirim.
Bu örnekte TTL, içerik önbellemesi ile ilgilidir.
Eğlence olsun diye bir değer verelim. Metod imzasına baktığımızda, saniye cinsinden yaşam süresini
temsil eden bir tam sayı kabul ettiğini görürüz. Bu cevabın 60 saniye yaşamasını bildirelim. Zalim
James Bond’un kötü bir örneği gibi.
²³http://api.symfony.com/2.2/Symfony/Component/HttpFoundation/Response.html
Cevaplar (Responses)
1
68
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('bizim/cevap', function()
{
$cevap = Response::make('Bond, James Bond.', 200);
$cevap->setTtl(60);
return $cevap;
});
Şimdi bizim cevap sunulduğu zaman, yaşamak için 60 saniyesi olacak. Gördüğünüz gibi, altta yatan
Symfony bileşenini sorgulayarak uygulama cevaplarımızı ihtiyaçlarımıza göre değiştirebileceğimiz
ileri seçeneklere sahip olabiliyoruz.
Taban Response nesnesindeki karmaşıklık sizi bunaltmasın. Çoğu keresinde, basit HTTP cevapları
sunmak için Laravel’in View ve Response sınıflarını kullanmakla mutlu olacaksınız. Yukarıdaki
örnek, yalnızca uygulamalarıyla özel senaryolar için oynamak isteyen ileri kullanıcılar için iyi bir
başlangıç noktası olarak hizmet eder.
Cevap Kısayolları
Laravel arkadaşınızdır. Bu doğru… Hayır aslında doğru değil. Laravel bir arkadaştan daha fazlasıdır.
O sizi seviyor. Gerçekten seviyor. Bundan dolayı, hayatınızı kolaylaştırmak için birtakım cevap
kısayolları sağlıyor. Kullanabileceğimiz neler var bir bakalım.
JSON Cevapları
Uygulamamız içinde çoğu kez JSON olarak sunmak isteyeceğimiz bazı veriler olacak. Bunlar basit
bir nesne veya bir değerler dizisi olacaktır.
Laravel, bir takım ayrıntılarıyla birlikte JSON sonuçlarına özgü cevap nesnesi yapılandıran bir
Response::json() metodu sağlamaktadır. Örneğin uygun bir HTTP Content-Type başlığı.
Bunları elle de ayarlayabilirdik ancak Laravel bunu bizim için yapacakken niçin zahmet edelim?
Haydi bu metodu iş üstünde görelim.
Cevaplar (Responses)
1
69
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('markdown/cevap', function()
{
$veri = array('demir', 'adam', 'harika');
return Response::json($veri);
});
Bir diziyi Response::json() metoduna teslim edince o bir JSON stringine dönüştürülür ve bizim
yeni Response nesnesinin gövdesi olarak ayarlanır. İlgili başlıklar, sağlanan verinin bir JSON stringi
olduğunu açıklayacak şekilde ayarlanmıştır. Web tarayıcısı aşağıdaki gövdeyi alacaktır:
1
["demir","adam","harika"]
Hem özlü hem gerçek. Temiz ve fonksiyonel JSON API’leri inşa etmek için bu kısayolun tadını
çıkarın!
İndirme Cevapları
Dosyaların sunulması belirli başlıkların doğrudan ayarlanmasını gerektirir. Neyse ki Laravel Response::download()
kısayolunu kullanarak bu işi sizin adınıza üstlenmektedir. Nasıl işlediğine bakalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('dosya/indir', function()
{
$dosya = 'dosya_yolu.pdf';
return Response::download($dosya);
});
Şimdi eğer /dosya/indir URI’sini gezinirsek, tarayıcı bir cevap göstermek yerine bir indirme
başlatacaktır. Response::download() metodu, parametre olarak cevap döndüğü zaman sunulacak
bir dosyanın dosya yolunu almaktadır.
Özel bir HTTP durum kodu ve bir başlık dizisi yapılandırmak için opsiyonel ikinci ve üçüncü
parametreler de verebilirsiniz. Örneğin:
Cevaplar (Responses)
1
70
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('dosya/indir', function()
{
$dosya = 'dosya_yolu.pdf';
return Response::download($dosya, 418, array('demir', 'adam'));
});
Burada dosyamızı 418 (Ben bir çaydanlığım) HTTP durum kodu ve demir =adam değerinde header
ile birlikte sunacağız.
Bu bölüm benim orijinal olarak düşündüğümden çok uzun oldu, ama uygun cevap nesneleri
döndürmenin basit stringler döndürmekten çok daha değerli olacağını göreceğinizden eminim.
Sonraki bölümde rota filtrelerini ele alacağız, bunlar rotalarımızı korumamıza ve onlar çalışmadan
önce/çalıştıktan sonra eylemler uygulamamıza imkan verecekler.
Filtreler
Birkaç yıl öncesinde Jesse O’brien ve arkadaşlarının yerel hokey takımlarının Laravel Pandalarına
karşı oynadıkları son maçlarını seyretmek için özel bir etkinlik planladıkları zamanı hatırladım.
Laravel Pandalarının Londra Şövalyeleri tarafından asla yenilgiye uğratılamayacağını hepimiz biliyoruz, fakat Jesse dinlemedi. Bunun Şövalyeler için zafere doğru giden yolun başlangıcı olacağında
ısrar ediyordu.
Etkinliğin Londra’nın merkezindeki Hoser Hut’ta gerçekleştirilmesi planlanmıştı. ‘Çok kuzey’
Amerika’da (Maple şurubu ülkesi) doğmuş biri için dostça konuksever bir yer.
Ne yazık ki, Hoser Hut sınırdan gelenlere karşı öyle konuksever olmamakla bilinen bir üne sahipti.
Amerikalıların düzenli olarak Hoser Hut pencerelerinin dışına atıldığı bilinen bir gerçekti. Kötü
Amerikalıları dışarda tutmak için bir çeşit kapı filtresine ihtiyacı olduğu hükmüne varması bu
yüzdendi. Tabii ki, iyi ingiliz adamı Dayle Rees Hoser Hut’ta her zaman iyi karşılanırdı. O her yerde
iyi karşılanır.
Jesse, Hoser Hut’un önünde durup, gelen misafirlerin Kanada’lı olup olmadığını teyit etmek için
kimliklerini göstermelerini istemek üzere bir fedai tuttu.
Görüyorsunuz ki, Jesse’nin yaptığı bir filtre uygulamaktı. Filtrenin gereksinimlerini geçenler Laravel
Pandalarının Londra Şövalyelerini mahvettiğini görmek için sıcak ve rahat Hoser Hut’a giriş elde
edecekti. Buna karşın bara girmeye çalışan Amerikalılar filtreyi karşılayamayacak ve kendilerine
çizmenin parlak tarafı gösterilecekti.
Jesse’yi oyununa bırakalım ve uygulama rotalarımızı korumak için filtreleri nasıl kullanacağımızı
görelim.
Basit Filtreler
Filtreler bir rotaya tatbik edilebilecek belirli kurallar veya eylemler kümesidir. Bunlar bir rota
mantığının çalıştırılmasından önce veya sonra yapılabilirler ancak before filtrelerini daha yararlı
bulacaksınız. Before filtrelerini kullanarak, eğer belirli kurallar veya kriterler karşılanmazsa, uygulamanın akışını değiştirebiliriz. Bu filtreler rotalarımızı korumanın mükemmel bir yoludur.
Her zaman olduğu gibi, bir örnek bin kelime konuşmaktan iyidir. Bir filtreyi inceleyelim ancak önce
başka bir şeye ihtiyacımız var. Görelim:
Filtreler
1
72
<!-- app/views/dogumgunu.php -->
2
3
4
<h1>Mutlu yıllar!</h1>
<p>Mutlu yıllar Dayle, yaşa, varol!</p>
Süper! Artık doğum günü kutlama görünümümüz olduğuna göre, ilk filtremizi oluşturabiliriz. İşte
başlıyoruz:
1
<?php
2
3
// app/filters.php
4
5
6
7
8
9
10
Route::filter('dogumgunu', function()
{
if (date('d/m/y') == '12/12/84') {
return View::make('dogumgunu');
}
});
Bu ilk filtremiz oldu. Laravel, filtrelerimiz için genel bir yer olarak app/filters.php dosyasını sağlar
ancak aslında bunu istediğiniz yere koyabilirsiniz.
Yeni bir filtre oluşturmak için Route::filter() metodunu kullanıyoruz. Birinci parametresi dostça
bir isim olup, biraz sonra onu bir rota için filtre olarak atamak için kullanacağız. Bu örnekte ben
‘dogumgunu’ filtresi adını verdim. Rotaya ikinci parametre bir geriçağrı (callback) fonksiyonudur
ve örneğimizde bu bir anonim fonksiyondur (Closure).
Callback filtre çalıştığı zaman çağrılan bir fonksiyondur. Bu fonksiyon tıpkı bizim rota mantığımızda
kullandığımız gibi cevap tipinde bir nesne döndürürse, bu cevap döndürülecek ve rota mantığının
sonucunun yerine bu sunulacaktır. Şayet filtre geriçağrı fonksiyonundan hiçbir cevap döndürülmezse, o zaman rota mantığı normal şekilde devam edecektir.
Bu bize büyük bir güç verir, öyleyse ilerleyin ve kötü kahkahanızı atın. Ciddiyim, bu önemli bir iş.
Muahahahah!
Güzel, yapacağınız her şeyi ben söyleyeceğim. Gördüğünüz gibi ya uygulamanın akışını değiştirebiliriz veya bir eylem yapıp rota mantığının çalışmaya devam etmesine izin verebiliriz. Örneğin,
biz web sitemizde belirli tipteki bir kullanıcıya sadece belirli tipte bir içerik göstermek isteyebiliriz.
Bu başka bir sayfaya bir yönlendirme cevabı döndürmek yoluyla olabilir. Alternatif olarak, hangi
sayfaların ziyaret edildiğini görmek için filtre her çalıştığında bir günlük tutabiliriz. Belki de kendimi
öne alıyorum, örnek filtremize bir daha bakalım.
Filtreler
1
73
<?php
2
3
// app/filters.php
4
5
6
7
8
9
10
Route::filter('dogumgunu', function()
{
if (date('d/m') == '12/12') {
return View::make('dogumgunu');
}
});
Closure fonksiyonuna yakından baktığımızda, bir şart ve bir cevabımız olduğunu görüyoruz.
Filtremizde, eğer şu andaki tarih ’12/12/84’e, yani evrendeki en önemli kişinin doğduğu tarihe eşitse,
closure o zaman bir cevap döndürecektir. Şayet Closure’den cevap dönerse mutlu yıllar görünümüne
yönlendirileceğiz. Aksi takdirde rota mantığımız normal şekilde devam edecektir.
Tabii bir filtrenin işe yaraması için bir rotaya bağlamamız gerekiyor. Ancak, bunu yapmadan önce
rotanın yapısını biraz değiştirmemiz gerekiyor. Rotalama metodlarının ikinci parametre olarak bir
closure aldığını söylediğimi hatırlıyor musunuz? Pekala, ben yine beyaz bir yalan söyledim. Kusura
bakmayın.
Gördüğünüz gibi, rota metodları ikinci parametre olarak bir dizi de kabul edebilmektedir. Rotaya
ek parametreler atamak için bu diziyi kullanabiliriz. İkinci parametre olarak bir dizi verildiğinde bir
rotanın nasıl göründüğüne bir bakalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', array(function()
{
return View::make('hello');
}));
Görüyorsunuz, oldukça benzer. Yaptığımız Closure’ı diziye çevirmek. O aynen önceki yaptığı
işi görür. Aslında, closure’ı dizide tuttuğumuz sürece, başka değerler dahil edebiliriz. (Çevirenin
notu: hello görünümünün Laravel ilk kurulduğunda ön tanımlı olarak açılış sayfası göstermek
için oluşturulan görünüm olduğunu biliyorsunuz.) Şimdi filtreyi nasıl bağlayacağımıza geçiyoruz.
‘before’ filtre seçeneğine bakarak başlayalım.
Filtreler
1
74
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', array(
'before' => 'dogumgunu:12/12',
function()
{
return View::make('hello');
}
));
Görebileceğiniz gibi, dizimizin içinde başka bir seçenek oluşturduk. Dizideki ‘before’ anahtarı
framework’e rota mantığı çalıştırılmadan önce ‘dogumgunu’ filtresini çalıştırmak istediğimizi
söyler. ‘dogumgunu’ değeri filtremize verdiğimiz takma ad ile eşleşmektedir.
İlerleyelim ve /‘yi ziyaret ederek rotamızı çalıştıralım. Şimdi, bu günün Aralık’ın 12’si olmadığını
varsayarsak, bu durumda Laravel karşılama sayfasını göreceksiniz. Çünkü filtrenin şartlı mantığından kalınmış ve bir cevap döndürülmemiştir.
Pekiyi, filtre şartı geçip de cevap döndürdüğü zaman ne olacağını görmek için 12 Aralık olana kadar
bekleyelim.
Şaka yapıyorum, en iyisi filtreyi geçmeye zorlayacak şekilde değiştirelim. Şartı, boolean değer true
olarak değiştirebiliriz.
1
<?php
2
3
// app/filters.php
4
5
6
7
8
9
10
Route::filter('dogumgunu', function()
{
if (true) {
return View::make('dogumgunu');
}
});
Başlayalım, bir şeylerin değişip değişmediğini görmek için /‘i ziyaret edelim. Yaşasın, bu benim
doğum günüm! Benim için mutlu yıllar şarkısı söyleyelim. Aslında, Aralık’a kadar beklemek
lazım. O zaman doğumgünü filtre mantığının geçtiğini ve mutlu yıllar görünümü döndürüldüğünü
görebileceğiz.
Bir rota dizisinin ‘after’ seçeneğini kullanarak bir filtre bağlayabiliriz, bu tür filtre rota mantığınızdan
sonra çalıştırılacaktır. İşte bir örnek:
Filtreler
1
75
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', array(
'after' => 'dogumgunu',
function()
{
return View::make('hello');
}
));
Ancak, aklınızda tutmanız gereken bir şey var, after filtresi cevabın yerine ikame edilemez.
Dolayısıyla, ‘after’ kullanıldığı zaman bizim dogumgunu filtresi anlamsız olacaktır. Yine de bazı
günlüğe yazma işleri veya temizleme operasyonları yapabilirsiniz. İhtiyacınız olduğunda onun orada
olduğunu unutmayın yeter!
Çoklu Filtreler
Bilmeniz gereken başka bir şey de, bir rotaya istediğiniz sayıda filtre uygulayabileceğinizdir. Bu
eylemin bir örneğini görelim. İlk olarak, çoklu before filtreleri bağlayalım:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', array(
'before' => 'dogumgunu|yilbasi',
function()
{
return View::make('hello');
}
));
Burada rotaya hem ‘dogumgunu’ hem de ‘yilbasi’ before filtreleri bağladık. Yeni ‘yilbasi’ filtresinin
ne yapacağının mantığını senin hayal gücüne bırakıyorum ancak marifetli bir şey yapacağından
eminim.
Boru | karakteri bir filtre listesini ayırmakta kullanılır. Liste soldan sağa doğru çalıştırılır ve bir
cevap döndüren ilk filtre, isteği sonlandıracak ve o cevap sonuç olarak sunulacaktır.
İsterseniz çoklu filtre vermek yerine bir dizi de kullanabilirsiniz. Bu size belki daha ‘phpemsi’
gelebilir.
Filtreler
1
76
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', array(
'before' => array('dogumgunu', 'yilbasi'),
function()
{
return View::make('hello');
}
));
Size hangisi uygunsa onu kullanın, ben şahsen dizileri seviyorum. İsterseniz bir ‘before’ ve ‘after’
filtresini aynı anda da ekleyebilirsiniz, bunun gibi:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
12
Route::get('/', array(
'before'
=> 'dogumgunu',
'after'
=> 'yilbasi',
function()
{
return View::make('hello');
}
));
Doğal olarak, ilk önce before filtresi çalışacak, sonra rota mantığı ve son olarak da after filtresi
çalışacaktır.
İyi, filtreler tamam mı diyorsunuz? Bırakıp gitmek yok!
Filtre Parametreleri
Tıpkı PHP fonksiyonları gibi, filtreler de parametre kabul edebilirler. Bu, tekrarlardan kaçınmak için
harika bir yoldur ve esneklik artışı imkanı verir. Her zaman olduğu gibi, bir örnekle gidelim.
Filtreler
1
77
<?php
2
3
// app/filters.php
4
5
// before
6
7
8
Route::filter('test', function($route, $request)
{
9
10
});
11
12
// after
13
14
15
Route::filter('test', function($route, $request, $response)
{
16
17
});
Bir dakika, neden orada iki filtre var?
İyi fark ettiniz! Aslında onlar aynı filtre, ama yine de sizin sorunuz geçerli. Gördüğünüz gibi Laravel
‘before’ ve ‘after’ filtreleri için farklı parametre setleri sunmaktadır. Her iki filtrenin de $route ve
$request değişkenleri aldığını unutmayın. Aslında bunlara istediğiniz ismi verebilirsiniz ancak bu
şekilde isim vermemin bir nedeni var.
Eğer ilk parametreye var_dump() yaparsanız onun bir Illuminate\Routing\Route olgusu olduğunu
göreceksiniz. Hatırlayacaksınız, ‘Illuminate’ Laravel 4 bileşenleri için kullanılan kod adıdır. ‘Route’
sınıfı rotalama katmanı tarafından kullanılan bir rotayı temsil eder. Bu olgu, çalışmakta olan
güncel rotayı temsil eder. Zekice değil mi? ‘Route’ olgusu dev gibidir, bu Gallerli kurnaz adama
inanmıyorsanız gidin onu var_dump() yapın. İçindeki bilgilerin ayrıntısını sorgulayabilir, hatta
framework’ü manipüle etmek için bazı değerleri değiştirebilirsiniz. Bununla birlikte, bu ileri bir
konudur ve bu bölümün kapsamı içinde değildir, o yüzden en iyisi biz sonraki parametreye bakalım.
Tahmin edebileceğiniz gibi, sonraki parametre güncel istek nesnesinin bir olgusudur. Web sunucunuza gönderilen isteğin durumunu Illuminate\Http\Request olgusu temsil eder. Bu olgu zengin
ek bilgilerle birlikte URL’yi ve istekle geçirilen veriyi taşır.
After filtresi ek bir parametre alır, eylemi yapan rota filtresinden dönen bir cevap nesnesi olgusunu.
Bu olgu güncel isteğin cevabı olarak sunulan neyse odur.
Pekiyi, Laravel’in bize verdiği bu parametreler frameworkun ileri kullanıcıları için yararlı olabilir
ancak biz rota filtrelerimize kendi parametrelerimizi verebilsek harika olmaz mıydı? Bunu nasıl
yapabileceğimizi bir görelim.
İlk olarak bizim filtre Closure’una yer tutucu bir değişken eklememiz gerekiyor, bu değişken
Laravel’in kendi sağladığından daha sonra gelmelidir, bunun gibi:
Filtreler
1
78
<?php
2
3
// app/filters.php
4
5
6
7
8
9
10
Route::filter('dogumgunu', function($route, $request, $tarih)
{
if (date('d/m') == $tarih) {
return View::make('dogumgunu');
}
});
Bizim dogumgunu filtremiz bir $tarih parametresi kabul edecek şekilde değişmiş oldu. Eğer güncel
tarih verilen tarihe uyarsa bu durumda dogumgunu filtresi çalışacaktır.
Şimdi de rota filtrelerine parametrelerin nasıl verileceğini öğrenmemiz gerekiyor. Bir bakalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', array(
'before'
=> 'dogumgunu:12/12',
function()
{
return View::make('hello');
}
));
Rotaya atadığımızda iki nokta üst üste : karakterinden sonra gelen parametre filtremize geçirilir.
Şimdi bunu test edelim, tarihi bu günkü tarihe değiştirelim ve filtrenin ateşlendiğini izleyelim.
Eğer ek parametreler vermek istersek, Closure’de fazladan yer tutucu değişkenler vermemiz gerekiyor. Bu şöyle bir şey olacaktır.
1
<?php
2
3
// app/filters.php
4
5
6
7
8
Route::filter('dogumgunu', function($route, $request, $birinci, $ikinci, $ucuncu)
{
return "{$birinci} - {$ikinci} - {$ucuncu}";
});
Filtreler
79
İstediğimiz kadar parametre alabiliriz. Birden çok parametre vermek için öncelikle filtre adı ile
filtrenin parametreleri arasına iki nokta üst üste : eklemeliyiz. Parametrelerin kendileri de virgül
ile , ayrılmalıdır. İşte bir örnek:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', array(
'before'
=> 'dogumgunu:falan,filan,gibi',
function()
{
return View::make('hello');
}
));
‘falan’, ‘filan’ ve ‘gibi’ değerleri filtreye eklediğimiz yer tutuculara geçirilecektir. Tıpkı fonksiyonlarda olduğu gibi filtre parametrelerine ön tanımlı değerler atayabiliriz, böylece onları opsiyonel
yapmış oluruz. İşte bir örnek:
1
<?php
2
3
// app/filter.php
4
5
6
7
8
Route::filter('ornek', function($route, $request, $opsiyonel = 'Aynen!')
{
return $opsiyonel;
});
Opsiyonel parametreyi vermek veya vermemek. Bu size kalmış, O sizin frameworkünüz!
Filtrenizi daha verimli yapmak için istediğiniz kadar parametre kullanmakta serbestsiniz. Bu harika
özelliğin avantajını kullanın.
Filtre Sınıfları
Closure’ler harika. Bunlar gerçekten kullanışlıdır ve örneklerimde iyi iş yapar. Bununla birlikte,
bunlar yazdığımız mantığa sarılı kalırlar. Onları başlatamayız, bu da onların test edilmesini
zorlaştırır.
İşte bu sebeple, bir Closure gerektiren her Laravel özelliği bir alternatife de sahiptir. Bir PHP sınıfına.
Filtrelerimizi temsil etmek üzere bir sınıfı nasıl kullanacağımızı görelim.
Filtreler
80
Sınıf yapmadan önce, onu koyacak bir yere ihtiyacımız var. Şimdi /app klasöründe filters denen
yeni bir klasör oluşturalım ve bu yeni klasörü de içermesi için composer.json classmap’ımızı
güncelleyelim.
1
2
3
4
5
6
7
8
9
10
11
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/filters",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php"
]
}
Şimdi bizim dogumgunu filtremiz için yeni bir sınıf oluşturalım. İşte yapıyoruz:
1
<?php
2
3
// app/filters/Dogumgunu.php
4
5
6
7
8
9
10
11
12
13
class DogumgunuFilter
{
public function filter($route, $request, $tarih)
{
if (date('d/m') == $tarih) {
return View::make('dogumgunu');
}
}
}
Ben bu sınıfa ‘DogumgunuFilter’ adını verdim, siz ‘Filter’ son ekini kullanmak zorunda değilsiniz,
fakat ben böyle yapmayı seviyorum, gerisi size kalmış. Fakat zorunda olduğunuz bir şey var,
filter() metodu. Bu metod tıpkı bir Closure gibi çalışır. Aslında, tıpkı Closure gibi çalıştığı için
onu tekrar açıklamaya gerek yok. Öyle yapmak yerine bir filtrenin bir rotaya nasıl takılacağını
görebiliriz.
Öncelikle bir filtre takma adı oluşturmamız gerekiyor, Bir kez daha biz Route::filter() metodunu
kullanacağız. Bununla birlikte, bu sefer ikinci parametre olarak bir closure yerine bir string
geçeceğiz. Bunun gibi:
Filtreler
1
81
<?php
2
3
// app/routes.php
4
5
Route::filter('dogumgunu', 'DogumgunuFilter');
Bu metodun ikinci parametresi kullanacağımız filtre sınıfını tanımlayan bir stringtir. Eğer filtre sınıfı
bir aduzayı içinde ise, o zaman aduzayını da vermemiz gerekiyor.
Artık filtre takma adı oluşturduğumuza göre, rotaya bunu aynen daha önce yaptığımız gibi
ekleyebiliriz.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', array(
'before'
=> 'dogumgunu',
function()
{
return View::make('hello');
}
));
Tabii ki, Composer ve Laravel’in bizim filtre sınıfımızı bulabilmeleri için öncelikle composer
dump-autoload komutunu çalıştırmamız gerektiğini unutmayın.
Eğer kodunuzu tam olarak test etmek amacındaysanız, işiniz için en iyisi filtreleri sınıf olarak
yazmaktır. İlerideki bir bölümde test konusunu daha ayrıntılı göreceğiz.
Evrensel Filtreler
Eğer /app/filters.php dosyasının içine bakarsanız garip görünen iki filtre göreceksiniz. Bunlar
evrensel filtrelerdir ve uygulamanıza yapılan her istekten önce ve sonra çalışırlar.
Filtreler
1
82
<?php
2
3
// app/filters.php
4
5
6
7
8
App::before(function($request)
{
//
});
9
10
11
12
13
14
App::after(function($request, $response)
{
//
});
Bunlar ön tanımlı olarak tüm rotalara uygulanmaları dışında tam olarak normal filtreler gibi
çalışırlar. Yani bizim rotalarımızın before ve after dizi indekslerine bunları eklememize gerek
yoktur.
Default Filtreler
app/filters.phpde sizin için zaten oluşturulmuş bazı filtreler vardır. Bunlardan ilk üçüne bakalım.
1
<?php
2
3
// app/filters.php
4
5
6
7
8
Route::filter('auth', function()
{
if (Auth::guest()) return Redirect::guest('login');
});
9
10
11
12
13
14
Route::filter('auth.basic', function()
{
return Auth::basic();
});
15
16
17
18
19
Route::filter('guest', function()
{
if (Auth::check()) return Redirect::to('/');
});
Filtreler
83
Bu filtrelerin hepsi de Laravel’in kimlik doğrulama katmanı ile ilgilidir. Bunlar web uygulamamıza
o anda giriş yapmış veya yapmamış kullanıcılara rota erişimini kısıtlamak için kullanılabilir.
Sonraki bölümlerin birinde kimlik doğrulama katmanına daha yakından bakacağız ve bu filtrelerin
içeriği daha anlaşılır olacaktır. Şimdilik, bunların sizi orada beklediğini bilmeniz yeterli!
Dördüncü filtre siteler arası istek sahtekarlığı filtresidir ve şöyle görünmektedir:
1
<?php
2
3
// app/filters.php
4
5
6
7
8
9
10
11
Route::filter('csrf', function()
{
if (Session::token() != Input::get('_token'))
{
throw new Illuminate\Session\TokenMismatchException;
}
});
Rotalarınızı sizin uygulamanızdan başka bir kaynaktan post edilen isteklerden korumak için bu
filtreyi ilgili rotalarınıza bağlayabilirsiniz. Bu çok yararlı bir güvenlik önlemi olup, esas olarak
formlar veya veri gönderimi rotalarını korumak için kullanılmaktadır.
Laravel’in sağladığı filtrelerin avantajını kullanmaktan çekinmeyin, onlar size zaman kazandırmak
için oradalar.
Desen Filtreleri
Filtreyi tüm rotalarınıza elle bağlamak istemezsiniz değil mi? Hayır sizi suçlamıyorum. Parmaklar
yorulur, bir kitap yazdığım için bunu biliyorum. Zavallı küçük parmaklarınızı korumanın bir yolunu
bulmaya çalışayım. Burada bir desen filtresi var.
Desen filtreleri jokerli bir rota deseni vererek bir before filtresini çok sayıda rotaya eşlemenize imkan
verecektir. Bunu eylemde görelim.
1
<?php
2
3
// app/routes.php
4
5
Route::when('profile/*', 'dogumgunu');
Yukarıdaki Route::when() metodu, ‘profile/‘ ile başlayan tüm rota URI’lerinde ‘dogumgunu’ filtresini çalıştıracaktır. İlk parametredeki yıldız bir joker olarak davranacaktır. Bu, bir before filtresini
çok sayıda farklı rotaya bir seferde bağlamak için harika bir yoldur.
Denetçiler (Controllers)
Denetçilerin Oluşturulması
Basit rotalama bölümünde rotaların uygulamamızın yapısını oluşturan, küçük mantık ceplerinin,
closure’lara nasıl bağlandığını öğrenmiştik. Closure’lar bir uygulama yazmanın hoş ve hızlı bir
yoludur ve şahsen ben bu kitabın kod örnekleri içinde harika göründüklerine inanıyorum. Ancak,
uygulama mantığını barındırmak için tercih edilen yol Controller’dir.
Controller, rotalama mantığı için kullanılan bir sınıftır. Normalde, bir Denetçi sınıfı eylem (action)
olarak bilinen çok sayıda publik metod içerecektir. Eylemleri önceki bölümde kullanmış olduğumuz
closure’in direkt alternatifi olarak düşünebilirsiniz, bunlar hem görünüm olarak hem de işlevsel
olarak çok benzerdir.
Bir örnek göstermeden bir şey açıklamayı sevmiyorum. O yüzden gidip bir denetçiye bakalım.
İşte benim daha önce yaptıklarımdan birisi! Cue Blue Peter teması. Gerçekte bu son referans
sadece İngiliz halkı için mantıklı olabilir. Boşverin, yaptım artık ve onu silecek cesaretim yok! Peki,
denetçiler…
1
<?php
2
3
// app/controllers/MakaleController.php
4
5
6
7
8
9
10
class MakaleController extends BaseController
{
public function tumGoster()
{
return View::make('liste');
}
11
public function tekGoster($makaleId)
{
return View::make('tek');
}
12
13
14
15
16
}
İşte bizim denetçimiz! Güzel ve basit. Bu örnek bir blog veya diğer bazı CMS biçimleri için uygun
olacaktır. İdeal olarak, bir blog tüm makalelerin bir listesini gösterecek bir sayfaya ve ayrıntılı olarak
tek bir makaleyi gösterecek başka bir sayfaya sahip olacaktır. Bu aktivitelerin her ikisi de Makale
Denetçiler (Controllers)
85
kavramıyla ilgilidir, dolayısıyla bu mantığı bir arada gruplayabiliriz. Bu nedenle mantık tek bir
MakaleController içine alınmıştır.
Açıkçası, Denetçiye istediğiniz ismi verebilirsiniz. Sizin denetçiniz BaseController veya Controller‘den
birisini genişlettiği sürece Laravel ne yapıyor olduğunuzu bilecektir. Yine de, bir denetçi ismine
Controller son eki vermek, örneğin MakaleController demek web geliştiricilerinin kullandığı bir
standarttır. Eğer başkalarıyla çalışmayı planlıyorsanız, bu durumda standartlar son derece yararlıdır.
Denetçimiz, Laravel’in bizim için oluşturmuş olduğu app/controllers dizininde oluşturulmuştur.
Bu dizin, ön tanımlı olarak bizim composer.json’dan yüklenen bir ‘classmap dosyası’ oluyor. Şayet
app/controllers sizin iş akışınıza uygun değilse, o zaman bu dosyayı istediğiniz yere koyabilirsiniz.
Ancak, sınıfın Composer tarafından otomatik yüklenecek olmasını temin etmelisiniz. Bu konuda
daha ayrıntılı bilgi için lütfen Ön Bilgiler bölümündeki Composer kesimine başvurunuz.
Denetçimizin sınıf metodları gerçekte mantığımızı içeren şeylerdir. Tekrar ifade ediyorum, bunlara
istediğiniz ismi verebilirsiniz ama ben şahsen eğer sonuç bir web sayfası göstermek olacaksa bir ön ek
olarak ’show’ kullanmayı seviyorum. (Çeviri notu: Kitabın Türkçe çevirisinde ise dile daha uygun
olması açısından son ek olarak ’goster’ kullanmayı tercih ettim) Bu metodlar Laravel’in bunlara
rotalama yapabilmesi için public olmalıdır. Soyutlamak için sınıfa private metodlar ekleyebilirsiniz
ancak bunlar rotalanamaz. Aslında bu tür kodların konacağı daha iyi bir yer vardır, bunu modeller
bölümünde öğreneceğiz.
Birinci eylemimize yakından bakalım, bir blog makale listesini göstermek için kullanılacaktır.
1
2
3
4
public function tumGoster()
{
return View::make('liste');
}
Evet, ne kadar da tanıdık geliyor değil mi? Aynı etkiyi elde etmek için kullanabileceğimiz bir rota
closure’u ile karşılaştıralım.
1
2
3
4
Route::get('liste', function()
{
return View::make('liste');
});
Görebileceğiniz gibi iç fonksiyon neredeyse aynı. Tek fark denetçi eyleminin bir adı vardır, closure
ise anonimdir. Gerçekten de, denetçi eyleminde Closure’de olabilecek her kod bulunabilir. Yani
öğrendiğimiz her şey burada bile uygulanabilir durumdadır. Yazık, farklı olmuş olsaydı denetçiler
üzerine başka bir kitap satabilirdim!
Yine de, iki kod parçacığı arasında bir diğer fark var. Basit Rotalama bölümünde bir URI’nin
bir Closure içinde yer alan mantık parçasına nasıl rotalanacağını öğrenmiştik. Yukarıdaki rotalı
closure örneğinde /liste URI’si bizim uygulama mantığımıza yönlendirilecektir. Ancak bizim
Denetçiler (Controllers)
86
Denetçi eylemimizde bir URI’den hiç bahsedilmemiş. Laravel onun rotasını bizim kontrollere
yönlendireceğini nereden biliyor? Haydi denetçi rotalamasına bir göz atalım, umarım sorumuza
bir cevap bulabiliriz.
Controller Rotaları
Controller’lar derli topludur ve ortak mantıkları birlikte gruplamak için temiz bir yol sağlar. Ancak,
kullanıcılarımız ilgili mantığa gerçekten ulaşamadığı sürece işe yarayamazlar. Neyse ki, bir URI’yi
bir denetçiye bağlama metodu da Closure’ler için kullandıklarımıza benzerdir. Yakından bakalım.
1
<?php
2
3
// app/routes.php
4
5
Route::get('liste', 'MakaleController@tumGoster');
Bir URI’yi bir denetçiye bağlayabilmek için /app/routes.php dosyasında yeni bir rota tanımlamalıyız. Closure rotalamasında kullandığımız aynı Route::get() metodunu kullanıyoruz. Ancak ikinci
parametre tamamen farklıdır. Bu sefer bir string var.
Bu string bir -de işareti (@) ile ayrılmış iki kesimden oluşuyor. Son kesimde oluşturduğumuz
Denetçiye bir daha bakalım.
1
<?php
2
3
// app/controllers/MakaleController.php
4
5
6
7
8
9
10
class MakaleController extends BaseController
{
public function tumGoster()
{
return View::make('liste');
}
11
public function tekGoster($makaleId)
{
return View::make('tek');
}
12
13
14
15
16
}
Örnekte görüyoruz ki sınıf adımız MakaleControllerdır ve kendisine yönlendirmek istediğimiz
eyleme tumGoster adını vermişiz. Aralarında bir -de işareti (@) olacak şekilde ikisini bir araya
getirelim.
Denetçiler (Controllers)
1
87
MakaleController@tumGoster
Bu kadar basit işte. Artık temel rota bölümünde keşfettiğimiz metodların hepsini kullanabiliriz ve
onlara denetçileri gösterebiliriz. Örneğin bir POST HTTP istek fiiline cevap verecek bir denetçi şöyle
olacaktır.
1
<?php
2
3
// app/routes.php
4
5
Route::post('makale/yeni', 'MakaleController@yeniMakale');
Siz oldukça akıllı dostlarsınız, bu kitabı satın almıştınız değil mi? İşte şimdi görüyorsunuz ki, yukarıdaki rota /makale/yeni URI’sine yapılan POST isteklerine cevap verecektir ve MakaleController‘deki
yeniMakale() tarafından işlenecektir.
Bilmeniz gereken zarif bir şey: Denetçiniz aduzayı olabilir ve Laravel gözünü kırpmaz. Önemli olan
rota deklarasyonunuza aduzayını dahil ediniz, her şey mükemmel olacak! Bakın nasıl olduğunu
göstereyim.
1
<?php
2
3
// app/controllers/Makale.php
4
5
namespace Blog\Controller;
6
7
8
use View;
use BaseController;
9
10
11
12
13
14
15
16
class Makale extends BaseController
{
public function tumGoster()
{
return View::make('liste');
}
}
Burada diğer örnekte kullanılana benzer bir Controller var. Ancak bu sefer Blog\Controller aduzayı
içinde yer alıyor. Onun konumu aduzayının Controller kesimi içinde olduğu için ben sınıf adına
Controller son eki eklemedim. Bu benim kendi kişisel tercihim, bu eki koyup koymama kararını
size bırakıyorum.
Şimdi bu aduzaylı denetçiyi nasıl rotalayabileceğimizi görelim. Büyük ihtimalle zaten tahmin
etmişsinizdir!
Denetçiler (Controllers)
1
88
<?php
2
3
// app/routes.php
4
5
Route::post('liste', 'Blog\Controller\Makale@tumGoster');
Tıpkı önceki gibi, sadece bu sefer denetçinin adının önüne onun aduzayı eklenmiştir. Bakın,
aduzayının bir şeyleri karmaşıklaştırması gerekmiyor! Aduzaylı denetçinizi iç içe bir dizinde hatta
bir PSR-0 yükleme şemasında başka bir yerde bile saklayabilirsiniz. Composer sizin sınıfınızı nerede
bulacağını bildiği sürece Laravel bunu sorun yapmaz, onu kullanabilecektir.
RESTful Denetçiler
Laravel çözümler sunar, çoğunu biliyoruz. Seçenekler de verir, RESTful denetçileri bunun en önemli
örneğidir.
Rota metodlarını kullanarak eşleştirmek istediğimiz HTTP istek fiillerini tanımlayabildiğimizi
biliyoruz, Closure’lara rotalama yapıldığında bu gerçekten işimizi kolaylaştırıyor. Bununla birlikte,
denetçilere rotalama yaptığınız zaman istek fiilinin tanımını uygulamanız mantığında tutmak
isteyebilirsiniz. İyi bir haberim var, Laravel bu alternatif yapılandırmayı sağlamaktadır.
Denetçimizde küçük bir değişiklik yapmaya ne dersiniz?
1
<?php
2
3
// app/controllers/Makale.php
4
5
namespace Blog\Controller;
6
7
8
use View;
use BaseController;
9
10
11
12
13
14
15
class Makale extends BaseController
{
public function getForm()
{
return View::make('form');
}
16
public function postForm()
{
// Formu işleyen kod.
}
17
18
19
20
21
}
Denetçiler (Controllers)
89
Bir kere daha Makale denetçimiz var. Eylemlerin amacı yeni bir blog makalesi oluşturmak için bir
form sağlamak ve yeni bir blog makalesi oluşturulmasını işlemektir. Eylemlerin adlarının başına get
ve post getirilmiş olduğunu fark edeceksiniz. Bunlar bizim HTTP istek fiillerimizdir.
Yani, yukarıdaki örnekte son noktaları aşağıdaki URL’ler için ifade ettiğimizi düşünebilirsiniz:
1
2
GET /form
POST /form
RESTful denetçimize nasıl rotalama yapacağımızı da merak ediyor olabilirsiniz. Gördüğünüz gibi,
burada Route sınıfında fiil metodlarını kullanmak pek mantıklı olmayacaktır. O zaman, bireysel
eylemlere rota yapmaya elveda diyelim ve başka bir rotalama metoduna göz atalım.
1
<?php
2
3
// app/routes.php
4
5
Route::controller('makale', 'Blog\Controller\Makale');
Bu tek metod bizim RESTful denetçimizde sunulan tüm eylemleri rotalayacaktır. Metodun yazılışına
yakından bakalım.
İlk parametre taban URL’dir. Normalde RESTful rotalama bir nesne sunmak için kullanılır, bu
yüzden çoğu durumda taban URL o nesnenin adı olacaktır. Bunu, RESTful denetçimizin içinde
oluşturduğumuz eylemlere bir ön ekmiş gibi düşünebilirsiniz.
İkinci parametre zaten bildiğiniz bir şey. Kendisine rotalamak istediğimiz denetçidir. Tekrar ediyorum, Laravel aduzaylı bir denetçiyi bir rota hedefi olarak mutlulukla kabul edecektir, dolayısıyla
denetçilerinizi kendi ihtiyaçlarınıza uyacak şekilde organize etmekte özgürsünüz.
Görmüş olduğunuz gibi, denetçilerinize rotalama yapmak için bu metodun kullanılması orijinal rotalama yöntemlerine göre ayrı bir avantaj sağlar. Bu metod sayesinde, her eyleme bağımsız rotalama
yapmak zorunda kalmak yerine bir denetçi için sadece tek bir rotalama girişi sağlayabileceğiz.
Blade
Bu bölümde Kılıç Ustası (Blade) olmayı öğreneceğiz. Buna ihtiyacınız olacak. Bir PHP ustası olarak
hak ettiğiniz yeri alabilmeniz için silahlı mücadelede Ulu Lord Otwell’e maydan okumanız ve onu
yenilgiye uğratmanız gerekiyor.
Geçiş adetimiz böyledir. Ancak ondan sonra Laravel konseyi arasında hak ettiğiniz yeri alabilecek
ve Phil Sturgeon’ın masasında bir yer kazanabileceksiniz.
Ayda bir kez de, Laravel’in tepesindeki, pandalara binmiş korkunç konsey üyeleri diğer framework
geliştiricileri ile savaşmak için PHP savaş alanına geçerler. Bizim yanımızda mücadeleye katılmak
ve Laravel şerefine kavgaya tutuşmak isterseniz Kılıç kullanmayı öğrenmek zorundasınız.
Evet, ‘zorundasınız’ güçlü bir kelimedir. Demek istiyorum ki, aynı zamanda Blade şablonu ustası da
olabilirsiniz. Abartıldığı kadar değildir ve kızgın pandalara binmek de yok. Buna rağmen oldukça
kullanışlıdır ve belki de kodlayıcılar için sert mücadelelerden daha uygundur?
Evet, tamamsa Blade şablonlarına bir göz atalım.
Bu ‘Blade’ (Kılıç/Bıçak) adının da nereden geldiğini merak edebilirsiniz? Bu bölümü yazmaya
başladığımda Taylor’a sormaya karar vermiştim. Dönen cevapta, .NET web geliştirme platformunda
Blade söz diziminin türetilmiş olduğu Ustura (‘Razor’) adında bir şablonlama aracı olduğunu söyledi.
Ustura… bıçak… tıraş bıçağı. Hepsi bu. Komik bir şey yok, özgünüm. :(
Aslında, az önce söylediklerimi unutun. Öyküyü yeniden keşfedelim. Sadece bizim aramızda. Şablonlama dilinin adı Vampir avlama günlerinde Taylor ‘Blade’ ile kendine güvenini değiştirmesinden
geliyor. Gerçek öykü bu.
Peki, şimdi bir şablon yapalım.
Şablonların Oluşturulması
Biliyorum, biliyorum, ben zaten görünüm oluşturmayı size öğrettim değil mi? Görünümler uygulamanızın görsel tarafını mantık tarafından ayırmakta gerçekten yararlıdır ancak bu onlar üzerine
geliştirme yapılamaz anlamına gelmez.
Standart PHP şablonlarıyla yaşadığımız problem, mantık kısımlarının sağladığı verileri kullanmak
için onların içine çirkin php tagları eklemek zorunda olmamızdır. Bizim düzgün HTML şablonları
içinde yersiz görünürler. Onları kirletir! Bu beni öyle kızdırıyor ki… Ben… Ben şimdi. Hayır, bir an
için sakin olun.
Erm…
Blade
91
Tamam, öfkem dindi şimdi. Bu kötü PHP pisliğini yolumuzdan kaldırmak için bir Blade şablonu
oluşturalım. Başlamak için yeni bir dosya oluşturmamız gerekiyor. Blade şablonları standart view
dosyaları ile aynı konumda dururlar. Tek farklılık sadece .php yerine .blade.php uzantısını
kullanmalarıdır.
Şimdi basit bir şablon oluşturalım.
1
<!-- app/views/ornek.blade.php -->
2
3
4
<h1>Değerli Lord Otwell</h1>
<p>Laravel'in onuru için seni bir düelloya davet ediyorum.</p>
5
6
<?php echo $sincap; ?>
Burada blade şablonumuz oldu ve daha önce gördüklerimize benziyor. Bunun nedeni Blade’in
dosyayı ilk önce PHP olarak parse etmesindendir. Bizim $sincap‘ı görüyor musunuz? Her view
dosyasında bir sincap olmak zorundadır. Tamam, bu doğru değil, önce PHP parse edebileceğini
göstermek için koydum.
Biz bunu, normal bir view kullanımıyla aynı sözdizimi kullanarak gösterebiliriz. Bunun View::make()
metoduna ornek.blade geçirilmesini gerektirdiğini düşünebilirsin, ama bu doğru olmayacaktır.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('ornek', function()
{
return View::make('ornek');
});
Gördünüz mü? Laravel neyin bir Blade şablonu olduğunu bilir ve ona göre bakar. Bu sebeple,
View::make() cümlesi hiç değişmemiştir. Ne kolaylık ama!
Blade kullanımıyla ilgili bazı numaralar var. İlkini görelim mi?
PHP Çıktısı
Şablonlarınızın birçoğu uygulamanızın mantık kısmı tarafından sağlanan veriyi çıktılama (echo
yaptırma) işlerini içerir. Normalde buna benzer bir şeyler görünür:
Blade
1
92
<!-- app/views/ornek.blade.php -->
2
3
<p><?php echo $vampirAvcisiTaylor; ?></p>
Tam ayrıntılı değil ama geliştirilebilir. Şimdi de Blade şablon kullanarak değerleri nasıl echo
yaptıracağımızı görelim.
1
<!-- app/views/ornek.blade.php -->
2
3
<p>{{ $vampirAvcisiTaylor }}</p>
Şablon işlendiği zaman {{ çifte küme parantezleri }} ile sarmalanmış her şey Blade tarafından bir
echo’ya dönüştürülür. Bu çok daha temiz bir sözdizimidir ve yazması da çok kolaydır.
Blade şablon direktifleri doğrudan PHP’ye çevrildiği için, bu parantezler içerisinde metodlar da
dahil, her türlü PHP kodunu kullanabiliriz.
1
<!-- app/views/ornek.blade.php -->
2
3
<p>{{ date('d/m/y') }}</p>
Kapatmak için noktalı virgül kullanmanıza da gerek yok, sizin yerinize bunu Laraval yapar.
Bazen çıktınızdaki bir değeri escape ederek kendinizi korumak isteyebilirsiniz. Bu amaçla strip_tags() ve htmlentities() gibi metodları kullanmayı biliyor olabilirsiniz. Niçin? Bu şablonun
çıktısını ele alalım o zaman.
1
<!-- app/views/ornek.blade.php -->
2
3
<p>{{ '<script>alert(“BİR KANGAL SUCUK!”);</script>' }}</p>
Çok kötü bir kod parçası! Bu, sayfamızın içine JavaScript kodları enjekte edilmesine sebep olacak
ve ‘BİR KANGAL SUCUK!’ metnini içeren bir tarayıcı popup’ı gösterilecek. Neresi kötü ki! Pis ruby
geliştiricileri daima websitelerimizi kırmaya çalışırlar.
Şablonlarımızı çıktılardan koruyacak güce sahibiz. Eğer {{ iki }} yerine {{{ üç küme parantezi }}}
kullanırsak bu durumda çıktımız escape edilecek, açılı parantezler (yani, < ve >) HTML antitelerine
dönüştürülecek ve JavaScript sayfa içerisinde metin olarak gösterilecektir. Zararsız!
Bunu eylemde görelim.
Blade
1
93
<!-- app/views/ornek.blade.php -->
2
3
<p>{{{ '<script>alert("BİR KANGAL SUCUK!");</script>' }}}</p>
Bu pis Javascript kodunun etrafındaki parantezlerin sayısını değiştirdik. Nasıl göründüğünü görmek
için tarayıcıda sayfa kaynağına bakalım.
1
<!-- app/views/ornek.blade.php -->
2
3
<p>&lt;script&gt;alert(&quot;BİR KANGAL SUCUK!&quot;);&lt;/script&gt;</p>
Gördüğünüz gibi, HTML tagları ve diğer bazı karakterler karşılıkları olan HTML antiteleri ile
değiştirilmişlerdir. Sayfamız kurtuldu!
Devam ediyoruz!
Kontrol Yapıları
PHP’de çok sayıda kontrol yapısı vardır. If cümleleri, while, for ve foreach döngüleri. Bunları daha
önce duymamışsanız, bu kitap gerçekten size göre değildir!
Şablonlar içerisinde kontrol yapıları için iki nokta üst üste : kullanılan alternatif sözdizimi kullanımı
size tanıdık gelecektir. if cümleleri böyle görünecektir:
1
<!-- app/views/ornek.blade.php -->
2
3
4
5
6
7
<? if ($birsey) : ?>
<p>Bir şey doğrudur!</p>
<? else : ?>
<p>Bir şey yanlıştır!</p>
<? endif; ?>
Bu işe yarayacaktır ancak bunları yazmak çok eğlenceli değil. Bu sizi yavaşlatacaktır, neyse ki Blade
sizi kurtarmaya gelecektir!
Bir Blade şablonu içerisinde yukarıdaki kod parçası şöyle yazılır.
Blade
1
94
<!-- app/views/ornek.blade.php -->
2
3
4
5
6
7
@if ($birsey)
<p>Bir şey doğrudur!</p>
@else
<p>Bir şey yanlıştır!</p>
@endif
Bu daha temiz, değil mi? Neleri çıkarıp attığımıza bir göz atalım. En başta PHP açılma <? ve kapanma
?> tagları gitmiş. Büyük ihtimalle yazması en karmaşık olanlar bunlardı.
Ayrıca iki nokta üst üste : ve noktalı virgülü ; de çıkarabiliyoruz. Şablonlarımızda zaman kaybetmek
zorunda değiliz!
Son olarak, her zamanki sözdizimine bir ilave yapmışız. Kontrol cümlelerimizin olduğu satırların
başına bir -de @ sembolu getiriyoruz. Aslında tüm blade kontrol yapıları ve helper metodları başına
bu işaret getirilir, böylece şablon derleyicisi bunları nasıl işleyeceğini bilir.
Daha ileri bir örnek için karışıma bir elseif ekleyelim.
1
<!-- app/views/ornek.blade.php -->
2
3
4
5
6
7
8
9
@if ($birsey == 'Kırmızı Panda')
<p>Bir şey kırmızı, beyaz ve kahverengidir!</p>
@elseif ($birsey == 'Dev Panda')
<p>Bir şey siyah ve beyazdır!</p>
@else
<p>Bir şey bir sincap olabilir.</p>
@endif
Karışıma başka bir cümle ekledik, bunu yaparken aynı şekilde PHP tagları, iki nokta üst üste ve
noktalı virgülleri çıkardık ve başına @ işareti koyduk. Her şey mükemmel çalıştı.
İşte bir meydan okuma. Blade sözdizimiyle temsil edilen bir foreach PHP döngüsünü hayal etmeye
çalışın. Gözlerini kapat ve resmini hayal et. Odaklan… odaklan!
Buna benzedi mi?
95
Blade
1
<!-- app/views/ornek.blade.php -->
2
3
4
5
6
7
8
9
c~~p ,---------.
,---'oo )
\
( O O
)/
`=^='
/
\
,
.
/
\\ |-----'| /
||__|
|_|__|
Hayır? İyi, çünkü bu bir su aygırı. Ancak, eğer biraz aşağıdaki kod parçasına benziyorsa bir kurabiye
alabilirsin.
1
<!-- app/views/ornek.blade.php -->
2
3
4
5
@foreach ($birCokSey as $sey)
<p>{{ $sey }}</p>
@endforeach
Kurabiyenin tadını çıkar! Görebileceğin gibi döngü değerini çıktılamak için Blade {{ echo }}
sözdizimini kullandık. Bir for döngüsü tam düşündüğün gibi gözükür. Referans olması bakımından
bir örnek vereyim.
1
<!-- app/views/ornek.blade.php -->
2
3
4
5
@for ($i = 0; $i < 999; $i++)
<p>{{ $i }} kırmızı panda bile yeterli değildir!</p>
@endfor
Basit ve tam olarak ne bekliyorsak o. While döngüsü de aynı kuralı izler ancak yararlı bir referans
bölümü olmasını sağlamak için küçük bir örnek göstermek istiyorum.
1
<!-- app/views/ornek.blade.php -->
2
3
4
5
@while (Guzelmi($keiraKnightly))
<p>Büyük ihtimalle bu döngü asla bitmeyecek.</p>
@endwhile
Tamam, artık koşullu durum ustasısınız. Kimse sizi aşamalardan geçiremez, değil mi dostum?
PHP’nin ‘unless’ koşulu bile.
Yanılıyor muyum Dayle, o Ruby’de vardı. PHP’de var mıydı bilmi..
Tamam, yakaladınız. PHP’de ‘unless’ diye bir koşul durumu yok. Ancak, Blade ona imkan veren bir
helper sağlar. Haydi örneğimize bakalım.
Blade
1
96
<!-- app/views/ornek.blade.php -->
2
3
4
5
@unless (dunyaSonaEriyor())
<p>Hep gülümse.</p>
@endunless
Unless bir if cümlesinin tam karşıtıdır. Bir if cümlesi bir koşulun true bir değere eşit olup olmadığını
yoklar ve ondan sonra bir mantığı çalıştırır. Buna karşın unless cümlesi, sadece koşul false’e eşitse
çalışacaktır. Bunu kötümserler için bir kontrol yapısı olarak düşünebilirsiniz.
Şablonlar
Blade, şablonlarınızı daha kolay oluşturmanız ve yönetmeniz için diğer bazı helper metodlar
içermektedir. Ancak, bunlar görünümlerinizi sizin için yazmayacaklardır, belki de bunu Laravel
5’in görev listesine ekleyebiliriz?
• php artisan project:tamamla
• Görünüm kompozitörleri saç stillerini oluşturabilsin.
• Görünümler kendi kendine yazılabilsin.
Artık başlıyoruz. Bu özellikler konana kadar biz kendi şablonlarımızı kendimiz yazmak zorundayız.
Bu her şeyi bir dosyaya koymak zorundayız anlamına gelmez tabii ki.
PHP’de, güncel dosyanın içine bir dosya include() edebiliyor ve içeriğini çalıştırabiliyoruz.
Organizasyon amaçlarıyla görünümlerimizi de bu şekilde ayrı dosyalar haline ayırabiliriz. Laravel
bu amacı elde etmemize yardım eden @include() Blade helper metodu sağlamaktadır. Bu metod
sayesinde bir görünüm başka bir görünüme ithal edilip, içeriği bir Blade şablonu olarak parse edilir.
Bunun nasıl gerçekleştiğini bir örnek üzerinde görelim.
Sayfamızın ve belki de başka sayfaların başlığını taşıyan header.blade.php dosyası burası.
1
<!-- app/views/header.blade.php -->
2
3
<h1>Atlar ne zaman sucuk oluyor?</h1>
Bu da ilişkili footer şablonu.
1
<!-- app/views/footer.blade.php -->
2
3
<small>Verilen bilgiler 3 Mayıs 2013 tarihli araştırmalara dayalıdır.</small>
Asıl şablonumuz ise şu. Bizim rota Closure’umuz veya Controller eylemimiz tarafından gösterilecek
olan şablon.
Blade
1
97
<!-- app/views/ornek.blade.php -->
2
3
4
5
6
7
8
9
10
11
12
13
14
<!doctype html>
<html lang ="en">
<head>
<meta charset ="UTF-8">
<title>Atlar</title>
</head>
<body>
@include('header')
<p>Bu gece yarısından sonra efendim!</p>
@include('footer')
</body>
</html>
Görebileceğiniz gibi, ornek.blade.php şablonu içindeki helper metodları @include() helperini
kullanarak header ve footer şablonlarımızı çekiyor. Bu helper fonksiyonu, daha önce kullandığımız
View::make() metodundaki kısa formatla aynı şekilde bir görünümün adını parametre olarak alır.
Oluşan belgeye bir göz atalım.
1
2
3
4
5
6
7
8
9
10
11
12
<!doctype html>
<html lang ="en">
<head>
<meta charset ="UTF-8">
<title>Atlar</title>
</head>
<body>
<h1>Atlar ne zaman sucuk oluyor?</h1>
<p>Bu gece yarısından sonra efendim!</p>
<small>Verilen bilgiler 3 Mayıs 2013 tarihli araştırmalara dayalıdır.</small>
</body>
</html>
Dahil ettiğimiz şablonlar… güzel, sayfaya dahil edilmiştir. Bu bizim header ve footer şablonlarımızı
tekrarlı kullanılabilir ve KTE (Kendinizi Tekrar Etmeyin) yapar. Tekrar edilecek içerikten tasarruf
etmek ve içeriğini tek bir yerden düzenlenebilir hale getirmek için diğer sayfalara include edebiliyoruz. Bunu yapmanın daha da iyi bir yolu var, bu yüzden, okumaya devam edin!
Şablon Kalıtımı
Laravel Blade, kalıtımdan yararlanabilen şablonlar oluşturmak için bir yol sağlar. Birçok insan
bunu kafa karıştırıcı buluyor ancak aslında çok düzgün bir özelliktir. Yapabileceğim en iyi şekilde
Blade
98
basitleştirmeye çalışacağım ve umuyorum ki, şablon oluşturma sanatının keyifli bir deneyim
olduğunu göreceksiniz.
Herşeyden önce şablonlar hakkında düşünelim. Bir web sayfasının gerçekte her sayfada değişmeyen
bazı kısımları vardır. Bunlar göreceğimiz herhangi bir web sayfası için bulunması gereken taglardır.
İsterseniz buna demişbaş ya da klişe kodumuz diyelim. İşte bir örnek:
1
2
3
4
5
6
7
8
9
<!doctype html>
<html lang ="en">
<head>
<meta charset ="UTF-8">
<title></title>
</head>
<body>
</body>
</html>
Tüm sayfalarımız için bu düzeni kullanmak istiyoruz. Neden Laravel’e söylemiyoruz? Bunun bir
Blade düzeni olduğunu söyleyelim. Bunun için yapmamız gereken tek şey içerik ekleyeceğimiz
bazı yerleri tanımlamaktır. Laravel’de bu bölgelere ‘sections’ (kesimler) diyoruz. Bunları şöyle
tanımlıyoruz:
1
<!-- app/views/layouts/base.blade.php -->
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!doctype html>
<html lang ="en">
<head>
<meta charset ="UTF-8">
<title></title>
@section('head')
<link rel ="stylesheet" href ="style.css" />
@show
</head>
<body>
@yield('body')
</body>
</html>
Şimdi iki kesimi olan bir şablonumuz var. Önce body içindekine, yani en kolay olanına bakalım.
Şöyle görünüyor:
Blade
1
99
@yield('body')
Bu cümle Blade’e burada içini sonradan doldurabileceğimiz bir kesim oluşturmasını söyler. İlerde
onu tekrar ifade edebilmek için, biz ona ‘body’ takma adını veriyoruz.
Diğer kesim ise buna benziyor:
1
2
3
@section('head')
<link rel="stylesheet" href="style.css" />
@show
Bu ‘yield’ kesimine çok benzer ancak bazı varsayılan içerikleri sağlayabiliyorsunuz. Yukarıdaki
örnekte @section ile @show tagları arasındaki içerik, bir çocuk şablonu onu değiştirmeyi seçmediği
takdirde gösterilecektir.
Pekiyi bir çocuk şablondan ne anlıyoruz? Tamam, her zamanki gibi hemen bir örnek verelim.
1
<!-- app/views/home.blade.php -->
2
3
@extends('layouts.base')
4
5
6
7
8
@section('body')
<h1>Yaşasın!</h1>
<p>Bir şablonumuz var!</p>
@stop
Peki, ilerleyelim lütfen. İlk olarak ‘extends’ blade fonksiyonu var:
1
@extends('layouts.base')
Bu Blade’e içeriğimizi göstertmek için hangi düzeni kullanacağımızı söyler. Bu fonksiyona geçilecek
isim View::make()e geçtiğiniz gibi görünmelidir, dolayısıyla bu örnekte ‘app/views’ içindeki ‘layouts’ dizinindeki ‘base.blade.php’ dosyasını ifade etmiş oluyoruz. Görünümlerle iş yaparken nokta
(.) karakterinin bir dizin ayıracını temsil ettiğini hatırlayınız.
Laravel 3’te bu fonksiyona ‘@layout()’ denirdi, fakat Symfony’nin twig’i gibi diğer şablon motorlarıyla daha uyumlu hale getirmek için adı ‘@extends()’ olarak değiştirildi. Laravel 3 geliştiricilerinin
dikkatine!
Kullanacağımız düzeni artık biliyoruz, boşluklarını doldurmanın zamanı geldi. Ebeveyn şablon
içindeki kesimlere içerik enjekte etmek için @section blade fonksiyonunu kullanabiliriz. Buna
benzer:
Blade
1
2
3
4
100
@section('body')
<h1>Yaşasın!</h1>
<p>Bir şablonumuz var!</p>
@stop
‘@section’ fonksiyonuna parametre olarak, ebeveyn şablondaki kesime verdiğimiz takma adı
geçeriz. Hatırladınız mı? Ona ‘body’ adını vermiştik. ‘@section’ ve ‘@stop’ arasında yer alan her
şey ebeveyn şablonundaki ‘@yield(‘body’)’ kısmına enjekte edilecektir.
Bunu eylemde görmek için bir rota oluşturalım. Şablonu göstertmek için, gösterteceğimiz çocuk
şablonu için bir View::make() cevabı eklemek yeterli olacaktır. Şunun gibi:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('home');
});
Şimdi eğer / ziyaret edilir ve web tarayıcısındaki sayfa kaynağı görüntülenirse, sayfanın şuna
benzediğini görürüz:
1
2
3
4
5
6
7
8
9
10
11
12
<!doctype html>
<html lang ="en">
<head>
<meta charset ="UTF-8">
<title></title>
<link rel ="stylesheet" href ="style.css" />
</head>
<body>
<h1>Yaşasın!</h1>
<p>Bir şablonumuz var!</p>
</body>
</html>
Tamam, biçimlendirme biraz farklı olabilir ancak içerik aynı olacaktır. Bizim kesim ebeveyn şablona
enjekte edilmiş oldu. ‘head’ kesiminin içeriğini değiştirme yolu seçilmediği için, varsayılan değer
konulmuştur.
Gördüğünüz gibi, bu ebeveyn şablondan kalıtmak istediğimiz kadar çok çocuk şablonumuz olabilir.
Böylece demirbaş kodumuzu tekrarlamak zorunda kalmaktan kurtuluruz.
Şimdi çocuk şablonumuzda ‘head’ kesimi için bir içerik verecek şekilde küçük bir değişiklik yapalım.
Şöyle:
Blade
1
101
<!-- app/views/home.blade.php -->
2
3
@extends('layouts.base')
4
5
6
7
@section('head')
<link rel ="stylesheet" href ="digerstil.css" />
@stop
8
9
10
11
12
@section('body')
<h1>Yaşasın!</h1>
<p>Bir şablonumuz var!</p>
@stop
Düşündüğünüz gibi, head kesimine bizim ek CSS dosyamız enjekte edilecektir ve sayfanın kaynağı
görüntülendiğinde bu sefer şuna benzer:
1
2
3
4
5
6
7
8
9
10
11
12
<!doctype html>
<html lang ="en">
<head>
<meta charset ="UTF-8">
<title></title>
<link rel ="stylesheet" href ="digersitil.css" />
</head>
<body>
<h1>Yaşasın!</h1>
<p>Bir şablonumuz var!</p>
</body>
</html>
Head kesiminde ‘@section’ ve ‘@show’ arasında bazı varsayılan içerikler olduğunu hatırlıyor
musun? O zaman, onun yerine yenisini koyacağımıza o içeriğe ekleyemez miyiz. Bunu yapmak için
@parent helperini kullanabiliriz. Bunu kullanmak için çocuk şablonda değişiklik yapalım ve şöyle
olsun:
Blade
1
102
<!-- app/views/home.blade.php -->
2
3
@extends('layouts.base')
4
5
6
7
8
@section('head')
@parent
<link rel ="stylesheet" href ="digersitil.css" />
@stop
9
10
11
12
13
@section('body')
<h1>Yaşasın!</h1>
<p>Bir şablonumuz var!</p>
@stop
‘@parent’ helperi Blade’e parent damgasının ebeveynin aynı kesiminde bulunan varsayılan içerikle
değiştirilmesini söyler. Bu cümle biraz kafanızı karıştırmış olabilir ancak gerçekte oldukça basittir.
Netleşmesi için, isterseniz kaynağın nasıl değiştiğine bir göz atalım.
1
2
3
4
5
6
7
8
9
10
11
12
13
<!doctype html>
<html lang ="en">
<head>
<meta charset ="UTF-8">
<title></title>
<link rel ="stylesheet" href ="style.css" />
<link rel ="stylesheet" href ="digersitil.css" />
</head>
<body>
<h1>Yaşasın!</h1>
<p>Bir şablonumuz var!</p>
</body>
</html>
Gördünüz mü? Bizim ‘@parent’ damgası ebeveyndeki kesimin varsayılan içeriği ile değiştirildi.
Yeni menü girişleri veya ekstra varlık dosyaları (css, js dosyaları gibi) eklemek için bu metodu
kullanabilirsiniz.
Blade şablonları içinde istediğiniz kadar kalıtım zinciriniz olabilir, aşağıdaki örnek bunu çok iyi
göstermektedir.
Blade
1
103
<!-- app/views/birinci.blade.php -->
2
3
4
5
<p>Birinci</p>
@yield('mesaj')
@yield('son')
6
7
8
<!-- app/views/ikinci.blade.php -->
@extends('birinci')
9
10
11
12
13
@section('mesaj')
<p>İkinci</p>
@yield('mesaj')
@stop
14
15
16
<!-- app/views/ucuncu.blade.php -->
@extends('ikinci')
17
18
19
20
21
22
@section('mesaj')
@parent
<p>Üçüncü</p>
@yield('mesaj')
@stop
23
24
25
<!-- app/views/dorduncu.blade.php -->
@extends('ucuncu')
26
27
28
29
30
@section('mesaj')
@parent
<p>Dördüncü</p>
@stop
31
32
33
34
@section('son')
<p>Beşinci</p>
@stop
Woah çılgınsın sen! çıktının nasıl oluşturulduğunu görmek için kalıtım zincirini takip etmeye
çalışıyorum. Çocuk şablonlardan ebeveynlerine doğru gitmeye çalışmak en iyisi olabilir. Eğer
‘dorduncu’ görünümü göstertirsek, çıktı kaynağı şöyle olacaktır.
Blade
1
2
3
4
5
104
<p>Birinci</p>
<p>İkinci</p>
<p>Üçüncü</p>
<p>Dördüncü</p>
<p>Beşinci</p>
Basitçe söylemek gerekirse:
Birinci taban şablondur. İkinci birincinin çocuğu, Üçüncü ikincinin çocuğu, Dördüncü üçüncünün
çocuğudur.
Taban şablonun ‘son’ kesiminin dördüncü şablon dosyası tarafından sağlanan içerik olduğunu
da fark etmiş olabilirsiniz. Bunun anlamı şudur: Bir kesim için herhangi bir ‘katmandan’ içerik
sağlayabilirsiniz. Görebileceğiniz gibi, Blade çok esnektir.
Yorumlar
Belki zaten bildiğiniz gibi, HTML’nin kendi yorum metodu vardır. Şöyle bir şeydir.
1
<!-- Bu güzel bir HTML yorumudur. -->
Haklısın yorum, çok güzelsin ancak ne yazık ki, sayfa kaynağının geri kalanıyla birlikte bunu
da çıktıda gösterirsin. Geliştiricilerimiz için anlamlı bilgileri başka insanların okumasını aslında
istemeyiz.
HTML yorumlarının aksine, PHP yorumları sayfa ön-işlemi sırasında çıkarılıp atılır. Yani, sayfa
kaynağını görüntülediğinizde bunları göremezsiniz. Görünüm dosyalarımızda PHP yorumlarını şu
şekilde ekleyebiliriz:
1
<?php // Bu gizli bir PHP yorumudur. ?>
Elbette, içerik şimdi gizlendi. Bu doğru da olsa biraz çirkin değil mi? Ütopik Blade şablonlarımızda
çirkinliklere yer yok. İsterseniz bunun yerine Blade yorumu kullanalım, bunlar doğrudan PHP
yorumu olarak derlenir.
1
{{-- Bu güzel ve gizli bir Blade yorumudur. --}}
Görünümlerinize sadece geliştiricilerin göreceği notlar koymak istediğiniz zaman blade yorumlarını
kullanın.
Gelişmiş Rotalama
Ooo görüyorum, daha fazlası için geri dönmüşsünüz. Basit rotalama sizin için yeterli olmadı mı?
Biraz açgözlü müyüz ne? Laravel maceracısında korku yok, zira sizin için tatlı meyvelerim var.
Filtreler bölümünde rota tanımımıza daha fazla bilgi katılabilmesine imkan vermek için ikinci
parametre olarak bir dizi verebileceğimizi öğrenmiştik. Şunun gibi:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('/', array(
'before' => 'seksifiltre',
function() {
return View::make('hello');
}
));
Bu örnekte, rotamıza tatbik etmek istediğimiz filtrelerin neler olduğu hakkında bilgi vermek için
dizi sözdizimini kullanıyoruz. Bu kadarıyla bitmiyor, bu dizi ile daha çoğunu yapabilirsiniz. Neler
yapabileceğimize bir bakalım.
İsimli Rotalar
URI’ler güzel ve zariftir. Bunlar siteye yapısını vermek konusunda kesinlikle yardımcıdırlar ancak
daha kompleks bir siteniz olduğunda biraz uzun hale gelebilirler. Sitenizin URI’lerinin her birini tek
tek hatırlamak zorunda olmak istemezsiniz, bu çok can sıkıcı hale gelir.
Neyse ki, Laravel bu sıkıntıyı biraz hafifletecek isimli rotalama yeteneği sağlamaktadır. Gördüğünüz
gibi, rotalara bir takma isim verebiliyoruz, buna benzer bir şey oluyor:
Gelişmiş Rotalama
1
106
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('/benim/uzun/takvim/rotam', array(
'as' => 'takvim',
function() {
return View::make('takvim');
}
));
Rota dizimizin as anahtarını kullanarak bir rotaya takma bir ad atayabiliyoruz. Bunu kısa ancak
açıklayıcı tutmaya çalışın. Görüyorsunuz, Laravel uygulamanız tarafından sunulan kaynaklara
linkler üretmek için çok sayıda metoda sahiptir ve bunların birçoğu rota isimlerini destekleme
kapasitesindedir. Onların hepsini burada açıklamayacağım, önümüzdeki bir bölümde ayrıntılarına
girilecektir, yine de burada bir örnek vereyim.
1
// app/views/example.blade.php
2
3
{{ route('takvim') }}
Bu basit helper geçirilen takma adın verildiği isimli rotanın URL’sini çıktı verecektir. Verdiğimiz
örnekte http://localhost/benim/uzun/takvim/rotam döndürecektir. Küme parantezleri bir Blade
şablonu içindekini çıktılayacaktır. Blade’i hala hatırlıyorsunuz değil mi? Umarım öyledir!
Bu nasıl yararlı olacak? Peki, önce de söylediğim gibi, uzun URL’leri bir daha hatırlamak zorunda
olmayacaksınız. Gerçi, süper beyinli olabilirsiniz. URL’leri hatırlamak sizin için basit bir şey olabilir.
Tamam öyleyse, paylaşmak istediğim başka bir avantajı var.
Bir an için belirli bir rotaya linkli çok sayıda görünümünüz olduğunu düşünün. Eğer rota linkleri
elle girilmişse ve o rotanın URL’sini değiştirirseniz, o zaman URL’lerin hepsini de değiştirmeniz
gerekecek. Büyük bir uygulamada bu inanılmaz bir zaman kaybı olacaktır ve şunu kabul edelim,
artık siz bir Laravel geliştiricisisiniz. Sizin zamanınız büyük paralara bedeldir.
Eğer route() helperini kullanırsak ve sonra da URL’mizi değiştirmeye karar verirsek, o zaman
bu linklerin tümünü değiştirmemize gerek kalmaz. Bunlar takma adları sayesinde çüzümlenebileceklerdir. Ben eğer yapabilirsem her zaman için rotaları isimlendirmeye çalışırım, bu yeniden
yapılandırma gerektiğinde çok zaman kazandırır.
Redirect cevap nesnesini hatırlıyor musun? Güzel. İsimli bir rotaya redirekt yapmak için redirekt
nesnesinde route metodunu kullanabilirsiniz. Örneğin:
Gelişmiş Rotalama
1
107
<?php
2
3
return new Redirect::route('takvim');
Ayrıca, eğer güncel rotanın takma adını öğrenmek isterseniz, ‘Route’ sınıfında pratik currentRouteName()
methodunu kullanabilirsiniz. Bunun gibi:
1
<?php
2
3
// app/routes.php
4
5
$current = Route::currentRouteName();
Tüm bu gelişmiş özelliklerin Controller’lar ve rotalı Closure’lar için kullanılabilir olduğunu unutmayınız. Bir denetçiye rota yapmak için, rotalama dizisinde bir denetçi eylemiyle eşlik eden bir uses
parametresi eklemeniz yeterlidir.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/benim/uzun/takvim/rotam', array(
'as' => 'takvim',
'uses' => 'CalendarController@showCalendar'
));
Kolay, değil mi? Şimdi de rotalarımızı nasıl daha güvenli hale getirebileceğimize bir göz atalım.
Güvenli Rotalar
Rotalarınızın güvenli HTTP URL’lerine cevap vermesini, dolasıyla gizli verileri işleyebilmesini
isteyebilirsiniz. HTTPS URL’ler ihtiyaç duyduğunuzda güvenlik artışına imkan vermek için SSL
veya TLS protokolü üzerinde katmanlandırılmıştır. Rotalarınızın bu protokola uymalarını sağlamak
için şöyle yapıyoruz.
Gelişmiş Rotalama
1
108
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('gizli/icerik', array(
'https',
function () {
return 'Gizli sincap!';
}
));
Rotalarımıza HTTPS indeksi eklemek suretiyle, rotamız artık rotaya yapılan isteklere HTTPS
protokolu kullanarak cevap verecektir.
Parametre Sınırlamaları
Temel rotalama bölümünde uygulama mantığımız içinde URL yapısından parametreler kullanmayı
öğrenmiştik. Rota Closeure’ı için bu şuna benzer bir şeydir:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('kurtar/{prenses}', function($prenses)
{
return "Üzgünüm, {$prenses} başka bir kalede. :(";
});
İyi de, ben şimdiye kadar adı ‘!1337f15h’ olan bir prenses hiç duymadım. Bu benim için daha çok bir
counterstrike oyuncusu gibi geldi. Biz gerçekten de rotamızın uyduruk prenseslere cevap vermesini
istemeyiz, öyleyse neden parametremizin sadece harflerden oluşmasını temin etmeye çalışmıyor ve
onu geçerlilik denetiminden geçirmiyoruz.
Bunu çalışan bir örnekle görelim.
Gelişmiş Rotalama
1
109
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('kurtar/{prenses}', function($prenses)
{
return "Üzgünüm, {$prenses} başka bir kalede. :(";
})->where('prenses', '[A-Za-z]+');
Yukarıdaki örnekte, rota tanımımızın sonuna ilave bir where() metodu halkası ekledik. Bu ‘where’
metodu ilk paramete olarak yer tutucunun adını alır ve ikinci olarak bir düzenli ifade alır.
Burada düzenli ifadelerle ilgili ayrıntılara girmeyeceğim. O konu çok geniş, hayır gerçekten, o
inanılmaz derecede geniştir. O kendi başına tam bir kitap olur. Kısaca söylemek gerekirse, yukarıdaki
düzenli ifade prensesin adının büyük ya da küçük harflerden oluşmasını ve en azından bir harf
olmasını temin eder.
Eğer parametre, verilen düzenli ifadeyi yerine getiremezse, bu durumda rota eşleşmeyecektir.
Rotalayıcı da kolleksiyondaki diğer rotaları eşleştirmeyi denemeyi sürdürecektir.
Rotanız için istediğiniz kadar çok şart tutturabilirsiniz. Örnek olarak şuna bakalım:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('kurtar/{prenses}/{tekboynuzluat}', function($prenses, $tekboynuzluat)
{
return "{$prenses} {$tekboynuzluat}'ı sever";
})->where('prenses', '[A-Za-z]+')
->where('tekboynuzluat', '[0-9]+');
Buradaki tekboynuzluat parametresi bir veya daha çok rakamdan oluşma kuralına göre geçerlilik
kontrolünden geçirilir, çünkü ben tekboynuzluatların her zaman sayısal isimleri olduğunu biliyorum. Tıpkı iyi arkadaşım 3240012 gibi.
Rota Grupları
Filters bölümünde rotalarımıza nasıl koşul verebildiğimizi hatırlayın? Gerçekten kolaylıktı değil mi?
Birçok rota tanımına aynı filtreyi takmak zorunda olmak utanç verici olurdu.
Rotalarımızı enkapsüle edebilsek ve bir filtreyi bu konteynere uygulasak harika olmaz mıydı? Peki,
zaten tahmin etmiş olmalısın, ama tam olarak nasıl olduğunu gösteren bir örnek verelim.
Gelişmiş Rotalama
1
110
<?php
2
3
// app/routes.php
4
5
6
Route::group(array('before' => 'sadecedostlar'), function()
{
7
// Birinci Rota
Route::get('/birinci', function() {
return 'Dostum!';
});
8
9
10
11
12
// İkinci Rota
Route::get('/ikinci', function() {
return 'Dostuuuuum!';
});
13
14
15
16
17
// Üçüncü Rota
Route::get('/ucuncu', function() {
return 'Bana gel dostum.';
});
18
19
20
21
22
23
});
Yukarıdaki örnekte ‘Route’ nesnemizde group() metodunu kullanıyoruz. İlk parametre bir dizidir.
Bu da tıpkı rotalama metodlarımız içinde kullandıklarımız gibi çalışır. Filtreleri, güvenli indeksleri
ve diğer birçok rota filtreleri kabul edebilir. İkinci parametre bir Closure olacaktır.
Bu Closure içinde ek rotalar tanımladığınız zaman, bu rotalar grup özelliklerini miras alırlar.
Yukarıdaki grup içindeki üç rotanın tümünde ‘sadecedostlar’ before filtresi olacaktır.
Artık bölümün başında öğrendiğimiz rota dizi filtrelerini gruplarda kullanabiliyoruz, fakat biz aynı
zamanda rota gruplarına özgü bazı yeni özellikler de kullanabiliriz. Hadi bu yeni özelliklere bir göz
atalım.
Rotalara Ön Ek Koyma
Eğer birçok rotamız ortak bir URL yapısını paylaşıyorsa, az miktardaki tekrarları önlemek için bir
rota ön eki kullanabiliriz.
Şu örneğe bir göz atalım.
Gelişmiş Rotalama
1
111
<?php
2
3
// app/routes.php
4
5
6
Route::group(array('prefix' => 'kitaplar'), function()
{
7
// Birinci Rota
Route::get('/birinci', function() {
return 'Büyünün Rengi';
});
8
9
10
11
12
// İkinci Rota
Route::get('/ikinci', function() {
return 'Kazanan Adam';
});
13
14
15
16
17
// Üçüncü Rota
Route::get('/ucuncu', function() {
return 'Beyler ve Bayanlar';
});
18
19
20
21
22
23
});
Rota grubunun prefix dizi seçeneğini kullanarak, grup içinde tanımlanan URI’lerin tümü için bir ön
ek belirleyebiliyoruz. Örneğin, yukarıdaki üç rota şimdi aşağıdaki URL’lerde erişilebilirdir.
1
/kitaplar/birinci
2
3
/kitaplar/ikinci
4
5
/kitaplar/ucuncu
Rotalarınız içinde tekrarları önlemek ve bunları organizasyonel ve yapısal değerlerine göre gruplamak için rota ön ekleri kullanın.
Domain Rotalama
URI’ler bir rotayı ayırt etmenin tek yolu değildir. Host da değişebilir. Örneğin, aşağıdaki URL’ler
farklı kaynaklara başvurabilir.
Gelişmiş Rotalama
1
112
http://myapp.dev/my/route
2
3
http://another.myapp.dev/my/route
4
5
http://third.myapp.dev/my/route
Yukarıdaki örnekte alt domainlerin farklı olduğunu görebiliyorsunuz. Şimdi farklı domainlerden
farklı içerik sunmak için domain rotalama kullanımını keşfedelim.
İşte domain tabanlı bir rotalama örneği:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::group(array('domain' => 'myapp.dev'), function()
{
Route::get('my/route', function() {
return 'myapp.dev\'den selamlar!';
});
});
11
12
13
14
15
16
17
Route::group(array('domain' => 'another.myapp.dev'), function()
{
Route::get('my/route', function() {
return 'another.myapp.dev\'den selamlar!';
});
});
18
19
20
21
22
23
24
Route::group(array('domain' => 'third.myapp.dev'), function()
{
Route::get('my/route', function() {
return 'third.myapp.dev\'den selamlar!';
});
});
Rota gruplama dizisine ‘domain’ indeksini tutturmak suretiyle, bir host adı verebiliyoruz. Bu ad
rotalardan birinin içinde çalıştırılacağı güncel hostadına uymasını *zorunlu” hale getiriyor.
Host adı ya bir alt domain olabilir veya tamamen farklı bir alt domain olabilir. Web sunucunuz her
bir hosttan Laravel’e istekler sunacak şekilde yapılandırılmak şartıyla onları karşılaştırabilecektir.
Etki alanı tabanlı rotalama bu kadar değil. Tıpkı URI tabanlı rotalamada yaptığımız gibi, burada da
parametre olarak kullanmak için host adı kısımlarını alabiliyoruz. Bunu gösteren bir örnek şudur.
Gelişmiş Rotalama
1
113
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::group(array('domain' => '{kullanici}.myapp.dev'), function()
{
Route::get('profil/{sayfa}', function($kullanici, $sayfa) {
// ...
});
});
Tıpkı bizim URI paramatrelerine benzer şekilde, bir domain parametresi için { küme parantezi }
içinde bir yer tutucu verebiliyoruz. Bu parametrenin değeri, grup içinde tutulan rotaların parametrelerinden önce geçirilecektir.
Örneğin, eğer biz şu URL’yi ziyaret etmiş isek:
1
http://taylor.myapp.dev/profil/avatar
Bu durumda, iç Closure’a geçirilen ilk değer $kullanici ‘Taylor’ olacak ve ikinci değer $sayfa ise
‘avatar’ olacaktır.
Joker alt domain ve rotalama parametreleri kombinasyonu kullanmak suretiyle, domain’in başına
uygulamanız kullanıcılarının kullanıcı adını ekleyebiliyorsunuz.
URL Üretimi
Web uygulamanız rotalar ve URL’ler etrafında dönüp durur. Sonuçta, bunlar kullanıcılarınızı
sayfalarınıza yönlendiren şeylerdir. Günün sonunda, her web uygulamasının yapması gereken şey
sayfalar sunmaktır.
Sadece tek sayfa sunuyorsanız kullanıcılarınız uzun süreyle ilgilenmeyebilirler ve eğer onları
websiteniz veya web uygulamanız içinde gezdirmek istiyorsanız, o zaman webin kritik bir özelliğini
kullanmanız gerekecektir. Hangi özellik olduğunu mu soruyorsun? Hiper-linkler!
Hiper linkleri oluşturabilmemiz için uygulamamıza URL’ler inşa etmemiz gerekiyor. Bunları elle
yapabilirdik ancak Laravel URL oluşturulmasına yardım eden çok sayıda helper sağlamak suretiyle
bizi bu tür çabalardan kurtarıyor. Bu özelliklere bir bakalım.
Şimdiki URL
Laravel’de şimdiki URL’yi elde etmek kolaydır. Sadece URL::current() helperini kullanın. Bunu
test etmek için basit bir rota oluşturarak başlayalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/simdiki/url', function()
{
return URL::current();
});
Şimdi eğer /simdiki/url url’sini ziyaret edersek, aşağıdaki cevabı alırız.
1
http://myapp.dev/simdiki/url
Çok basitmiş değil mi? Şimdi de URL::full()‘a bakalım, gördüğünüz gibi bu da şimdiki URL’yi
döndürüyor.
Erm. Demin de bunu yapmamış mıydık?
Peki, küçük bir fark var. Hadi son rotayı bir daha deneyelim, ama bu sefer GET parametresi olarak
bazı ek bilgiler koyacağız.
URL Üretimi
1
115
http://myapp.dev/simdiki/url?falan =filan
URL::current() sonucunun ekstra istek verisini çıkardığını göreceksin, yani böyle:
1
http://myapp.dev/simdiki/url
Ama URL::full() metodu biraz farklı olacak. Bunu kullanacak mevcut rotamızı şöyle değiştirelim:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/simdiki/url', function()
{
return URL::full();
});
Sonra da tekrar /simdiki/url?falan =filan URL’sine gitmeyi deneyelim. Bu sefer aşağıdaki
sonucu alacağız:
1
http://myapp.dev/simdiki/url?falan =filan
Gördüğünüz gibi, URL::full() metodu ilave istek verisini de dahil etmektedir.
Bundan sonraki metod aslında şimdiki URL’yi elde etme yolu değil ancak ben bu alt başlık altında
olmasını uygun gördüm. Gördüğünüz gibi bu, ‘referer’ istek başlığı ile gösterilen gibi, önceki URL’yi
elde etmenin bir yöntemidir.
Çıktıyı göstermek için bir Redirekt cevap tipi kullanarak kurnazca bir tuzak kurdum. Şu örneğe bir
göz atalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('birinci', function()
{
// İkinci rotaya Redirect yapıyoruz.
return Redirect::to('ikinci');
});
10
11
12
13
14
Route::get('ikinci', function()
{
return URL::previous();
});
URL Üretimi
116
Yani bizim birinci rotamız ikinci rotaya redirekt yapıyor. İkinci rota da URL::previous() metodunu
kullanarak önceki isteğin URL’sini çıktı olarak verecektir.
Ne olacağını görmek için /birinci URI’sini ziyaret edelim.
Bir anlık gösterilen redirekt bilgisini görebilirsiniz ancak umuyorum ki aşağıdaki cevabı da alacaksınız:
1
http://demo.dev/birinci
Görüldüğü gibi, redirektten sonra, URL::previous metodu önceki isteğin URL’sini, bu örneğimiz
için birinci rotamızın URL’sini vermektedir. Bu kadar basit!
Framework URL’leri Üretimi
Bu kesim, sitemiz veya uygulamamızın farklı rotaları veya sayfalarını gezinmemize yardım edecek
URL’lerin üretilmesiyle ilgilidir.
URI’lere özgü URL üretimi ile başlayalım. Bunu URL::to() metodunu kullanarak yapabiliyoruz.
Bunun gibi:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('ornek', function()
{
return URL::to('birdiger/rota');
});
/ornek‘i ziyaret ettiğimizde alacağımız cevap şöyle bir şeydir.
1
http://demo.dev/birdiger/rota
Görüleceği gibi, Laravel istek yaptığımız rota için bir URL inşa etmiştir. birdiger/rota‘nın mevcut
olmadığını ama ona bir link yapabildiğimize dikkat ediniz. URI’ler için link üretirken bu hususu
unutmayın.
URL::to() metoduna bir dizi şeklinde ek parametreler belirtebilirsiniz. Bu parametreler rotanın
sonuna eklenecektir. İşte bir örnek:
URL Üretimi
1
117
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('ornek', function()
{
return URL::to('birdiger/rota', array('falan', 'filan'));
});
Oluşan string aşağıdaki şekli alacaktır.
1
http://myapp.dev/birdiger/rota/falan/filan
Eğer üretilen URL’lerin HTTPS protokolu kullanmasını isterseniz, bu durumda iki seçeneğiniz var.
Birinci seçenek URL::to() metoduna üçüncü bir parametre olarak true geçmektir, şunun gibi:
1
URL::to('birdiger/rota', array('falan', 'filan'), true);
Buna karşın, URL’niz için parametre vermek istemezseniz, ikinci parametre olarak boş bir dizi veya
null geçmek zorundasınız. Bunun yerine daha tanımlayıcı URL::secure() metodunu kullanmak
daha etkilidir, şöyle:
1
URL::secure('birdiger/rota');
Burada da URL::secure() metoduna ikinci parametre olarak dizi şeklinde rota parametresi geçebilirsiniz, bunun gibi:
1
URL::secure('birdiger/rota', array('falan', 'filan'));
Sonraki üretme metodumuza geçelim. Gelişmiş rotalama bölümünde rotalarımıza takma ad vermeyi
öğrendiğimizi hatırlıyor musunuz? İsimli rotalar şuna benzer:
URL Üretimi
1
118
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('en/iyi/intikamci', array('as' => 'ironman', function()
{
return 'Tony Stark';
}));
9
10
11
12
13
Route::get('ornek', function()
{
return URL::route('ironman');
});
Eğer /ornek rotasını ziyaret edersek aşağıdaki cevabı alırız.
1
http://myapp.dev/en/iyi/intikamci
Laravel rotamızın takma adını almıştır ve eşlik eden URI’yi bulmuştur. Eğer URI’yi değiştirirsek,
çıktı da değişecektir. Bu birçok görünüm için tek bir URI değiştirmekte çok yararlıdır.
Tıpkı URL::to() metodu gibi, URL::route() metodu da ikinci metod parametresi olarak bir
parametreler dizisi alabilir. Yalnız, bu o parametreleri URI içinde doğru sırada ekleyecektir. Nasıl
olduğunu göstereyim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('{birinci}/en/{ikinci}/intikamci', array(
'as' => 'ironman',
function($birinci, $ikinci) {
return "Tony Stark, {$birinci} en {$ikinci} intikamci.";
}
));
11
12
13
14
15
Route::get('ornek', function()
{
return URL::route('ironman', array('bilinen', 'iyi'));
});
Eğer aşağıdaki URL’yi ziyaret edersek…
URL Üretimi
1
119
http://myapp.dev/ornek
Laravel boşlukları, vermiş olduğumuz parametrelerle doğru sırada dolduracaktır. Cevap olarak
aşağıdaki URL gösterilecektir.
1
http://myapp.dev/bilinen/en/iyi/intikamci
Bu tipte son bir rota metodunu daha bilmemiz gerekiyor, yani denetçi eylemlerine nasıl rota
yapacağımızı. Aslında bu oldukça basit olanlardan biridir, çünkü URL::route() metodu ile aynı
deseni izler. Bir örnek üzerinden görelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
12
// Denetçimiz.
class Stark extends BaseController
{
public function tony()
{
return 'Beni memnun etmek için bana güvenmelisin.';
}
}
13
14
15
// Stark denetçiye rota.
Route::get('evet/ben/iron/man', 'Stark@tony');
16
17
18
19
20
Route::get('ornek', function()
{
return URL::action('Stark@tony');
});
Bu örnekte, ‘tony()’ adlı bir eylemi olan ‘Stark’ adında yeni bir denetçi oluşturuyoruz. Denetçi
eylemi için bir rota oluşturuyoruz. Sonra da URL::action() metodunun değerini döndüren bir ornek
rotasını oluşturuyoruz. Bu metodun birinci parametresi URL’sini elde etmek istediğimiz sınıf ve
eylemin kombinasyonudur. Bu parametrenin formatı denetçilere rotalama için kullandığımızla aynı
şekildedir.
Eğer /ornek URL’yi ziyaret edersek, aşağıdaki cevabı alırız.
URL Üretimi
1
120
http://myapp.dev/evet/ben/iron/man
Laravel istek yaptığımız denetçi-eylem çifti için URL’yi tanımladı ve onu bir cevap olarak sundu.
Tıpkı diğer metodlarda olduğu gibi, URL::action() metoduna da ikinci parametre olarak bir
parametre dizisi geçebiliyoruz. Nasıl olduğunu görelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
12
// Denetçimiz.
class Stark extends BaseController
{
public function tony($tonyNedir)
{
// ...
}
}
13
14
15
// Stark denetçisi için rota.
Route::get('tony/en/{birinci}/deha', 'Stark@tony');
16
17
18
19
20
Route::get('ornek', function()
{
return URL::action('Stark@tony', array('narsist'));
});
Son örneğimizde olduğu gibi, URL::action() metoduna bir parametre olarak tek parametreli bir
dizi veriyoruz ve Laravel parametre doğru konumda olacak şekilde denetçimize URL oluşturur.
Alacağımız URL bunun gibidir.
1
http://myapp.dev/tony/en/narsist/deha
Evet, rota URL üretilmesi bu kadar. Bir parça tekrarlar olduysa özür dilerim ancak umuyorum ki iyi
bir başvuru bölümü oldu.
Varlık URL’leri
Resimler, CSS dosyaları ve JavaScript gibi varlıkların URL’lerinin biraz farklı halledilmesi gereklidir.
Çoğunuz Laravel’de zarif URL’ler kullanıyor olacaksınız. Bu, index.php ön denetçiyi çıkarmak için
URL’yi yeniden yazma ve URL’lerimizi daha SEO dostu yapma işidir.
URL Üretimi
121
Bununla birlikte bazı durumlarda zarif URL’ler kullanmak istemeyebilirsiniz. Ancak eğer önceki
alt bölümlerde söylenen helperleri kullanarak bir varlık için link yapmaya çalışırsanız, o zaman
URL’nin index.php kısmı kalacak ve varlık linkleri kırık olacaktır.
Zarif URL’lerde bile, varlıklarımız için göreli URL’ler kullanarak link vermek istemeyiz, çünkü bizim
rota segmentlerimiz bir klasör yapısını karıştıracaktır.
Her zamanki gibi, Laravel ve Taylor bizden bir adım önde. Varlıklarımız için mutlak URL’ler üretecek
helperlar sağlanmıştır. Bu helperlardan bir kısmını görelim.
Birincisi bir URL::asset() metodumuz var, hemen örneğe geçelim. Metodun birinci parametresi
varlık için göreli dosya yoludur.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('ornek', function()
{
return URL::asset('img/logo.png');
});
Şimdi eğer /ornek ziyaret edilirse, aşağıdaki cevapla karşılaşılacaktır.
1
http://myapp.dev/img/logo.png
Laravel bizim için mutlak varlık dosya yolunu oluşturmuştur. Varlıklarımızı refere etmek için
güvenli bir HTTPS protokolu kullanmak istersek, URL::asset() metoduna ikinci bir parametre
olarak true geçebiliriz, bunun gibi:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('ornek', function()
{
return URL::asset('img/logo.png', true);
});
Şimdi /ornek URL’den aşağıdaki cevabı alırız.
URL Üretimi
1
122
https://demo.dev/img/logo.png
Mükemmel! Laravel güvenli varlık URL’leri üretmek için ayrıca çok daha açıklayıcı bir metod da
sağlamaktadır. Basitçe URL::secureAsset() metodunu kullanın ve varlığınızın göreli dosya yolunu
parametre olarak geçin.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('ornek', function()
{
return URL::secureAsset('img/logo.png');
});
Bu rotadan gelen cevap önceki metodla aynı olacaktır.
1
https://demo.dev/img/logo.png
Üretim Kısayolları
Önceki alt başlıklarda bahsi geçen metodlar, görünümlerinizde kullanılmak için elinizin altındadır.
İlerleyelim ve bu özelliklerin sağladıkları avantajların tümünü almaya başlayalım.
Bununla birlikte, görünümlerinizdeki mantıkların kısa ve temiz olması iyi olacaktır. Ayrıca, parmaklarınızdan stresi de alır. Bu yüzden Laravel URL sınıfında bulunan metodlardan bir kısmı için
bazı kısayollar sağlamıştır. Neler var biraz yakından bakalım.
Birincisi url() fonksiyonudur. URL::to() metodu ile aynı parametreleri alır, o nedenle tekrar
anlatmayacağım. İşte nasıl çalıştığını görün.
1
<!-- app/views/ornek.blade.php -->
2
3
<a href ="{{ url('benim/rotam', array('falan', 'filan'), true) }}">Rotam</a>
Tarayıcıda gösterilen görünüm sayfa kaynağındaki linke bakacak olursak, şunu görürüz.
1
<a href ="https://demo.dev/benim/rotam/falan/filan">Rotam</a>
Bu URL URL::to() metodu ile olanla aynı tarzda oluşturulmuştur. Önceki gibi, güvenli bir URL
üretmek için de bir kısayol metodu vardır. Şöyle görünür:
URL Üretimi
1
123
<!-- app/views/ornek.blade.php -->
2
3
<a href ="{{ secure_url('benim/rotam', array('falan', 'filan')) }}">Rotam</a>
secure_url() fonksiyonu URL::secure() metodu ile aynı şekilde yazılır, birinci parametre rotadır
ve ikinci parametre ise rota parametrelerine eklenecek bir dizidir.
route() fonksiyonu da URL::route() metodunun kısayoludur ve isimli rotalar için URL üretmekte
kullanılır. Şuna benzer:
1
<!-- app/views/ornek.blade.php -->
2
3
<a href ="{{ route('benimrotam') }}">Rotam</a>
Artık tahmin ediyorsunuzdur, rota URL’si oluşturmanın üçüncü metodu için de bir kısayol vardır.
URL::action() metodunun kısayolu olarak action() fonksiyonu kullanılabilir ve denetçi eylemlerine linkler üretmek için kullanılabilmektedir.
1
<!-- app/views/ornek.blade.php -->
2
3
<a href ="{{ action('BenimController@eylemim') }}">Linkim</a>
URL::action() metodunda olduğu gibi bunlar da ikinci parametre olarak rota parametreleri alabilir
ve güvenli URL üretmek için üçüncü parametre alabilir.
1
<!-- app/views/ornek.blade.php -->
2
3
<a href ="{{ action('BenimController@eylemim', array('falan'), true) }}">Linkim</a>
URL::asset() metodunun kısayolu asset() fonksiyonudur ve diğer tüm kısayollarda olduğu gibi
aynı fonksiyon parametrelerini alır. İşte bir örnek:
1
<!-- app/views/ornek.blade.php -->
2
3
<img src="{{ asset('img/logo.png') }}" />
Benzer şekilde, URL::secureAsset() metodunun kısayolu secure_asset() fonksiyonudur. Buna
benzer:
URL Üretimi
1
124
<!-- app/views/ornek.blade.php -->
2
3
<img src="{{ secure_asset('img/logo.png') }}" />
Görünümlerinizin içeriğini basitleştirmek ve tekrarlı gerilme yaralanmalarını önlemek için bu kısa
yolları kullanmaktan çekinmeyin.
İstek Verisi
Veri ve verinin düzenlenmesi her web uygulaması için önemlidir. Bunların çoğu verinin elde
edilmesi, değiştirilmesi, oluşturulması ve saklanmasına dayanır.
Veriler her zaman uzun süreli şeyler olmak zorunda değildir. Bir HTML formunda sağlanan veya
bir isteğe tutturulan veriler ön tanımlı olarak sadece istek süresince kullanılabilirler.
İstekten gelen veriyi değiştirmeden veya saklamadan önce onu elde etmemiz gerekecektir. Neyse ki,
Laravel istek verisine erişme için uzun soluklu, karmaşık bir yönteme sahiptir. Hemen bir örneğe
bakalım.
1
<?php
2
3
// app/providers/input/data/request.php
4
5
namespace Laravel\Input\Request\Access;
6
7
8
use Laravel\Input\Request\Access\DataProvider;
use Laravel\Input\Request\Access\DataProvider\DogBreed;
9
10
11
12
13
14
15
16
17
18
19
20
class Data extends DataProvider
{
public function getDataFromRequest($requestDataIndicator)
{
$secureRequestDataToken = sin(2754) - cos(23 + 52 - pi() / 2);
$retriever = $this->getContainer()->get('retriever');
$goldenRetriever = $retriever->decorate(DogBreed::GOLDEN);
$request = $goldenRetriever->retrieveCurrentRequestByImaginaryFigure();
return $request->data->input->getDataByKey($requestDataIndicator);
}
}
21
22
// app/routes.php
23
24
25
$myDataProvider = new Laravel\Input\Request\Access\Data;
$data = $myDataProvider->getDataFromRequest('example');
Eveet, önce bir DataProvider oluşturuyoruz Klaaaaahahahahhaa! Git yaa! Sadece ortalığı karıştırıyorum. Laravel asla çirkin ve karmaşık bir şey sağlamaz, şimdiye kadar bunu biliyor olmalısınız!
İstek Verisi
126
Hmm, sadece kaç kişinin kitabı kapatacağını ve Laravel’e bir daha dönmeyeceğini merak etmiştim.
Sanırım tamam!
Artık istek verilerine erişimin gerçek yöntemlerini görebiliriz.
Verileri Alma
Verilerin elde edilmesi kolaydır. URL’mizden bir GET verisini nasıl elde ettiğimizi anlatan örneğimize
atlayabiliriz. Bu tip veriler bir anahtar/değer çiftleri şeklinde ilgili URL’ye eklenir. Bunlar PHP $_GET
dizisinde saklanmasını beklediğimiz şeylerdir.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$veri = Input::all();
var_dump($veri);
});
Input::all() metodu güncel istek içinde taşınan hem $_POST hem de $_GET verisinin ilişkisel
dizisini döndürmek için kullanılır. URL’ye katılan bazı ‘GET’ tipi verileri olan bir URL vererek bunu
test edelim.
1
http://myapp.dev/?falan =bu&filan =o
Aşağıdaki cevabı alırız. Bu, URL’ye sağladığımız verinin ilişkisel bir dizisidir.
1
array(2) { ["falan"]=> string(2) "bu" ["filan"]=> string(1) "o" }
İstek verisi başka bir kaynaktan gelen bir $_POST verisi olabilir. Bunu göstermek için basit bir form
için başka bir rota oluşturacağız. Form ile başlayalım.
İstek Verisi
1
127
<!-- app/views/form.blade.php -->
2
3
<form action ="{{ url('/') }}" method ="POST">
4
5
6
<input type ="hidden" name ="falan" value ="bu" />
<input type ="hidden" name ="filan" value ="o" />
7
8
<input type ="submit" value ="Gönder" />
9
10
</form>
/ URL’sine post edilecek bazı gizli verileri olan bir form oluşturduk. Ancak, bunu test etmek için
rota üzerinde çalışmalıyız. O zaman rota dosyamıza bakalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::post('/', function()
{
$veri = Input::all();
var_dump($veri);
});
10
11
12
13
14
Route::get('post-form', function()
{
return View::make('form');
});
Burada formumuzu göstermek için ilave bir rota ekledik. Ancak, fark etmemiş olabileceğiniz başka
bir küçük değişiklik de var. Görebiliyor musunuz?
Eğlenceli değil mi? ‘Waldo nerede’ oyununa benzedi bu. Peki, eğer fark etmemişseniz işte şu. Biz
orijinal rotamızı sadece POST metodu isteklerine cevap verecek şekilde değiştirdik. Birinci rotamız
şimdi Route::post() metodunu kullanıyor.
Bunun sebebi formun metodunu POST olarak ayarlamamızdır. Bizim hedef rotamız rota oluşturmakta kullandığımız HTTP fiili ile uyuşmadığı sürece eşleşemeyecektir.
Şimdi /post-form rotasını ziyaret edelim ve alacağımız cevap çeşitinin ne olacağını görmek için
‘Gönder’ düğmesine basalım.
İstek Verisi
1
128
array(2) { ["falan"]=> string(2) "bu" ["filan"]=> string(1) "o" }
Mükemmel, post verimiz doğru olarak elde edildi. Ancak bu ilginç bir soru doğurdu. Bir POST
rotasında bile, hala URL’ye veri ekleyebiliyoruz. Şimdi şunu merak ediyorum, eğer ikisinde de aynı
anahtarlar olursa hangisi öncelik alacak?
Bunu anlamanın tek bir yolu var. Bu teoriyi test etmek için formumuzu değiştireceğiz.
1
<!-- app/views/form.blade.php -->
2
3
<form action ="{{ url('/') }}?falan =get&filan =get" method ="POST">
4
5
6
<input type ="hidden" name ="falan" value ="bu" />
<input type ="hidden" name ="filan" value ="o" />
7
8
<input type ="submit" value ="Gönder" />
9
10
</form>
Hadi gidiyoruz. Hedeflediğimiz formumuzun URL’sine ekstra veri tutturduk. Gönder düğmesine
tekrar basalım ve ne olacağını görelim.
1
array(2) { ["falan"]=> string(3) "get" ["filan"]=> string(3) "get" }
Öyle görünüyor ki, GET verisi en son işleniyor ve değerler değiştiriliyor. Artık biliyoruz ki, istek
veri dizisinde GET verisi POST verisinden öncelik alıyor. İkisini de kullanırsanız bunun olacağını
unutmayınız.
Bazı durumlarda tüm istek veri dizisini elde etmek yararlı olacaktır, yine de anahtara göre tek bir
bilgi parçasını almak da isteyebiliriz. Input::get() metodu bu işe yarar. Hadi görelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$veri = Input::get('falan');
var_dump($veri);
});
Rotamızı get() metodunu kullanması için bir kez daha değiştirdik, fakat bu sefer tek bir veri parçasını elde etmek için anahtarın adını bir string olarak vererek Input::get() metodunu kullanıyoruz.
Şimdi veri parçasının başarıyla elde edildiğini görmek için /?falan =bu URL’sini ziyaret edelim.
İstek Verisi
1
129
string(2) "bu"
Harika! Peki, eğer bu veri mevcut olmasaydı ne olacaktı? Onu da /‘yi ziyaret ederek görebiliriz.
1
NULL
Peki neden null bir değerimiz oldu? Şey!, eğer bir şey Laravel’de bulunamazsa, bir istisna atmak
veya uygulamamızın çalışmasını engellemek yerine null sunmayı sever. Bunun yerine, Laravel çok
daha yararlı şeyler yapar. Son bir çare olarak ön tanımlı bir değer sağlamamıza imkan verir.
Rotamızda, Input::get() metoduna bir son çare sağlayacak şekilde değişiklik yapalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$veri = Input::get('falan', 'bu');
var_dump($veri);
});
Şimdi / URI’sinden gelen sonuca göz atabiliriz.
1
string(2) "bu"
Mükemmel, işe yaradı! Ön tanımlı değer vermek suretiyle, metodun sonucunun her zaman bir string
olmasını temin etmiş olduk.
Yine de, eğer hala istek veri parçasının mevcut olup olmadığını bilmek istiyorsak, Input::has()
seçeneğini kullanabiliriz. Onu iş yaparken görelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$sonuc = Input::has('falan');
var_dump($sonuc);
});
İstek Verisi
130
Eğer / ziyaret edilirse, bool(false) sonucu alınır ve /?falan =bu ziyaret edilirse bool(true)
alınır. Biz bir boolean değer çağırdık, o ya true ola… sadece şaka yapıyorum! Boolean değerin ne
olduğunu bildiğinizi tabii ki biliyorum. Kafanızi rahatlatmak istediğinizde Input::get() metodunu
kullanmaktan çekinmeyin.
Pufff…
Hala mutsuz musunuz? Öyleyse mızmızın birisiniz! Ne yani, tek bir değer istemiyorsunuz, tüm
değerlerden oluşan bir dizi istemiyorsunuz? Ah anladım, siz verilerin bir alt kümesini istiyor
olmalısınız. Merak etmeyin dostum. Laravel bunu da yapar.
İlk olarak Input::only() metodunu inceleyelim. Bu ne söylüyorsa onu yapar?
İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$sonuc = Input::only(array('falan', 'filan'));
var_dump($sonuc);
});
Yukarıdaki örnekte biz istek veri değerlerinden ilişkisel bir dizi olarak döndürmek istediklerimizin
anahtarlarını içeren bir diziyi Input::only() metoduna geçiriyoruz. Aslında, bu dizi opsiyoneldir.
Her anahtarı ek parametreler olarak da metoda geçebilirdik, şunun gibi:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$sonuc = Input::only('falan', 'filan');
var_dump($sonuc);
});
Şimdi aşağıdaki URL’yi ziyaret ederek cevabı test edelim.
İstek Verisi
1
131
http://myapp.dev/?falan =bir&filan =iki&fesmekan =uc
Uyguladığımız metod önemli değil. Sonuç aynı olacaktır.
1
array(2) { ["falan"]=> string(3) "bir" ["filan"]=> string(3) "iki" }
Laravel istek verilerinden istek yaptığımız anahtarlara uyan alt kümeyi döndürmüştür. Bu veri
ilişkisel bir dizi şeklinde dönmüştür. Pek tabii, only() metodunun bir de karşıtı vardır. except()
metodu.
except() metodu verilerden verdiğimiz anahtarları dışarıda tutan bir ilişkisel dizi döndürecektir.
Burada da anahtarları isterseniz bir dizi olarak geçebilirsiniz, mesela:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$sonuc = Input::except(array('falan', 'fesmekan'));
var_dump($sonuc);
});
Veya, anahtarları ek parametreler listesi olarak verebilirsiniz, bunun gibi:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$sonuc = Input::except('falan', 'fesmekan');
var_dump($sonuc);
});
Şimdi aşağıdaki URL’yi ziyaret edelim.
1
http://demo.dev/?falan =bir&filan =iki&fesmekan =uc
Verdiğimiz anahtarlara uymayan anahtar ve değerlerden oluşan aşağıdaki ilişkili diziyi alırız.
Input::only() metodunun tam karşıtı.
İstek Verisi
1
132
array(1) { ["filan"]=> string(3) "iki" }
Önceki Input
Bizim POST ve GET istek verileri sadece tek bir istek için kullanılabilir. Bunlar kısa ömürlü değerlerdir.
Bilgisayarınızda RAM’de saklanan bilgiler gibidir.
İstek verilerimizi bir veri deposuna taşımadığımız sürece, onu çok uzun süreyle tutamayız. Buna
karşın, Laravel’e bir başka istek döngüsü süresince tutmasını söyleyebiliriz.
Bunu gösterebilmek için, Laravel rotalama motoruna başka bir zeki tuzak kurabiliriz. Bir Redirect
cevabı döndürmenin, tıpkı browser yenilenmesi gibi yeni bir istek döngüsü oluşturduğunu zaten
görmüştük. Sonraki metodumuzu test etmek için bunu kullanabiliriz.
Şimdi iki tane rota oluşturalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Redirect::to('yeni/istek');
});
9
10
11
12
13
Route::get('yeni/istek', function()
{
var_dump(Input::all());
});
Burada yeni/istek rotasına redirekt yapan bir rotamız var. Bu ilk rotaya bazı GET verisi verelim ve
ne olduğunu görelim. Deneyeceğimiz URL şudur.
1
http://myapp.dev/?falan =bir&filan =iki
Redirekt sonrası alacağımız cevap şöyledir.
1
array(0) { }
Gördünüz mü? Bu defa size yalan söylemedim. Redirekt sonrasında, cevap verisi boş bir diziye
ayarlandı. Hiçbir cevap verisi yok. Input::flash() metodunu kullanarak, Laravel’e istek verisini
ek bir istek süresince tutmasını söyleyebiliriz.
İlk rotamızı değiştirelim. Rotanın tamamını tekrar veriyoruz ancak bu sefer Input::flash()
metodunu kullanıyoruz.
İstek Verisi
1
133
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
Input::flash();
return Redirect::to('yeni/istek');
});
10
11
12
13
14
Route::get('yeni/istek', function()
{
var_dump(Input::all());
});
Şimdi aynı URL’yi tekrar ziyaret edersek, alacağımız cevap… ah bekle.
1
array(0) { }
Ah haklısınız! Biz istek verisini şimdiki ve önceki istekleri birlikte karıştırmak istememiştik. Bu onları
karıştırır ve karmaşıklaştırır. Laravel ve Taylor akıllıdır. Bunlar istek verisini başka bir koleksiyonda
saklarlar.
Rotamızı tekrar değiştirelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
Input::flash();
return Redirect::to('yeni/istek');
});
10
11
12
13
14
Route::get('yeni/istek', function()
{
var_dump(Input::old());
});
Input::old() metodu Laravel’in bizim önceki istekten flash ettiğimiz bütün veri dizisini istediğimizi
bilmesini sağlar. Hepsi bu kadar ve siz de hızlı olun!
Bizim eski verinin nasıl gözüktüğüne bakalım, /?falan =bir&filan =iki URL’sini yeniden ziyaret
edeceğiz.
İstek Verisi
1
134
array(2) { ["falan"]=> string(3) "bir" ["filan"]=> string(3) "iki" }
Mükemmel! İşte tam aradığımız şey. Şimdi önceki istekten flash()ladığımız veriyi tutabiliyoruz.
Input::get()de olduğu gibi, önceki veri dizisinden tek bir değer de alabiliriz. Kodu görelim!
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
Input::flash();
return Redirect::to('yeni/istek');
});
10
11
12
13
14
Route::get('yeni/istek', function()
{
var_dump(Input::old('falan'));
});
Input::old() metoduna bir string geçmek suretiyle, tek bir değer döndürecek bir anahtar belirtebiliyoruz. Önceki veri dizisinde etki göstermek dışında tam olarak Input::get() metodu gibi çalışır.
Verinin tamamını flashlamamız gerekmez, biliyorsunuz değil mi? Laravel bizi hiçbir şeye zorlamaz.
Bu sebeple, verilerin sadece bir alt kümesini flashlayabiliriz. Bir nevi daha önce kullandığımız only()
ve except() metodları gibi iş görür. Ancak bu sefer elde edilen veriye değil, flashlanan veriye
başvururuz.
Vay be, kelimelerle ifade etmek her zaman daha zor oluyor. Laravel’in temiz sözdizimini kullanan,
güzel, açıklayıcı bir örnek görmek harika olmaz mı? Tamamen katılıyorum!
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
Input::flashOnly('falan');
return Redirect::to('yeni/istek');
});
10
11
12
Route::get('yeni/istek', function()
{
İstek Verisi
var_dump(Input::old());
13
14
135
});
Bu defa, Laravel’e önceki veri koleksiyonumuzdan sadece ‘falan’ anahtarını flashlamasını söyledik.
Redirektten sonra, dönen sonucu görmek için önceki veri koleksiyonunun tamamını dump ediyoruz.
İlerleyelim ve /?falan =bir&filan =iki URL’sinden ne döndüğüne bir bakalım.
1
array(1) { ["falan"]=> string(3) "bir" }
Laravel sadece falan için bir değer vermiştir. Bu, redirekt edilen istek için saklanan tek değerdir
ve tam olarak bizim istemiş olduğumuz şeydir! only() metodunda olduğu gibi, flashOnly()
metodunun da bir karşıtı vardır ve o da except()e benzer tarzda çalışır. O, sadece sonraki istek
için verdiğimiz değerlere uymayan değerleri saklayacaktır. Hızla bir göz atalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
Input::flashExcept('falan');
return Redirect::to('yeni/istek');
});
10
11
12
13
14
Route::get('yeni/istek', function()
{
var_dump(Input::old());
});
Laravel’e sadece ‘falan’ anahtarı olmayan istek değerlerini saklamasını istediğimizi söyledik. Tabii
ki, Laravel iyi bir framework gibi davranarak aşağıdaki sonucu verir. -Gözde LaravelŞimdi /?falan =bir&filan =iki URL’sini bir kez daha ziyaret edelim.
1
array(1) { ["filan"]=> string(3) "iki" }
Mükemmel! ‘falan’ hariç hepsini aldık.
Tıpkı get(), only() ve except() metodları gibi old(), flashOnly() ve flashExcept() metodları
da ya bir parametreler listesi, ya da bir dizi alabilirler. Mesela:
İstek Verisi
1
2
3
136
Input::old('birinci', 'ikinci', 'ucuncu');
Input::flashOnly('birinci', 'ikinci', 'ucuncu');
Input::flashExcept('birinci', 'ikinci', 'ucuncu');
4
5
6
7
Input::old(array('birinci', 'ikinci', 'ucuncu'));
Input::flashOnly(array('birinci', 'ikinci', 'ucuncu'));
Input::flashExcept(array('birinci', 'ikinci', 'ucuncu'));
Bu gerçekten size kalmış! İstek verilerinizi anahtarlar şeklinde mevcut bir dizi kullanarak sınırlandırmak istediğinizde ikinci seçenek gerçekten yararlı olabilir. Diğer durumlarda parametrelerin
sıralandığı ilk yöntemin daha temiz bir kod olduğunu düşünüyorum.
Önceki örneklerde, input verimizi flash ettik, ondan sonra da sonraki isteğe redirekt yaptık. Bunlar
web uygulamalarında her zaman olan şeylerdir. Örneğin, kullanıcılarınızın bir form doldurduğunu
ve gönder düğmesine bastığını düşünün. Formu işleyen mantık kısmı cevapta bir hata olup olmadığına karar verecek ve buna göre form verisini flash edecek veya formu gösteren rotaya redirekt
yapacaktır. Böylece formu mevcut verilerle doldurabileceksiniz ve kullanıcılarınız bu bilgilerin
tümünü tekrar girmek zorunda kalmayacaklardır.
İşte Taylor bunun yaygın bir uygulama olduğunu tespit etmiş. Bu sebeple de withInput() metodu
dahil edilmiş. Aşağıdaki örneğe bir göz atalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Redirect::to('yeni/istek')->withInput();
});
Eğer redirekt zincirine withInput() metod halkasını takarsanız, Laravel sizin için şimdiki istek
verisinin tamamını sonraki isteğe flashlayacaktır. Ne kadar hoş değil mi? Laravel sizi seviyor.
Gerçekten seviyor. Bazı gecelerde, yatağınıza kıvrılıp yatarken Laravel gizlice odanıza girer ve
yatağınızın yanına oturur. Yanaklarınızı yumuşak yumuşak okşar ve size tatlı ninniler söyler.
Anneniz bile sizi asla Laravel kadar çok sevmeyecektir.
Afedersiniz, biraz konu dışına çıktım yine. Her neyse, withInput() zinciri aşağıdaki kod parçasına
benzer bir tarzda çalışır.
İstek Verisi
1
137
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
Input::flash();
return Redirect::to('yeni/istek');
});
Bir numara olarak, withInput() zincir metodu ile flashOnly() ve flashExcept() de kullanabilirsiniz. İşte bir Input::flashOnly() alternatifi.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Redirect::to('yeni/istek')->withInput(Input::only('falan'));
});
withInput() zincir metoduna Input::only() metodunun sonucunu geçmek suretiyle, istek verisini
Input::only() metodu içinde tanımlanan anahtarlar kümesine sınırlayabiliyoruz.
Benzer şekilde, istek verisini yukarıdaki örneğin tersine sınırlamak için withInput() metoduna
Input::except() metodunu geçebiliyoruz. Şöyle mesela:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Redirect::to('yeni/istek')->withInput(Input::except('falan'));
});
Artık standart istek verisine nasıl erişeceğimizi biliyoruz fakat dosyalar biraz daha karmaşıktır. İstek
verisi olarak sunulan dosyalar hakkında nasıl bilgi alabileceğimizi görelim şimdi de.
İstek Verisi
138
Gönderilmiş (Uploaded) Dosyalar
Uygulamamızın alabileceği tek istek verisi metin verileri değildir. Çok parçalı kodlanmış bir formun
(multipart encoded form) bir parçası olarak gönderilmiş olan dosyaları da alabiliriz.
Bu veriyi nasıl alacağımızı görmeden önce, bir test ortamı oluşturmamız gerekiyor. Bu özelliği
GET verisini kullanarak gösteremem çünkü gerçekte şimdiki URL’ye bir dosya ekleyemeyiz. Bunun
yerine basit bir form yapalım. Bir görünümle başlayalım.
1
<!-- app/views/form.blade.php -->
2
3
4
5
<form action ="{{ url('form-isle') }}"
method ="POST"
enctype ="multipart/form-data">
6
7
8
9
<input type ="file" name ="kitap" />
<input type ="submit">
</form>
Burada bir dosya yükleme alanı ve bir gönder düğmesi olan bir formumuz var. ‘multipart/form-data’
kodlama tipi olmadıkça dosyaların yüklenmesi çalışmayacaktır.
Güzel, devam edelim. Görünüm yanında başka şeyler de gerekmiyor mu? Tamam, doğru, formu
göstermek için bir rotaya ihtiyacımız var. Bu nasıl sizce?
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
Yeni bir şey yok orada, umarım şaşırmadınız!
OMG, BU NE?
Erm, belki de rotalama bölümüne geri dönseniz daha iyi olacak! Geri kalanlarımız için ikinci bir rota
oluşturalım ve ne aldığımızı görmek için istek verisini dump edelim.
İstek Verisi
1
139
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('form-isle', function()
{
var_dump(Input::all());
});
Peki, formu görmek için şimdi / rotasını ziyaret edelim. Şimdi dump edilen istek verisinden ne
aldığımızı görmek için bir dosya upload etmemiz gerekiyor. Tamam bir dosya gerekiyor… hrm. Ooo,
eveet, zeki bir dostumun Laravel hakkında harika bir kitap yazdığını hatırladım. Bu PDF’yi upload
edeyim!
Bu kitabı genele açık bir siteye upload etmenizi teşvik etmediğime dikkat edin. Zaten bir hafta içinde
çıkacak ve telif ihlali konusunda zaten 5 email gönderdim! Sayıyı artırmanın alemi yok!
Tamam, Code Bright PDF’yi seçin ve gönder düğmesine basın! Ne cevap aldınız?
1
array(0) { }
Hey, ne yapıyorsun Laravel? Bizim dosyamızı ne yaptın? Ah, tamam doğru, PHP’de, dosyalar $_GET
veya $_POST dizilerinde depolanmıyor. PHP bu değerleri $_FILES dizisinde saklar… ama Laravel
(tabi, aslında Symfony) bu dizi yerine güzel bir sarıcı sağlamaktadır. Bak nasıl yapılıyor.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('form-isle', function()
{
var_dump(Input::file('kitap'));
});
İstek Verisi
140
İkinci rotamızı değiştirdik, bir string olarak elde etmek istediğimiz inputun name niteliğini (formdaki)
vererek Input::file() metodunun değerini dump ettik.
Bizim dosyamızı temsil eden bir nesneyi elde ettik.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
object(Symfony\Component\HttpFoundation\File\UploadedFile)#9 (7) {
["test":"Symfony\Component\HttpFoundation\File\UploadedFile":private]=>
bool(false)
["originalName":"Symfony\Component\HttpFoundation\File\UploadedFile":private]=>
string(14) "codebright.pdf"
["mimeType":"Symfony\Component\HttpFoundation\File\UploadedFile":private]=>
string(15) "application/pdf"
["size":"Symfony\Component\HttpFoundation\File\UploadedFile":private]=>
int(2370413)
["error":"Symfony\Component\HttpFoundation\File\UploadedFile":private]=>
int(0)
["pathName":"SplFileInfo":private]=>
string(36) "/Applications/MAMP/tmp/php/phpPOb0vX"
["fileName":"SplFileInfo":private]=>
string(9) "phpPOb0vX"
}
Güzel! Tamam… Evet çok güzel olmayabilir. Yaptık ama! Neyse ki, bu nesne kendisiyle etkileşim
yapabileceğimiz bazı metodlara sahiptir. Bu nesnenin metodlarının Symfony projesine ait olduğunu
ve bir kısmının PHP SplFileInfo sınıfından türetildiğini unutmayın. Açık kaynağın güzelliği budur,
paylaşım önem vermektir! Symfony ve PHP’nin isimlendirme gelenekleri Laravel’inkinden bir parça
daha uzun olma eğilimindedir ancak etkilidir.
Şimdi birincisine bir göz atalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('form-isle', function()
{
return Input::file('kitap')->getFileName();
});
İstek Verisi
141
Bizim file nesnemize getFileName() metodunu ekledik ve onun değerini ‘form-isle’ rota Closure’unun sonucu olarak atadık. Sonucu görmek için Code Bright PDF’yi bir kez daha gönderelim.
1
phpaL1eZS
Bekle bir saniye, bu ne? Sizdeki sonuç bundan farklı da görünse yine kafa karıştırıcı bir isim olacaktır.
Gördüğünüz gibi bu, yüklediğimiz dosyaya geçici konumunda verilen geçici dosya adıdır. Eğer
isteğin sonunda onu başka bir yere taşımazsanız, oradan kaldırılacaktır.
Geçici isim şu an için çok yararlı değildir. Upload edildiği zaman dosyanın gerçek adının ne
olduğunu bulmaya çalışalım. Hmm, şu metodu deneyelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('form-isle', function()
{
return Input::file('kitap')->getClientOriginalName();
});
Bu sefer getClientOriginalName() metodunu deneyeceğiz. Kitabı upload ettikten sonra alacağımız
sonucu görelim.
1
codebright.pdf
Şimdi ona daha benzedi! Dosyanın gerçek adını elde ettik. Metodun adı biraz hantal ama doğru bir
şekilde iş görüyor.
Pek tabii, dosya gönderilerini düşününce, dosya adından başka bilgiler de söz konusudur. Şimdi de
dosya boyutunu nasıl bulacağımızı görelim.
İstek Verisi
1
142
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('form-isle', function()
{
return Input::file('kitap')->getClientSize();
});
Dosya yükledikten sonra alacağımız şey bir sayıdır.
1
2370413
Bu sayılar kazanan piyango numaralarıdır. Bilet almıştınız değil mi? Bu güzel olurdu ama üzgünüm,
bu sadece gönderilen dosyanın boyutudur. Symfony API değerin formatının ne olduğunu söylemiyor ama zekice bazı matematik işlemleriyle bunun bayt cinsinden dosya boyutu olduğunu keşfettim.
Üzerinde çalıştığımız dosyanın ne türde olduğunu bilmek işimize yarayabilir, o zaman bu amaca
hizmet eden bir çift metodu görelim.
Birincisi getMimeType() metodudur. Haydi uygulayalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('form-isle', function()
{
return Input::file('kitap')->getMimeType();
});
Alacağımız sonuç bir mime tipidir. Bu, dosyaları tanımlamak için kullanılan bir gelenektir. Code
Bright kitabının sonucu şudur.
İstek Verisi
1
143
application/pdf
Bu sonuçla biz dosyanın bir PDF olduğunu açıkça görebiliyoruz. ‘file’ sınıfı mime tipinden dosya
uzantısını tahmin etmeyi deneyebileceğimiz bir metoda da sahiptir. Bu guessExtension() metodunu eyleminde görelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('form-isle', function()
{
return Input::file('kitap')->guessExtension();
});
Bir kez daha gönderdiğimizde aşağıdaki cevabı alırız.
1
pdf
Mükemmel, dosyamız kesinlikle bir PDF.
Pekiyi, bunu nasıl izleyeceğiz? Göndermiş olduğumuz dosya orada bekleyip durmuyor. Eğer dosyayı
istek sona ermeden önce taşımazsak, onu kaybedeceğiz. Böyle olsun istemiyorum! O çok güzel bir
kitap. Onu muhafaza etmeliyim.
Öncelikle bu dosyanın şu anda nerede bulunduğunu bulmaya çalışalım. UploadedFile sınıfı bu
görevde bize yardımcı olacak bir metoda sahiptir. Hadi görelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
return View::make('form');
});
İstek Verisi
10
11
12
13
144
Route::post('form-isle', function()
{
return Input::file('kitap')->getRealPath();
});
Göndermiş olduğumuz dosyanın şu andaki konumunu elde etmek için getRealPath() metodunu
kullanabiliyoruz. Code Bright’ı gönderince alacağımız cevabın ne olduğunu görelim.
1
/tmp/php/phpLfBUaq
Artık dosyamızın nerede olduğunu biliyoruz, ilerde onu kullanmak için dosyamızı bir yerlere
kaydetmek için copy() veya rename() gibi şeyleri rahatlıkla kullanabiliriz. Daha iyi bir yolu var
bunun. File nesnesi üzerinde move() metodunu kullanabiliriz. Bu metodun nasıl çalıştığını görelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
14
Route::post('form-isle', function()
{
Input::file('kitap')->move('/storage/dizin');
return 'Dosya taşındı.';
});
Bu move() metodunun ilk parametresi dosyanın taşınacağı hedef konumdur. Web sunucunuzu
çalıştıran kullanıcının bu hedef konuma yazma izni olduğundan emin olun, aksi takdirde bir istisna
oluşacaktır.
Kitabı bir kez daha upload edip ne olduğunu görelim. Eğer storage dizininize bakarsanız, dosyanın
taşınmış olduğunu göreceksiniz ancak ismi hala PHP’nin ona verdiği geçici isimdir.
Belki biz geçici isim yerine dosyanın gerçek ismini tutmak isteyebiliriz. Neyse ki, move() metodu
dosyaya bizim kendi seçeceğimiz bir isim vermemize imkan veren ilave, opsiyonel bir parametre de
kabul eder. Eğer dosyanın gerçek adını elde eder ve onu move() metoduna ikinci parametre olarak
geçersek, hedef konumuna daha mantıklı bir isimle varacaktır.
Bir örnek üzerinden görelim.
İstek Verisi
1
145
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
14
15
Route::post('form-isle', function()
{
$isim = Input::file('kitap')->getClientOriginalName();
Input::file('kitap')->move('/storage/dizin', $isim);
return 'Dosya taşındı.';
});
İlk olarak gerçek dosya adını getClientOriginalName() metoduyla elde ediyoruz. Sonra bu değeri
move() metoduna ikinci parametre olarak geçiyoruz.
Şimdi storage dizinine bir daha bakalım. İşte orada! ‘codebright.pdf’ adında bir dosyamız var.
Bu bölümde dosyalar hakkında anlatacaklarım bu kadar, fakat ayıracak bir zamanınız varsa
UploadedFile sınıfı için Symfony API belgelerine²⁴ ve bu sınıftan türetilen metodlara bakmanızı
öneririm.
Çerezler
Çerezlere bakmak istemiyorum çünkü geçen yıldan beri düşük karbonhidrat diyetindeyim. Bu şeyler
sadece şekerle dolu. Bunu yapmanın hiçbir yolu yok. Üzgünüm çocuklar.
Düşük karbonhidratlı bademli kurabiyelere ne dersin?
Oo bak hele. Düşük karbonhidrat püf noktalarının hepsini biliyorsunuz yoksa bilmiyor musunuz?
Güzel, çerezler düşük karbonhidratlı ise anlatabilirim sanırım.
Pekala, çerezler (cookies) nedir? Bunlar gerçekte besin değildir. Bunlar bazı verilerin istemci
tarafında yani tarayıcıda depolanması için bir yöntemdir. Bunlar pek çok şey için gerçekten
kullanışlı olabilir. Örneğin, kullanıcılara sitenizi ilk kez ziyaret ettikleri zaman bir mesaj göstermek
isteyebilirsiniz. Bir çerez mevcut değilse bu iletiyi gösterebilirsiniz ve mesaj görüldüğü zaman, çerezi
ayarlarsınız.
Çerezlerde istediğiniz her şeyi saklayabilirsiniz. İstediğiniz kadar üretken olun! Dikkatinizi çekebildim mi? Güzel! Bir çerez nasıl oluşturuluyor bakabiliriz öyleyse.
²⁴http://api.symfony.com/2.0/Symfony/Component/HttpFoundation/File/UploadedFile.html
İstek Verisi
146
Çerezlerin Ayarlanması ve Elde Edilmesi
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
$cookie = Cookie::make('az-sekerli', 'bademli kurabiye', 30);
});
Yeni bir çerez oluşturmak için Cookie::make() metodunu kullanabiliyoruz. Çok açıklayıcı değil mi?
Güzel iş Taylor!
Metodun birinci parametresi çerezimizi tanımlamak için kullanacağımız bir anahtardır. İleride değeri elde etmek için bu anahtarı kullanmamız gerekecek. İkinci parametre ise çerezimizin değeridir.
Verdiğimiz örnekte ‘bademli kurabiye’ stringidir. Üçüncü ve son parametre Laravel’in bu çerezi
dakika cinsinden ne süreyle tutacağını bilmesini sağlar. Yukarıdaki örneğimizde, çerez 30 dakika
boyunca var olacaktır. Bu zaman geçtikten sonra çerez sona erecek ve bir daha elde edilemeyecektir.
Çerezin işlevselliğini test edebilmek için rotamızda küçük bir değişiklik yapalım. Yeni rota dosyamız
bunun gibi gözükür:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$kurabiye = Cookie::make('az-sekerli', 'bademli kurabiye', 30);
return Response::make('Ham ham.')->withCookie($kurabiye);
});
10
11
12
13
14
15
Route::get('/ham-ham', function()
{
$kurabiye = Cookie::get('az-sekerli');
var_dump($kurabiye);
});
URL /ya cevap veren ilk rotamızda, önceki örneğimizde yaptığımız aynı yolla bir çerez oluşturuyoruz. Ancak bu sefer, withCookie() metodunu kullanarak onu cevabımıza tutturuyoruz.
Bu withCookie() metodu bir cevap nesnesine bir çerez bağlamak için kullanılabilmektedir. Cevap
sunulduğu zaman, çerez oluşturulur. withCookie() metoduna geçilen tek parametre oluşturduğumuz çerezdir.
İstek Verisi
147
/ham-ham rotası Cookie::get() metodunu kullanarak çerezi elde edecektir. Birinci ve tek parametre
elde edilecek çerezin ismidir. Bu örnekte bizim ‘az-sekerli’ çerezimizi elde ediyor ve sonucu dump
ediyoruz.
Şimdi çıktıyı test edelim. Önce çerezimizi ayarlamak için / rotasını ziyaret edelim.
1
Ham ham.
Harika, cevap sunulmuştur ve çerezimiz ayarlanmış olmalıdır. Emin olmak için /ham-ham rotasını
ziyaret edelim.
1
string(16) "bademli kurabiye"
Mükemmel! Çerezimizi başarıyla elde ettik.
Eğer 30 dakika beklersek ve sonra çerezi bir kez daha elde etmeye çalışırsak null değerini alacağız.
Input::get() metodu gibi, Cookie::get() metodu da ikinci parametre olarak ön tanımlı bir değer
kabul edecektir, şöyle bir şey:
1
$kurabiye = Cookie::get('az-sekerli', 'tavuklu');
Güzel, şimdi eğer az şekerli çerez yoksa en azından tavuklu alacağız.
Bir çerez ayarlanmış olup olmadığını yoklamak için Cookie::has() metodunu kullanabiliriz. Bu
metod birinci parametre olarak çerezin adını alır ve boolean bir sonuç döndürür. Şuna benzer.
1
2
3
4
Route::get('/ham-ham', function()
{
var_dump(Cookie::has('az-sekerli'));
});
Daha önceden bahsettiğim gibi, çerezlerinizin bir bitiş zamanı olacaktır. Belki çerezlerinizin sona
ermesini istemezsiniz? Laravel’in zihin okuma gücü sayesinde, asla sona ermeyecek bir çerez
oluşturmak için Cookie::forever() metodunu kullanabiliriz.
Metodun birinci ve ikinci parametreleri aynıdır: bir anahtar ve bir çerez değeri. İşte bir örnek:
İstek Verisi
1
148
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$kurabiye = Cookie::forever('az-sekerli', 'bademli kurabiye');
return Response::make('Ham ham.')->withCookie($kurabiye);
});
Mutfak dolabınızdaki çerezlerden farklı olarak, forever() metoduyla yapılanların bir son kullanma
tarihi yoktur.
Eğer bir çerezi silmek veya daha doğrusu… sona ermeye zorlamak istersek Cookie::forget()
metodunu kullanabiliriz. Bu metodun tek parametresi unutmak istediğimiz çerezin adıdır. İşte bir
örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
Cookie::forget('az-sekerli');
return 'Sanırım tavuklu yemek zorunda kalacağız.';
});
Aynen öyle, az şekerli kurabiyemiz gitti.
Çerez Güvenliği
Laravel’in zeki küçük bir maymun olduğunu gösteren başka bir örnek.
Bir kullanıcı tarayıcıda saklanan çerezleri düzenleyebilir. Eğer çereziniz websiteniz için bir şekilde
önemliyse, kullanıcın ona müdahale etmesini istemezsiniz. Bizim çerezlerimizin tahrif edilmediğinden emin olmalıyız.
Neyse ki, Laravel bizim çerezlerimizi bir kimlik doğrulama kodu ile işaretler ve kriptolar, böylece
kullanıcılarımız çerezleri okuyamazlar ve onları düzenleyemezler.
Eğer bir çerez tahrif edilmiş ve kimlik doğrulama kodu Laravel’in beklediği gibi değilse, bu durumda
Laravel tarafından göz ardı edilecektir. Çok zekice değil mi?
Formlar
Los Angeles’ta soğuk, karanlık bir geceydi, fakat Dolby Tiyatrosunun ışıkları sokakları öyle aydınlatıyordu ki, gündüz zannedebilirdiniz. Bildiğiniz gibi mekan Şubat ayındaki Akademi Ödülleri için
ayarlanmıştı, o gece sunulan Oscarların hepsini Iron Man kazanmıştı, kesinlikle, tamamen müthiş
bir şeydi.
Aslında, bu kitap iyi satarsa California’da bir mansiyon alabilirim; belki onun altında süper bir laboratuvar oluşturabilirim. Orada kırmızı pandaya benzer bir dış iskelet modası üzerine çalışabilirim.
Tony Stark’tan farklı olarak süpermodelleri yalnız bırakırım. Emma’nın bu komik işe aday olacağını
hiç sanmıyorum.
Ben kendime ‘The Fire Fox’ adını verdim ve gücümü ve aletlerimi PHP toplumu içerisinde ortaya
çıkan argümanları sekmeler ve boşluklara yerleştirmek için kullanıyorum. Ayrıca Mozilla davasını
alma gücüne sahibim. Buna rağmen belki de hiç Audi süremeyeceğim. Ben onun yerine… tamam.
Afedersiniz, biraz konu dışına çıktım. Neredeydik?
Ah evet, Dolby Tiyatrosu. Görüyorsunuz, Oscarlar sadece çok daha yüksek profilli bir olayın, Laravel
dört galasının önünü açıyordu. Laravel 3 tarafından kurtarılmış PHP geliştiricileri yeni frameworkun
lansmanını bekleyen milyonlarla bir araya gelmişti. Kalabalıklar sokakları dolduruyordu ve her
yerde gazeteciler vardı! Hayal edin, gerçekten değil. Bunun çoğunu hayal edeceksiniz, ben sizin
için kurgusal bir yazar değilim. Sadece bunun televizyonda nasıl görüneceğini hayal edin.
Uzun, siyah bir limuzin kaldırıma yaklaşır. Kapı açılır ve uzun bir figür kırmızı halıda yürümeye
başlar. Herkes çıldırır! Kadınlar üstlerini yırtar, erkekler birbirlerinin üstlerini yırtar, gazeteciler
üstlerini yırtar, herkes üstsüzdür. Bunun nereye gideceğini bilmiyorum ancak bu benim öyküm, ben
herkesin üstsüz olduğunu söylüyorum. Neyse, bu figür şüphesiz Laravel’i yazan Taylor Otwell’dan
başkası değil.
Taylor tiyatronun girişine adımını atmadan önce, gazeteciler Laravel dört konusunda bombardımana tuttular. O üstsüz gazetecileri itmeye çalıştı ancak onlardan çok vardı.
Laravel dört üzerinde ne zamandır çalışıyorsunuz?
“Şu anda bir yıl kadar.” diyor Taylor, devam edebilmek için tek şansının sorulardan bir kısmına cevap
vermek olduğunu düşünerek. Üstsüz gazeteci sorusuna aldığı cevaptan tatmin olmuş görünüyor ve
Taylor’un geçmesine izin veriyor.
Başka bir üstsüz gazeteci hızla Taylor’un önüne geçiyor ve başka bir soru soruyor.
Laravel dört üzerinde ne zamandır çalışıyorsunuz?
Formlar
150
Bekle bir saniye. Bu soru zaten sorulmamış mıydı? “Tekrar değil.” diye düşündü, kendini tekrar
etmekten nefret ederdi. “Bir yıl kadar.” dedi sert bir sesle. Kalabalığın arasından kendini öne itmişti
ki, yolu başka bir üstsüz gazeteci tarafından kesildi.
Laravel oldukça uzun bir proje. Onu yazmak ne kadar zaman aldı?
Taylor ani bir şey yaptı, cebine uzandı ve bir klavye çıkardı. Taylor gibi uzun cepler… oh klavye
oraya nasıl sığdı, ben bununla yapıyorum. Klavyeyle gazetecinin yüzüne vurdu ve üstsüz adamı
yakındaki bir çöp bidonuna gönderdi.
“Belgeleri okumadınız mı!?” Taylor yağlı çöp bidonundan çıkmaya çalışan gazeteciye bağırdı. “Ben
form oluşturucusu yaptım, bu sayede formlarınızı şablonlar içinde tanımlayabiliyorsunuz. Eğer
önceden bir form yapmış olsaydınız, tüm bu tekrarları önleyebilirdik. BEN TEKRARDAN NEFRET
EDERİM”, diye hırladı Taylor. Etrafında döndü ve kuliste onu bekleyen Laravel ekibinin olduğu
kapıya fırladı.
Evet. Tüm bunlar form oluşturucu inşa etmek içindi. Hayal kırıklığına uğramış gibi bakmayın bana!
Ben teknik bir yazarım, kurgusal yazar değil. Ne bekliyordunuz ki… Bir Buz ve Ateş Şarkısı mı?
Peki, onunla başlayalım!
Bu bölüm gerçek bir ikilem arz ediyor. Tamam, sadece bu bölüm değil. Problemi benden daha iyi
çözüp çözemeyeceğinizi görelim. Birbirine geçmiş konular işte burada.
• İstek Verisi
• Formlar
• Geçerlilik Denetimi
Gördüğünüz gibi, bunların hepsi büyük konular ancak aynı zamanda birbirilerine dayanıyorlar. İstek
verisini nasıl elde edeceğinizi bilmeden bir formdan gelen cevabı test edemezsiniz. Geçerlilik denetimini öğrenmeden önce formların yeniden doldurulmasını gösteremezsiniz. Gerçek bir kördüğüm.
Peki, problemi nasıl çözeceğime geçiyorum. Formlar nasıl oluşturulur ve bunlar çatı rotalarına
nasıl hedef gösterilir onu öğrenmekle başlıyoruz. Ancak, formlarımızın açıklamalarından bir kısmı
sonraki bölümde yer alacaktır ve orada geçerlilik denetimi yaptıktan sonra formların tekrar nasıl
doldurulacağını öğreneceğiz.
Haydi başlayalım!
Formların Açılması
Veriyi göndermeden önce onu nereye göndereceğimize karar vermemiz gerekiyor. URL üretimi
bölümünde çatı URL’lerinin nasıl oluşturulduğunu görmüştük. O bölümden kaptığımız becerilerle
bir form açma tagını kolaylıkla oluşturabiliriz, doğru mu?
İsterseniz basit bir görünüm oluşturalım:
Formlar
1
151
<!-- app/views/form.blade.php -->
2
3
<form action ="{{ url('bizim/hedef/rota') }}" method ="POST">
4
5
</form>
Burada bizim formun action niteliği için bir çatı URL’si sağlamak amacıyla url() helper metodunu
kullanıyoruz. form.blade.php blade şablonumuzu göstermek için bir rota closure’ı da ekleyelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
Mükemmel! Gösterilecek kaynağı görmek için şimdi / url’yi ziyaret edelim. Şuna benzer görünecektir.
1
<!-- app/views/form.blade.php -->
2
3
<form action ="http://demo.dev/bizim/hedef/rota" method ="POST">
4
5
</form>
Formumuz şimdi toplayacağı veriler için bir hedef konuma sahiptir. Bu benim formları oluşturma
eğilimim ancak Laravel bir seçenekler frameworküdür. Burada belki sizin tercih edeceğiniz başka
bir form açma HTML’si oluşturma yöntemini veriyorum. Bir bakalım.
1
<!-- app/views/form.blade.php -->
2
3
{{ Form::open(array('url' => 'bizim/hedef/rota')) }}
4
5
{{ Form::close() }}
Form açılış tagı oluşturmak için Form::open() üretici metodunu kullanıyoruz. Bu metod birçok
parametreli tek bir parametre kabul eder. Kafanız mı karıştı? Bir diziden bahsediyorum!
Yukarıdaki örnekte formun hedefi olmasını istediğimiz bir rota değeri olan URL indeksini kullanıyoruz. Ayrıca formu kapatmak için Form::close() metodunu kullanıyoruz, gerçi bunu yapmamızı
gerektiren bir sebep göremiyorum. Diğer kaynak üretme metodları yanında daha düzgün görünüyor
değil mi? Bunu ya da bunun yerine </form> kullanmayı seçmek size kalmış.
Gösterilen form görünümümüzün kaynağı şöyledir.
Formlar
1
152
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
<form method ="POST" action ="http://demo.dev/bizim/hedef/rota" accept-charset ="UTF\
-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
</form>
Harika, formumuz aynı hedef için oluşturulmuş.
Burada sizden gerçekten önemli bir şey isteyeceğim, buradaki gizli _token niteliğini lütfen göz ardı
eder misiniz? Merak etmeyin, ilerde geri döneceğiz! Şimdilik o yokmuş gibi yapacağım.
Bekleyin, biz sadece bir tek URL niteliğini verdik. Neden orada başka nitelikler var?
Konuyu _token inputundan uzak bir yöne değiştirmek için yaptım.
_token nedir?
Harika! Tamam, sizin sorunuza geri dönelim. Gördüğünüz gibi, Laravel web uygulamaları yapılırken
en popüler seçimin POST formlar olduğunu bilir. Bu sebeple, HTML’nin kabul ettiği ön tanımlı GET
metodunu geçersiz kılarak ön tanımlı olarak POST metodunu kullanacaktır.
accept-charset niteliği ise Laravel’in bize sağladığı bir diğer yararlı niteliktir. Formu gönderirken
karakter kodlaması olarak ‘UTF-8‘ kullanılmasını temin edecektir. Bu çoğu durum için mantıklı bir
ön tanımdır.
Belki de mantıklı ön tanımlar bizim durumumuza uygun değildir? Bunları kendimiz nasıl verebiliriz?
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
{{ Form::open(array(
'url' => 'bizim/hedef/rota',
'method' => 'GET',
'accept-charset' => 'ISO-8859-9'
)) }}
8
9
{{ Form::close() }}
Burada formumuzun metodu olarak GET kullanmak istediğimizi belirtmek için Form::open()
dizimizin method anahtarını kullandık. Ayrıca, kodlama için farklı bir karakter seti sağlamak için
accept-charset anahtarını kullandık.
Yeni HTML kaynağımız şöyledir.
Formlar
1
153
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
<form method ="GET" action ="http://demo.dev/bizim/hedef/rota" accept-charset ="ISO-\
8859-9">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
</form>
Mükemmel! Biz uygun ön tanımlar için Laravel’in görüşlerine değer verirken, onları geçersiz kılmak
için tüm esnekliğe de sahibiz. Bu denli güçlü olduğumuza göre neden metod niteliği olarak ‘DELETE’
HTTP fiilini kullanan bir form oluşturmayalım.
Formun metodunu method dizi anahtarı ile değiştirebileceğimizi biliyoruz. Hadi yapalım.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
{{ Form::open(array(
'url' => 'bizim/hedef/rota',
'method' => 'DELETE'
)) }}
7
8
{{ Form::close() }}
Bana mükemmel görünüyor, şimdi oluşturulan HTML kaynağını kontrol edelim.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
<form method ="POST" action ="http://demo.dev/bizim/hedef/rota" accept-charset ="UTF\
-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<input name ="_method" type ="hidden" value ="DELETE">
</form>
Bekleyin, orada neler oluyor? Biz bir ‘POST’ metodu istememiştik. O ekstra input nedir? Aaa evet,
şimdi hatırladım. HTML formları biraz aptaldır. Bildiğiniz gibi, HTML4 formlarda sadece ‘POST’
ve ‘GET’ metodlarını destekler. HTML5 ilave metodları da destekler ancak biz uygulamalarımızı
HTML5’i tam destekleyen tarayıcılarla sınırlı tutmak istemeyiz.
Merak etmeyin, Laravel’in kolunun altında başka bir hile var. ‘_method’ adında gizli bir input
vermek suretiyle, HTML5 uyumlu olmayan HTTP fiillerini temsil edecek bir değer verebiliyoruz.
Laravel’in rotalaması bu POST isteğine bakacak, ‘_method’ verisi dahil edildiğini görecek ve uygun
eyleme rota yapacaktır. Bu hem HTML4 hem de HTML5’te çalışacaktır, ne kadar harika değil mi?
Formlar
154
Geçtiğimiz bölümde dosya göndermeyi ve onlar hakkında bilgi almayı öğrendiğimizi hatırlıyor
musunuz? Dosyaların düzgün biçimde gönderilebilmesi için ilgili formun ‘multipart/form-data’
değerinde bir ‘enctype’ niteliği olması gerektiğini de hatırlıyor olabilirsiniz.
İşte Laravel bu ‘enctype’ niteliğini etkin hale getirmek için kolay bir yol sağlamıştır. Nasıl yaptığını
görelim.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
{{ Form::open(array(
'url'
=> 'bizim/hedef/rota',
'files'
=> true
)) }}
7
8
{{ Form::close() }}
Değeri boolean true olan files adlı yeni bir anahtar vermek suretiyle, Laravel dosya gönderimlerine
imkan vermek için gerekli niteliği ekleyecektir. Yukarıdaki örnekle üretilen kaynak şöyledir.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
<form method ="POST"
action ="http://demo.dev/bizim/hedef/rota"
accept-charset ="UTF-8"
enctype ="multipart/form-data">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
</form>
Ben okunması kolay olsun diye cevapta küçük bir biçimlendirme yaptım, fakat içeriği değiştirmedim.
Görebileceğiniz gibi, Laravel bizim adımıza doğru kodlama tipini vermiştir. Tekrar teşekkürler
Laravel!
URL üretimi ve rotalama bölümlerinde isimli rotalar ve denetçi eylemlerine URL üretmenin özel
metodlar kullanılarak yapılabildiğini öğrenmiştiniz. Şimdi Form::open() metodunda özel anahtarlar
kullanarak bu rota ve eylemleri nasıl hedefleyebileceğimizi görelim.
Önce isimli rotaları nasıl hedefleyebileceğimize bakalım. İşte bir örnek.
Formlar
1
155
<!-- app/views/form.blade.php -->
2
3
4
5
{{ Form::open(array(
'route'
=> 'benim_rotam'
)) }}
6
7
{{ Form::close() }}
url anahtarını kullanmak yerine, route anahtarını kullanıyoruz ve bir değer olarak rotanın adını
veriyoruz. Bu kadar basit! Aslında, denetçi eylemlerini hedeflemek de bu kadar kolaydır. İsterseniz
başka bir örneğe göz atalım.
1
<!-- app/views/form.blade.php -->
2
3
4
5
{{ Form::open(array(
'action'
=> 'BenimController@eylemim'
)) }}
6
7
{{ Form::close() }}
Formumuzun hedefinin bir denetçi eylemi olması için, url ve route anahtarları yerine, action
anahtarını kullanıyoruz ve değer olarak denetçi-eylem çiftini veriyoruz.
Pekiyi, artık formların nasıl açılacağını biliyoruz, bazı alanlar eklemeye başlama zamanının geldiğini
düşünüyorum. Haydi başlayalım!
Form Alanları
Formlar bir veri toplama yolu sağlamadıkları sürece çok işe yaramazlar. Laravel’in form oluşturma
kitaplığını kullanarak üretebileceğimiz form alanlarının bir kısmını görelim.
Alan Etiketleri
Durun, alanların kendileri dışında ihtiyacımız olan başka bir şey var. İnsanların ne değerler
gireceğini bilmesi için etiketlere (label) ihtiyacımız var. Etiketler Form::label() metodu kullanılarak
üretilebilirler. Haydi bir örneğe bakalım.
Formlar
1
156
<!-- app/views/form.blade.php -->
2
3
4
5
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::label('ilk_isim', 'Ad') }}
{{ Form::close() }}
Form::label() metodunun ilk parametresi tarif ettikleri alanın name niteliğine tekabül eder. İkinci
değer ise etikette görülecek metindir. Üretilen kaynağa bakalım.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<label for ="ilk_isim">Ad</label>
</form>
Süper! Alanlarımızı tarif etmek için hep label kullanabiliriz. Bölüm içindeki diğer alan örnekleri ile
de bunları kullanacağım.
label() metoduna üçüncü bir parametre vererek, üretilen label elementi içinde başka nitelik ve
değer çiftleri verebileceğimizi unutmayın. Üçüncü parametre nitelik ve değer çiftlerinden oluşan bir
dizidir. İşte bir örnek.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::label('ilk_isim', 'Ad',
array('id' => 'ilk_isim')) }}
{{ Form::close() }}
Yukarıdaki örnekte label elementine değeri ‘ilk_isim’ olan bir ‘id’ niteliği eklemiş olduk. Üretilen
kod şöyledir.
Formlar
1
157
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<label for ="ilk_isim" id ="ilk_isim">Ad</label>
</form>
Harika! Tamam, şimdi alanlarımızı nasıl etiketleyeceğimizi öğrendik, artık biraz alan oluşturmaya
geçebiliriz.
Text Alanları
Text alanları string veri değerleri toplamak için kullanılabilirler. Bu alan tipinin jeneratörü şöyledir.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::label('ilk_isim', 'Ad') }}
{{ Form::text('ilk_isim', 'Taylor Otwell') }}
{{ Form::close() }}
Form::text() metodunun ilk parametresi ilgili elementin name niteliğidir. Bu nitelik, istek veri
koleksiyonumuzdaki alanın değerini saptamak için kullanacağımız şeydir.
İkinci parametre opsiyoneldir. İnput elementi için bir ön tanım değeridir. Bu metodla üretilen
kaynağa bir göz atalım.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
10
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<label for ="ilk_isim">Ad</label>
<input name ="ilk_isim" type ="text" value ="Taylor Otwell" id ="ilk_isim">
</form>
İnput elementimiz oluşturulmuş ve ‘name’ niteliği bir ön tanım değerine ayarlanmıştır. Tıpkı
Form::label() metodunda olduğu gibi, Form::text() metodu da nitelik ve değer çiftlerinden
oluşan bir dizi biçiminde üçüncü bir opsiyonel parametre daha alabilmektedir, mesela şöyle:
Formlar
1
158
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::label('ilk_isim', 'Ad') }}
{{ Form::text('ilk_isim', 'Taylor Otwell',
array('id' => 'ilk_isim')) }}
{{ Form::close() }}
Gerçek şu ki, input jeneratörlerinden her biri son bir parametre olarak opsiyonel bir nitelikler
dizisi alabilmektedir. Bunların hepsi aynı şekilde iş görür, bu yüzden her kesimde bu özelliği
açıklamayacağım. Ama bunu hep aklınızda tutun!
Textarea Alanları
Textarea alanı text inputuyla aynı şekilde iş görür ama textarea çok satırlı veriye izin vermektedir.
Bunların nasıl üretildiğine göz atalım.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::label('aciklama', 'Açıklama') }}
{{ Form::textarea('aciklama', 'Şimdiye kadarki en iyi alan!') }}
{{ Form::close() }}
İlk parametre elementin name niteliğidir ve ikinci parametre yine varsayılan değerdir. Üretilen HTML
kaynağında gözükecek form elementi şöyle olacaktır.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
10
11
12
13
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<label for ="aciklama">Açıklama</label>
<textarea name ="aciklama"
cols ="50"
rows ="10"
id ="aciklama">Şimdiye kadarki en iyi alan!</textarea>
</form>
Üretilen kaynaktan görebileceğiniz gibi, Laravel bazı ön tanımlı değerler sağlamıştır, örneğin
alandaki sütun ve satır miktarı gibi. Opsiyonel son parametre ile bu ön tanımlı değerleri geçersiz
kılıp değiştirebiliyoruz. Umarım bu nitelikler dizisini unutmazsınız!
Formlar
159
Password Alanları
Bazı şeyler sırdır. Aslında, isterseniz sırlarınızı bana açıklayabilirsiniz. Ben bunları daha fazla
gülelim diye teknik bir kitapta şaka olarak yayınlayacak tiplerden değilim.
Bununla birlikte, şayet kullanıcılarınızın en gizli sırlarını uygulamamıza güvenle yazabilmelerini
isterseniz, bu durumda yazdıkları karakterlerin ekranda gözükmemesini temin etmemiz gerekiyor.
‘password’ giriş tipi bunun için idealdir. Bunu nasıl üretebileceğimizi görelim.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::label('gizli', 'Süper Gizli') }}
{{ Form::password('gizli') }}
{{ Form::close() }}
Form::password() metodunun ilk parametresi name niteliğidir. Her zaman olduğu gibi, diğer
element niteliklerinden oluşan bir dizi şeklinde son bir opsiyonel parametre de alabilir.
Yukarıdaki örnekten üretilen kaynak şöyledir.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
10
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<label for ="gizli">Süper Gizli</label>
<input name ="gizli" type ="password" value ="" id ="gizli">
</form>
Onay Kutuları
Onay kutuları boolean değerleri almanıza imkan verir. Bir onay kutusu form elementinin nasıl
oluşturulacağına bir göz atalım.
Formlar
1
160
<!-- app/views/form.blade.php -->
2
3
4
5
6
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::label('pandalar_sevimlidir', 'Pandalar sevimli midir?') }}
{{ Form::checkbox('pandalar_sevimlidir', '1', true) }}
{{ Form::close() }}
Form::checkbox() metodunun ilk parametresi alanın name niteliğidir ve ikinci parametre alanın
value niteliğidir. Üçüncü parametre ise onay kutusunun ön tanımlı olarak onaylı olup olmayacağını
belirten opsiyonel bir parametredir. Onay kutusu için ön tanımlı değer onaysız olmasıdır.
Yukarıdaki örnekle üretilen kaynak şudur.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
10
11
12
13
14
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<label for ="pandalar_sevimlidir">Pandalar sevimli midir?</label>
<input checked ="checked"
name ="pandalar_sevimlidir"
type ="checkbox"
value ="1"
id ="pandalar_sevimlidir">
</form>
Seçenek Düğmeleri
Radio buttonların yazım şekli onay kutularınınki gibidir. Farkı, küçük bir değerler grubu içinden bir
seçim yapma yöntemi olarak birkaç radio giriş tipi kullanabilmenizdir. İşte bir örnek.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::label('panda_rengi', 'Pandalar ne renktir?') }}
{{ Form::radio('panda_rengi', 'kirmizi', true) }} Kırmızı
{{ Form::radio('panda_rengi', 'siyah') }} Siyah
{{ Form::radio('panda_rengi', 'beyaz') }} Beyaz
{{ Form::close() }}
Formlar
161
Yukarıdaki örnekte aynı name niteliğine sahip birden çok seçenek düğmesi olduğu dikkatinizi
çekecektir. Bir defada onların sadece birisini seçebiliyor olacağız. Bu şekilde, kullanıcılarımıza bir
pandanın rengini seçme imkanı vereceğiz.
Form gösterildiği zaman üretilmiş kaynak şöyle gözükür.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<label for ="panda_rengi">Pandalar ne renktir?</label>
<input checked ="checked"
name ="panda_rengi"
type ="radio"
value ="kirmizi"
id ="panda_rengi"> Kırmızı
<input name ="panda_rengi"
type ="radio"
value ="siyah"
id ="panda_rengi"> Siyah
<input name ="panda_rengi"
type ="radio"
value ="beyaz"
id ="panda_rengi"> Beyaz
</form>
Seçim Kutuları
Seçim kutuları veya açılır kutular uygulamamız kullanıcılarının değerler arasından seçim yapmasını
sağlamanın bir başka yoludur. Bunların nasıl inşa edileceğini görelim.
Formlar
1
162
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
10
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::label('panda_rengi', 'Pandalar ne renktir?') }}
{{ Form::select('panda_rengi', array(
'kirmizi'
=> 'Kırmızı',
'siyah'
=> 'Siyah',
'beyaz'
=> 'Beyaz'
), 'kirmizi') }}
{{ Form::close() }}
Form::select() metodu birinci parametre olarak bir name niteliği kabul eder ve ikinci parametre
olarak anahtar-değer çiftlerinden oluşan bir dizi alır. İstek verisi içinde döndürülecek olan seçilmiş
seçeneğin anahtarıdır. Üçüncü ve opsiyonel parametre ise ön tanımlı olarak seçili gözükecek değerin
anahtarıdır. Yukarıdaki örnekte, sayfa yüklendiği zaman ‘Kırmızı’ seçeneği seçili vaziyette olacaktır.
İşte üretilen kaynak.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
10
11
12
13
14
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<label for ="panda_rengi">Pandalar ne renktir?</label>
<select id ="panda_rengi" name ="panda_rengi">
<option value ="kirmizi" selected ="selected">Kırmızı</option>
<option value ="siyah">Siyah</option>
<option value ="beyaz">Beyaz</option>
</select>
</form>
Eğer istersek, seçim kutusu seçeneklerini kategorilere göre organize edebiliriz. Bunu yapabilmek
için yapacağımız tek şey, ikinci parametre olarak çok boyutlu bir dizi vermektir. Dizinin ilk düzeyi
kategori olacak ve ikinci düzeyi tıpkı önceki gibi değerler listesi olacaktır.
Bir örnek verelim.
Formlar
1
163
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::label('bear', 'Ayılar?') }}
{{ Form::select('bear', array(
'Panda' => array(
'kirmizi'
=> 'Kırmızı',
'siyah'
=> 'Siyah',
'beyaz'
=> 'Beyaz'
),
'Karakter' => array(
'pooh'
=> 'Pooh',
'baloo'
=> 'Baloo'
)
), 'siyah') }}
{{ Form::close() }}
Değerler dizimize yeni bir boyut ekledik. Dizinin üst düzeyi şimdi ‘Panda’ ve ‘Karakter’ olarak
ayrıldı. Bu iki grup, açılır kutu içinde ‘optgroup’ element tipleri olarak temsil edileceklerdir.
Üretilen kaynak kodu şöyledir.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<label for ="bear">Ayılar?</label>
<select id ="bear" name ="bear">
<optgroup label ="Panda">
<option value ="kirmizi">Kırmızı</option>
<option value ="siyah" selected ="selected">Siyah</option>
<option value ="beyaz">Beyaz</option>
</optgroup>
<optgroup label ="Karakter">
<option value ="pooh">Pooh</option>
<option value ="baloo">Baloo</option>
</optgroup>
</select>
</form>
Formlar
164
Email Alanı
Email alanı Form::text() metodu ile aynı şekilde yazılır. Aralarındaki fark Form::email() metodunun, istemci tarafından girilen değerin geçerli bir email adresi olmasını temin etmek için geçerlilik
kontrolünden geçirileceği yeni bir HTML5 input elementi oluşturmasıdır.
Başka bir örnek.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::label('email', 'E-Mail Adresi') }}
{{ Form::email('email', '[email protected]') }}
{{ Form::close() }}
Form::email() metodunun birinci parametresi input elementimizin name niteliğidir. İkinci parametre ön tanımlı valuedir. Her zaman olduğu gibi ve umarım unutmamışsınızdır, ek element nitelikleri
için, dizi biçiminde son bir opsiyonel parametre söz konusudur.
Yukarıdaki örnek gösterildiğinde sayfa kaynağı şu şekildedir.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
10
11
12
13
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<label for ="email">E-Mail Adresi</label>
<input name ="email"
type ="email"
value ="[email protected]"
id ="email">
</form>
Dosya Gönderme (File Upload) Alanı
Önceki bölümde, gönderilen dosyaların nasıl işleneceğini öğrenmiştik. Şimdi de bir dosya gönderme
alanının nasıl üretileceğini görelim.
Formlar
1
165
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
{{ Form::open(array(
'url'
=> 'benim/rotam',
'files' => true
)) }}
{{ Form::label('avatar', 'Avatar') }}
{{ Form::file('avatar') }}
{{ Form::close() }}
Form::file() metodunun ilk parametresi file upload elementinin name niteliğidir, ancak, gönderme
işinin çalışabilmesi bakımından formumuzun çok parçalı kodlama tipini içermesi için files seçe-
neğiyle açılmasını temin etmemiz de gerekmektedir.
İşte üretilen sayfa kaynağı.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
10
11
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8"
enctype ="multipart/form-data">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<label for ="avatar">Avatar</label>
<input name ="avatar" type ="file" id ="avatar">
</form>
Gizli (Hidden) Alanlar
Bazen, alanlarımız girdi elde etmek amacını taşımaz. Formlarımızla birlikte ekstra veri sağlamak
için hidden alanları kullanabiliriz. Hidden alanların nasıl üretildiğine bir göz atalım.
1
<!-- app/views/form.blade.php -->
2
3
4
5
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::hidden('panda', 'luishi') }}
{{ Form::close() }}
Form::hidden() metodu için birinci parammetre name niteliğidir, Arkasından ne geleceğini görmediğinize bahse girerim! İkinci parametre tabii ki valuedir.
Üretilen sayfa kaynağı böyle gözükür.
Formlar
1
166
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<input name ="panda" type ="hidden" value ="luishi">
</form>
Form Düğmeleri
Onları gönderemediğimiz takdirde formlar işimize yaramayacaktır. Kullanabileceğimiz düğmelere
biraz yakından bakalım.
Submit Düğmesi
Önce gönder düğmesi. Klasik dışı bir şey yok! Şöyle görünüyor.
1
<!-- app/views/form.blade.php -->
2
3
4
5
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::submit('Kaydet') }}
{{ Form::close() }}
Form::submit() birinci parametre valuedir, value bir düğme söz konusu olduğunda düğmeyi
tanımlamak için kullanılan etikettir. İnput üretme metodlarının hepsinde olduğu gibi, button üretme
metodları da son bir opsiyonel parametre olarak ek nitelikler dizisi kabul edebilecektir.
Yukarıdaki örnekten üretilen HTML kaynak kodu şudur.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<input type ="submit" value ="Kaydet">
</form>
Harika! Artık formlarımızı gönderebiliyoruz. Alternatif düğme şekillerine de bir göz atalım.
Formlar
167
Normal Düğmeler
Formumuzu göndermek için kullanmak istemediğimiz bir düğmeye ihtiyacımız olduğunda, Form::button()
metodunu kullanabiliriz. Bir örnek verirsek:
1
<!-- app/views/form.blade.php -->
2
3
4
5
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::button('Gülümse') }}
{{ Form::close() }}
Form::button() metodunun parametreleri daha önce bahsettiğimiz Form::submit() metodunun
tam aynısıdır. Örneğimizden üretilen kaynak şöyledir.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<button type ="button">Gülümse</button>
</form>
Resim (Image) Düğmeleri
Formlarınızı göndermek için klasik düğme yerine, HTML5 image button tipini kullanabilirsiniz. İşte
bir örnek.
1
<!-- app/views/form.blade.php -->
2
3
4
5
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::image(asset('benim/image.gif', 'submit')) }}
{{ Form::close() }}
Form::image() metodu için birinci parametre düğme için kullanacağımız bir imajın URL’sidir. Ben
URL üretmek için asset() helper metodunu kullandım. İkinci parametre buttonun değeridir.
Üretilen kaynak kodu buradadır.
Formlar
1
168
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<input src ="https://demo.dev/benim/image.gif" type ="image">
</form>
Reset Düğmesi
Reset düğmesi form içeriğini temizlemek için kullanılabilmektedir. Uygulamanız kullanıcıları tarafından, bir yanlışlık yaptıkları zaman kullanılırlar. Nasıl üretileceğine bir örnek üzerinden bakalım.
1
<!-- app/views/form.blade.php -->
2
3
4
5
{{ Form::open(array('url' => 'benim/rotam')) }}
{{ Form::reset('Temizle') }}
{{ Form::close() }}
Form::reset() metodu için birinci paramete düğme üzerinde gözükmesini istediğiniz etikettir.
Yukarıdaki örnekten üretilen kaynak şöyledir.
1
<!-- app/views/form.blade.php -->
2
3
4
5
6
7
8
9
<form method ="POST"
action ="http://demo.dev/benim/rotam"
accept-charset ="UTF-8">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
<input type ="reset" value ="Temizle">
</form>
Form Makroları
Önceki kesimlerde çeşitli form girişi üretim metodlarını keşfetmiştik. Bunlar büyük bir zaman
kazandırabilmektedir ancak belki de kendi uygulamanıza özgü özel bir form input tipiniz olabilir.
Şansımıza, Taylor bunu zaten düşünmüş. Laravel kendi form jeneratörlerinizi tanımlamanıza imkan
veren bir metodla birlikte gelmektedir, isterseniz nasıl çalıştığına bir bakalım.
Peki, form makrolarımızı nereye koyacağız? Ah anlıyorum, önce app/macros.php adında bir dosya
oluşturalım, sonra da onu rotalar dosyamızda include edebiliriz. Bu dosya içeriği şöyle olsun:
Formlar
1
169
<?php
2
3
// app/macros.php
4
5
6
7
8
Form::macro('adSoyad', function()
{
return '<p>Ad Soyad: <input type ="text" name ="ad_soyad"></p>';
});
Bir makro tanımlamak için Form::macro() metodunu kullanıyoruz. Birinci parametresi bizim form
alanımızı oluşturacak metod tarafından kullanılacak isimdir. Bu sebeplerle bu ismin yazım biçimini
camelCase yapmanızı öneririm.
İkinci parametre bir Closure’dur. Bu closure’dan dönen değer alanımızı oluşturmak için gerekli
kaynak kodu olmak zorundadır.
Bu yeni makromuzdan gelen sonucu gösteren basit bir rota oluşturalım. İşte burada:
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Form::adSoyad();
});
Bu rotamız Form sınıfındaki adSoyad() metodunun değerini döndürüyor. Bu sıradan bir statik metod
değildir, Laravel’in kendi form makrolarımızı çağırabilmemizi sağlayan bir sihre sahiptir. Metodun
adı makromuza vermiş olduğumuz takma adla eşleşmektedir. Şimdi oluşturduğumuz rotadan ne
döndüğüne bir göz atalım.
1
<p>Ad Soyad: <input type ="text" name ="ad_soyad"></p>
Görüldüğü gibi, Closure’umuzun sonucu döndürülmüştür.
Form makrolarımıza paremetre vermek istersek ne yapacağız? Elbette, problem değil! Önce makromuzu değiştirelim.
Formlar
1
170
<?php
2
3
// app/macros.php
4
5
6
7
8
Form::macro('adSoyad', function($isim)
{
return '<p>Ad Soyad: <input type ="text" name ="'.$isim.'"></p>';
});
Makro closure’u içinde yer tutucular vermek suretiyle, bu yer tutucularının değerlerini göstertilecek
kaynakta kullanabiliyoruz. Yukarıdaki örnekte yeni input tipimiz için bir name niteliği sağlayan bir
metod eklemiştik.
Şimdi de bu yeni parametrenin nasıl verileceğini görelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Form::adSoyad('yeni_alan');
});
Evet, kolay oldu değil mi? Biz sadece Laravel’in bize sağladığı sihirli metoda parametremizin
değerini geçiyoruz. İsterseniz / rotamızın yeni sonucuna bir bakalım.
1
<p>Ad Soyad: <input type ="text" name ="yeni_alan"></p>
Müthiş, Yeni name değeri alanımıza yerleştirilmiş durumda! Tekrarları önlemenin bir başka harika
yolu. Tadını çıkarın!
Form Güvenliği
Formlarımızdan veri alabilmek güzel şey. Orada olmalarının gerekçesi bu zaten. Sorun şu ki, bizim
rotalarımız kendi formlarımızın bir hedefi olabiliyorsa, sitemize tehlikeli veri gönderen dış bir
kaynağı nasıl durduruyor?
Bize gönderilen verinin kendi websitemize ait olduğunu garanti altına almanın bir yoluna ihtiyacımız var. Problem değil. Sizin için bu işi Laravel yapar. ‘Siteler Arası İstek Sahtekarlığı’ (Cross Site
Request Forgery) veya kısaca CSRF denen bu form saldırılarını büyük ihtimalle biliyorsunuzdur.
İşte şimdi, bunu şimdilik merak etmeyin dediğim _token hidden alanını hatırladınız mı? Tamam,
artık onun hakkında konuşmaya başlamak istiyorum.
Formlar
171
ARGH. Meraktan ölecektik!
Peki, tamam. İzin verin bu token’in ne olduğunu anlatayım. Bu, Laravel’in ne için olduğunu
bildiği bir çeşit gizli paroladır. Application yapılandırma dosyamızdaki ‘secret’ değeri kullanılarak
üretilmektedir. Eğer Laravel’e input verilerimiz içinde bu değeri kontrol etmesini söylersek, o zaman
bir form tarafından sağlanan verilerin kendi uygulamamıza ait olduğundan emin olabiliriz.
Bunun değerini Session::token() metodunun sonucuyla karşılaştırmak suretiyle token karşılaştırma işini kendimiz yapabilirdik fakat Laravel daha iyi bir seçenek sunmuştur. Filtreler bölümündeki
ön tanımlı filtreleri hatırlıyor musunuz? Evet, Laravel ön tanımlı bir csrf filtresine sahiptir. Gelin
bunu bir rotaya bağlayalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::post('/form-isle', array('before' => 'csrf', function()
{
// Gönderilen form verisini işle.
}));
Form verilerimizi işleyecek rotaya bir before filtresi olarak csrf filtresi bağlamak suretiyle, bu _token alanının mevcut ve doğru olmasını garantiye alabiliyoruz. Eğer bizim token yoksa veya onu
kurcalamışlarsa, bu durumda bir Illuminate\Session\TokenMismatchException atılacak ve rota
mantığımız çalıştırılmayacaktır.
İlerideki bir bölümde, bu şartları uygun biçimde işleyebilmek için hata işleyicileri belirli istisna
tiplerine nasıl bağlayacağımızı öğreneceğiz.
Eğer formumuzun etrafını sarmak için Form::open() metodunu kullanmışsanız, güvenlik tokeni
default olarak dahil edilecektir. Ancak, eğer bu tokeni elle yazılan formlara eklemek istiyorsanız, o
zaman basitçe token üretme metodu eklemek zorundasınız. İşte bir örnek.
1
<!-- app/views/form.blade.php -->
2
3
4
5
<form action ="{{ url('form-isle') }}" method ="POST">
{{ Form::token() }}
</form>
Bu Form::token() metodu formumuzun içine gizli token alanı ekleyecektir. Üretilecek olan kaynak
şöyledir.
Formlar
1
172
<!-- app/views/form.blade.php -->
2
3
4
5
6
<form action ="http://demo.dev/form-isle" method ="POST">
<input name ="_token" type ="hidden" value ="83KCsmJF1Z2LMZfhb17ihvt9ks5NEcAwFoR\
FTq6u">
</form>
Özetlemek gerekirse, dış uygulama ve kullanıcılarının formlarınıza veri gönderebilmesini istiyor
olmadığınız sürece, rotalarınızın işleyeceği form verilerinizi korumak için ‘CSRF’ filtresini kullanmalısınız.
Geçerlilik Denetimi (Validation)
Birkaç ay önce gerçekten bir strese girmiştim. İş Laravel 4 salımının montajlanmasıydı ki, her
framework geliştiricisinin üstleneceği tipik rol yanında yeni sitenin tasarlanması ve inşa edilmesi,
belge değişikliklerinin cevaplanması anlamına geliyordu. Zor zamanlardı. Bütün bunların üstüne bir
de, Code Bright’ı en az Code Happy kadar başarılı olacak şekilde inşa edebilme, böylece topluluğa
yeni geliştiriciler katma baskısı altındaydım.
Yapmam gereken şey Phill Parklarının birinde kısa bir mola almaktı. Bildiğiniz gibi, Phill Parkları
dünyanın değişik yerlerinde geliştiricilerin tatil yaptığı parklardır. Bunlar orijinal olarak iki bin yılında Phill Parklarının CEO’su Phill Sparks tarafından açılmıştı. Bu parklar günümüzde geliştiricilerin
dinlenmek ve rahatlamak için gidebileceği tek yerdir.
Phill Parkları zor bir çalışma yılının sonunda gevşemek isteyen geliştiriciler için çok çeşitli aktiviteler sunmaktadır. Bu etkinlikler arasında rekabetçi FizzBuzz turnuvaları, sekmeler ve boşluklar
münazaraları ve ense sakalı yarışmaları yer almaktadır.
Phill Parklarını biliyor olsaydım, Laravel dört çıkana kadar dinlenip, eğlenebilirdim. Oh tamam, en
azından ne olduğunu öğrenmiş oldunuz? Şanslı bir Code Bright okuyucusu olarak, parklara giriş
için % 90 indirim sağlayacak bir kupona erişebilirsiniz. Bu anlaşmadan yararlanmak için yapmanız
gereken tek şey Phill Spark’ı IRC, Twitter, E-Mail ile veya bizzat izlemek ve ‘CODE_BRIGHT_TROLLS_YOU’ kupon kodunu aktarmaktır. Eğer hemen cevap vermezse, onu kupon kodu ile spama
tutun. Er veya geç, tatil kodunuzu yakalayacaktır.
Peki, bunun geçerlilik denetimiyle ne ilgisi var?
Evet, bu bölümü yazmaya başladığımda, kafamda harika, büyük bir plan vardı. Ne yazık ki, tamamen
Phill Parkları’nın sunduğu eğlence ve oyunlara daldım ve artık bir fikrim yok. Sadece bir şey yaptım
işte. Bu güne kadar ne doğru çıktı ki?
Phill Parkları geliştiriciler için çok özel yerlerdir ve sadece geliştiriciler içindir. Geliştiricilerin
problemi, dikkat etmedikleri takdirde, web geliştirme endüstrisindeki diğer rolleri tespit etmelerinin
zor olmasıdır. Örneğin, tasarımcılar.
Sinsi tasarımcıların geliştirici kılığında Phill Parklarına sızmasını istemeyiz. Onlar dekorasyon
seçimlerimizi yargılamaya ve kafeterya menülerinde Comic Sans kullanmamızdan yakınmaya
başlayacaklardır. Atmosfer tamamen berbat olacaktır! Hayır, parkta bunları hiç istemeyiz. Ziyaretçilerimizin geliştirici olduklarından emin olmamızı sağlayan bir geçerlilik kontrolü yöntemine
ihtiyacımız var. Bakın, geri getirebileceğimi söylemiştim.
Geçerliliğine bakacağımız şey bir nitelikler kümesidir. Bir park ziyaretçisinin bazı nitelikleri hakkında düşünelim. Oo, hoo, biliyorum. Önce bir liste yapabiliriz! Listeler eğlencelidir!
Geçerlilik Denetimi (Validation)
•
•
•
•
174
Favori içeceği.
Web browser tercihi.
Yüz kılı tipi.
Kadınlarla konuşma yeteneği.
Mükemmel, parka gelen ziyaretçileri tarif edebileceğimiz bazı niteliklerimiz var. Bazı geçerlilik
sınırlamaları veya kurallarını kullanarak, ziyaretçimizin bir geliştirici olduğundan emin olabiliriz.
Bu park için, bir park ziyaretçisinin niteliklerinin aşağıdaki gereksinimleri karşıladığını garanti altına
almaya çalışacağız.
•
•
•
•
Favori içeceği: Ayran.
Web browser tercihi: Google Chrome.
Yüz kılı tipi: Ense sakalı.
Kadınlarla konuşma yeteneği: Yok.
Uyarı: Bu değerler sadece eğlence içindir. Örneğin, yaptığı şeyler fantastik olan birçok kadın
web geliştirici biliyorum ve bunların diğer kadınlarla konuşabilme yeteneğine sahip olduklarından
oldukça eminim. Ayrıca, şu anda benim geleneksel ense sakalı değil de muhteşem van dayk sakalım
var. Ayrana batırılmış bir şey olsa da…
Her neyse, bir park ziyaretçisinin niteliklerinin bir geliştiricininkine uyduğundan emin olduktan
sonra geçip parka girmelerine izin verebiliriz. Ancak, eğer resepsiyona aşağıdaki niteliklere sahip
sinsi bir adam gelirse…
•
•
•
•
Favori içeceği: Starbuck Frappechino.
Web browser tercihi: Mobile Safari.
Yüz kılı tipi: Yok.
Kadınlarla konuşma yeteneği: Kadınlarla konuşur, kesinlikle.
…bu durumda, hayır olamaz, bu bir tasarımcı veya bir Ruby geliştiricisidir ve onu kapıdan içeri
sokamayız.
Bir kez daha ifade edeyim, bu sadece bir parça eğlence olsun diye. Lütfen bana nefret mektupları
göndermeyin. İlerideki bir bölümde ana karakteri öldürene kadar bekleyin… Erm, yani, unutun
gitsin.
Geçerlilik için yaptığımız uygulamayla, parka girecek insanların sahtekarlar değil geliştiriciler
olduğundan emin olabiliyoruz.
Phill Parkları artık güvenlidir, ya uygulamalarınız? Beklediğimiz neyse tam onu aldığımızdan emin
olmak için Laravel’de geçerlilik denetimini nasıl yapabileceğimizi görebiliriz.
Geçerlilik Denetimi (Validation)
175
Basit Geçerlilik Denetimi
Kullanıcılarınıza güvenmeyin. IT sektöründe çalışırken öğrendiğim bir şey varsa, o da eğer uygulamanızda zayıf bir nokta varsa, kullanıcılarınızın onu bulacağıdır. Bunlar onu istismar edecek
diyorsam bana güvenin. Onlara fırsat vermeyin. İyi girdi aldığınızdan emin olmak için geçerlilik
denetimi kullanın.
İyi girdi demekle neyi kastediyoruz? Peki, bir örnek üzerinden bakalım.
Uygulamanız için kayıt bilgileri toplamakta kullanılan bir HTML formunuz olsun. Aslında, küçük
bir gözden geçirme oturumu olması çok iyi olacak. Formu Blade şablon motoru ve form oluşturucu
ile oluşturalım.
İşte görünümümüz burada.
1
<!-- app/views/form.blade.php -->
2
3
<h1>Phill Parkları için kayıt formu</h1>
4
5
{{ Form::open(array('url' => 'tescil')) }}
6
7
8
9
{{-- Kullanıcı Adı alanı. ------------------------}}
{{ Form::label('kullanici_adi', 'Kullanıcı Adı') }}
{{ Form::text('kullanici_adi') }}
10
11
12
13
{{-- Email adresi alanı. -------------------}}
{{ Form::label('email', 'Email adresi') }}
{{ Form::email('email') }}
14
15
16
17
{{-- Password alanı. ------------------------}}
{{ Form::label('password', 'Şifre') }}
{{ Form::password('password') }}
18
19
20
21
{{-- Password teyit alanı. -----------}}
{{ Form::label('password_confirmation', 'Şifre tekrarı') }}
{{ Form::password('password_confirmation') }}
22
23
24
{{-- Form gönderme düğmesi. --------------------}}
{{ Form::submit('Kayıt Ol') }}
25
26
{{ Form::close() }}
Voah, ne güzel bir görünüm?
Geçerlilik Denetimi (Validation)
176
Bu güzel bir görünüm.
Mükemmel, zihinsel olarak hala Code Happy okuma şartlarında olmanıza sevindim.
Görebileceğiniz gibi kayıt formumuz /tescil rotasını hedef alıyor ve ön tanımlı olarak POST istek
fiilini kullanacak.
Bir kullanıcı adı için bir text alanımız, kullanıcının email adresi için bir email alanımız ve bir şifre ve
bir şifre teyidi almak için iki password alanımız var. Formun en altında da formu neşeli bir şekilde
göndermek için kullanabileceğimiz bir submit buttonumuz var.
Dikkat ederseniz, blade kaynağına eklediğim yorumlar var. Bu benim forma geri dönmek zorunda
kalırsam, istediğim alanlara kolayca göz atabilmemi sağladığını düşündüğüm bir alışkanlıktır.
Aynısını yapmakta serbestsiniz, ama eğer istemiyorsanız, bu konuyu dert etmeyin! Kendi bildiğiniz
gibi kodlayın!
Pekiyi, şimdi bu formu göstermek ve işlemek için bir çift rotaya ihtiyacımız olacak. Şimdi ilerleyelim
ve bunları routes.php dosyamıza ekleyelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
Route::post('/tescil', function()
{
$veri = Input::all();
13
// formu işle...
14
15
});
Formumuzu göstermek için kullanacağımız bir GET / rotası, onun gönderdiklerini işlemek için de
bir POST /tescil rotamız oldu.
Form işleyen rotamızda formdan tüm verileri topladık ancak henüz onları kullanamayız. Niçin?
Pekiyi, tamam, göstereyim.
Devam edin ve / rotasını yükleyin, formu göreceksiniz. Pekala, formu doldurmayın, sadece ‘Kayıt
Ol’ düğmesine basın. İkinci rota tetiklendiği için ve formumuz boş bilgi gönderdiği için ekran boş
gözükecek. Eğer biz boş bilgi kullanırsak uygulamamız için ciddi problemlere, hatta bir güvenlik
açığına bile yol açabilir. Bu kötü bir veridir.
Geçerlilik Denetimi (Validation)
177
Bunun yerine verilerimizin iyi veri olmasını garanti altına almak için geçerlilik denetimini kullanarak bu sıkıntıyı önleyebiliriz. Geçerlilik denetimi yapabilmek için öncelikle bir geçerlilik
sınırlamaları listesi vermemiz gerekir. Bunu bir dizi biçiminde yapabiliriz. Hazır mısınız? Güzel,
görelim o zaman.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
Route::post('/tescil', function()
{
$veri = Input::all();
13
$kurallar = array(
'kullanici_adi' => 'alpha_num'
);
14
15
16
17
});
Tamam, küçük bir şeyle başladık. Bir geçerlilik kural kümesi ilişkisel bir dizi biçimini alıyor. Bu
dizinin anahtarı geçerlilik denetiminden geçirilecek alanı temsil eder. Dizinin değeri ise geçerlilik
denetimi için kullanılacak bir veya birçok kuraldan ibaret olacaktır. Biz tek bir alanda tek bir
geçerlilik sınırlamasına bakarak başlayacağız.
1
2
3
array(
'kullanici_adi' => 'alpha_num'
)
Yukarıdaki örnekte, ‘kullanici_adi’ alanının alpha_num geçerlilik kuralına uyduğunu doğrulamak
istiyoruz. Bu alpha_num kuralı bir değerin sadece alfanümerik karakterlerden oluştuğunu garantiye
almak için kullanılabilmektedir.
Kuralımızın doğru çalıştığından emin olmak için geçerlilik nesnesini ayarlayalım. Laravel içinde
geçerlilik denetimi yapmak için, öncelikle bir Validation nesnesi olgusu oluşturmamız gerekiyor.
İşte başlıyoruz.
Geçerlilik Denetimi (Validation)
1
178
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('/tescil', function()
{
// Tüm istek verilerini alalım.
$veri = Input::all();
14
// Geçerlilik sınırlama kümesini oluşturalım.
$kurallar = array(
'kullanici_adi' => 'alpha_num'
);
15
16
17
18
19
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar);
20
21
22
});
Yeni bir geçerlilik denetimcisi olgusu oluşturmak için Validator::make() metodunu kullanabiliyoruz. make() metodunun birinci parametresi geçerlilik denetiminden geçirilecek bir veri dizisidir. Bu
örneğimizde istek verimizi denetimden geçirmek istiyoruz ancak başka bir veri dizisini de geçerlilik
denetiminden geçirebilirdik. Metodun ikinci parametresi ise veriyi denetlemek için kullanılacak olan
kurallar kümesidir.
Artık geçerlilik denetçisi olgumuzu oluşturduğumuza göre, girilen verilerin koyduğumuz geçerlilik
sınırlamalarına uyup uymadığını kontrol etmek için bu olguyu kullanabiliriz. İsterseniz bir örnekle
gidelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
Route::post('/tescil', function()
Geçerlilik Denetimi (Validation)
11
179
{
// Tüm istek verilerini alalım.
$veri = Input::all();
12
13
14
// Geçerlilik sınırlama kümesini oluşturalım.
$kurallar = array(
'kullanici_adi' => 'alpha_num'
);
15
16
17
18
19
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar);
20
21
22
if ($gecerlikci->passes()) {
// Normalde veriyle bir şeyler yapacağız.
return 'Veri kaydedildi.';
}
23
24
25
26
27
return Redirect::to('/');
28
29
});
Geçerlilik denetiminin sonucunu test etmek için, validatör olgumuzda passes() metodunu kullanabiliyoruz. Bu metod geçerlilik denetiminden başarıyla geçilip geçilmediğini gösteren boolean bir
cevap döndürecektir. Bir true cevabı verinin geçerlilik kurallarının tamamına uyduğunu gösterir.
Bir false cevabı ise verinin geçerlilik gereksinimlerini karşılamadığını gösterir.
Yukarıdaki örnekte, veriyi saklayacak mıyız, yoksa giriş formuna redirekt mi yapacağımıza karar
vermek için bir if cümlesi kullanıyoruz. Şimdi / URI’sini ziyaret ederek bunu test edelim.
İlk olarak kullanici_adi alanına ‘serginari’ değerini girip submit düğmesine basalım. ‘serginari’ kullanıcı adının alfanümerik karakterlerden oluştuğunu biliyoruz, bu nedenle geçerlilik denetiminden
başarıyla geçecektir. Bu itibarla aşağıdaki cümle ile karşılaşacağız.
1
Veri kaydedildi.
Mükemmel, tam beklediğimiz gibi. Şimdi gelin bu denetimin sonucunu tersine çevirelim. Devam
edelim ve / URI’yi bir kez daha ziyaret edelim. Bu sefer ‘!!!’ değerini girelim. Bir ünlem işaretinin ne
bir alfabe harfi ne de bir rakam karakteri olmadığını biliyoruz, bu durumda denetimden kalacaktır.
Submit düğmesine basalım, tekrar kayıt formuna redirekt olacağız.
Ben bu passes() metodunu sevdim, gerçekten yararlı. Tek sorun biraz iyimser olması. Kendimize
karşı dürüst olalım, dünya mükemmel bir yer değil. Biz hiçbirimiz Robert Downey Jr değiliz. Biraz
daha kötümser olamaz mıyız? Mükemmel, bekleyin… Tatmin edici. O zaman bunun yerine fails()
metodunu deneyelim.
Geçerlilik Denetimi (Validation)
1
180
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('/tescil', function()
{
// Tüm istek verilerini alalım.
$veri = Input::all();
14
// Geçerlilik sınırlama kümesini oluşturalım.
$kurallar = array(
'kullanici_adi' => 'alpha_num'
);
15
16
17
18
19
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar);
20
21
22
if ($gecerlikci->fails()) {
return Redirect::to('/');
}
23
24
25
26
// Normalde veriyle bir şeyler yapacağız.
return 'Veri kaydedildi.';
27
28
29
});
Bu fails() metodu passes() metodunun tam aksi boolean döndürür. Nasıl olağanüstü kötümser
ama! O anki ruh halinize hangisi uygunsa onu kullanabilirsiniz. İsterseniz, PHP yorumlarını
kullanarak hislerinizi de yazabilirsiniz.
Bazı geçerlilik kuralları parametre alabilmektedir. Şimdi alpha_num kuralımızı min ile değiştirelim.
Kaynak şöyledir.
Geçerlilik Denetimi (Validation)
1
181
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('/tescil', function()
{
// Tüm istek verilerini alalım.
$veri = Input::all();
14
// Geçerlilik sınırlama kümesini oluşturalım.
$kurallar = array(
'kullanici_adi' => 'min:3'
);
15
16
17
18
19
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar);
20
21
22
if ($gecerlikci->passes()) {
// Normalde veriyle bir şeyler yapacağız.
return 'Veri kaydedildi.';
}
23
24
25
26
27
return Redirect::to('/');
28
29
});
min geçerlilik kuralı ilgili değerin verilen parametreye eşit veya ondan daha büyük olmasını
garantiye alır. Parametresi iki nokta üst üsteden (:) sonra verilir. Bu validation kuralı biraz özeldir.
Girilen veriye dayalı olarak farklı tepki verecektir.
Örneğin, bir string değerde, parametremizin ‘3’ olması değerin en azından 3 karakter uzunluğunda
olmasını temin edecektir. Sayısal bir değerde ise değerin matematiksel olarak 3’e eşit veya daha
büyük olmasını garanti edecektir. Son olarak, gönderilen bir dosya için min geçerlilik kuralı
gönderilen dosyanın kilobayt olarak boyutunun verilen parametreye eşit veya daha büyük olmasını
sağlama alacaktır.
Yeni geçerlilik kuralımızı test etmeye geçelim. Önce / sayfasını ziyaret edelim ve kullanici_adi
alanına ‘Jo’ değerini girelim. Formu gönderdiğinizde, kayıt formuna geri yönlendirileceksiniz.
Bunun sebebi değerimizin yeterince uzun olmamasıdır.
Geçerlilik Denetimi (Validation)
182
Yani, o diyor ki…
Oo hayır değil. Bu ciddi bir kitap, onu ‘o dedi ki’ şakalarıyla kirletemeyiz.
Tamam, şimdiye dek iki geçerlilik sınırlaması kullandık ancak ikisini birlikte kullanmak istersek
ne yapacağız? Problem değil. Laravel alanlarımız üzerinde herhangi bir sayıda geçerlilik kuralı
kullanmamıza imkan vermektedir. Bunu nasıl yapabileceğimize bir göz atalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('/tescil', function()
{
// Tüm istek verilerini alalım.
$veri = Input::all();
14
// Geçerlilik sınırlama kümesini oluşturalım.
$kurallar = array(
'kullanici_adi' => 'alpha_num|min:3'
);
15
16
17
18
19
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar);
20
21
22
if ($gecerlikci->passes()) {
// Normalde veriyle bir şeyler yapacağız.
return 'Veri kaydedildi.';
}
23
24
25
26
27
return Redirect::to('/');
28
29
});
Görmüş olduğunuz gibi, kurallar dizimizin değer kısımları içinde pipe | karakterleri ile ayrılmış
birden çok geçerlilik sınırlaması geçebiliyoruz. Bu bölüme çok daha erken gelecektik ancak ben işte
Linux, evde Mac kullanıyorum ve pipe tuşunu bulmak 30 saniyemi aldı.
Geçerlilik Denetimi (Validation)
183
Her neyse, birden çok geçerlilik kuralı vermenin alternatif bir yolu var. Altmışlarında bir büyükbaba
değilseniz, pipoları anlamayabilirsiniz. İsterseniz, ek geçerlilik kurallarını belirtmek için çok boyutlu
bir dizi kullanabilirsiniz.
İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('/tescil', function()
{
// Tüm istek verilerini alalım.
$veri = Input::all();
14
// Geçerlilik sınırlama kümesini oluşturalım.
$kurallar = array(
'kullanici_adi' => array('alpha_num', 'min:3')
);
15
16
17
18
19
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar);
20
21
22
if ($gecerlikci->passes()) {
// Normalde veriyle bir şeyler yapacağız.
return 'Veri kaydedildi.';
}
23
24
25
26
27
return Redirect::to('/');
28
29
});
Laravel esnek bir framework’tür ve kullanıcılarına seçenekler verir. Birden çok geçerlilik kuralı
atamak için siz kendi tercih ettiğiniz yöntemi kullanın. Ben bir ingiliz beyefendisini taklit etmek
için pipo kullanacağım.
Geçerlilik Denetimi (Validation)
184
Geçerlilik Kuralları
Ey insanlar, dinleyin. Bir sürü doğrulama kuralı bulunmaktadır. Bu yüzden, bunları tek seferde
göreceksek, tam dikkatli olmanız gerekecek. Eğer bunları okurken yatağınızda iseniz, kitabı kapatın
ve uyuyun. Daha uyanık durumda olmanız gerekecek.
Kullanabileceğimiz geçerlilik kuralları alfabetik sırada verilmiştir.
accepted
Bu kural olumlu bir teyit verildiğini sağlama bağlamak için kullanılabilir. Geçerlilik denetiminden
geçirilen değer şu değerlerden biri ise geçecektir: ‘yes’, ‘on’ veya sayısal 1. Bu kuralın amacı
kullanıcının bir şeyi kabul ettiğini garantiye alma istediğinizi karşılamaktır, örneğin bir kullanım
şartları onay kutusu.
1
2
3
array(
'alan' => 'accepted'
);
active_url
‘active_url’ kuralı değerin geçerli bir URL olmasını kontrol edecektir. Bunu yapmak için PHP’nin
kendi checkdnsrr() metodunu kullanır, bu metod sadece URL yapısını kontrol etmez, aynı zamanda
DNS kayıtlarınız içinde bu URL’nin mevcut olduğunu da kesinleştirir.
1
2
3
array(
'alan' => 'active_url'
);
after
‘after’ geçerlilik kuralı tekli bir parametre kabul eder: bir zamanı temsil eden bir string. Bu
kural, ilgili alanın verilen parametredekinden sonraki bir tarihi taşımasını temin edecektir. Laravel
karşılaştırma için gerek değeri gerek kural parametresini bir zaman damgasına çevirmek için
PHP’nin strtotime() metodunu kullanacaktır.
Geçerlilik Denetimi (Validation)
1
2
3
185
array(
'alan' => 'after:12/12/13'
);
alpha
‘alpha’ geçerlilik kuralı sağlanan değerin tamamıyla alfabe karakterlerinden oluşmasını garantiye
alır.
1
2
3
array(
'alan' => 'alpha'
);
alpha_dash
‘alpha_dash’ kuralı sağlanan değerin alfabe karakterleri yanında, tire - ve/veya alt tire _ karakterlerinden oluşmasını temin eder. Bu geçerlilik kuralı kimi URL parçalarını geçerlilik denetiminden
geçirmekte çok yararlıdır.
1
2
3
array(
'alan' => 'alpha_dash'
);
alpha_num
‘alpha_num’ kuralı sağlanan değerin alfabe ve sayı karakterlerinden oluşmasını garantiye alır.
Kullanıcı adı alanlarını geçerlilik denetiminden geçirmek için bu kuralı kullanmayı seviyorum.
1
2
3
array(
'alan' => 'alpha_num'
);
before
‘before’ kuralı tekli bir parametre alır. Sorgulanacak değer parametredekinden önce olmalıdır,
karşılaştırma için her ikisi de PHP’nin strtotime() metoduyla zaman damgasına dönüştürülürler.
Bu kural ‘after’ kuralının tam tersidir.
Geçerlilik Denetimi (Validation)
1
2
3
186
array(
'alan' => 'before:12/12/13'
);
between
‘between’ kuralı iki parametre alır. Geçerlilikten geçirilecek değer bu iki parametre arasında bulunan
bir büyüklükte olmalıdır. Karşılaştırma şekli karşılaştırılacak verinin tipine bağlıdır. Örneğin sayısal
alanlarda karşılaştırma matematiksel bir karşılaştırma olacaktır. Bir string için, karşılaştırma stringin karakter sayısına dayalı olarak yapılacaktır. Bir dosya için ise, karşılaştırma dosyanın kilobayt
cinsinden boyutuna dayalı olacaktır.
1
2
3
array(
'alan' => 'between:5,7'
);
confirmed
‘confirmed’ geçerlilik kuralı ilgili alanın adına _confirmation eklenmiş bir ada sahip başka bir
alanın mevcut olmasını garantiye alır. Geçerlilikten geçirilecek değer, bu diğer alanın değeriyle
aynı olmalıdır. Bu kuralın kullanım yerlerinden birisi password alan teyitleridir ve kullacının iki
alandan birinde bir yazım hatası yapmamasını temin etme amacını güder. Aşağıdaki örnek ‘alan’
ile ‘alan_confirmation’ alanlarının değerlerinin aynı olmasını temin eder.
1
2
3
array(
'alan' => 'confirm'
);
date
‘date’ geçerlilik kuralı değerimizin geçerli bir tarih olduğunun sağlamasını yapar. Değeri doğrulamak
için PHP’nin kendi strtotime() metodunu çalıştıracaktır.
Geçerlilik Denetimi (Validation)
1
2
3
187
array(
'alan' => 'date'
);
date_format
‘date_format’ geçerlilik kuralı değerimizin bir parametre olarak sağlanan formata uygun bir tarih
stringi olmasını temin eder. Bir tarih format stringinin nasıl yapıldığını öğrenmek için PHP
belgelerinde date() metoduna bakınız.
1
2
3
array(
'alan' => 'date_format:d/m/y'
);
different
‘different’ geçerlilik kuralı ilgili değerin kural parametresinde belirtilen alandaki değerden farklı
olduğunun sağlandığını grantiye alır.
1
2
3
array(
'alan' => 'different:bir_diger_alan'
);
email
‘email’ geçerlilik kuralı geçerlilik denetiminden geçirilecek değerin geçerli bir email adresi olmasını
sağlama alacaktır. Kayıt formu oluşturulurken bu kural çok işe yarar.
1
2
3
array(
'alan' => 'email'
);
exists
‘exists’ geçerlilik kuralı alan değerinin kural parametresi tarafından tanımlanan bir veritabanı
tablosu içerisinde olmasını garanti edecektir. Araştırılacak olan sütun, geçerlilikten geçirilecek alanla
aynı isimde olacaktır. Alternatif olarak, bir sütun ismi belirtmek için opsiyonel bir ikinci parametre
verebilirsiniz.
Bu kural, kayıt formlarında bir kullanici_adi’nın başka bir kullanıcı tarafından daha önceden alınmış
olup olmadığını yoklamak için çok yararlı olacaktır.
Geçerlilik Denetimi (Validation)
1
2
3
188
array(
'alan' => 'exists:users,kullanici_adi'
);
Kurala geçirilen ek parametre çiftleri sorguya ek where cümlecikleri olarak eklenecektir. Bunun gibi:
1
2
3
array(
'alan' => 'exists:users,kullanici_adi,role,admin'
);
Yukarıdaki örnek, girilen değerin users tablosunun kullanici_adi sütununda mevcut olup olmadığını
kontrol edecektir. Aynı zamanda, role sütunu da ‘admin’ değerinde olmak zorundadır.
image
‘image’ geçerlilik kuralı upload edilen dosyanın geçerli bir imaj dosyası olmasını sağlama alır.
Örneğin, dosyanın uzantısı şunlardan birisi olmalıdır: .bmp, .gif, .jpeg veya .png.
1
2
3
array(
'alan' => 'image'
);
in
‘in’ geçerlilik kuralı alanın değerinin parametrede sağlanan değerlerden biriyle aynı olmasını temin
eder.
1
2
3
array(
'alan' => 'in:siyah,kahverengi,beyaz'
);
integer
En kolaylarından birisi! ‘integer’ geçerlilik kuralı alanın değerinin bir tam sayı olmasını sağlama
bağlar. Hepsi bu!
Geçerlilik Denetimi (Validation)
1
2
3
189
array(
'alan' => 'integer'
);
ip
‘ip’ geçerlilik kuralı alanın değerinin iyi biçimlendirilmiş bir IP adresi taşıdığından emin olmak için
kontrol yapacaktır.
1
2
3
array(
'alan' => 'ip'
);
max
‘max’ geçerlilik kuralı ‘min’ kuralının tam tersidir. Alanın büyüklüğünün verilen parametreye eşit
veya parametreden daha az olmasını garantiye alır. Eğer alan bir string ise parametre stringin
karakter uzunluğu demek olacaktır. Sayısal değerler için karşılaştırma matematikle yapılacaktır.
Dosya gönderme alanları için karşılaştırma dosyanın kilobayt cinsinden boyutuna dayalı olarak
yapılacaktır.
1
2
3
array(
'alan' => 'max:3'
);
mimes
‘mimes’ geçerlilik kuralı sağlanan stringin bir Fransız pandomimcisi ismi olmasını temin eder. Şaka,
şaka. Bu kural bir upload dosyasının tipinin parametrede verilenlerden birine uymasını garantiye
almak için kontrol yapacaktır.
1
2
3
array(
'alan' => 'mimes:pdf,doc,docx'
);
Geçerlilik Denetimi (Validation)
190
min
‘min’ geçerlilik kuralı ‘max’ kuralının tam karşıtıdır. Bir alan değerinin verilen parametreye eşit
veya ondan daha büyük olmasını garantilemek için kullanılabilir. Eğer alan bir string ise parametre
stringin karakter uzunluğu demek olacaktır. Sayısal değerler için karşılaştırma matematikle yapılacaktır. Dosya gönderme alanları için karşılaştırma dosyanın kilobayt cinsinden boyutuna dayalı
olarak yapılacaktır.
1
2
3
array(
'alan' => 'min:5'
);
not_in
Adının düşündürdüğü gibi, bu geçerlilik kuralı ‘in’ kuralının tam tersidir. Alan değerinin sağlanan
parametre listesi içinde mevcut olmamasını temin edecektir.
1
2
3
array(
'alan' => 'not_in:mavi,mor,pembe'
);
numeric
‘numeric’ kuralı geçerlilikten geçirilecek alanın sayısal bir değer taşıdığından emin olmamızı
sağlayan kontrolü yapacaktır.
1
2
3
array(
'alan' => 'numeric'
);
regex
‘regex’ geçerlilik kuralı Laravel’in Validation bileşeninde bulunan en esnek kuraldır. Bu kuralla,
geçerlilikten geçirilecek alanın uyması gereken özel bir düzenli ifadeyi bir parametre olarak
verebilirsiniz. Kendi başına bir kitap olmaya değer çok büyük bir konu olması nedeniyle bu kitapta
düzenli ifadeler ayrıntılı olarak anlatılmayacaktır.
Pipe | karakterlerinin düzenli ifadeler içerisinde kullanılabilmesi nedeniyle, ‘regex’ kuralını kullandığınız zaman birden çok geçerlilik kuralını birleştirmek için pipe kullanmak yerine içi içe dizi
kullanmayı unutmayınız.
Geçerlilik Denetimi (Validation)
1
2
3
191
array(
'alan' => 'regex:[a-z]'
);
required
‘required’ geçerlilik kuralı ilgili alanın geçerlilik veri dizisinde mevcut olmasını garantilemek için
kullanılabilir.
1
2
3
array(
'alan' => 'required'
);
required_if
‘required_if’ geçerlilik kuralı ilgili alanın sadece kuralın ilk parametresinde belirtilen bir alanın değerinin, kuralın ikinci parametresiyle sağlanan değere eşit olması halinde mevcut olma zorunluğunu
garanti eder.
1
2
3
array(
'alan' => 'required_if:kullanici_adi,serginari'
);
required_with
‘required_with’ ilgili alanın sadece kural parametreleri ile tanımlanan bir veya daha fazla alanın da
mevcut olması durumunda mevcut olmasını sağlama almak için kullanılır.
1
2
3
array(
'alan' => 'required_with:yas,boy'
);
required_without
‘required_without’ kuralı ‘required_with’ kuralının tam karşıtıdır. Alanımızın sadece kural parametreleri ile tanımlanan alanlar mevcut olmadığı zaman mevcut olmasını temin etmek için
kullanılabilir.
Geçerlilik Denetimi (Validation)
1
2
3
192
array(
'alan' => 'required_without:yas,boy'
);
same
‘same’ geçerlilik kuralı ‘different’ kuralının tam tersidir. İlgili alanın değerinin kural parametresi ile
tanımlanan başka bir alanınki ile aynı olmasını garantilemek için kullanılır.
1
2
3
array(
'alan' => 'same:yas'
);
size
‘size’ kuralı alanın değerinin kural parametresi olarak sunulan belli bir büyüklükte olmasını
sağlamak için kullanılabilir. Eğer alan bir string ise parametre stringin karakter uzunluğu demek
olacaktır. Sayısal değerler için karşılaştırma matematikle yapılacaktır. Dosya gönderme alanları için
karşılaştırma dosyanın kilobayt cinsinden boyutuna dayalı olarak yapılacaktır.
1
2
3
array(
'alan' => 'size:8'
);
unique
‘unique’ kuralı mevcut alanın değerinin kural parametresi ile tanımlanan veritabanı tablosu içerisinde zaten bulunuyor olmamasını garantiye alır. Ön tanımlı olarak, kuralımız değerine bakılacak
tablo sütunu olarak alanın adını kullanacaktır, ancak ikinci bir kural parametresi içinde alternatif
bir sütun verebilirsiniz. Kayıt formları işleneceği zaman bir kullanıcının verdiği kullanıcı adının
benzersiz olup olmadığını kontrol etmek için yararlı bir kuraldır.
1
2
3
array(
'alan' => 'unique:users,kullanici_adi'
);
Benzersizlik kuralı tarafından göz ardı edilecek bir takım satır ID’lerini listelemek için fazladan
opsiyonel parametreler kullanabilirsiniz.
Geçerlilik Denetimi (Validation)
1
2
3
193
array(
'alan' => 'unique:users,kullanici_adi,4,3,2,1'
);
url
‘url’ geçerlilik kuralı alanın geçerli bir URL taşıdığından emin olmak için kullanılabilir. ‘active_url’ geçerlilik kuralından farklı olarak, ‘url’ kuralı DNS kayıtlarını kontrol etmez, sadece stringin
formatını kontrol eder.
1
2
3
array(
'alan' => 'url'
);
Peki, onların hepsi bu kadar. O kadar da kötü değilmiş, değil mi? Gerçi henüz tam bitirmiş değiliz.
Şimdi de hata mesajlarına bir bakalım.
Hata Mesajları
İlk bölümde geçerlilik denetiminin nasıl yapılacağını ve geçememe durumunda bir forma nasıl tekrar
redirekt yapılacağını öğrendik. Buna karşın, bu yöntem kullanıcıya çok yapısal bir geri bildirim yolu
sunmamaktadır.
Neyse ki, Laravel geçerlilik denetiminin neden başarısız olduğunu açıklayan çok sayıda hata mesajı
toplayabilmektedir. Bu bilgilere nasıl erişebileceğimize bir bakalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
Route::post('/tescil', function()
{
Geçerlilik Denetimi (Validation)
194
// Tüm istek verilerini alalım.
$veri = Input::all();
12
13
14
// Geçerlilik sınırlama kümesini oluşturalım.
$kurallar = array(
'kullanici_adi' => 'alpha_num'
);
15
16
17
18
19
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar);
20
21
22
if ($gecerlikci->passes()) {
// Normalde veriyle bir şeyler yapacağız.
return 'Veri kaydedildi.';
}
23
24
25
26
27
// Geçerlilik hata mesajları nesnesini toparla.
$errors = $gecerlikci->messages();
28
29
30
return Redirect::to('/');
31
32
});
Yukarıdaki örnekte, validatör olgumuzun messages() metodunu kullanarak geçerlilik hata mesajları
nesnesine erişebileceğimizi göreceksiniz. Şimdi, form rotamıza redirekt yaptığımıza göre, bu hata
mesajlarına formumuz içinden nasıl erişebileceğiz?
Peki, bu amaçla withErrors() metodunu kullanabileceğimizi düşünüyorum.
Sana inanmıyorum, yalan söylemeye devam ediyorsun!
Oo öyle mi? Kontrol et o zaman.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
Route::post('/tescil', function()
Geçerlilik Denetimi (Validation)
11
195
{
// Tüm istek verilerini alalım.
$veri = Input::all();
12
13
14
// Geçerlilik sınırlama kümesini oluşturalım.
$kurallar = array(
'kullanici_adi' => 'alpha_num'
);
15
16
17
18
19
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar);
20
21
22
if ($gecerlikci->passes()) {
// Normalde veriyle bir şeyler yapacağız.
return 'Veri kaydedildi.';
}
23
24
25
26
27
return Redirect::to('/')->withErrors($gecerlikci);
28
29
});
Bu withErrors() zincir metoduna validatör olgusunu geçtiğimizi fark edeceksiniz. Bu metod,
hataları formdan oturuma flash edecektir. Devam etmeden önce, örneğimize bazı geçerlilik kuralları
ekleyelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('/tescil', function()
{
// Tüm istek verilerini alalım.
$veri = Input::all();
14
15
16
17
18
// Geçerlilik sınırlama kümesini oluşturalım.
$kurallar = array(
'kullanici_adi'
=> 'required|alpha_num|min:3|max:32',
'email'
=> 'required|email',
Geçerlilik Denetimi (Validation)
'password'
19
=> 'required|confirm|min:3'
);
20
21
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar);
22
23
24
if ($gecerlikci->passes()) {
// Normalde veriyle bir şeyler yapacağız.
return 'Veri kaydedildi.';
}
25
26
27
28
29
return Redirect::to('/')->withErrors($gecerlikci);
30
31
});
Şimdi, form görünümünden hata mesajlarına nasıl erişebildiğimizi görebiliriz.
1
<!-- app/views/form.blade.php -->
2
3
<h1>Phill Parkları için kayıt formu</h1>
4
5
{{ Form::open(array('url' => 'tescil')) }}
6
7
8
9
10
11
<ul class="errors">
@foreach($errors->all() as $mesaj)
<li>{{ $mesaj }}</li>
@endforeach
</ul>
12
13
14
15
{{-- Kullanıcı Adı alanı. ------------------------}}
{{ Form::label('kullanici_adi', 'Kullanıcı Adı') }}
{{ Form::text('kullanici_adi') }}
16
17
18
19
{{-- Email adresi alanı. -------------------}}
{{ Form::label('email', 'Email adresi') }}
{{ Form::email('email') }}
20
21
22
23
{{-- Password alanı. ------------------------}}
{{ Form::label('password', 'Şifre') }}
{{ Form::password('password') }}
24
25
26
{{-- Password teyit alanı. -----------}}
{{ Form::label('password_confirmation', 'Şifre tekrarı') }}
196
Geçerlilik Denetimi (Validation)
27
197
{{ Form::password('password_confirmation') }}
28
29
30
{{-- Form gönderme düğmesi. --------------------}}
{{ Form::submit('Kayıt Ol') }}
31
32
{{ Form::close() }}
Görünümümüz yüklendiği zaman $errors değişkeni görünüm verisine eklenir. O her zaman
oradadır ve her zaman için bir hata mesajı konteyneri olgusudur, dolayısıyla, onun varlığını veya
içeriğini kontrol etme konusunda endişelenmenize gerek yoktur. Eğer önceki bir istekteki hata
mesajlarımızı oturuma flash etmek için withErrors() kullanmışsak, bu durumda Laravel onları
otomatik olarak errors nesnesine ekleyecektir. Ne kolaylık!
$errors hata mesajları olgusu üzerinde all() metodunu kullanmak suretiyle tüm hata mesajlarına
bir dizi şeklinde erişebiliyoruz. Yukarıdaki görünümde, bütün mesaj dizisini baştan sona dolaşarak,
onların her birini bir liste elementi içinde çıktılıyoruz.
Haklısınız, bu kadar gevezelik yeter. Bir deneyelim artık onu. Gidip / URL’sini ziyaret edelim.
Herhangi bir bilgi girmeden formu gönderelim ve ne olacağını görelim.
Forma tekrar yönlendirildik. Ancak bu sefer bir takım hata mesajları görüntülendi. (Çevirenin notu:
Burada hata mesajları Laravel yüklemesinin ön tanımlı dili ingilizce olarak gösterilmiştir. Türkçe dil
dosyalarını yükleyip, ön tanımlı dili ayarlayarak hata mesajlarını Türkçe gösterebilirsiniz.)
• The kullanici_adi field is required.
• The email field is required.
• The password field is required.
Harika! artık uygulamamızın kullanıcıları kayıtla ilgili geçerlilik hatalarının farkında oluyorlar.
Ancak, eğer hata mesajları açıkladıkları alanlara daha yakın olurlarsa kullanıcılarımız için daha
uygun olacaktır. Görünümde küçük bir değişiklik yapalım öyleyse.
1
<!-- app/views/form.blade.php -->
2
3
<h1>Phill Parkları için kayıt formu</h1>
4
5
{{ Form::open(array('url' => 'tescil')) }}
6
7
8
9
10
11
{{-- Kullanıcı Adı alanı. ------------------------}}
<ul class="errors">
@foreach($errors->get('kullanici_adi') as $mesaj)
<li>{{ $mesaj }}</li>
@endforeach
Geçerlilik Denetimi (Validation)
12
13
14
198
</ul>
{{ Form::label('kullanici_adi', 'Kullanıcı Adı') }}
{{ Form::text('kullanici_adi') }}
15
16
17
18
19
20
21
22
23
{{-- Email adresi alanı. -------------------}}
<ul class="errors">
@foreach($errors->get('email') as $mesaj)
<li>{{ $mesaj }}</li>
@endforeach
</ul>
{{ Form::label('email', 'Email adresi') }}
{{ Form::email('email') }}
24
25
26
27
28
29
30
31
32
{{-- Password alanı. ------------------------}}
<ul class="errors">
@foreach($errors->get('password') as $mesaj)
<li>{{ $mesaj }}</li>
@endforeach
</ul>
{{ Form::label('password', 'Şifre') }}
{{ Form::password('password') }}
33
34
35
36
{{-- Password teyit alanı. -----------}}
{{ Form::label('password_confirmation', 'Şifre tekrarı') }}
{{ Form::password('password_confirmation') }}
37
38
39
{{-- Form gönderme düğmesi. --------------------}}
{{ Form::submit('Kayıt Ol') }}
40
41
{{ Form::close() }}
Tek bir alan için bir hatalar dizisini elde etmek için geçerlilik hataları nesnesinde get() metodunu
kullanabiliyoruz. Bunun için sadece alanın adını get() metoduna ilk parametre olarak geçiniz.
Şimdi formu tekrar submit edelim. Bu sefer kullanici_adi alanına tek bir ünlem ! işareti koyun.
Şimdi kullanici_adi alanının üstünde ortaya çıkan hatalara bir göz atalım.
• The kullanici_adi may only contain letters and numbers.
• The kullanici_adi must be at least 3 characters.
Bu daha iyi. Evet… Bir parça daha iyi. Pek çok form uygulamanın kullanıcılarını bunaltmamak için
alan başına sadece tek bir geçerlilik hatası gösterir.
Bunu Laravel geçerlilik hataları nesnesi ile nasıl yapabileceğimizi görelim.
Geçerlilik Denetimi (Validation)
1
199
<!-- app/views/form.blade.php -->
2
3
<h1>Phill Parkları için kayıt formu</h1>
4
5
{{ Form::open(array('url' => 'tescil')) }}
6
7
8
9
10
{{-- Kullanıcı Adı alanı. ------------------------}}
<span class ="error">{{ $errors->first('kullanici_adi') }}</span>
{{ Form::label('kullanici_adi', 'Kullanıcı Adı') }}
{{ Form::text('kullanici_adi') }}
11
12
13
14
15
{{-- Email adresi alanı. -------------------}}
<span class ="error">{{ $errors->first('email') }}</span>
{{ Form::label('email', 'Email adresi') }}
{{ Form::email('email') }}
16
17
18
19
20
{{-- Password alanı. ------------------------}}
<span class ="error">{{ $errors->first('password') }}</span>
{{ Form::label('password', 'Şifre') }}
{{ Form::password('password') }}
21
22
23
24
{{-- Password teyit alanı. -----------}}
{{ Form::label('password_confirmation', 'Şifre tekrarı') }}
{{ Form::password('password_confirmation') }}
25
26
27
{{-- Form gönderme düğmesi. --------------------}}
{{ Form::submit('Kayıt Ol') }}
28
29
{{ Form::close() }}
Geçerlilik hataları nesnesinde first() metodunu kullanmak ve parametre olarak alan ismini
geçmek suretiyle, o alana ait ilk hata mesajını elde edebiliyoruz.
Bir kez daha kullanici_adi alanına sadece bir ünlem ! işareti koyarak formu submit edelim. Bu sefer
ilk alanımız için sadece tek bir hata mesajı alacağız.
• The kullanici_adi may only contain letters and numbers.
Ön tanımlı durumda geçerlilik mesajları olgusunun metodları, hata mesajları olmadığı takdirde boş
bir dizi veya null döndürür. Bunun anlamı şudur: mesajların mevcut olup olmadığını kontrol etme
zorunda kalmaksızın onu kullanabilirsiniz. Ancak bazı sebeplerle bir alan için bir hata mesajı mevcut
olup olmadığını yoklamak isterseniz has() metodunu kullanabilirsiniz.
Geçerlilik Denetimi (Validation)
1
2
3
200
@if($errors->has('email'))
<p>Hey, bir hata var!</p>
@endif
all() ve first() metodlarıyla ilgili önceki örneklerimizde hata mesajlarımızı HTML elementleri
ile sarmış olduğumuzu fark etmişsinizdir. Ancak, eğer bizim metodlarımızdan biri null döndürürse,
HTML hala gösterilecektir.
all() ve first() metodlarına ikinci parametre olarak kapsayıcı HTML’yi string formatında geçmek
suretiyle görünüm kaynak kodumuzda boş HTML elementlerinin gözükmesini engelleyebiliriz.
Örneğin, ikinci bir parametre olarak sarıcı liste elementleri alan all() metodu şöyledir.
1
<!-- app/views/form.blade.php -->
2
3
<h1>Phill Parkları için kayıt formu</h1>
4
5
{{ Form::open(array('url' => 'tescil')) }}
6
7
8
9
10
11
<ul class="errors">
@foreach($errors->all('<li>:message</li>') as $mesaj)
{{ $mesaj }}
@endforeach
</ul>
12
13
14
15
{{-- Kullanıcı Adı alanı. ------------------------}}
{{ Form::label('kullanici_adi', 'Kullanıcı Adı') }}
{{ Form::text('kullanici_adi') }}
16
17
18
19
{{-- Email adresi alanı. -------------------}}
{{ Form::label('email', 'Email adresi') }}
{{ Form::email('email') }}
20
21
22
23
{{-- Password alanı. ------------------------}}
{{ Form::label('password', 'Şifre') }}
{{ Form::password('password') }}
24
25
26
27
{{-- Password teyit alanı. -----------}}
{{ Form::label('password_confirmation', 'Şifre tekrarı') }}
{{ Form::password('password_confirmation') }}
28
29
30
{{-- Form gönderme düğmesi. --------------------}}
{{ Form::submit('Kayıt Ol') }}
31
32
{{ Form::close() }}
Geçerlilik Denetimi (Validation)
201
all() metodunun ikinci parametresinin :message kısmı, dizi oluşturulduğunda gerçek hata mesajı
ile değiştirilecektir.
first() metodu da aynı opsiyonel parametreye sahiptir.
1
<!-- app/views/form.blade.php -->
2
3
<h1>Phill Parkları için kayıt formu</h1>
4
5
{{ Form::open(array('url' => 'tescil')) }}
6
7
8
9
10
{{-- Kullanıcı Adı alanı. ------------------------}}
{{ $errors->first('kullanici_adi', '<span class ="error">:message</span>') }}
{{ Form::label('kullanici_adi', 'Kullanıcı Adı') }}
{{ Form::text('kullanici_adi') }}
11
12
13
14
15
{{-- Email adresi alanı. -------------------}}
{{ $errors->first('email', '<span class ="error">:message</span>') }}
{{ Form::label('email', 'Email adresi') }}
{{ Form::email('email') }}
16
17
18
19
20
{{-- Password alanı. ------------------------}}
{{ $errors->first('password', '<span class ="error">:message</span>') }}
{{ Form::label('password', 'Şifre') }}
{{ Form::password('password') }}
21
22
23
24
{{-- Password teyit alanı. -----------}}
{{ Form::label('password_confirmation', 'Şifre tekrarı') }}
{{ Form::password('password_confirmation') }}
25
26
27
{{-- Form gönderme düğmesi. --------------------}}
{{ Form::submit('Kayıt Ol') }}
28
29
{{ Form::close() }}
Özel Geçerlilik Kuralları
Ah, anlıyorum, Laravel’in size verdikleriyle mutlu olamadınız? Siz kendi yaptığınız geçerlilik
metodlarınız olsun istiyorsunuz? Çok güzel, büyük silahları almanın zamanı geldi. Laravel size kendi
kurallarınızı belirlemenize izin verecek kadar esnektir.
Bunun nasıl yapılabileceğine bir göz atalım.
Geçerlilik Denetimi (Validation)
1
202
<?php
2
3
// app/routes.php
4
5
6
7
8
Validator::extend('harika', function($alan, $deger, $parametreler)
{
return $deger == 'harika';
});
9
10
11
12
13
Route::get('/', function()
{
return View::make('form');
});
14
15
16
17
18
Route::post('/tescil', function()
{
// Tüm istek verilerini alalım.
$veri = Input::all();
19
// Geçerlilik sınırlama kümesini oluşturalım.
$kurallar = array(
'kullanici_adi'
=> 'harika',
);
20
21
22
23
24
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar);
25
26
27
if ($gecerlikci->passes()) {
// Normalde veriyle bir şeyler yapacağız.
return 'Veri kaydedildi.';
}
28
29
30
31
32
return Redirect::to('/')->withErrors($gecerlikci);
33
34
});
Özel geçerlilik kuralları için ön tanımlı bir konum tanımlanmış değildir, bu nedenle ben örneği basitleştirmek amacıyla kuralı routes.php dosyasının içine koydum. Siz isterseniz bir validators.php
dosyasını include edip, özel geçerlilik kurallarını orada verebilirsiniz.
Kendi ‘harika’ geçerlilik kuralımızı kullanici_adi alanımıza tutturduk. Bu geçerlilik kuralının nasıl
oluşturulduğuna daha yakından bakalım.
Geçerlilik Denetimi (Validation)
1
203
<?php
2
3
// app/routes.php
4
5
6
7
8
Validator::extend('harika', function($alan, $deger, $parametreler)
{
return $deger == 'harika';
});
Özel bir geçerlilik kuralı oluşturmak için Validator::extend() metodunu kullanıyoruz. Metoda
geçilen ilk parametre geçerlilik kuralına verilecek olan takma addır. Bu isim, kuralı alana bağlamak
için kullanılacaktır. Metodun ikinci parametresi bir closure’dir. Closure true sonuçlu bir boolean
değer döndürürse, geçerlilik denemesi denetimi geçmiş olacaktır. Closure’dan eğer boolean false
döndürülürse, geçerlilik girişimi denetimden kalmış olacaktır.
Closure’a koyulan parametreler şu şekildedir. Birinci parametre geçerlilikten geçirilecek alanın adını
taşıyan bir stringtir. Yukarıdaki örnekte ilk parametre ‘kullanici_adi’ stringini taşıyacaktır.
Extent closure’unun ikinci parametresi alanın değerini taşır.
Üçüncü parametre ise geçerlilik kuralına geçilen bir parametreler dizisidir. Gerektiğinde geçerlilik
kurallarını özelleştirmek için bunları kullanın.
Eğer özel geçerlilik kurallarınızı bir closure yerine bir sınıf içerisinde tanımlamayı tercih ederseniz,
bunu yapmanız mümkün olmayacaktır. Bir şeyler isteyip durmayın.
Bekleyin azıcık, sadece şaka yapıyorum. Laravel bunu yapabilir. Bunu gerçekleştirecek bir sınıf
oluşturalım.
1
<?php
2
3
// app/validators/OzelGecerlilik.php
4
5
6
7
8
9
10
11
class OzelGecerlilik
{
public function harika($alan, $deger, $parametreler)
{
return $deger == 'harika';
}
}
Görebileceğiniz gibi, geçerlilik sınıfımız geçerlilik closure’unda olan aynı metod yazılımına sahip
değişik sayıda metodlar içermektedir. Bu, özel bir geçerlilik sınıfının istediğimiz kadar çok sayıda
geçerlilik kuralına sahip olabileceği anlamına gelmektedir.
Geçerlilik Denetimi (Validation)
204
Tekrar ifade edeyim, bu sınıflar için ideal bir yer yoktur, bu yüzden kendi proje yapınızı siz
tanımlamak durumundasınız. Ben kendi geçerlilik sınıfımı app/validators klasörüne koymayı ve
Composer ile bu klasörü classmap yapmayı tercih ediyorum.
Evet, her şey açıklığa kavuştu.
Bir saniye, geçerlilik sınıfı geçerlilik takma adı içermiyor.
Ah evet, neredeyse unutuyordum. Çok iyisiniz dikkatli okuyucu! Gördüğünüz gibi, geçerlilik
kuralının bulunabilmesi için yine Validator::extend() metodunu kullanmamız gerekiyor. En iyisi
bir göz atalım.
1
<?php
2
3
// app/routes.php
4
5
Validator::extend('harika', 'OzelGecerlilik@harika');
Bu sefer Validator::extend() metoduna ikinci parametre olarak bir string verildi. Tıpkı bir
kontrollere rota yaparken olduğu gibi bu string geçerlilik kural sınıfının sınıf adıyla, kuralı temsil
eden eylemin bir @ sembolüyle birleştirilmesinden oluşuyor.
İlerideki bir bölümde, özel geçerlilik kuralları sağlamanın daha gelişmiş, alternatif bir yöntemi olarak
Validation sınıfının nasıl genişletileceğini öğreneceğiz.
Özel Geçerlilik Mesajları
Laravel yerleşik geçerlilik kurallarının tümü için ön tanımlı geçerlilik mesajları sağlamıştır, fakat siz
ön tanımlı olan birilerini istemezseniz veya uygulamanızı ana dil olarak İngilizce kullanmayan bir
bölge için yazmak isterseniz ne olacak?
Tamam panik yapmayın! Laravel yerleşik geçerlilik mesajlarını geçersiz kılıp değiştirmenize izin
verecektir. Bizim sadece ek bir dizi inşa etmemiz ve onu validator olgusunun make() metoduna
geçmemiz gerekiyor.
Hey, Validator::make() metodunu zaten görmüştük!
Bu doğru, ama bir kez daha yalan söyledim.
Neden bana işkence ediyorsun?
Gerçekten emin değilim. Bu noktada sanırım onu bir hobi olarak düşünüyorum. Her neyse, örnek
bir özel geçerlilik mesajları dizisi görmeye ne dersiniz?
Geçerlilik Denetimi (Validation)
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('form');
});
9
10
11
12
13
Route::post('/tescil', function()
{
// Tüm istek verilerini alalım.
$veri = Input::all();
14
// Geçerlilik sınırlama kümesini oluşturalım.
$kurallar = array(
'kullanici_adi'
=> 'min:3',
);
15
16
17
18
19
// Özel mesajlar dizisini inşa edelim.
$mesajlar = array(
'min' => 'Hey dostum, bu alan yeterince uzun değil.'
);
20
21
22
23
24
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar, $mesajlar);
25
26
27
if ($gecerlikci->passes()) {
// Normalde veriyle bir şeyler yapacağız.
return 'Veri kaydedildi.';
}
28
29
30
31
32
return Redirect::to('/')->withErrors($gecerlikci);
33
34
});
Ne büyük bir örnek, odak düğmesini tıklayalım.
Klavyemde odak düğmesini bulamıyorum.
Pekala, eğer bir Mac kullanıyorsan, sekme tuşunun bir üstündedir. Bunun ± gibi gözükür.
Emin misin?
205
Geçerlilik Denetimi (Validation)
206
Peki, bu düğmenin ne anlama geldiği konusunda başka fikrin var mı?
Ah, anlıyorum.
Tamam, hadi örneğe odaklanalım.
1
<?php
2
3
4
5
6
// Özel mesajlar dizisini inşa edelim.
$mesajlar = array(
'min' => 'Hey dostum, bu alan yeterince uzun değil.'
);
7
8
9
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar, $mesajlar);
Mesajlar dizisi Validator::make() metodu için opsiyonel bir üçüncü parametredir. Bu dizi vermek
istediğiniz ve aynı zamanda ön tanımlı geçerlilik mesajlarını geçersiz kılacak olan özel geçerlilik
mesajlarını içerir. Geçerlilik mesaj dizisinin anahtar kısmı geçerlilik kuralının adını temsil eder ve
değer kısmı geçerlilik kuralı başarısız olduğunda gösterilecek mesajı temsil eder.
Yukarıdaki örnekte min geçerlilik kuralı için geçerlilik denetiminden kalma mesajını değiştirmiş
oluyoruz.
Bu metodu bizim özel geçerlilik kurallarımız için geçerlilik hataları sağlamak amacıyla da kullanabiliriz. Bizim özel kurallarımızda ön tanımlı geçerlilik hata mesajları olmaz, bu yüzden böyle yapmak
normalde iyi bir fikirdir.
1
<?php
2
3
4
5
6
// Özel mesajlar dizisini inşa edelim.
$mesajlar = array(
'harika' => 'Lütfen yeterince harika bir değer girin.'
);
7
8
9
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar, $mesajlar);
Eğer belirli bir alan için özel bir hata mesajı vermek istersek, bunu dizi içindeki anahtar olarak alanın
adını, bir nokta . karakterini ve geçerlilik tipini vererek yapabiliyoruz.
Geçerlilik Denetimi (Validation)
1
207
<?php
2
3
4
5
6
// Özel mesajlar dizisini inşa edelim.
$mesajlar = array(
'kullanici_adi.min' => 'Hmm, bu biraz küçük görünüyor.'
);
7
8
9
// Yeni bir validator olgusu oluşturalım.
$gecerlikci = Validator::make($veri, $kurallar, $mesajlar);
Yukarıdaki örnekteki mesaj sadece ‘kullanici_adi’ alanı için min geçerlilik kuralından kalındığı
zaman gösterilecektir. Diğer tüm min kalmalarında ön tanımlı hata mesajı kullanılacaktır.
Geçerlilik denetimi üzerine anlatacaklarım şimdilik bu kadar. Daha sonraki bir bölümde Laravel’in
validator sınıfının nasıl genişletileceğini ve tercüme edilebilir geçerlilik hata mesajlarının nasıl
sağlanacağını öğreneceğiz, fakat şimdilik, veritabanları hakkında bilgi sahibi olmak için sonraki
bölüme geçelim.
Veritabanları
Şimdi bir itirafta bulunmak zorundayım. Ben büyük bir veritabanı hayranı değilim. Çocukken
birisi tarafından ısırıldım. Isırıldım ve siz bunu gördünüz, çifte utanç. Tamam, tamam, yine şaka
yapıyorum. Asıl sebebi bir veritabanının kardeşimi öldürmüş olması.
Bir kez daha şaka yaptım, bir kardeşim yok benim. Onlardan neden hoşlanmadığımı aslında bilmiyorum, sanıyorum ben görsel şeyleri, zarif şeyleri, eğlenceli şeyleri seviyorum. Veritabanları ise sadece
büyük veri ağlarıdır, şık değildirler ve eğlence karşıtıdırlar. Neyse ki, bu günlerde bizim veritabanı
satırlarına nesne olguları olarak erişmemize imkan veren güzel ORM’ler tarafından şımartıldık.
İlerideki bir bölümde Laravel’in kendine ait Eloquent adındaki ORM’si hakkında daha çok duracağız.
Eloquent güzeldir ve benim gibi acımasız bir veritabanı düşmanı için bile, veritabanlarıyla çalışmayı
keyifli bir deneyim haline getirmektedir.
Pekiyi, bir an için nefretimi bir tarafa bırakayım ve bir veritabanı kavramı üzerinde konuşayım. Ona
neden ihtiyaç duyuyoruz? Tamam, gerek olmayabilir mi?
Uygulamanızın, gelecekte yapılacak isteklerde kullanmak üzere veri saklaması gerekiyor mu?
Ben sadece statik sayfalar göstermek istiyorum.
Tamam, o zaman bir veritabanı gerekmez fakat çok sayıda istekler boyunca veri saklamanız ve onu
uygulamanızın diğer rotalarında göstermeniz veya kullanmanız gerektiğinde ne olur? İşte o zaman
bir veri depolama yöntemine ihtiyacınız var demektir ve bundan sonraki birkaç bölümü okumaktan
memnun olacaksınız.
Soyutlama
Peki, Laravel dört ile kullanabileceğimiz veritabanları nelerdir? Bakalım aşağıdakilerden biri size
hoş gelecek mi.
•
•
•
•
MySQL Community / Standard / Enterprise Server²⁵
SQLite²⁶
PostgreSQL²⁷
SQL Server²⁸
Görebileceğiniz gibi, bir veritabanı platformu seçerken, bol miktarda seçeneğiniz var. Bu kitap için,
ben MySQL Community Server Edition²⁹ kullanacağım. Bu mükemmel bir ücretsiz platformdur ve
²⁵http://www.mysql.com/products/
²⁶http://www.sqlite.org/
²⁷http://www.postgresql.org/
²⁸http://www.microsoft.com/en-us/sqlserver/default.aspx
²⁹http://www.mysql.com/products/community/
Veritabanları
209
geliştirme için kullanılanların en popülerlerinden biridir.
Başka bir veritabanı kullanıyor olmaktan endişelenmek zorunda değilsiniz. Gördüğünüz gibi, Laravel bir soyutlama katmanı sağlamaktadır, bu katman frameworkün veritabanı bileşenlerini HAM
SQL’den farklı veritabanı tipleri için farklı sorgular sağlamaktadır. Basitçe ifade etmek gerekirse,
SQL sözdizimi konusunu dert etmenize gerek yoktur, o işi Laravel’e bırakın.
Laravel’in veritabanı soyutlama katmanını kullanmanın başka bir avantajı güvenliktir. Çoğu durumda, ben başka türlü göstermediğim sürece, Laravel’den veritabanlarına gönderdiğiniz değerlerin
escape edilmesini dert etmek zorunda değilsiniz. Çeşitli biçimlerdeki enjeksiyon saldırılarını önleme
çabası olarak, Laravel bu değerleri sizin yerinize escape edecektir.
Bir an için Laravel’in veritabanı soyutlama katmanının esnekliğini tartabiliriz. Yani, istediğiniz
zaman veritabanı sunucunuzu değiştirebilirsiniz ve bunu yaparken önceden yazmış olduğunuz
veritabanı kodunuzda herhangi bir değişiklik yapmak zorunda kalmazsınız ve basit güvenlik
konuları hakkında endişelenmenize gerek olmaz. Projelerimizden büyük bir iş yükünü kaldırmış
oluyoruz gibi geliyor. Değerlerin escape edilmesi klişe işlerdendir, onu bizim yapmamız gerekmez.
Bırakın o işi Laravel yapsın.
Şimdi bir veritabanı kullanmak istediğimizi biliyoruz, bunlardan birini kullanmak için Laravel’i nasıl
ayarlayacağımızı görelim. Merak etmeyin, bu oldukça basit bir süreçtir! Öncelikle yapılandırma
seçeneklerine bir göz atalım.
Yapılandırma
Laravel’in veritabanı yapılandırmasının tümü app/config/database.php dosyasındadır. Hatırlaması kolay değil mi? Dosya boyunca bir gezi yapalım ve mevcut yapılandırma seçeneklerinden bir
kısmına bakalım.
1
2
3
4
5
6
7
8
9
10
/*
|-------------------------------------------------------------------------| PDO Fetch Style
|-------------------------------------------------------------------------|
| By default, database results will be returned as instances of the PHP
| stdClass object; however, you may desire to retrieve records in an
| array format for simplicity. Here you can tweak the fetch style.
|
*/
11
12
'fetch' => PDO::FETCH_CLASS,
Laravel’in veritabanı bileşenlerinden birini çalıştıran bir sorgudan döndürülen satırlar ön tanımlı
durumda PHP stdClass nesnesi biçiminde olacaktır. Bu, onun sütunlarındaki veriye şuna benzer
bir formatta erişebileceğiniz anlamına gelir.
210
Veritabanları
1
<?php
2
3
4
echo $kitap->isim;
echo $kitap->yazar;
Bununla birlikte, satırların döndürüleceği formatı değiştirmek isterseniz, veritabanı yapılandırmasının fetch seçeneğini daha elverişli bir şeye değiştirebilirsiniz. Bu seçeneğin değerini PDO::FETCH_ASSOC olarak değiştirirsek satırlarımızı tutmak için ilişkisel bir PHP dizisi kullanırız. Veritabanı
satırlarımıza şimdi aşağıdaki tarzda erişebiliriz.
1
<?php
2
3
4
echo $kitap['isim'];
echo $kitap['yazar'];
PDO fetch yollarının tam bir listesi için, PHP PDO sabitleri belgeleri sayfasına³⁰ gidip, FETCH_ ile
başlayan sabitlere bakınız.
Sonraki bakacağımız yer connections dizisi. Ön tanımlı durumunda bu dizi şuna benzer.
1
<?php
2
3
'connections' => array(
4
5
6
7
8
9
'sqlite' => array(
'driver'
=> 'sqlite',
'database' => __DIR__.'/../database/production.sqlite',
'prefix'
=> '',
),
10
11
12
13
14
15
16
17
18
19
20
'mysql' => array(
'driver'
=>
'host'
=>
'database' =>
'username' =>
'password' =>
'charset'
=>
'collation' =>
'prefix'
=>
),
'mysql',
'localhost',
'database',
'root',
'',
'utf8',
'utf8_unicode_ci',
'',
21
³⁰http://www.php.net/manual/en/pdo.constants.php
211
Veritabanları
'pgsql' => array(
'driver'
=>
'host'
=>
'database' =>
'username' =>
'password' =>
'charset' =>
'prefix'
=>
'schema'
=>
),
22
23
24
25
26
27
28
29
30
31
'pgsql',
'localhost',
'database',
'root',
'',
'utf8',
'',
'public',
32
'sqlsrv' => array(
'driver'
=> 'sqlsrv',
'host'
=> 'localhost',
'database' => 'database',
'username' => 'root',
'password' => '',
'prefix'
=> '',
),
33
34
35
36
37
38
39
40
41
42
),
Woah, büyük bir varsayılan bağlantı listesi! Bu, başlamayı çok kolay hale getiriyor. Şimdi, yukarıdaki diziye bakarak, her veritabanı tipi için farklı bir anahtar olduğunu düşünebilirsiniz. Ancak
eğer daha yakından bakacak olursanız, iç dizilerden her birinde, veritabanı tipini belirtmek için
kullanılabilecek bir driver olduğunu fark edeceksiniz. Bunun anlamı, farklı MySQL veritabanı
bağlantılarından oluşan bir diziniz olabilir demektir. Örneğin böyle:
1
<?php
2
3
'connections' => array(
4
5
6
7
8
9
10
11
12
13
14
'mysql' => array(
'driver'
=>
'host'
=>
'database' =>
'username' =>
'password' =>
'charset'
=>
'collation' =>
'prefix'
=>
),
'mysql',
'localhost',
'database',
'root',
'',
'utf8',
'utf8_unicode_ci',
'',
212
Veritabanları
15
'mysql_2' => array(
'driver'
=> 'mysql',
'host'
=> 'localhost',
'database' => 'database2',
'username' => 'root',
'password' => '',
'charset'
=> 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix'
=> '',
),
16
17
18
19
20
21
22
23
24
25
26
'mysql_3' => array(
'driver'
=> 'mysql',
'host'
=> 'localhost',
'database' => 'database3',
'username' => 'root',
'password' => '',
'charset'
=> 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix'
=> '',
),
27
28
29
30
31
32
33
34
35
36
37
38
),
Birtakım farklı veritabanı bağlantılarına sahip olmakla, bir veritabanından diğerine geçiş yapabiliriz.
Dolayısıyla uygulamamız tek bir veritabanına sahip olmak zorunda kalmayacaktır. Çok esnek, sizin
de aynı fikirde olduğunuzu sanıyorum.
Connections dizisinin birinci anahtarı bağlantıya verilen bir takma isimdir, kodumuz içinden belirli
bir veritabanında bir eylem yapma ihtiyacımız olduğunda bu ismi kullanabileceğiz. Veritabanlarınıza istediğiniz ismi verebilirsiniz! Şimdi köfte ve patateslere geçelim. Belirli bir bağlantı dizisindeki
anahtarlara yakından bakacağız. Bir örnek daha verelim.
1
2
3
4
5
6
7
8
9
10
'my_connection'
'driver'
'host'
'database'
'username'
'password'
'charset'
'collation'
'prefix'
),
=>
=>
=>
=>
=>
=>
=>
=>
=>
array(
'mysql',
'localhost',
'database',
'root',
'',
'utf8',
'utf8_unicode_ci',
'',
213
Veritabanları
driver seçeneği bağlanmak istediğimiz veritabanı tipini belirtmek için kullanılabilir.
1
'driver'
=> 'mysql',
Olabilecek değerler şunlardır.
•
•
•
•
mysql - MySQL
sqlite - SQLite
pgsql - PostgreSQL
sqlsrv - SQL Server
Sonraki anahtar host anahtarı veritabanı sunucusunu barındıran makinenin ağ konumunu belirtmek için kullanılabilir.
1
'host'
=> 'localhost',
Buraya bir IP adresi (168.122.122.5) veya bir host adı (veritabani.ornek.com) da verebilirsiniz. Yerel geliştirme ortamında mevcut makinenizi ifade etmek için esas olarak 127.0.0.1 veya localhost
kullanacaksınız.
SQLite Veritabanı
SQLite veritabanları disk üzerindeki bir konumda depolanır ve bu nedenle bir host girişi
yoktur. Bu sebeple, bir SQLite veritabanı bağlantı bloğundan bu anahtarı çıkartabilirsiniz.
Bağlantı dizisinin sonraki indeksi database seçeneğidir.
1
'database'
=> 'veritabanı_adı',
Bu bağlantıda üzerinde iş yapacağımız veritabanının adını tanımlamakta kullanılan bir string
değerdir. Bir SQLite veritabanı olması durumunda, veritabanını saklamak için kullanılan dosyayı
belirtmekte kullanılır, örneğin:
1
'database' => __DIR__.'/dosya/yolu/database.sqlite',
username ve password anahtarları veritabanı bağlantınız için erişim kimlik bilgileri sağlamak
amacıyla kullanılabilirler.
214
Veritabanları
1
2
'username'
'password'
=> 'dayle',
=> 'emma_w4tson_is_hot',
SQLite Veritabanı
Bir kez daha, SQLite veritabanları burada bir parça farklıdır. Bunlarda erişim kimlik
bilgileri olmaz. Bir SQLite bağlantı bloğundan bu anahtarları çıkartabilirsiniz.
Sonraki yapılandırma anahtarı charset olup, bir veritabanı bağlantısı için varsayılan karakter
kümesini belirtmekte kullanılabilir.
1
'charset'
=> 'utf8',
SQLite Veritabanı
Tahmin ettin onu sen! SQLite veritabanı bu seçeneği desteklemez. Bağlantı dizininden bu
anahtarı dışarda tutmanız yeterlidir.
Varsayılan veritabanı karşılaştırmasını collation anahtarını kullanarak ayarlayabilirsiniz.
1
'collation' => 'utf8_unicode_ci',
SQLite Veritabanı
Bir kez daha, SQLite benzersiz bir kar tanesi olmayı seçer. SQLite için karakter seti veya
collation anahtarı sağlamanız gerekmez.
Son olarak prefix seçeneğimiz var, veritabanı tablolarınıza ortak bir ön ek koymakta kullanılabilir.
1
'prefix'
=> '',
Hazırlama
Sonraki birkaç bölümdeki örnekleri çalışmak istiyorsanız, çalışan bir veritabanı bağlantısı kurmak
isteyebilirsiniz. Bunun için bir veritabanı platformunu indirin ve yükleyin.
Sonra bir bağlantı dizisi oluşturun ve gerekli tüm parametreleri doldurun. Neredeyse vardınız.
Sadece Laravel’e default olarak hangi veritabanı bağlantısını kullanacağınızı söylemeniz gerekiyor.
Veritabanı yapılandırma dosyası app/config/database.php dosyasına tekrar bakalım.
Veritabanları
1
2
3
4
5
6
7
8
9
10
215
/*
|-------------------------------------------------------------------------| Default Database Connection Name
|-------------------------------------------------------------------------|
| Here you may specify which of the database connections below you wish
| to use as your default connection for all database work. Of course
| you may use many connections at once using the Database library.
|
*/
11
12
'default' => 'mysql',
default dizi anahtarına, oluşturmuş olduğumuz yeni bağlantının tanımlayıcısını koyacağız. Bu sa-
yede, veritabanını kullanmak istediğimiz her seferinde bir bağlantı belirtmek zorunda kalmayacağız.
Peki, veritabanlarının seni heyecanlandırdığını biliyorum. Seni tuhaf, garip şahıs seni! Daha fazla
zamanımı alma. Sayfayı çevir de şema oluşturucusunu inceleyelim.
Şema Oluşturucusu
Evet, artık veritabanında bir şeyler saklamaya karar verdik. Veritabanı tam olarak basit anahtardeğer deposu değildir. Veritabanı içerisinde, verilerimizde farklı tiplerden oluşan yapılar olabilir ve
ilişkiler olabilir. Seksi, harika ilişkiler.
Yapılandırılmış verilerimizi saklayabilmemiz için, öncelikle yapıyı tanımlamamız gerekiyor. Bu
kitap SQL üzerine yazılmamıştır, bu nedenle ben bir veritabanı tablosu ve onun sütunlarıyla ilgili
kavramları biliyor olmanızı bekliyorum. Bu bölümde Schema sınıfına bakacağız, tablolarımızın yapısını tanımlamak için bu sınıfı kullanabiliriz. Bu bölümde herhangi bir veri saklaması olmayacaktır,
bu nedenle zihninizde içerik değil hep yapı olsun.
Sonraki bölümde, veritabanı yapınızı inşa etmeye başlamak için ideal konumu öğreneceksiniz,
fakat her özelliği tek başına açıklamayı düşünüyorum. Şimdilik, şema oluşturma kodlarımızı rota
closure’ları içinde yazacağız. Daha fazla zaman kaybetmeden, sorgu oluşturucuna hızlı bir bakış
yapalım.
Tabloların Oluşturulması
Bir tablo oluşturmak için Schema sınıfının create() metodunu kullanmamız gerekiyor. İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', function()
{
Schema::create('users', function($table)
{
// Henüz ayrıntıya girmiyoruz.
});
});
Schema::create() metodu iki parametre alır. Birincisi oluşturmak istediğimiz tablonun adıdır. Bu
örneğimizde ‘users’ adında bir tablo oluşturuyoruz. Oluşturacağımız tablo şayet bir nesne tipini temsil eden verileri saklamak için kullanılacaksa tablonun adını o nesnenin ingilizce çoğulunun küçük
harfleri olarak vereceğiz. (Çevirenin açıklaması: Aslında buna zorunlu değiliz. Örneğin tablo adına
users yerine uyeler adını da verebilirdik. Ancak isimlendirmenin Laravel’in bazı ön tanımlarına ve
isimlendirme geleneklerine uygun olması bize birçok kolaylıklar sağlayacaktır.) Veritabanı sütunları
Şema Oluşturucusu
217
ve tabloları çoğu kez snake-casing kullanılarak isimlendirilir, yani tüm karakterler küçük harftir ve
boşluklar alt tire ile (_) değiştirilmiştir.
İkinci parametre anonim bir fonksiyon (Closure) olup kendisi de tek bir parametre alır. Yukarıdaki
örnekte, ben bu parametreye $table adını verdim fakat siz ne isterseniz o ismi verebilirsiniz! Bu
$table parametresi tablo yapısını inşa etmekte kullanılacaktır.
Şimdi tablomuza otomatik artan birincil anahtar ekleyelim, bu sayede tablo satırlarımız benzersiz
bir indeks ile tanımlanabilecektir.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', function()
{
Schema::create('users', function($table)
{
$table->increments('id');
});
});
increments() metodu bizim $table‘ olgumuzda yeni bir otomatik artan sütun oluşturmak amacıyla
bulunmaktadır. Otomatik artan bir sütun, her bir satır eklendikçe artan bir tam sayıyı otomatik
olarak elde edebilecektir. Bu 1 ile başlayacaktır. Bu sütun aynı zamanda tablo için birincil anahtar
olacaktır. increments metodunun birinci parametresi oluşturulacak sütunun adıdır. Ne kadar basit
değil mi?
Devam edelim ve bu tabloya daha başka sütunlar ekleyelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
12
13
14
15
Route::get('/', function()
{
Schema::create('users', function($table)
{
$table->increments('id');
$table->string('username', 32);
$table->string('email', 320);
$table->string('password', 60);
$table->timestamps();
});
});
Şema Oluşturucusu
218
Mükemmel! Artık users tablomuzun yapısını oluşturmak için bir planımız oldu. Her bir sütun
hakkında şimdilik endişe etmeyin, sonraki kesimde ayrıntılı olarak göreceğiz. Öncelikle, rota
Closure’umuzu ateşlemek için / URI’sini ziyaret ederek bu tablomuzu inşa edelim.
Şimdi bizim için oluşturulan veritabanı yapısına göz atabiliriz. Tabii ben kullanmak için hangi
veritabanını seçtiğinizi bilmiyorum fakat bu kitap için ben mySQL kullanıyorum, bu nedenle
veritabanına mySQL komut satırı arayüzünü kullanarak bakacağım. Siz elbette kendinize en uygun
gelen yazılımı kullanabilirsiniz.
1
2
mysql> use myapp;
Database changed
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> describe users;
+------------+------------------+-----+----------------+
| Field
| Type
| Key | Extra
|
+------------+------------------+-----+----------------+
| id
| int(10) unsigned | PRI | auto_increment |
| username
| varchar(32)
|
|
|
| email
| varchar(320)
|
|
|
| password
| varchar(60)
|
|
|
| created_at | timestamp
|
|
|
| updated_at | timestamp
|
|
|
+------------+------------------+-----+----------------+
6 rows in set (0.00 sec)
Evet, ben kitap biçimlendirme sınırlamalarına uyması için tablo açıklamasını biraz basitleştirdim
ama anladığınızı umuyorum. Bizim users tablomuzun yapısı, $table nesnesiyle oluşturduğumuz
plan kullanılarak inşa edilmiştir.
$table nesnesinde kullanabileceğimiz metod ve sütunların ne olduğunu merak ediyor olmalısınız?
Tamam, görelim!
Sütun Türleri
$table plan nesnesinde bulunan metodları incelemeye geçebiliriz. Bazı şeyleri basitleştirmek için
rota Closure kısmını göstereceğim, o yüzden biraz hayal gücünüzü kullanmanız gerekecek! Haydi
başlıyoruz.
increments
increments metodu tabloya bir otomatik artan tam sayı birincil anahtarı ekleyecektir. Bu metod,
ilerideki bir bölümde öğreneceğimiz Eloquent ORM modelleri için yapı inşa ederken çok yararlı bir
metodtur.
Şema Oluşturucusu
1
219
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->increments('id');
});
Bu increments() metodu için birinci ve tek parametre oluşturulacak sütunun adıdır. Oluşan tablo
yapısı şöyledir:
1
2
3
4
5
+-------+------------------+-----+----------------+
| Field | Type
| Key | Extra
|
+-------+------------------+-----+----------------+
| id
| int(10) unsigned | PRI | auto_increment |
+-------+------------------+-----+----------------+
bigIncrements
Oo, demek increments metodu sizin için yeterince büyük olmadı? İyi işte, bigIncrements() metodu
olağan increments’tan daha büyük bir tam sayı oluşturacaktır.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->bigIncrements('id');
});
Tıpkı increments() metodu gibi, bigIncrements() metodu da tek bir string parametre olarak sütun
adını alacaktır.
1
2
3
4
5
+-------+---------------------+-----+----------------+
| Field | Type
| Key | Extra
|
+-------+---------------------+-----+----------------+
| id
| bigint(20) unsigned | PRI | auto_increment |
+-------+---------------------+-----+----------------+
string
string() metodu varchar sütunları oluşturmak için kullanılabilir, varchar sütunu kısa string
değerleri saklamaya yarar.
Şema Oluşturucusu
1
220
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->string('nickname', 128);
});
string() metodunun birinci parametresi oluşturulacak sütunun adıdır, bununla birlikte, stringin
karakter cinsinden uzunluğunu tanımlamak için ikinci bir opsiyonel parametresi de olabilir. Bunun
ön tanımlı değeri 255’tir.
1
2
3
4
5
+----------+--------------+
| Field
| Type
|
+----------+--------------+
| nickname | varchar(255) |
+----------+--------------+
text
text() metodu bir varchar sütun tipine sığmayacak kadar büyük miktarda metinleri depolamak için
kullanılabilir. Örneğin, bir blog makalesinin gövde metnini taşımak için bu sütun tipi kullanılabilir.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->text('body');
});
text() metodu tek parametre kabul eder. Oluşturulacak olan sütunun ismi.
1
2
3
4
5
+-------+------+
| Field | Type |
+-------+------+
| body | text |
+-------+------+
Şema Oluşturucusu
221
integer
Integer sütun tipi tam sayı değerlerini saklamak için kullanılabilir, şaşırmadınız ya? Bunu daha ilginç
hale getirecek başka bir şey bulamadım! Evet, Başka bir tablonun otomatik artan id değerine referans
yaparken integer alanlarının yararlı olacağını düşünüyorum. Bu metodu tablolar arasında ilişkiler
oluşturmak için kullanabiliriz.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->integer('role_id');
});
integer() metodunun birinci parametresi sütunun adıdır. İkinci parametre sütunun otomatik artan
olup olmadığını tanımlamak için kullanılan boolean bir değerdir. Üçüncü parametre tam sayının
işaretsiz olup olmadığını tanımlamak için kullanılır. İşaretli bir tam sayı pozitif veya negatif olabilir,
buna karşın eğer bir integeri işaretsiz olarak tanımlarsanız, bu durumda sadece pozitif olabilecektir.
İşaretli tam sayılar –2,147,483,648 ile 2,147,483,647 aralığındaki tam sayıları taşıyabilir, işaretsiz bir
integer ise 0 ile 4,294,967,295 arasındaki tam sayıları tutabilir.
1
2
3
4
5
+---------+---------+
| Field
| Type
|
+---------+---------+
| role_id | int(11) |
+---------+---------+
bigInteger
Big integer değerleri tam olarak normal tam sayılar gibi iş görür ancak daha büyük aralığa sahiptir.
İşaretli bir integer –9,223,372,036,854,775,808 ile 9,223,372,036,854,775,807 arasındadır ve işaretsiz
tam sayılar 0 ile 18,446,744,073,709,551,615 aralığındadır. Bu boyutlardaki tam sayılar normalde inç
cinsinden bel ölçüsünü saklamak için kullanılır!
Şema Oluşturucusu
1
222
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->bigInteger('waist_size');
});
Tam sayı çeşitlerinin hepsinde de yazım şekli integer() metodunun tam aynısıdır, bu yüzden
bunu tekrarlamakla zaman kaybetmeyeceğim! Eğer çoktan unutmuşsanız, ‘integer’ kesimine tekrar
bakmanız gerekecektir?
1
2
3
4
5
+------------+------------+
| Field
| Type
|
+------------+------------+
| waist_size | bigint(20) |
+------------+------------+
mediumInteger
Başka bir tam sayı tipidir, Bu sütun türlerini biraz daha hızlı dolaşalım mı? Artık sadece sütun değer
aralıklarını belirteceğim. İşaretli aralık –8388608 ile 8388607 arasıdır. İşaretsiz aralık 0 ile 16777215
arasıdır.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->mediumInteger('size');
});
Metodun yazım şekli integer() metodu ile aynıdır.
1
2
3
4
5
+--------+-------------+
| Field | Type
|
+--------+-------------+
| size
| mediumint(9)|
+--------+-------------+
tinyInteger
Bu başka bir integer türü sütundur. İşaretli aralık –128 ile 127. İşaretsiz aralık 0 ile 255 arasıdır.
Şema Oluşturucusu
1
223
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->tinyInteger('size');
});
Bu metodun yazım şekli integer() metodu ile aynıdır.
1
2
3
4
5
+-------+------------+
| Field | Type
|
+-------+------------+
| size | tinyint(1) |
+-------+------------+
smallInteger
Bu başka bir integer türü sütundur. İşaretli aralık –32768 ile 32767 arasıdır. İşaretsiz aralık 0 ile 65535
arasıdır.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->smallInteger('size');
});
Metodun yazım şekli integer() metodu ile aynıdır.
1
2
3
4
5
+-------+-------------+
| Field | Type
|
+-------+-------------+
| size | smallint(6) |
+-------+-------------+
float
Float sütun türleri kayan noktalı sayıları saklamak için kullanılır. Şu şekilde tanımlanabilirler.
Şema Oluşturucusu
1
224
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->float('size');
});
Birinci parametre sütunu tanımlamak için kullanılan isimdir. Opsiyonel ikinci ve üçüncü parametreler değerin uzunluğunu belirtmek ve değeri göstermekte kullanılacak ondalık sayısını belirtmek
için kullanılabilir. Bu parametreler için ön tanımlı değerler sırasıyla 8 ve 2’dir.
1
2
3
4
5
+-------+------------+
| Field | Type
|
+-------+------------+
| size | float(8,2) |
+-------+------------+
decimal
decimal() metodu … neydi, bir saniye… desimal değerleri saklamak için kullanılır! float()
metoduna çok benzer.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->decimal('size');
});
Bu metod birinci parametre olarak bir sütun adı alır ve sütunu tanımlamak için uzunluk ve ondalık
basamak sayısını temsil eden opsiyonel iki parametre daha alabilir. Bu opsiyonel parametreler için
ön tanımlı değerler yine 8 ve 2’dir.
1
2
3
4
5
+-------+--------------+
| Field | Type
|
+-------+--------------+
| size | decimal(8,2) |
+-------+--------------+
Şema Oluşturucusu
225
boolean
Bütün değerler sayılar ve karakterlerden ibaret değildir. Bazıları sadece iki durumda olurlar, true
veya false, 1 veya 0. Boolean sütun tipleri bu değerleri temsil etmek için kullanılır.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->boolean('hot');
});
Boolean metodunun tek parametresi oluşturacağı alanın adıdır.
1
2
3
4
5
+-------+------------+
| Field | Type
|
+-------+------------+
| hot
| tinyint(1) |
+-------+------------+
Yukarıdaki örnekteki tinyint bir yazım hatası değildir. Tiny integerler 1 veya 0 gibi boolean
değerleri göstermek için kullanılırlar. Bu kısmı yazmakla meşgulken mutfakta neredeyse yandığımı
da belirtmek istiyorum. Teknik bir yazarın tehlikeli yaşamı hakkında bilgi sahibi olmak istersiniz
diye düşündüm! Hayır? İyi, sütun tanımlamalarına devam edelim öyleyse.
enum
Numaralandırılmış tür bir izin verilenler listesinde yer alan stringleri saklamak için kullanılır. İşte
bir örnek.
1
<?php
2
3
4
5
6
7
Schema::create('example', function($table)
{
$izinliler = array('Ali', 'Veli', 'Selami');
$table->enum('who', $izinliler);
});
Birinci parametre oluşturulacak olan sütunun adıdır. İkinci parametre bu enum tipi için izin verilen
değerlerden oluşan bir dizidir.
Şema Oluşturucusu
1
2
3
4
5
226
+-------+-----------------------------+------+
| Field | Type
| Null |
+-------+-----------------------------+------+
| who
| enum('Ali','Veli','Selami') | NO
|
+-------+-----------------------------+------+
date
Adından da anlaşılacağı gibi, date() metodu tarihleri depolayan sütunlar oluşturmak için kullanılır.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->date('when');
});
Birinci ve tek parametre oluşturulacak olan sütunun adını belirtmek için kullanılır.
1
2
3
4
5
+-------+------+
| Field | Type |
+-------+------+
| when | date |
+-------+------+
dateTime
dateTime() metodu sadece bir tarihi saklamakla kalmayıp, aynı zamanda zamanı da saklayacaktır.
Şaka yapmıyorum, gerçekten öyle yapar. Biliyorum, biliyorum, bu metodların birçoğu birbirine
benziyor fakat bana güvenin, bu çok harika bir referans bölümü olacak!
Şema Oluşturucusu
1
227
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->dateTime('when');
});
Bir kez daha, oluşturulacak sütunun adı tek parametredir.
1
2
3
4
5
+-------+----------+
| Field | Type
|
+-------+----------+
| when | datetime |
+-------+----------+
time
Zamanlarınızla birlikte tarih olsun istemiyor musunuz? Güzel! O zaman onun yerine time()
metodunu kullanın.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->time('when');
});
Bir kez daha, time() metodunun ilk ve tek parametresi oluşturulacak sütunun adıdır.
1
2
3
4
5
+-------+------+
| Field | Type |
+-------+------+
| when | time |
+-------+------+
timestamp
Tarih ve zamanı TIMESTAMP (zaman damgası) formatında saklamak için timestamp() metodu
kullanılabilir. Şaşırtıcı mı? Hayır? Oo… iyi, nasıl çalıştığına bakalım o zaman.
Şema Oluşturucusu
1
228
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->timestamp('when');
});
İlk ve tek değeri oluşturulacak olan veritabanı sütununun adıdır.
1
2
3
4
5
+-------+-----------+---------------------+
| Field | Type
| Default
|
+-------+-----------+---------------------+
| when | timestamp | 0000-00-00 00:00:00 |
+-------+-----------+---------------------+
binary
İkili verileri saklayacak sütunlar oluşturmak için binary() metodu kullanılabilir. Bu sütun tipleri,
imajlar gibi ikili dosyaları saklamak için yararlı olacaklardır.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->binary('image');
});
Binary metodu için tek parametre oluşturulacak sütunun adıdır.
1
2
3
4
5
+--------+------+
| Field | Type |
+--------+------+
| image | blob |
+--------+------+
Şema Oluşturucusu
229
Özellikli Sütun Türleri
Laravel farklı kullanımları olan birkaç adet özel sütun tipi içermektedir. Onlara bir bakalım. Bunun
için öncelikle timestamps() metodunu göreceğiz.
Bu timestamps() metodu tabloya iki tane ‘TIMESTAMP’ sütunu eklemek için kullanılabilmektedir.
Bir satırın oluşturulduğu ve güncellendiği zamanı göstermek amacıyla created_at ve updated_at sütunları kullanılabilmektedir. Daha sonraki bir bölümde bir ORM olgusu oluşturulduğu veya
güncellendiği zaman Laravel’in kendi Eloquent ORM’sinin bu sütunları otomatik olarak nasıl güncel
tutabileceğini öğreneceğiz. Şimdi timestamps() metodunun nasıl kullanılacağını görelim.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->timestamps();
});
timestamps() metodu bir parametre almaz. Oluşturulan tablo yapısı şu şekildedir.
1
2
3
4
5
6
+------------+-----------+---------------------+
| Field
| Type
| Default
|
+------------+-----------+---------------------+
| created_at | timestamp | 0000-00-00 00:00:00 |
| updated_at | timestamp | 0000-00-00 00:00:00 |
+------------+-----------+---------------------+
Diğer metodumuz softDeletes() metodudur. Zaman zaman bir tablo satırını, içindeki veriyi
gerçekte silmeksizin, silinmiş olarak işaretlemek isteyebilirsiniz. Eğer gelecek bir zamanda veriyi
restore edebilmek istiyorsanız bu yararlı olacaktır. softDeletes() metoduyla satır üzerinde satırın
silinmiş olduğunu gösteren bir gösterge sütunu koyabilirsiniz. Oluşan sütunun adı deleted_at
ve tipi ‘TIMESTAMP’ olacaktır. Aynı şekilde, Laravel’in Eloquent ORM’si bir ORM olgusunda
delete metodu kullandığınız takdirde satırı gerçekten silmeksizin bu sütunu güncelleyebilmektedir.
Tablomuza deleted_at sütununu şu şekilde ekleyebiliyoruz.
Şema Oluşturucusu
1
230
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->softDeletes();
});
Bu softDeletes() metodu herhangi bir parametre almaz. İşte oluşan tablo.
1
2
3
4
5
+------------+-----------+------+
| Field
| Type
| Null |
+------------+-----------+------+
| deleted_at | timestamp | YES |
+------------+-----------+------+
Sütun Niteleyicileri
Sütun niteleyicileri create() metodu ile oluşturduğumuz sütunlara ekstra sınırlamalar veya özellikler eklemek için kullanılabilirler. Örneğin, daha önce hem otomatik artan hem de birincil alan
özelliğine sahip bir tablo indeks sütunu oluşturmak için increments() metodunu kullanmıştık.
Bu yararlı bir kısa yoldur, fakat gelin bir başka sütunu birincil anahtara dönüştürmek için sütun
niteleyicilerini nasıl kullanabileceğimizi görelim.
Önce yeni bir sütun oluşturacağız ve onun benzersiz değerler taşıması gerektiğini bildireceğiz.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->string('username')->unique();
});
Sütun oluşturma metodumuza unique() metodunu zincirlemek suretiyle, veritabanına bu sütun
için kopya değerlere izin verilemeyeceğini söylemiş olduk. Birincil anahtarımız satırları tek tek
tanımlamak için kullanılmalıdır, bu yüzden, kopya değerler içermesini istemeyiz, yapalım o zaman!
Şimdi ‘username’ sütununu tablonun birincil anahtarı yapıyoruz.
Şema Oluşturucusu
1
231
<?php
2
3
4
5
6
7
Schema::create('example', function($table)
{
$table->string('username')->unique();
$table->primary('username');
});
Bu primary() metodunu kullanarak herhangi bir sütunu bir birincil anahtar olarak işaretleyebiliriz.
Bu metodun tek parametresi anahtar olarak işaretlenecek olan sütunun adını temsil eden bir stringtir.
Yeni oluşturduğumuz tablonun açıklamasını görelim.
1
2
3
4
5
+----------+--------------+------+-----+---------+-------+
| Field
| Type
| Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| username | varchar(255) | NO
| PRI | NULL
|
|
+----------+--------------+------+-----+---------+-------+
Harika! Yeni bir birincil anahtarımız oldu.
Burada güzel bir numara vereyim, gerek primary() key gerek unique() metodu bir diğerinde etki
gösterebilir veya mevcut bir değere akıcı biçimde zincirlenebilirler. Yani yukarıdaki örneği şu şekilde
de yazabiliriz:
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->string('username')->unique()->primary();
});
Yukarıdaki örnek, sütun niteleyicilerinin mevcut bir sütun tarifine zincirlenebileceğini gösteriyor.
Alternatif olarak sütun niteleyicileri bir parametre olarak bir sütun adı sağlanmak suretiyle tek tek
de kullanılabilmektedir.
Şema Oluşturucusu
1
232
<?php
2
3
4
5
6
7
8
Schema::create('example', function($table)
{
$table->string('username');
$table->unique('username');
$table->primary('username');
});
Tablonuz için tek bir birincil anahtar olmasından tatmin olmazsanız, önceki örnekte kullandığımız
primary() metoduna sütun adlarından oluşan bir dizi sağlamak suretiyle çoklu birleşik anahtarlar
kullanabilirsiniz. Haydi bakalım.
1
<?php
2
3
4
5
6
7
8
9
10
Schema::create('example', function($table)
{
$table->integer('id');
$table->string('username');
$table->string('email');
$anahtarlar = array('id', 'username', 'email');
$table->primary($anahtarlar);
});
Şimdi üç yeni sütunumuz birleşik bir anahtar olarak davranacaktır, böylece, bu sütunlarda yer alan
değerlerin herhangi bir kombinasyonu belirli bir role benzersiz referans olacaktır. mySQL ‘describe’
ile elde edilen çıktıyı görelim şimdi.
1
2
3
4
5
6
7
+----------+--------------+------+-----+---------+-------+
| Field
| Type
| Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| id
| int(11)
| NO
| PRI | NULL
|
|
| username | varchar(255) | NO
| PRI | NULL
|
|
| email
| varchar(255) | NO
| PRI | NULL
|
|
+----------+--------------+------+-----+---------+-------+
Bilgi aramak için indeksler olarak kullanılacak sütunları işaretleyerek sorgularımızı hızlandırabiliyoruz. Bir sütunu bir indeks olarak işaretlemek için index() metodunu kullanabiliyoruz. Aynı
satırda kullanılabilir, örneğin şöyle:
Şema Oluşturucusu
1
233
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->integer('age')->index();
});
Veya tek başına da kullanılabilir, mesela böyle:
1
<?php
2
3
4
5
6
7
Schema::create('example', function($table)
{
$table->integer('age');
$table->index('age');
});
Her iki şekilde de sonuç aynı olacaktır. İlgili sütun bir indeks olarak işaretlenecektir.
1
2
3
4
5
+-------+---------+------+-----+---------+-------+
| Field | Type
| Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| age
| int(11) | NO
| MUL | NULL
|
|
+-------+---------+------+-----+---------+-------+
Ayrıca, birden çok sütunu indeksler olarak işaretlemek için sütun isimlerinden oluşan bir diziyi
index() metoduna geçebiliyoruz. İşte bir örnek.
1
<?php
2
3
4
5
6
7
8
Schema::create('example', function($table)
{
$table->integer('age');
$table->integer('weight');
$table->index(array('age', 'weight'));
});
Oluşan tablo yapısı şöyledir.
Şema Oluşturucusu
1
2
3
4
5
6
234
+--------+---------+------+-----+---------+-------+
| Field | Type
| Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| age
| int(11) | NO
| MUL | NULL
|
|
| weight | int(11) | NO
|
| NULL
|
|
+--------+---------+------+-----+---------+-------+
Bazen bir sütunun null bir değer içerip içeremeyeceğini belirtmek için bir sınırlama ayarlamak
isteyebilirsiniz. Bir sütunun null değerler alabilmesini nullable() metodunu kullanarak ayarlayabiliyoruz. Bu, zincir metodunun bir parçası olabilir, mesela böyle:
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->string('name')->nullable();
});
Oluşan tablo yapısı şöyledir.
1
2
3
4
5
+-------+--------------+------+
| Field | Type
| Null |
+-------+--------------+------+
| name | varchar(255) | YES |
+-------+--------------+------+
Görebileceğiniz gibi, bu sütun artık null bir değer taşıyabilecektir. Eğer bir sütunun null bir değeri
olmasına izin vermek istemiyorsak nullable() zincirleme metoduna ilk parametre olarak boolean
false geçebiliyoruz, bunun gibi:
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->string('name')->nullable(false);
});
Oluşan tablo yapısına bir daha bakarsak.
Şema Oluşturucusu
1
2
3
4
5
235
+-------+--------------+------+
| Field | Type
| Null |
+-------+--------------+------+
| name | varchar(255) | NO
|
+-------+--------------+------+
Görebileceğiniz gibi, ‘name’ sütunu artık null bir değer içeremez.
Yeni bir satır oluşturulduğunda, bir sütunun ön tanımlı bir değer içermesini istersek, yeni sütun
tanımında default() metodunu zincirlemek suretiyle ön tanımlı değer sağlayabiliriz. İşte bir örnek.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->string('name')->default('Sergin Arı');
});
default() metodunun birinci ve tek parametresi sütun için amaçlanan ön tanım değeridir. Oluşan
tablo yapısının nasıl olduğuna bir göz atalım.
1
2
3
4
5
+-------+--------------+------+-----+------------+
| Field | Type
| Null | Key | Default
|
+-------+--------------+------+-----+------------+
| name | varchar(255) | NO
|
| Sergin Arı |
+-------+--------------+------+-----+------------+
Yeni bir satır oluştururken ‘name’ sütununa bir değer sağlamazsak, bu durumda onun değeri ön
tanımlı ‘Sergin Arı’ olacaktır.
Göreceğimiz son bir sütun niteliyici var. Bu gerçekten gerekli değildir ancak güzel bir kısayoldur.
Önceki kesimdeki integer sütun oluşturmayı hatırlıyor musunuz? Bir integerin işaretli olup olmadığını ve sütunun negatif bir değer içerip içeremeyeceğini belirtmek için boolean bir parametre
kullanmıştık. Pekala, integer bir sütunun negatif değerler içeremeyeceğini belirtmek için unsigned()
zincirleme metodunu kullanabiliriz. İşte bir örnek.
Şema Oluşturucusu
1
236
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->integer('age')->unsigned();
});
unsigned() zircirleme metodunu kullandıktan sonra oluşan tablo yapısı şöyledir.
1
2
3
4
5
+-------+------------------+
| Field | Type
|
+-------+------------------+
| age
| int(10) unsigned |
+-------+------------------+
İster boolean anahtarını, ister unsigned() metodunu kullanabilirsiniz, tercih tamamen size kalmış.
Tabloların Güncellenmesi
Bir tablo oluşturulduktan sonra, onu değiştirmenin bir yolu yoktur.
Emin misiniz, çünkü başlık diyor ki…
Eminim, Kesinlikle bir yolu yok.
Hmm, ama başlık tabloların güncellenmesi diyor?
Gitmeme izin vermiyor musun? Güzel, biraz şekerleme yapacaktım ama beni ikna ettin. Tabloların
güncellenmesini öğrenmek istiyorsun, başlayalım öyleyse.
Her şeyden önce, daha önce oluşturduğumuz bir tablonun adını Schema::rename() metodunu
kullanarak kolaylıkla değiştirebiliriz. Bir örnek üzerinden görelim.
Şema Oluşturucusu
1
237
<?php
2
3
4
5
6
7
// users tablosunu oluştur.
Schema::create('users', function($table)
{
$table->increments('id');
});
8
9
10
// users tablosunun adını idiots olarak değiştir.
Schema::rename('users', 'idiots');
Bu rename() metodunun birinci parametresi değiştirmek istediğimiz tablonun adıdır. Metodun
ikinci parametresi ise tablonun yeni adıdır.
Eğer mevcut bir tablonun sütunlarını değiştirmek istiyorsak, Schema::table() metodunu kullanmamız gerekir. Daha yakından görelim.
1
<?php
2
3
4
5
6
Schema::table('example', function($table)
{
// $table'yu modifikasyon kodu...
});
table() metodu, daha önce bir tablo oluşturmak için kullandığımız create() metodunun neredeyse
aynısıdır. Tek farkı, metodun birinci parametresi içinde belirtilen mevcut bir tablo üzerinde etki
göstermesidir. Bu metodta da ikinci parametre bir tablo oluşturucusu olgusunu parametre olarak
alan bir Closure’dir.
Mevcut bir tabloya yeni sütunlar eklemek için, önceki kesimlerde keşfettiğimiz sütun oluşturma
metodlarının hepsini kullanabiliriz. İşte bir örnek.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->increments('id');
});
7
8
9
10
11
Schema::table('example', function($table)
{
$table->string('name');
});
Şema Oluşturucusu
238
Yukarıdaki örnekte bir birincil anahtarı olan ‘example’ tablosunu inşa etmek için Schema::create()
metodunu kullanıyoruz. Daha sonra, mevcut tabloya bir string sütunu eklemek için Schema::table()
metodunu kullanıyoruz.
mySQL komut satırı describe example; komutu ile elde edilen sonuç şöyledir:
1
2
3
4
5
6
+-------+------------------+-----+----------------+
| Field | Type
| Key | Extra
|
+-------+------------------+-----+----------------+
| id
| int(10) unsigned | PRI | auto_increment |
| name | varchar(255)
|
|
|
+-------+------------------+-----+----------------+
Şimdi bir tabloya ilave sütunlar eklemek için önceki kesimde öğrendiğimiz sütun oluşturma metodlarının herhangi birini kullanabiliyoruz. Her oluşturma metodunu tek tek anlatmak istemiyorum,
yazılımlarında değişiklik yok. Eğer hızlı bir tazeleme dersi gerekirse, o zaman ‘Sütun Türleri’
kesimine bir kez daha bakılabilir.
Eğer bir sütunun tablomuzda artık olmamasına karar verirsek, onu çıkartmak için dropColumn()
metodunu kullanabiliriz. Bu eyleme bir göz atalım.
1
<?php
2
3
4
5
6
7
Schema::create('example', function($table)
{
$table->increments('id');
$table->string('name');
});
8
9
10
11
12
Schema::table('example', function($table)
{
$table->dropColumn('name');
});
Yukarıdaki örnekte, iki sütunu olan ‘example’ tablosunu oluşturuyoruz. Daha sonra bu tablodan
‘name’ sütununu çıkartmak için dropColumn() metodunu kullanıyoruz. dropColumn() metodu
string bir parametre alacaktır ve bu parametre çıkartmak istediğimiz sütunun adı olacaktır.
İşte yukarıdaki kod çalıştırıldıktan sonra ‘example’ tablomuzun nasıl göründüğü.
Şema Oluşturucusu
1
2
3
4
5
239
+-------+------------------+------+-----+----------------+
| Field | Type
| Null | Key | Extra
|
+-------+------------------+------+-----+----------------+
| id
| int(10) unsigned | NO
| PRI | auto_increment |
+-------+------------------+------+-----+----------------+
Görebileceğiniz gibi, ‘name’ sütunu başarıyla çıkartılmıştır.
Şayet bir defada birden çok sütunu çıkartmak istiyorsak, ya dropColumn() metoduna ilk parametre
olarak sütun adlarından oluşan bir dizi geçebiliriz…
1
<?php
2
3
4
5
6
Schema::table('example', function($table)
{
$table->dropColumn(array('name', 'age'));
});
…ya da sütun isimleri için birden çok string parametreler verebiliriz.
1
<?php
2
3
4
5
6
Schema::table('example', function($table)
{
$table->dropColumn('name', 'age');
});
Hangi yöntem kodlama tarzınıza uygunsa onu kullanabilirsiniz.
Sütunları o kadar da düşürmek zorunda değiliz. Eğer istersek sadece adını değiştirebiliriz. Bir örnek
üzerinde görelim bunu.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->string('name');
});
7
8
9
10
11
Schema::table('example', function($table)
{
$table->renameColumn('name', 'nickname');
});
Şema Oluşturucusu
240
renameColumn() metodu bir sütunun adını değiştirmek için kullanılır. Metodun birinci parametresi
adını değiştirmek istediğimiz sütunun adıdır ve ikinci parametresi sütunun yeni adıdır. Yukarıdaki
örnekten oluşan tablo yapısı şöyle olacaktır.
1
2
3
4
5
+----------+---------------+
| Field
| Type
|
+----------+---------------+
| nickname | varchar(255) |
+----------+---------------+
Şimdi, önceki kesimde oluşturduğumuz birincil anahtarları hatırlayalım. Bazı sütunların artık
birincil anahtarlar olmamasını istersek ne yapacağız? Problem değil, sadece anahtarı çıkartırız. İşte
bir örnek.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->string('name')->primary();
});
7
8
9
10
11
Schema::table('example', function($table)
{
$table->dropPrimary('name');
});
Bu dropPrimary() metodunu kullanırken bir parametre olarak bir sütun adını veriyoruz. Bu sütundan birincil anahtar niteliği çıkartılacaktır. Bu kod çalıştırıldıktan sonra tablonun nasıl göründüğüne
bir göz atalım.
1
2
3
4
5
+-------+--------------+------+-----+---------+-------+
| Field | Type
| Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| name | varchar(255) | NO
|
| NULL
|
|
+-------+--------------+------+-----+---------+-------+
Görebileceğiniz gibi, name sütunu artık birincil anahtar değildir. Bir tablodan değişik sayıda birleşik
anahtarları çıkartmak için dropPrimary() metodunun birinci parametresi olarak sütun adlarından
oluşan bir dizi sunabiliriz. İşte bir örnek.
Şema Oluşturucusu
1
241
<?php
2
3
4
5
6
7
8
Schema::create('example', function($table)
{
$table->string('name');
$table->string('email');
$table->primary(array('name', 'email'));
});
9
10
11
12
13
Schema::table('example', function($table)
{
$table->dropPrimary(array('name', 'email'));
});
Bir sütunun unique niteliğini dropUnique() metodunu kullanarak çıkartabiliriz. Bu metod tek bir
parametre alır: alt tire ile birleştirilmiş tablo adı, sütun adı ve ‘unique’ kelimesi. Bir sütundan unique
niteliğinin çıkartılması için bir örnek verelim.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->string('name')->unique();
});
7
8
9
10
11
Schema::table('example', function($table)
{
$table->dropUnique('example_name_unique');
});
Tekrar ifade edeyim, eğer istersek dropUnique() metoduna aynı formatta yazılmış sütun isimlerinden oluşan bir dizi geçebiliriz. İşte bir örnek.
Şema Oluşturucusu
1
242
<?php
2
3
4
5
6
7
Schema::create('example', function($table)
{
$table->string('name')->unique();
$table->string('email')->unique();
});
8
9
10
11
12
13
Schema::table('example', function($table)
{
$sutunlar = array('example_name_unique', 'example_email_unique');
$table->dropUnique($sutunlar);
});
Son olarak, bir tablo sütunundan index niteliğini düşürmek için… bir saniye bekleyin… evet, tahmin
ettiğin gibi. dropIndex() metodunu kullanabiliriz. Aynı dropUnique() metodunda kullandığımız
formatta, yani alt tire ile birleştirilmiş tablo adı, sütun adı ve ‘index’ kelimesini vermemiz yeterlidir.
Örneğin:
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->string('name')->index();
});
7
8
9
10
11
Schema::table('example', function($table)
{
$table->dropIndex('example_name_index');
});
Nedense ben dropIndex() metoduna sütun adlarından oluşan bir dizi geçemedim. Bunu Taylor’a
soracağım ve değişiklik olursa bölümü güncelleyeceğim. Şimdilik devam edelim.
Tabloların Düşürülmesi
Bir tabloyu düşürmek için bacaklarını kesmeniz yeterlidir.
Şaka yapıyorum, bir tabloyu Schema::drop() metodunu kullanarak düşürebiliriz, bu metodu iş
üstünde görelim.
Şema Oluşturucusu
1
243
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->string('name');
});
7
8
Schema::drop('example');
Bir tabloyu düşürmek için sadece Schema::drop() metoduna ilk parametre olarak tablonun adını
geçiyoruz. Varsa görmek için tabloyu açıklatmayı deneyelim.
1
2
mysql> describe example;
ERROR 1146 (42S02): Table 'myapp.example' doesn't exist
Güzel, çalıştı sanırım! Tablo gitmiş gibi görünüyor.
Şayet mevcut olmayan bir tabloyu drop etmeye kalkarsak bir hata alırız. Bundan kaçınmak için
bunun yerine dropIfExists() metodunu kullanabiliriz. İsminden de anlaşılacağı gibi bu metod
sadece bir tablo mevcut ise düşürecektir. İşte bir örnek.
1
<?php
2
3
4
5
6
Schema::create('example', function($table)
{
$table->string('name');
});
7
8
Schema::dropIfExists('example');
Tıpkı drop() metodu gibi, dropIfExists() metodu da tek bir parametre alır, düşürülecek tablonun
adını.
Şema Püf Noktaları
Püf noktaları? Belki de değil. Bununla birlikte bu kesim, önceki kesimlere tam uymayan metodlar
için kullanılmıştır. Daha fazla zaman kaybetmeden ilk metoda bir göz atalım.
Şema değişikliklerimizi alternatif bir veritabanı veya bağlantıda yapmak için Schema::connection()
metodunu kullanabiliriz. Bunu bir örnekle görelim.
Şema Oluşturucusu
1
244
<?php
2
3
4
5
6
Schema::connection('mysql')->create('example', function($table)
{
$table->increments('id');
});
7
8
9
10
11
Schema::connection('mysql')->table('example', function($table)
{
$table->string('name');
});
Bu connection() metodunun zircirlenecek herhangi bir Schema sınıfı metodundan önce gelmesi
gerekir. Bu metodun ilk parametresi sonraki metodların dayanacağı veritabanı bağlantısının ismidir.
Birden çok veritabanı kullanan bir uygulama yazmanız gerektiğinde connection() metodu çok
yararlı olabilir.
Bunun dışında, sütunların ve tabloların mevcut olup olmadığını kontrol etmekte kullanabileceğimiz
bir çift metod var. En iyisi bir örnekle devam edelim.
1
<?php
2
3
4
5
6
7
8
if (Schema::hasTable('author')) {
Schema::create('books', function($table)
{
$table->increments('id');
});
}
Bir tablonun mevcut olup olmadığını yoklamak için hasTable() metodunu kullanabiliyoruz. Bu
metodun birinci parametresi kontrol etmek istediğimiz tablonun adıdır. Yukarıdaki örnekte, ‘books’
tablosunu sadece ‘author’ tablosu mevcutsa oluşturuyoruz.
Zaten tahmin etmiş olabileceğiniz gibi, bir sütunun mevcut olup olmadığını kontrol etmek için
benzer bir metodumuz var. Onu da başka bir örnekle görelim.
Şema Oluşturucusu
1
245
<?php
2
3
4
5
6
7
8
if (Schema::hasColumn('example', 'id')) {
Schema::table('example', function($table)
{
$table->string('name');
});
}
Bir tabloda bir sütun olup olmadığını kontrol etmek için Schema::hasColumn() metodunu kullanabiliyoruz. Bu metodun birinci parametresi ilgili tablodur ve ikinci parametresi bulmak istediğimiz
sütunun adıdır. Yukarıdaki örnekte ‘example’ tablosuna bir ‘name’ sütunu eklenecektir.
Eğer bir veritabanı dahisi olduysanız, tablo tarafından kullanılan depolama motorunu değiştirmek
isteyebilirsiniz. İşte bir örnek.
1
<?php
2
3
4
5
6
7
Schema::create('example', function($table)
{
$table->engine = 'InnoDB';
$table->increments('id');
});
Yapacağınız tek şey tablo planındaki engine niteliğinin değerini kullanmak istediğimiz depolama
motorunun adına değiştirmektir. Burada mySQL veritabanı için kullanılabilecek depolama motorlarının bir kısmı verilmiştir:
•
•
•
•
•
•
•
•
•
•
MyISAM
InnoDB
IBMDM2I
MERGE
MEMORY
EXAMPLE
FEDERATED
ARCHIVE
CSV
BLACKHOLE
Bu depolama motorlarıyla ilgili daha fazla bilgi için lütfen ilgili konudaki mySQL belgelerine³¹
bakınız.
mySQL veritabanları için, after() metodunu kullanmak suretiyle bir tablonun sütunlarını yeniden
sıralayabilirsiniz. İşte bir örnek.
³¹http://dev.mysql.com/doc/refman/5.1/en/storage-engines.html
Şema Oluşturucusu
1
246
<?php
2
3
4
5
6
7
Schema::create('example', function($table)
{
$table->string('name')->after('id');
$table->increments('id');
});
Yapmanız gereken, sırasını değiştirmek istediğiniz sütuna after() metodunu zincirlemektir. Bu
metodun tek parametresi kendisinden sonra gelmesini istediğiniz yeni sütunun adıdır. Bu metodu
kullanmakta serbestsiniz, ancak ben tablolarınızı istediğiniz sırada basit yolla inşa etmenizi öneririm,
bu çok daha temiz gözükecektir.
Pekala, veritabanı inşa etme hususunda benden bu kadar. Neden şemalarımızı inşa etmek için daha
uygun bir yer konusunu öğrenmiyoruz? Haydi öyleyse, migrasyonlar bölümüne geçelim.
Migrasyonlar
Dayle malikanesinde oldukça etkili bir sistemimiz var. Günün tüm görevlerinin kırmızı pandalar
görevli ordusu tarafından hiçbir karışıklık olmadan tamamlanmasını sağlayan bir sistem. Onu sizinle
paylaşmak istiyorum. Elemanlarımın görev listesi şu şekilde. Onlara yardım edebilir misiniz?
•
•
•
•
•
•
09:00 AM - Dayle’i yıka ve giydir.
10:00 AM - Kahvaltı için nadir ve egzotik et çeşitleri pişir ve ızgara yap.
12:00 PM - (Öğle Yemeği) Pandalar bir ağaca tırmanacak ve bir süre uyuyacaklar.
02:00 PM - Apple donanımlarını cilala.
04:00 PM - Bir sonraki Code Bright bölümü için yazı tahtasını hazırla.
09:00 PM - Uyuyan Dayle’i yazı tahtasından sürükleyerek yatağına sok.
Evet, kırmızı pandalarım için listem böyle. Oldukça yoğun bir günleri var ve onlar olmadan ne
yapardım bilemiyorum. Problem listenin çok belirli bir sırada olması. Yazı tahtasına uğramamdan
önce pandaların beni yatağa sokmalarını istemeyiz, aksi takdirde yeni bir bölüm yazamam. Ayrıca,
bu görevlerin iki kez yapılması da olmaz. Pandaların bu görevleri sırasıyla ve bir kez yaptıklarından
emin olmaları gerekir.
Pandalar o kadar akıllı ki, tüm sorunlarına çözüm bulabiliyorlar. Ben onlara bir kalem ve orijinal
listenin olduğu bir defter verdim, onlara hediye vermişim gibi heyecanlandılar. Yuvarlanıp durdular.
Her neyse, sonunda kendi listelerini kendileri yapmaya karar verdiler, çifte eğlence?
Pandalar ikinci bir liste yazmaya karar verdiler. Sıradaki ilk görevi tamamlar tamamlamaz ikinci
listeye görevin zamanını ve adını yazacaklardı. Böylece, aynı görev asla tekrarlanmayacaktı.
Kabul etmeliyim ki kötü bir fikir değildi. Neyse ki, bazı akıllı dostlarım veritabanları için de benzer
bir fikir buldular. Şimdi migrasyonları görelim.
Temel Kavram
Veritabanlarınızı inşa ederken onun yapısını elle oluştururdunuz. Sütunlarınızı tanımlamak için bazı
hoş SQL yazarsınız ama yanlışlıkla veritabanınızı drop yaparsanız ne olur? Bir ekip olarak çalışıyorsanız ne olur? Veritabanınızı senkronize tutmak için SQL dumplarınızı tüm ekibe göndermek
zorunda kalmak istemezsiniz.
İşte migrasyonların yararı burada kendini gösteriyor. Migrasyonlar veritabanınızın yapısını veya
içeriğini değiştirmek için kullanılan birtakım PHP betikleridir. Migrasyonlar zaman damgalıdır,
dolayısıyla her zaman düzgün sırada çalıştırılırlar.
Migrasyonlar
248
Laravel sizin ön tanımlı veritabanı bağlantınızdaki başka bir tablo içinde, daha önce hangi migrasyonların çalıştığının kaydını tutar. Bu yolla, sadece eklenmiş olan migrasyonları çalıştıracaktır.
Migrasyonları kullanarak, siz ve ekibiniz her zaman tutarlı ve kararlı bir durumda aynı veritabanı
yapısına sahip olacaksınız. Biliyor musunuz? Eylem yapmak konuşmaktan daha iyi ifade eder. Gelin
bir migrasyon oluşturalım ve öğrenme sürecine başlayalım.
Migrasyonların Oluşturulması
Bir migrasyon oluşturmak için Artisan komut satırı arayüzünü kullanmamız gerekiyor. Gidin bir
terminal penceresi açın ve proje klasörünüze geçin. Önceki bölümde şema inşa etmeyi öğrenmiştik
ve orada şemaları kullanmak için daha iyi bir yer olduğunu söylemiştim. Tabii ki, migrasyonlardan
söz ediyordum. Users tablomuzu oluşturmak için kullandığımız şema yapısını bir daha oluşturalım
şimdi. Bir create_users migrasyonu inşa etmek için Artisan kullanmaya başlayacağız.
1
2
3
4
$ php artisan migrate:make create_users
Created Migration: 2013_06_30_124846_create_users
Generating optimized class loader
Compiling common classes
Artisan migrate:make komutunu çağırdık ve yeni migrasyonumuzun adını geçtik. Laravel app/database/migration
klasörü içinde yeni bir migrasyon şablonu üretmiş olacaktır. Bu şablon, migrate:make komutuna
geçtiğiniz parametre ve bir zaman damgasıyla birlikte adlandırılan bir dosyanın içerisinde olacaktır.
Bu örneğimiz için şablonumuz aşağıdaki dosyada yer alacaktır.
1
app/database/migrations/2013_06_30_124846_create_users.php
Şimdi metin editörümüzde bu dosyayı açalım ve içinde ne olduğuna bakalım.
1
<?php
2
3
use Illuminate\Database\Migrations\Migration;
4
5
class CreateUsers extends Migration {
6
7
8
9
10
11
12
/**
* Run the migrations.
*
* @return void
*/
public function up()
249
Migrasyonlar
{
13
//
14
}
15
16
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
17
18
19
20
21
22
23
24
25
26
27
}
Burada bizim migrasyon sınıfımız var. Migrasyonları oluşturmak için her zaman Artisan komutu
kullanmanızın önemi burada, zaman damgalarını ve dolayısıyla veritabanı yapınızın geçmişini riske
etmek istemezsiniz. İyi bir okuyucu olun, komut kullanın.
Migrasyon sınıfının içinde iki adet publik metodumuz, up() ve down() bulunuyor. Bu iki metod arasında bir çizgi hayal edin veya dostumuz Barney’nin hayal gücünü öğrenmediyseniz bir yorumunun
içine bir şeyler yazın.
Bu çizginin hangi tarafına bakarsanız, diğerinin tam tersi olacak. up() metodunda ne yaparsanız,
down() metodunda geri almalısınız. Görüyorsunuz, migrasyonlar iki yönlüdür. Biz veritabanımızın
yapısını veya içeriğini güncellemek için bir migrasyon kullanabiliriz ama aynı zamanda onu tekrar
orijinal durumuna döndürmek için de migrasyon kullanabiliriz.
Öncelikle up() metodunu dolduralım.
1
<?php
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function($table)
{
$table->increments('id');
$table->string('name', 128);
250
Migrasyonlar
$table->string('email');
$table->string('password', 60);
$table->timestamps();
14
15
16
});
17
18
}
Umarım bu şema oluşturma kodunda kafanızı karıştıran bir şey yoktur. Eğer anlamadığınız bir şey
varsa ‘Şema Oluşturucusu’ bölümüne bir daha bakabilirsiniz.
Peki, biliyoruz ki, up’da ne varsa, down’da da o gelecek. Bu sebeple, down() metodunu alalım ve
up() metodundaki yapı değişikliğinin tersini oluşturalım.
İşte yapıyoruz…
1
<?php
2
3
4
5
6
7
8
9
10
11
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('users');
}
Tamam, tamam, sanırım tam tersi değil. Ben önce sütunları tek tek düşürüp, sonra da tabloyu düşüreceğini sanmıştım. İyi de, görüyorsun, ikisi de aynı sonucu verecektir. users tablosu düşürülmüş
olacaktır. Öyleyse bunu niye tek satırda yapmayalım ki?
Sonraki kesime geçmeden önce, migrasyon oluşturulmasıyla ilgili birkaç püf noktasını görelim.
migrate:make komutunda --create ve --table anahtarlarını kullanarak, bir tablo oluşturma
kodunu otomatik olarak oluşturabiliyoruz.
Sadece çalıştıralım…
1
php artisan migrate:make create_users --create --table=users
…ve aşağıdaki migrasyon kodunu alalım.
Migrasyonlar
1
251
<?php
2
3
4
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
5
6
class CreateUsers extends Migration {
7
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function(Blueprint $table)
{
$table->increments('id');
$table->timestamps();
});
}
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('users');
}
22
23
24
25
26
27
28
29
30
31
32
}
Mükemmel! Bu kısayol bize büyük bir zaman kazandırdı. Yeni tablomuz için Schema::create()
ve Schema::drop() metodlarının eklenmesi yanında, Laravel increments() ve timestamps() metodları da eklemiştir. Bu, Eloquent ORM uyumlu modellerin çok hızlı bir şekilde oluşturulmasını
sağlamaktadır. Şimdilik Eloquent konusunu çok fazla dert etmeyin, yakın bir zamanda onu da
keşfedeceğiz.
Migrasyonların oluşturulmasında son bir püf noktası onların ön tanımlı app/database/migrations
dizininden farklı bir konuma nasıl koyulabileceğidir. Migrasyon sınıfımız için yeni bir konum
tanımlamak için --path anahtarını kullanabiliriz.
Migrasyonlar
1
2
3
4
252
$ php artisan migrate:make create_users --path=app/migs
Created Migration: 2013_06_30_155341_create_users
Generating optimized class loader
Compiling common classes
Şimdi migrasyonumuz projemizin köküne göre app/migs dizini içerisinde oluşturulacaktır. Bununla
birlikte, migrasyonlarınızı çalıştırdığınızda Artisan ön tanımlı olarak bu yeni konuma bakmayacaktır, bu nedenle migrasyonlarınızı nerede bulacağını bilmesini sağlamalısınız. Bir sonraki kesimde bu
konu üzerinde daha çok bilgi öğreneceğiz.
Migrasyonların Çalıştırılması
Tüm bu çabaları yeni migrasyonumuzu oluşturmak için verdik, onu çalıştıramadığımız takdirde
büyük bir utanç olacaktır. Veritabanını migrasyonları kullanmaya hazırlayalım. Hatırlarsınız,
Laravel’in migrasyonların durumunu kayıt altına almak için bir veritabanı tablosu kullandığını
söylemiştim? Tamam işte, öncelikle o tabloyu oluşturmamız gerekiyor.
Migrasyonlar tablosuna istediğiniz ismi verebilirsiniz. Bu tablo adı için yapılandırma yeri app/config/database.php
içerisindedir.
1
2
3
4
5
6
7
8
9
10
/*
|-------------------------------------------------------------------------| Migration Repository Table
|-------------------------------------------------------------------------|
| This table keeps track of all the migrations that have already run for
| your application. Using this information, we can determine which of
| the migrations on disk have not actually be run in the databases.
|
*/
11
12
'migrations' => 'migrations',
Dizinin migrations anahtarında mantıklı bir ön tanım sağlanmıştır. Sizin migrations anahtarının
değerini migrasyon durumunuzu izlemek için kullanmak istediğiniz tablonun adıyla değiştirmeniz
yeterlidir.
Başka bir Artisan komutunu çalıştırarak migrations tablomuzu yükleyebiliriz. Şimdi install
komutunu çalıştıralım.
Migrasyonlar
1
2
253
$ php artisan migrate:install
Migration table created successfully.
Bakın şimdi veritabanımızı incelersek ve oluşturulmuş olanı görmek için migrations tablosuna
bakarsak.
1
2
3
4
5
6
7
8
mysql> describe migrations;
+-----------+--------------+------+-----+---------+-------+
| Field
| Type
| Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| migration | varchar(255) | NO
|
| NULL
|
|
| batch
| int(11)
| NO
|
| NULL
|
|
+-----------+--------------+------+-----+---------+-------+
2 rows in set (0.01 sec)
İki alanı olan yeni bir tablo oluşturulmuş. Migrasyonlar tablosunun işleriyle kendinizi yormayın,
size düşen sadece onu oluşturmak ve migrasyon sisteminizi yüklemektir.
Tamam, size bir yalan daha söyledim. Bu neden devam edip duruyor bilmiyorum? Belki de
bir psikiyatriste falan gitmem lazım. Her neyse, migrasyonlar tablosunu yüklemeniz gerektiğini
söylemiştim ya, yalan söyledim.
Göreceğiniz gibi, migrasyonları çalıştırdığınızda bu tablo mevcut değilse Laravel onu bizim için
otomatik olarak oluşturacaktır. Sizin için migrasyonlar sistemini yükleyecektir ama şimdi, en
azından migrate:install komutunu öğrenmiş oldunuz, değil mi? Tüm bu aldatmacayı sanki ben
planlamışım gibi…
Pekala, haydi başlayalım ve migrasyonumuzu ilk kez çalıştıralım. Bunun için migrate komutunu
kullanabiliriz.
1
2
$ php artisan migrate
Migrated: 2013_06_30_124846_create_users
Komutun çıktısı çalıştırılmış olan migrasyonların bir listesidir. Bizim ‘users’ tablomuz oluşturulmuş
mu oluşturulmamış mı görmek için veritabanımıza bir bakalım.
Migrasyonlar
1
2
3
4
5
6
7
8
9
10
11
12
254
mysql> describe users;
+------------+------------------+
| Field
| Type
|
+------------+------------------+
| id
| int(10) unsigned |
| name
| varchar(128)
|
| email
| varchar(255)
|
| password
| varchar(60)
|
| created_at | timestamp
|
| updated_at | timestamp
|
+------------+------------------+
6 rows in set (0.01 sec)
Kitap biçimlendirmesine uygun olması için tabloyu bir miktar kısalttım fakat görüyoruz ki ‘users’
tablomuz düzgün bir şekilde oluşturulmuş. Müthiş!
Gelin şimdi users tablomuza bir ‘title’ sütunu ekleyelim. Önceden yapmış olduğunuz migrasyonu
açarak, ilgili şemayı bu yeni sütunu içerecek şekilde güncellemek size cazip gelmiş olabilir. Bunu
yapmayın lütfen.
Biliyorsunuz, eğer takım arkadaşlarınızdan birisi proje üzerinde çalışıyorsa ve bizim ilk migrasyonumuzu zaten çalıştırmış ise, bu durumda bizim değişikliğimizi alamayacak ve veritabanımız farklı
durumlarda olacaktır.
Bunun yerine, en iyisi biz veritabanımızı değiştirmek için yeni bir migrasyon oluşturalım. Haydi
yapıyoruz.
1
2
3
4
$ php artisan migrate:make add_title_to_users
Created Migration: 2013_06_30_151627_add_title_to_users
Generating optimized class loader
Compiling common classes
Yeni migrasyona tanımlayıcı bir isim verdiğim dikkatinizi çekecektir, siz de bu deseni izlemelisiniz.
Şimdi up() metodu içerisinde users tablomuzun şemasını title sütunu ekleyecek şekilde değiştirelim.
255
Migrasyonlar
1
<?php
2
3
use Illuminate\Database\Migrations\Migration;
4
5
class AddTitleToUsers extends Migration {
6
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function($table)
{
$table->string('title');
});
}
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
20
21
22
23
24
25
26
27
28
29
30
}
Harika, ‘users’ tablomuza ihtiyacımız olan sütunu ekleyecek. Şimdi birlikte söyleyelim.
up’ta ne gelirse, down’da o gidecektir.
Haklısınız, bu migrasyon sınıfına down() metodu sağlamamız gerekiyor. Tablomuzu ‘title’ sütununu
çıkartacak şekilde değiştirelim şimdi.
Migrasyonlar
1
256
<?php
2
3
use Illuminate\Database\Migrations\Migration;
4
5
class AddTitleToUsers extends Migration {
6
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function($table)
{
$table->string('title');
});
}
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function($table)
{
$table->dropColumn('title');
});
}
20
21
22
23
24
25
26
27
28
29
30
31
32
33
}
Mükemmel, artık Laravel bizim migrasyonumuzu çalıştırabilecek ve gerekirse tüm değişiklikleri geri
alabilecek durumda. Migrasyonlarımızı tekrar çalıştıralım.
1
2
$ php artisan migrate
Migrated: 2013_06_30_151627_add_title_to_users
Laravel zaten çalıştırmış olduğumuz önceki migrasyonlarımızı bilir ve dolayısıyla sadece son
migrasyon sınıfımızı çalıştırır. Şimdi tablomuzu bir kez daha inceleyelim.
Migrasyonlar
1
2
3
4
5
6
7
8
9
10
11
12
13
257
mysql> describe users;
+------------+------------------+
| Field
| Type
|
+------------+------------------+
| id
| int(10) unsigned |
| isim
| varchar(128)
|
| email
| varchar(255)
|
| password
| varchar(60)
|
| created_at | timestamp
|
| updated_at | timestamp
|
| title
| varchar(255)
|
+------------+------------------+
7 rows in set (0.00 sec)
Görebileceğiniz gibi, yeni sütunumuz users tablosuna eklenmiştir. Eğer migrasyonumuz tamamlanır
ve ekibin geri kalanı ile paylaşılırsa, onlar kendi veritabanlarını yeni yapıya uygun olarak almak için
sadece migrate komutunu çalıştırabileceklerdir.
Bir sebeple mevcut migrasyon dosyalarımızın birini değiştirmemiz gerekirse, tüm migrasyonları geri
çevirmek, ondan sonra da onları bir kez daha çalıştırmak için migrate:refresh Artisan komutunu
kullanabiliriz. Şimdi bunu users tablomuzda deneyelim.
1
2
3
4
5
6
$ php artisan migrate:refresh
Rolled back: 2013_06_30_151627_add_title_to_users
Rolled back: 2013_06_30_124846_create_users
Nothing to rollback.
Migrated: 2013_06_30_124846_create_users
Migrated: 2013_06_30_151627_add_title_to_users
Migrasyonlarımız doğru bir sırada down() metodları kullanılarak geri alınmıştır ve sonra da kendi
up() metodları kullanılarak tekrar çalıştırılmıştır. Veritabanımız bir kez daha ideal durumuna
gelmiştir.
Önceki kesimde, migrasyonlarımızı dosya sisteminde yeni bir konuma yazmak için --path anahtarı
kullandığımızı hatırladınız mı? Evet, onları nasıl çalıştıracağınızı göstereceğime söz vermiştim.
Arada bir yalan söyleyebilirim ama verdiğim sözden asla dönmem. Gelin şimdi bu standart dışı
migrasyonlarımızı nasıl çalıştırabileceğimize bir bakalım.
1
2
$ php artisan migrate --path=app/migs
Migrated: 2013_06_30_155341_create_users
Bakın, çok kolay? Migrasyonlarımızı depolayacağımız konumu uygulama köküne göreli belirtmek
için yine sadece --path anahtarını kullanıyoruz.
Migrasyonların iki yönlü olduklarını söyledim, dolayısıyla onları geri alabilmemiz gerekmez mi?
Alalım öyleyse.
258
Migrasyonlar
Geri Alma (Rolling Back)
Dönüyor, dönüyor, nehirde dönüyooor…
Bunun için özgünüm, biraz dağıldım galiba. Şimdi şeyleri… ah evet! Migrasyonların geri alınmasını
görelim. Migrasyonlarımızı çalıştırmak için migrate kullanabileceğimizi biliyoruz, ama onları nasıl
geri alacağız?
Peki, takım arkadaşlarımızın birinin migrasyonuna dayalı olarak veritabanımızı yeniden yapılandırmak için migrate komutunu kullanmış olduğumuzu varsayalım. Ne yazık ki, bizim arkadaşın şema
değişikliği bizim kodumuzun bir kısmını göçürttü ve uygulamamızı da göçük bıraktı.
Takım arkadaşımızın yapmış olduğu değişiklikleri geri almamız gerekiyor. Bunu yapmak için
rollback komutunu kullanabiliriz. Bunu bir deneyelim mi?
1
2
$ php artisan migrate:rollback
Rolled back: 2013_06_30_151627_add_title_to_users
rollback komutunu kullandığımızda, Laravel sadece bizim migrate kullanarak en son çalıştırmış
olduğumuz migrasyonları geri alır. Son defa çalıştırdığımız migrate asla olmamış gibi olur.
Eğer bütün migrasyonları geri almak istiyorsak, reset komutunu kullanabiliriz.
1
2
3
4
$ php artisan migrate:reset
Rolled back: 2013_06_30_151627_add_title_to_users
Rolled back: 2013_06_30_124846_create_users
Nothing to rollback.
Bu reset komutunun bizim ‘migrations’ tablomuzu kaldırmayacağına dikkat ediniz.
Migrasyon Püf Noktaları
Aa, daha fazlasını yapmak ister misiniz? Anlıyorum. Tamam, merak etmeyin, geri almayla ilgili
değil. Migrasyonlar sisteminin birkaç özelliğini daha öğrenmeye ne dersiniz?
Veritabanı yapılandırma dosyası app/config/database.phpde keşfettiğimiz connections dizisini
hatırlıyor musun? Herhangi bir migrasyon komutuna --database anahtarı eklemek suretiyle
migrasyonlarımızı başka bir bağlantıda gerçekleştirebiliriz, mesela böyle:
259
Migrasyonlar
1
2
3
$ php artisan migrate --database=mysql
Migrated: 2013_06_30_124846_create_users
Migrated: 2013_06_30_151627_add_title_to_users
Şimdi bizim migrasyonlarımız yapılandırma dosyası içindeki mysql takma adını verdiğimiz bağlantıda yapılacaktır.
Hım… Bakıyorum hala etkilenmiş değilsiniz? Peki o zaman, sizin için bir püf noktam daha var.
Zaman zaman seni şımarttığımı düşünüyorum ama itiraf etmeliyim. Siz harika bir dinleyicisiniz.
Migrasyonlarımızı veritabanını değiştirmeksizin çalıştırabiliyoruz ve migrasyonlarımızın sonucu
olarak amaçlanan SQL sorgularını görebiliyoruz. Bu sayede, sonraki migrasyonumuzun ne yapacağını görmek için, veritabanımıza herhangi bir hasar riski vermeksizin bir kontrol yapabiliyoruz.
Bu, hata ayıklama işleri için gerçekten yararlıdır.
Bir migrasyon komutunun, hedeflediği SQL sonucunu görmek için sadece --pretend anahtarını
ekleyin. İşte bir örnek.
1
2
3
4
5
6
7
$ php artisan migrate --pretend
CreateUsers: create table `users` (`id` int unsigned not null
auto_increment primary key, `name` varchar(128) not null,
`email` varchar(255) not null, `password` varchar(60) not null,
`created_at` timestamp default 0 not null, `updated_at` timestamp
default 0 not null) default character set utf8 collate utf8_unicode_ci
AddTitleToUsers: alter table `users` add `title` varchar(255) not null
Şimdi biz, --pretend anahtarı eklenmeseydi veritabanımızda çalıştırılacak olan sorguların neler
olduğunu görebiliyoruz. Güzel bir püf noktası, değil mi?
Evet, beni şımar…
Size söylemiştim!
Bir sonraki bölümde Eloquent ORM’ye göz atıyor olacağız. Eloquent, veritabanı satırlarınızı nesne
yönelimli programlamaya güzelce uyum sağlamaları için PHP nesneleri olarak temsil etmenin
harika bir yöntemidir.
Eloquent ORM
Veritabanımızı nasıl yapılandırabileceğimizi ve veritabanımız içerinde tablolar oluşturmak için
şema oluşturucusunu nasıl kullanabileceğimizi öğrendik ama işin asıl kısmına şimdi geldik ve
veritabanında nasıl bilgi saklayacağımızı öğreneceğiz.
Laravel’in veritabanı bileşenleriyle daha önce karşılaşmış olanlardan bir kısmınız, hatta Laravel 3
kullanmakta olanlardan biri olsanız dahi neden ORM ile başlamayı tercih ettiğimi merak ediyor
olabilirsiniz? Neden önce SQL cümleleri ve sonra da sorgu inşa edilmesi ile başlamıyorum?
Peki, bir adım geri atalım ve neden burada olduğumuzu düşünelim. Siz bir geliştiricisiniz, aslında bir
PHP geliştiricisi! Bu kitabı okuduğunuza göre bir PHP 5+ geliştiricisi olduğunuzu ve nesne yönelimli
geliştirmeyi benimseyeceğinizi umuyorum.
Uygulamamızdaki varlıkları nesneler olarak tanımlarsak, onları nesneler olarak saklamak, nesneler
olarak elde etmek ve daha birçok şey anlamlı olacaktır.
İsterseniz bir online kitap mağazası yazdığımızı düşünelim.
Nesne yönelimli uygulama tasarımı bize uygulamamız içerisinde nesneler tanımlamamız gerektiğini
öğretti. Peki, bir kitap mağazası kitaplar olmadan çok başarılı olamayacaktır, değil mi? Bu yüzden,
uygulamamız tarafından kullanılan her bir kitabı temsil edecek bir kitap nesnesi istememiz makul
bir değişiklik olacaktır. Normalde, bu uygulama nesneleri uygulamamızın iş modeli kısmını temsil
ettikleri için bunları ‘Modeller’ olarak ifade edeceğiz. İşte bir örnek.
1
<?php
2
3
4
5
6
7
8
9
10
class Book
{
/**
* Kitabımızın adı.
*
* @var string
*/
public $name;
11
12
13
14
15
16
17
/**
* Kitabımız için bir açıklama.
*
* @var string
*/
public $description;
Eloquent ORM
18
261
}
19
20
21
22
23
$book = new Book;
$book->name = 'Şeker Portakalı';
$book->description = 'Fakir bir aile çocuğu olan Zeze\'nin yaşadığı olayları anla\
tan kitap!';
Harika!
Benim kişisel favorilerimden biri olan José Mauro De Vasconcelos’un ‘Şeker Portakalı’ kitabını temsil
eden bir kitap oluşturduk! Şimdi bu kitabı veritabanımızda saklayalım. Şema oluşturucusunu kullanmış olduğumuzu ve gerekli tüm sütunlarıyla birlikte bir ‘books’ tablosu oluşturmuş olduğumuzu
var sayıyoruz.
Önce bir SQL sorgusu oluşturmamız gerekecek. Şimdi sizin, güvenlik önlemleri için ‘prepared’ bir
sorgu oluşturmak istediğinizi biliyorum fakat örneği basit tutmak istiyorum. Bunu yapacaksınız…
1
<?php
2
3
4
5
6
7
8
9
10
$query = "
INSERT INTO
books
VALUES (
'{$book->name}',
'{$book->description}'
);
";
Nesnemizi veritabanına eklemek için bir SQL sorgusu oluşturduk. Bu sorgu normalde hangi
veritabanı adaptörü kullanıyorsak o kullanılarak çalıştırılacaktır.
Veritabanında sadece veri saklamak için bir SQL sorgu stringi oluşturmak zorunda olmanın gerçek
bir utanç olduğunu düşünüyorum. Eğer saklamak için onu bir stringe dönüştüreceksek, neden ilk
etapta nesne oluşturma zahmetine giriyoruz ki? Veritabanından onu elde ederken de yine aynı
şekilde nesne inşa etmek zorunda kalacağız. Bana sorarsanız bu büyük bir zaman kaybı…
Bu çirkin SQL sorguları oluşturma zorunluğu olmaksızın nesnelerimizi doğrudan veritabanına
‘atmanın’ mümkün olması gerektiğini düşünüyorum. Hmm, belki böyle bir şey vardır?
262
Eloquent ORM
1
<?php
2
3
4
5
6
7
$book = new Book;
$book->name = 'Şeker Portakalı';
$book->description = 'Fakir bir aile çocuğu olan Zeze\'nin yaşadığı olayları anla\
tan kitap!';
$book->save();
Bu save() metodu işin SQL tarafını bizim için halletse. Nesneler veritabanında kalıcı olsa. Bu harika
olurdu! Birisi bu fikrimi hayata geçirmeli.
Bu zaten yapılmış bulunuyor dostum.
Ne, gerçekten mi? Utandım… Bana şöhret ve servet getirecek bir fikir olduğunu düşünmüştüm. Peki,
sanırım gerçekten iyi bir şeydir.
Ah evet, şimdi hatırladım. Bu işlevsellik kısaca ‘ORM’ler denen nesne ilişkili eşleştiriciler (object relational mappers) tarafından sağlanmıştır. ORMler bize uygulama nesnelerimizi veritabanı
tablolarına ve bu nesnelerin tek tek olgularını satırlar olarak eşleştirme imkanı vermek için
kullanılmaktadırlar. Bu nesnelerin sınıf niteliklerini tablonun sütunları olarak düşünebilirsiniz.
ORM nesne elde edilmesi ve sürdürülmesi işini bizim adımıza üstlenecektir ve biz tek bir satır SQL
yazmak zorunda kalmayacağız. Bu büyük bir haber, zira ben SQL’e tahammül edemiyorum! Çirkin
ve can sıkıcı. Nesneler çok daha eğlenceli, değil mi?
Birçok ORM birden çok nesne türü arasındaki ilişkileri yönetme yeteneği de sunmaktadır. Örneğin
kitaplar ve yazarlar. Yazarlar ve yayıncılar.
Laravel ‘Eloquent’ adındaki kendi ORM bileşeniyle birlikte gelmektedir. Adının Eloquent (etkili) olması çok doğrudur. Sözdizimi oldukça güzeldir ve uygulamanızın veritabanı katmanıyla etkileşimini
bir angarya yerine hoş bir deneyim haline getirir.
Bir depolama katmanı düşündüğümde aklıma CRUD kelimesi gelir. Hayır bu sefer SQL’i sevmediğimden bahsetmiyorum, onun yerine depolama katmanı üzerinden yapılabilecek eylemlerden söz
ediyorum.
•
•
•
•
C - Create yeni bir satır OLUŞTUR.
R - Read mevcut satırları OKU.
U - Update mevcut satırları GÜNCELLE.
D - Delete mevcut satırları SİL.
Bu eylemleri sırasıyla ele alarak Eloquent hakkında daha fazla bilgi öğrenebiliriz. Eloquent model
olgularının oluşturulmasıyla başlayacağız.
Eloquent ORM
263
Yeni Modellerin Oluşturulması
İlk Eloquent modelimizi oluşturmadan önce ilginç bir veri konusu örneği gerekiyor. Hım… Yeni bir
oyun bilgisayarı topladım, bu yüzden video oyunlarıyla gidelim. Video oyunlarını temsil edecek
bazı nesneler oluşturabiliriz, ama öncelikle bir tablo şeması oluşturmamız gerekiyor.
‘games’ tablomuz için şema inşa etmek amacıyla yeni bir migrasyon oluşturacağız.
1
<?php
2
3
// app/database/migrations/2013_07_10_213946_create_games.php
4
5
use Illuminate\Database\Migrations\Migration;
6
7
class CreateGames extends Migration {
8
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('games', function($table)
{
$table->increments('id');
$table->string('name', 128);
$table->text('description');
});
}
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('games');
}
24
25
26
27
28
29
30
31
32
33
34
}
Eloquent ORM
264
Umarım bu örnek kod için bir açıklama yapmaya gerek yok. Bu örneğin içinde kafanızı karıştıran
bir şey varsa, şema oluşturucusu bölümüne bir kez daha bakın.
Tablomuza games adını vermiş olduğumuz dikkatinizi çekecektir. Bunun nedeni Eloquent modelimize Game adını vermek istememizdir. Eloquent zekidir, ön tanımlı olarak bizim nesnelerimizin
olgularını saklamak için kullanılacak tablo olarak model adının çoğulunu arayacaktır. (Çevirenin
notu: İngilizce çoğulunu arayacaktır. Bu nedenle tablo adını oyunlar olarak değil games olarak
verdik.) Bu davranış gözardı edilip değiştirilebilir fakat şimdilik işleri basit tutalım.
Eloquent modellerinin temel gereksinimleri vardır. Bir modelin id adında otomatik artan bir
sütunu olmalıdır. Bu, tablo içerisindeki tek bir satırı tanımlamak için kullanılabilecek benzersiz bir
primer indekstir. increments() metodunu kullanmak suretiyle bu sütunu tablo yapısına kolaylıkla
ekleyebilirsiniz.
Veritabanımızı güncellemek için migrasyonu çalıştıralım.
1
2
$ php artisan migrate
Migrated: 2013_07_10_213946_create_games
Artık başlayabiliriz. Bizim oyunları temsil edecek yeni bir Eloquent modeli oluşturalım.
1
<?php
2
3
// app/models/Game.php
4
5
6
class Game extends Eloquent
{
7
8
}
Burada bizim oyunları temsil etmekte kullanabilceğimiz tam bir Eloquent modeli var. Şaşırdınız mı?
Evet biraz az görünüyor ama bu gerçekten iyi bir şey. Diğer birçok ORM, veritabanı şemanızın bir
XML eşleştirmesini yapmanızı isteyecek veya nesneyi temsil eden veritabanı tablo sütunlarının her
birisi için ek açıklamalar oluşturmanızı isteyecektir. Eloquent bazı mantıklı varsayımlar yaptığı için
bunu yapmanıza gerek yoktur.
Hadi şimdi yeni bir oyun oluşturalım.
Eloquent ORM
1
265
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', function()
{
$game = new Game;
$game->name = 'Assassins Creed';
$game->description = 'Assassins VS templars.';
$game->save();
});
Hey, çok bildik görünüyor. İlk etapta nesnelerimizi kalıcı yapmak istememiş miydik? Bu temiz
ve basit oldu. Bizim ‘Game’ modelimizin yeni bir olgusunu oluşturuyoruz ve bu olgunun ilgili
tablo sütunlarına eşleştirilen public niteliklerine gerekli değerleri ayarlıyoruz. Bunları yaptıktan
sonra, yeni satırı veritabanında kalıcı hale getirmek için nesne üzerinde sadece save() metodunu
çağırıyoruz.
En iyisi biz / URI’sini ziyaret edelim. Rota mantığımızda sorgu çalıştırılacağı ve bir şey döndürülmeyeceği için herhangi bir cevap almayı beklemiyoruz. Buna karşın oldukça farklı bir şey alıyoruz.
Bir hata ekranı alıyoruz. Güzel bir hata ekranı. Gerçekten güzel bir hata ekranı! Temacının biraz
ciddi becerileri olması gerekirdi değil mi? Heh… Her neyse, bu hata nedir ki?
1
2
3
4
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'updated_at' in 'field lis\
t' (SQL: insert into `games` (`name`, `description`, `updated_at`, `created_at`) \
values (?, ?, ?, ?)) (Bindings: array ( 0 => 'Assassins Creed', 1 => 'Assassins V\
S templars.', 2 => '2013-07-14 16:30:55', 3 => '2013-07-14 16:30:55', ))
Eloquent yeni modelimizi oluştururken tablomuzun updated_at ve created_at sütunlarını şimdiki
zaman ile doldurmaya çalışır. Bunun sebebi bizden tablo şemasını inşa ederken ->timestamps()
metodu eklemiş olmamızı beklemesidir. Oluşturma/güncelleme zamanlarının kayda alınması kimseye zarar vermediğine göre, bu mantıklı bir ön tanımdır. Bununla birlikte, daha önceden mevcut bir
veritabanı tablosu kullanıyorsanız veya basitçe veritabanı tablonuzda zaman damgası sütunlarının
bulunmasını istemiyorsanız, bu işlevselliği devre dışı bırakmak isteyebilirsiniz.
Eloquent modellerinin otomatik zaman damgası güncellemelerini devre dışı bırakmak için tek
yapacağınız şey modelinize yeni bir public nitelik eklemektir.
Eloquent ORM
1
266
<?php
2
3
// app/models/Game.php
4
5
6
7
8
class Game extends Eloquent
{
public $timestamps = false;
}
Public $timestamps niteliği Eloquent taban sınıfından miras alınmıştır. Bu nitelik otomatik zaman
damgası işlevselliğini etkinleştirmek veya devre dışı bırakmak için kullanılabilen boolean bir
değerdir. Yukarıdaki örnekte biz onu false olarak ayarlıyoruz, böylece zaman damgalarını devre
dışı bırakmak istediğimizi Eloquent’in bilmesini sağlıyoruz.
Şimdi / URI’sini bir kez daha ziyaret edelim. Bu sefer sayfa boş bir sonuç gösterir. Panik yapmayın,
bunun nedeni sadece rota mantığımızdan bir sonuç döndürmemiş olmamızdır. Herhangi bir hata
mesajı almadık, demek ki SQL sorgumuz çalıştırılmış olmalı. Sonucu görmek için games tablomuzu
inceleyelim.
1
2
3
4
5
6
7
8
9
mysql> use myapp;
Database changed
mysql> select * from games;
+----+-----------------+------------------------+
| id | name
| description
|
+----+-----------------+------------------------+
| 1 | Assassins Creed | Assassins VS templars. |
+----+-----------------+------------------------+
1 row in set (0.00 sec)
Yeni satırımızın düzgün biçimde eklenmiş olduğunu görebiliyoruz. Güzel! Tek bir satır SQL yazmadan yeni bir kayıt eklemiş olduk. Zaferimizi kutlayabiliriz.
Nesnemiz için bir id değeri belirtmek zorunda olmadığımızı fark etmişsinizdir. Bu id sütunu
otomatik olarak artırılır, bu yüzden veritabanı katmanı satırlarımızın numarasını bizim için işleyecektir. Bir Eloquent modelinde id sütununu değiştirmek genel olarak kötü bir fikirdir. Gerçekten
ne yaptığınızı bilmiyorsanız bunu yapmaya kalkışmayın.
Otomatik zaman damgalarını devre dışı bırakmak için $timestamps niteliğini kullanmıştık. Gelin
şimdi bunları etkinleştirdiğimiz zaman ne olacağını bir görelim. Öncelikle veritabanı şemamızı
değiştirmemiz gerekiyor. Veritabanı şemasını elle değiştirmek de kötü bir fikirdir, mevcut migrasyonları güncellemek de. Bunun nedeni bizim veritabanı durumumuzun takım arkadaşlarımızla ‘outof-sync’ (eşitlenmemiş) hale gelebilmesidir. Bunun yerine, takım arkadaşlarımızın bizim yaptığımız
değişiklikleri de çalıştırabilmeleri için yeni bir migrasyon oluşturalım.
Eloquent ORM
1
2
267
$ php artisan migrate:make add_timestamps_to_games
Created Migration: 2013_07_14_165416_add_timestamps_to_games
Migrasyonumuz oluşturuldu. Migrasyonumuza bu migrasyondaki amacımızı gösteren bir isim
verdiğim dikkatinizi çekecektir. İleride bu migrasyonu çalıştıracak takım arkadaşlarınız için bu çok
yararlı bir bilgi olacaktır. Oyun tablomuza zaman damgaları eklemek için şema oluşturucusunu
kullanalım.
1
<?php
2
3
// app/database/migrations/2013_07_14_165416_add_timestamps_to_games.php
4
5
use Illuminate\Database\Migrations\Migration;
6
7
class AddTimestampsToGames extends Migration {
8
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('games', function($tablo)
{
$tablo->timestamps();
});
}
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
22
23
24
25
26
27
28
29
30
31
32
}
‘games’ tablomuzu değiştirmek için Schema::table() ve zaman damgaları sütunlarını otomatik
268
Eloquent ORM
olarak eklemek için timestamps() metodunu kullandık. Şimdi hanımefendiler, beyefendiler, birlikte
söyleyelim.
up’ta ne gelirse, down’da o gidecektir!
Gerçekten çabuk öğreniyorsunuz! Büyük iş. Şimdi, down() metodu içerisinde tablodan zaman
damgası sütunlarını çıkartalım.
1
<?php
2
3
// app/database/migrations/2013_07_14_165416_add_timestamps_to_games.php
4
5
use Illuminate\Database\Migrations\Migration;
6
7
class AddTimestampsToGames extends Migration {
8
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('games', function($tablo)
{
$tablo->timestamps();
});
}
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('games', function($tablo)
{
$tablo->dropColumn('updated_at', 'created_at');
});
}
22
23
24
25
26
27
28
29
30
31
32
33
34
35
}
Eloquent ORM
269
down() metodu içerisinde, tablodan updated_at ve created_at sütunlarını kaldırmak için dropColumn()
şema oluşturucusu metodunu kullandık. Ben bunun için güzel bir dropTimestamps() metodu
olabileceğini düşünmüştüm ama göründüğü kadarıyla yok. Problem değil! Bu açık kaynak bir proje,
dolayısıyla ileride bir fırsatını bulduğumda bir çekme isteğinde bulunmam yeterli olur. İma et, ima
et…
Yeni sütunları ‘games’ tablomuza eklemek için yeni migrasyonumuzu çalıştıralım.
1
2
$ php artisan migrate
Migrated: 2013_07_14_165416_add_timestamps_to_games
Şimdi bir seçeneğimiz var, ya modelimiz içinde $timestamps niteliğini true’ya ayarlayabiliriz,
böylece otomatik zaman damgalama özelliği etkinleştirilmiş olur.
1
<?php
2
3
// app/models/Game.php
4
5
6
7
8
class Game extends Eloquent
{
public $timestamps = true;
}
Veya… o niteliği sadece kaldırabiliriz. Bunun nedeni ebeveyn Eloquent modelinde $timestamps
niteliğinin ön tanımlı değerinin true olmasıdır. Bunun değeri bizim model içine kalıtılmış olacaktır.
1
<?php
2
3
// app/models/Game.php
4
5
6
class Game extends Eloquent
{
7
8
}
Mükemmel, Artık yeni bir satır eklemek için tekrar / URI’sini çalıştırabiliriz. Sonucu görmek için
veritabanındaki ‘games‘ tablosunu inceleyeceğiz.
Eloquent ORM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
270
mysql> use myapp;
Database changed
mysql> select * from games;
+----+-----------------+------------------------+---------------------+----------\
-----------+
| id | name
| description
| created_at
| updated_a\
t
|
+----+-----------------+------------------------+---------------------+----------\
-----------+
| 1 | Assassins Creed | Assassins VS templars. | 2013-07-14 17:14:13 | 2013-07-1\
4 17:14:13 |
+----+-----------------+------------------------+---------------------+----------\
-----------+
1 row in set (0.00 sec)
Görebileceğiniz gibi, created_at ve updated_at sütunları bizim için şimdiki zaman damgası ile
doldurulmuştur. Bu büyük bir zaman kazancı! Uygulamanız kullanıcılarına uygulamanızı kullanma
yıl dönümlerini hatırlatmak isteyebilirsiniz ve bu amaçla şimdiki tarihle created_at sütununu
karşılaştırabilirsiniz.
Sonraki kesime geçmeden önce sizinle küçük bir püf noktasını paylaşmak istiyorum. Eğer veritabanı
tablonuzun isminin modelinizin isminin çoğul hali olmasını istemezseniz, bu durumda Eloquent’in
bunu bilmesini sağlamanız gerekecektir. Modelinize $table public niteliğini ekleyiniz ve bunun
değerini tablonuzun adı olan stringe ayarlayınız. Eloquent gelecekte bu modelle ilgili tüm sorgular
için bu tablo adını kullanacaktır.
1
<?php
2
3
// app/models/Game.php
4
5
6
7
8
class Game extends Eloquent
{
public $table = 'gamezilla_roar';
}
Modellerinizi aduzaylı seçerseniz basit tablo isimleri sağlamak için $table niteliğini kullanmanız
gerekecektir. Bunun nedeni aduzayı ve sınıf kombinasyonundan oluşan MyApp\Models\Game gibi bir
şeyin, diğer aduzayları içindeki ve kendileri de veritabanını kullanan paketlerle çatışmaları önlemek
için my_app_models_games isminde bir tablo olmasını beklemesine yol açacaktır. Ayrıca Eloquent’in
çok zeki olduğunu ve ‘camel case’ aduzayı veya model isimlerini ‘snake case’ türüne genişleteceğini
de fark edeceksiniz.
Veritabanı tablomuzda yeni satırlar oluşturmak için onlara PHP nesneleri muamelesi yapmak için
Eloquenti nasıl kullanacağımızı öğrenmiş olduk. Bundan sonra da bu mevcut satırları nasıl elde
edeceğimize bir göz atalım.
Eloquent ORM
271
Mevcut Modellerin Okunması
Eloquent model olgularının sorgulanması için çok sayıda metod sağlar. İlerideki bir bölümde
bunların hepsini inceleyeceğiz ama şimdilik veritabanımızdan id sütununa göre tek bir model olgusu
elde etmek için find() metodunu kullanacağız. İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$game = Game::find(1);
return $game->name;
});
Veritabanımızdaki id değeri 1 olan satırı temsil eden bir Game olgusu elde etmek için modelimizin
statik find() metodunu kullandık. Daha sonra sütun değerlerini elde etmek için model olgusunun
public niteliklerine erişebiliriz. Sonucu görmek için / URL’sini ziyaret edelim.
1
Assassins Creed
Harika, mevcut değerimiz elde edilmiştir. Statik find() metodu Eloquent ebeveyn sınıfından miras
alınmıştır ve modelinizin içinde oluşturulmasına gerek yoktur. Daha önceden şöylediğim gibi, diğer
birçok elde etme metodu vardır ve onları Eloquent modellerinin sorgulanması hakkındaki daha
sonraki bir bölümde keşfedeceğiz. Şimdilik, mevcut tablo satırlarının nasıl güncelleneceğine bir göz
atabiliriz.
Mevcut Modellerin Güncellenmesi
Eğer çok yakın zamanda yeni bir model oluşturmuşsanız, değişiklikleri onu atadığınız bir değişkene
yapmalısınız. Önceki kesimde Game modelimizin yeni bir olgusunu oluşturmuştuk ve bu olguyu
$game değişkenine atamış, sütunlarını güncellemiş ve veritabanımızda kalıcı hale getirmek için
save() metodunu kullanmıştık.
Eloquent ORM
1
272
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', function()
{
$game = new Game;
$game->name = 'Assassins Creed';
$game->description = 'Assassins VS templars.';
$game->save();
});
Model olgumuzu sırf save() ettik diye onu değiştiremeyiz anlamı çıkmaz. Onun değerlerini doğrudan değiştirebiliriz ve mevcut satırı güncellemek için save() metodunu bir kez daha çağırabiliriz.
Görüyorsunuz, save() metodunu yeni bir nesnede ilk defa kullanırsanız, yeni bir satır oluşturacak
ve id sütununa otomatik artan bir değer atayacaktır. save() metoduna daha sonra yapılan çağrılar
ise sadece veritabanımızda mevcut olan bu satırın sütunlarına yapılan değişiklikleri kalıcı hale
getirecektir.
Aşağıdaki örneğe bir göz atın.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('/', function()
{
$game = new Game;
$game->name = 'Assassins Creed';
$game->description = 'Show them what for, Altair.';
$game->save();
11
$game->name = 'Assassins Creed 2';
$game->description = 'Requiescat in pace, Ezio.';
$game->save();
12
13
14
15
$game->name = 'Assassins Creed 3';
$game->description = 'Break some faces, Connor.';
$game->save();
16
17
18
19
});
Yukarıdaki örneğin games tablosunda üç giriş oluşturacağını düşünebilirsiniz ama bu yanlış olacaktır. Bu kodla Game sınıfından sadece tek bir yeni olgu oluşturuyor olduğumuz dikkatinizi çekecektir.
Eloquent ORM
273
save() metoduna gelecekte yapılacak tüm çağrılar bu mevcut veritabanı satırının değiştirilmesi işini
yerine getirecektir. Veritabanımız içinde, bu nesnenin en son kaydedilmiş durumu mevcut olacaktır.
Bu örneği göstermek için ben veritabanındaki games tablosunu truncate edeceğim (tabloyu boşaltacağım). Evde takip ediyorsanız siz de bunu yapın.
1
2
mysql> truncate games;
Query OK, 0 rows affected (0.00 sec)
Rota mantığımızı çalıştırmak için / URI’sini bir kez daha ziyaret edelim. games tablomuz için oluşan
içerik şöyledir.
1
2
3
4
5
6
7
8
9
10
11
12
mysql> select * from games;
+----+-------------------+---------------------------+---------------------+-----\
----------------+
| id | name
| description
| created_at
| upda\
ted_at
|
+----+-------------------+---------------------------+---------------------+-----\
----------------+
| 1 | Assassins Creed 3 | Break some faces, Connor. | 2013-07-14 17:38:50 | 2013\
-07-14 17:38:50 |
+----+-------------------+---------------------------+---------------------+-----\
----------------+
1 row in set (0.00 sec)
Görebileceğiniz gibi son save() metodumuzla ‘Assassins Creed 3’ güncellenmiştir.
Modelimizin mevcut bir olgusuna yapılmış bir referansa sahipsek yukarıdaki metod çok yararlıdır
ama ya sahip değilsek? Modeli uzun bir zaman önce oluşturmuşsak ne olur? Bunu yapabilmek
için, mevcut bir veritabanı satırını temsil eden bir olguyu elde etmek amacıyla find() metodunu
kullanabiliriz ve ondan sonra da uygun şekilde değiştirebiliriz.
İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', function()
{
$game = Game::find(1);
$game->name = 'Assassins Creed 4';
$game->description = 'Shiver me timbers, Edward.';
$game->save();
});
Eloquent ORM
274
games tablomuzu inceleyecek olursak, daha önce eklenen satırın id değerinin 1 olduğunu görürüz.
Modelimiz üzerinde statik find() metodunu ve bilinen bir id’i kullanarak, mevcut o tablo satırını
temsil eden bir Game olgusunu elde edebiliyoruz. Bu yeni olguyu döndürdükten sonra, daha önce
yaptıklarımızla aynı şekilde sütun değerlerini değiştirebiliyor ve save() kullanabiliyoruz.
İşte ortaya çıkan tablo satırı.
1
2
3
4
5
6
7
8
9
10
11
12
mysql> select * from games;
+----+-------------------+----------------------------+---------------------+----\
-----------------+
| id | name
| description
| created_at
| upd\
ated_at
|
+----+-------------------+----------------------------+---------------------+----\
-----------------+
| 1 | Assassins Creed 4 | Shiver me timbers, Edward. | 2013-07-14 17:38:50 | 201\
3-07-14 17:49:28 |
+----+-------------------+----------------------------+---------------------+----\
-----------------+
1 row in set (0.00 sec)
Görebileceğiniz gibi mevcut satırımız uygun şekilde güncellenmiştir. Bir kez daha, tek bir SQL
satırı yazmadık. Sadece güzel, etkili, Eloquent PHP yazdık. Ayrıca, updated_at sütununun otomatik
olarak şimdiki zaman ile doldurulduğunu da görüyorsunuz. Çok kullanışlı!
Mevcut Modellerin Silinmesi
Eloquent modellerinin silinmesi basit bir süreçtir. İlk olarak, silmek istediğimiz model olgusunu
ellerimize almamız gerekiyor. Örneğin, bir önceki alt bölümde keşfettiğimiz find() metodunu
kullanabiliriz.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
$game = Game::find(1);
});
Bir Eloquent model olgususunu kirli eldivenlerimiz arasına aldıktan sonra, veritabanından modelimiz tarafından temsil edilen satırı kaldırmak için delete() metodunu kullanabiliriz.
Eloquent ORM
1
275
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$game = Game::find(1);
$game->delete();
});
Ayrıca, id sütun değerlerini ve destroy() statik metodunu kullanarak veritabanından tek bir veya
birden çok model olgusunu silebiliriz.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
Game::destroy(1);
});
Birden çok kaydı yok etmek için, ya destroy() metoduna id değerlerini parametreler olarak
geçebiliriz…
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
Game::destroy(1, 2, 3);
});
… veya id değerlerinden oluşan bir dizi geçeriz, mesela şöyle:
Eloquent ORM
1
276
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
Game::destroy(array(1, 2, 3));
});
Bu tamamen size kalmış!
SQL belirli bir kayıt alt kümesini sorgulamak için birtakım farklı ve karmaşık yollar sunmaktadır.
Dert etmeyin, Eloquent bu basit görevi de yapacaktır. Sonraki bölümde, Eloquent ORM’de bulunan
çeşitli sorgulama metodlarını öğreneceğiz. İlerle ve sayfayı çevir!
Eloquent Sorguları
Önceki bölümde veritabanı satırlarımızı sütunlar olarak ve tablolarımızı sınıflar olarak nasıl ifade
edeceğimizi öğrendik. Bu, Yapılandırılmış Sorgulama Dili (Structured Query Language, kısaca SQL)
kullanan cümleler yazma gereğini ortadan kaldırır ve kodun çok daha okunabilir olmasını sağlar.
Biz PHP yazıyoruz değil mi? Başka bir dil ekleyerek neden işleri zorlaştırma zahmetine girelim ki?
Peki, SQL’de bazı iyi kısımlar da var. Örneğin, Q kısmı. Query, yani sorgulama. SQL sayesinde,
sadece gereken sonuçları elde etmek için birtakım karmaşık karşılaştırmalar kullanabiliriz ve
aritmetik ayarlayabiliriz. Bu işlevselliğin tümünün Eloquent ile klonlanması çok büyük bir görev
olurdu ama neyse ki, Eloquent en yararlı sorgular için uygulanabilecek farklı metodlara sahiptir.
Eksik olan tüm parçalar için de, Eloquent ORM sonuç olguları döndürecek olan SQL cümleleri
sağlamak için ham sorgular kullanabiliriz. Biraz sonra buna daha yakından bakacağız. Ama öncelikle
veritabanımızı bu bölüm için hazırlayalım.
Hazırlık
‘Seeding’ olarak bilinen bir tekniği kullanarak veritabanımızı örnek verilerle nasıl doldurabileceğimizi birazdan öğreneceğiz ama şimdilik Eloquent kullanarak veritabanımızda bazı sahte veriler
oluşturacağız. Burada yeni bir işlevsellik kullanmak istemiyorum, en son bölümlerde öğrenmiş
olduğumuz becerileri kullanacağız.
İlk olarak örnek tablomuz için şema inşa etmek amacıyla bir migrasyon oluşturmamız gerekiyor.
Demo verisi olarak müzik albümleri kullanacağız. Bir albums tablosu inşa etmek için bir migrasyon
oluşturalım.
1
2
$ php artisan migrate:make create_albums
Created Migration: 2013_07_21_103250_create_albums
Artık metod taslaklarını yeni albums tablomuzun şemasını inşa etmek için gereken kodlarla
doldurabiliriz.
Eloquent Sorguları
1
278
<?php
2
3
use Illuminate\Database\Migrations\Migration;
4
5
// app/database/migrations/2013_07_21_103250_create_albums.php
6
7
class CreateAlbums extends Migration {
8
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('albums', function($table)
{
$table->increments('id');
$table->string('title', 256);
$table->string('artist', 256);
$table->string('genre', 128);
$table->integer('year');
});
}
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('albums');
}
26
27
28
29
30
31
32
33
34
35
36
}
Migrasyonumuzun up() metodunda, albums adında yeni bir tablo oluşturmak için Schema facade
kullanıyoruz. Bu tablo albüm ismi, müziği yapan sanatçı ve müziğin tarzı için varchar sütunları
içerecek. Ayrıca Eloquent ORM tarafından gerekli olduğu için otomatik artan id alanımız ve son
olarak albümün çıktığı seneyi saklamak için bir integer alanımız var.
down() metodunda tabloyu düşürüyoruz, böylece veritabanı orijinal haline geri dönüyor.
Eloquent Sorguları
279
Veritabanını yapılandırmak için migrasyonumuzu çalıştıralım.
1
2
3
$ php artisan migrate
Migration table created successfully.
Migrated: 2013_07_21_103250_create_albums
Veritabanımız artık örnek albüm verimizi tutacak bir yapıya sahip. Şimdi tablomuzla PHP nesneleri
kullanarak etkileşimde bulunabilmek için bir Eloquent model tanımı oluşturmamız gerekiyor.
1
<?php
2
3
// app/models/Album.php
4
5
6
7
8
class Album extends Eloquent
{
public $timestamps = false;
}
Güzel, basit, temiz. Bu kesim içindeki örnekleri basitleştirmek için Album model tanımımızda
zaman damgalarını devre dışı bıraktık. Normalde modellerimin hepsine zaman damgaları eklemeyi
severim. Bunu yapınca hafif bir performans yükü olabilir, ayrıca küçük bir ekstra depo alanı
gerektirir ama bir uygulama modeli için denetim takibi sağlamakta zaman damgalarını çok yararlı
buluyorum.
Şimdi bütün yapmamız gereken, veritabanı tablomuzu sahte albüm verileriyle doldurmaktır. Az önce
de söylediğim gibi, bu görev için ideal olanı veritabanı seeding’i kullanmaktır ama şimdilik basitçe
bir rota Closure’u oluşturacağız. Sonuç olarak pek tekrar karşılaşmayacağız ama bu sefer görünüp
gitmesine izin verelim. Bu rotayı sadece bir kez ziyaret etmeyi amaçlıyoruz.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
12
13
Route::get('/seed', function()
{
$album = new Album;
$album->title
= 'Some Mad Hope';
$album->artist
= 'Matt Nathanson';
$album->genre
= 'Acoustic Rock';
$album->year
= 2007;
$album->save();
280
Eloquent Sorguları
14
15
16
17
18
19
$album = new Album;
$album->title
$album->artist
$album->genre
$album->year
$album->save();
=
=
=
=
'Please';
'Matt Nathanson';
'Acoustic Rock';
1993;
$album = new Album;
$album->title
$album->artist
$album->genre
$album->year
$album->save();
=
=
=
=
'Leaving Through The Window';
'Something Corporate';
'Piano Rock';
2002;
$album = new Album;
$album->title
$album->artist
$album->genre
$album->year
$album->save();
=
=
=
=
'North';
'Something Corporate';
'Piano Rock';
2002;
$album = new Album;
$album->title
$album->artist
$album->genre
$album->year
$album->save();
=
=
=
=
'...Anywhere But Here';
'The Ataris';
'Punk Rock';
1997;
$album = new Album;
$album->title
$album->artist
$album->genre
$album->year
$album->save();
=
=
=
=
'...Is A Real Boy';
'Say Anything';
'Indie Rock';
2006;
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
});
Bunlar benim kişisel favorilerimden bir kısmı. Punk rock hayranı olmayanlarınızın müzik zevkinden
çok rahatsız olmayacağını ve bölüme devam edeceğini umut ediyorum.
Görebileceğiniz gibi, sahte satırlarımızın her birisi için yeni bir Album model olgusu oluşturuyoruz,
tüm alanlarını dolduruyor ve doldurulmuş modeli veritabanına kaydediyoruz.
Devam edelim ve albums tablosunu örnek verilerimizle doldurmak için /seed URI’sini ziyaret
edelim. Bu rotadan bir cevap döndürmediğimiz için boş bir sayfa alacaksınız.
Eloquent Sorguları
281
Örnek verimiz veritabanına yazılmış olduğuna göre /seed rotasını silebilirsiniz. Ona artık ihtiyacımız olmayacak! Hazırlığımız tamamlandı, şimdi Eloquent sorguları kunusunu öğrenebiliriz.
Eloquent’ten String’e
PHP’deki nesneler opsiyonel olarak bir __toString() metodu içerebilir. Geçmişte buna rastlamış
olabilirsiniz, PHP 5.2’de çift alt tire _ ön ekli diğer sihirli metodlarla birlikte bu metod da eklenmişti.
Bu metod nesnenin bir string olarak nasıl temsil edileceğini kontrol altına almak için kullanılabilir.
Bu metod sayesinde Eloquent modellerimiz de bir string olarak ifade edilebilecek. Gördüğünüz gibi,
kendi modellerimizle genişlettiğimiz Eloquent taban sınıfı bir __toString() metodu içermektedir.
Bu metod Eloquent modelimizin değerlerini temsil edecek bir JSON stringi döndürecektir.
Bir örnek görünceye kadar bu biraz kafa karıştırıcı gelebilir. İsterseniz önce Eloquent model
olgularımızın taşıdığı değerlerin normal yolla gösterilmesine bir göz atalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$album = Album::find(1);
return $album->title;
});
Yukarıdaki örnekte Album modelimizin miras aldığı statik find() metodunu kullanıyoruz ve id sütun
değeri 1 olan albüm tablo satırını temsil eden bir model olgusunu elde etmek için integer 1 değerini
geçiyoruz. Daha sonra, görünüm cevabı olarak gösterilmek üzere model olgusunun title niteliğini
döndürüyoruz.
/ URI’sini ziyaret ettiğimiz takdirde aşağıdaki cevabı alırız.
1
Some Mad Hope
Beklediğimiz gibi, veritabanımıza ilk eklenen sahte albümün ismi. Şimdi rotamızı, bunun yerine rota
Closure’undan bir cevap olarak model olgusunun kendisini döndürecek şekilde değiştirelim.
Eloquent Sorguları
1
282
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::find(1);
});
Cevabı incelemek için / URI’sini ziyaret edelim.
Bu bana JSON gibi geldi! Çatı tarafından oluşturulan tüm JSON stringleri veri transferinde bant
genişliğinden tasarruf etmek amacıyla tüm ekstra beyaz boşluk ve girintilemeleri ortadan kaldırmaktadır. Bu bölüm içerisinde JSON örneklerinin hepsini elimle güzelleştireceğim, bu nedenle eğer
sizdeki çıktılar bu bölümde gösterilenlerden biraz daha karışık görünürse şaşırmayın.
Yukarıdaki çıktıyı güzelleştirelim.
1
{
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
2
3
4
5
6
7
}
Laravel, Eloquent model olgumuzun değerlerini bir JSON stringi olarak göstermek için, olgunun miras almış olduğu __toString() metodunu çalıştırır. JSON veri sunan RESTful API’ler oluştururken
bu gerçekten çok yararlı olur. Ayrıca, bu bölümün geri kalan kısmındaki sorgularımızın çıktılarını
göstermek için de harika bir yoldur.
Bazı Eloquent metodları yukarıdaki örnekle döndürülen tek bir model olgusu yerine bir sonuç olarak
çok sayıda model olguları döndürecektir. Gelin şimdi tüm satırları Eloquent model olguları olarak
elde etmekte kullanılan all() metoduna kısaca bir göz atalım.
Eloquent Sorguları
1
283
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', function()
{
$albums = Album::all();
foreach ($albums as $album) {
echo $album->title;
}
});
albums tablomuzun satırlarını temsil eden Eloquent model olgularından oluşan bir dizi elde etmek
için Album modelimizin all() metodunu kullanıyoruz. Ondan sonra da dizi boyunca dolaşarak her
bir Album olgusunun ismini çıktılıyoruz.
İşte / URI’sinden alacağımız sonuç.
1
2
Some Mad HopePleaseLeaving Through The WindowNorth...Anywhere But Here...Is A Rea\
l Boy
Güzel, albüm isimlerinin tamamı burada. Bir HTML satır kesmesi <br /> elementi eklemediğimiz
için hepsi birbirine yapışmış. Bunu dert etmeyin, en azından hepsini elde etmiş olduk.
Bu konuda gerçekten üzgünüm fakat size bir kez daha yalan söyledim. Şayet daha önceden Laravel 3
kullanmışsanız, model olgularından oluşan bir dizi döndüren bir getirme metodu kavramı size bildik
gelecektir. Ancak Laravel 4 bu tür metodlardan bir dizi döndürmez, onun yerine bir Collection
döndürür.
Sana inanmıyorum. Eğer o bir dizi döndürmüyorsa, sonuçların bir ucuncan diğer ucuna
nasıl döngü yaptık?
Çok basit. Collection nesnesi nesnenin dolaşılmasına imkan veren bir arayüzü uygular. Standart
PHP dizileriyle aynı işlevsellik kullanılarak tek tek dolaşılabilmektedir.
Hmm, Anlıyorum. Yine de bir yalancının sözüne bu kadar kolay inanmak istemiyorum.
Ah anlıyorum, ek bir kanıt istiyorsunuz? Ne ile çalıştığımızı görmek için $albums niteliğini dump
edelim öyleyse. Bu işe yarayacaktır.
Eloquent Sorguları
1
284
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$albums = Album::all();
var_dump($albums);
});
/ URI’sini ziyaret ettiğimizde aşağıdaki cevabı alırız.
1
2
3
4
5
6
7
8
9
object(Illuminate\Database\Eloquent\Collection)[134]
protected 'items' =>
array (size=6)
0 =>
object(Album)[127]
public 'timestamps' => boolean false
protected 'connection' => null
protected 'table' => null
protected 'primaryKey' => stri
10
11
... daha birçok bilgi ...
Voah, bu sefer yalan söylemiyormuşsun!
Görebileceğiniz gibi, birden çok model olgusu döndüren bir metodun sonucu Illuminate\Database\Eloquent\Coll
bir olgusu tarafından temsil edilmektedir. Model olgularımızın dahili bir dizisini tutan items
adındaki bu nesneyi var_dump çıktısıyla görebiliyoruz.
Koleksiyon nesnesinin avantajı model olgularımızın dönüştürülmesi ve elde edilmesiyle ilgili
birtakım yararlı yöntemler de içermesidir. İlerideki bir bölümde bu metodları daha ayrıntılı olarak
inceleyeceğiz ama şimdilik Collection nesnesinin bir __toString() metodu da içerdiğini bilmek
yeterlidir. Bu metod model nesnelerimiz üzerinde bir tekine benzer bir tarzda fonksiyon görür fakat
çok boyutlu bir dizi yerine bir JSON stringi oluşturur.
Rota Closure’umuzun cevabı olarak, all() metodunun sonucu olan Collection nesnesini döndürelim. Bunun gibi:
Eloquent Sorguları
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::all();
});
/ URI’sini ziyaretten sonra alacağımız cevap şöyledir.
1
2
[
{
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
3
4
5
6
7
8
9
},
{
id: 2,
title: "Please",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 1993
10
11
12
13
14
15
16
},
{
id: 3,
title: "Leaving Through The Window",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
17
18
19
20
21
22
23
},
{
id: 4,
title: "North",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
24
25
26
27
28
29
30
31
},
{
id: 5,
285
Eloquent Sorguları
title: "...Anywhere But Here",
artist: "The Ataris",
genre: "Punk Rock",
year: 1997
32
33
34
35
},
{
36
37
id: 6,
title: "...Is A Real Boy",
artist: "Say Anything",
genre: "Indie Rock",
year: 2006
38
39
40
41
42
}
43
44
286
]
Her bir albümümüzün değerlerini tesmil eden bir nesneler dizisini taşıyan bir JSON stringi alıyoruz.
Peki neden __toString() işlevselliğini şimdi öğreniyoruz ki? Bu bölümde bir JSON API inşa etmeyi
amaçlamıyoruz değil mi? Hayır, şimdilik buna hazır değiliz.
Biliyorsunuz, bölümün geri kalanı boyunca çalıştıracağım sorguların sonuçlarını göstermek için
JSON çıktısı kullanacağım. Sonuç kümelerimizi göstermek için bu bir demet foreach() döngüsünden daha okunaklı olacaktır. Bu sonuçların neden JSON olarak çıktılandığını artık tam olarak
biliyorsunuz. Herkes kazandı!
Veritabanımızı sahte verilerle doldurduğumuza ve sorgu sonuçlarını göstermek için bir yol belirlediğimize göre artık Eloquent sorgularının yapısına bir göz atabiliriz.
Sorgu Yapısı
Eloquent sorguları birtakım kural ve kriterlere dayalı sonuçlar elde etmek için kullanılır. Her zaman
albüm satırlarınızın hepsini elde etmek istemezsiniz. Bazen sadece tek bir sanatçının diskografisini
elde etmek isteyeceksiniz. Bu durumlarda, sadece istediğimiz sanatçının bir title sütununa sahip
olan satırları istemek için bir sorgu kullanacağız.
Eloquent sorguları üç kısma ayrılabilir.
• Model.
• Sorgu Sınırlamaları (constraints)
• Getirme (fetch) metodları.
Model, üzerinde sorgulama yapmak istediğimiz model olgusudur. Bu kesimdeki örneklerin hepsi de
Album modeline dayalı sorgular oluşturacaktır.
Eloquent Sorguları
287
Sorgu sınırlamaları tablo satırlarımızın bir alt kümesini eşleştirmek için kullanılan kurallardır. Bu
yolla, sadece ilgilendiğimiz satırları döndürebiliriz. SQL’de kullanılan en bildik sınırlama WHERE
cümleciğidir.
Son olarak fetch metodlarımız var. Bunlar sorguyu gerçekleştirmek ve sonuç döndürmek için
kullanılan metodlardır.
En basit haliyle bir eloquent sorgusunun yapısına bir göz atalım.
1
<?php
2
3
Model::fetch();
Sorgularımızın tümü Eloquent modellerimizin birinde etki gösterecektir. Sınırlama metodları tamamen isteğe bağlıdır ve yukarıdaki örnekte bulunmamaktadır. Ondan sonra bir fetch metodumuz var.
Bu isimde bir metod mevcut değildir, sadece bir sorgunun şeklini göstermek için kullanıyoruz. Bir
sorgu zincirinin ilk metodu her zaman için statik denen iki tane iki nokta üstü üste :: iledir.
Eloquent sorgularında hiç sınırlama olmayabilir, bir tek sınırlama veya birçok sınırlama olabilir. Bu
tamamen size kalmış. Bir sorgunun tek bir sınırlama ile nasıl göründüğünü gösterelim.
1
<?php
2
3
4
Model::constraint()
->fetch();
Dikkat ederseniz burada sınırlama statik metodtur ve fetch metodumuz bu ilk metodun sonuna
zincirlenmiştir. Sorgulara istediğimiz kadar sınırlama ekleyebiliriz, örneğin:
1
<?php
2
3
4
5
6
Model::constraint()
->constraint()
->constraint()
->fetch();
Sınırlamalar bütünüyle opsiyoneldir ama tüm sorgular bir model ile başlamak ve bir fetch metoduyla
bitmek zorundadır. İşte Album modelimizi kullanan bir örnek.
Eloquent Sorguları
1
288
<?php
2
3
Album::all();
Buradaki Album bizim modelimizdir ve all() bizim fetch metodlarımızdan biridir, çünkü sorgumuzun sonucunu elde etmek için kullanılmıştır.
Fetch metodları ya tek bir model olgusu ya da model olgularından oluşan bir Collection döndürmek
için kullanılabilir. Ancak, daha önce öğrendiğimiz gibi, bunların her ikisi de bir rota Closure’unun
veya kontroller eyleminin cevabı olarak JSON formatında ifade edilebilecektir.
Sorgu sınırlamalarının opsiyonel olduğunu biliyoruz, bu itibarla, elimizde bulunan çeşitli fetch
metodlarına bakmakla başlayabiliriz.
Fetch Metodları
Önceki bölümlerde karşılaşmış olabileceğiniz birkaç fetch metoduyla başlayalım. Birincisi, find()
metodumuz var.
Find
find() metodu ilgili satırın id sütununa göre tek bir Eloquent model olgusu elde etmek için
kullanılabilir. Eğer metodun ilk parametresi bir tam sayı ise, bu durumda sadece tek bir olgu
döndürülecektir. İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::find(1);
});
Burada id değeri 1 olan veritabanı satırını elde etmek istediğimiz için sadece tek bir model olgusu
döndürülür.
Eloquent Sorguları
1
{
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
2
3
4
5
6
7
289
}
Bunun yerine, eğer id değerlerinden oluşan bir dizi verirsek, model olgularından oluşan bir
Collection alırız.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::find(array(1, 3));
});
İşte sonuç. id sütununun değeri 1 ve 3 olan satırları temsil eden model olgularını içeren bir
koleksiyon.
1
[
{
2
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
3
4
5
6
7
},
{
8
9
id: 3,
title: "Leaving Through The Window",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
10
11
12
13
14
}
15
16
]
All
Tabloda yer alan tüm satırları temsil eden model olgularından oluşan bir koleksiyon döndürmek için
all() metodu kullanılabilir. Burada all() metodunun bir örneği görülmektedir.
Eloquent Sorguları
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::all();
});
Veritabanımızda yer alan tüm Albüm olgularını içeren bir koleksiyon alırız.
1
2
[
{
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
3
4
5
6
7
8
9
},
{
id: 2,
title: "Please",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 1993
10
11
12
13
14
15
16
},
{
id: 3,
title: "Leaving Through The Window",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
17
18
19
20
21
22
23
},
{
id: 4,
title: "North",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
24
25
26
27
28
29
30
31
},
{
id: 5,
290
Eloquent Sorguları
title: "...Anywhere But Here",
artist: "The Ataris",
genre: "Punk Rock",
year: 1997
32
33
34
35
},
{
36
37
id: 6,
title: "...Is A Real Boy",
artist: "Say Anything",
genre: "Indie Rock",
year: 2006
38
39
40
41
42
}
43
44
291
]
First
Normalde bir model olguları koleksiyonu döndürülecek durumlarda, koleksiyondaki birinci model
olgusunu elde etmek amacıyla first() fetch metodu kullanılabilir. Bir sorgunun bir model olguları
koleksiyonu yerine tek bir olgu döndürmesini istediğiniz durumlarda bu çok yararlıdır. Bir sınırlama
olmadan, first() metodu veritabanı tablosundaki sadece ilk satırı döndürecektir.
İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::first();
});
Tek bir model olgusu alırız, veritabanı tablomuzda saklanan birinci albümü.
1
{
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
2
3
4
5
6
7
}
Eloquent Sorguları
292
Update
Model olgularımızı sadece okumak zorunda değiliz, onları değiştiriebiliriz de. update() metodunu
kullanarak, Eloquent sorgusunun sonucu olan tablo satırlarının değerlerini güncelleyebiliriz. Her
bir satırın sütun değerlerini değiştirmek için update() metoduna birinci parametre olarak sadece
bir anahtar-değer dizisi geçilir. Bu dizinin anahtarları değiştirilecek sütunun adını temsil eder ve
değer kısmı o sütun için istenilen yeni değeri temsil eder.
Ancak, update() metodu özel bir metodtur ve bir sınırlama olmadan kullanılamaz, bundan dolayı
örneğimizde basit bir where() sınırlaması kullanacağız. Eğer bunu anlamazsanız endişelenmeyin.
Bir sonraki kesimde sınırlamalar konusunu göreceğiz. Şimdi albums tablomuzu değiştirecek bir
örnek veriyorum. (Merak etmeyin, ondan sonraki örnek için onu eski haline getireceğim.)
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
Album::where('artist', '=', 'Matt Nathanson')
->update(array('artist' => 'Dayle Rees'));
9
return Album::all();
10
11
});
Burada artist alanının değeri Matt Nathanson olan tüm satırların artist alanlarının değerlerini
Dayle Rees olarak değiştiriyoruz. update() metodu model olguları elde etmez, bu yüzden onun
yerine all() kullanarak tüm model olgularından oluşan bir koleksiyon döndürüyoruz.
1
2
[
{
id: 1,
title: "Some Mad Hope",
artist: "Dayle Rees",
genre: "Acoustic Rock",
year: 2007
3
4
5
6
7
8
9
10
11
12
13
14
},
{
id: 2,
title: "Please",
artist: "Dayle Rees",
genre: "Acoustic Rock",
year: 1993
Eloquent Sorguları
},
{
15
16
id: 3,
title: "Leaving Through The Window",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
17
18
19
20
21
},
{
22
23
id: 4,
title: "North",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
24
25
26
27
28
},
{
29
30
id: 5,
title: "...Anywhere But Here",
artist: "The Ataris",
genre: "Punk Rock",
year: 1997
31
32
33
34
35
},
{
36
37
id: 6,
title: "...Is A Real Boy",
artist: "Say Anything",
genre: "Indie Rock",
year: 2006
38
39
40
41
42
}
43
44
293
]
Gördüğünüz gibi, artık bir rock yıldızıyım. Müthiş!
Delete
Tıpkı update() metodu gibi, delete() metodu da herhangi bir olgu döndürmeyecektir. Bunun
yerine, sorgunun sonucu olan satırları veritabanı tablosundan kaldıracaktır.
Eloquent Sorguları
1
294
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
Album::where('artist', '=', 'Matt Nathanson')
->delete();
9
return Album::all();
10
11
});
Artist sütununun değeri Matt Nathanson olan tüm albümleri sorguluyoruz ve sonra da bu satırları
veritabanından silmek için delete() metodunu kullanıyoruz.
1
2
[
{
id: 3,
title: "Leaving Through The Window",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
3
4
5
6
7
8
9
},
{
id: 4,
title: "North",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
10
11
12
13
14
15
16
},
{
id: 5,
title: "...Anywhere But Here",
artist: "The Ataris",
genre: "Punk Rock",
year: 1997
17
18
19
20
21
22
23
24
25
26
27
},
{
id: 6,
title: "...Is A Real Boy",
artist: "Say Anything",
genre: "Indie Rock",
Eloquent Sorguları
year: 2006
28
}
29
30
295
]
Matt Nathanson’ın albümleri veritabanımızdan kaldırıldı. Ne kadar utanç verici bir durum, onun
müzikleri çok güzeldi!
Kısa bir ipucu vereyim. Belirli bir model için tüm tablo satırlarını silmek istiyorsanız, truncate()
metodu size daha tanımlayıcı gelebilir.
İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
Album::truncate();
return Album::all();
});
Gördüğünüz gibi tablodaki tüm satırlar yok oldu!
1
[ ]
Get
Get bizim için en önemli fetch metodudur. Sorgu sonuçlarını elde etmek için kullanılır. Örneğin,
bir sonuç kümesini tek bir sanatçıya sınırlamak için bir where() sınırlaması kullanırsak, ondan
sonra all() tetikleyici metodunu kullanmak anlamlı olmayacaktır. Onun yerine, bir model olgu
koleksiyonunu elde etmek için get() metodunu kullanırız.
Kafanız karışmadı ya? İşte bir where() sınırlamasıyla eşlik eden bir get() metodu.
Eloquent Sorguları
1
296
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
return Album::where('artist', '=', 'Something Corporate')
->get();
});
Burada, artist sütununun değeri Something Corporate olan model olgularının bir koleksiyonunu
alırız.
1
[
{
2
id: 3,
title: "Leaving Through The Window",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
3
4
5
6
7
},
{
8
9
id: 4,
title: "North",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
10
11
12
13
14
}
15
16
]
get() metodunun opsiyonel bir parametresi vardır. Ona sütun adlarından oluşan bir dizi geçebilir-
siniz ve sonuç nesnesi sadece bu sütunların değerlerini içerecektir. İşte bir örnek.
Eloquent Sorguları
1
297
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
return Album::where('artist', '=', 'Something Corporate')
->get(array('id', 'title'));
});
get() metoduna değerleri id ve title olan bir dizi geçiyoruz, alacağımız sonuç kümesi şöyledir.
1
[
{
2
id: 3,
title: "Leaving Through The Window"
3
4
},
{
5
6
id: 4,
title: "North"
7
8
}
9
10
]
Gördüğünüz gibi, sonuçlarda sadece istemiş olduğumuz sütunlar bulunmaktadır.
Pluck
Bu pluck() metodu tek bir sütundan bir değer elde etmek için kullanılabilir. İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::pluck('artist');
});
Birinci ve tek parametresi değerini elde etmek istediğimiz sütunun adıdır. Eğer sorgu birden çok
sonuçla eşleşirse, bu durumda sadece ilk sonuç döndürülecektir. Yukarıdaki örnekle alacağımız sonuç
şu şekildedir.
Eloquent Sorguları
1
298
Matt Nathanson
Lists
pluck() metodu belirli bir sütun için sadece tek bir değer alıp getirirken, lists() metodu belirtilen
sütun için tüm sonuç olgularının değerlerinden oluşan bir dizi getirecektir. Bir örnekle bunu daha
açık anlatalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::lists('artist');
});
Aynı şekilde, lists() metodu da tek bir parametre alır. Tüm değerlerini elde etmek istediğimiz
sütunun adını. İşte sorgumuzdan gelen sonuç.
1
[
"Matt Nathanson",
"Matt Nathanson",
"Something Corporate",
"Something Corporate",
"The Ataris",
"Say Anything"
2
3
4
5
6
7
8
]
Göreceğiniz gibi, tablomuzun tüm satırlarının artist sütunundaki değerleri elde ettik.
ToSql
Haklısınız, bu gerçekte bir fetch metodu değildir ama çok yararlıdır! Normalde bir fetch metodu
kullanabileceğiniz her yerde, tipik olarak bir sorgu zincirinin en sonunda, bu toSql() metodunu
kullanabilirsiniz ve sorguyu temsil eden SQL stringini döndürecektir.
Bir örnekle görelim.
299
Eloquent Sorguları
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
return Album::where('artist', '=', 'Something Corporate')
->toSql();
});
Önceki örneğimize benziyor, ama bu sefer get() yerine toSql() çağırıyoruz. İşte alacağımız sonuç.
1
select * from `albums` where `artist` = ?
Hata ayıklama için çok yararlı!
Oradaki soru işareti nedir?
Laravel’in sorgu oluşturucusu ‘prepared’ cümleler kullanır. Yani buradaki soru işaretleri sizin
gerçek değerlerinizle veya ‘binding’lerinizle değiştirilecek olan yer tutuculardır. Bunun yararı bir
SQL enjeksiyon girişimini engellemek için bağlamalarınızın stringe yerleştirilmeden önce escape
edilebilmesidir.
Artık getirme metodlarını keşfettiğimize göre, sorgulamalarımıza kurallar eklemeyi öğrenmenin
zamanı geldi demektir.
Sorgu Sınırlamaları
Önceki bölümdeki fetch metodları veritabanımızdan model koleksiyonları ve olgularını elde etmekte
yararlıdırlar. Ancak, bazen sadece belirli satırlar için sorguda ince ayarlar yapmamız gerekir. Sorgu
sınırlamaları bu işe yarar.
Matematikte, küme tabanlı aritmetik büyük bir değerler kümesinin bir alt kümesini alabilmemize
imkan vermektedir. Bu aslında sorgu sınırlamaları kullanarak gerçekleştirmeye çalıştığımız şeydir
ancak bu bölümün içine sonuçların sırasını değiştirecek bazı dönüştürme metodlarını da koydum.
En sık kullanılan SQL sorgu kısıtlaması olan WHERE cümlesini temsil eden bir metod ile başlayalım.
Where
İnceleyeceğimiz ilk sınırlama metodu where() metodudur. Geçmişte SQL kullanmışsanız, sütun değerlerine uyan tablo satırlarını elde etmek için kullanılan WHERE cümleciği ile karşılaşmış olmalısınız.
Bir örnekle gidelim.
Eloquent Sorguları
1
300
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
return Album::where('artist', '=', 'Matt Nathanson')
->get();
});
Sonuçları sadece artist sütununun değeri Matt Nathanson olan albümlere sınırlamak için where()
metodunu kullanıyoruz.
where() metodu üç parametre alacaktır. Birinci parametre karşılaştırma yapmak istediğimiz sütunun adıdır. Bu örnekte karşılaştırmayı artist sütununda yapmak istiyoruz. İkinci parametre
karşılaştırma için kullanılacak işlemcidir. Örneğimizde artist sütununun bir değere eşit olmasını
temin etmek istiyoruz, bu yüzden eşittir = sembolü kullanıyoruz.
SQL tarafından desteklenen <, >, =>, =< ve benzeri diğer karşılaştırma operatörlerinin herhangi
birisini kullanabiliriz. İstediğiniz sonuçları elde etmek için operatör türlerini tecrübe ediniz.
Üçüncü parametre karşılaştırılacak değerdir. Örneğimizde, artist sütununun Matt Nathansona
uymasını temin etmek istiyoruz, o yüzden buradaki Matt Nathanson değer oluyor.
Bir kez daha ifade edeyim, where() metodu yalnızca bir sorgu sınırlamasıdır. Bir sonuç Collectionu
elde etmek için get() metodunu kullanacağız. / URI’sinden dönen cevaba bir göz atalım.
1
[
{
2
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
3
4
5
6
7
},
{
8
9
id: 2,
title: "Please",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 1993
10
11
12
13
14
}
15
16
]
Eloquent Sorguları
301
Mükemmel. Veritabanında artist sütununun değeri Matt Nathanson olan her iki albüm de
döndürüldü. Bu, bir müzik websitesinde belirli bir sanatçının diskografisini gösteren kesimler yapma
niyetinde olduğumuzda çok işe yarayacaktır.
get() ve first() metodlarının birbiriyle değiştirilebilir olduğunu bilmekte yarar var. Mevcut
örneğimizi, verilen şarta uyan sadece ilk olguyu getirecek şekilde değiştirelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
return Album::where('artist', '=', 'Matt Nathanson')
->first();
});
Sorgu şimdi verilen sınırlamaya uyan sadece ilk satırı temsil eden bir model olgusunu getirecek. İşte
/ URI’den gelen sonuç.
1
{
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
2
3
4
5
6
7
}
where() metodunda başka bir operatörü deneyelim. LIKEa ne dersiniz? Bu LIKE SQL operatörü bir
joker olarak bir yüzde % sembolü kullanmak suretiyle bir stringin parçalarını karşılaştırmak için
kullanılabilmektedir.
İşte bir örnek.
Eloquent Sorguları
1
302
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
return Album::where('title', 'LIKE', '...%')
->get();
});
Yukarıdaki örnekte, title alanı art arda üç nokta karakteri ... ile başlayan tüm satırları getirmek
istiyoruz. Buradaki yüzde % işareti, üç noktadan sonra gelen değerleri dikkate almadığımızı veritabanının bilmesini sağlayacaktır. Yan bir not olarak, art arda üç noktaya ‘ellipsis’ denildiğini biliyorum,
ben sadece ana dili ingilizce olmayan okuyucular için daha kolay olacağını düşündüm.
Şimdi / URI’sinden gelen sonuca bir göz atalım.
1
[
{
2
id: 5,
title: "...Anywhere But Here",
artist: "The Ataris",
genre: "Punk Rock",
year: 1997
3
4
5
6
7
},
{
8
9
id: 6,
title: "...Is A Real Boy",
artist: "Say Anything",
genre: "Indie Rock",
year: 2006
10
11
12
13
14
}
15
16
]
Her ikisinin ismi de art arda üç nokta ile başlayan ‘…Anywhere But Here’ ve ‘…Is A Real Boy’ title’li
harika albümleri içeren bir sonuç koleksiyonu alıyoruz.
Bir sorgu içerisinde tek bir where() metodu ile kısıtlandırılmış değiliz. Değişik sayıda farklı kriterlere
dayalı satırlar elde etmek için birden çok where() metodunu birbirine zincirleyebiliriz. İşte bir örnek.
Eloquent Sorguları
1
303
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('/', function()
{
return Album::where('title', 'LIKE', '...%')
->where('artist', '=', 'Say Anything')
->get();
});
Yukarıdaki örnekte, title sütununun değeri art arda üç nokta ile başlayan ve artist sütununun değeri
‘Say Anything’ olan satırları bulmak istiyoruz. Buradaki ve önemlidir. Bir satırın sonuç kümesinde
olması için her iki sınırlamaya da uyması gereklidir.
Yukarıdaki örnekten elde edilen sonuç şöyledir.
1
[
{
2
id: 6,
title: "...Is A Real Boy",
artist: "Say Anything",
genre: "Indie Rock",
year: 2006
3
4
5
6
7
}
8
9
]
Tek bir model olgusunu yani üç nokta ile başlayan bir ismi ve ‘Say Anything’ değerini taşıyan bir
artisti olan bir albümü içeren bir koleksiyon. Biz, Say Anything’in ‘…Is A Real Boy’ albümünü içeren
bir Collection alıyoruz. Kişisel gözdelerimden birisi!
OrWhere
Her zaman her iki sınırlamaya da uydurmak zorunda değiliz. Kimi zaman iki durumdan birisine
uyması bizim için yeterlidir. Bunun gibi durumlarda, orWhere() metodunu kullanabiliriz. Aslına
bakılırsa, bu bölümdeki sınırlamaların pek çoğunun, uyma konusunda değişmeli bir sınırlamaya
imkan vermek üzere, or ile başlayan alternatif bir versiyonu vardır. Bu sebeple, gelecekte or metod
varyasyonları için ayrı kesimler sunmayacağım.
Her zaman olduğu gibi, örnek burada.
Eloquent Sorguları
1
304
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('/', function()
{
return Album::where('title', 'LIKE', '...%')
->orWhere('artist', '=', 'Something Corporate')
->get();
});
Sağladığımız ilk sınırlamada, album title’ının üç nokta (evet, bunun adının ellipsis olduğunu biliyorum) ile başlaması gerektiğini söyledik. Sonra da sonuç kümesinin artist sütununun değerinin
Something Corporate olan sonuçlardan ibaret olmasını da söyleyen bir orWhere() metodunu dahil
ettik.
Şimdi sonuca bir göz atalım.
1
2
[
{
id: 3,
title: "Leaving Through The Window",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
3
4
5
6
7
8
9
},
{
id: 4,
title: "North",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
10
11
12
13
14
15
16
},
{
id: 5,
title: "...Anywhere But Here",
artist: "The Ataris",
genre: "Punk Rock",
year: 1997
17
18
19
20
21
22
23
24
25
},
{
id: 6,
title: "...Is A Real Boy",
Eloquent Sorguları
artist: "Say Anything",
genre: "Indie Rock",
year: 2006
26
27
28
}
29
30
305
]
Album title üç nokta ile başlayan veya artist sütununun değeri Something Corporate olan sonuç
olgularından oluşan bir Collection alıyoruz.
Tablo satırlarını gerekli sonuç setine filtrelemek için ihtiyacınız olan birçok where() ve orWhere()
metodunu birbirine zincirleyebilirsiniz.
WhereRaw
Sonuç kümesi üzerinde bir WHERE şartı gerçekleştiren bir SQL stringi sağlamak için whereRaw()
metodu kullanılabilir. İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', function()
{
return Album::whereRaw('artist = ? and title LIKE ?', array(
'Say Anything', '...%'
))
->get();
});
whereRaw() metodu ilk parametre olarak bir SQL stringi alır. String içerisindeki tüm soru ? işaretleri,
metoda ikinci parametre olarak verilen dizinin aynı sıradaki elemanlarıyla değiştirilecektir. Daha
önce SQL ile prepared cümlelere özellikler bağlamış iseniz, bu söz dizimi size tanıdık gelecektir.
SQL enjeksiyon saldırılarını engellemek için, verilen değerler escape edilecektir.
Sorgu oluşturucusunda gerekli dönüştürmeler gerçekleştirildikten sonra oluşan SQL şöyle gözükecektir:
1
artist = 'Say Anything' and title LIKE '...%'
Sorgumuzun sonucu aşağıdaki gibidir.
Eloquent Sorguları
1
[
{
2
id: 6,
title: "...Is A Real Boy",
artist: "Say Anything",
genre: "Indie Rock",
year: 2006
3
4
5
6
7
}
8
9
306
]
where() türü sınırlamalarınıza ek olarak karmaşık SQL gerektiren durumlarda whereRaw() metodunu kullanabilirsiniz. Tıpkı where() metodu gibi, whereRaw() metodu da sonuç kümesini sınırlamak
için birden daha fazla ve diğer sınırlama metodlarıyla birlikte zincirlenebilir. Bir kez daha ifade
edelim, alternatif durumlara imkan vermek için bir orWhereRaw() metodu da bulunmaktadır.
WhereBetween
whereBetween() metodu bir sütun değerinin verilen iki değer arasında olduğunu kontrol etmek için
kullanılır. Aslında her şeyin bir örnekle daha iyi açıklanacağını düşünüyorum. Garip, değil mi? Belki
de Laravel kodunun genellikle kendisi konuştuğu içindir!
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
return Album::whereBetween('year', array('2000', '2010'))
->get();
});
whereBetween() metodu için birinci parametre karşılaştırmak istediğimiz sütunun adıdır. İkinci
parametre bir başlama ve bir bitiş değeri olmak üzere iki değerden oluşan bir dizidir. Yukarıdaki
örnekte, salınım yılı yani year sütununun değeri 2000 ile 2010 arasında olan albümleri arıyoruz. İşte
sonuçlar.
Eloquent Sorguları
1
[
{
2
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
3
4
5
6
7
},
{
8
9
id: 3,
title: "Leaving Through The Window",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
10
11
12
13
14
},
{
15
16
id: 4,
title: "North",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
17
18
19
20
21
},
{
22
23
id: 6,
title: "...Is A Real Boy",
artist: "Say Anything",
genre: "Indie Rock",
year: 2006
24
25
26
27
28
}
29
30
307
]
Sonuç beklediğimiz gibi 2000lerdeki albümler.
Tıpkı diğer where() türü metodlar gibi, ihtiyacınız olduğu kadar çok sayıda zincirleyebilirsiniz ve
bir orWhereBetween() alternatif metodu da bulunmaktadır.
WhereNested
whereNested() metodu, bir sorguya birden çok where sınırlaması uygulamanın temiz bir yoludur.
Metoda ilk parametre olarak sadece bir Closure geçersiniz ve Closure’a istediğiniz isimde bir yer
tutucu parametre verirsiniz. Ben $query ismini vermekten hoşlanıyorum.
Eloquent Sorguları
1
308
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
12
13
Route::get('/', function()
{
return Album::whereNested(function($query)
{
$query->where('year', '>', 2000);
$query->where('year', '<', 2005);
})
->get();
});
Bu Closure içerisinde $query nesnesine birçok where() türü sınırlama veya orWhere() türü
sınırlama uygulayabilirsiniz, bunlar daha sonra ana sorgunuzun parçası olacaklardır. Çok daha derli
toplu gözükür! Yukarıdaki örnekten elde edilen sonuç kümesi şöyledir.
1
[
{
2
id: 3,
title: "Leaving Through The Window",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
3
4
5
6
7
},
{
8
9
id: 4,
title: "North",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
10
11
12
13
14
}
15
16
]
Bu metod için bir orWhereNested() alternatifi olmadığını unutmayın. Ama bir sır vereyim…
orWhere()e de bir Closure geçebilirsiniz. İşte bir örnek.
Eloquent Sorguları
1
309
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Route::get('/', function()
{
return Album::whereNested(function($query)
{
$query->where('year', '>', 2000);
$query->where('year', '<', 2005);
})
->orWhere(function($query)
{
$query->where('year', '=', 1997);
})
->get();
});
Burada salınım yılı 2000 ile 2005 arasında olan veya 1997 yılında salınmış bir albüm istiyoruz.
Yukarıdaki metodla üretilen SQL şu şekildedir.
1
select * from `albums` where (`year` > ? and `year` < ?) or (`year` = ?)
Yukarıdaki sorgudan gelen sonuçlar şunlardır.
1
2
[
{
id: 3,
title: "Leaving Through The Window",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
3
4
5
6
7
8
9
},
{
id: 4,
title: "North",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
10
11
12
13
14
15
16
17
},
{
id: 5,
Eloquent Sorguları
title: "...Anywhere But Here",
artist: "The Ataris",
genre: "Punk Rock",
year: 1997
18
19
20
21
}
22
23
310
]
WhereIn
whereIn() metodu bir sütun değerinin bir değerler kümesi içinde mevcut olup olmadığını yoklamak
için kullanılabilir. Elinizde zaten bir olası değerler dizisi olduğunda bu gerçekten yararlı olur. Onu
nasıl kullanabileceğimizi görelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$values = array('Something Corporate', 'The Ataris');
return Album::whereIn('artist', $values)->get();
});
whereIn() metodunun ilk paramtresi üzerinde karşılaştırma yapmak istediğimiz sütundur. İkinci
değer içinde arama yapılacak değerler dizisidir.
Yukarıdaki sorguyla oluşan SQL şöyle gözükür.
1
select * from `albums` where `artist` in (?, ?)
Örnek sorgudan alacağımız sonuçlar koleksiyonu şöyledir.
1
2
[
{
id: 3,
title: "Leaving Through The Window",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
3
4
5
6
7
8
9
10
},
{
id: 4,
Eloquent Sorguları
title: "North",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
11
12
13
14
},
{
15
16
id: 5,
title: "...Anywhere But Here",
artist: "The Ataris",
genre: "Punk Rock",
year: 1997
17
18
19
20
21
}
22
23
311
]
whereIn() metodu da orWhereIn() şeklinde olağan metod alternatifine sahiptir ve birden çok kere
zincirlenebilir.
WhereNotIn
whereNotIn() metodu whereIn() metodunun tam karşıtıdır. Bu sefer bir değerler listesi verirsiniz
ve sütun değerleri bu küme içinde olmamalıdır.
Bir örnek üzerinden görelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$values = array('Something Corporate', 'The Ataris');
return Album::whereNotIn('artist', $values)->get();
});
Aynı şekilde, ilk parametre olarak karşılaştırma sütununu ve ikinci parametre olarak değerler
dizimizi geçtik.
İşte oluşan SQL.
1
select * from `albums` where `artist` not in (?, ?)
Son olarak, işte örnek sorgumuzun sonuç seti. Değerler dizimiz içindeki sanatçılar ile tanımlanmamış
tüm albümler.
Eloquent Sorguları
1
[
{
2
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
3
4
5
6
7
},
{
8
9
id: 2,
title: "Please",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 1993
10
11
12
13
14
},
{
15
16
id: 6,
title: "...Is A Real Boy",
artist: "Say Anything",
genre: "Indie Rock",
year: 2006
17
18
19
20
21
}
22
23
312
]
Bir kez daha, bir alternatif olarak orWhereNotIn() de bulunmaktadır.
WhereNull
Bir sütun değeri NULL olan satırları getirme ihtiyacınız olduğunda whereNull() sınırlaması kullanılabilir. Bir örneği kontrol edelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::whereNull('artist')->get();
});
whereNull() metodu için tek parametre bir null değer taşımasını umut ettiğimiz sütunun adıdır. Bu
sorgu için üretilen SQL’e bir göz atalım.
Eloquent Sorguları
1
313
select * from `albums` where `artist` is null
Şimdi de sorgunun sonuç kümesini görelim.
1
[ ]
Aa bu doğru, veritabanımızda hiçbir NULL değerimiz yok! Bu bölümü tekrar yazmak istemiyorum,
o yüzden burada hayal gücünüzü kullanmanız gerekecek. Eğer bir NULL değerli artist sütunumuz
olsaydı, bu durumda o satır sonuç kümemizde gözükecekti.
Evet, tahmin ettiğiniz gibi! Bir orWhereNull() metodu da bulunmaktadır.
WhereNotNull
Bu whereNotNull() metodu whereNull() metodunun tam tersidir, bu nedenle bu sefer bazı sonuçlar
görebileceğiz. Bu metod bir sütun değeri NULLa eşit olmayan satırları döndürecektir. Gelin daha
yakından bakalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::whereNotNull('artist')->get();
});
Metodun birinci ve tek parametresi sütun adıdır. Bu sorgu için üretilen SQL şöyledir.
1
select * from `albums` where `artist` is not null
Bu da örnek sorguya uyan sonuç kümesidir.
Eloquent Sorguları
1
[
{
2
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
3
4
5
6
7
},
{
8
9
id: 2,
title: "Please",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 1993
10
11
12
13
14
},
... 4 tane daha ...
15
16
17
314
]
Veritabanımızdaki albümlerin hepsi. Bunun nedeni artist sütunlarının hiçbirinin değerinin NULL
olmamasıdır.
Bir kez daha, or türü bir sorgu gerçekleştirmek için orWhereNotNull() metodu da vardır.
OrderBy
orderBy() metodu sorgunuzdan dönen sonuçları belirli bir sütunun değerine göre sıralamak için
kullanılabilir. Bir örnekle devam edelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('/', function()
{
return Album::where('artist', '=', 'Matt Nathanson')
->orderBy('year')
->get();
});
orderBy() metodu için birinci parametre ona göre sıralamak istediğimiz sütunun adıdır. Ön tanımlı
olarak sıralama artan sırada yapılacaktır.
İşte üretilen SQL.
Eloquent Sorguları
1
315
select * from `albums` where `artist` = ? order by `year` asc
Bu sorgudan elde edilen sonuç seti şu şekildedir.
1
[
{
2
id: 2,
title: "Please",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 1993
3
4
5
6
7
},
{
8
9
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
10
11
12
13
14
}
15
16
]
Harika, albümlerimiz salınma yılına göre artan sırada döndürüldü. Azalmasını istersek ne yapacağız? Dert etmeyin, bu Laravel kapsamındadır!
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('/', function()
{
return Album::where('artist', '=', 'Matt Nathanson')
->orderBy('year', 'desc')
->get();
});
Burada orderBy() metoduna desc değerinde ikinci bir parametre ekliyoruz. Bu, Laravel’e sonuçlarımızı azalan sırada elde etmek istediğimizi bildirir. İşte üretilen SQL.
1
select * from `albums` where `artist` = ? order by `year` desc
Bu da şimdiki sonuç kümesi.
Eloquent Sorguları
1
[
{
2
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
3
4
5
6
7
},
{
8
9
id: 2,
title: "Please",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 1993
10
11
12
13
14
}
15
16
316
]
Şaşırtıcı değişim! Sonuçlarımız şimdi azalan sırada.
orderBy() metodunu bu bölümdeki her türlü sınırlama kombinasyonuyla birlikte kullanabilirsiniz.
Ek sıralama sağlamak için ilave orderBy() metodları da kullanabilirsiniz, sıralama bu metodları
yazdığınız sırada yapılacaktır.
Take
Sonuç setini sınırlamak için take() metodu kullanılabilir. İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
return Album::take(2)
->get();
});
take() metodu için ilk parametre onunla sınırlamak istediğiniz satır sayısıdır. Yukarıdaki örnekte
sorgunun sadece iki sonuç nesnesi döndürmesini istiyoruz.
Bu sorgu ile üretilen SQL şudur.
Eloquent Sorguları
1
317
select * from `albums` limit 2
Son olarak, aldığımız sonuç kümesi budur.
1
[
{
2
id: 1,
title: "Some Mad Hope",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 2007
3
4
5
6
7
},
{
8
9
id: 2,
title: "Please",
artist: "Matt Nathanson",
genre: "Acoustic Rock",
year: 1993
10
11
12
13
14
}
15
16
]
Take metodu diğer sorgu sınırlamalarıyla kombinasyon halinde kullanılabilir. Karıştırın ve bulun!
Skip
take() metodu kullanıldığı zaman, sorgu sonuç kümesi için bir offset sağlamak için skip() metodu
kullanılabilir. İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('/', function()
{
return Album::take(2)
->skip(2)
->get();
});
skip() metodu bir offset sağlayacak tek bir parametre alır. Yukarıdaki örnekte, ilk iki satır sonuç
kümesine alınmayacaktır. Üretilen SQL burada.
Eloquent Sorguları
1
318
select * from `albums` limit 2 offset 2
Alacağımız sonuç kümesi şudur.
1
[
{
2
id: 3,
title: "Leaving Through The Window",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
3
4
5
6
7
},
{
8
9
id: 4,
title: "North",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
10
11
12
13
14
}
15
16
]
Gördüğünüz gibi veritabanındaki birinci ve ikinci satırlar atlanmış, üçüncü satırdan başlanmıştır.
Sihirli Where Sorguları
Evet, şimdi sihirli bir şeyler yapma zamanı! Artık, where() sorgusuyla bildiklerinizden daha
fazlası olmalı. where() sorgusu sonuç kümeniz içinde bir sütunu belirli bir değere kısıtlamaktan
sorumludur. İşte hatırlamanızı sağlayacak bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
return Album::where('artist', '=', 'Something Corporate')
->get();
});
Burada her satırının artist sütununun değeri Something Corporatee eşit olan bir sonuç kümesi
elde etme niyetindeyiz. Bu, veritabanı satırlarını arzu ettiğimiz sonuç kümesine kısıtlamanın güzel,
Eloquent Sorguları
319
temiz bir yoludur. Daha temiz hale gelebilir mi? Evet, olur mu olur! Sihirli where sorgu sözdizimi
kullanabiliriz.
Aşağıdaki örnekle daha yakından görelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::whereArtist('Something Corporate')->get();
});
Bir dakika, bu whereArtist() metodu nedir? Sorgu sınırlamaları bölümümüzde böyle bir şey
öğrenmedik. Pekala, bu yöntem biraz özeldir. Öncelikle sonucu görmek için / URI’sini ziyaret
edelim.
1
[
{
2
id: 3,
title: "Leaving Through The Window",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
3
4
5
6
7
},
{
8
9
id: 4,
title: "North",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
10
11
12
13
14
}
15
16
]
Bu bizim bir eşittir = operatorünün olduğu where() metoduyla aynı tarzda fonksiyon yapıyor gibi
görünüyor. Doğru, ne olduğunu açıklama vakti. Biliyorsunuz, where() eşittir sorgusu tüm sorgular
arasında belki de en sık kullanılanıdır ve bundan dolayı Taylor yararlı bir kısayol sağlamıştır.
Siz sadece where() metoduna sorgulamak istediğiniz sütunun adını ekleyebileceksiniz. Öncelikle,
karşılaştırmak istediğiniz alanın ilk harfini büyük harf yapmanız gerekiyor. Örneğimizde, artist
sütununu kullandık, bu nedenle oluşan metod adı whereArtist() oldu. Şayet alan adınız snake cased, örneğin shoe_size ise, bu durumda her kelimenin ilk harfini büyük harf, yani whereShoeSize()
yapmanız gerekiyor.
Bu sihirli where() metodunun tek parametresi sütun için istenen değerdir. Bir başka örneğe bakalım.
Eloquent Sorguları
1
320
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::whereTitle('North')->get();
});
title sütunun değeri North olan tüm ambümleri getirelim. İşte sorgudan elde edilen sonuç.
1
[
{
2
id: 4,
title: "North",
artist: "Something Corporate",
genre: "Piano Rock",
year: 2002
3
4
5
6
7
}
8
9
]
Harika, albümümüz orada! Sütun değerlerine göre ORM olguları elde etmek istediğiniz zamanlarda
sihirli where() sorgusunu aklınıza getirin.
Sorgu Scope’ları
Aynı sorguları tekrar tekrar kullandığınızı fark ettiğinizde sorgu scope’ları çok yararlı olabilir. Bir
örnek vererek başlayalım. İsimleri üç nokta ile başlayan iki albümü hatırladınız mı? ‘…Is A Real Boy’
ve ‘…Anywhere But Here’. Üç nokta ile başlayan albümleri getirmenin uygulamamızda sık yapılan
bir eylem olduğunu hayal edelim.
İstersek her seferinde albümleri sorgulayabiliriz, bunun gibi.
Eloquent Sorguları
1
321
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::where('title', 'LIKE', '...%')->get();
});
Bu son derece tekrarlayan bir şey olurdu değil mi? Kendi kendimizi tekrarlamak istemeyiz. Neden
bir Query Scope kullanmıyoruz? Haydi başlayalım. Öncelikle Album modelimize tekrar uğrayalım.
Şu anda aşağıdaki gibi görünür.
1
<?php
2
3
// app/models/Album.php
4
5
6
7
8
class Album extends Eloquent
{
public $timestamps = false;
}
Bu modele yeni bir metod ekleyelim. Artık modelimiz şöyle gözükecek.
1
<?php
2
3
// app/models/Album.php
4
5
6
7
class Album extends Eloquent
{
public $timestamps = false;
8
public function scopeTriplePeriod($query)
{
return $query->where('title', 'LIKE', '...%');
}
9
10
11
12
13
}
Modelimize scopeTriplePeriod() metodunu ekledik. Bu belirli bir fonksiyonu olan özel bir metod
olup sık kullanılan sorguları tekrarlı kullanmamıza yardım edecektir. Tüm scope metodları scope
kelimesiyle başlar ve bir tanımlayıcı ile devam eder. Metod tek bir parametre, bir $query nesnesi alır.
Eloquent Sorguları
322
Bu nesne, önceki kesimlerde keşfettiklerimize benzer şekilde sorgular inşa etmek için kullanılabilir.
Verdiğimiz örnekte, where() metodumuzdan değer döndürmek için return cümlesini kullanıyoruz.
Bu where() metodu önceki örneğimizdeki aynı şekli alacaktır.
Şimdi rota dosyamıza tekrar geri dönelim. Mevcut sorgumuzu değiştirelim. Yeni rota Closure’umuz
şöyle.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return Album::triplePeriod()->get();
});
where() sorgumuz yerine triplePeriod() scope’unu çağırıyoruz. Sonra da sonuçları getirmek için
bunun sonucu üzerinde sadece get() metodunu çağırıyoruz. Dikkat ederseniz metod adının scope
kısmı dahil edilmemiştir, bunu metod çağrılarınızın dışında tutmayı unutmayın! Sonuca bir göz
atalım.
1
[
{
2
id: 5,
title: "...Anywhere But Here",
artist: "The Ataris",
genre: "Punk Rock",
year: 1997
3
4
5
6
7
},
{
8
9
id: 6,
title: "...Is A Real Boy",
artist: "Say Anything",
genre: "Indie Rock",
year: 2006
10
11
12
13
14
}
15
16
]
Mükemmel, işte beklediğimiz sonuç kümesi. Kendinizi tekrar etmeyi azaltmak için istediğiniz kadar
scope kullanabilirsiniz.
Eloquent Koleksiyonları
Koleksiyonları seviyorum. Bende onlardan çok var. Çocukken yumurta kapları ve transformer
oyuncakları toplardım. Bir erişkin olarak video oyunları ve manga çizgi romanları biriktiriyorum.
Asosyal koleksiyonlar en iyisidir.
Laravel’in de kendi koleksiyonları var. Bir diğerine yardım etmeye ve topluluğu geliştirmeye hevesli
harika bir hayranlar koleksiyonu var. Ona katkıda bulunan muhteşem bir geliştiriciler koleksiyonu
var. İsminin nereden geldiği hakkında pek çoğu yanlış olan bir öyküler koleksiyonu var. Ayrıca
Eloquent Collection’ları da var.
Collection Sınıfı
Eloquent koleksiyonları Laravel’in Collection sınıfının sorgu sonuçlarıyla ilgili bazı yararlı metodlarla genişletilmiş halidir. Collection sınıfının kendisi bir nesneler dizisi için sadece bir sarıcıdır
fakat bu dizinin öğelerini yakalamanıza yardım eden bir grup ilginç metoda sahiptir.
Laravel üçte, çoklu sonuçlar sağlamakta kullanılan bir sorgu metodundan bir model olguları dizisi
döndürülürdü. Buna karşın, Laravel dörtte bunun yerine bir model olguları Collectionu alacaksınız.
Endişelenmeyin, bu bir dizinin bazı özelliklerini miras aldığı için, bir sonuçlar koleksiyonu boyunca
PHP’nin sunduğu döngü türleriyle tekrarlar yapmaya devam edebilirsiniz. Ancak, koleksiyon natif
bir tip değil bir sınıf olduğu için nesnede kullanılabilecek metodlar da vardır.
Collection sınıfında bulunan metodlara bir göz atalım. Örnek verilerimiz için önceki bölümdeki
albums tablosunu kullanacağız ve koleksiyonun aşağıdaki gibi bir Album::all() çağrısının sonucu
olduğunu varsayacağız.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
$collection = Album::all();
});
Collection Metodları
Collection sınıfında bulunan metodları görelim. Bu metodların bir kısmı ekleme ve bunların
anahtarlarına göre elementler getirme ile ilgilidir. Ancak, Eloquent sonuçları söz konusu olduğunda,
Eloquent Koleksiyonları
324
bu anahtarlar model olgularının temsil ettiği tablonun birincil anahtarlarıyla eşleştirilmez ve
böylece bu metodlar bizim için çok yararlı olacaklardır. Bunun yerine, kullanımları olan metodları
anlatacağım.
All
all() metodu Collection nesnesi tarafından kullanılan dahili bir dizinin yakalanması için kulla-
nılabilir. Bunun anlamı, eğer sonuçlarınızın Laravel 3 ile sağlananla aynı olmasını istiyorsanız, bu
durumda sadece all() metodunu çağırarak olgu dizinizi elde edeceksiniz demektir.
Sonucu var_dump() edelim.
Kodumuz şöyle.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$collection = Album::all();
var_dump($collection->all());
});
Ve işte sonuç.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
array (size=6)
0 =>
object(Album)[127]
public 'timestamps' => boolean false
protected 'connection' => null
protected 'table' => null
protected 'primaryKey' => string 'id' (length=2)
protected 'perPage' => int 15
public 'incrementing' => boolean true
protected 'attributes' =>
array (size=5)
'id' => int 1
'title' => string 'Some Mad Hope' (length=13)
'artist' => string 'Matt Nathanson' (length=14)
'genre' => string 'Acoustic Rock' (length=13)
'year' => int 2007
protected 'original' =>
Eloquent Koleksiyonları
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
325
array (size=5)
'id' => int 1
'title' => string 'Some Mad Hope' (length=13)
'artist' => string 'Matt Nathanson' (length=14)
'genre' => string 'Acoustic Rock' (length=13)
'year' => int 2007
protected 'relations' =>
array (size=0)
empty
protected 'hidden' =>
array (size=0)
empty
protected 'visible' =>
array (size=0)
empty
protected 'fillable' =>
array (size=0)
empty
protected 'guarded' =>
array (size=1)
0 => string '*' (length=1)
protected 'touches' =>
array (size=0)
empty
protected 'with' =>
array (size=0)
empty
public 'exists' => boolean true
protected 'softDelete' => boolean false
1 =>
object(Album)[128]
... DAHA TONLARCA BİLGİ ...
Gördüğünüz gibi, Eloquent model olgularımızdan oluşan bir dizimiz var.
First
Koleksiyonun first() metodu kümedeki ilk elemanı elde etmek için kullanılabilir. Bu, koleksiyonun
dahili dizisi içinde bulunan ilk eleman olacaktır.
Haydi görelim.
Eloquent Koleksiyonları
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$collection = Album::all();
var_dump($collection->first());
});
Sonucu görmek için şimdi / URI’sini ziyaret edelim. Ne olacağını bekliyorsunuz?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
object(Album)[127]
public 'timestamps' => boolean false
protected 'connection' => null
protected 'table' => null
protected 'primaryKey' => string 'id' (length=2)
protected 'perPage' => int 15
public 'incrementing' => boolean true
protected 'attributes' =>
array (size=5)
'id' => int 1
'title' => string 'Some Mad Hope' (length=13)
'artist' => string 'Matt Nathanson' (length=14)
'genre' => string 'Acoustic Rock' (length=13)
'year' => int 2007
protected 'original' =>
array (size=5)
'id' => int 1
'title' => string 'Some Mad Hope' (length=13)
'artist' => string 'Matt Nathanson' (length=14)
'genre' => string 'Acoustic Rock' (length=13)
'year' => int 2007
protected 'relations' =>
array (size=0)
empty
protected 'hidden' =>
array (size=0)
empty
protected 'visible' =>
array (size=0)
empty
326
Eloquent Koleksiyonları
31
32
33
34
35
36
37
38
39
40
41
42
43
44
327
protected 'fillable' =>
array (size=0)
empty
protected 'guarded' =>
array (size=1)
0 => string '*' (length=1)
protected 'touches' =>
array (size=0)
empty
protected 'with' =>
array (size=0)
empty
public 'exists' => boolean true
protected 'softDelete' => boolean false
Aynen öyle! Bu, albümlerimizden birini temsil eden tek bir model olgusu. Tabloya eklediğimiz ilk
satır. Dikkat ederseniz, ilk satır olmasının nedeni sorgunun bir parçası olarak all() metodunu
kullanmış olmamız. Eğer farklı bir sorgu kullanmış olsaydık, o zaman sonuç kümemizdeki dizi farklı
bir sırada olabilirdi ve first() çağrısı farklı bir sonuç verebilirdi.
Last
Bu çok açık olmalı. first() metodu koleksiyonların dahili dizisi içindeki ilk değeri elde etmek için
kullanılıyor, demek ki last() metodu dizinin son öğesini getirmek durumunda.
Haydi bu teoriyi ispatlayalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$collection = Album::all();
var_dump($collection->last());
});
İşte / URI’sinden gelen sonuç.
Eloquent Koleksiyonları
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
object(Album)[138]
public 'timestamps' => boolean false
protected 'connection' => null
protected 'table' => null
protected 'primaryKey' => string 'id' (length=2)
protected 'perPage' => int 15
public 'incrementing' => boolean true
protected 'attributes' =>
array (size=5)
'id' => int 6
'title' => string '...Is A Real Boy' (length=16)
'artist' => string 'Say Anything' (length=12)
'genre' => string 'Indie Rock' (length=10)
'year' => int 2006
protected 'original' =>
array (size=5)
'id' => int 6
'title' => string '...Is A Real Boy' (length=16)
'artist' => string 'Say Anything' (length=12)
'genre' => string 'Indie Rock' (length=10)
'year' => int 2006
protected 'relations' =>
array (size=0)
empty
protected 'hidden' =>
array (size=0)
empty
protected 'visible' =>
array (size=0)
empty
protected 'fillable' =>
array (size=0)
empty
protected 'guarded' =>
array (size=1)
0 => string '*' (length=1)
protected 'touches' =>
array (size=0)
empty
protected 'with' =>
array (size=0)
empty
328
Eloquent Koleksiyonları
43
44
329
public 'exists' => boolean true
protected 'softDelete' => boolean false
Güzel! Bu internal dizideki son albüm. Aynı zamanda, veritabanının son satırı ama bu sadece
koleksiyonu elde etmek için kullandığımız sorgu nedeniyledir.
Shift
shift() metodu first() metoduna benzer. Koleksiyonun dahili dizisindeki ilk değeri getirecektir.
Ancak, first() metodundan farklı olarak, shift() metodu aynı zamanda bu değeri diziden
çıkartacaktır. Küçük bir test yaparak bunu ispatlayalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', function()
{
$collection = Album::all();
var_dump(count($collection));
var_dump($collection->shift());
var_dump(count($collection));
});
Testimiz bir değeri shift() etmemizden önce ve sonra PHP’nin count() metodunu kullanarak
koleksiyon içindeki elemanların sayısını gösterecek. Koleksiyonun bir dizinin birçok özelliğini miras
aldığını unutmayın, bu bize bir koleksiyonda count() metodu kullanma imkanı veriyor.
Testimizin sonucuna bir göz atalım.
1
2
3
4
5
6
7
8
9
10
11
12
int 6
object(Album)[127]
public 'timestamps' => boolean false
protected 'connection' => null
protected 'table' => null
protected 'primaryKey' => string 'id' (length=2)
protected 'perPage' => int 15
public 'incrementing' => boolean true
protected 'attributes' =>
array (size=5)
'id' => int 1
'title' => string 'Some Mad Hope' (length=13)
Eloquent Koleksiyonları
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
330
'artist' => string 'Matt Nathanson' (length=14)
'genre' => string 'Acoustic Rock' (length=13)
'year' => int 2007
protected 'original' =>
array (size=5)
'id' => int 1
'title' => string 'Some Mad Hope' (length=13)
'artist' => string 'Matt Nathanson' (length=14)
'genre' => string 'Acoustic Rock' (length=13)
'year' => int 2007
protected 'relations' =>
array (size=0)
empty
protected 'hidden' =>
array (size=0)
empty
protected 'visible' =>
array (size=0)
empty
protected 'fillable' =>
array (size=0)
empty
protected 'guarded' =>
array (size=1)
0 => string '*' (length=1)
protected 'touches' =>
array (size=0)
empty
protected 'with' =>
array (size=0)
empty
public 'exists' => boolean true
protected 'softDelete' => boolean false
int 5
Görebileceğiniz gibi, Album model olgumuzu alıyoruz. Bu, first() metodunu kullanarak aldığımızın
aynısıdır. Bununla birlikte, eğer iki integer değere bakarsanız, dizinin boyutunun azalmış olduğunu
fark edeceksiniz. Bunun sebebi olgunun sadece metodtan döndürülmüş olmakla kalmayıp, aynı
zamanda diziden de çıkartılmış olmasıdır.
Eloquent Koleksiyonları
331
Pop
Pop onlarca yıldır bir müzik türüdür ve genel olarak alkol tüketimini artırmak için veya gençlerin
vahşi hayallerini teşvik etmek için kullanılmaktadır.
Aa evet, pop aynı zamanda Eloquent model olgu koleksiyonunda bir metodtur. Bu metod shift()
metoduna benzer şekilde çalışır, şöyle ki, dahili dizinin en sonundaki değeri döndürür ve onu diziden
kaldırır. Testimizle onun sonucunu da yakalayalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/', function()
{
$collection = Album::all();
var_dump(count($collection));
var_dump($collection->pop());
var_dump(count($collection));
});
İşte sonuç.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int 6
object(Album)[138]
public 'timestamps' => boolean false
protected 'connection' => null
protected 'table' => null
protected 'primaryKey' => string 'id' (length=2)
protected 'perPage' => int 15
public 'incrementing' => boolean true
protected 'attributes' =>
array (size=5)
'id' => int 6
'title' => string '...Is A Real Boy' (length=16)
'artist' => string 'Say Anything' (length=12)
'genre' => string 'Indie Rock' (length=10)
'year' => int 2006
protected 'original' =>
array (size=5)
'id' => int 6
'title' => string '...Is A Real Boy' (length=16)
'artist' => string 'Say Anything' (length=12)
Eloquent Koleksiyonları
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
332
'genre' => string 'Indie Rock' (length=10)
'year' => int 2006
protected 'relations' =>
array (size=0)
empty
protected 'hidden' =>
array (size=0)
empty
protected 'visible' =>
array (size=0)
empty
protected 'fillable' =>
array (size=0)
empty
protected 'guarded' =>
array (size=1)
0 => string '*' (length=1)
protected 'touches' =>
array (size=0)
empty
protected 'with' =>
array (size=0)
empty
public 'exists' => boolean true
protected 'softDelete' => boolean false
int 5
Dizinin son elemanını alıyoruz ve count() metodlarımızdan gelen sonuç, dizinin uzunluğunun
azalmış olduğunu gösteriyor. Bunun nedeni, elde ettiğimiz değerin dahili diziden çıkartılmış
olmasıdır.
Each
Javascript veya PHP için ‘Underscore’ kitaplığını kullanmışsanız, sonraki birkaç metod size tanıdık
gelecektir. Sonuçlarımızda dolaşmak için bir foreach() döngüsü oluşturmak yerine, each() metoduna bir Closure geçebiliriz.
Bu en iyi şekilde bir örnekle açıklanabilir.
Eloquent Koleksiyonları
1
333
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
12
Route::get('/', function()
{
$collection = Album::all();
$collection->each(function($album)
{
var_dump($album->title);
});
});
Closure’umuz tekrarlama içindeki güncel nesne için bir yer tutucu olacak bir parametre alıyor. Bu
closure daha sonra each() metoduna geçiliyor. Her tekrar için modelin ‘title’ sütununun değerini
dump ediyoruz.
Sonucu inceleyelim.
1
2
3
4
5
6
string
string
string
string
string
string
'Some Mad Hope' (length=13)
'Please' (length=6)
'Leaving Through The Window' (length=26)
'North' (length=5)
'...Anywhere But Here' (length=20)
'...Is A Real Boy' (length=16)
Süper! Bunlar istediğimiz gibi albüm isimleri.
Map
map() fonksiyonu each() metoduna benzer bir şekilde iş görür. Ancak, koleksiyon elemanlarında
dolaşmak ve onlarla iş yapmak, bir sonuç olarak yeni bir koleksiyon döndürmek için kullanılabilir.
Albüm isimlerimizin tümünün başına An ode to a fair panda: getirmek istediğimizi düşünelim.
Bunu map() fonksiyonunu kullanarak yapabiliriz.
Eloquent Koleksiyonları
1
334
<?php
2
3
// app/routes.php
4
5
6
7
Route::get('/', function()
{
$collection = Album::all();
8
$new = $collection->map(function($album)
{
return 'An ode to a fair panda: '.$album->title;
});
9
10
11
12
13
var_dump($new);
14
15
});
İlk önce Collection::map() metodunun değerini bir değişkene atıyoruz. Daha sonra koleksiyonu
each() metoduyla aynı şekilde dolaşıyoruz fakat bu defa yeni koleksiyonda bulunmasını istediğimiz
her bir değeri döndürüyoruz.
İşte sonuç.
1
2
3
4
5
6
7
8
9
object(Illuminate\Database\Eloquent\Collection)[117]
protected 'items' =>
array (size=6)
0 => string 'An ode to a fair panda: Some Mad Hope' (length=37)
1 => string 'An ode to a fair panda: Please' (length=30)
2 => string 'An ode to a fair panda: Leaving Through The Window' (length=50)
3 => string 'An ode to a fair panda: North' (length=29)
4 => string 'An ode to a fair panda: ...Anywhere But Here' (length=44)
5 => string 'An ode to a fair panda: ...Is A Real Boy' (length=40)
Şimdi, tekrarcı map() metodumuzu kullanarak inşa ettiğimiz stringlerden oluşan bir koleksiyonumuz
oldu.
Filter
filter() metodu bir Closure kullanmak suretiyle, yeni oluşturulacak koleksiyonda yer alacak
eleman sayısını azaltmak için kullanılabilir. Eğer Closure’un sonucu true ise bu durumda tekrardaki
güncel eleman yeni oluşturulan koleksiyona verilecektir. Eğer Closure’un tekrarlaması bir false
döndürürse veya hiçbir şey döndürmezse, o zaman bu eleman yeni koleksiyonda olmayacaktır.
Bu bir örnekle daha kolay anlaşılabilir.
Eloquent Koleksiyonları
1
335
<?php
2
3
// app/routes.php
4
5
6
7
Route::get('/', function()
{
$collection = Album::all();
8
$new = $collection->filter(function($album)
{
if ($album->artist == 'Something Corporate') {
return true;
}
});
9
10
11
12
13
14
15
var_dump($new);
16
17
});
filter() metodu ve Closure’umuzla koleksiyonu dolaşıyoruz. Her bir tekrarda, eğer model olgumuzdaki artist sütununun değeri ‘Something Corporate’ ise true döndürüyoruz. Bu, o model
olgusunun yeni koleksiyonda olacağını işaret eder.
İşte sonuç.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
object(Illuminate\Database\Eloquent\Collection)[117]
protected 'items' =>
array (size=2)
2 =>
object(Album)[135]
public 'timestamps' => boolean false
protected 'connection' => null
protected 'table' => null
protected 'primaryKey' => string 'id' (length=2)
protected 'perPage' => int 15
public 'incrementing' => boolean true
protected 'attributes' =>
array (size=5)
'id' => int 3
'title' => string 'Leaving Through The Window' (length=26)
'artist' => string 'Something Corporate' (length=19)
'genre' => string 'Piano Rock' (length=10)
'year' => int 2002
protected 'original' =>
Eloquent Koleksiyonları
336
array (size=5)
'id' => int 3
'title' => string 'Leaving Through The Window' (length=26)
'artist' => string 'Something Corporate' (length=19)
'genre' => string 'Piano Rock' (length=10)
'year' => int 2002
20
21
22
23
24
25
3 =>
object(Album)[136]
public 'timestamps' => boolean false
protected 'connection' => null
protected 'table' => null
protected 'primaryKey' => string 'id' (length=2)
protected 'perPage' => int 15
public 'incrementing' => boolean true
protected 'attributes' =>
array (size=5)
'id' => int 4
'title' => string 'North' (length=5)
'artist' => string 'Something Corporate' (length=19)
'genre' => string 'Piano Rock' (length=10)
'year' => int 2002
protected 'original' =>
array (size=5)
'id' => int 4
'title' => string 'North' (length=5)
'artist' => string 'Something Corporate' (length=19)
'genre' => string 'Piano Rock' (length=10)
'year' => int 2002
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Yer kazanmak amacıyla sonuçları bir miktar kısalttım ama ‘Something Corporate’ın iki albümüne
sahip olduğumuzu açıkça görebiliyorsunuz.
Sort
sort() metodu koleksiyonu sıralamak için kullanılabilir. İki değer arasında bir karşılaştırmayı
temsil eden ve integer değerler kullanan uasort() PHP metodu Closure’a yardım eder. Closure
iki parametre alır, onlara A ve B diyelim. Closure’umuzdan integer bir sonuç verecek şekilde,
kurallarımızı uygularız.
Eğer A > B ise 1 döndürürüz. Eğer A < B ise -1 döndürürüz. Eğer A = B ise 0 döndürürüz.
İşte bir örnek.
Eloquent Koleksiyonları
1
337
<?php
2
3
// app/routes.php
4
5
6
7
Route::get('/', function()
{
$collection = Album::all();
8
$collection->sort(function($a, $b)
{
$a = $a->year;
$b = $b->year;
if ($a === $b) {
return 0;
}
return ($a > $b) ? 1 : -1;
});
9
10
11
12
13
14
15
16
17
18
$collection->each(function($album)
{
var_dump($album->year);
});
19
20
21
22
23
});
İki parametreli bir closure sağlıyoruz ve bunlar koleksiyondaki iki albümü temsil ediyor. Karşılaştırmayı kolaylaştırmak için önce $a ve $b‘ye year sütunlarını atıyoruz. Eğer $a ile $b eşit ise 0
döndürüyoruz. Şayet $a, $bden daha büyükse 1, aksi takdirde -1 döndürüyoruz.
Bu metod yıkıcıdır. Orijinal koleksiyonu değiştirir. Yeni sıralamayı göstermek için koleksiyonu
dolaşarak year değerlerini dump ediyoruz. İşte sonuç.
1
2
3
4
5
6
int
int
int
int
int
int
1993
1997
2002
2002
2006
2007
Görebileceğiniz gibi, albümlerimiz şimdi yeara göre artan sırada düzenlenmiştir. Burada size bir
ev ödevi veriyorum. Yukarıdaki örneğin sadece bir karakterini değiştirerek albüm yıllarını azalan
sırada göstermeye çalışın. Yapabileceğinizi düşünüyorum!
Eloquent Koleksiyonları
338
Reverse
reverse() metodu dahili dizideki modelleri tersine çevirmek için kullanılabilir. Bu gerçekten bir
örnek gerektiriyor mu? Ee devam edelim, siz her şeyden önce yakışıklı okuyucularsınız…
1
<?php
2
3
// app/routes.php
4
5
6
7
Route::get('/', function()
{
$collection = Album::all();
8
$collection->each(function($album)
{
var_dump($album->title);
});
9
10
11
12
13
$reverse = $collection->reverse();
14
15
$reverse->each(function($album)
{
var_dump($album->title);
});
16
17
18
19
20
});
İlk olarak albümlerin hepsini dolaşarak isimlerini çıktılıyoruz. Sonra koleksiyonu tersine çeviriyoruz
ve yeni koleksiyonu $reverse değişkenine atıyoruz. Ondan sonra da, ne değişmiş olduğunu görmek
için $reverse koleksiyonunu dolaşıyoruz.
Sonuç burada.
1
2
3
4
5
6
string
string
string
string
string
string
'Some Mad Hope' (length=13)
'Please' (length=6)
'Leaving Through The Window' (length=26)
'North' (length=5)
'...Anywhere But Here' (length=20)
'...Is A Real Boy' (length=16)
7
8
9
10
11
string '...Is A Real Boy' (length=16)
string '...Anywhere But Here' (length=20)
string 'North' (length=5)
Eloquent Koleksiyonları
12
13
14
339
string 'Leaving Through The Window' (length=26)
string 'Please' (length=6)
string 'Some Mad Hope' (length=13)
Harika! Koleksiyonumuz ters çevrilmiş.
Merge
merge() metodu iki koleksiyonu birleştirmek için kullanılabilir. Metodun tek parametresi birleştiri-
lecek koleksiyondur. İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
Route::get('/', function()
{
$a = Album::where('artist', '=', 'Something Corporate')
->get();
$b = Album::where('artist', '=', 'Matt Nathanson')
->get();
11
$result = $a->merge($b);
12
13
$result->each(function($album)
{
echo $album->title.'<br />';
});
14
15
16
17
18
});
Yukarıdaki örnekte iki sorgulama yapıyoruz. Sonuç kümelerinden $a, ‘Something Corporate’
sanatçısının albümlerinden oluşan bir koleksiyondur. Küme $b ise ‘Matt Nathanson’ albümlerini
içerir. Bu koleksiyonları birleştirmek için merge() metodunu kullanıyoruz ve sonucu $result adını
verdiğimiz yeni bir koleksiyona atıyoruz. Sonra da bir each() döngüsü içinde albüm başlıklarını
ekrana basıyoruz.
İşte sonuçlar.
Eloquent Koleksiyonları
1
2
3
4
340
Leaving Through The Window
North
Some Mad Hope
Please
Slice
slice() metodu PHP’nin array_slice() fonksiyonunun eşdeğeridir. Bir koleksiyon offseti kulla-
narak modellerin bir alt kümesini oluşturmak için kullanılabilir. Karışık mı geldi? Şuna bir göz atın.
1
<?php
2
3
// app/routes.php
4
5
6
7
Route::get('/', function()
{
$collection = Album::all();
8
$sliced = $collection->slice(2, 4);
9
10
$sliced->each(function($album)
{
echo $album->title.'<br />';
});
11
12
13
14
15
});
slice() metodunu kullanarak yeni bir koleksiyon oluşturuyoruz. Birinci parametre yeni alt kü-
menin başlayacağı pozisyondur. Burada dilime dizinin ikinci elementinde başlamasını söylüyoruz.
İkinci opsiyonel parametre koleksiyonun uzunluğudur. Örneğimizde slice() metoduna, dizinin
ikinci elemanından sonra dört eleman istediğimizi söylüyoruz.
Şimdi sonuca bir göz atalım.
1
2
3
4
Leaving Through The Window
North
...Anywhere But Here
...Is A Real Boy
slice() metoduna negatif bir değer geçebileceğinizi biliyor musunuz? Örneğin…
341
Eloquent Koleksiyonları
1
<?php
2
3
// app/routes.php
4
5
6
7
Route::get('/', function()
{
$collection = Album::all();
8
$sliced = $collection->slice(-2, 4);
9
10
$sliced->each(function($album)
{
echo $album->title.'<br />';
});
11
12
13
14
15
});
Birinci parametre olarak -2 geçmekle, koleksiyonun koleksiyonun sondan iki elemandan başlatılmasını söylüyoruz. İşte sonuç.
1
2
...Anywhere But Here
...Is A Real Boy
Bir saniye, biz dört model getirmesini istememiş miydik?
İstedik ama dizinin sonundan itibaren ikinci elemanda olduğumuz için, getirebileceğimiz sadece iki
eleman var. slice() metodu koleksiyonun etrafını sarmaz.
IsEmpty
isEmpty() metodu konteynerin içinde elemanlar olup olmadığını yoklamak için kullanılabilir. Ne
geleceğini görmediğinizden eminim! Bu metod hiçbir değer almaz ve boolean bir sonuç döndürür.
İşte bir örnek.
Eloquent Koleksiyonları
1
342
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
// Bu sorgu elemanlar döndürecektir.
$a = Album::all();
9
// Bu sorgu döndürmeyecektir.
$b = Album::where('title', '=', 'foo')->get();
10
11
12
var_dump($a->isEmpty());
var_dump($b->isEmpty());
13
14
15
});
Aha, kurnazca bir tuzak! İlk sorgunun sonuçlar döndüreceğini ve ikinci sorgunun döndürmeyeceğini
biliyoruz. Her ikisinden de ne alacağımızı görmek için isEmpty() metodunun sonucunu dump
ediyoruz.
1
2
boolean false
boolean true
Birinci isEmpty() boolean false döndürür, çünkü dizinin içinde elemanlar var. İkinci koleksiyon
boştur ve isEmpty() metodu boolean true döndürür.
ToArray
toArray() metodu koleksiyonun dahili dizisini döndürmek için kullanılabilir. Ayrıca, dizi içerisinde
bir diziye dönüştürülebilecek elemanlar, örneğin nesneler de bu süreç sırasında dönüştürülecektir.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$collection = Album::all();
var_dump( $collection->toArray() );
});
Sonucumuz burada.
Eloquent Koleksiyonları
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
343
array (size=6)
0 =>
array (size=5)
'id' => int 1
'title' => string 'Some Mad Hope' (length=13)
'artist' => string 'Matt Nathanson' (length=14)
'genre' => string 'Acoustic Rock' (length=13)
'year' => int 2007
1 =>
array (size=5)
'id' => int 2
'title' => string 'Please' (length=6)
'artist' => string 'Matt Nathanson' (length=14)
'genre' => string 'Acoustic Rock' (length=13)
'year' => int 1993
2 =>
array (size=5)
'id' => int 3
'title' => string 'Leaving Through The Window' (length=26)
'artist' => string 'Something Corporate' (length=19)
'genre' => string 'Piano Rock' (length=10)
'year' => int 2002
... ve diğerleri ...
Görebileceğiniz gibi, sadece koleksiyonu temsil eden bir dizi döndürülmüş değil, aynı zamanda
içindeki model olguları da dizilere dönüştürülmüş.
ToJson
toJson() metodu bir koleksiyonu onun içeriğini temsil etmekte kullanılabilecek bir JSON stringine
dönüştürecektir. Önceki bölümde bir JSON cevabı sunması için koleksiyonların doğrudan bir rota
Closure’u veya bir controller eyleminden nasıl döndürüleceğini öğrenmiştik. Koleksiyonun bir
JSON’a dönüştürülmesine imkan veren toString() metodu, dahili olarak toJson() metoduna bir
çağrı yapmaktadır.
Bir örnekle bir göz atalım.
Eloquent Koleksiyonları
1
344
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$collection = Album::all();
var_dump( $collection->toJson() );
});
Yukarıdaki örnekten gelen sonuç şöyledir.
string ‘[{“id”:1,”title”:”Some Mad Hope”,”artist”:”Matt Nathanson”,”genre”:”Acoustic Rock”,”year”:2007},{“id”:2,”title”:”
Nathanson”,”genre”:”Acoustic Rock”,”year”:1993},{“id”:3,”title”:”Leaving Through The Window”,”artist”:”Something
Corporate”,”genre”:”Piano Rock”,”year”:2002},{“id”:4,”title”:”North”,”artist”:”Something Corporate”,”genre”:”Piano
Rock”,”year”:2002},{“id”:5,”title”:”…Anywhere But Here”,”artist”:”The Ataris”,”genre”:”Punk Rock”,”year”:1997},{“id”:6,
A Real Boy”,”artist”:”Say Anything”,”genre”:”Indie Rock”,”year”:2006}]’ (length =570)
Bu, tüm koleksiyonumuzun bir JSON stringi ile temsil edilmiş halidir.
Count
Önceki örneklerimizin birinde koleksiyonun dahili dizisinde yer alan model olgularının adedini
saymak için PHP’nin count() metodunu kullanmıştım. Hay aptal kafam! Koleksiyonun kendi
count() metodunu tamamen unutmuşum. Açıkçası aynı şeyi yapar. Göstereyim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$collection = Album::all();
var_dump( $collection->count() );
});
İşte sonuç. Sabırsızlandığınıza bahse girerim!
int 6
Sonuç aynı, bu durumda hangisini kullanmalıyız? Bizim için hangisi daha uygun. Bu kararı size
bırakıyorum. Şu ana kadar kitapta 300 sayfadan fazla ilerlediniz, yaşlı ve bilgesiniz. Ama hala
yakışıklısınız. İyi iş çıkardınız!
Eloquent Koleksiyonları
345
En İyi Uygulamalar
Koleksiyonlardaki metodların bir kısmı sorgu oluşturucusunda bulunanların kopyalarıdır. Örneğin,
sadece tek bir model olgusu elde etmek için eloquent sorguları oluştururken first() metodunu
kullanabiliyoruz. Aynı zamanda, bir koleksiyonun içindeki ilk elemanı elde etmek için koleksiyonun
first() metodunu da kullanabiliyoruz. Aşağıdaki örneğe yakından bakalım.
1
2
Album::all()->first();
Album::first();
Bu iki satırın her ikisi de aynı sonuca varır. Bir sonuç olarak albums tablosundaki ilk eleman
döndürülecektir. Öyleyse hangisini kullanmalıyız?
Peki, her zaman olduğu gibi, cevap ‘duruma bağlı’. Evet, kimsenin bu cevabı duymaktan hoşlanmadığını biliyorum fakat bazen gerçek böyledir. Şimdi iki farklı senaryo görelim.
Birinci senaryoda, şablonlarımızın birinde veritabanımızda sakladığımız ilk albümün ismini göstermek istiyoruz. Kesinlikle, bir problem değil! Eloquent sorgu oluşturucusu üzerinde first()
metodunu kullanabiliriz.
1
Album::first();
İkinci senaryoda, tüm albüm isimlerini göstermek istiyoruz ama aynı zamanda ilk albümün ismini
kendi küçük kutusu içerisinde de göstermek istiyoruz. Örneğin, ‘yılın albümü’ kesimi. Tamam,
sanırım şöyle bir şeyler yapabiliriz.
1
2
$allAlbums = Album::all();
$albumOfTheYear = Album::first();
Gerekeni yapacağından eminim ama bu metod veritabanında iki sorgu çalıştıracak. Veritabanı sunucusuna gönderilen sorgu sayısının artması, uygulama performasını hızla azaltmanın bir yoludur.
Bizim örneğimizde önemli olmayabilir ama kurumsal yazılımınız içinde beklenen her saniye para
kaybı demektir.
Sorgu sorumluluklarından bir kısmını koleksiyona kaydırmak suretiyle veritabanı sunucusu üzerindeki baskıyı bir miktar hafifletebiliriz ve sorgu sayısını azaltabiliriz.
1
2
$allAlbums = Album::all();
$albumOfTheYear = $allAlbums->first();
Eloquent Koleksiyonları
346
Albümlerimizin hepsini eskisi gibi getiriyoruz ama yılın albümünü elde etmek için bu sefer
koleksiyonun first() metodunu kullanıyoruz. Bu yalnızca tek sorgu ile sonuçlanır.
Bir sorgunun koleksiyon üzerinde mi yoksa veritabanı üzerinde mi yapılacağının seçimi bir tercih
meselesidir. Veritabanında sorgulama yapılması, koleksiyon üzerinde yapılsaydı web sunucusunda
yüksek CPU / bellek kullanımına yol açabilecek daha hızlı ve daha karmaşık aramalara imkan
verecektir. Yine aynı şekilde, koleksiyonun etkin biçimde kullanılmasıyla da, uygulamanız daha az
SQL sorgusu oluşundan yararlanacaktır.
Ne zaman koleksiyon helper metodu kullanacağınıza karar verirken en iyi hükmü verin. Size
güveniyorum!
Eloquent İlişkileri (Relationships)
Eloquent şehrinde soğuk ve yağmurlu bir geceydi. Yağmur damlaları, Zack’ın ofisinin penceresinden
umutsuz kalanların gözyaşları gibi akıp, sokaklara iniyordu. Eloquent şehri bir zamanlar huzurlu bir
yerdi ve her şey saf ve temiz görünüyordu. Bozulmalar başladı. Barış ortamı yerini çete savaşları,
kaçakçılık ve diğer suçlara bırakmıştı. Yerel kolluk kuvvetleri uzun bir zaman önce satın alınmıştı
ve artık sokaklardaki şiddete kör gözlerle bakıyordu.
Zack Kitzmiller bir zamanlar Eloquent şehri polis kuvvetlerinin bir parçasıydı ama bu bozuk
şehirdeki son dürüst adam olarak işini bırakmak zorunda kalmıştı ve şehri kendi bildiği yolla
düzeltmeye girişti. Bir özel dedektif oldu.
Zack 14.üncü kattaki ofisinde, pencerenin yakınında eski meşe masasında oturuyordu, masada bir
şişe viski ve ucuz bir puro vardı. Ofis de tıpkı Zack gibi çok perişan vaziyetteydi, bir süredir hiç
temizlenmemişti. Ucuzdu ve kapıdan pek para geçtiği yoktu.
Tık tık.
Yoksa oldu mu? Kapı aralığından uzun boylu güzel bir sarışın adımlarını attı. Zack yüzünü bu melek
kadına çevirdi.
“Dedektif siz misiniz?” diye sordu gizemli sarışın.
“Adım Zack Kitzmiller güzelim ama sen bana istediğin gibi hitap edebilirsin.” dedi Zack, sakalla
kaplı yüzünde bir sırıtmayla.
“Bay Kitzmiller, benim adım Pivoté Tableux ve kocamı öldüren adamı arıyorum. Cinayet suçu Enrico
Barnez’e aittir (belongs to).”
“Enrico Barnez şehirdeki en azılı uyuşturucu baronudur, çok iyi gizlenir ve onu bulmak kolay
olmayacak.” diye homurdandı Zack. “Bu çok maliyetli olacak, bunu karşılayacak paran olduğundan
emin misin?”
“Bay Kitzmiller, para bir problem olmayacak. Çok param var (has many)” diyerek küçümsedi Pivoté.
“Duymak istediğim tek şey buydu.” dedi Zack soğuk bir gülümsemeyle.
Zack fötr şapkasını ve uzun ceketini giydi ve çıktı.
Zack liman ambarının ana girişine yürüdü. Parçalanmış gemi ve tanınamaz vaziyetteki taşıt
parçaları bütün zemine yayılmıştı.
Eloquent İlişkileri (Relationships)
348
“Sen Messy Stinkman misin?” diye bağırdı Zack, kaymış görünümlü gemi işçisine.
“Beyki oyum, beyki deyilimn.. kimin soğduğuna bağlı.”
“Patronunu arıyorum. Enrico Barnez. Onu nerede bulabilirim?” dedi Zack, sert bir biçimde.
“Bakıyn, benim patronun birçoğ (has many) teylikeli arkadaşı var. Onu nerede bulacağını söylersem
nasıl güvende kalırım? Bak bu ambar hepsi onun (belongs to). Her şey onun. Üzgünüm dostum, sağa
yardım edemem.”
Zack küçük bir gövde gösterisi olmadan bay Stinkman’dan daha fazla bilgi alamayacağı hükmüne
vardı. Yakındaki bir eksozu eline aldı ve Messy dışındaki cehennemin yedi gölgesini dövmeyi
başardı. Zack Kitzmiller tam bir mangal yüreklidir.
“Pekalağ, verejeğim. Enrico’nun nerede olduğu öğrenmek istiyoğsun? Arkanı dön.” diye mızırdandı
Messy.
Zack yavaşça arkaya döndü ve göğsü Enrico Barnez’in yüzüne geldi. Biliyorsunuz, Enrico güçlü
bir adamdır. Çok güçlü ama çok kısa bir adam. Onun doğal avları canavarlar ve vahşi sincaplardı.
Eloquent şehrinin sakinlerini saymıyorum bile.
“Yabancı, beni neden arıyorsun?”
Zack önünde duran adamın ne kadar tehlikeli biri olduğunu biliyordu. Cevap vermemeye karar
verdi, onun yerine uzun ceketinin cebinden bir silah çekti ve Enrico’nun alnına nişanladı.
Yeterince hızlı değildi, Enrico da silahını çekti. Zack and Enrico’yu bir soğukluk almıştı. Her iki
adam da uzun bir nefes aldılar ve derin bir nefes verdiler. Bir silah ateşlendi ve bir beden yere düştü.
Hayat sadece kek ve pastalardan ibaret değildir.
İlişkilere Giriş
Önceki bölümlerde veritabanı tablolarımızda saklanan satırların nesneler olarak temsil edilmesini
öğrenmiştik. Sınıf olguları tek bir satırı temsil ediyordu. Bunun anlamı nesneleri en küçük formuna
ayırıyoruz demektir. Kitap, Meyve, Kalem, her ne için iseler o.
Bu nesneler basit olduklarından dolayı, onlarla ilişkili veriler saklamak istediğimizde yeni nesneler
oluşturmamız ve onlar arasında bir ilişki kurmamız gerekiyor. İşte ‘relationship’ ile anlatmak
istediğim budur? Sarılmalar, öpüşmeler ve terli yatak çarşafları tarzında ilişkiyi kast etmiyorum.
Bu tarz bir ilişki en iyi şekilde bir örnek ile anlaşılacaktır.
Önceki bölümdeki Book modelimizi ele alalım. Kitapları bir an düşünürsek, aklımıza bunların birisi
tarafından yazılmış olması gerektiği gelir. Benim gerçekte mevcut olmadığıma inanmak istediğinizi
biliyorum ama acı gerçek şu ki ben varım. Oralarda bir yerlerde Laravel fanatiği çılgın bir İngiliz
adam var. Orada başka yazarlar da var, sadece ben değil.
Tamam, bir kitabın bir yazara ait (belongs to) olduğunu biliyoruz. İlk ilişkimiz oldu. Yazarın kitaba
bir ‘bağlantısı’ vardır. Bir bakıma yazar kitabı tanımlamaktadır. Kitabın bir özelliği, örneğin title
Eloquent İlişkileri (Relationships)
349
gibi bir property’si olması gerekmez. Hayır, bir yazar kendi özellikler kümesine sahiptir. Bir ismi,
bir doğum yeri, bir favori pizza türü. Kendi modeli olmayı hak ediyor. Bir Author sınıfı.
Peki bunları birbirine nasıl bağlayacağız? İlişkisel veritabanları sayesinde ilişkileri tanımlamak için
yabancı anahtarlar (foreign key) kullanabiliyoruz. Bunlar normalde integer sütunlardır.
İki örnek tablomuzu inşa edelim.
books
1
2
3
4
5
6
7
+---------+------------------------+
| id (PK) | name
|
+---------+------------------------+
| 1
| Code Sexy
|
| 2
| Code Dutch
|
| 3
| Code Bright
|
+----------------------------------+
Burada içinde üç kitap barındıran bir Book tablomuz var. Dikkat ederseniz, her bir kitapta Eloquent
ORM tarafından gerekli olan bir benzersiz integer birincil anahtar var. Bu her bir satırı tek tek
tanımlamak için kullanılabilmektedir. Şimdi de ikinci tablomuza bakalım.
authors
1
2
3
4
5
6
7
+---------+------------------------+
| id (PK) | name
|
+---------+------------------------+
| 1
| Dayle Rees
|
| 2
| Matthew Machuga
|
| 3
| Shawn McCool
|
+----------------------------------+
Burada da fevkalade yakışıklı üç geliştiricinin isimlerini taşıyan başka bir tablomuz var. Buna Author
tablosu diyeceğiz. Dikkat ettiyseniz yine her satırın benzersiz bir tanımlayıcı olarak kullanılabilecek
integer bir birincil anahtarı var.
Peki, şimdi bu iki tablo arasında bir ilişki kuralım. Bu, bir Book ile bir Author arasındaki ilişkidir,
şimdilik eş-yazarlıkları dikkate almayacağız ve bir kitabın sadece bir yazarı olduğunu kabul
edeceğiz. Book tablosuna yeni bir alan ekleyelim.
books
Eloquent İlişkileri (Relationships)
1
2
3
4
5
6
7
350
+---------+------------------------+-----------------+
| id (PK) | name
| author_id (FK) |
+---------+------------------------+-----------------+
| 1
| Code Sexy
| 2
|
| 2
| Code Dutch
| 3
|
| 3
| Code Bright
| 1
|
+----------------------------------+-----------------+
İlk yabancı anahtarımızı ekledik. Bu author_id alanıdır. Tekrar belirteyim, bu bir integer alandır
ama bu tablonun satırlarını tanımlamak için kullanılmaz, onun yerine başka bir tablodaki ilişkili
satırları tanımlamak için kullanılır. Bir yabancı anahtar alanı normalde yakın bir tablodaki bir
birincil anahtarı tanımlamak için kullanılır ancak bazı durumlarda başka alanları tanımlamak için
de kullanılabilir.
Bir yabancı indeks eklenmesinin yazar ve bir kitap arasında bir ilişkiyi ne şekilde oluşturduğunu
görebildiniz mi? author_id foreign indeksimiz Author tablosu içerisindeki primer keyi ifade ediyor.
Book tablosundaki üç satıra daha yakından bakalım. Code Bright kitabı, 1 olan bir author_ide
sahiptir. Şimdi Author tablosunda 1 olan satıra baktığımızda Dayle Rees göreceğiz. Bu, Code Bright
kitabı Dayle Rees tarafından yazılmıştır demektir. Bu kadar basit.
Neden yabancı anahtarı Author tablosuna koymadık?
Çünkü, bir yazarın birden çok kitabı olabilecektir, öyle değil mi? Örneğin, şu ilişkiyi ele alalım.
authors
1
2
3
4
5
6
7
+---------+------------------------+
| id (PK) | name
|
+---------+------------------------+
| 1
| Dayle Rees
|
| 2
| Matthew Machuga
|
| 3
| Shawn McCool
|
+----------------------------------+
books
Eloquent İlişkileri (Relationships)
1
2
3
4
5
6
7
8
351
+---------+------------------------+-----------------+
| id (PK) | name
| author_id (FK) |
+---------+------------------------+-----------------+
| 1
| Code Sexy
| 2
|
| 2
| Code Dutch
| 3
|
| 3
| Code Bright
| 1
|
| 4
| Code Happy
| 1
|
+----------------------------------+-----------------+
Bakın, Dayle Rees, yani ben, bakın ben nasıl… ay canım bu cümleyi mahvettim. Bana ait iki kitap
olduğunu göreceksiniz. Hem ‘Code Happy’ hem de ‘Code Bright’ın, değeri 1 olan bir author_idleri
var. Demek ki, onların her ikisi de Dayle Rees tarafından yazılmış.
İsterseniz şimdi yukarıdaki örnekteki yabancı anahtarı Author tablosunda göstermeye çalışalım.
authors
1
2
3
4
5
6
7
+---------+------------------------+---------------+-----------+
| id (PK) | name
| book_one (FK) | book_two |
+---------+------------------------+---------------+-----------+
| 1
| Dayle Rees
| 3
| 4
|
| 2
| Matthew Machuga
| 1
| null
|
| 3
| Shawn McCool
| 2
| null
|
+----------------------------------+---------------+-----------+
books
1
2
3
4
5
6
7
8
+---------+------------------------+-----------------+
| id (PK) | name
| author_id (FK) |
+---------+------------------------+-----------------+
| 1
| Code Sexy
| 2
|
| 2
| Code Dutch
| 3
|
| 3
| Code Bright
| 1
|
| 4
| Code Happy
| 1
|
+----------------------------------+-----------------+
Gördüğünüz gibi, Author tablosuna birden çok yabancı anahtarlar eklemek zorunda kaldık. Sadece
o değil, bazı yazarların iki kitabı olmadığı için sütunların birçoğunun içinde null değerler olacak.
Yazarımızın üç veya dört kitabı olursa ne olacak? Sütun ekleyip duramayız, tablolarımız çok dağınık
görünmeye başlar!
Çok güzel, yabancı anahtarı Book tablosunda tutacağız.
Eloquent İlişkileri (Relationships)
352
Bu ilişki türlerinin adlarını neden öğrenmiyoruz? Eloquent ile bu ilişki türlerini uygularken çok
yararlı olacaktır. Haydi başlayalım.
Book’ta onun tek yazarını tanımlayan id alanımız var. Bu, o bir Author’e aittir (belongs to) anlamına
gelir. İlk ilişkimizin adı işte budur.
Book belongs_to Author
İlişkilerde ters varyasyonlar da vardır. Eğer bir Book bir Author’e aitse (belongs_to), bu durumda
bir Author birçok Book’a sahiptir (has_many). Şimdi başka bir ilişki adını öğrenmiş olduk.
Author has_many Book
Eğer yazar bunun yerine tek bir kitaba sahip olsaydı ama books tablosu birincil anahtar tanımını
yapsaydı, o zaman has_one ilişki türünden söz edecektik.
Bu ilişki türlerini unutmayınız ama devam edelim ve bir dördüncüye geçelim. Şimdi başka bir örnek
gerekiyor. Hmm… ‘Beğendiklerim’ sistemi nasıl? Orada Userlar Booku oylayabiliyor. Zaten keşfetmiş
olduğumuz ilişkileri kullanarak bunu ifade edelim.
User has_many Book
Book has_many User
Bu ‘has many’ oluşturulan beğeniler ilişkisidir. Bir beğeniyi ifade etmek için tam bir varlığa
ihtiyacımız yok çünkü herhangi bir niteliği yok. Hem ilişki hem de onun ters ilişkisi has_many
olduğu takdirde, yeni bir ilişki tipi uygulamamız gerekiyor. Herşeyden önce, has_many demek
yerine belongs_to_many diyeceğiz. Böylece diğer ilişkilerle karıştırılmamasını sağlıyoruz. Bu yeni
tipteki ilişki bir many_to_many ilişkidir ve ek bir tablo gerektirir.
Tablo yapılarını görelim.
users
1
2
3
4
5
6
7
+---------+------------------------+
| id (PK) | name
|
+---------+------------------------+
| 1
| Dayle Rees
|
| 2
| Matthew Machuga
|
| 3
| Shawn McCool
|
+----------------------------------+
books
Eloquent İlişkileri (Relationships)
1
2
3
4
5
6
7
8
353
+---------+------------------------+
| id (PK) | name
|
+---------+------------------------+
| 1
| Code Sexy
|
| 2
| Code Dutch
|
| 3
| Code Bright
|
| 4
| Code Happy
|
+----------------------------------+
book_user
1
2
3
4
5
6
7
8
+-------------------------+
| id | user_id | book_id |
+-----+---------+---------+
| 1
| 1
| 2
|
| 2
| 1
| 3
|
| 3
| 2
| 2
|
| 4
| 3
| 2
|
+-----+---------+---------+
Bekle bir dakika, bu üçüncü tablo neyin nesi?
İyi gördünüz! O bizim join tablomuz veya pivot tablomuz veya lookup tablomuz veya intermediatory
tablomuz veya hibbiti-bibbiti yapan tablomuz. O kadar çok adı var ki. Laravel belgeleri onları
genellikle pivot tablolar olarak ifade eder, bu yüzden ben de onu kullanacağım. Ne zaman bir
many_to_many ilişkiye ihtiyacınız olsa, bir pivot tablonuz olması gerekecek. Bu tablo, diğer
tablolardaki satırları tanımlamak için iki yabancı anahtar kullanmak suretiyle iki varlığı birbirine
bağlayan veritabanı tablosudur.
Pivot tablonun ilk iki satırına baktığımızda user ‘Dayle Rees’in hem ‘Code Dutch’ hem de ‘Code
Bright’i beğendiğini görebiliyoruz. Ayrıca, kullanıcılar Matthew Machuga ve Shawn McCool’ın her
ikisinin de Code Dutch’i beğendiğini görüyoruz.
Bir polymorphic ilişki olarak bilinen ek bir ilişki tipi de vardır. Karmaşık doğası ve uzun bir adı
olması nedeniyle bunu daha sonraki bir bölümde, İleri Eloquent taktikleri bölümünde göreceğiz.
Şimdilik ona ihtiyacımız yok.
Bu kadar yeterli. Biraz da farklı şeyler öğrenelim! Uygulamalı öğrenme. Artık, kullanabileceğimiz
çeşitli ilişkileri öğrendiğimize göre bunları Eloquent ile nasıl uygulayacağımızı öğrenelim.
İlişkilerin Uygulanması
Pekala, ilk evreyi kuralım. Önce birkaç tablo yapısı inşa etmemiz gerekiyor. Normalde her tablo için
yeni bir migrasyon oluştururdum ama örnekleri basitleştirmek için hepsini bir migrasyonun içine
koyacağım.
Eloquent İlişkileri (Relationships)
354
Artists, Albums ve Listeners kullanan bir sistem kuracağız. Listeners kısaca çeşitli albümleri
dinlemek isteyen kullanıcılardır. Şimdi ilişkiler konusunu düşünelim.
•
•
•
•
An Artist has_many Albums. Bir sanatçının birçok albümü vardır.
An Album belongs_to an Artist. Bir albüm bir sanatçıya aittir.
A Listener belongs_to_many Albums. Bir dinleyici birçok albüme aittir.
An Album belongs_to_many Listeners. Bir albüm birçok dinyeyiciye aittir.
Bir Artist ile birçok Album arasında basit bir ilişkimiz var ve Listenerlar ile Albümler arasında
birçoktan birçoğa şeklinde ilişkimiz var. Tablo yapımızı düşünecek olursak, eğer bir Album bir
Artist’e ait (belongs_to) ise bu durumda ilişkiyi tanımlayıcı yabancı anahtar Albüm tablosunda
olacaktır. Birçoktan birçoğa ilişki ise bir pivot tablo gerektirecek.
Eloquent’in tablo isimlerinin modelinkinin çoğul biçimi olmasını istediğini (belirli durumlar dışında)
biliyoruz ama pivot tablonun ismi ne olacak? Pivot tablo için ön tanımlı format ilişkili iki modelin
tekil biçiminin bir alt tire _ ile birleştirilmesidir. Olguların sıralaması alfabetik düzende yapılmalıdır.
Yani pivot tablomuzun adı album_listener olacaktır.
Bütün yabancı anahtar sütunları aynı isimlendirme geleneğini takip eder. İlişkili modelin tekil formuna _id eklenir. Bu geleneğe göre pivot tablomuz hem album_id hem de listener_id sütunlarını
içerecek.
İnşa edilen migrasyona bir göz atalım, ondan sonra da yeni şeyleri daha ayrıntılı inceleyelim.
1
<?php
2
3
use Illuminate\Database\Migrations\Migration;
4
5
// app/datebase/migrations/2013_08_26_130751_create_tables.php
6
7
class CreateTables extends Migration {
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('artists', function($table)
{
$table->increments('id');
$table->string('name', 64);
Eloquent İlişkileri (Relationships)
355
$table->timestamps();
20
});
21
22
Schema::create('albums', function($table)
{
$table->increments('id');
$table->string('name', 64);
$table->integer('artist_id');
$table->timestamps();
});
23
24
25
26
27
28
29
30
Schema::create('listeners', function($table)
{
$table->increments('id');
$table->string('name', 64);
$table->timestamps();
});
31
32
33
34
35
36
37
Schema::create('album_listener', function($table)
{
$table->integer('album_id');
$table->integer('listener_id');
});
38
39
40
41
42
}
43
44
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('artists');
Schema::drop('albums');
Schema::drop('listeners');
Schema::drop('album_listener');
}
45
46
47
48
49
50
51
52
53
54
55
56
57
58
}
Not: Bu makaleyi yazdığım sırada yabancı anahtar sınırlamalarını çalıştıramadım. Sürekli aşağıdaki
hatayı aldım.
Eloquent İlişkileri (Relationships)
1
2
3
356
SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint (SQL: alte\
r table `albums` add constraint albums_artist_id_foreign foreign key (`artist_id`\
) references `artists` (`id`)
Bu hatayı düzeltmek için ileride bu bölüme geri döneceğimizi sanıyorum. Neyin yanlış olduğu
konusunda daha iyi bir fikriniz varsa, bana bir email göndermekten çekinmeyin!
Album, Artist ve Listener için şema oluşturucusu girişleri Eloquent model şema tanımlarındaki
tipik şeylerdir ancak yeni olarak bir pivot tablomuz var. Biz sadece bir album_listener tablosu
oluşturuyoruz ve yabancı anahtarlar olarak rol alacak iki integer alan ekliyoruz. Bu tablo iki model
olgusu arasında sadece bir ‘bağ’, bir ‘join’ olarak etki göstereceği için, timestamps veya bir birincil
anahtarı olmasına gerek yoktur.
Eloquent modellerimizi oluşturma zamanı. Artist ile başlayalım.
1
<?php
2
3
4
5
6
7
8
9
10
class Artist extends Eloquent
{
// Artist __has_many__ Album
public function albums()
{
return $this->hasMany('Album');
}
}
Burada yeni bir şeyler var! Eloquent model tanımımızın içinde bir ilişki metodumuz var. Bunu biraz
daha yakından inceleyelim.
1
public function albums()
Public metodun adı sıkı bir format gerektirmez. İlişkiyi ifade etmek için kullanabileceğimiz bir takma
ad olarak hizmet eder. Bu metoda kolaylıkla relatedAlbums() de diyebilirdik.
1
return $this->hasMany('Album');
İlişki metodunun içerisinde $this->hasMany() metodunun sonucunu döndürüyoruz. Bu, Eloquent
taban sınıfından miras alınan birçok ilişki metodundan sadece birisidir. İlişki metodunun birinci
parametresi ilişkili modelin tam adıdır. Eğer ileride Album modelimizi aduzaylı yapmaya karar
verirsek, bir parametre olarak tam aduzaylı sınıf adını eklememiz gerekecektir.
Album tablomuzdaki yabancı anahtarımıza, ön tanımlı artist_id yerine farklı isimlendirme yapmak
istersek, bu metoda opsiyonel bir ikinci parametre şeklinde alternatif sütun adı belirtebiliyoruz.
Örneğin:
Eloquent İlişkileri (Relationships)
1
357
return $this->hasMany('Album', 'the_related_artist');
Peki tam olarak ne döndürmüş oluyoruz? Şimdilik bunu dert etmeyin! Önce, model tanımlarımızın
oluşturulması işlemini tamamlayalım. Bundan sonra bir Album modelimiz var.
1
<?php
2
3
4
5
6
7
8
9
class Album extends Eloquent
{
// Album __belongs_to__ Artist
public function artist()
{
return $this->belongsTo('Artist');
}
10
// Album __belongs_to_many__ Listeners
public function listeners()
{
return $this->belongsToMany('Listener');
}
11
12
13
14
15
16
}
Album tablosu iki ilişki metoduna sahip. Tekrar ifade edeyim, public metodlarımızın isimleri önemli
değildir. İlk metodun içeriğine bakalım.
1
return $this->belongsTo('Artist');
Bu tabloda yabancı anahtar olması nedeniyle, Album modelinin bir Artist ile ilişkili olduğunu ifade
etmek için $this->belongsTo() metodunu kullanıyoruz. Birinci parametre bir kez daha ilişkili
modelin ismidir ve yine aynı şekilde alternatif bir sütun adı kullanmak için opsiyonel bir ikinci
parametre geçilebilmektedir.
İkinci metod birçoktan birçoğa ilişkimizin bir tarafını şekillendiriyor. Daha yakından bakalım.
1
return $this->belongsToMany('Listener');
Bu $this->belongsToMany() metodu Eloquent’e ilişkili modeller için bir pivot tabloya bakması
gerektiği bilgisini vermektedir. Birinci parametre yine aynı şekilde ilişkili modelin (varsa aduzayıyla
birlikte) adıdır. Bu sefer farklı bir opsiyonel parametre seti söz konusudur. Başka bir örnek inşa
edelim.
Eloquent İlişkileri (Relationships)
1
358
return $this->belongsToMany('Listener', 'my_pivot_table', 'first', 'second');
İkinci opsiyonel parametre ilişkili nesneler için kullanılacak pivot tablonun adıdır. Üçüncü ve
dördüncü parametreler pivot tablo içerisinde bizim nesnelerimizi ilişkilendirmekte kullanılan iki
yabancı anahtarın alternatif isimlendirme şemalarını tanımlamak için kullanılır.
Bir modelimiz kaldı. Şimdi de Listener modelini görelim.
1
<?php
2
3
4
5
6
7
8
9
10
class Listener extends ELoquent
{
// Listener __belongs_to_many__ Album
public function albums()
{
return $this->belongsToMany('Album');
}
}
Listener ters tarafını şekillendiriyor… Pekiyi, gerçekten tersini mi? Listener birçoktan birçoğa
ilişkinin diğer tarafını şekillendiriyor. İlişkiyi inşa etmek için bir kez daha $this->belongsToMany()
metodunu kullanabiliyoruz.
Her şey tamam! Modellerimiz oluşturuldu. Artık model olguları oluşturabiliriz.
İlişkilendirme ve Sorgulama
İlk olarak bir Artist ve bir Album ile ikisi arasında bir ilişki oluşturalım. Demo kodu için güzel ve
basit bir yol olduğuna inandığım için / rota Closure’umu kullanacağım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$artist = new Artist;
$artist->name = 'Eve 6';
$artist->save();
10
11
12
$album = new Album;
$album->name = 'Horrorscope';
Eloquent İlişkileri (Relationships)
359
$album->artist()->associate($artist);
$album->save();
13
14
15
return View::make('hello');
16
17
});
Bir saniye, bu yeni satır nedir?
1
$album->artist()->associate($artist);
Metod zincirlerini kıralım. Bu birinci metod.
1
$album->artist();
Bu metodu hatırladınız mı? Bu, Album nesnesinde oluşturmuş olduğumuz ilişki metodudur. Tam
olarak ne dördürdüğünü şimdi gördünüz mü? Bu metod, tıpkı bir önceki bölümde kullandıklarımız
gibi, bir Eloquent sorgu oluşturucusu olgusu döndürüyor.
Bununla birlikte, bu sorgu oluşturucusu olgusunda bizim için yerleştirilmiş bazı sınırlamalar olması
gerekmektedir. Bu sorgu oluşturucu tarafından temsil edilen güncel sonuçlar kümesi Albumümüzün
bağlı olduğu Artistler kümesi (bu örnek için bir koleksiyon) olacaktır.
Yani bu aşağıdakiyle aynı anlama gelir.
1
Artist::where('album_id', '=', __ŞİMDİKİ__GÜNCEL__ALBÜMÜMÜZ__);
Çok kullanışlı, değil mi? İsteseydik daha fazla sorgu zincirleyebilirdik. Örneğin:
1
$album->artist()->where('genre', '=', 'rock')->take(2);
Bir sorgu oluşturucunun bir sonuç koleksiyonu döndürebilmesi için zincirin son halkasına bir
tetikleme metodunun getirilmesi gerektiğini unutmayın. Mesela şöyle:
1
$album->artist()->where('genre', '=', 'rock')->take(2)->get();
Bu aynı zamanda, aşağıdakini yapmak suretiyle ilişkili artistlerden oluşan tam bir koleksiyon elde
edebileceğimiz anlamına gelmektedir.
1
$album->artist()->get();
Veya first() tetikleme metodunu kullanarak tek bir olgu getirebileceğimiz.
Eloquent İlişkileri (Relationships)
1
360
$album->artist()->first();
Oh ne esneklik! Pekala, şimdi önceki örneğimizi ele alalım.
1
$album->artist()->associate($artist);
Daha önce associate görmemiştim!
Panik yapmayın! Sırf yeni diye bir şeyin kötü olması gerekmez. Sadece örümcekler kötüdür. Bir de
Kevin Bacon.
Bu associate metodu ilişkiler üzerinde iki nesne arasında ilişki oluşturmak için kullanabileceğimiz
bir helper metodudur. Bu metod ilişkinin yabancı anahtarını uygun şekilde güncelleyecektir.
Yani, $album olgumuza associate() metodunu zincirlemekle Album tablo satırımızın artist_id
sütununu Artistin ID’i ile güncellemiş oluyoruz. İsteseydik, doğrudan bir yabancı anahtar da
tutturabilirdik.
İşte bir örnek.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$artist = new Artist;
$artist->name = 'Eve 6';
$artist->save();
10
$album = new Album;
$album->name = 'Horrorscope';
$album->artist_id = $artist->id;
$album->save();
11
12
13
14
15
return View::make('hello');
16
17
});
Her iki kod parçası da aynı sonucu verecek ve iki nesne arasında bir ilişki oluşturulmuş olacaktır.
İlişkilendirmek istediğiniz modeli associate() metoduna geçmeden önce save() etmeyi unutmayın. Bunun sebebi, model olgusunun ilişki oluşturabilmesi için bir birincil anahtar gerektirmesi ve
bir modelin sadece save edildikten sonra bir birincil anahtar alabilecek olmasıdır.
Birçoktan birçoğa ilişkiler kuran ilişkili nesneler hafifçe farklı bir yolla halledilir. Listener modelini
de getirerek daha yakından bakalım.
Eloquent İlişkileri (Relationships)
1
361
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$artist = new Artist;
$artist->name = 'Eve 6';
$artist->save();
10
$album = new Album;
$album->name = 'Horrorscope';
$album->artist()->associate($artist);
$album->save();
11
12
13
14
15
$listener = new Listener;
$listener->name = 'Naruto Uzumaki';
$listener->save();
$listener->albums()->save($album);
16
17
18
19
20
return View::make('hello');
21
22
});
Şu kısma odaklanalım:
1
2
3
4
$listener = new Listener;
$listener->name = 'Naruto Uzumaki';
$listener->save();
$listener->albums()->save($album);
Yeni Listener model olgumuzu doldurduktan sonra, onu save() etmeliyiz. Bunun nedeni, pivot
tablosunda yeni bir giriş oluşturabilmemiz için bir birincil anahtar gerekmesidir.
Bu sefer, ilişkimiz üzerinde associate() metodunu kullanmak yerine, save() metodunu kullanıyoruz ve ilk parametre olarak ilişkilendirilecek nesneyi geçiyoruz. Etki aynıdır, sadece bu defa ilişkiyi
tanımlayan pivot tablo güncellenecektir.
Şayet bir modeli doğrudan birincil anahtarı ile ilişkilendirmek isterseniz, birinci parametre olarak
birincil anahtar kabul eden attach() metodunu kullanabilirsiniz. Örneğin,
1
$album->artist()->attach(2);
İki nesne arasındaki bir ilişkiyi kaldırmak için, ilişki üzerinde detach() metodunu kullanabiliriz.
Birinci parametre olarak ya bir birincil anahtar ya da bir nesne geçmemiz yeterlidir.
Örneğin:
Eloquent İlişkileri (Relationships)
1
362
<?php
2
3
// app/routes.php
4
5
6
7
8
9
Route::get('/', function()
{
$album = Album::find(5);
$listener = Listener::find(2);
$album->listeners()->detach($listener);
10
return View::make('hello');
11
12
});
İlişki metodumuzda hiç parametre geçmeden detach() metodunu çağırmak suretiyle, bir nesne için
tüm ilişkileri kaldırabiliriz.
İşte bir örnek.
1
$album->listeners()->detach();
Artık albumün ilişkili olduğu dinleyiciler olmayacak.
Eloquent birçok inanılmaz özellikleri olan güzel ve geniş bir konudur ama şu anda size onların
hepsini yüklemek istemiyorum. Bu sadece öğrenmiş olduğunuz temellerden bir kısmını unutmanıza
sebep olacaktır.
Artık basit bir uygulama inşa etmeye başlamak için gerekli tüm bilgilere sahibiz. Sahip olduğum birçok Playstation 3 oyununu yönetmek için bir sistem inşa edeceğiz. Kendime ait bir şeyi unutmaktan
nefret ederim!
Daha fazla Eloquent için hala açlık duyuyorsanız panik yapmayın. İlerideki bir bölümde bu
olağanüstü zarif ORM çözümüne geri döneceğiz.
Bir Uygulama İnşa Edelim 1:
Playstation Oyun Koleksiyonu
Beklediğimiz zaman sonunda geldi! Tam bir Laravel uygulaması inşa edeceğiz. Sizin de benim
kadar heyecanlı olduğunuzu biliyorum. İlk uygulamamız basit bir uygulama olacak: Playstation
oyunlarımı yönetecek bir envanter uygulaması. Bende çok fazla (veya sadece yeterince) Playstation
oyunu var!
Uygumamız çok sayıdaki oyunu, bunların isimlerini, yayıncılarını ve oyunu tamamlamış olup olmadığımızı yönetecek. Basit bir CRUD (create/read/update/delete, oluştur/oku/güncelle/sil) tabanlı bir
uygulama olacak, ancak önceki bölümlerde öğrenmiş olduğumuz özellikler için harika bir başlangıç
noktası olacaktır.
Uygulama tamamlandıktan sonra, frameworkün daha ileri özelliklerine göz atmaya başlayacağız ve
uygulamamıza ekstra özellikler eklemek için bunları nasıl kullanacağımızı göreceğiz.
Daha fazla zaman kaybetmeyelim. Uygulamamızı planlayarak başlamamız gerekiyor.
Konu üzerinde düşünmeye başlayalım.
Uygulamamızın video oyunları yöneteceğini biliyoruz, öyleyse her oyun için kaydetmek istediğimiz
özelliklere neden bir göz atmıyoruz.
• Title (Adı)
• Publisher (Yayıncısı)
• Completed (Tamamlanma Durumu)
Sonra da video oyunu özelliklerinin hangi şekli alacağı üzerinde düşünmemiz gerekiyor. Bu, bu
bölümde daha sonra veritabanı şeması inşa etmemize yardım edecektir. Her alana bakacağız ve bir
tip ve uygunsa bir uzunluk atayacağız.
• Title string 128
• Publisher string 128
• Completed boolean
Bu şemanın video oyunlarımızı anlamlı bir şekilde tanımlamamıza imkan vereceğini düşünüyorum.
Title ve publisher alanlarını saklamak için 128 harf uzunluğundaki stringler kullanılabilir ve oyunun
tamamlanıp tamamlanmadığını kontrol etmek için basit bir boolean kullanılabilir.
Daha sonra, uygulamamızın sahip olacağı özellikleri düşüneceğiz. Bunun için başka bir liste yapalım.
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
•
•
•
•
364
Video oyun kayıtları oluşturma.
Video oyun kayıtlarını listeleme.
Video oyun kayıtlarını düzenleme.
Video oyun kayıtları silme.
Yeni video oyun kayıtları oluşturabilmek ve oluşturmuş olduklarımızın bir listesini görmek istiyoruz.
Ayrıca, video oyun kayıtlarımızı güncellemek veya silmek de istiyoruz. Uygulamamızı daha önce
bir ‘CRUD’ uygulaması olarak tarif etmemizin nedeni budur. Bu pislik anlamındaki crud değildir!
Fantastik bir şey olacak! Birçok gerçek dünya uygulamasına tatbik edilecek ortak bir desendir.
Ondan sonra, bu eylemlerin gerçekleştirilmesini sağlamak için gerekli sayfalar hakkında düşüneceğiz. Haydi, bakalım… Oluştur sayfası yeni oyunları girmek için bir form olmasını gerektirecek.
Liste görünümü, daha önce oluşturduğumuz oyunların hepsini listelemek için tablo görünümünde
olacak.
Sonraki kısım biraz daha incelik istiyor. İyi bir kullanıcı deneyimi sağlamak için oyunlar listesinde
bir ‘Düzenle’ düğmesi olacak, ancak ilgili oyunu düzenleyecek bir form gösteren bir sayfa da
istiyoruz. Birçok geliştirici hem oluşturmak hem de düzenlemek için tek bir sayfa kullanacaktır ama
basit tutmak için biz onları ayrı tutacağız.
Son olarak, video oyunlarını listeleyen sayfada bir ‘Sil’ düğmesi de gösterilecek. Ben, oyunun silinip
silinmeyeceğini teyit etmek için başka bir sayfa isteyeceğimizi düşünüyorum. Ne de olsa, silme
işlemi oldukça yıkıcı bir iştir!
Peki, bu sayfaları özetleyelim.
•
•
•
•
Oyunların listesi.
Oyun oluşturma formu.
Oyun düzenleme formu.
Oyun silme teyidi.
Harika! Başlamak için ihtiyacımız olan her şeye sahip olduğumuzu düşünüyorum.
Şimdi hacking zamanı!
Bu projenin tüm kodları github web sitesindeki bir depoda bulunacaktır: sineld/games-app³².
Download edebilir ve içeriğiyle gönlünüze göre oynayabilirsiniz!
İlk olarak Laravel frameworkün bir kopyasını games proje klasörüme aktaracağım. Her zaman
olduğu gibi, composer kullanarak framework bağımlılıklarının tamamını install etmemiz gerekecek.
³²http://github.com/sineld/gamesapp
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
1
2
3
4
5
365
$ composer install
Loading composer repositories with package information
Installing dependencies (including require-dev)
- Installing doctrine/lexer (dev-master bc0e1f0)
Loading from cache
6
7
8
- Installing doctrine/annotations (v1.1.2)
Loading from cache
9
10
11
- Installing doctrine/collections (dev-master bcb5377)
Loading from cache
12
13
14
- Installing doctrine/cache (v1.1)
Loading from cache
15
16
17
- Installing doctrine/inflector (dev-master 8b4b3cc)
Loading from cache
18
19
... daha birçok şey ...
20
21
22
- Installing laravel/framework (4.0.x-dev 733492c)
Downloading: 100%
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
monolog/monolog suggests installing mlehner/gelf-php (Allow sending log messages \
to a GrayLog2 server)
monolog/monolog suggests installing ext-amqp (Allow sending log messages to an AM\
QP server (1.0+ required))
monolog/monolog suggests installing ext-mongo (Allow sending log messages to a Mo\
ngoDB server)
monolog/monolog suggests installing doctrine/couchdb (Allow sending log messages \
to a CouchDB server)
monolog/monolog suggests installing raven/raven (Allow sending log messages to a \
Sentry server)
symfony/translation suggests installing symfony/config ()
symfony/translation suggests installing symfony/yaml ()
symfony/routing suggests installing symfony/config ()
symfony/routing suggests installing symfony/yaml ()
symfony/debug suggests installing symfony/class-loader ()
symfony/event-dispatcher suggests installing symfony/dependency-injection ()
symfony/http-kernel suggests installing symfony/class-loader ()
symfony/http-kernel suggests installing symfony/config ()
symfony/http-kernel suggests installing symfony/dependency-injection ()
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
43
44
45
46
47
366
predis/predis suggests installing ext-phpiredis (Allows faster serialization and \
deserialization of the Redis protocol)
Writing lock file
Generating autoload files
Generating optimized class loader
Harika, artık başlayabiliriz. Veritabanıyla başlayalım.
Veritabanı
Öncelikle geliştirme platformumuzda bir veritabanı oluşturmak isteriz. Ben games adında bir MySQL
veritabanı oluşturacağım.
Sonra da uygulama veritabanı yapılandırma dosyasını yeni veritabanımızı ifade edecek şekilde
güncellememiz gerekecek. Benimki şöyledir.
1
// app/config/database.php
2
3
4
5
6
7
/*
|-------------------------------------------------------------------------| Default Database Connection Name
|-------------------------------------------------------------------------*/
8
9
'default' => 'mysql',
10
11
12
13
14
15
/*
|-------------------------------------------------------------------------| Database Connections
|-------------------------------------------------------------------------*/
16
17
'connections' => array(
18
19
20
21
22
23
24
25
26
'mysql' => array(
'driver'
=>
'host'
=>
'database' =>
'username' =>
'password' =>
'charset'
=>
'collation' =>
'mysql',
'localhost',
'games',
'root',
'falanfilan',
'utf8',
'utf8_unicode_ci',
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
'prefix'
27
367
=> '',
),
28
29
30
),
Ondan sonra da şema oluşturucu bileşenini kullanarak veritabanı şemamızı oluşturalım. Artisan
komut satırı arayüzü (Artisan CLI) kullanarak yeni bir migrasyon iskeleti üreteceğiz.
1
2
3
$ php artisan migrate:make create_games
Created Migration: 2013_09_14_155847_create_games
Generating optimized class loader
Şemamızı oluşturmak için up() ve down() metodlarındaki boşlukları dolduracağız. Daha önce
kafamızda planladığımız özellik tiplerini ve uzunluklarını kullanacağız.
Sonuçta migrasyon şöyle olacak.
1
<?php
2
3
use Illuminate\Database\Migrations\Migration;
4
5
// app/database/migrations/2013_09_14_155847_create_games.php
6
7
class CreateGames extends Migration {
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('games', function($table)
{
$table->increments('id');
$table->string('title', 128);
$table->string('publisher', 128);
$table->boolean('complete');
$table->timestamps();
});
}
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
368
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('games');
}
26
27
28
29
30
31
32
33
34
35
36
}
Buradaki up() metodunda planlama bölümünde bahsettiğimiz özellikleri olan games tablomuzu
oluşturuyoruz. Ayrıca otomatik artan id sütunu ve timestamps sütunlarını da ekliyoruz, çünkü
oyunlarımızı Eloquent ORM olguları şeklinde temsil etmek niyetindeyiz.
drop() metodunda tablomuzu çıkartıyoruz. Sanki hiç yokmuş gibi olacak!
Bu etapta, Artisan migrate komutunu çalıştırarak mevcut migrasyonla lokal veritabanımızda şema
inşa edebiliriz.
1
2
3
$ php artisan migrate
Migration table created successfully.
Migrated: 2013_09_14_155847_create_games
Veritabanımız şimdi güncellenmiştir. İlgili şema uygulamaya geçirilmiştir. Video oyunumuzu temsil
etmek için neden bir Eloquent modeli oluşturmayalım? İşte yeni sınıfımız.
1
<?php
2
3
// app/models/Game.php
4
5
6
class Game extends Eloquent
{
7
8
}
Ne kadar da kolay oldu! Eloquent’in sadeliğini her zaman için gerçekten eğlenceli bulmuşumdur.
Bir süreliğine hep birlikte manyakça gülelim ve sonra da bir sonraki kesime geçelim.
Muahahahaahhaahahahhahahahahaah. öhhö öhö.
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
369
Controller
Bu uygulamada sadece dört sayfa olacak ve bu nedenle tek bir controller yeterli olacaktır. Gelin şimdi
controlleri oluşturalım ama metodları iskelet halinde kalsın. Ayrıca birkaç rota ile ileride implemente
edeceğimiz boş görünümler de oluşturacağız. Controllere GamesController adını vereceğiz. Tank
Engine Thomas’ın söylediği FatController ile bir ilgisi yok. Ön tanımlı HomeControlleri silmeyi
unutmayın, ona ihtiyacımız yok!
1
<?php
2
3
// app/controllers/GamesController.php
4
5
6
7
8
9
10
11
class GamesController extends BaseController
{
public function index()
{
// Oyunların listesini göster.
return View::make('index');
}
12
13
14
15
16
17
public function create()
{
// Oyun oluşturma formunu göster.
return View::make('create');
}
18
19
20
21
22
public function handleCreate()
{
// Oluşturma formu gönderimini işle.
}
23
24
25
26
27
28
public function edit(Game $game)
{
// Oyun düzenleme formunu göster.
return View::make('edit');
}
29
30
31
32
33
34
public function handleEdit()
{
// Düzenleme formu gönderimini işle.
}
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
370
public function delete(Game $game)
{
// Sil teyiti sayfasını göster.
return View::make('delete');
}
35
36
37
38
39
40
public function handleDelete()
{
// Sil teyidini işle.
}
41
42
43
44
45
}
Uygulamamız için yeni eylemlerden oluşan tam bir demet implemente ediyoruz. Bir kısmı sadece
uygun bir form göstermekte kullanılıyor, diğerleri form gönderimlerini işlemekte kullanılıyor.
Dikkat ederseniz, edit() ve delete() metodları parametre olarak tip dayatmalı bir Game olgusu
kabul ediyor. Bunun üzerinde durmayın, rota kısmında bunu açıklayacağım!
Bu controller tarafından kullanılacak boş görünümler oluşturalım. Daha sonra onları güncelleyeceğiz. Ön tanımlı bulunan hello görünümünü sileceğiz.
•
•
•
•
app/views/index.blade.php
app/views/create.blade.php
app/views/edit.blade.php
app/views/delete.blade.php
Rotalar
Controller eylemlerimizin URL’ler aracılığıyla erişilebilir olması için rotalarımızı oluşturmamız
gerekiyor. Haydi oluşturalım…
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
/*
|-------------------------------------------------------------------------| Application Routes
|-------------------------------------------------------------------------|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
12
13
14
371
| and give it the Closure to execute when that URI is requested.
|
*/
15
16
17
// Rota parametrelerini bağla.
Route::model('game', 'Game');
18
19
20
21
22
23
// Sayfaları göster.
Route::get('/', 'GamesController@index');
Route::get('/create', 'GamesController@create');
Route::get('/edit/{game}', 'GamesController@edit');
Route::get('/delete/{game}', 'GamesController@delete');
24
25
26
27
28
// Form gönderimlerini işle.
Route::post('/create', 'GamesController@handleCreate');
Route::post('/edit', 'GamesController@handleEdit');
Route::post('/delete', 'GamesController@handleDelete');
Bir dakika, bu Route::model() metodu nedir? Laravel’in sunduğu kara büyünün güzel bir parçasıdır.
Route::model() metodu, bu metodun ilk parametresiyle aynı isimdeki herhangi bir rota parametresinin, bu metodun ikinci parametresi ile tanımlanan bir Eloquent model olgusu olacağını bilmesini
sağlayacaktır. Dolayısıyla, yukarıdaki örnekte, game adındaki herhangi bir rota parametresi (örneğin
/edit/{game} içindeki), Eloquent Game model olgularına bağlanacaktır.
Laravel bir parametre olarak verilen integere bakacak ve birincil anahtara göre model üzerinde
bir arama gerçekleştirecektir. Daha sonra, bulduğu bu model olgusunu controller eylemine bir
parametre olarak geçecektir. Ne kadar kullanışlı, değil mi? Cevap… ÇOK!
Rotalama sürecinin geri kalan kısmını zaten biliyor olmalısınız. Uygulama sayfalarımızı göstermek
için kullanılan dört GET rotamız ve form gönderimlerini işlemek için üç POST rotamız var.
Her şeyin beklediğimiz gibi çalıştığından emin olalım. Basit bir web sunucusu başlatmak için Artisan
serve komutunu kullanacağız ve uygulamamızı test edeceğiz.
1
2
3
$ php artisan serve
Laravel development server started on http://localhost:8000
(Laravel geliştirme sunucusu http://localhost:8000 üzerinde başladı)
Geliştirme sunucumuzun 8000 portunda çalışıyor olduğunu görüyoruz, öyleyse boş şablonumuzu
aldığımızdan emin olmak için http://localhost:8000i ziyaret edelim. Benim için harika çalışıyor!
Şablonlamaya geçebiliriz.
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
372
Görünümler
Şablonumuz için Twitter Bootstrap kullanacağız. Twitter Bootstrap tipik bir uygulama HTML’sini
bizim için halledecek bir CSS frameworküdür. Prototip uygulamalar için onu çok harika buluyorum.
Bu kitap bir front-end geliştirme kitabı olmadığı için şablonları tarif etmeyeceğim ama bazı yaygın
şablonlama problemlerini çözeceğiz.
Her şeyden önce, şablonlarımızın bazı ortak kodları paylaşacağını biliyoruz. Bunların hepsi basit bir
HTML5 şablonuyla sarmalanmış olacaktır, şuna benzer bir şey:
1
2
3
4
5
6
7
8
9
10
<!doctype html>
<html lang ="en">
<head>
<meta charset ="UTF-8">
<title>Oyunlar Koleksiyonu</title>
</head>
<body>
<!-- website içeriği burada olacak -->
</body>
</html>
Uygulamamızın ortak bir stil tablosu paylaşacağını da biliyoruz, bu nedenle bunu her sayfaya
eklemek zorunda olmak istemiyoruz. Eğer öyle yapsaydık, ileride stil tablosunun konumunu
değiştirmek istediğimizde şablonların tümünü güncellememiz gerekecekti.
Öyle yapmak yerine, bu problemi Blade şablonlama motorunun şablon kalıtımından yararlanarak
çözeceğiz. layout adında bir taban şablon oluşturacağız ve diğer sayfalarımızın hepsi bunu genişletecek.
layout.blade.php görünümümüz aşağıdaki gibidir. Bu e-okuyucularda kötü görünür ama kompakt
bir HTML yapamam. Bunun için üzgünüm!
1
2
3
4
5
6
7
8
9
10
11
<!doctype html>
<html lang ="en">
<head>
<meta charset ="UTF-8">
<title>Oyunlar Koleksiyonu</title>
<link rel ="stylesheet" href ="{{ asset('css/bootstrap.min.css') }}" />
</head>
<body>
<div class ="container">
<nav class ="navbar navbar-default" role ="navigation">
<div class ="navbar-header">
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
12
13
14
15
16
17
18
19
373
<a href ="{{ action('GamesController@index') }}" class ="navbar-bra\
nd">Oyunlar Koleksiyonu</a>
</div>
</nav>
@yield('content')
</div>
</body>
</html>
Böylece birkaç önemli satırı olan basit bir HTML5 yapımız var. Bunlara biraz daha yakından
bakalım. Birincisi, kullanacağımız CSS stylesheeti belirten satır.
1
<link rel="stylesheet" href="{{ asset('css/bootstrap.min.css') }}" />
Bütün görünümlerimizde tek bir küresel stylesheet kullanmak istiyoruz. Twitter Bootstrap CSS’yi
ana layout’a dahil etmekle, onu genişleten tüm görünümlerimizde onu kullanabiliyor olacağız.
Varlıklarımız için projemizin public klasörüne göreli bir path belirtmek amacıyla asset() helper
fonksiyonunu kullanıyoruz. Böylece, uygulamamıza rotalamakta kullanılan path, varlıklarımız için
olan URL’lere müdahale etmeyecektir.
Bundan sonraki önemli satır menüdeki home bağlantısıdır. Yakından bakalım.
1
2
<a href ="{{ action('GamesController@index') }}" class ="navbar-brand">Oyunlar Kole\
ksiyonu</a>
Aynı şekilde, rotalama sistemimize dayalı path ile çatışmaları engellemek için yine bir helper fonksiyonu kullanıyoruz. Bu sefer, framework rotamız için mutlak bir URL üretmek amacıyla action()
fonksiyonunu kullanıyoruz. {{ echo ayraçları }} ile birlikte action() kullanmakla, uygulamamız
için home linkini oluşturuyoruz. action() fonksiyonunun parametresi bir @ simgesiyle birleştirilmiş
bir controller ve bir eylem çiftidir, tıpkı rota tanımlarken kullandığımız gibi.
Şimdi şablonun son ve en önemli satırına gelelim! Yakından bakalım.
1
@yield('content')
Bu @yield() blade şablon metodu layout’umuz içinde enjekte edilebilir bir kesim oluşturmamıza
imkan verir. Yani, layout’umuzu genişleten şablonlar bu bölgeye içerik enjekte edebileceklerdir. Bu
bölgeyi tanımlamak için ‘content’ ismini kullandık.
Artık bu layoutu genişletecek birkaç görünüm oluşturabiliriz. Oyunlarımızın tümünü listelemek için
kullanacağımız görünümle başlayalım. Görünüm dosyasının kendini düzenlemeden önce, index()
eylemimizi bir miktar mantık içerecek şekilde değiştirelim.
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
1
2
3
4
5
6
374
public function index()
{
// Oyunların listesini gösterir.
$games = Game::all();
return View::make('index', compact('games'));
}
Sistemimizdeki oyunların tümünün bir koleksiyonunu elde etmek için index() eylemimizde Game::all()
kullanıyoruz. Daha sonra bu koleksiyonu index görünümümüze geçmek için compact() ve View::make()
kullanıyoruz. Şayet daha önce compact() kullanmadıysanız, dizi anahtarları olarak isimlerinin kullanıldığı çok sayıda değişkenden oluşan bir dizi oluşturur. Bu, extract() PHP fonksiyonunun tam
tersidir. Görünümlerime sadece az sayıda değerler geçmem gerektiği zaman compact() kullanmayı
seviyorum.
Oyunlarımızın tam bir koleksiyonuna sahip olduğumuza göre, neden onları göstermek için bir view
oluşturmayalım? app/views/index.blade.php dosyasının içi şöyledir.
1
@extends('layout')
2
3
4
5
6
@section('content')
<div class="page-header">
<h1>Tüm Oyunlar<small> Hepsini yakalamak lazım!</small></h1>
</div>
7
8
9
10
11
12
13
<div class="panel panel-default">
<div class="panel-body">
<a href="{{ action('GamesController@create') }}" class="btn btn-prima\
ry">Oyun Oluştur</a>
</div>
</div>
14
15
16
17
18
19
20
21
22
23
24
25
26
@if ($games->isEmpty())
<p>Hiçbir oyun yok! :(</p>
@else
<table class="table table-striped">
<thead>
<tr>
<th>Adı</th>
<th>Yayıncısı</th>
<th>Tamamlanmış</th>
<th>Eylemler</th>
</tr>
</thead>
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
375
<tbody>
@foreach($games as $game)
<tr>
<td>{{ $game->title }}</td>
<td>{{ $game->publisher }}</td>
<td>{{ $game->complete ? 'Evet' : 'Hayır' }}</td>
<td>
<a href="{{ action('GamesController@edit', $game->id) }}"\
class="btn btn-default">Düzenle</a>
<a href="{{ action('GamesController@delete', $game->id) }\
}" class="btn btn-danger">Sil</a>
</td>
</tr>
@endforeach
</tbody>
</table>
@endif
@stop
İlk satırdaki @extends('layout') blade fonksiyonu, bu görünüm için sadece bir dakika önce
oluşturduğumuz layoutu genişletmek istediğimizi gösterir. Bu layout bir sarmalayıcı olacaktır ve
bu görünüm onun içerdiği yield’li bölgelere enjeksiyon yapacaktır.
Sonra bir @section var.
1
2
3
@section('content')
<!-- HTML burada -->
@stop
@section blade fonksiyonu, bizim layoutumuzda @yield blade fonksiyonunu kullanarak tanımlamış
olduğumuz bölgelere içerik enjekte etmekte kullanılır. Bu fonksiyonun tek parametresi, içine içerik
enjekte etmek istediğimiz bölgenin adıdır.
Sayfamızı oluşturmak için bazı klişe HTML oluşturuyoruz ve önemli olan bir sonraki satır @if blade
fonksiyonunun olduğu kısımdır. İlk olarak isEmpty() metodunu kullanarak, oyun koleksiyonumuzun boş olup olmadığını kontrol ediyoruz. Eğer hiçbir oyunumuz yoksa, kullanıcının bunu bilmesini
sağlamak için küçük bir paragraf çıktısı vereceğiz.
Eğer oyunlarımız varsa, onları göstermeliyiz. Bir tablo ve bunun içinde tablo gövdesi ve bir döngü
oluşturuyoruz. Bu döngü tüm oyunlarımızı baştan sona dolaşarak oyunlarımızın önemli nitelikleri
olan ad (title) ve yayıncısını (publisher) çıktılayacak.
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
1
2
3
4
5
6
7
8
9
10
11
12
13
376
@foreach($games as $game)
<tr>
<td>{{ $game->title }}</td>
<td>{{ $game->publisher }}</td>
<td>{{ $game->complete ? 'Evet' : 'Hayır' }}</td>
<td>
<a href="{{ action('GamesController@edit', $game->id) }}" class="btn btn-\
default">Düzenle</a>
<a href="{{ action('GamesController@delete', $game->id) }}" class="btn bt\
n-danger">Sil</a>
</td>
</tr>
@endforeach
Oyunun tamamlanmış olup olmadığını göstermek için bir string çıktısı vermek amacıyla {{ echo
ayraçları }} ile kombine edilmiş PHP ternary operatorü kullanıyoruz. Bu ya ‘Evet’ veya ‘Hayır’
olacak.
1
{{ $game->complete ? 'Evet' : 'Hayır' }}
Son olarak, bir oyunu düzenlemek veya sistemden silmek için kullanabileceğimiz iki düğmemiz var.
1
2
3
4
<a href ="{{ action('GamesController@edit', $game->id) }}" class ="btn btn-default"\
>Düzenle</a>
<a href ="{{ action('GamesController@delete', $game->id) }}" class ="btn btn-danger\
">Sil</a>
Burada da yine action() helper fonksiyonu kullanıyoruz, ancak ikinci bir parametre de veriyoruz.
Bu parametre düzenlemek veya silmek istediğimiz oyunun id’sidir.
Bir sonraki işimiz sistemde yeni bir oyun oluşturmak için kullanılan görünümü güncellemek. İşte
app/views/create.blade.php görünümü.
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
1
377
@extends('layout')
2
3
4
5
6
@section('content')
<div class ="page-header">
<h1>Oyun Oluştur<small> ve günün birinde tamamla!</small></h1>
</div>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<form action ="{{ action('GamesController@handleCreate') }}" method ="post" rol\
e ="form">
<div class ="form-group">
<label for ="title">Adı</label>
<input type ="text" class ="form-control" name ="title" />
</div>
<div class ="form-group">
<label for ="publisher">Yayıncısı</label>
<input type ="text" class ="form-control" name ="publisher" />
</div>
<div class ="checkbox">
<label for ="complete">
<input type ="checkbox" name ="complete" /> Tamamlandı mı?
</label>
</div>
<input type ="submit" value ="Oluştur" class ="btn btn-primary" />
<a href ="{{ action('GamesController@index') }}" class ="btn btn-link">İpta\
l</a>
</form>
@stop
Create sayfası da index’e benzer bir tarzda taban layoutu genişletiyor. Ancak bu sefer, ‘content’
bölgesine bir form enjekte ediyoruz. Bu forma daha yakından bakalım.
1
2
<form action="{{ action('GamesController@handleCreate') }}" method="post" role="f\
orm">
Burada action() metodunu formumuz için bir action niteliği sağlamak için kullanıyoruz. Şimdi burada neden formlar bölümünde öğrenmiş olduğumuz form açılma metodlarından birinin kullanılmadığını merak ediyor olabilirsiniz? Bu doğrudur, öyle yapmak form metodunu belirtmek zorunluğunu
ortadan kaldıracaktır. Bununla birlikte, ben rota helperleri kullanmanın daha temiz görünümlere
götürdüğüne inanıyorum. Bunlar PHP olduklarından daha çok HTML’dirler ve kullanmış olduğum
metin editöründe sözdizimi vurgulanması daha iyi gözüküyor. Bu bir tercih meselesidir, burada
kendi kararınızı kendiniz verin lütfen.
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
378
Formun geri kalan kısmı oldukça standarttır. Bir oyunun özelliklerini gösteren çeşitli form alanlarımız var. Bir submit düğmemiz ve bir de kullanıcının fikrini değiştirmesi halinde kullanacağı bir
iptal düğmemiz bulunuyor.
Sonra silme görünümünü güncelleyelim. Kullanıcımız veritabanından bir oyun silmek istediğinde
teyit etmek istiyoruz. İşte app/views/delete.blade.php içeriği.
1
@extends('layout')
2
3
4
5
6
7
8
9
10
11
12
13
14
@section('content')
<div class ="page-header">
<h1>{{ $game->title }} Silinecek<small> Emin misiniz?</small></h1>
</div>
<form action ="{{ action('GamesController@handleDelete') }}" method ="post" rol\
e ="form">
<input type ="hidden" name ="game" value ="{{ $game->id }}" />
<input type ="submit" class ="btn btn-danger" value ="Evet" />
<a href ="{{ action('GamesController@index') }}" class ="btn btn-default">H\
ayır, yapma!</a>
</form>
@stop
Şimdi halledelim şu işi. Başka bir görünüm formumuz var. Hedefi handleDelete rotasıdır ve silinmek
istenen oyunu tanımlamakta kullanılabilecek bir hidden alanı bulunuyor.
‘Evet’ düğmesi formun gönderilmesine yol açacak ve oyun silinecek. ‘Hayır’ düğmesi ise sadece
home sayfasına redirect yapacak. Bu bir iptal mekanizması olarak etki gösterir.
Pekiyi, yapacağımız bir şey kaldı mı? Oo, ah, haklısınız! Edit görünümü. Yeni bir oyun girişi
oluşturduktan sonra bu oyunlar dokunulmaz olmayacaklar. Onları değiştirebilmeliyiz, bu nedenle
bunun için bir görünüm oluşturabiliriz. app/views/edit.blade.php görünümümüz şöyledir.
1
@extends('layout')
2
3
4
5
6
7
@section('content')
<div class ="page-header">
<h1>Oyun Düzenle <small> Devam et, tamamlandı olarak işaretle!</small></h\
1>
</div>
8
9
10
11
12
<form action ="{{ action('GamesController@handleEdit') }}" method ="post" role =\
"form">
<input type ="hidden" name ="id" value ="{{ $game->id }}">
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
379
<div class ="form-group">
<label for ="title">Adı</label>
<input type ="text" class ="form-control" name ="title" value ="{{ $game-\
>title }}" />
</div>
<div class ="form-group">
<label for ="publisher">Yayıncısı</label>
<input type ="text" class ="form-control" name ="publisher" value ="{{ $g\
ame->publisher }}" />
</div>
<div class ="checkbox">
<label for ="complete">
<input type ="checkbox" name ="complete" {{ $game->complete ? 'chec\
ked' : '' }} /> Tamamlandı mı?
</label>
</div>
<input type ="submit" value ="Kaydet" class ="btn btn-primary" />
<a href ="{{ action('GamesController@index') }}" class ="btn btn-link">İpta\
l</a>
</form>
@stop
Bir kez daha ‘content’ kesimine bir form enjekte ediyoruz fakat Game olgusu zaten mevcut
olduğundan, form alanlarını önceden tanımlı değerleriyle doldurabiliriz. Gerektiğinde ‘Tamamlandı
mı’ onay kutusunun ‘checked’ niteliğini uygulamak üzere yine ternary operator kullanıyoruz.
Harika! Şimdi bütün görünümlerimiz tamamlanmış oldu ve verilerimizle uğraşmak için gerekli tüm
modellerimiz fonksiyonel durumda. Artık controller eylem iskeletlerindeki boşlukları doldurarak
görünümlerimizi ve verilerimizi birbirine tutturma zamanı.
Uygulama Mantığı
Index rotamızla başlayalım. Yapacağımız bir şey var mı?
1
2
3
4
public function index()
{
// Oyunların listesini göster.
$games = Game::all();
5
return View::make('index', compact('games'));
6
7
}
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
380
Ah, yapacağınızı söyleyeyim! İhtiyacımız olan tek şey oyunlarımızın koleksiyonu. Ne kadar kolaymış değil mi? create() eylemi üzerinde düşünmemize gerek yok, çünkü bu sadece görünümümüzü
göstermeye hizmet etmektedir. Bunun yerine handleCreate() eylemine bir bakalım.
1
2
3
4
public function handleCreate()
{
// Oluşturma formu gönderimini işler.
}
Buradaki yorum satırının form gönderimini bizim için işlemeyeceğinden eminim. Boşlukları niye
doldurmuyoruz? Yeni bir game nesnesi oluşturmakla başlayalım ve bunun özelliklerini yeni oyun
oluştur formumuzdan gönderilen değerlerle dolduralım.
1
2
3
4
5
6
7
8
9
public function handleCreate()
{
// Oluşturma formu gönderimini işler.
$game = new Game;
$game->title = Input::get('title');
$game->publisher = Input::get('publisher');
$game->complete = Input::has('complete');
$game->save();
}
Yeni bir Game model olgusu oluşturuyoruz ve onun özelliklerini, Input::get() metodunu kullanarak isteğimizden elde edilen değerlerle dolduruyoruz. Son olarak, ilgili satırı veritabanında kalıcı
hale getirmek için save() metodunu kullanıyoruz.
Bu sayfadan bir cevap döndürmemiz gerekiyor, böylece bir hata almayacağız. Kullanıcı anlamlı
bir şeyler almalıdır. Bir başarıldı sayfası gösterebilirdik, fakat bu sadece zaman kaybı olacak.
Kullanıcı index sayfasına dönmek için tekrar tıklamak zorunda kalacak. Neden doğrudan index’e
yönlendirmeyelim?
1
2
3
4
5
6
7
8
public function handleCreate()
{
// Oluşturma formu gönderimini işler.
$game = new Game;
$game->title = Input::get('title');
$game->publisher = Input::get('publisher');
$game->complete = Input::has('complete');
$game->save();
9
return Redirect::action('GamesController@index');
10
11
}
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
381
Bu sefer, şablonlarımız içinde ürettiğimiz linklere benzer bir format kullanarak bir redirekt cevabı
döndürüyoruz. Sonra da edit() eylemimizi güncelleyelim. Mevcut hali şöyledir.
1
2
3
4
5
public function edit(Game $game)
{
// Oyun düzenleme formunu göster.
return View::make('edit');
}
Edit formumuzun, formu oyunumuzdaki ön tanımlı öğelerle doldurabilmesi gerekiyor, bu nedenle
görünüme oyun olgumuzu geçmemiz gerekiyor. Bunu şöyle değiştirebiliriz.
1
2
3
4
5
public function edit(Game $game)
{
// Oyun düzenleme formunu göster.
return View::make('edit', compact('game'));
}
Görünüm veri dizisi oluşturmak için bir kez daha compact() metodunu kullandık. Artık düzenleme
formu gönderilerini işleme zamanı. Bu handleEdit() eylemidir ve şu anda şöyle duruyor.
1
2
3
4
public function handleEdit()
{
// Düzenleme formu gönderimini işle.
}
Tamam, çok düz oldu değil mi? Bu yorumun düzenlenen oyunu veritabanında kalıcı hale getirmeyeceğinden oldukça eminim. Boşlukları dolduralım. Edit formu hidden bir alan şeklinde oyunun ID
değerini sağlamaktadır ve böylece oyun olgusunu elde etmek ve sütun değerlerini değiştirmek için
bu hidden alan değerini kullanabiliriz.
1
2
3
4
5
6
7
8
public function handleEdit()
{
// Düzenleme formu gönderimini işle.
$game = Game::findOrFail(Input::get('id'));
$game->title
= Input::get('title');
$game->publisher
= Input::get('publisher');
$game->complete
= Input::has('complete');
$game->save();
9
return Redirect::action('GamesController@index');
10
11
}
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
382
Bu daha iyi oldu! Game sınıfı üzerinde findOrFail() metodunu kullandık. Bu, find() metoduna
çok benzer, farklı olarak eğer sağlanan birincil anahtara sahip bir nesne bulunamazsa bir uygulama
404 olayını tetikleyecektir. Game nesnesine sahip olduktan sonra onu formumuzdan gelen bilgilerle
dolduruyoruz. Son olarak, güncellenen Game olgusunu kaydediyoruz ve index sayfasına redirekt
yapıyoruz.
Oyunlarımız şimdi oluşturulabilecek ve düzenlenebilecek. Şimdi bir oyunu silme işlevselliği sağlama
zamanı geldi. GamesControllerdeki handleDelete() eyleminin mevcut haline bir bakalım.
1
2
3
4
public function handleDelete()
{
// Sil teyidini işle.
}
Devam edelim ve boşlukları dolduralım.
1
2
3
4
5
6
public function handleDelete()
{
// Sil teyidini işle.
$id = Input::get('game');
$game = Game::findOrFail($id);
$game->delete();
7
return Redirect::action('GamesController@index');
8
9
}
Tıpkı handleEdit() eyleminde olduğu gibi, delete formumuzla sağlanan game hidden alanı tanımlayıcısını kullanarak bir oyun olgusunu elde ediyoruz. Sonra da ilgili satırı veritabanından çıkartmak
için olgu üzerinde sadece delete() metodunu kullanıyoruz. Oyunun silinmesiyle birlikte index
sayfasına geri dönebiliriz.
Şimdi GamesControllerin bitmiş halini görebiliriz.
1
<?php
2
3
// app/controllers/GamesController.php
4
5
6
7
8
9
10
class GamesController extends BaseController
{
public function index()
{
// Oyunların listesini göster.
$games = Game::all();
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
11
return View::make('index', compact('games'));
12
13
}
14
15
16
17
18
19
public function create()
{
// Oyun oluşturma formunu göster.
return View::make('create');
}
20
21
22
23
24
25
26
27
28
public function handleCreate()
{
// Oluşturma formu gönderimini işle.
$game = new Game;
$game->title
= Input::get('title');
$game->publisher
= Input::get('publisher');
$game->complete
= Input::has('complete');
$game->save();
29
return Redirect::action('GamesController@index');
30
31
}
32
33
34
35
36
37
public function edit(Game $game)
{
// Oyun düzenleme formunu göster.
return View::make('edit', compact('game'));
}
38
39
40
41
42
43
44
45
46
public function handleEdit()
{
// Düzenleme formu gönderimini işle.
$game = Game::findOrFail(Input::get('id'));
$game->title
= Input::get('title');
$game->publisher
= Input::get('publisher');
$game->complete
= Input::has('complete');
$game->save();
47
return Redirect::action('GamesController@index');
48
49
}
50
51
52
public function delete(Game $game)
{
383
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
384
// Sil teyiti sayfasını göster.
return View::make('delete', compact('game'));
53
54
}
55
56
public function handleDelete()
{
// Sil teyidini işle.
$id = Input::get('game');
$game = Game::findOrFail($id);
$game->delete();
57
58
59
60
61
62
63
return Redirect::action('GamesController@index');
64
}
65
66
}
Uygulamamızın tüm mantığı 66 satır tutuyor. Ne kadar güzel bir şey! Artık yeni uygulamamızı
deneme zamanı. Haydi başlayalım.
Rahatlayın
Öncelikle, uygulamanızın PHP web sunucusu tarafından servis edildiğinden emin olun. Eğer servis
edilmiyorsa, serve komutunu kullanabilirsiniz.
1
php artisan serve
Şimdi, oyunlarımızla ağzına kadar dolu index sayfamızla karşılaşmak için http://localhost:8000
adresini ziyaret edin… o da ne. Oyunlarımız nerede? Pekala, sorun değil. Gelin ilk oyunumuzu
oluşturalım.
‘Oyun Oluştur’ düğmesine tıklayın ve gösterilecek yeni bir oyun oluşturun. Ben adı kutusuna
‘Beyond: Two Souls’ ve yayıncısı alanına ‘Sony Computer Entertainment’ gireceğim. Bu oyunu
henüz tamamlayamadım, çünkü iki gün boyunca çıkmamış olacak (baş döndürücü heyecanla
bekliyorum), bu nedenle sadece ‘Oluştur’ düğmesine basabiliriz.
Şimdi index sayfamızda “Beyond: Two Souls” gösterilecek. Harika bir haber! Bu oyunu zaten tamamlamıştım. (Gerçekte değil, fakat burada düşünce gücümüzü kullanalım.) Oyunumuzu güncelleme
zamanı. Devam edin ve ‘Düzenle’ düğmesini tıklayın. Şimdi, ‘Tamamlandı mı’ kutusuna onay
koyabilirsiniz ve kaydı güncellemek için ‘Kaydet’ düğmesini tıklayabilirsiniz.
Bunu yapmak beni çok üzüyor ama işlevselliği test etmek için neden ‘Beyond’u silmeyelim ki. ‘Sil’
düğmesine basın ve oyunu silmek için ‘Evet’i tıklayın. İşlevselliğin hepsinin beklendiği gibi çalıştığı
görülüyor. Laravel’le ilk uygulamanızı tamamlamanızı tebrik ediyorum!
Bu projenin tam kodunu github’daki sineld/games-app³³ ambarında bulabileceğinizi unutmayın.
³³http://github.com/sineld/gamesapp
Bir Uygulama İnşa Edelim 1: Playstation Oyun Koleksiyonu
385
Ev Ödevi
Herkes toplansın, bu hafta sonunun ev ödevleri dağıtılacak. Tamam, dürüstlükle ifade etmem
gerekirse, kitapların ‘Alıştırmalar’ kısmını nadiren kullanırım, dolayısıyla böyle hissediyorsanız bu
kesimi atlayabilirsiniz.
Şu anda bizim uygulamamız formlarından gelen her değeri kabul edecektir. Örneğin ‘boş’ bir
adı olan bir oyun oluşturabileceğiz. Bu bizim sistemimizi bozacaktır. Öyleyse neden bir başvuru
olarak ‘Geçerlilik Denetimi’ bölümümüzü kullanarak uygulamamıza geçerlilik denetimi eklemeye
çalışmıyoruz?
Gelecek bölümde, uygulamamızı yetkilendirilmemiş kullanımlardan korumak için kullanılabilecek
authentication (kimlik doğrulama) konusunu göreceğiz. Bu bölüme geri dönüp authentication işini
uygulamaya çalışabiliriz.
Ödev için zamanınız yok mu? Dert etmeyin! Bir sonraki bölüme devam edelim.
Kimlik Doğrulama (Authentication)
Normalde bölümlere küçük bir esprili öykü ile başladığımı biliyorum ama bu hafta zamanım az, bu
yüzden plan şöyle. Eğer hep birlikte çalışırsak ve hayal gücünüzü kullanırsak benim Ian Landsman,
eğitimli iki aslan, bir şişe advocaat ve Roma metro sistemi hakkında gerçekten eğlenceli bir öykü
yazmış olduğumu düşünebiliriz. Tamam. İşte yaptık.
Peki bu bölümde neyi öğreneceğiz? Tümüyle gizli şeylere sahibiz değil mi? Kimsenin bilmesini
istemediğimiz karanlık sırlar? Ah, sadece benim mi? Tamam, eğer bu karanlık sırlarınız olmak
zorundaysa, onları meraklı gözlerden korumak için güvenli bir yolunuz olsun istersiniz. Bu bölümde,
bizim sınırlandırılmış içeriğimize sadece kimliği doğrulanmış kişilerin erişebilmesini temin etmek
için Laravel authentication sistemini nasıl kullanabileceğimizi öğreneceğiz.
Tamam o zaman! Başlayalım. Başlamadan önce, veritabanı doğru biçimde yapılandırılmış yeni
bir Laravel projesine ihtiyacımız var. Veritabanının nasıl kurulduğunu hatırlıyor musunuz? Eğer
hatırlamıyorsanız, neden Veritabanları bölümünü bir kez daha incelemiyorsunuz?
Authentication sistemini kullanmaya başlamadan önce, session’larımızı nerede saklayacağımıza
karar vermemiz gerekiyor. Bir session PHP ve Laravel’in istekler arasında kullanıcınızı hatırlamasına
izin verecek tarayıcı cookie’siyle eşlik eden küçük bir metin yüküdür. En iyisi daha fazla bilgi için
app/config/session.php dosyasının içine bir bakalım.
1
2
3
4
5
6
7
8
9
10
11
12
13
/*
|-------------------------------------------------------------------------| Default Session Driver
|-------------------------------------------------------------------------|
| This option controls the default session "driver" that will be used on
| requests. By default, we will use the lightweight native driver but
| you may specify any of the other wonderful drivers provided here.
|
| Supported: "native", "cookie", "database", "apc",
|
"memcached", "redis", "array"
|
*/
14
15
'driver' => 'database',
Gördüğünüz gibi, Laravel bize sessionlarımızı saklamak için çok sayıda yol teklif etmektedir. Siz
uygulamanız için en uygun olan depolama metodu hangisi ise onu kullanabilirsiniz ama şu an için
sessionları bir veritabanı dosyasında saklamak amacıyla ‘database’ seçeneğini kullanacağız.
Kimlik Doğrulama (Authentication)
387
Bu yapılandırma dosyasında sessionların uzunluğu, session cookiesinin ismi, sessionlar tablosunun
ismi ve daha fazlasını içeren çok sayıda ek yapılandırma seçenekleri bulunmaktadır. Bu örnek için bu
ayarların hepsini ön tanımlı değerleriyle bırakacağız ama mevcut olan daha fazla yapılandırmaların
ne olduğunu taramak ve görmekten çekinmeyin.
Session depolama yöntemi olarak veritabanında karar kıldığımıza göre, içinde session bilgilerimizi
saklayacağımız bir tablo oluşturmamız gerekiyor. Laravel yine aynı şekilde renkli kıyafetler içinde
gökyüzünden iniyor. Günümüzü kurtarmak için hazır ve istekli. Derin ve patlayıcı bir erkek sesiyle
Laravel “Session tablonuzu istihdam etmek için sadece artisan session:table komutunu kullanın.”
diyor.
“Teşekkürler Laravel!”. Haydi onu deneyelim.
1
2
$ php artisan session:table
Migration created successfully!
Bu artisan session:table komutu veritabanı içerisinde session tablomuzu oluşturacak bir migrasyon oluşturmuştur. Migrasyonları zaten öğrenmiştik, bu yüzden, üretilmiş migrasyonu çalıştırmak
için migrate komutunu çalıştırabiliriz.
1
2
$ php artisan migrate
Migrated: 2013_12_07_231153_create_session_table
Veritabanımızı incelersek session tablomuzun oluşturulmuş olduğunu görürüz. Harika! Sonra da,
uygulamamıza login yapacak bazı kullanıcılar oluşturmamız gerekiyor.
Ön tanımlı olarak, Laravel app/models/User.php dosyasında bir User Eloquent modeli ile birlikte
gelir. Ona bir göz atalım.
1
<?php
2
3
4
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
5
6
class User extends Eloquent implements UserInterface, RemindableInterface {
7
8
9
10
11
12
13
14
/**
* The database table used by the model.
*
* @var string
*/
protected $table = 'users';
Kimlik Doğrulama (Authentication)
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = array('password');
15
16
17
18
19
20
21
/**
* Get the unique identifier for the user.
*
* @return mixed
*/
public function getAuthIdentifier()
{
return $this->getKey();
}
22
23
24
25
26
27
28
29
30
31
/**
* Get the password for the user.
*
* @return string
*/
public function getAuthPassword()
{
return $this->password;
}
32
33
34
35
36
37
38
39
40
41
/**
* Get the e-mail address where password reminders are sent.
*
* @return string
*/
public function getReminderEmail()
{
return $this->email;
}
42
43
44
45
46
47
48
49
50
51
52
}
Vay canına! Burada çok kullanışlı şeyler var. Her kesime daha yakından bakalım.
388
Kimlik Doğrulama (Authentication)
1
389
<?php
2
3
4
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
5
6
class User extends Eloquent implements UserInterface, RemindableInterface {
User modelimiz hem UserInterface hem de RemindableInterface interfacelerini implemente
ediyor. Bunlar ne içindir? Bunlardan UserInterface modelin Laravel’in kendi authentication
sistemi ile kimlik doğrulaması yapması için gerekli tüm metodları içerdiğini Laravel’in bilmesini
sağlar. RemindableInterface ise şifre hatırlatıcı e-postaları gönderme işlevselliğine imkan vermek
için Laravel’in kullanıcı e-posta adresini veya diğer iletişim bilgilerini alıp getirmesine izin verir.
$table özelliğinin ne olduğunu Eloquent ORM bölümünden biliyoruz, peki sonraki özellik nedir?
1
2
3
4
5
6
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = array('password');
Bu $hidden dizisi içinde yer alan alanlar toJson() ve __toString() metodları kullanılarak üretilen
her türlü çıktıdan dışlanacaktır. Bu sonuçlar içerisinde kullanıcımızın şifresinin gösterilmesini
istemeyebiliriz. Eğer isterseniz tabii ki bu özelliği çıkartabilirsiniz. Ben onu orada bırakacağım.
Modelde tanımlanmış olan metodlara daha ayrıntılı olarak bakabiliriz. getAuthIdentifier() metodu ile başlayacağız.
1
<?php
2
3
4
5
6
7
8
9
10
11
/**
* Get the unique identifier for the user.
*
* @return mixed
*/
public function getAuthIdentifier()
{
return $this->getKey();
}
Bu metod kullanıcı için benzersiz bir tanımlayıcı olarak davranacak bir model özelliği belirlememize
imkan verir. Ön tanımlı olarak, taban modelden miras aldığı $this->getKey() metodunu kullanarak
birincil anahtara ayarlanmıştır; ancak, bunu istediğimiz herhangi bir benzersiz özelliğe değiştirebiliriz. Şimdilik onu key olarak bırakalım.
Kimlik Doğrulama (Authentication)
1
2
3
4
5
6
7
8
9
390
/**
* Get the password for the user.
*
* @return string
*/
public function getAuthPassword()
{
return $this->password;
}
getAuthPassword() metodu model içerisinde kullanıcı şifresi için kullanılan özelliği tanımlamak
için kullanılır. Ön tanımlı değer password sütunudur ama aynı şekilde bunu da kendi gereksinimle-
rimize göre değiştirebiliriz.
Son olarak, bir getReminderEmail() metodumuz var.
1
2
3
4
5
6
7
8
9
/**
* Get the e-mail address where password reminders are sent.
*
* @return string
*/
public function getReminderEmail()
{
return $this->email;
}
Bu metod kullanıcı e-posta adresini taşıyacak model özelliğini tanımlamak için kullanılır. Bu daha
sonra, eğer Laravel’in yerleşik şifre hatırlatma mekanizmasını kullanmak istersek kullanılabilecektir.
User modelimiz için bir migrasyon oluşturmaya geçebiliriz. Şema oluşturucusu ve migrasyon
sisteminin nasıl kullanıldığını hatırlıyorsunuz değil mi? Şuna benzer bir şeydir…
1
2
3
$ php artisan migrate:make create_user_table
Created Migration: 2013_12_07_233727_create_user_table
Generating optimized class loader
Şimde de üretilen migrasyonu dolduralım. Bunu bir ‘username’, ‘password’ ve ‘email’ alanı ile
basit tutacağız. Şifre alanımız, bcrypt kriptolu şifre uzunluğunu tam desteklemek için 60 karakter
uzunluğunda olacak. İleride ayrıntısı olacak!
Kimlik Doğrulama (Authentication)
1
391
<?php
2
3
use Illuminate\Database\Migrations\Migration;
4
5
class CreateUserTable extends Migration {
6
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function($table)
{
$table->increments('id');
$table->string('username', 128)->unique();
$table->string('password', 60);
$table->string('email', 320)->unique();
$table->timestamps();
});
}
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('users');
}
24
25
26
27
28
29
30
31
32
33
34
}
Aynısının iki kez olmasını istemediğimiz için ‘username’ ve ‘email’ alanlarında unique() sınırlandırma da ekledim. Ayrıca, bir otomatik artan birincil anahtar ve model zaman damgaları eklemek
de kimseye zarar vermez. Migrasyonlarımızı tekrar çalıştırabiliriz.
1
2
$ php artisan migrate
Migrated: 2013_12_07_233727_create_user_table
Kimlik Doğrulama (Authentication)
392
Şimdi, sisteme kullanıcılar eklemek için bir yol sağlanması gerekiyor. Yaşasın! Bir form gerekiyor.
Tüm web geliştiricleri formları sever… değil mi? Bölümün hızını artırmak için bu şartlarda kayıt
formumuz için geçerlilik denetimini atlayacağız ve bu hususa izole biçimde yaklaşacağız ama
geçerlilik denetimi implemente etmek istiyorsanız neden Geçerlilik Denetimi (Validation) bölümüne
bir göz atıp onu denemeyesiniz ki? Bunu bir ev ödevi olarak kabul edin!
Kayıt formumuz için bir view oluşturarak başlayabiliriz. Onu gerçekten basit tutacağız. Şu anda,
bu iş yanında bir parça ön taraf geliştirme de yaptığımız için form elementlerini <p> tagları ile
sarmalamanın mantıklı olmadığını biliyorum. Ancak bu örnek projeye bir style-sheet eklemek
istemiyorum, bu yüzden onları sadece form alanlarımızı bir block stili içinde göstermek için
kullanıyorum.
İşte basit form view’imiz, ben onu app/views/create_user_form.blade.php olarak kaydettim.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<title>Kullanıcı oluştur</title>
</head>
<body>
<form action ="{{ url('user') }}" method ="post">
<p><label for ="username">Kullanıcı adı:</label></p>
<p><input type ="text" name ="username" placeholder ="Kullanıcı adı" /><\
/p>
<p><label for ="email">Email:</label></p>
<p><input type ="text" name ="email" placeholder ="Email" /></p>
<p><label for ="password">Şifre:</label></p>
<p><input type ="password" name ="password" placeholder ="Şifre" /></p>
<p><input type ="submit" value ="Oluştur"></p>
</form>
</body>
</html>
Tam bir sanat eseri, eminim bana katılıyorsunuz! HTML yazarken kendimi rahat hissediyorum, bu
nedenle Laravel’in form oluşturucu bileşenini nadiren kullanıyorum. Eğer siz istiyorsanız, kullanın.
Sizi durduracak değilim! Laravel kendi kararlarımızı verme konusunda bizi özgür bırakır ve ben de
sizin bu özgürlüğün avantajından yararlanmanızı teşvik ediyorum.
User modelimizin gerektirdiği üç alanın ve bir ‘Oluştur’ submit düğmesinin olduğu bir form
oluşturduk. Basit olması adına csrf tokeni kullanmadım ve formun POST /user rotamızı hedeflemesi
için url() helperi kullandım. Devam edelim ve gerekli rotaları oluşturalım.
Kimlik Doğrulama (Authentication)
1
393
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/', function()
{
return View::make('hello');
});
9
10
11
12
13
Route::get('/user', function()
{
return View::make('create_user_form');
});
14
15
16
17
18
19
20
21
22
Route::post('/user', function()
{
$user = new User;
$user->username
= Input::get('username');
$user->password
= Hash::make(Input::get('password'));
$user->email
= Input::get('email');
$user->save();
23
return Response::make('Kullanıcı oluşturuldu! Yaşasın!');
24
25
});
routes.php dosyamızdaki frameworkün / hoşgeldiniz rotasını bıraktık. Sonra da yeni kullanıcı
oluştur formunu basitçe göstermek için kullanılacak bir GET /user rotası ekledik.
Son olarak, yeni bir User olgusu oluşturan bir POST /user rotamız var. Kullanıcı oluştur formumuzdan elde ettiğimiz değerleri ayarlıyor ve kullanıcıyı veritabanında kalıcı hale getiriyor.
Dikkat ederseniz kullanıcı şifresini Laravel’in kendi authentication sistemi için kullandığı bcrypt
kriptolama mekanizması ile kriptolamak için Hash::make() metodunu kullandık. Bu Hash::make()
metodunu bir değer üzerinde birden çok kullanabileceğinizi ve her seferinde farklı string sonuçlar
alabileceğinizi belirtmekte yarar var. Panik yapmayın! Bu normal bir davranıştır. Burada becerikli
bir matematik yürütülmektedir ve şifrenizin hala tasdik edileceği konusunda sizi temin ederim.
Rotamızdan, eylemin başarılı olduğunu bilmemizi sağlayan basit bir metin cevabı döndürüyoruz.
Harika! Şimdi, yeni bir kullanıcı oluşturmak için /user adresini ziyaret edin. Eğer lokal web sunucunuzu public dizinine yöneltmediyseniz, PHP’nin yerleşik web sunucucusuyla hızlı bir başlangıç
yapmak için php artisan serve komutunu kullanabilirsiniz.
Kimlik Doğrulama (Authentication)
1
394
Kullanıcı oluşturuldu! Yaşasın!
Sonunda, gizli içeriğimizi güvenceye alabiliyoruz. Ama bir dakika, bir şeyleri unutuyoruz. Gizli
içerik. Sonra yine… gizli içerik… hmm. Evet, gizli içerik hayran olduğum ünlülerin bir listesi olacak.
Diğer yarımı hiç sormayın! Bunu gerçekten güvenli bir hale getirmemiz gerekiyor.
İlk olarak, bir view göstermek için bir GET /crush rotası oluşturalım.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/crush', function()
{
return View::make('crush');
});
Daha sonra da ünlüler için bir view oluşturmamız gerekiyor. Devam edelim ve app/views/crush.blade.php
view’i içinde, benim hayran olduğum ünlülerin bir HTML listesini oluşturalım.
1
<ul>
2
<li>Keira Knightley</li>
<li>Emma Watson</li>
</ul>
3
4
Burada Keira Knightley ve Emma Watson için iki liste girişimiz bulunuyor. Çok güzel iki bayan.
Evet, Emma Watson şimdi gayet güzel ama onu düşünüp de ilk Harry Potter filmini seyretmenizi
hiç önermem. ÇOK kötü hissedeceksiniz. Bunu yapmayın. Oldukça travmatik bir deneyimdir. zangır
zangır titreme
Neyse, devam edelim…
Şimdi en gizli verilere sahip olduğumuza göre onu korumamız gerekiyor. En temel authentication
biçimiyle başlayabiliriz: HTTP basic auth. Basic auth tarayıcı ile web sunucusu arasında bir iş
birliğinin olduğu bir authentication sistemidir. Bu sistem özel bir giriş formuna gerek olmaksızın
bir kimlik doğrulama mekanizması oluşturmamıza imkan verektedir. Başlamak için mükemmel bir
yoldur.
Sistemimiz içerisinde kimlik doğrulamayı zorlamak için bir rotayı korumak gerekir. Bunu bir
rota filtresi kullanmak suretiyle gerçekleştirebiliriz. Bizim GET /crush rotamızı meraklı gözlerden
korumak için auth.basic filtresini kullanabiliriz.
Rotanın güncellenmiş hali şöyledir:
Kimlik Doğrulama (Authentication)
1
2
3
4
5
6
7
395
Route::get('/crush', array(
'before' => 'auth.basic',
function()
{
return View::make('crush');
}
));
GET /crush rotamızın ikinci parametresini bir dizi olarak değiştirdik, böylece rotaya ek bilgiler
tutturabileceğiz. Buraya auth.basic adında bir before rota filtresi dahil ettik. Bu, app/filters.php
dosyası içerisinde bulabileceğiniz yerleşik bir filtre metodudur. HTTP temel authentication yoluyla
bu rotayı korumak için yapmamız gereken tek şey bu filtreyi dahil etmektir.
Haydi bir deneyelim. İlk önce tarayıcımızda GET /crush URL’sini ziyaret etmemiz gerekiyor. Web
tarayıcımız kendi HTTP temel giriş forumunu gösterecektir. Bu, kullandığınız tarayıcıya bağlı olarak
farklı bir görünümde olabilir.
Ön tanımlı olarak, HTTP temel authentication mekanizması kullanıcı adı olarak User email adresini
kullanacaktır. Dolayısıyla, devam edip kullanıcı oluştur formuna girdiğimiz email adresi ve şifreyi
buraya girebiliriz.
Hayran olduğum ünlülerin listesi gösterildi. Bu liste şimdi basic auth ile korunmuştur ve sadece
doğru kullanıcı bilgileri (credentials) girilirse gösterilecektir. Tarayıcımızın sessionu sona erinceye
kadar, bu sayfaya erişme haklarımız devam edecektir.
Çıkış yapma nasıl olacak? Peki, bu amaçla da bir GET /logout rotası oluşturabiliriz. Ona yakından
bakalım.
1
2
3
4
5
Route::get('/logout', function()
{
Auth::logout();
return Response::make('Şimdi çıkış yaptınız. :(');
});
Auth::logout() metodu bizim authentication oturumunu yok edecektir. Eğer GET /logout rotasını
ziyaret eder, sonra da GET /crush‘a geri dönerseniz giriş yapmanızın tekrar istendiğini göreceksiniz.
Email adresi ile giriş yapılması iyidir ama eğer biz bunun yerine username alanını kullanarak
kimlik doğrulaması yapmak isteseydik ne olacaktı? Elbette! Problem değil. Sadece app/filters.php
içindeki auth.basic filtresini aşağıdaki gibi değiştirin.
Kimlik Doğrulama (Authentication)
1
2
3
4
396
Route::filter('auth.basic', function()
{
return Auth::basic('username');
});
Temel login işlemi için kullanıcı adı olarak hangi alanın kullanılacağını belirtmek için Auth::basic()
metoduna ilk parametre olarak bir string verdik. Bizim örneğimizde, HTTP basic auth’un User
modelinin username alanını kullanmasını belirtiyoruz.
Tekrar deneyelim, oturumunuzu yok etmek için GET /logout rotasını ziyaret edin. Şimdi GET /crush
rotasını bir kez daha ziyaret edin. Bu sefer giriş yapmak için username ve password alanlarınızı
kullanın.
Harika, hayran olduklarımızı yine gördük.
HTTP basic auth ile yapılması çok hızlı olmakla birlikte, tarayıcıların kendi giriş formlarına
dayanmak zorunda kalıyoruz. Ne yazık ki, onun nasıl görüneceği üzerinde hiçbir kontrole sahip
değiliz ve pek de esnek değildir. En iyisi Laravel’in kendi auth sistemini kullanmaya çalışalım ve bir
giriş formu oluşturalım.
İlk olarak app/views/login_form.blade.php dosyası içinde, giriş yapma kimlik bilgilerini gireceğimiz bir form view’i gerekecek.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<title>Giriş Yap</title>
</head>
<body>
<form action ="{{ url('login') }}" method ="post">
<p><label for ="username">Kullanıcı adı:</label></p>
<p><input type ="text" name ="username" placeholder ="Kullanıcı adı" /><\
/p>
<p><label for ="password">Şifre:</label></p>
<p><input type ="password" name ="password" placeholder ="Şifre" /></p>
<p><input type ="submit" value ="Giriş Yap"></p>
<p><input type ="checkbox" name ="remember" /> <label for ="remember">Be\
ni hatırla.</label></p>
</form>
</body>
</html>
İşte giriş formumuz. Kullanıcı oluştur formuna çok benziyor, tek farkı bu sefer sadece username ve
password alanlarımız var. Form, POST /login URI’sini hedefleyecek. Ayrıca, şifrenin sonraki login
girişimlerinde hatırlanabilmesini ifade etmek için bir ‘Beni hatırla.’ onay kutumuz var. Eminim daha
önce bunun gibi bir sistem görmüşsünüzdür. İnternet kullanıyorsunuz değil mi? :)
Kimlik Doğrulama (Authentication)
397
Giriş formumuzu göstermek için bir rota oluşturalım. Ona GET /login rotası diyeceğiz. Biliyorum.
Üretken, değil mi?
1
<?php
2
3
// app/routes.php
4
5
6
7
8
Route::get('/login', function()
{
return View::make('login_form');
});
Sonra da, süper gizli GET /crush rotamızı auth.basic yerine auth filtresi kullanacak şekilde
değiştirelim. Bunun gibi.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
Route::get('/crush', array(
'before' => 'auth',
function()
{
return View::make('crush');
}
));
Şimdi çıkış yapalım ve /crush rotasını bir kez daha ziyaret edelim.
Ne oldu? Daha şimdi oluşturduğumuz giriş formuna yönlendirildik. Daha fazla ayrıntı için app/filters.php
dosyasındaki auth filtresine bir göz atalım.
1
2
3
4
Route::filter('auth', function()
{
if (Auth::guest()) return Redirect::guest('login');
});
Aha, Görüyorum. Bu auth filtresi kullanıcının bir misafir olduğunu ve sisteme giriş yapmamış
olduğunu belirlemek için Auth::guest() metodunu kullanıyor. Eğer öyleyse, /login formuna bir
Redirect cevabı döndürmek için Redirect::guest() metodunu kullanacaktır.
Vavv! Login form rotamızı çağıran bu muymuş? Çok uygun, değil mi? Neredeyse bu işleri önceden
planlamışım gibi.
Artık giriş formumuz olduğuna göre, form gönderildiğinde onu işleyecek bir rota gerekiyor. Bir POST
/login rotası oluşturabiliriz.
Kimlik Doğrulama (Authentication)
1
398
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
12
Route::post('/login', function()
{
$credentials = Input::only('username', 'password');
if (Auth::attempt($credentials)) {
return Redirect::intended('/');
}
return Redirect::to('login');
});
Bu sistemle kimlik doğrulaması yapmak için, öncelikle bir kimlik bilgileri dizisi inşa etmemiz gerekiyor. username ve password değerlerini istek verisinden çekiyoruz. Şifrenin hashingi (karıştırılması)
ile ilgilenmemiz gerekmiyor, Laravel bunu bizim için yapacaktır.
Kimlik bilgileri dizisine başka sınırlamalar ekleyebileceğimizi bilmek önemlidir. Örneğin, hesabın
aktif olduğundan emin olmak için 'enabled' => true ekleyebiliriz.
Kimlik bilgileri dizisi inşa edildikten sonra, onu Auth::attempt() metoduna geçiyoruz. Bu metod
login girişiminin başarılı olup olmadığını göstermek için kullanılan boolean bir sonuç döndürecektir.
Eğer kimlik doğrulama girişimi başarılı olmuşsa, bu durumda session’ımız oluşturulmuş olacak ve
artık sisteme giriş yapmış olacağız.
Yukarıdaki örnekte Redirect::intended() metodu dikkatinizi çekecektir. Bu neyin nesi ki? Pekala,
online olarak ikinci el bir araba bulmaya çalıştığınızı düşünün. Neden? Pekiyi, çünkü yeni olanların
hepsi aynı ve can sıkıcı görünüyor. Neyse, dostunuz Dayle sizin ilgilenebileceğiniz bir araba için
size direkt bir link gönderiyor. Eski bir WRX Impreza veya böyle bir şey. İyi ama ne yazık ki bu site
arabaları görmek için giriş yapmanızı gerektiriyor. Dayle’in size gönderdiği linki tıklıyorsunuz ve
login formuna yönlendiriliyorsunuz.
Siteye giriş yaptıktan sonra sitenin ana sayfasına yönlendiriliyorsunuz. Bu ne kadar kullanışsız, değil mi? Siz arabayı görmek istemiştiniz. Şimdi linki tekrar tıklamak zorundasınız. İşte
Redirect::intended() metodu bu durumları önlemek için kullanılabilmektedir.
Redirect::intended() metodu, kullanıcılarımızı, kimlik doğrulama filtremize rastlamadan önce
erişme girişiminde bulundukları rotaya yönlendirecektir. Authentication işlemi tamamlandıktan
sonra, onları gitmek istemiş oldukları yere geri gönderiyoruz. Bu metodun ilk parametresi ön tanımlı
bir rotadır; ön tanımlı bir rota vermenin amacı eğer kullanıcılar login rotasına direkt girmişlerse,
onlara uygun bir sayfa sunulmasını temin etmektir.
Yeni oluşturmuş olduğumuz kimlik doğrulama sistemini test edebilir miyiz? Öncelikle, sistemden
çıkış yaptığımızdan emin olmak için GET /logout rotasına gidiniz. Sonra da login rotamıza
yönlendirecek GET /crush rotamıza gidiniz. Formu doldurup gönderin, GET /crush sayfasına
varacağınızı umuyorum. Aferin size!
Kimlik Doğrulama (Authentication)
399
Tamam, şeye geçebiliriz… ah bir dakika. ‘Beni hatırla.’ işlevselliğini unuttuk! Geri dönüp onu
düzenleyebileceğimizi düşünebiliriz, ama biliyor musunuz, onu hemen burada düzeltebiliriz?
POST /login rotamızı bir kez daha düzenleyelim.
1
<?php
2
3
// app/routes.php
4
5
6
7
8
9
10
11
12
13
Route::post('/login', function()
{
$credentials = Input::only('username', 'password');
$remember = Input::has('remember');
if (Auth::attempt($credentials, $remember)) {
return Redirect::intended('/');
}
return Redirect::to('login');
});
Beni hatırla onay kutumuzu temsil eden boolean bir değeri almak için bir satır ekledik. Sonra da
bu $remember değerini Auth::attempt() metoduna ikinci bir parametre olarak geçiyoruz. Şimdi
eğer giriş formunda bu kutu onaylanmışsa, siteye giriş yapmak istediğimiz bir sonraki sefer bizim
username ve password’ü hatırlayacaktır. Tamam o zaman. Şimdi geçebiliriz.
Sisteme giriş yaptıktan sonra, şu anda giriş yapmış kullanıcıyı saptayabilmek yararlı olabilir. Dert
etmeyin! Taylor bu konuyu da çoktan düşünmüş. Auth::user() metodu hali hazırda giriş yapmış
olan kullanıcıyı temsil eden bir Eloquent ORM olgusu döndürecektir. Bunun anlamı Eloquent
içindeki her şeyi kullanabilmemiz demektir. Kullanıcının ilişkilerine veya belki username özelliği
gibi daha basit bir şeye erişebiliriz.
1
2
$user = Auth::user();
echo $user->username;
Pekala! Hanımlar, beyler. Bugün sizinle paylaşacağım son bir püf noktası var. Siz şimdiye kadar
yöneticilerin başka bir kullanıcıya ‘benzemesine’ imkan veren bir web uygulaması gördünüz mü.
Örneğin “Derek’e değiş”. Hayır? Peki, inanın bana bizim oralarda vahşi doğada böyle uygulamalar
var. Çoğunlukla CMS türü şeyler.
Neyse, biraz yorulmaya başladığımı ve yazılarımın bir parça garip olmaya gittiğini fark ettim, bu
nedenle yüksek bir ses tonunda bitireceğim ve aynı işlevselliği sizin kendi uygulamalarınızda nasıl
elde edebileceğinizi göstereceğim.
Kimlik Doğrulama (Authentication)
1
2
3
400
$user = User::find(5);
Auth::login($user);
// Şimdi Derek'iz!
Tamam, işte yaptık! Benzemek istediğimiz kullanıcıya giriş yaptırtmak için yalnızca Auth::login()
metoduna, o kullanıcıya ait bir Eloquent modeli olgusu geçiyoruz. Süper yararlı!
O nerede olacak?
Bu küçük bölüm Taylor ve benim neredeyse günlük bir temelde aldığımız bir soruya dayanıyor.
“xxx nerede olacak?” veya “xxx’i nereye koyacağım?”. Bu ilginç bir sorudur. Bir şekilde, biz bu
soruların controllerler ve modeller gibi sınıflar için standart konumlar uydurmamızdan kaynaklandığını düşündük ama PHP dünyasında ve composer tabanlı bir uygulamada bize daha önce
kullandıklarımızdan daha fazla özgürlük verilmiştir.
Kendi kendimize bir cevap bulmaya çalışalım. Gözlerinizi kapatın.
Kahretsin, yine aynısını yaptım. Şimdi ben size daha sonra gözlerinizi açmanızı nasıl söyleyebilirim?
Ah tamam, sıkıldığınızı ve sonunda gözlerinizi açtığınızı var sayıyorum. Gözlerin kapatılması,
benim zamanımın çoğunu harcadığım bir yer olan hayal gücü için bir gereksinimdir. Bir mutfakta
olduğunuzu hayal edin. Biliyorum! Heyecan verici değil mi? Elinizde bir tabak var. Tabağı nereye
koyarsınız?
Biliyorum! Gerçekten zor bir soru. Öyle çok yer var ki, örneğin, onu kurutma rafına koyabilirdik
ama o aslında ıslak değil, öyle değil mi? Bence oraya koyulması iyi olurdu ama sizce bu mantıklı
mı? Sanırım onu mutfak tezgahının üstüne koyabiliriz, orada kesinlikle işe yarayacaktır fakat siz
düzenliliği düşünüyorsunuz?
Dolaba koyabiliriz! Eğri oturup doğru konuşalım! Gözlerden uzak, karmaşıklık yok. Bu mükemmel
oldu. Peki o zaman, bu tabağı hemen dolaba koyal… ah. Hangi dolaba? Oh hayır, bir başka soru.
Gördüğünüz gibi, bu aslında bir tercih meselesidir. Tabağınızı nereye koyacağınızı size ben söyleyemem. Onu sizin için yerleştiremem. Onu bir daha bulamazsınız. Ben bütün tabaklarımı mutfak
tavanına selobantla bantlamayı severim. Yarı organizasyon yarı güvenlik mekanizmasıdır.
Beklediğiniz cevap bu değildi, değil mi?
Gee… Çok sağol Dayle! YA DA SANA FAYLE DİYEBİLİR MİYİM?!
Komik, evet. Bunu ilk defa duyuyor değilim. Bir dakika sakin kalın. İsim falan takmayın. O gerçektir,
ben size kodunuzu nereye koyacağınızı söyleyemem ama karar vermenize yardımcı olabilirim.
Projenizi nasıl şekillendireceğinizi gösterebilirim. Bu nasıl geliyor?
Peki… Sanırım bunu yapmam gerekiyor?
Bu daha iyi oldu! Teşekkürler.
Tamam öyleyse, bir an için Laravel kodlarını düşünelim. Şöyle bakabiliriz, iki tip kodumuz var. İyi
kod ve köt… haha sadece şaka. Prosedürel kodumuz ve nesne yönelimli kodumuz var.
O nerede olacak?
402
Bir dakika! Prosedürel kod mu? Ben modern bir programcıyım, burada QBasic yazmıyoruz. Bana bazı nesneler verin!
Sakin ol Ready McRead. Bu akşam çok alıngansın, öyle değil mi? PHP’de her zaman için prosedürel
bir şeyler vardır. İşte eğlenceli bir oyun. Sadece sınıfı olan çalışan bir PHP uygulaması yazmayı
denesene. Başka hiçbir kod olmayan.
:(
Evet, ben de öyle düşünmüştüm. PHP’de public static void main() eşdeğeri bir şey yoktur. N’aber
Java kankaları? Bizim nesne yönelimli kodumuzun çalışabilmesi için bir miktar bootstrapping
(önceden kod yüklemesi) yapılması gerekiyor.
1
<?php
2
3
4
$a = new MyApplication;
$a->init(); // Azap makinemizi başlat.
Bu bir PHP bootstrap örneğidir. Sınıfımızın bir olgusunu oluşturmamız ve ilk metodunu başlatmamız gerekiyor. Eğer bunu yapmazsak, prosedürel koda takılıp kalırız.
Laravel farklı değildir. Gidin public/index.php dosyasına bir bakın. Bu dosya, yeni bir istek
aldığımız zaman web sunucusunun karşılaştığı ilk dosyadır.
1
2
3
4
5
6
7
<?php
/**
* Laravel - A PHP Framework For Web Artisans
*
* @package Laravel
* @author
Taylor Otwell <[email protected]>
*/
8
9
10
11
require __DIR__.'/../bootstrap/autoload.php';
$app = require_once __DIR__.'/../bootstrap/start.php';
$app->run();
Taylor’un güzelim yorumlarından birçoğunu çıkarttım ama amacı gördünüz. Bir parça procedural
bir kodumuz var ve ondan sonra $app->run(); ile uygulamayı başlatıyoruz.
Bu sadece çekirdeğe özgü değildir, devam edin ve app/routes.php veya app/global/start.php
dosyasına bir göz atın. Hepsi prosedürel kod. Niye böyle?
Çoğu durumda, Laravel içindeki prosedürel dosyalar ya yapılandırma için ya da bir şeyleri kayda
geçirmek için kullanılır. Event dinleyicileri, model gözlemcileri, rotalar, özel validatorler. Bu
bileşenlerin mantığı çoğu keresinde bir sınıf içine konur ve nesne yönelimli kodumuzdur ama
Laravel’in onun mevcut olduğunu bilmesi için…
O nerede olacak?
1
403
<?php
2
3
Validator::extend('foo', 'Me\Validators\Validate@validate');
… yukarıdaki gibi bir satır kullanırız.
Eğer bunu yapmasaydık, bu durumda Laravel dosyalarımızı bulmak için dizinler boyunca dönüp
durmak zorunda kalacaktı. Bu dosyaların bilinen bir konumda olması gerekirdi. Bu çok esnek olmaz
değil mi? Bir saniye için siz ve ben yatak odanızı düşünelim…
Işıkları söndürün. - Müzik setinde Barry White açın.
Haklısınız. Şimdi iş zamanı. Her neyse, sizin yatak odanızda manolya duvarlar, kahverengi bir halı ve
bir köşede bir yatak yok, değil mi? Hiçbir şekilde. Orası sizin alanınız. Keira Knightley posterleriniz
var. Star Wars heykelcikleriniz var. Bir yerlerde bir sürü biblolarınız var. Onları kişiselleştirdiniz. O
sizin kişiliğinizi yansıtıyor.
Bu, kod temelinizin nasıl olması gerektiğidir. Tamam… eğer bir ekipteyseniz, bu durumda ekibinizin
kişiliğini ve ihtiyaçlarını yansıtacaktır. Şimdilik solo projeler hakkında düşünebiliriz. Bu uygulama
sizindir. Kuralları siz koyarsınız. Siz büyük kahunasınız.
İşte Laravel’in Validatorleri ve Observerları ve varsayılan konumlarda bulunan diğer tüm eğlenceli
şeyleri aramamasının nedeni budur. Onları, sizin onların yaşamasını istediğiniz yere koymakta
özgürsünüz.
Peki controllers ve models için ne diyeceksin?
Evet, evet… Bunlar için app klasöründe dizinler olduğunu biliyorum. Bunun nedeni herkesin
kendi MVC frameworklerinden controllers ve models bekliyor olmalarıdır. Eğer bu frameworkü
boş bir dizin olarak sürseydik insanlar ondan uzak dururlardı. N’aparsınız, bir yerden başlamak
zorundasınız.
Ama bu, bu dizin yapısına yapışmak zorundasınız anlamına gelmez. Sizi onu kullanmak için
zorlamıyoruz. Kalıpları kırın!
Gelin her bir kod türüne göz atalım ve proje ortamımızı özelleştirmek için yapabileceğimiz şeyleri
görelim.
Prosedürel Kod
Prosedürel kodu düşündüğümüzde radarımıza önce app/routes.php geliyor. Hmm, Acaba bu
routes.php nereden çalıştırılıyor? Peki, en iyisi vendor/laravel/framework/src/Illuminate/Foundation/start.p
içine bir göz atalım. Bu sadece bir tahmin!
O nerede olacak?
1
404
<?php
2
3
// Diğer bootstrap kodunun çoğu buradadır...
4
5
6
7
8
9
10
11
12
13
14
/*
|-------------------------------------------------------------------------| Load The Application Routes
|-------------------------------------------------------------------------|
| The Application routes are kept separate from the application starting
| just to keep the file a little cleaner. We'll go ahead and load in
| all of the routes now and return the application to the callers.
|
*/
15
16
$routes = $app['path'].'/routes.php';
17
18
if (file_exists($routes)) require $routes;
19
20
});
Hey, işte orada! Yani, ne şanslı bir tahminmiş? Eğer app/routes.php mevcutsa, bootstrap süreci
sırasında yüklenecektir. Bu kod parçası vendor dizininde bulunduğu için ve uygulamanızla birlikte
versiyonlanması beklenemeyeceği için, oraya ek dosyalar ilave etmek kötü bir fikir olacaktır.
Devam edelim ve bu start.php dosyasına biraz daha bakalım. Bu kesimi görüyor musunuz?
1
<?php
2
3
4
5
6
7
8
9
10
11
12
/*
|-------------------------------------------------------------------------| Load The Application Start Script
|-------------------------------------------------------------------------|
| The start script gives us the application the opportunity to override
| any of the existing IoC bindings, as well as register its own new
| bindings for things like repositories, etc. We'll load it here.
|
*/
13
14
$path = $app['path'].'/start/global.php';
15
16
if (file_exists($path)) require $path;
405
O nerede olacak?
Dahil edilen yoruma bakarsanız, bu app/start/global.php dosyasında IoC konteynerindeki
bağlamaları override edebileceğimizi söylüyor. Bunun anlamı, Laravel’in bileşenleri bu noktada
yüklenmiş ve kullanıma hazırdır demektir.
Bunun ifade ettiği şey, bizim kendi dosyalarımızı include etmek için eski, iyi dostumuz require()
metodunu app/start/global.php dosyasında kullanabileceğimiz ve prosedürel kodumuz için kendi
yapımızı tanımlayabileceğimizdir.
Bunun en iyi şekilde bir örnekle gösterileceğini düşünüyorum.
1
<?php
2
3
// Daha birçok bootstrap kodu..
4
5
6
7
8
9
10
11
12
13
14
/*
|-------------------------------------------------------------------------| Require The Filters File
|-------------------------------------------------------------------------|
| Next we will load the filters file for the application. This gives us
| a nice separate location to store our route and application filter
| definitions instead of putting them all in the main routes file.
|
*/
15
16
require app_path().'/filters.php';
17
18
// Kendi dosya yapılarımızı oluşturuyoruz.
19
20
21
22
23
24
25
require
require
require
require
require
require
app_path().'/observers.php';
app_path().'/validators.php';
app_path().'/listeners.php';
app_path().'/composers.php';
app_path().'/services.php';
app_path().'/helpers.php';
//
//
//
//
//
//
Model gözlemcileri.
Özel validatorler.
Event dinleyiciler.
View kompozerleri.
Konteyner bağlamaları.
Helper fonksiyonlar.
Bu PHP dosyalarını gerçekten oluşturmayı unutmayın, aksi takdirde uygulamanız hata verecektir.
app_path() helperi /app dizininin dosya yolunu verir. Biz şimdi içerisinde prosedürel kodlarımızı
saklayacağımız bir demet farklı dosyalar oluşturmuş olduk. Böylece routes.php dosyasını tıka basa
doldurup da her şeyi karıştırmayacağız.
Bu dosyaları uygulamanızı organize etmenin en iyi yolu yolu olarak ele almayın. Kendi yapınızı
oluşturmayı unutmayın. Bu sizin yatak odanızdır. Benim değil, sizin sevdiğiniz grupların posterlerini asın.
O nerede olacak?
406
İsterseniz, /app dizinini bozabilirsiniz. Hatta, işte küçük bir sır. Ben kendi uygulamalarımı inşa
ederken, bu app klasörü neredeyse boştur. Sadece yapılandırma kalır.
Geçenlerde yeni bir işe başladığımda, dostum Anthony, ki kitapta onu andığım için şimdi yukarı
aşağı atlıyor ve mutluluk dansı yapıyor veya ondan izin almadığım için Pazartesi günü bana P45
teslim edilecektir, uygulamayı nasıl inşa ettiğimi görünce şaşırmıştı.
Bu, github’da gördüğünüz Laravel uygulama örneklerine benzer bir şey değildir. app dizininde hiçbir
şey yoktur. Routes dosyam /src/routes.php dosyasındadır. Bunun sebebi benden proje ortamı
tanımlamamı istemişti, ben onu kendi tercihlerim için yapmıştım. Açıkçası, eğer büyük bir ekipte
çalışıyorsanız, bütün ekibe mantıklı gelen bir yapıyı bir işbirliği içinde inşa etmeniz gerekecektir.
Kimsenin kafasını karıştırmak istemezsiniz.
Peki benim kendi örneklerim içinde Laravel uygulamaları için bu yapıyı neden kullanmadığımı
merak ediyor olabilirsiniz? Pekala, bir eğitimci olmanın ufak bir açmazı vardır. Bir teknik önerdiğiniz
zaman, insanlar onu bir standart haline çevirme eğilimindedir. Arkadaşlarına ondan bahsederler ve
çok geçmeden o bir problemi çözmenin tek yolu haline gelir.
Ben bunun uygulama yapısı için de böyle olacağından endişelendim. Değilse, onun sizin ya da
ekibiniz için kişisel bir şey olmasını isterdim. Bu sebeple benim örneklerim genellikle Laravel
tarafından önceden tanımlanan konumları kullanmaktadır.
Artık prosedürel kodumuzu inşa etme gücüne sahip olduğumuza göre, sınıflarımızı nereye koyacağımıza bakabiliriz.
Nesne Yönelimli Kod
Laravel uygulamanız Composer tarafından güçlendirilmiştir. Bu, sınıflarınızı inşa ederken büyük
bir özgürlük sağlar. En iyisi Laravel’le birlikte gelen default composer.json dosyasına ve özellikle
bunun autoload kesimine bir göz atalım.
1
2
3
4
5
6
7
8
9
10
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php"
]
},
Şimdi, biz bunların hepsini kitabın composer bölümünde okumuştuk, bu yüzden classmap içindeki
sınıfların ve kayıtlı dizinlerde tutulan tüm sınıfların, bir composer dump komutu çalıştırıldığı her
seferinde dev bir sınıf isimleri eşleştirmesine çevrileceğini biliyorsunuz.
O nerede olacak?
407
Bu, Laravel’in dosyalarımızı nasıl bulduğudur. Bir sorun, Laravel 4 ilk salındığı zaman insanların
Composer kullanmıyor olmaları ve yeni bir sınıf oluşturdukları her seferinde composer dump kulllanmayı çoğu kez unutmalarıdır. Bu, kullanıcılardan gelen “Laravel benim sınıfımın bulunamadığını
söylüyor.” diyen yüzlerce soruya yol açmıştır. Bunlardan tonlarca aldık.
Bu problemi engellemek için, Taylor framework çekirdeği içine ikinci bir yükleme mekanizması
ekledi. İsterseniz app/start/global.php dosyasına bir kez daha bakalım. Bu sefer dosyanın en
üstüne bakıyoruz.
1
<?php
2
3
4
5
6
7
8
9
10
11
12
/*
|-------------------------------------------------------------------------| Register The Laravel Class Loader
|-------------------------------------------------------------------------|
| In addition to using Composer, you may use the Laravel class loader to
| load your controllers and models. This is useful for keeping all of
| your classes in the "global" namespace without Composer updating.
|
*/
13
14
ClassLoader::addDirectories(array(
15
app_path().'/commands',
app_path().'/controllers',
app_path().'/models',
app_path().'/database/seeds',
16
17
18
19
20
21
));
Bu dizinlerde bulunan sınıflar, bir sınıf eklediğiniz her seferinde composer dump çalıştırılmasını gerektirmez. Bu yüzden, eğer sınıflarımız için yeni konumlar eklemek istiyorsak basitçe ya
composer.json dosyasına ya da app/start/global.php dosyasına ekstra dizinler ekleyebiliriz.
Bu iyidir ama ben Composer’in gerçek gücünün sınıflarınızı aduzaylı yaptığınız zaman devreye
girdiğini düşünüyorum. İlerleyelim ve Laravel uygulamamız için composer.json dosyasında yeni
bir otomatik yükleme mekanizması kuralım.
O nerede olacak?
1
2
3
4
5
6
7
8
9
408
"autoload": {
"classmap": [
"app/database/migrations",
"app/database/seeds"
],
"psr-0": {
"Example": "src"
}
},
psr-0 sınıf yüklemesi kullanmak için, öncelikle bir taban aduzayı düşünmeniz gerekecektir. Yu-
karıdaki örnekte ben ‘Example’ aduzayı kullandım ki, sınıflarının ve dizin yapısının proje köküne
göreceli olarak src dizini içinde bulunduğunu işaret ediyor.
Siz taban aduzayı için büyük ihtimalle Example dışında bir şey kullanacaksınız. İnsanlar genellikle
kendi soyadlarını veya şirket isimlerini kullanırlar. Example dışında herhangi bir şey. Lütfen!
Bir kez composer update çalıştırıp kendi yükleme mekanizmamızı kayda geçirdikten sonra, artık
src klasörü içinde, bir Example alt dizini içinde bulunduğu sürece, istediğimiz herhangi bir yapıyı
oluşturmakta özgürüz.
Örneğin, uygulamamız buna benzer görünebilir.
1
2
3
4
5
6
7
8
src/Example/Controllers/UsersController.php
src/Example/Controllers/BooksController.php
src/Example/Models/User.php
src/Example/Models/Book.php
src/Example/Validators/UsersValidator.php
src/Example/Validators/BooksValidator.php
src/Example/Observers/BooksObserver.php
src/Example/SomethingDoer/Other/DoSomething.php
Görüyorsunuz değil mi? Kendi dizin yapımızı oluşturabiliyoruz. Eğer Controllers dizininin isminden
hoşlanmıyorsanız, ona başka bir isim vermekte serbestsiniz. Laravel ve Composer, sınıflarınızı
nereye koyacağınız konusunda bir kurala sahip değildir. Bu sizin yatak odanızdır.
Sadece şunu unutmayın, klasör yapınız ile Sınıf ismi/aduzayı aynı olmalıdır. Örneğin, yukarıdaki
örnekte, src/Example/Validators/UsersValidator.php dosyamızın içereceği şey şudur.
O nerede olacak?
1
409
<?php
2
3
namespace Example\Validators;
4
5
6
7
8
class UsersValidator
{
// Burada kodunuz yer alır...
}
Laravel’in “kayda geçirme (registration)” türündeki metodlarından herhangi birinde aduzaylı sınıflar kullanılırken, sınıf ismi yanında tam aduzayını sağladığınızdan emin olun. Örneğin, aduzaylı bir
dosyaya nasıl rota yapılacağı şu şekildedir.
1
Route::get('/', 'Example\Controllers\UsersController@showUsers');
Aduzaylı sınıfları kullanırken düşebileceğiniz başka bir ‘hata’ vardır. Ben bunu sıklıkla unutuyorum!
Ne kadar aptalım. Sınıfınız aduzaylı olduğu zaman, PHP o sınıf içinde kullandığınız tüm sınıfların
aynı aduzayında yer alacağını varsayar. Hızlı bir örnekle bakabiliriz.
1
<?php
2
3
namespace Example\Controllers;
4
5
6
7
8
9
10
11
class UsersController extends Controller
{
public function showUsers()
{
return 'all the users! \o/';
}
}
Burada Laravel’in taban Controller sınıfını genişleten aduzaylı UsersController sınıfımız var.
Doğru? Doğru mu!?
Yanlış.
Example\Controllers dizininde bulunduğumuz için, PHP bizim Example\Controllers\Controller
dosyasını genişletiyor olduğumuza hükmedecektir. Kök aduzayındaki Controller‘i kullanma niye-
timizi açıkça ifade etmemiz gerekir.
410
O nerede olacak?
1
<?php
2
3
namespace Example\Controllers;
4
5
use Controller;
6
7
8
9
10
11
12
13
class UsersController extends Controller
{
public function showUsers()
{
return 'all the users! \o/';
}
}
Şimdi controllerimiz beklendiği gibi fonksiyon görecektir.
Bu çalışıyor olmasına karşın ben biraz mızmızım. Anlatabilir misin? Pekala, alias use etmek yerine
ben gerçek sınıfı use etmek isterim. Ne? Siz \Controller‘in bir alias olduğunu bilmiyor musunuz?
Gidin ve app/config/app.php dosyasına bakın.
1
'aliases' => array(
2
'App'
'Artisan'
'Auth'
'Blade'
'Controller'
3
4
5
6
7
=>
=>
=>
=>
=>
'Illuminate\Support\Facades\App',
'Illuminate\Support\Facades\Artisan',
'Illuminate\Support\Facades\Auth',
'Illuminate\Support\Facades\Blade',
'Illuminate\Routing\Controller',
8
.. diğer birçok alias ..
9
10
'Seeder'
'Session'
'SSH'
'Str'
'URL'
'Validator'
'View'
11
12
13
14
15
16
17
=>
=>
=>
=>
=>
=>
=>
'Illuminate\Database\Seeder',
'Illuminate\Support\Facades\Session',
'Illuminate\Support\Facades\SSH',
'Illuminate\Support\Str',
'Illuminate\Support\Facades\URL',
'Illuminate\Support\Facades\Validator',
'Illuminate\Support\Facades\View',
18
19
),
Hey, bakın, bakın! Bir deste alias. Siz Laravel’in kendi sınıflarının hepsini kök aduzayında yığdığını
düşünmediniz, değil mi? Aliases dizisinde bu aliasların temsil ettiği gerçek sınıfları görebiliriz.
O nerede olacak?
411
Ben kendi aduzaylı sınıflarımı yazarken, gerçek sınıfları use etmeyi seviyorum. Bunun kodu daha
tanımlayıcı yaptığını ve kullanabileceğiniz paketlerin açıklama odaklı dokümantasyon üretimini
iyileştirdiğini düşünüyorum.
İşte başka bir örnek. İlerleyin. Bölümün sonunda özgürlük var!
1
<?php
2
3
namespace Example\Controllers;
4
5
6
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Input;
7
8
9
10
11
12
13
14
class UsersController extends Controller
{
public function showUsers()
{
return Input::get('name');
}
}
Tamam, ona sahibiz artık. Kendi uygulamanızı standart dışı bir formatta inşa etmek için çeşitli yollar
keşfettiniz. Lütfen bu bölüm içindeki örnekleri kelimesi kelimesine kopyalamayın. Deneyin ve kendi
proje yapılarınızı inşa etmeye çalışın. O sizin yatak odanızdır.
Bu yüzden, bir daha aklınıza şu soru geldiği zaman…
xxx’i nereye koyacağım?
xxx şeyleri indirmemelisin, seni pis sapık! Sadece şaka yapıyorum.
Cevap her zaman şu olacak… istediğiniz yere!
Pek Yakında
Hey, kitabımın gerisi nerede?
Kitabın tanımlama sayfasında açıkça yazdığım gibi, bu başlık devam ederken yayınlanmaktadır. Bu
sayfayı gördüğünüze göre, kitap henüz bitmemiş demektir.
Endişelenmeyin, bu başlık için büyük planlarım var. Bu kitabın framework için tam bir bilgi
kaynağı olduğunu görene kadar bölümler yazmaya ve düzeltmeler ve güncellemeler eklemeye
devam edeceğim. Bu başlığı satın almış olanlar için gelecekteki bölümler ve güncellemelerin hepsi
ücretsizdir. Her güncelleme sonrası Leanpub’tan bir eposta alacaksınız. Bu kadar basit işte!
Yazmamı desteklediğiniz için sizlerden her birine ve hepinize burada bir kez daha teşekkür etmek
istiyorum. Her iki başlığı yazarken gerçekten çok zevk aldım ve gelecekte daha çok yazma niyetindeyim. Siz dostlarım kitaplarımı alarak ve geri bildirimlerinizi elektronik yolla postalayarak beni
desteklemeseydiniz, büyük bir ihtimalle ben bu harika hobimi (işimi?) bulamayacaktım.
Eğer Code Happy ve Code Bright size bir şekilde yardımcı olduysa, kitabın URL’sini arkadaşlarınızla
paylaşırsanız bundan büyük bir memnuniyet duyacağım. Eğer kaybettiyseniz o şurada duruyor:
http://leanpub.com/codebright-tr³⁴. :)
Şu anda bu başlık için genel planım aşağıdaki gibidir.
• Temel framework kavramlarını ve bileşenlerini ayrıntılarıyla açıkla.
• Öğrendiklerimiz üzerine inşa edilmiş basit bir uygulama için bir ‘bir-uygulama-yapalım’
bölümü yaz.
• Daha ileri framework özelliklerini ortaya koy.
• Daha ileri özelliklere dayalı birkaç ‘bir-uygulama-yapalım’ bölümü.
• En uygun kullanımlar ve zekice püf noktaları.
Ayrıca, başlıktan aldığım geri bildirimlere dayalı olarak bir SSS bölümü de düşünüyorum, bu
nedenle, eğer gelecekteki bir özellik bölümünde açıklanacağını düşünmediğiniz çarpıcı bir sorunuz
varsa, lütfen bana bildirin.
Üzerinde konuşmak istediğiniz bir şey varsa, benimle [email protected]³⁵ veya daylerees on Twitter³⁶ üzerinden temesa geçebilirsiniz. Beni çoğu kez Freenode IRC’deki #laravelde de bulabilirsiniz
ancak hemen cevap veremezsem lütfen darılmayın, bir gündüz işim de var!
³⁴http://leanpub.com/codebright-tr
³⁵mailto:[email protected]
³⁶http://twitter.com/daylerees
Pek Yakında
Code Bright’ın bir parçası olduğunuz için tekrar teşekkürler.
Sevgiler,
Dayle ve onun sadık kırmızı pandalar ordusu.
xxx
413

Benzer belgeler