# readonly value object'lerimde "withX" tarzı kopyalama metotlarını nasıl temiz yazarım?

> `readonly`'de withX yeni instance döndürür: taşınabilir yol `new self` + named arguments, PHP 8.5'te doğrulama basitse `clone($this, [...])`.

- Soruldu: 2026-08-25
- Yanıtlandı: 2026-08-28
- Soran: Kaan
- Etiketler: php, oop
- Kaynak: https://www.muhammetsafak.com.tr/sor-bakalim/readonly-value-objectlerimde-withx-tarzi-kopyalama-metotlarini-nasil-temiz-yazarim/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
**Soru:** Domain katmanımda para (Money) ve adres (Address) gibi value object'lerim var ve bunları PHP 8.2'nin `readonly` özelliğiyle immutable yaptım. Amacım nesne bir kez kurulduktan sonra hiç değişmemesi.

Ama immutable kopyalar üretmeye çalışırken sürekli "Cannot modify readonly property" hatası alıyorum. Örneğin bir `withAmount()` metodunda property'yi güncellemeye çalışıyorum ve patlıyor. Bu "withX" tarzı kopyalama metotlarını temiz ve kendini tekrar etmeden nasıl yazarım?


Kısa cevap: `readonly`'de mutasyon yoktur; kopyalarken property'yi değiştirmeye çalışmak yerine yeni bir instance dönmelisiniz. Aldığınız hata tam da bunu söylüyor — `$this->amount = ...` yasaktır, çünkü readonly property yalnızca kurulduğu scope'ta bir kez set edilebilir.

1. **Kök neden.** readonly property'yi ilk atamadan sonra clone içinde bile (sıradan bir metotta) yeniden yazamazsınız; runtime bunu bilinçli olarak engeller. Yani withX'in içinde `$this`'i değiştirmek imkânsız. (Küçük bir düzeltme: `readonly` PHP 8.2 değil, **PHP 8.1** ile geldi.)
2. **Taşınabilir ve temiz desen: `new self` + named arguments.** Her withX, constructor'ı yeni değerlerle çağırır; böylece tüm invariant'lar tekrar doğrulanır. PHP 8.1'den beri her yerde çalışır:
   ```php
   public function withAmount(int $amount): static
   {
       return new self(amount: $amount, currency: $this->currency);
   }
   ```
3. **Alan çoğaldıkça named arguments tekrarı kırar.** Çok alanlı bir VO'da her withX'te sadece değişeni yazar, gerisini `$this->...` ile taşırsınız; named arguments sıraya bağımlılığı ortadan kaldırır ve okunurluğu korur.
4. **PHP 8.3+: `__clone` yalnızca deep-clone içindir.** 8.3 ile readonly property'leri `__clone` içinde yeniden başlatabilirsiniz, ama bu withX'e dışarıdan yeni bir değer taşımaz; asıl amacı iç içe nesneleri (örneğin bir `DateTimeImmutable`) derin kopyalamaktır. withX'i bununla çözmeye çalışmayın.
5. **PHP 8.5: wither için tam bu var.** 8.5, kopyalarken property override etmenizi sağlar; wither deseni tek satıra iner:
   ```php
   public function withAmount(int $amount): static
   {
       return clone($this, ['amount' => $amount]);
   }
   ```
6. **Doğrulamayı unutmayın.** `new self` her kopyayı constructor'dan geçirdiği için doğrulamayı (negatif tutar, geçersiz currency) bedavaya alırsınız. `clone($this, [...])` ise constructor'ı çağırmaz; invariant kontrolünüz varsa clone yolunda onu elle tetiklemeniz gerekir.

**Sonuç:** Ben olsam PHP 8.2/8.3'te `new self` + named arguments ile giderdim — hem taşınabilir hem de her kopyayı doğrulamadan geçiriyor. 8.5'e geçtiyseniz ve doğrulama basitse `clone($this, [...])` daha zarif; ama invariant'larınız katıysa, o güveni kaybetmemek için ben yine constructor tabanlı yolu tercih ederdim.

## İlgili Yazılar

- [Modern PHP'de value object tasarlamak](/blog/modern-phpde-deger-nesneleri-value-object-tasarlamak/) — Blog
- [PHP 8.1 yolda: enum'lar ve readonly ile daha güvenli modeller](/blog/php-8-1-yolda-enumlar-ve-readonly-ile-daha-guvenli-modeller/) — Blog
- [Integer cent olarak sakladığım para alanları için custom cast mı yoksa accessor/mutator mı kullanmalıyım?](https://www.muhammetsafak.com.tr/sor-bakalim/integer-cent-olarak-sakladigim-para-alanlari-icin-custom-cast-mi-yoksa/) — Sor Bakalım
- [Tip güvenli koleksiyonlar için PHPStan generics mi kullanmalıyım, yoksa her tür için ayrı sınıf mı yazmalıyım?](https://www.muhammetsafak.com.tr/sor-bakalim/tip-guvenli-koleksiyonlar-icin-phpstan-generics-mi-kullanmaliyim-yoksa-her-tur/) — Sor Bakalım
- [PHP durum sabitlerimi backed enum'a taşımak kod tabanıma ne kazandırır, neyi zorlaştırır?](https://www.muhammetsafak.com.tr/sor-bakalim/php-durum-sabitlerimi-backed-enuma-tasimak-kod-tabanima-ne-kazandirir-neyi/) — Sor Bakalım
