+ 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: sinan@sinaneldem.com.tr ¹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 me@daylerees.com⁵ adresine göndermek suretiyle destek verebilirsiniz. Hatalar keşfedildikçe düzeltilecektir. Düzeltmeler kitabın sonraki baskılarında yer alacaktır. ⁵mailto:me@daylerees.com Geribildirim Aynı şekilde, kitap içeriği konusunda veya başka nedenle me@daylerees.com⁶ 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:me@daylerees.com Çeviriler Code Bright’ı kendi dilinize çevirmek isterseniz, lütfen planlarınızla birlikte me@daylerees.com⁷ 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:me@daylerees.com 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", "stan@marvel.com", "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", "stan@marvel.com", "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": "stan@marvel.com", 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", "stan@marvel.com", "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", "stan@marvel.com", "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": "stan@marvel.com", "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><script>alert("BİR KANGAL SUCUK!");</script></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', 'me@daylerees.com') }} {{ 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 ="me@daylerees.com" 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 <taylorotwell@gmail.com> */ 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 me@daylerees.com³⁵ 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:me@daylerees.com ³⁶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