---
title: 'Nesne–Tablo Bağlaması'
source: 'https://academia.sh/tr/kurslar/mvc-cercevesi/nesne-tablo-baglamasi'
course: 'Model–Görünüm–Denetleyici Çerçevesi'
language: tr
updated: '2026-10-21T05:03:41+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Tablo satırı ile kayıt, tablo ile model arasındaki karşılığı kurar'
    - 'Kaydedilen bir değerin kalışının kalıcı olduğunu dört istekte ölçer'
    - 'Kimliklerin tablodan türediğini ve olmayan kimliğin nil verdiğini gösterir'
    - 'Çekirdek modelin saklanan nesnenin kendisini döndürdüğünü ve birçok eşleyiciden bu yönüyle ayrıldığını ölçer'
---

# Nesne–Tablo Bağlaması

Bir tablo satırını nesneye, bir tabloyu modele bağlayan katmanın ölçümü; kaydedilen değerin başka bir istemcide de okunduğunu ve modelin bu çekirdekteki sınırını gösterir.

Önceki konunun son dersi, derlenmiş bir şablonun herkes için ortak olduğunu ve bir kez derlendikten sonra bayatlayabildiğini ölçtü: şablon bir istemcide derlendi, başka bir istemci aynı derlenmiş şablonu çalıştırdı. Şablon çizilirken bir ürünü gösterir. O ürünün nereden geldiği ise henüz sorulmadı. Bu konu ona ayrılmıştır: bir kullanıcı formdan ürünü kaydetti; ürünü başka bir tarayıcıdaki başka bir kullanıcı okuyor mu?

Kursun ekseni aynıdır: **bu değer hangi isteğe kadar kalıyor?** Dört kalış vardır. Değer yalnız yazıldığı istekte duruyorsa kalışı **bu istek**, aynı istemcinin yalnız bir sonraki isteğinde duruyorsa **sonraki istek**, aynı istemcinin bütün sonraki isteklerinde ama başka istemcide yoksa **oturum**, başka bir istemcide de okunuyorsa **kalıcı**dır. Bu dersin gözlemi bir ürün kaydıdır ve beklenen kalış kalıcıdır; ama kalışı bir etiket söylemez, ölçüm söyler.

Dersin kavramı **nesne–tablo bağlaması** (object-relational mapping) ve onu kuran yazılım parçası olan **eşleyici**dir. Veritabanında veri tablolarda, tablolar satırlarda durur; programda veri nesnelerde durur. Eşleyici iki dünyayı birbirine bağlar: bir tablo bir **modele**, tablonun bir satırı o modelin bir **kaydına** karşılık gelir. Aynı kavram M12/K04'te PHP ile kurulmuştu. Orada ölçülen şey "bu parça bu istekte koştu mu" sorusuydu; burada kavram aynı, ölçü başkadır: kaydın kendisi değil, kaydın hangi isteğe kadar kaldığı sorulur.

## Ölçüm Çekirdeği

Çekirdek bir çerçeve değil, bir çerçevenin modelidir; sandbox'ta çerçeve kütüphanesi yoktur ve ağ kapalıdır. İlk dosya, kursun her dersinde birebir aynı olan küçük istek döngüsü ve kalışı sınıflandıran yardımcıdır. Bu blok kaynaktır; dosya diske yazılır ve `require_relative` ile çekilir.

```ruby
# kalis.rb — kucuk bir istek dongusu ve bir degerin hangi istege kadar kaldigini siniflandiran yardimci

# Flash: bu istekte yazilan deger yalniz bir sonraki istege tasinir.
class Flash
  def initialize(gelen)
    @gelen = gelen
    @yeni = {}
  end

  def [](anahtar) = @yeni.fetch(anahtar) { @gelen[anahtar] }

  def []=(anahtar, deger)
    @yeni[anahtar] = deger
  end

  def birakilan = @yeni
end

# Her istek yeni bir baglam alir: params, flash ve istek deposu her istekte bostur.
class Baglam
  attr_reader :uygulama, :params, :oturum, :flash, :istek_deposu

  def initialize(uygulama, oturum)
    @uygulama = uygulama
    @params = {}
    @oturum = oturum
    @flash = Flash.new(oturum.delete(:flash) || {})
    @istek_deposu = {}
  end
end

# Uygulamanin deposu butun isteklerde ve butun istemcilerde aynidir;
# oturum istemciye gore secilir.
class Uygulama
  attr_reader :depo

  def initialize
    @oturumlar = Hash.new { |h, istemci| h[istemci] = {} }
    @depo = { urunler: {}, sema: { urunler: %i[ad fiyat] }, sablonlar: {},
              kuyruk: [], onbellek: {}, sayac: 0 }
  end

  def istek(istemci)
    baglam = Baglam.new(self, @oturumlar[istemci])
    sonuc = yield baglam
    baglam.oturum[:flash] = baglam.flash.birakilan unless baglam.flash.birakilan.empty?
    sonuc
  end
end

# Kural: istek 1'de (istemci A) deger yazilir; istek 2 ve 3 (A) ile istek 4 (B) okur.
# B goruyorsa kalici; A'nin ucuncu istegi goruyorsa oturum; yalniz ikinci istek
# goruyorsa sonraki; hicbiri gormuyorsa bu_istek.
def kalis(yaz, oku)
  uygulama = Uygulama.new
  ilk = uygulama.istek("A") { |b| yaz.call(b); oku.call(b) }
  raise ArgumentError, "deger yazildigi istekte okunamadi" if ilk.nil?

  gorulen = [["A", 2], ["A", 3], ["B", 4]].map do |istemci, sira|
    uygulama.istek(istemci) { |b| oku.call(b) }.nil? ? "--" : "#{istemci}#{sira}"
  end
  tur = if gorulen[2] != "--" then "kalici"
        elsif gorulen[1] != "--" then "oturum"
        elsif gorulen[0] != "--" then "sonraki"
        else "bu_istek"
        end
  [tur, ["A1", *gorulen].join(" "), ilk]
end
```

`Uygulama` bütün isteklerde ve bütün istemcilerde aynı `depo`yu taşır; oturum ise istemciye göre seçilir. Her istek yeni bir `Baglam` alır ve `istek_deposu` her istekte boştur. `kalis` yazan ve okuyan iki lambda'yı alır: birinci istekte A istemcisi ikisini de çalıştırır, sonra A'nın iki isteği ve B'nin bir isteği yalnız okur. Değeri B görüyorsa kalış kalıcıdır; B görmüyor ama A'nın üçüncü isteği görüyorsa oturum; yalnız ikinci istek görüyorsa sonraki; hiçbiri görmüyorsa bu istektir. Kural değerin davranışını okur, saklandığı yeri okumaz.

Bu dersin dosyası modeli verir. Kesit, çekirdekten dersin ihtiyaç duyduğu kadarıdır: `Urun` modeli.

```ruby
# baglama.rb — urun modeli: tablo bir modele, satir bir kayda karsilik gelir
require_relative "kalis"

module Urun
  def self.olustur(b, nitelikler)
    hatalar = []
    hatalar << "ad bos olamaz" if nitelikler[:ad].to_s.empty?
    hatalar << "fiyat negatif olamaz" if nitelikler[:fiyat].to_i.negative?
    return { hatalar: } unless hatalar.empty?

    tablo = b.uygulama.depo[:urunler]
    id = tablo.size + 1
    tablo[id] = nitelikler.merge(id:)
    { id:, hatalar: }
  end

  # Ayni istekte ayni sorgu ikinci kez veritabanina gitmez.
  def self.bul(b, id)
    sorgular = (b.istek_deposu[:sorgular] ||= {})
    sorgular.fetch([:urunler, id]) do
      b.istek_deposu[:sorgu_sayisi] = b.istek_deposu.fetch(:sorgu_sayisi, 0) + 1
      sorgular[[:urunler, id]] = b.uygulama.depo[:urunler][id]
    end
  end
end
```

Bu blok da kaynaktır. `olustur` bir kayıt yazar: tablo `depo[:urunler]` karma yapısıdır, anahtarı kimlik, değeri satırdır. Kimlik tablonun boyundan türer — ilk kayıt `1`, ikincisi `2` — ve satır, verilen niteliklere kimliğin eklenmesiyle oluşur. Doğrulama satırları şimdilik yalnız hata listesini doldurur; üçüncü ders onlara döner. `bul` bir kimlikle satırı okur; sorgu önbelleği dördüncü dersin konusudur, burada yalnız `depo[:urunler][id]` okuması önemlidir.

## Kaydın Kalışı

Kaydedilen ürünü başka bir tarayıcı okuyor mu? Yazan lambda bir `kalem` kaydeder, okuyan lambda tablodan birinci satırın adını alır.

```ruby
# olcum.rb — kayit gozleminin kalisi
require_relative "baglama"

kalem = ->(b) { Urun.olustur(b, ad: "kalem", fiyat: 12) }
oku = ->(b) { b.uygulama.depo[:urunler][1]&.fetch(:ad) }
p kalis(kalem, oku)
```
```
["kalici", "A1 A2 A3 B4", "kalem"]
```

Üç alan okunur. Kalış **kalıcı**: kural B'nin dördüncü isteğinde de değeri gördü. Kanıt satırı `A1 A2 A3 B4` dört isteğin hepsinin `kalem`i okuduğunu söyler; hiçbirinde `--` yok. Son alan, birinci istekte okunan değerdir. Önceki konunun sonunda sorulan sorunun cevabı budur: kaydedilen ürünü başka tarayıcı okuyor. **VK1.** Bu ölçüm tek süreç içindir. Çekirdeğin deposu bellektedir; ölçüm, süreç kapanıp yeniden açıldığında verinin kalıp kalmadığını kapsamaz.

Kalıcılığın nedeni `depo`nun nerede durduğudur. Flash ve istek deposu `Baglam` ile birlikte her istekte yeniden doğar; oturum istemciye bağlıdır; `depo` ise `Uygulama` nesnesindedir ve uygulama bütün istekleri karşılar. Model, değeri en uzun yaşayan yere yazdığı için kalış kalıcı çıktı.

Kalıcılığın bir sonucu daha vardır: tablo istemciler arasında ortaktır, bu yüzden iki istemcinin kayıtları aynı kimlik dizisine girer. A bir ürün oluşturur, B başka bir ürün oluşturur, A tabloyu okur.

```ruby
# iki_istemci.rb — iki istemci, tek tablo
require_relative "baglama"

uygulama = Uygulama.new
uygulama.istek("A") { |b| Urun.olustur(b, ad: "kalem", fiyat: 12) }
uygulama.istek("B") { |b| Urun.olustur(b, ad: "defter", fiyat: 30) }
uygulama.istek("A") do |b|
  tablo = b.uygulama.depo[:urunler]
  p tablo.keys
  p tablo.values.map { |satir| satir[:ad] }
end
```
```
[1, 2]
["kalem", "defter"]
```

A'nın üçüncü isteği iki kaydı da gördü; B'nin kaydı `2` kimliğini aldı, çünkü kimlik tablonun boyundan türer ve tablo A'nın kaydıyla birlikte tek parçadır. Kalıcı bir değer yalnız yazan istemciye ait olmaz: bir istemcinin yazdığı, bir başkasının kimliğini belirler. **VK5.** İki istek burada art arda koşar; aynı anda iki istek geldiğinde kimliklerin çakışıp çakışmayacağı bu çekirdekte ölçülmemiştir ve bu ders bunu iddia etmez.

## Modelin Üstlendiği İş

Eşleyici olmasaydı her denetleyici tabloya kendi yolunu bulmak zorunda kalırdı: satırı hangi anahtarla yazacağını, kimliği nasıl üreteceğini, okuduğu satırın hangi alanlara sahip olduğunu bilirdi. Model bu bilgiyi tek yerde toplar. `olustur` kimliği üretir ve satırı yazar; `bul` kimlikle satırı okur; çağıran, tablonun karma yapısının nasıl kurulduğunu bilmeden iki yöntemle çalışır. Bu, MVC'nin (Model–Görünüm–Denetleyici, Model–View–Controller) model katmanıdır: denetleyici isteği yorumlar, görünüm sayfayı çizer, model ise verinin nerede ve hangi biçimde durduğunu bilen tek katmandır.

Bu ayrımın kazancı, değişikliğin tek yerde yapılmasıdır. Satırın biçimi değişirse yalnız `olustur` ve `bul` güncellenir; onları çağıran kod aynı kalır. Bu derste ölçülen kalıcılık da modelin bir sonucudur: kaydı hangi yöntemin yazdığı bilindiği için kaydın nereye yazıldığı da bilinir.

## Tablo, Satır ve Kimlik

Aynı modelin içine bir istek boyunca bakılır: iki kayıt oluşturulur, tablonun anahtarları ve ikinci satır yazdırılır, olmayan bir kimlik sorulur.

```ruby
# satir.rb — tablo, satir ve kimlik
require_relative "baglama"

uygulama = Uygulama.new
uygulama.istek("A") do |b|
  ilk = Urun.olustur(b, ad: "kalem", fiyat: 12)
  ikinci = Urun.olustur(b, ad: "defter", fiyat: 30)
  p [ilk[:id], ikinci[:id]]
  p b.uygulama.depo[:urunler].keys
  p b.uygulama.depo[:urunler][2]
  p Urun.bul(b, 99)
end
```
```
[1, 2]
[1, 2]
{ad: "defter", fiyat: 30, id: 2}
nil
```

İki `olustur` çağrısı `[1, 2]` kimliklerini verdi ve tablonun anahtarları aynı listedir: kimlik, satırın tablodaki adresidir. İkinci satır, verilen iki nitelik ile kimliğin birleşimidir. Olmayan kimlik hata fırlatmadı, `nil` verdi; modelin "bulunamadı" cevabı bir istisna değil, bir değerdir. Bu yüzden `bul`u çağıran kod `nil` ihtimalini kendisi ele alır: modelin dilinde "yok", bir istisna ile değil, `nil` ile söylenir. **VK2.** Kimlikler tablonun boyundan türediği için bu ölçüm hiç silme yapılmadığı varsayımıyla geçerlidir; silme bu çekirdekte yoktur ve ölçülmemiştir.

## Girdi ile Satır

`olustur` satırı, çağıranın verdiği karma yapıdan değil, `merge` ile kurulan yeni bir karma yapıdan oluşturur. Bu yüzden çağıranın elindeki nitelikler ile tablodaki satır aynı kalışı paylaşmaz. Ölçüm nitelikleri bir yerel değişkene koyar, kaydı oluşturur, sonra değişkeni değiştirir.

```ruby
# girdi.rb — cagiranin karmasi ile tablodaki satir
require_relative "baglama"

uygulama = Uygulama.new
uygulama.istek("A") do |b|
  nitelikler = { ad: "kalem", fiyat: 12 }
  Urun.olustur(b, nitelikler)
  nitelikler[:ad] = "degisti"
  p nitelikler.key?(:id)
  p b.uygulama.depo[:urunler][1][:ad]
  p nitelikler.equal?(b.uygulama.depo[:urunler][1])
end

yaz = lambda do |b|
  nitelikler = { ad: "kalem", fiyat: 12 }
  Urun.olustur(b, nitelikler)
  nitelikler[:ad] = "degisti"
end
oku = ->(b) { b.uygulama.depo[:urunler][1]&.fetch(:ad) }
p kalis(yaz, oku)
```
```
false
"kalem"
false
["kalici", "A1 A2 A3 B4", "kalem"]
```

Çağıranın karma yapısı `id` anahtarını hiç almadı, tablodaki satır ise `"kalem"` olarak kaldı ve iki nesne aynı nesne değil. Değiştirilen yerel değişken isteğin içindedir ve istekle birlikte gider; tablodaki satır kalıcıdır. Kalış kuralı da bunu doğrular: yazan lambda kayıttan sonra yerel karma yapıyı değiştirse bile B'nin dördüncü isteği `"kalem"` okur. Çekirdek modelde kopya yazarken alınır, okurken alınmaz: sonraki bölümde `bul` tablodaki nesnenin kendisini verir. Bu asimetri modelin bir özelliğidir; gerçek bir eşleyicinin iki yönde ne yaptığı burada ölçülmemiştir.

## Sınırlayıcı Ölçüm

Model gerçek bir eşleyicinin tamamı değildir ve sınırı bir yerde görünür. `bul`, tablodaki satırı mı, onun bir kopyasını mı döndürüyor? İki nesnenin aynı nesne olup olmadığı `equal?` ile sorulur. Sonra okunan kayıt, kaydetmeden değiştirilir.

```ruby
# sinir.rb — model saklanan nesnenin kendisini dondurur
require_relative "baglama"

uygulama = Uygulama.new
uygulama.istek("A") do |b|
  Urun.olustur(b, ad: "kalem", fiyat: 12)
  okunan = Urun.bul(b, 1)
  p okunan.equal?(b.uygulama.depo[:urunler][1])
  okunan[:ad] = "degisti"
  p b.uygulama.depo[:urunler][1][:ad]
end
```
```
true
"degisti"
```

`bul` tablodaki nesnenin kendisini döndürdü: `equal?` `true` verdi. Okunan kayıt değiştirilince tablo da değişti; ortada bir kaydetme çağrısı yoktur. Birçok eşleyicide durum böyle değildir: her okuma, satırdan yeni bir nesne kurar ve nesne üzerindeki değişiklik ancak açık bir kaydetmeyle satıra yazılır. Bu bir iddia olarak değil, çekirdek modelin sınırı olarak okunmalıdır. Aradaki farkı küçük bir yardımcı görünür kılar; yardımcı bu dersin kendi kodudur ve böyle bir eşleyicinin okuma biçimini taklit eder.

```ruby
# kopya.rb — gercek esleyicinin okuma bicimini taklit eden yardimci
require_relative "baglama"

def taze_oku(b, id) = b.uygulama.depo[:urunler][id]&.dup

def kaydet(b, kayit)
  b.uygulama.depo[:urunler][kayit[:id]] = kayit.dup
end

uygulama = Uygulama.new
uygulama.istek("A") do |b|
  Urun.olustur(b, ad: "kalem", fiyat: 12)
  kopya = taze_oku(b, 1)
  kopya[:ad] = "degisti"
  p b.uygulama.depo[:urunler][1][:ad]
  kaydet(b, kopya)
  p b.uygulama.depo[:urunler][1][:ad]
  p kopya.equal?(b.uygulama.depo[:urunler][1])
end
```
```
"kalem"
"degisti"
false
```

Kopya değiştirildiğinde tablo `"kalem"` olarak kaldı; değişiklik ancak `kaydet` çağrısıyla tabloya geçti. Son satır, tablodaki nesnenin okunan kopyayla aynı nesne olmadığını doğrular: `false`. Kalıcılık bu yardımcıda değişikliğin kaydedilmesine bağlıdır. **VK3.** Bu yardımcı bir kopya alıp kaydı geri yazar; gerçek bir eşleyicinin ne yaptığını bütünüyle temsil etmez, yalnız kaydetmeden önce ve sonra tablonun ne gördüğünü ayırır.

Son olarak aynı iki davranış kalış kuralının önüne konur: kaydı oluşturan ama ardından yalnız bellekteki nesneyi değiştiren yazan lambda.

```ruby
# ayni_nesne.rb — kaydetmeden degistirilen kayit
require_relative "baglama"

yaz = lambda do |b|
  Urun.olustur(b, ad: "kalem", fiyat: 12)
  Urun.bul(b, 1)[:ad] = "degisti"
end
oku = ->(b) { b.uygulama.depo[:urunler][1]&.fetch(:ad) }
p kalis(yaz, oku)
```
```
["kalici", "A1 A2 A3 B4", "degisti"]
```

Kalış yine kalıcı ve değer `"degisti"`. Çekirdek modelde okunan nesne ile tablodaki nesne aynı olduğu için kaydetme adımı yoktur: kayda yapılan her dokunuş, doğrudan başka istemcinin de okuduğu yere yazılır. Bu yüzden çekirdekte kalışı kalıcı olan şey kaydın oluşturulması kadar, kayıt nesnesine yapılan her değişikliktir. Bu, modelin bir sadeleştirmesidir ve gerçek eşleyicinin davranışı olarak okunmamalıdır.

## Kaydı Tutmak ile Kaydetmek

Karşıt ölçüm, aynı bilginin tabloya yazılmadan yalnız istekteki bir değişkende tutulmasıdır. Yazan lambda ürünün adını `istek_deposu`na koyar; tabloya hiçbir şey yazmaz.

```ruby
# tutulan.rb — tabloya yazilmayan ayni bilgi
require_relative "baglama"

yaz = ->(b) { b.istek_deposu[:urun] = { ad: "kalem", fiyat: 12 } }
oku = ->(b) { b.istek_deposu[:urun]&.fetch(:ad) }
p kalis(yaz, oku)
```
```
["bu_istek", "A1 -- -- --", "kalem"]
```

Değer aynı, `"kalem"`; kalış değişti. Kanıt satırı `A1 -- -- --` yalnız birinci istekte okunduğunu gösterir ve kural **bu istek** der. İki ölçümdeki fark verinin içeriğinde değil, nereye yazıldığındadır: `olustur` değeri tabloya yazdı, bu lambda bir istek değişkenine. Nesne–tablo bağlaması tam bu farkı kurar. Modelin yazdığı şey, istek bittiğinde silinmeyen yerdir. Bir denetleyicinin yerel değişkenindeki ürün adı ise istekle birlikte kaybolur. **VK4.** Bu karşıt ölçüm değişkeni `istek_deposu`nda tutar; ölçülen, bu çekirdekte tabloya yazılmayan bir değerin hiçbir isteğin ötesine geçmediğidir, başka bir saklama yerinin davranışı değildir.

## Özet

- Nesne–tablo bağlaması, bir tabloyu bir modele, tablonun bir satırını o modelin bir kaydına bağlar; bu bağı kuran parçaya eşleyici denir.
- Bu dersin gözlemi olan kayıt, kalışı kalıcı olan değerdir: ilk istekte kaydedilen ürün A'nın sonraki iki isteğinde ve B'nin dördüncü isteğinde okundu.
- Kalıcılığın nedeni `depo`nun uygulamada durmasıdır; istek deposu, flash ve oturum bu kalışı vermez.
- Kimlikler tablodan türer ve olmayan kimlik `nil` verir; modelin cevabı bir istisna değildir.
- Çekirdek model saklanan nesnenin kendisini döndürür; birçok eşleyici her okumada yeni nesne kurar ve değişiklik ancak kaydetmeyle tabloya geçer.
- `olustur`, çağıranın karmasından kopya kurar; sonradan değiştirilen karma satırı etkilemez.

## Sonraki Adım

Kayıt kalıcıdır, ama kayıtların biçimi bir tablo şemasına bağlıdır. Şemaya sonradan bir sütun eklendiğinde bu eski satırlarda var mı? Bir sonraki ders, şemayı değiştiren göçleri aynı ölçüyle okur.
