# Integer cent olarak sakladığım para alanları için custom cast mı yoksa accessor/mutator mı kullanmalıyım?

> DB'de integer cent, modelde immutable `Money`: eşlemeyi tek bir `CastsAttributes` sınıfına toplayın, currency'yi aynı cast'ta ikinci kolon yapın.

- Soruldu: 2026-07-29
- Yanıtlandı: 2026-08-04
- Soran: Sıla
- Etiketler: laravel, eloquent, php
- Kaynak: https://muhammetsafak.com/tr/sor-bakalim/integer-cent-olarak-sakladigim-para-alanlari-icin-custom-cast-mi-yoksa/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
**Soru:** Tutarları veritabanında integer cent olarak saklıyorum (`price_cents` gibi). Modellerimde bir `Money` value object'i istiyorum ama cent temsili her yere sızmasın — Blade'de, servis katmanında, testlerde sürekli `/100` yapmak istemiyorum.

Laravel'de bunu bağlamak için custom cast (`CastsAttributes`) mı yoksa klasik accessor/mutator mu daha doğru? İleride tutarın yanına bir `currency` kolonu da ekleyebilirim; bu tercih o durumu nasıl etkiler?


Kısa cevap: Custom cast (`CastsAttributes` implement eden bir sınıf) kullanın.

## Kısa cevap

Cast'lar tam olarak value object ↔ kolon eşlemesi için tasarlandı; accessor/mutator daha eski ve tek kolonluk bir VO için daha hantal bir araçtır. `Money` gibi bir değer nesnesini modern PHP'de nasıl tasarladığımı [değer nesneleri yazısında](/tr/blog/modern-phpde-deger-nesneleri-value-object-tasarlamak/) anlatmıştım; buradaki soru o nesneyi Eloquent'e hangi kancadan bağlayacağınız.

## Neden

1. **Custom cast bu iş için biçilmiş kaftandır.** `get()`/`set()` içeren tek bir cast sınıfı yazar, onu her model ve her kolonda yeniden kullanırsınız. Accessor'lar model başına, attribute başına metotlardır; aynı Money mantığını her modelde tekrar yazmak zorunda kalırsınız.

2. **Çok kolonlu senaryoyu cast temiz çözer.** Tutar ve currency iki ayrı kolonsa, cast `get()`/`set()` içindeki `$attributes` dizisi üzerinden ikisini birden okuyup yazabilir. `set()`, tek attribute yerine `['amount_cents' => ..., 'currency' => ...]` gibi bir dizi döndürebilir. Accessor/mutator ile iki kolonu tek objeye map edip geri iki kolona yazmak çirkinleşir. İleride currency ekleyecekseniz bu tek başına cast'ı seçtirir.

3. **Cast query builder'a uygulanmaz.** Cast yalnızca model attribute'larına giriş/çıkışta çalışır; `->where('price', $money)` gibi bir sorgu cast'tan geçmez. Filtrelerde hâlâ ham cent geçmeniz gerekir (`->where('price', $money->cents())`). Bu, insanların en sık takıldığı noktadır ve tercihinizden bağımsız olarak geçerlidir.

## Ne yapmalı

1. **Depolamayı integer cent bırakın.** `set()` içinde saklanacak int cent'i döndürün, `get()` içinde `Money::fromCents($value)` kurun. Model size her zaman bir `Money` verdiği için cent temsili dışarı sızmaz; kod tabanının geri kalanı `/100` görmez. Tek kolonluk çekirdek şöyle:

   ```php
   /** @implements CastsAttributes<Money, Money> */
   final class MoneyCast implements CastsAttributes
   {
       public function get($model, string $key, $value, array $attributes): ?Money
       {
           return $value === null ? null : Money::fromCents((int) $value);
       }

       public function set($model, string $key, $value, array $attributes): ?int
       {
           return $value?->cents();
       }
   }

   // modelde:
   protected function casts(): array
   {
       return ['price' => MoneyCast::class];
   }
   ```

2. **Money'yi immutable tutun ve ona bir `cents()` verin.** VO'yu `readonly` yapın; para aritmetiğini (float'tan kaçınarak) ve formatlamayı orada tutun — Blade'de değil. Eşitlik (`equals`) ve currency uyuşmazlığı kontrolü de VO'nun işidir, cast'ın değil. Sorgularda kullanmak üzere `cents()`'i dışa açın.

3. **Kenar durumları unutmayın.** Nullable kolon için `get()`/`set()` içinde `null`'ı ele alın; cast her hydration'da çalıştığı için ucuz kalsın. Parametreleri (varsayılan currency gibi) `:` ile cast parametresi olarak verebilirsiniz; parametreler cast'ın constructor'ına ulaşır.

**Sonuç:** Ben olsam custom cast + DB'de integer cent + immutable `Money` VO üçlüsünü kurardım; currency'yi de aynı cast içinden ikinci kolon olarak yönetirdim. Accessor/mutator'ı yalnızca tek modele özgü, VO'ya bile değmeyen basit bir hesaplanmış alan için saklardım. Böylece cent temsili modelin arkasında kalır ve geri kalan her katman yalnızca `Money` görür.

## İlgili Yazılar

- [Modern PHP'de value object tasarlamak](/tr/blog/modern-phpde-deger-nesneleri-value-object-tasarlamak/) — Blog
- [Birçok modele bağlanan yorumlar için polymorphic ilişki mi yoksa ayrı tablolar mı; index ve foreign key açısından hangisi daha sağlıklı?](https://muhammetsafak.com/tr/sor-bakalim/bircok-modele-baglanan-yorumlar-polymorphic-iliski-ayri-tablolar/) — Sor Bakalım
- [İşlediğim satırları aynı anda güncellerken chunk yerine chunkById mı kullanmalıyım?](https://muhammetsafak.com/tr/sor-bakalim/isledigim-satirlari-ayni-anda-guncellerken-chunk-yerine-chunkbyid-mi-kullanmaliyim/) — Sor Bakalım
- [Eloquent'te N+1'i erken yakalamak için Model::preventLazyLoading'i yalnızca lokalde mi yoksa production'da da mı açmalıyım?](https://muhammetsafak.com/tr/sor-bakalim/eloquentte-n-1i-erken-yakalamak-icin-model-preventlazyloadingi-yalnizca-lokalde-mi/) — Sor Bakalım
