---
title: Denetleyiciler
source: 'https://academia.sh/tr/kurslar/web-uygulama-cercevesi/denetleyiciler'
course: 'Web Uygulama Çerçevesi'
language: tr
updated: '2026-10-21T05:03:39+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Rotanın bir denetleyici sınıfına ve eylem yöntemine bağlanmasını ve yapıcı bağımlılığının nesne kabıyla çözülmesini okur'
    - 'Kaynak denetleyicisinin yedi eylemini yedi rotaya açan kuralı ve tek eylemli denetleyiciyi karşılaştırır'
    - 'Eylemin koştu sınıfına düşmesi için denetleyicinin kurulmasının gerektiğini ölçer'
    - 'Tanımsız eylemin rotasının kaydedilmediği durumda isteğin 404 ya da başka bir eyleme düştüğünü ölçer'
---

# Denetleyiciler

Denetleyici sınıfı ve eylem yöntemi, kaynak denetleyicisinin yedi eylemi rotalara açması ve tek eylemli denetleyici; yapıcı bağımlılıklarının nesne kabı tarafından çözülmesi.

Önceki ders isteği bir rotaya düşürdü, ama rotanın gösterdiği şeyi bir fonksiyon olarak varsaydı. Gerçek uygulamalarda eylem bir sınıfın yöntemidir ve sınıf başka nesnelere dayanır. Bu ders rotadan eyleme giden yolu açar: sınıfı kim kurar, bağımlılığını kim sağlar ve ancak o zaman eylem çağrılır.

## Denetleyici Sınıfı ve Eylem

Denetleyici (controller), ilgili eylemleri bir sınıfta toplar. Eylem, o sınıfın bir yöntemidir ve bir isteği karşılar: parametreleri alır, bir iş yapar ve bir yanıt döndürür. Rota artık bir fonksiyon değil, bir sınıf adı ve bir yöntem adı taşır. Çerçeve isteği bu rotaya eşledikten sonra üç şey yapar: sınıfı kurar, yöntemi çağırır ve yöntemin döndürdüğü yanıtı geri verir.

Sınıfın kurulması kolay bir adım değildir, çünkü denetleyici başka nesnelere dayanabilir. Bir yazı denetleyicisi yazıları saklayan bir depoya ihtiyaç duyar ve bunu yapıcısında bir parametre olarak ister. Rotanın bu depoyu sağlaması beklenemez; kuran taraf çerçevedir. Çerçeve bunu **nesne kabına** (object container) bırakır: ayrıntısı sonraki derslerde açılan, sınıf adından nesne kuran fabrikaya. Burada bilinmesi gereken tek şey, kabın yapıcı parametrelerinin tipine bakıp onları da kurmasıdır. Bağımlılık Enjeksiyonu dersinde ölçülen şey bu çözümlemenin yapısıydı; burada yalnız denetleyicinin kurulup eylemin çağrılması önemlidir.

`denetleyici.php` bu zinciri tek dosyada kurar. `Yonlendirici` rotaya bir sınıf ve yöntem adı kaydeder, `isle()` eşleşen rotanın denetleyicisini kaptan alır ve eylemi çağırır. Kap, yalnız sınıf tipli yapıcı parametresini çözer; sınıf tipi olmayan bir parametre bu modelin kapsamı dışındadır. Dosya KAYNAK olarak verilir, ölçümler onu yükler.

```php
<?php
// denetleyici.php — rotayı bir denetleyici sınıfına ve eylem yöntemine bağlar
declare(strict_types=1);
require_once 'kostu.php';

final class Yanit {
    public function __construct(public int $durum, public string $govde = '') {}
}

final class NesneKabi {
    public function al(string $ad): object {
        $bagimliliklar = [];
        foreach ((new ReflectionClass($ad))->getConstructor()?->getParameters() ?? [] as $p) {
            $bagimliliklar[] = $this->al($p->getType()->getName());
        }
        return new $ad(...$bagimliliklar);
    }
}

final class Yonlendirici {
    private array $rotalar = [];
    public function __construct(private NesneKabi $kap) {}
    public function rotalar(): array { return $this->rotalar; }
    public function rota(string $yontem, string $kalip, string $parca, string $sinif, string $eylem = '__invoke'): void {
        $this->rotalar[] = [$yontem, $kalip, $parca, $sinif, $eylem];
    }
    public function kaynak(string $ad, string $sinif): void {
        $yedi = [
            ['GET', "/$ad", 'liste'], ['GET', "/$ad/olustur", 'olustur'], ['POST', "/$ad", 'kaydet'],
            ['GET', "/$ad/{id}", 'goster'], ['GET', "/$ad/{id}/duzenle", 'duzenle'],
            ['PUT', "/$ad/{id}", 'guncelle'], ['DELETE', "/$ad/{id}", 'sil'],
        ];
        foreach ($yedi as [$yontem, $kalip, $eylem]) {
            if (method_exists($sinif, $eylem)) $this->rota($yontem, $kalip, $eylem, $sinif, $eylem);
        }
    }
    public function isle(string $yontem, string $yol): Yanit {
        Gunluk::yeniIstek();
        foreach ($this->rotalar as [$y, $kalip, $parca, $sinif, $eylem]) {      // ilk eşleşen kazanır
            $desen = '#^' . preg_replace('#\{(\w+)\}#', '(?P<$1>[^/]+)', $kalip) . '$#';
            if ($y === $yontem && preg_match($desen, $yol, $m)) {
                Gunluk::yola($parca);
                $denetleyici = $this->kap->al($sinif);       // yapıcı bağımlılıkları çözülür
                Gunluk::kostu($parca);
                return $denetleyici->$eylem(array_filter($m, 'is_string', ARRAY_FILTER_USE_KEY));
            }
        }
        return new Yanit(404);
    }
}
```

Çekirdek `kostu.php` her derste aynı yirmi iki satırla yeniden verilir.

```php
<?php
// kostu.php — beklenen bir parça bu istekte koştu mu?
declare(strict_types=1);

final class Gunluk {
    public static array $yol = [];         // isteğin yoluna konan parçalar
    public static array $kostu = [];       // istek içinde çağrılanlar
    public static array $ertelenen = [];   // isteğin dışına bırakılanlar
    public static function yeniIstek(): void { self::$yol = self::$kostu = self::$ertelenen = []; }
    public static function yola(string $parca): void { self::$yol[] = $parca; }
    public static function kostu(string $parca): void { self::$kostu[] = $parca; }
    public static function ertele(string $parca): void { self::$ertelenen[] = $parca; }
}

function sinifla(string $parca): string {
    return match (true) {
        in_array($parca, Gunluk::$kostu, true) => 'koştu',
        in_array($parca, Gunluk::$ertelenen, true) => 'ertelendi',
        in_array($parca, Gunluk::$yol, true) => 'atlandı',
        default => 'bağlanmadı',
    };
}
```

**ÇT12.** `isle()` iki ayrı bildirim yapar ve ikisinin arasında denetleyici kurulur. `Gunluk::yola()` rota eşleştiğinde çağrılır; `Gunluk::kostu()` ise `$this->kap->al($sinif)` satırından sonra, yani denetleyici kurulduktan sonra çağrılır. Kurulum bir istisna atsaydı, parça yola konmuş ama koştu listesine girmemiş olurdu; `sinifla()` kuralına göre o parça **atlandı** sayılırdı. Bu sıra bilinçlidir: koştu, eylemin çağrılacağı noktaya gelindiğini söyler; çağrıdan önceki her adım yoldadır, ama henüz koşmamıştır.

## Kaynak Denetleyicisi ve Tek Eylemli Denetleyici

Çoğu uygulamada bir varlık aynı yedi eylem etrafında döner: listele, oluşturma formunu göster, kaydet, göster, düzenleme formunu göster, güncelle ve sil. Her biri için bir rota yazmak yinelemedir. **Kaynak denetleyicisi** (resource controller) bu yedi eylemi tek bir kayıtla rotalara açar. `kaynak()` yedi satırlık bir tablo taşır ve tanımlı her eylem için bir rota kaydeder.

| Yöntem | Kalıp | Eylem |
|---|---|---|
| GET | /yazilar | liste |
| GET | /yazilar/olustur | olustur |
| POST | /yazilar | kaydet |
| GET | /yazilar/{id} | goster |
| GET | /yazilar/{id}/duzenle | duzenle |
| PUT | /yazilar/{id} | guncelle |
| DELETE | /yazilar/{id} | sil |

Tablodaki sıra rastgele değildir. `/yazilar/olustur` kalıbı `/yazilar/{id}` kalıbından önce gelir, çünkü önceki derste ölçüldüğü gibi ilk eşleşen kazanır: sıra ters olsaydı `olustur` sözcüğü bir `id` olarak okunurdu. Kaynak denetleyicisi yedi rotayı bu sırayla kaydeder ve dar kalıbı geniş kalıptan önce koyar.

Her eylem bir yöntemdir, ama bazı eylemler tek başına bir sınıfı hak eder. **Tek eylemli denetleyici** yalnızca `__invoke` yöntemini taşır ve rota yalnızca sınıf adını verir. Yayınlama gibi, bir varlığın standart yedi eylemine girmeyen bir işlem için uygundur. Aşağıdaki dosya hem kaynak denetleyicisini hem tek eylemli denetleyiciyi kaydeder, yedi rotayı ve bir ek rotayı yazdırır, sonra iki isteği sınıflar.

```php
<?php
// denetleyiciler.php — kaynak denetleyicisi yedi eylemi yedi rotaya açar
declare(strict_types=1);
require_once 'denetleyici.php';

final class Depo {}

final class YaziDenetleyicisi {
    public function __construct(private Depo $depo) {}
    public function liste(): Yanit { return new Yanit(200, 'liste'); }
    public function olustur(): Yanit { return new Yanit(200, 'form'); }
    public function kaydet(): Yanit { return new Yanit(201, 'kaydedildi'); }
    public function goster(array $p): Yanit { return new Yanit(200, 'yazı ' . $p['id'] . ' (' . $this->depo::class . ')'); }
    public function duzenle(array $p): Yanit { return new Yanit(200, "düzenle {$p['id']}"); }
    public function guncelle(array $p): Yanit { return new Yanit(200, "güncellendi {$p['id']}"); }
    public function sil(array $p): Yanit { return new Yanit(204); }
}

final class YayinlaDenetleyicisi {
    public function __invoke(array $p): Yanit { return new Yanit(200, "yayında {$p['id']}"); }
}

$y = new Yonlendirici(new NesneKabi());
$y->kaynak('yazilar', YaziDenetleyicisi::class);
$y->rota('POST', '/yazilar/{id}/yayinla', 'yayinla', YayinlaDenetleyicisi::class);

foreach ($y->rotalar() as [$yontem, $kalip, $parca]) {
    printf("%-6s %-22s %s\n", $yontem, $kalip, $parca);
}
foreach ([['GET', '/yazilar/7', 'goster'], ['POST', '/yazilar/7/yayinla', 'yayinla']] as [$yontem, $yol, $parca]) {
    $yanit = $y->isle($yontem, $yol);
    printf("%-4s %-19s %d %-17s %s %s\n", $yontem, $yol, $yanit->durum, $yanit->govde, $parca, sinifla($parca));
}
```

```
GET    /yazilar               liste
GET    /yazilar/olustur       olustur
POST   /yazilar               kaydet
GET    /yazilar/{id}          goster
GET    /yazilar/{id}/duzenle  duzenle
PUT    /yazilar/{id}          guncelle
DELETE /yazilar/{id}          sil
POST   /yazilar/{id}/yayinla  yayinla
GET  /yazilar/7          200 yazı 7 (Depo)    goster koştu
POST /yazilar/7/yayinla  200 yayında 7        yayinla koştu
```

**ÇT13.** Yedi eylem yedi rotaya açıldı ve sekizinci rota tek eylemli denetleyiciye ait. `/yazilar/7` isteğinde `YaziDenetleyicisi` kuruldu ve yapıcısındaki `Depo` parametresi kap tarafından `ReflectionClass` ile çözüldü; yanıt gövdesindeki `Depo` adı bunun kanıtıdır. Hiçbir yerde `new Depo()` yazılmadı. Eylem **koştu**. Tek eylemli denetleyici de aynı biçimde kuruldu ve `__invoke` çağrıldı; `yayinla` da koştu.

Bu dersin ölçü sınıfı **koştu**'dur. Ama koştu sınıfı yeni bir şeyi söylüyor: bir eylemin koşması için yalnız rotanın eşleşmesi yetmez, denetleyicinin kurulabilmesi de gerekir. Kurulamayan bir denetleyicinin eylemi çağrılmaz; `sinifla()` kuralını okursak o parça yolda kalır ve atlandı sınıfına düşer. Bu dersin ölçümleri bunu koşturmaz, yalnız kurala dayanır.

## Denetleyicinin İşi

Denetleyici, isteğin sınırında durur. İsteğin parametrelerini alır, işi başka nesnelere yaptırır ve yanıtı kurar. İşin kendisini, yani bir yazının nasıl saklanacağını ya da hangi kuralla doğrulanacağını denetleyicide yazmak, o kodu isteğe bağlar: bir komut satırı betiğinden ya da bir işçiden çağrılamaz. Bu yüzden eylem küçük tutulur ve gerçek iş, yapıcıda istenen `Depo` gibi bir nesnede durur. Denetleyicinin yapıcıda bağımlılık istemesinin nedeni de budur: eylem işi yapan nesneyi kendisi kurmaz, kendisine verilmesini bekler.

Bu ölçümde yanıtlar bilerek kısa tutuldu: `sil` eylemi yalnızca 204 durumuyla bir yanıt döndürür, `kaydet` 201 ile bir ileti verir. Eylemin döndürdüğü her şey `Yanit` nesnesidir; eylem çıktıyı kendisi yazmaz. Çerçeve yanıtı alır ve göndermeyi üstlenir. Eylemin yanıt yerine doğrudan çıktı yazması, yanıttan sonra koşan işlerin ve ara katmanların dönüş yolunda yanıta dokunmasını imkânsız kılar.

İki denetleyici biçimi arasındaki seçim bir üslup kararıdır. Kaynak denetleyicisi, standart yedi eylem etrafında dönen bir varlık için tek sınıfla yedi rotayı kaydeder ve adresleri kuralla belirler. Tek eylemli denetleyici, yedi eylemin hiçbirine uymayan bir işlem için kullanılır ve rota tablosunda sınıfın adı işlemin adıdır. Birini seçmek eşleşmeyi değiştirmez; ikisinde de ilk eşleşen kazanır ve ikisinde de eylemin koşması denetleyicinin kurulmasına bağlıdır.

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

Kaynak denetleyicisi, tanımlı olmayan eylemler için rota kaydetmez: `kaynak()` içindeki `method_exists` denetimi yedi eylemi süzer. `TaslakDenetleyicisi` yalnız `liste` ve `goster` taşır. Bu durumda `GET /taslaklar/7/duzenle` isteği ne olur? Parça yoldaki başka bir adıma mı bağlanır, yoksa istek 404 mü verir? İkinci istek `GET /taslaklar/olustur`, yani tanımsız bir eylemin sabit adresidir.

```php
<?php
// eksik.php — tanımsız eylemin rotası hiç kaydedilmez
declare(strict_types=1);
require_once 'denetleyici.php';

final class TaslakDenetleyicisi {
    public function liste(): Yanit { return new Yanit(200, 'taslaklar'); }
    public function goster(array $p): Yanit { return new Yanit(200, "taslak {$p['id']}"); }
}

$y = new Yonlendirici(new NesneKabi());
$y->kaynak('taslaklar', TaslakDenetleyicisi::class);

foreach ([['/taslaklar/7/duzenle', 'duzenle'], ['/taslaklar/olustur', 'olustur']] as [$yol, $parca]) {
    $yanit = $y->isle('GET', $yol);
    printf("%-21s %d %-16s %s %s\n", $yol, $yanit->durum, $yanit->govde, $parca, sinifla($parca));
}
```

```
/taslaklar/7/duzenle  404                  duzenle bağlanmadı
/taslaklar/olustur    200 taslak olustur   olustur bağlanmadı
```

**ÇT14.** Sonuç iki ayrı biçimde çıktı. `duzenle` için rota hiç kaydedilmediğinden hiçbir kalıp isteğe uymadı: yanıt **404** oldu ve `duzenle` **bağlanmadı**. Bu bir bağlanmadı olmasına rağmen sessiz değildir, çünkü 404 durum kodu isteğin hiçbir rotaya düşmediğini söyler. `olustur` için ise tablo sabit adresi kaydetmedi, ama `/taslaklar/{id}` kalıbı `olustur` sözcüğünü bir `id` olarak kabul etti: yanıt **200** ve gövde `taslak olustur`, parça yine **bağlanmadı**. Aynı sınıf, bu kez bir durum koduyla değil sessizce göründü. İki durumda da geliştiricinin beklediği parça aynı adlandırmayla sorgulanıp aynı sınıfa düştü; ayrımı yanıtın durumu yaptı.

Bu sonuç önceki dersle birleşir: bir rotanın hiç kaydedilmemesi ile bir rotanın gölgelenmesi aynı bağlanmadı sınıfına düşer, ama biri 404 ile, öteki 200 ile gelir. Kaynak denetleyicisinin eksik bıraktığı eylemi geliştirici fark etmeyebilir; çerçeve bunu bir uyarı olarak vermez, çünkü `method_exists` süzmesi bilerek sessizdir.

## Özet

- Rota bir sınıf adı ve yöntem adı taşır; çerçeve eşleşmeden sonra denetleyiciyi nesne kabından alır ve yapıcı bağımlılıklarını `ReflectionClass` ile çözdürür.
- Kaynak denetleyicisi yedi eylemi yedi rotaya açar ve dar kalıbı geniş kalıptan önce kaydeder; tek eylemli denetleyici yalnızca `__invoke` taşır.
- `/yazilar/7` isteğinde denetleyici kuruldu, `Depo` bağımlılığı çözüldü ve eylem koştu; yayinla da aynı biçimde koştu.
- Tanımsız `duzenle` eyleminin rotası kaydedilmediği için istek 404 verdi ve parça bağlanmadı; tanımsız `olustur` eylemi `/taslaklar/{id}` kalıbına düştü, yanıt 200 oldu ve parça yine bağlanmadı.
- Bu dersin ölçü sınıfı **koştu**'dur: eylemin koşması için rotanın eşleşmesi ve denetleyicinin kurulması gerekir.

## Sonraki Adım

Rota denetleyiciyi kurdu ve eylemi çağırdı. Ama her isteğin eylemden önce geçmesi gereken adımlar da vardır: oturumun açık olup olmadığı, girdinin geçerli olup olmadığı. Bu denetimler eylemin içine yazılırsa her denetleyicide yinelenir. Sonraki ders, Ara Katmanlar, bu denetimleri eylemin önüne koyar ve tek bir soruyu soruyor: önündeki katman yanıt verirse eylem ve arkadaki katmanlar ne olur?
