Kategoriler
Teknik

PHP’de Blog veya İçerik Yönetim Sistemi Oluşturmak

En popüler içerik yönetim sistemi/blog yazılımı olan WordPress’i bir çoğumuz kullanmıştır kullanmasına ama bazılarımız var ki WordPress’in nasıl çalıştığını merak edip, bazılarımız da eklenti/tema geliştirmeye merak salmışızdır. Tüm bunlardan doğru, karşımıza çıkan binlerce fonksiyon, yöntem, ıvır, zıvır vesaire derken kafamız çorba gibi oluyor. Bu çorbalaşmış kafayı netleştirmek için bu yazıda basit bir blog sistemi nasıl yazılır ondan bahsedeceğim. Elbette ki WordPress alternatifi olmayacak. Sadece işimizi görebilecek, basit ve kullanışlı bir sistem hakkında konuşacağız.

Veritabanı Yapısı

Veritabanı yapısından başlayalım ki sistemi kafamızda oturtmamız daha kolay olsun ve rahatça içselleştirebilelim. Bir blog sistemi aslında en minimal haliyle 2 tablo ile çalışıp güçlü hale getirilebilir. Birincisi içerik tablosu, diğeri ise kategorilerin bulunduğu tablo. Yorum tablosunu atlıyorum, yorum kısmını Disqus veya Facebook yorumlarıyla da çözebiliriz.

İçerikler Tablosu

İçerik tablosu, oluşturmak istediğimiz blog sisteminin kalp niteliğini taşıyan tablo olacaktır. Çünkü her türlü içeriği (blog gönderisi, sayfalar, video içerikleri vs.) burada tutacağız. Hemen nasıl göründüğüne bakalım:

contents tablosu

id slug type title body tags category_id created_at
1 merhaba-dunya post Merhaba Dünya İlk Gönderi, . Lorem lipsum dolor sit amet. Merhaba, Dünya 1 2018-07-18 11:27:53
2 hakkimda page Hakkımda Hakkımda sayfası içeriği 2018-07-18 11:37:12
3 harika-bir-video-cektim video Harika Bir Video Çektim! Youtube embed veya bağlantısı Youtube, Video, Macera 2 2018-07-18 12:42:19

Genel hatlarıyla içerik tablomuz bu şekle sahip olacak. Burada slug sütunu, kalıcı bağlantıyı ifade ediyor. Kullanıcı içeriğe erişirken https://foo.com/kalici-baglanti URL’ini kullanabilmemiz için, veritabanımızda slug sütununun olması şart. Eğer https://foo.com/content.php?id=5 şeklinde bir yapı tercih etmiş olsaydı, slug sütunu koymamıza gerek kalmazdı.

type sütunu ise, içeriğin türünü belirlediğimiz tablo olacak. Ben üç tane içerik türü ekledim, siz hayal gücünüze göre bunu ayarlayabilirsiniz. post blog gönderisini, page blog içindeki bir sayfayı video ise bir video gönderisini temsil ediyor. Mesela buna alternatif/ek olarak gallery eklenip, bir fotoğraf galerisi türü oluşturabilirsiniz.

title gönderinin başlığını, body ise gönderinin içeriğini tutacak olan sütunlar olacak. tags sütununda ise, virgülle ayrılmış biçimde etiketler tutacağız. Böylelikle bir etiket sistemimiz olmuş olacak.

created_at sütunu ise oluşturma tarihini ifade ediyor.

category_id ise kritik bir sütun. Burada mevcut gönderinin hangi kategoriye ait olduğunu belirteceğiz. Bunu yaparken kategorinin adını değilde, ID’sini vereceğiz. Peki vereceğimiz bu ID’yi nerede, nasıl kullanıp ne olduğunu nasıl anlayacağız?

MySQL, SQLite, PostgreSQL gibi veritabanları ilişkisel veritabanı türünde olduğu için, bir tablodaki satırı, başka tablodaki satır veya satırlarla bağlayabiliyor/ilişkilendirebiliyoruz. Şimdi bu blog/içerik yönetim sisteminde, her bir içerik bir adet kategoriye ait olmuş olacak (Sayfa türündeki içerikler herhangi bir kategoriye sahip olmasa da olur).

Kategoriler Tablosu

Kategorileri tuttuğumuz bu tabloda sistem içerisindeki temel taksonomiyi en basit haliyle gerçekleştirmiş olacağız. Daha karmaşık ve spesifik yöntemler var ama, bu yazının konusu basitlik olduğundan fazla detaya girmeyeceğiz. Evvela, tablomuzun yapısın görelim:

categories tablosu

id name description post_count
1 Genel Diğer kategorilere dahil olmayan içerikler 1
2 Eğlence Eğlenceli içeriklerin bulunduğu muazzam bölüm! 1

Şimdi önceki tasarladığımız tablo olan contents tablosuna dönelim ve oradaki category_id sütununa bakalım. Bu sütunda 1 değerini alanların bu tablodaki yani categories tablosundaki ID’si 1 olan satıra ait olduğunu anlıyoruz. 2 değerini alanlar ise, Eğlence kategorisine dahilmiş.

İçerikler tablosundaki 100 tane satır/içerik 1 ID’li kategoriye ait olabilir. Aynı şekilde diğer 35 içerik de 2 ID’li kategoriye ait olabilir. Böylelikle her içerik bir kategoriye ait olmuş olur. Her içeriğin en fazla bir kategorisi olabilir. Birden fazla kategoriye sahip olamaz (buna One To Many ilişkilendirme deniyor). Bu yaptığımız sistemde bu mümkün değil. Çok daha farklı bir ilişkilendirme sisteminde bir içerik birden fazla kategoriye ait olacak şekilde ayarlayabilirdik (WordPress’te birden fazla kategori seçebiliyorsunuz mesela ve bu yöntemin adı Many To Many ilişkilendirme) ama yapmadık. İşleri karıştırmadık. 🙂

Temel olarak, veritabanı yapısı bu şekilde. Peki PHP ile oluşturacağımız rotalar/sayfalar hangi işleri yapacak? Hemen kısaca değinelim.

İçerik Sayfası

İçerik sayfasında, veritabanında slug ile eşleşen veriyi alıp, sayfada işleleyeceğiz. Örnek SQL sorgusu şöyle olacak:

SELECT * FROM contents WHERE slug=?

Veritabanından alacağımı veri, göstereceğimiz içerik olacak. type sütunundaki değere göre de gösterilecek içerik için farklı işlevler gerçekleştirebiliriz.

Kategori Sayfası

Kategori sayfasında, kategori ID’sine göre içerikleri alabiliriz. (Kategoriler için slug da kullanabilirdik) Örnek SQL sorgusunu şöyle görelim:

SELECT * FROM contents WHERE category_id=?

Böylelikle kategoriye ait içerikleri de elde etmiş olduk. Şimdilik anlatacaklarım bundan ibaret. Herhangi bir sorunuz varsa severek cevaplarım. Bir sonraki yazıda görüşmek üzere.

Kategoriler
Teknik

PHP ile Basit Bir Router Nasıl Yapılır

PHP’de gelen istekleri (request) cevaplamak (response) için en kullanışlı yöntemlerden birisi router kullanmaktır. Popüler PHP ve PHP olmayan web çatılarının (framework) olmazsa olmazıdır router/yönlendirici/rotacı.

Rotalar, belli özelliklere sahiptir. Bunlardan en önemli üçü şunlardır:

  1. Rota yolu (/sayfa/5, /gonderi/3 gibi)
  2. İstek metdodları (GET, POST, PUT, PATCH, DELETE vs. gibi)
  3. Geri çağırım işlevleri (en basit tabiriyle fonksiyonlar veya sınıf metodları)

Eğer tüm bunları sağlarsak, sağlıklı bir rotacı yaratabilir ve rahatlıkla kullanabiliriz. Bunların dışında middleware(lar) (ara katmanlar olarak çevrilebilir), rota isimlendirmesi gibi birçok özellik/parametreye sahip olabilirler.

Şimdi vereceğim örnekte yalnızca GET ve POST istek metodlarını karşılayacak olan basit bir rotacı yazacağız.

Başlayalım.

Evvela bir Router sınıfımızı yaratıp gerekli metodları yaratalım.

Router.php

class Router
{
  /**
   * Mevcut yolu tutar
   * @var string
   */
  protected $actualPath;

  /**
   * Mevcut istek metodunu tutar
   * @var string
   */
  protected $actualMethod;

  /**
   * Tanımlanmış rotaları tutar
   * @var array
   */
  protected $routes = [];

  /**
   * 404 Sayfasını tutar
   * @var Closure|string
   */
  protected $notFound;

  /**
   * Rotacıyı başlatır
   * @param string $currentPath Mevcut yol
   * @param string $currentMethod Mevcut istek metodu
   */
  public function __construct($currentPath, $currentMethod)
  {
    $this->actualPath = $currentPath;
    $this->actualMethod = $currentMethod;

    // Sayfa bulunamadı rotasını ayarlayalım
    $this->notFound = function(){
      http_response_code(404);
      echo '404 Not Found';
    };
  }

  /**
   * Yeni bir GET rotası yaratır
   * @param string $path İstek yolu
   * @param Closure|string $callback Geri çağırım işlevi
   * @return void 
   */
  public function get($path, $callback)
  {
    $this->routes[] = ['GET', $path, $callback];
  }

  /**
   * Yeni bir POST rotası yaratır
   * @param string $path İstek yolu
   * @param Closure|string $callback Geri çağırım işlevi
   * @return void
   */
  public function post($path, $callback)
  {
    $this->routes[] = ['POST', $path, $callback];
  }

  /**
   * Rotalar tanımlandıktan sonra eşleşen rotayı bulup çalıştırır
   * @return mixed
   */
  public function run()
  {
    foreach ($this->routes as $route) {
      list($method, $path, $callback) = $route;

      $checkMethod = $this->actualMethod == $method;
      $checkPath = preg_match("~^{$path}$~ixs", $this->actualPath, $params);

      if ($checkMethod && $checkPath) {
        array_shift($params);
        return call_user_func_array($callback, $params);
      }
    }

    return call_user_func($this->notFound);
  }
}

Yukarıdaki yazdığım sınıfta her metodun üstündeki açıklama bölümlerinde alacağı parametreleri ve metodların ne iş yaptığını yazdım. Ama biz yine de metodların ne işe yaradığını detaylandıralım:

  1. __construct metodunda Router sınıfı oluşturulurken iki parametre aldık. Bunlardan $currentPath parametresi kullanıcının sayfaya girmek istediğinde oluşacak olan URL barındaki yol değeridir. Yani https://yilmazdemir.com.tr/herhangi/bir/sey dizgesindeki /herhangi/bir/sey. $_SERVER['REQUEST_URI'] ile elde edebiliyoruz. Bir sonraki adımda yapacağız.
  2. İkinci parametre $currentMethod parametresi. Kullanıcıdan gelen mevcut istek metoduna işaret eder. GET, POST, PUT, PATCH, DELETE gibi değerlerdir. Ama biz detaya inmeden yalnızca GET ve POST tiplerini işleyeceğiz. Daha detaylı bilgi için HTTP Protokolü’nü araştırabilirsiniz. İstek metodunu $_SERVER['REQUEST_METHOD'] ile elde edeceğiz.
  3. Metodun içinde ayrıca belirttiğimiz bir anonim fonksiyon var. Bu herhangi bir rota eşleşmediğinde döndürmek için kullanacağımız fonksiyon. Ben doğrudan kodlu (hard-coded) biçimde yazdım ama bir notFound metodu yazılarak dinamikleştirilebilir.
  4. get ve post metodlarında ise rota tanımlaması yaptırıyoruz. Sınıf içerisinde $routes sınıf değişkenine yeni bir değer ekleyerek aldığımız $path ve $callback parametreleriyle rotamızı oluşturuyoruz.
  5. Son olarak rotayı çalıştırabilmek için bir metoda ihtiyacımız var. Bu metod run metodu. Örneğimizde bu metod sınıf değişkeni olan $routes değişkeniyle bir döngü oluşturuyor ve işlemlerimizi burada yapıyoruz. list() fonksiyonu ile her bir rota tanımlamamız için dize değerlerini değişken yapıyoruz. $checkMethod ve $checkPath değişkenleriyle de bir doğrulama yapıyoruz. Bu doğrulamada bizim rotaları oluştururken tanımladığımız değerlerle kullanıcıdan gelen değerler eşleşiyor mu bunu arıyoruz. Eğer doğru (true) dönerse $callback‘i çalıştırıp döndürüyoruz. Eğer hiçbir uyuşma yoksa döngüden sonra 404 döndürüyoruz.

index.php

require 'Router.php';

$path = $_SERVER['REQUEST_URI'];
$method = $_SERVER['REQUEST_METHOD'];

$router = new Router($path, $method);

$router->get('/', function(){
  echo 'Ana Sayfa';
});

$router->get('/haber/([d]+)', function($id){
  echo "Haber ID'si: {$id}";
});

$router->post('/foo', function(){
  echo 'Diğer işlemler';
});

$router->run();

Router hazır. Rota parametresi için düzenli ifadeler (Regular Expressions) kullanıyoruz. Bunlar daha da basitleştirilebilir. Ayrıca bazı buglar da mevcut. Bunları kullandıkça fark edersiniz elbette. Yeni özellikler katmak sizin hayal gücünüze bağlı. Örneğin /haberler rotası oluşturduğunuz ve sayfalamayı QueryString ile yapacaksınız. Yani /haberler?sayfa=5 şeklinde bir ifade maalesef ki eşleşmeyecektir. Bu durumda istek yolunu düzenledikten sonra sınıf içine tanımlamanız gerekiyor.

Apache için htaccess şu şekilde:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

Kolay gelsin.

Kategoriler
Teknik

İşimizi Kolaylaştıran 10 PHP Kodlama Yöntemi

1. Üçlü (Ternary) Operatör

Üçlü operatör, if/else işlemlerimizi kolaylaştırmamızı sağlayan soru işareti (?) ve iki nokta üst üste (:) işaretiyle oluşturduğumuz bir yöntemdir. Bu yöntem işimizi hem kolaylaştırır hem de hızlı bir hale getirir. Örneğini görelim:

$sayfa = isset($_GET['sayfa']) ? $_GET['sayfa'] : 1;
// veya
echo isset($degisken) ? $degisken : 'Değişken yok!';

2. Hata Yönetimi & İstisna Yakalama

Programlama ile uğraşıyorsak hatalar bu işin olmazsa olmazlarından. Hatasız kul olmadığı gibi, geliştirme sürecinde de uygulama olmuyor. Tüm programlama dillerinde olduğu gibi PHP’de de hata yönetimi oldukça önemli.

try {
  // herhangi bir şey
} catch (Exception $e) {
  echo 'Hata var: ' . $e->getMessage();
}

3. list() Fonksiyonu

Dizi öğelerini değişkenlere atamak için list() fonksiyonu iyi bir araçtır.

$kisi = ['Yılmaz Demir', '[email protected]'];
list($isim, $eposta) = $kisi;

şuna denk düşer:

$isim = $kisi[0];
$eposta = $kisi[1];

4. compact() Fonksiyonu

Ben MVC ile yazdığım uygulamalarımda görünüm dosyasına parametre gönderirken genelde compact() fonksiyonu kullanıyorum. Bu çok daha kolay oluyor ve kod yapısını iyileştiriyor. Örneğin:

$baslik = 'Ana Sayfa';
$gonderiler = $gonderi->hepsi();

$this->render('gonderiler.php', compact('baslik', 'gonderiler'));

compact('baslik', 'gonderiler') şuna denk düşer:

array('baslik' => $baslik, 'gonderiler' => $gonderiler);

5. Tek Satırda if

Metodlarda/fonksiyonlarda eğer olağandan farklı bir durum olursa, süslü parantez kullanmadan if yapısını şu şekilde kullanıyorum:

if (!is_numeric($_GET['id']) return false;

6. Bulunduğunuz Dizini İşaretleyin

Programa herhangi bir dosya dahil ederken (require, include) veya bir dosya çağırırken dosya yoluna bulunulan dizini eklemek oldukça yararlı bir kullanım.

define('CONFIG_DIR', __DIR__.'/config');
// veya
require __DIR__.'/config.php';

7. İsimleri Kısaltarak Anlamsızlaştırmayın

Bizler makine değiliz. Kodlama esnasında ‘zamandan kazanırım’ diyerek değişken, fonksiyon, metod, sınıf vs. isimlerini anlamsızlaştırmak veya kısaltmak hem bize, hem de daha sonra kodu okumak isteyen insanlara epey sıkıntı çıkaracaktır. Örneğin:

$syf = 5; // $sayfa = 5;
$pg = 5; // $page = 5;

8. Çift Tırnak ve Tek Tırnak Farkı

PHP’de çift tırnak ve tek tırnak farklı amaçlar için kullanılır. Bazı programlama dillerinde bu fark gözetildiği gibi PHP’de de gözetilir. Çift tırnak işareti arasında değişkenleri yazdırabiliyorken, tek tırnak işareti arasında yazdıramayız. Aynı zamanda tek tırnak işaretleri arasında ASCII kontrol karakterleri kullanılamıyor. Örneklendirelim:

$isim = 'Yılmaz';
echo "Merhaba $isim"; // doğru

// şu kullanım geçersizdir:
echo 'Merhaba $isimn'; // veya echo 'merhabanisim';

// daha güvenli değişken kullanımı içinse:
echo "Merhaba sevgili {$isim}!";

9. strlen() alternatifi

Birazdan göstereceğim kullanım strlen() fonksiyonu için ufak bir alternatif olabilir. Ama türkçe karakterler için kullandığımız mb_strlen() için bir alternatif olamıyor. Şöyle ki:

$kullaniciAdi = 'demir';
echo isset($kullaniciAdi[15]) ? 'Kullanıcı adı 15 karakterden fazla olamaz' : null;
echo !isset($kullaniciAdi[2]) ? 'Kullanıcı adı 3 karakterden az olamaz' : null;

10. Fonksiyonları Tanımlarken Hatayı Önlemek

Genelde, düzenli kod yazımında ve ortak çalışma yapıldığında bu yöntem çokça kullanılır. Birçok geliştirici bunu alışkanlık haline getirmiştir, size de önerim bu şekilde kullanmanızdır. Fonksiyon tanımlamadan önce, o fonksiyonun tanımlanıp tanımlanmadığını kontrol etmek.

if (!function_exists('permalink')) {
  function permalink($string)
  {
     # kodlar...
  }
}

Umarım hepsi iş görür, iyi kodlamalar.

Kategoriler
Teknik

Model Katmanı ve Veritabanı İşlemlerini Kolaylaştırmak

İlk yazıda bahsettiğimiz gibi, model katmanı veritabanı işlemlerimizi düzene sokmak ve kolaylaştırmak adına ihtiyaç duyduğumuz bir katman. Bu yazıda yapacağımız MVC sisteminin model çekirdeğini kodlayacağız. Haydi başlayalım.

/app/core/model.php:

class model
{
    /**
     * Veritabanını nesnesini tutar
     * @var PDO
     */
    public $db;

    /**
     * Veritabanı nesnesini oluşturur
     */
    public function __construct()
    {
        $this->db = new PDO(DB_DSN, DB_USR, DB_PWD);
    }
}

En basit haliyle bu şekilde olan bir model katmanı bizim birçok ihtiyacımızı karşılayacaktır. Sınıf içerisinde $db değişkeni ekleyip, buna PDO nesnesi ile veritabanı bağlantığımızı bağladık. Görece bu yeterlidir, fakat ben işimizi daha da kolaylaştırmak istiyorum. Şimdi iki tane daha metod yazacağız. Bu metodlardan birisi fetch() ve fetchAll() metodları olacak. Bilindiği üzere bu metodlar PDO ile veri alışverişi yaptığımızda veri döndüren metodlardı. Haydi işimize koyulalım.

fetch() metodu

/**
 * Tek satırlık veri döndüren sorgu çalıştırır
 * @param string $query SQL sorgusu
 * @param array $params varsa parametreler
 * @return array
 */
public function fetch($query, array $params = [])
{
    $sth = $this->db->prepare($query);
    $sth->execute($params);
    return $sth->fetch();
}

Ne işimizi görecek bu metod? Bununla daha hızlı sorgular oluşturup, parametre ile beraber veriyi alabileceğiz. PDO’da fetch() metodu tekil veri döndürür. Bu metodun örnek kullanımı şu şekilde olabilir:

$post = $model->fetch('SELECT * FROM post WHERE id=:id', [':id' => 5]);

Bu satır, 5 numaralı ID’ye sahip olan veriyi döndürecektir. Aynı metodun bir de fetchAll() ile query() olanını ekleyelim:

/**
 * Birden fazla satır döndüren sorgu çalıştırır
 * @param string $query SQL sorgusu
 * @param array $params varsa parametreler
 * @return array
 */
public function fetchAll($query, array $params = [])
{
    $sth = $this->db->prepare($query);
    $sth->execute($params);
    return $sth->fetchAll();
}

/**
 * Sorgu çalıştırır
 * @param string $query SQL sorgusu
 * @param array $params varsa parametreler
 * @return array
 */
public function query($query, array $params = [])
{
    $sth = $this->db->prepare($query);
    return $sth->execute($params);
}

Bunu ise şu şekilde kullanabileceğiz:

$posts = $model->fetchAll('SELECT * FROM post WHERE draft=:draft', [':draft' => 'taslak_olmayan_gonderi']);

Bu kadar kolay. Şimdi, sınıfımızın son haline bakalım:

class model
{
    /**
     * Veritabanını nesnesini tutar
     * @var void
     */
    public $db;

    /**
     * Veritabanı nesnesini oluşturur
     */
    public function __construct()
    {
        $this->db = new PDO(DB_DSN, DB_USR, DB_PWD);
    }

    /**
     * Tek satırlık veri döndüren sorgu çalıştırır
     * @param string $query SQL sorgusu
     * @param array $params varsa parametreler
     * @return array
     */
    public function fetch($query, array $params = [])
    {
        $sth = $this->db->prepare($query);
        $sth->execute($params);
        return $sth->fetch();
    }

    /**
     * Birden fazla satır döndüren sorgu çalıştırır
     * @param string $query SQL sorgusu
     * @param array $params varsa parametreler
     * @return array
     */
    public function fetchAll($query, array $params = [])
    {
        $sth = $this->db->prepare($query);
        $sth->execute($params);
        return $sth->fetchAll();
    }

    /**
     * Sorgu çalıştırır
     * @param string $query SQL sorgusu
     * @param array $params varsa parametreler
     * @return array
     */
    public function query($query, array $params = [])
    {
        $sth = $this->db->prepare($query);
        return $sth->execute($params);
    }
}

Bu yazdığımız çekirdek sınıfı, yarattığımız bir model dosyasında veya bir controller metodunda şu şekilde kullanabileceğiz:

Model dosyasında:

class post extends model
{
    public function getAll()
    {
        return $this->fetchAll('SELECT * FROM post ORDER BY created DESC');
    }
}

Controller dosyasında:

class postController extends controller
{
    public function getAction($id)
    {
        $model = $this->model('post');
        $post = $model->fetch('SELECT * FROM post WHERE id=?', [$id]);
        var_dump($post);
    }
}

Artık, MVC sistemimiz hazır.


Adım adım:

  1. Yeni Başlayanlar İçin PHP’de MVC
  2. MVC Uygulaması İçin İlk Adımlar: App Sınıfı
  3. MVC’de Giriş Sayfası, Ayarlamalar ve İlk Controller
  4. Controller ve View Çekirdek Sınıflarını Oluşturmak
  5. Model Katmanı ve Veritabanı İşlemlerini Kolaylaştırmak (Şu an buradasınız)
  6. Proje/Uygulamanın Deposu
  7. Uygulamanın bitmiş halini indirin
Kategoriler
Teknik

Controller ve View Çekirdek Sınıflarını Oluşturmak

View Katmanının Çekirdek Sınıfı

/app/core/view.php:

class view
{
  /**
   * Görünüm dosyasını yorumlayan metod
   * @param string $view görünüm dosyası
   * @param array $params parametreler
   */
  public static function render($view, array $params = [])
  {
    /**
     * Eğer dosya varsa
     */
    if (file_exists($file = VDIR."/{$view}.php")) {
      /**
       * $params dizesindeki verileri extract fonksiyonu
       * ile değişken haline döndürüyoruz
       */
      extract($params);

      /**
       * Çıktı tamponlamasını başlatıyoruz
       */
      ob_start();

      /**
       * View dosyası içeriğini çağırıyoruz
       */
      require $file;

      /**
       * Çıktı tamponun içeriğini döndürüp siliyoruz
       */
      echo ob_get_clean();
    /**
     * Dosya yoksa programı sonlandır
     */
    } else {
      exit("Görünüm dosyası bulunamadı: $view");
    }
  }
}

Burada view sınıfı içerisine, statik bir render sınıfı oluşturuyoruz. Statik sınıflar, normal fonksiyonlar gibi ulaşılabilen sınıflardır. Burada bunu kullanmamızın özel bir sebebi yok.

Metod parametrelerinden aldığımız verilerle işlemlerimize devam edelim. View dosyasının varlığını kontrol ediyoruz ve varsa işlemlerimizi yapıyoruz. extract() fonksiyonu bir dizedeki değerleri alıp, değişkene çevirmemize yarıyor. Detaylarını, PHP’nin dökümantasyonunda bulabilirsiniz. ob_start() fonksiyonu ile çıktı tamponlamasını başlatıyoruz yani çıktıyı tampon belleğe alıyoruz. Akabinde view dosyamızı dahil edip ob_get_clean() fonksiyonu ile tamponladığımız çıktıyı temizleyerek yazdırıyoruz. Hepsi bu.

View katmanıyla olan işimiz yalnızca bu kadar. Şimdi gelelim Controller katmanının sınıfına. Bu katman, yaratacağımız bir Controller’a yardımcı olmak için işimize yarayacak. Yaratacağımız Controller’ı -sayfasını diyelim- genişleterek işlemlerimizi kolaylaştıracak. Haydi girişelim.

İlkin, render metodu ile view dosyası yorumlayacağız.

/app/core/controller.php:

class controller
{
  /**
   * View dosyası çağırmamıza yarayan metod
   * @param string $file dosyasını adını alır
   * @param array $params parametreleri alır
   * @return void view sınıfından render metodu döner
   */
  public function render($file, array $params = [])
  {
    return view::render($file, $params);
  }
}

Yaptığımız şey çok basit, render metodu ile, view sınıfındaki render metodunu döndürdük. Yani kısacası view yorumlamak, view sınıfının işidir dedik. Şimdi, model işlemlerine bir göz atalım.

/app/core/controller.php (devamı):

class controller
{
  /**
   * View dosyası çağırmamıza yarayan metod
   * @param string $file dosyasını adını alır
   * @param array $params parametreleri alır
   * @return void view sınıfından render metodu döner
   */
  public function render($file, array $params = [])
  {
    return view::render($file, $params);
  }

  /**
   * Model dizininden model dosyası çağırır
   * @param string $model model dosyası adı
   * @return void model sınıfı
   */
  public function model($model)
  {
    // Eğer model dosyası varsa çağırıp döndürelim
    if (file_exists($file = MDIR."/{$model}.php")) {
      require_once $file;
      // Eğer model sınıfı tanımlıysa model sınıfını döndür
      if (class_exists($model)) {
        return new $model;
      // Model sınıfı tanımlı değilse programı durdur
      } else {
        exit("Model dosyasında sınıf tanımlı değil: $model");
      }
    // Eğer sınıf yoksa, hata döndürelim
    } else {
      exit("Model dosyası bulunamadı: {$model}.php");
    }
  }

  /**
   * Yönlendirme yapar
   * @param string $path yol
   */
  public function redirect($path)
  {
    header("Location: {$path}");
  }

  /**
   * URL oluşturur
   * Bu sınıfı görünüm dosyası içinde rahat kullanmak için statik yaptık
   * @return string URL
   */
  public static function url()
  {
    return URL.'/?url='.implode('/', func_get_args());
  }
}

Sınıfa üç tane yeni metod ekledik. Bunlardan ilki olan model metoduna göz atalım. Bu metod, model dosyası çağırmak için işimize yarayacak olan metodumuz olacak. Böylelikle Controller ile model ilişkisini tamamlamış olacağız.

Evvela file_exists() fonksiyonu ile dosya varlığı kontrolü yapıyoruz ve varsa require_once() ile sayfaya dahil ediyoruz. require_once() kullanmamın sebebi, olası durumlarda sayfaya iki kere dahil edilmesini engellemek. Sonrasında sınıfın varlığını kontrol ediyoruz ve model metodunu bu sınıf olarak döndürüyoruz (new $model).

Gelelim redirect() metoduna, bu metod ihtiyaç halinde yönlendirmemizi yapıyor. URL’ye göre header’ı ayarlayıp, yönlendirmeye yardımcı oluyor. url() methodu ise URL dizgesi üretmemize yardımcı oluyor.

Olanlar bunlar. Dilersek eklemeleri kendi isteğimize göre yapabiliriz. Artık bir controller oluştururken, bu sınıfı genişleteceğiz (extend). Yani şöyle:

class blogController extends controller
{
  public function indexAction()
  {
    $blog_model = $this->model('blog'); // app/models/blog.php çağırır
    $this->render('blog/post.php'); // app/views/blog/post.php çağırır
    }
}

Kolay gelsin.


Adım adım:

  1. Yeni Başlayanlar İçin PHP’de MVC
  2. MVC Uygulaması İçin İlk Adımlar: App Sınıfı
  3. MVC’de Giriş Sayfası, Ayarlamalar ve İlk Controller
  4. Controller ve View Çekirdek Sınıflarını Oluşturmak (Şu an buradasınız)
  5. Model Katmanı ve Veritabanı İşlemlerini Kolaylaştırmak
  6. Proje/Uygulamanın Deposu
  7. Uygulamanın bitmiş halini indirin