Blade şablon motoru: tekrar eden HTML'i bitirmek
Görünüm katmanını mantıktan ayırmanın pratik kazancı: Blade layout, section ve partial kullanımı.
Laravel’de çalışmaya devam ediyorum. Bu ay görünüm katmanına odaklandım. Daha önce PHP şablonlarında yaptığım şey şuydu: her sayfada header.php ve footer.php dahil etmek. Küçük projelerde idare eder ama büyüdükçe hangi sayfanın neyi dahil ettiğini takip etmek güçleşiyor.
Blade, Laravel’in şablon motorudur. Saf PHP şablonlarından farklı olarak template inheritance ve partial’lar için temiz bir sözdizimi sunar.
Neden şablon motoru?
Ham PHP şablonlarında HTML ile PHP kodu iç içe giriyor. Uzun bir sayfada <?php foreach(...): ?> ile <?php endforeach; ?> arasında kaybolmak kolay. Blade bunu @foreach ve @endforeach gibi direktiflerle daha okunabilir yapıyor. Ayrıca çıktıyı otomatik olarak XSS’e (Siteler Arası Betik Çalıştırma — Cross-Site Scripting) karşı kaçıyor.
Eski yöntemde <?= $kullanici->ad ?> yazdığımda XSS koruması yoktu; kullanıcıdan gelen veriyi ekrana basıyordum. Blade’in {{ $kullanici->ad }} sözdizimi otomatik olarak htmlspecialchars() çağırıyor — bu güvenliği varsayılan hale getiriyor.
Ana düzen (layout) oluşturmak
Önce temel iskelet olan düzen dosyasını oluşturuyorum: app/views/layouts/ana.blade.php
<!DOCTYPE html>
<html>
<head>
<title>@yield('baslik') | Sitem</title>
<link rel="stylesheet" href="/css/uygulama.css">
</head>
<body>
@include('partials.menu')
<div class="icerik">
@yield('icerik')
</div>
@include('partials.altbilgi')
</body>
</html>
@yield('icerik'), çocuk sayfaların kendi içeriklerini buraya enjekte edebileceği bir yer tutucu tanımlar. @include ile tekrar eden bölümleri ayrı dosyalara taşıyorum.
Çocuk görünüm
app/views/anasayfa.blade.php:
@extends('layouts.ana')
@section('baslik', 'Ana Sayfa')
@section('icerik')
<h1>Hoş geldiniz</h1>
<p>Bu sitenin ana sayfasıdır.</p>
@foreach($haberler as $haber)
<div class="haber">
<h2>{{ $haber->baslik }}</h2>
<p>{{ $haber->ozet }}</p>
</div>
@endforeach
@endsection
@extends('layouts.ana') hangi düzeni kullandığını bildirir. @section ile o düzendeki @yield alanlarını dolduruyorsunuz. {{ $degisken }} sözdizimi HTML özel karakterlerini kaçırarak güvenli çıktı üretir.
Koşullar ve döngüler
Blade direktifleri saf PHP eşdeğerleriyle birebir örtüşüyor:
@if($kullanici->girisYapti())
<a href="/cikis">Çıkış</a>
@else
<a href="/giris">Giriş</a>
@endif
@forelse($yorumlar as $yorum)
<p>{{ $yorum->metin }}</p>
@empty
<p>Henüz yorum yok.</p>
@endforelse
@forelse, liste boşken @empty bloğunu gösterir. Bu kombinasyonu çok sık kullanıyorum; eskiden count($dizi) > 0 kontrolü ayrıca yazardım.
Kısmi görünümler (partials)
Tekrar eden parçaları ayrı dosyalara taşımak düzeni koruyor. Menüyü app/views/partials/menu.blade.php dosyasına koyuyorum:
<nav>
<a href="/">Ana Sayfa</a>
<a href="/hakkimda">Hakkımda</a>
@if(Auth::check())
<a href="/panel">Panel</a>
@endif
</nav>
Her sayfada @include('partials.menu') yazıyorum, gerisini Blade hallediyor.
Denetleyiciden görünüme veri aktarmak
Denetleyicide (controller) değişkeni görünüme aktarmanın birkaç yolu var:
// compact() ile
$haberler = Haber::all();
return View::make('anasayfa', compact('haberler'));
// with() ile zincir
return View::make('anasayfa')->with('haberler', Haber::all());
Her ikisi de aynı sonucu veriyor; compact() birden fazla değişken varken daha pratik.
PHP ifadesi çalıştırmak
Zaman zaman Blade direktifi olmayan PHP’ye ihtiyaç duyulabiliyor. @php ile blok açılabilir:
@php
$toplam = $fiyat * $adet;
@endphp
<p>Toplam: {{ $toplam }} TL</p>
Bunu nadiren kullanıyorum; iş mantığı görünüm katmanına girmemeli. Ama basit hesaplamalar için iş görüyor.
Kaçış gerektiren durum: ham HTML basmak
Blade’in {{ }} sözdizimi her zaman HTML kaçışı yapıyor — bu iyi. Ama veritabanında HTML içerik saklanıyorsa ve onu olduğu gibi basmak gerekiyorsa {!! !!} sözdizimi kullanılıyor:
{!! $makale->icerik !!}
Bunu yalnızca güvendiğiniz kaynaklardan gelen içerik için kullanmak gerekiyor. Kullanıcının girdiği veriyi {!! !!} ile basmak, XSS korumasını tamamen devre dışı bırakır. Bu ayrımı öğrendiğimde bir CMS projesinde görmüştüm: düzenleyiciden gelen HTML makaleler için {!! !!} doğruydu, ama yorum alanları için aynı sözdizimini kullansaydık güvenlik açığı çıkardı.
Önceki yöntemle karşılaştırma
Eski header.php / footer.php yaklaşımında her sayfaya iki include yazıyordum ve her ikisinin de birbirine bağımlı olduğunu aklımda tutuyordum. Blade’in kalıtım modeli bunu tersine çeviriyor: merkezi düzen dosyası var, çocuk sayfalar ona bağlanıyor. Sayfa eklemek çok daha temiz.
Birkaç haftalık kullanımdan sonra şablon motorunun getirdiği düzeni beğendim. Özellikle @forelse ve @include kombinasyonu tekrar eden kodu belirgin şekilde azalttı. Bir de XSS korumasının varsayılan gelmesi, güvenliği her seferinde “hatırlanması gereken bir şey” olmaktan çıkarıyor.
Blade’in bir özelliği daha var: @stack ve @push direktifleriyle sayfaya özgü CSS veya JavaScript bloklarını düzen dosyasındaki belirli bir noktaya enjekte edebiliyorsunuz. Örneğin bir sayfa özel bir JavaScript kütüphanesi gerektiriyorsa, onu o sayfanın görünümünden düzenin </body> etiketinden önce tanımlanmış @stack('scripts') alanına gönderebilirsiniz. Tüm JS’i tek bir bundle’a koymak yerine, sayfaya özgü betiği sadece o sayfa yüklendiğinde dahil etmek bu yolla mümkün oluyor.