Laravel’ın $casts özelliği, çok fazla dikkat çekmeden verileri nasıl işleyeceğinizi şekillendiren o sessiz kolaylıklardan biridir. Diziye boolean veya datetime gibi yerleşik bir tip eklediğinizde, Eloquent, ham veritabanı dizelerini değerler kontrolcünüze (controller) veya görünümlerinize (view) ulaşmadan önce otomatik olarak daha kullanıcı dostu bir hale getirir. Kodunuzu düzenli tutar. Ancak, kendi yardımcı fonksiyonunuzu (helper) doğrudan bu dizinin içinde çağırmaya çalıştığınız anda, framework buna direnir. 'username' => 'encrypt_data()' gibi bir şey yazmak işe yaramayacaktır. Laravel ya yerleşik bir cast anahtar kelimesi ya da CastsAttributes arayüzünü uygulayan bir sınıf bekler. Bu gereksinim, casting (tür dönüştürme) katmanının Eloquent'in hydration ve serialization döngüsünün derinliklerinde çalışmasından kaynaklanır; çalışma zamanında mevcut olmayabilecek rastgele bir fonksiyon dizesi yerine, net bir sözleşmeye sahip, öngörülebilir bir nesneye ihtiyaç duyar.
$casts Dizisinde Yardımcı Fonksiyonlar Neden Başarısız Olur
Eloquent bir modeli yüklediğinde, her sütunu nasıl dönüştüreceğine karar vermek için $casts dizisini tarar. Framework; string, integer, array, encrypted gibi belirli ilkel (primitive) türleri ve CastsAttributes arayüzünü uygulayan tam nitelikli sınıf isimlerini (fully-qualified class names) tanır. Dizgeyi (string) kod olarak değerlendirmez. Bu nedenle 'encrypt_data()' bilinmeyen bir cast türü olarak kabul edilir ve bu da bir hatayı tetikler. Laravel bunu ayrıştırsaydı (parse) bile; model örneğini (instance), öznitelik anahtarını (attribute key), mevcut değeri ve çevreleyen öznitelikleri fonksiyonunuza doğru sırayla aktarmanın güvenilir bir yolu olmayacaktı. Arayüz, tam olarak sizin özel mantığınız ile Eloquent'in iç işleyişi arasındaki bu el sıkışmayı standartlaştırmak için vardır.
Bu boşluğu doldurmak için iki sağlam yolunuz var. Biri uzun vadeli yeniden kullanımı destekler; diğeri ise yalnızca tek bir model içinde hızlı bir yama yapmanız gerektiğinde hızı ön plana çıkarır.
Yöntem 1: Özel Bir Cast Sınıfı Yazın
Eğer aynı dönüşüm birden fazla alan için geçerliyse veya birkaç modele yayılıyorsa, özel bir cast sınıfı kullanmak daha temiz bir yaklaşımdır. Kendi dosyasında yaşar, izole bir şekilde birim testine (unit test) tabi tutulabilir ve modellerinizi tekrarlayan boilerplate kodlardan arındırır.
app/Casts/CustomEncrypt.php dosyasını oluşturarak başlayın. Namespace (ad alanı), genellikle App\Casts olan autoloading kurulumunuzla eşleşmelidir. Sınıf, Illuminate\Contracts\Database\Eloquent\CastsAttributes arayüzünü uygulamalıdır; bu da sizi get ve set olmak üzere iki metot tanımlamaya zorlar.
<?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
class CustomEncrypt implements CastsAttributes
{
public function get(Model $model, string $key, mixed $value, array $attributes): mixed
{
return decrypt_data($value);
}
public function set(Model $model, string $key, mixed $value, array $attributes): mixed
{
return encrypt_data($value);
}
}
Metot imzaları önemlidir. Eloquent, her metoda dört argüman iletir. $model, doldurulan veya kaydedilen örnektir; bu sayede mantığınız diğer alanlara bağlıysa onları inceleyebilirsiniz. $key, o anda dönüştürülen sütun adıdır. $value, bir get işleminde veritabanından gelen ham dizge veya null değeridir ya da bir set işleminde kullanıcı tarafından sağlanan değerdir. $attributes, o satıra ait tüm ham sütun dizisidir. Her seferinde dördünü de kullanmanız gerekmez, ancak arayüz bunları gerektirir.
Yukarıdaki get metodunda, decrypt_data($value) fonksiyonu, Eloquent satırı veritabanından çektikten sonra ve değer modelinize ulaşmadan önce çalışır. set metodunda ise encrypt_data($value), INSERT veya UPDATE işleminden önce çalışarak veritabanının hiçbir zaman düz metni (plaintext) görmemesini sağlar.
Bunu bağlamak için, modelinizin $casts dizisi içinde sınıfa referans verin:
protected $casts = [
'username' => CustomEncrypt::class,
'password' => CustomEncrypt::class,
];
Sınıf sabitini (class constant) kullandığınız için geri kalanını autoloader halleder. Uygulamanızın daha sonra şifreleme şemasını değiştirmesi gerekirse, tek bir dosyayı düzenlemeniz yeterlidir ve eşlenen tüm alanların davranışı anında değişir. Birçok tablo üzerinde hassas verileri yönetirken bu merkeziyetçiliğin yerini doldurmak zordur.
Yöntem 2: Accessor ve Mutator Kullanın
Bazen sadece tek bir yerde önemli olan bir dönüşüm için yeni bir dosya oluşturmak istemezsiniz. Laravel'in Attribute sınıfı, PHP 8+ closure sözdizimini kullanarak doğrudan model üzerinde get ve set mantığı tanımlamanıza olanak tanır.
use Illuminate\Database\Eloquent\Casts\Attribute;
protected function username(): Attribute
{
return Attribute::make(
get: fn ($value) => decrypt_data($value),
set: fn ($value) => encrypt_data($value),
);
}
Burada metot adı, hedeflediğiniz sütun veya öznitelik ile eşleşmelidir. Eloquent veritabanından username değerini okuduğunda, ham değeri get closure'ı üzerinden geçirir. Modelin username özelliğine yeni bir değer atadığınızda, Eloquent sorguyu oluşturmadan önce set closure'ı bu değeri şifreler.
Bu yaklaşım, tam bir cast sınıfları dizininin gereğinden fazla (overkill) göründüğü prototiplerde veya eski (legacy) modellerde parlar. Dezavantajı ise tekrardır. Eğer daha sonra email, phone ve backup_code alanlarının da aynı işlemi gerektirdiğine karar verirseniz, bu closure'ları birden fazla metot veya model arasında kopyalamak zorunda kalırsınız. Bu karmaşa birikir. Ayrıca, tüm modeli başlatmadan (booting) bu mantığı bir birim testinde yeniden kullanmanızı da engeller.
İkisi Arasında Seçim Yapmak
Yeniden kullanılabilirlik, test edilebilirlik ve modelleri yalın tutmak sizin için önemliyse özel bir cast sınıfı kullanın. Bu, diğer geliştiricilere bu dönüşümün uygulamanızda geçici bir çözüm değil, birinci sınıf bir kavram olduğunu gösterir.
Mantık gerçekten yerelleşmişse, deneysel ise veya mevcut sprintten daha uzun süre hayatta kalması pek olası değilse bir accessor kullanın. Bu, kod tabanına küçük dosyalar saçmadan hızlı hareket etmenizi sağlar. Sadece aynı desen ikinci veya üçüncü kez ortaya çıktığında bir cast sınıfına refactor etmeye hazır olun.
Akılda Tutulması Gereken Pratik Detaylar
Özel cast'ler güçlüdür ancak dikkat etmezseniz sizi şaşırtabilecek davranışlar sergileyebilirler. İlk olarak, get metodunun veritabanından dönen her şeyi, null dahil, aldığını unutmayın. Eğer decrypt_data null girdiyi kabul etmiyorsa, buna karşı önlem alın:
public function get(Model $model, string $key, mixed $value, array $attributes): mixed
{
return is_null($value) ? null : decrypt_data($value);
}
İkinci olarak, cast'ler dizi (array) ve JSON serileştirme sırasında çalışır. Bir modeli API kaynağı olarak döndürdüğünüzde veya toArray() metodunu çağırdığınızda, cast get mantığı hala geçerli olur. Bu genellikle istediğiniz şeydir, ancak şifresi çözülmüş değerleri dışarı açıyorsanız ve bunların üzerine ek görünürlük kontrolleri katmanları eklemeniz gerekiyorsa bunu hatırlamakta fayda var.
Üçüncü ve en önemlisi, cast işlemi verinin çekilmesinden sonra gerçekleşir. Dönüştürülmüş değer üzerinden sorgulama yapamazsınız. User::where('username', 'john_doe')->first() gibi bir sorgu, john_doe dizgisini doğrudan veritabanına gönderir. Bu değer asla şifre çözme mantığınızdan geçmez. Eğer sütun veritabanında şifreli (encrypted at rest) olarak tutuluyorsa, şifrelenmiş metnin (ciphertext) kendisi üzerinde arama yapmadığınız sürece bu sorgu hiçbir sonuç bulamayacaktır. Veritabanı erişim modellerinizi buna göre planlayın; çünkü cast'ler, yerel veritabanı fonksiyonlarının veya indekslenmiş düz metin sütunlarının yerini tutmaz.
Özel cast sınıfları aynı zamanda küçük yapılandırma parçaları için de harika yerlerdir. Eğer yapıcı metot (constructor) argümanlarına ihtiyacınız varsa —örneğin bir şifreleme modu veya bir format dizgisi geçmek gibi— Laravel, burada açıklanan temel kurulumun bir adım ötesinde olsa da, 'field' => CustomEncrypt::class . ':arg' gibi bir ifade kullanarak $casts dizisi aracılığıyla bunları destekler.
Asıl Çıkarım
Yardımcı fonksiyonları doğrudan $casts içine yerleştirmeyi engelleyen kısıtlama keyfi bir bürokrasi değildir. Sizi açık, test edilebilir ve yeniden kullanılabilir kod yazmaya yönlendirir. Özel cast sınıfları, dağınık satır içi mantıkları, modeller arasında çoğaltma yapmadan paylaşabileceğiniz güvenilir bileşenlere dönüştürür. Accessor'lar ise ayrı bir dosya oluşturmanın gereksiz bir formalite gibi hissettirdiği durumlarda, hızlı ve yerelleştirilmiş düzeltmeler için kapıyı açık tutar. Her ikisinde de ustalaşın, problemin kapsamına göre seçim yapın ve uygulamanız büyüdükten çok sonra bile Eloquent katmanınız okunabilir kalacaktır.
