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 void
	 */
	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

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

Controller çekirdek sınıfı, controllers dizininde oluşturduğumuz her controller sınıfını genişletmek (extend) için kullanacağımız sınıftır. Bu sayede view dosyalarını çağırıp, model sınıflarını kullanabileceğiz.

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

MVC’de Giriş Sayfası, Ayarlamalar ve İlk Controller

PHP ile bir uygulama yapmaya başlarken öncelikle yapmamız gereken dizin veveritabanı gibi birçok etkenin hiyerarşisini kafamızda tahayyül etmektir. Ardından dosya ve dizinleri oluşturarak işe başlarız. İşte bunların yapısını, sistem içerisinde de rahatça kontrol edebilmek için bazı sabitlere ihtiyaç duyarız. Sabitlerin konusuna şu sayfadan ulaşabilirsiniz. Sabit tanımlamalar ile dizinlerdeki dosyaları rahatça sayfaya dahil edebilecek (require, include) ve işlemlerimizi yapabileceğiz. Basit bir MVC sistemi kodladığımız için bunu en basit haliyle, index.php içerisinde yapacağız. Başlayalım:

define('ROOT_DIR', __DIR__); // Kök dizin
define('APP_DIR', ROOT_DIR.'/app'); // Uygulama dizini
define('CORE_DIR', APP_DIR.'/core'); // Çekirdek dizini
define('MDIR', APP_DIR.'/models'); // Model dizini
define('VDIR', APP_DIR.'/views'); // View dizini
define('CDIR', APP_DIR.'/controllers'); // Controller dizini
define('URL', 'http://localhost'); // Sistemin çalışacağı URL

Bu tanımlamalar sırasıyla, kök dizini, uygulama dizinini, çekirdek dizinini, model/view/controller dizinlerini işaret ediyor. Bunları daha ileride işimizi kolaylaştırmaları açısından kullanacağız. Peki neden böyle bir şeye ihtiyaç duyuyoruz? Olası bir dizin değişikliği durumunda örneğin; uygulama dizininin yerini değiştirdiğimizde uygulama içindeki birçok kod satırından değiştirmek yerine, index.php içerisinde kontrol edebilmiş olacağız.

// Veritabanı ayarlamalarını yapıyoruz
// Eğer ki veritabanı işlemi yapmayacaksak ayarlamak şart değil
define('DB_DSN', 'mysql:host=localhost;dbname=blog;charset=utf8');
define('DB_USR', 'root');
define('DB_PWD', 'root');

Model dosyamızla haşır neşir olduğumda bu veritabanı bilgileri ile çalışacağız, bunlar da bir kenarda dursun.

Şimdi gelelim çekirdek dizinindeki dosyalara. /core dizininde app.php oluşturmuştuk, aynı zamanda üç tane daha dosya oluşturalım ve adları model.php, view.php, controller.php olsun.

// Çekirdek sınıflarımızı dahil ediyoruz
// Bu uygulamanın çalışması için mecburi
require CORE_DIR.'/app.php';
require CORE_DIR.'/model.php';
require CORE_DIR.'/view.php';
require CORE_DIR.'/controller.php';

Sistemi çalıştıran noktaya gelelim ve yazalım:

// Uygulamamızı oluşturuyoruz
$app = new app;

// Oluşturduğumuz uygulamayı çalıştırıyoruz
$app->run();

index.php içeriği şu şekilde olacak:

define('ROOT_DIR', __DIR__); // Kök dizin
define('APP_DIR', ROOT_DIR.'/app'); // Uygulama dizini
define('CORE_DIR', APP_DIR.'/core'); // Çekirdek dizini
define('MDIR', APP_DIR.'/models'); // Model dizini
define('VDIR', APP_DIR.'/views'); // View dizini
define('CDIR', APP_DIR.'/controllers'); // Controller dizini
define('URL', 'http://localhost'); // Sistemin çalışacağı URL

// Veritabanı ayarlamalarını yapıyoruz
// Eğer ki veritabanı işlemi yapmayacaksak ayarlamak şart değil
define('DB_DSN', 'mysql:host=localhost;dbname=blog;charset=utf8');
define('DB_USR', 'root');
define('DB_PWD', 'root');

// Çekirdek sınıflarımızı dahil ediyoruz
// Bu uygulamanın çalışması için mecburi
require CORE_DIR.'/app.php';
require CORE_DIR.'/model.php';
require CORE_DIR.'/view.php';
require CORE_DIR.'/controller.php';

// Uygulamamızı oluşturuyoruz
$app = new app;

// ve oluşturduğumuz uygulamayı çalıştırıyoruz
$app->run();

Geçerli Controller dosyamızı hazırlayalım. App sınıfı başlatıcısında geçerli controller’ı defaultController olarak tanımlamıştık. Şimdi bunu biraz daha somutlaştıralım:

/app/controllers/defaultController.php:


class defaultController
{
  /**
   * indexAction metodu, giriş aksiyonudur
   * eğer sorgu dizgesinde (/?url=sorgu/digesi) hiçbir şey
   * belirtilmemişse bu sınıf ve metod tetiklenir
   */
  public function indexAction()
  {
    echo 'Merhaba Dünya!';
  }

  public function fooAction()
  {
    echo 'Burası fooAction!';
  }
}

Gelin şimdi ilk denemelerimizi yapalım:

ES1 ES2

Buraya kadar her şey yolunda! Ama büyük eksiklikler var. Controller dosyamızda view ve model kullanabilmek/çağırabilmek için sınıfımızı genişletmemiz gerekiyor. Bunu da bir sonraki yazıda işleyeceğiz.

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 (Şu an buradasınız)
  4. Controller ve View Çekirdek Sınıflarını Oluşturmak
  5. Model Katmanı ve Veritabanı İşlemlerini Kolaylaştırmak
  6. Proje/Uygulamanın Deposu
  7. Uygulamanın bitmiş halini indirin

MVC Uygulaması İçin İlk Adımlar: App Sınıfı

İkinci yazıdan merhaba arkadaşlar. Bu yazıda çalışmalara başlayıp ortaya güzel şeyler çıkaracağız. Evvela MVC uygulamasında kendimize uygun bir dizin yapısı belirlememiz gerekiyor. Bizim yapacağımız sistem şu şekilde olacak:

/app
  /controllers
    defaultController.php
    postController.php
  /core
    app.php
    controller.php
    model.php
    view.php
  /models
    post.php
  /views
    -görünüm dosyaları-
index.php

Evvela, /core dizinindeki app.php dosyası ile başlıyoruz. app sınıfı, bizim temel işlemlerimizi yapacak olan sınıftır. Kabaca anlatmak gerekirse bir kullanıcı siteye girdiği vakit ilk olarak app sınıfından geçecek ve kullanıcının ne istediğini app sınıfında yapacağımız işlemlerle anlayıp yönlendireceğiz. app sınıfı ile controller dosyalarını tetikleyecek, controller dosyaları ile model ve view katmanlarını kullanacağız.

/app/core/app.php:

class app
{
	/**
	 * Sınıf içerisinde tutulacak değerler
	 * __construct metodu ile belirleyip
	 * run metodu ile kullanacağız
	 */
	public $controller, $action, $params;

	/**
	 * Controller ve Action'ı belirleyen başlatıcı metod
	 */
	public function __construct()
	{
		/**
		 * Eğer url sorgusu varsa, başındaki ve
		 * sonundaki / işaretlerini siliyoruz
		 * yoksa geçerli olarak default/index
		 */
		$url = isset($_GET['url']) && !empty($_GET['url']) ? 
			trim($_GET['url'], '/') : 'default/index';

		/**
		 * URL dizgesini / karakterleriyle bölüyoruz
		 * Böylelikle her bölüme ulaşabileceğiz
		 */
		$url = explode('/', $url);

		/**
		 * Controller ve Action'ı belirliyoruz
		 * Eğer $url[0] varsa onu $url[0].'Controller' yani $url[0]'ın default olduğunu varsayalım
		 * indexController olacaktır, eğer yoksa defaultController olarak ayarla dedik
		 * Aynı işlemi action için de yapıyoruz. Action $url[1]'de yer alıyor
		 */
		$this->controller = isset($url[0]) ? $url[0].'Controller' : 'defaultController';
		$this->action = isset($url[1]) ? $url[1].'Action' : 'indexAction';

		/**
		 * array_shift fonksiyonu, dizedeki ilk elemanı siler/kaldırır
		 */
		array_shift($url);
		array_shift($url);

		/**
		 * $url[0] ve $url[1]'i aldık, gerisi parametre. 
		 * Yani default/index/1/2/3'ün 1/2/3 olan yeri. 
		 */
		$this->params = $url;
	}
}

Dikkat edilmesi gereken nokta $_GET['url'] kodunu kullanarak neler yaptığımız. Öncelikle varlığını sorguluyor varsa sağ ve solunda bulunan / karakterlerini trim fonksiyonu ile siliyoruz. Eğer yoksa, geçerli olarak hangi controller’ı alacağını belirliyoruz.

Uygulamada geçerli controller katmanı defaultController’dır. Geçerli aksiyon ise indexAction’dır. Aldığımız URL’i explode ile parçalara ayırıyoruz ve sınıf içerisinde bulunan değişkenlere veriyoruz. Çünkü daha sonrasında bunları run() metodunda kullanacağız.

Şimdi gelelim run() metoduna. Burada URL barından aldığımız değerlere göre hareket edip controller dosyalarını çağıracağız ve bunlara göre işlemler yapacağız. Haydi.

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

class app
{
	/**
	 * Sınıf içerisinde tutulacak değerler
	 * __construct metodu ile belirleyip
	 * run metodu ile kullanacağız
	 */
	public $controller, $action, $params;

	/**
	 * Controller ve Action'ı belirleyen başlatıcı metod
	 */
	public function __construct()
	{
		/**
		 * Eğer url sorgusu varsa, başındaki ve
		 * sonundaki / işaretlerini siliyoruz
		 * yoksa geçerli olarak default/index
		 */
		$url = isset($_GET['url']) && !empty($_GET['url']) ? 
			trim($_GET['url'], '/') : 'default/index';

		/**
		 * URL dizgesini / karakterleriyle bölüyoruz
		 * Böylelikle her bölüme ulaşabileceğiz
		 */
		$url = explode('/', $url);

		/**
		 * Controller ve Action'ı belirliyoruz
		 * Eğer $url[0] varsa onu $url[0].'Controller' yani $url[0]'ın default olduğunu varsayalım
		 * indexController olacaktır, eğer yoksa defaultController olarak ayarla dedik
		 * Aynı işlemi action için de yapıyoruz. Action $url[1]'de yer alıyor
		 */
		$this->controller = isset($url[0]) ? $url[0].'Controller' : 'defaultController';
		$this->action = isset($url[1]) ? $url[1].'Action' : 'indexAction';

		/**
		 * array_shift fonksiyonu, dizedeki ilk elemanı siler/kaldırır
		 */
		array_shift($url);
		array_shift($url);

		/**
		 * $url[0] ve $url[1]'i aldık, gerisi parametre. 
		 * Yani default/index/1/2/3'ün 1/2/3 olan yeri. 
		 */
		$this->params = $url;
	}

	/**
	 * Uygulamayı başlatır
	 */
	public function run()
	{
		// Eğer Controller dosyası varsa $file değişkenini yol olarak belirle
		if (file_exists($file = CDIR."/{$this->controller}.php")) {
			// Dosyayı sistemimize dahil edelim
			require_once $file;
			// Eğer sınıf yaratılmışsa/varsa controller'ımızı çağıralım
			if (class_exists($this->controller)) {
				// controller'ı çağıralım:
				$controller = new $this->controller;
				// Eğer metod varsa ve yaratılmışsa
				if (method_exists($controller, $this->action)) {
					// call_user_func ile controller ve metodu çağırıyoruz
					call_user_func_array([$controller, $this->action], $this->params);
				// Eğer method yoksa programdan çık
				} else {
					exit("Metod mevcut değil: {$this->action}");
				}
			// Sınıf yoksa ve yaratılmamışsa programdan çık
			} else {
				exit("Sınıf mevcut değil: $this->controller");
			}
		// Controller dosyası yoksa programdan çık
		} else {
			exit("Controller dosyası mevcut değil: {$this->controller}.php");
		}
	}
}

__construct() metodunda topladığımız bilgilerle birkaç kontrol sağlayarak işe başlıyoruz. file_exists fonksiyonu ile dosyanın var olup olmadığını soruyoruz. Burada CDIR isimli bir sabit var, bunu daha sonra index.php’de Controller dizinini işaret eden sabit olarak ayarlayacağız. Controller varsa sayfayı dahil ediyoruz, sonra sınıf ve sonrasında ise metod kontrolü yapıyoruz. Eğer her şey yolundaysa temel noktaya geliyoruz. call_user_func_array() fonksiyonu ile daha önceden belirlenmiş olan controller, action ve parametreleri çağırıyoruz. Sonucunda sistemin yapı taşını oturtmuş oluyoruz.

Örnek call_user_func_array() Fonksiyonu Kullanımı

Bu fonksiyon oldukça iş gören bir fonksiyondur. Basit bir örnekle açıklayayım:

function printMessage($name)
{
  echo "Merhaba {$name}!";
}

call_user_func_array('printMessage', ['Durruti']);

Buradaki fonksiyonu printMessage('Durruti') şeklinde de yazdırabilirdik yalnız bazı spesifik durumlarda bunu kullanamıyoruz o yüzden bu fonksiyona ihtiyaç duyabiliyoruz. Sınıflarla ise, şu şekilde çalışıyor:

class Alert
{
  public function name($name)
  {
    return "Dikkat et {$name}!";
  }
}

echo call_user_func_array([new Alert, 'name'], ['Onur']);

Son örneğimizde ise, daha kolay anlaşılabilmesi adına uygulamada kullandığımıza yakın bir örnek vereceğim. Yalnız bu sadece ufak bir senaryo, bu şekilde kullanmıyoruz:

class postController
{
 public function getAction($id)
 {
 echo 'Gösterilecek gönderi ID: ' . $id;
 }
}

if ($_GET['controller'] === 'post' && $_GET['action'] === 'get') {
 call_user_func_array([new postController, 'getAction'], [5]);
}

Umarım aklınızda yer etmiştir. Bu konuyu biraz daha internette araştırarak yeni şeyler katabilirsiniz kendinize. Bu bölüm şimdilik bu kadar. Önümüzdeki yazıda, kullanıcının ilk karşılaşacağı index.php sayfasını işleyeceğiz.

Görüşmek üzere.

Adım adım:

  1. Yeni Başlayanlar İçin PHP’de MVC
  2. MVC Uygulaması İçin İlk Adımlar: App Sınıfı (Şu an buradasınız)
  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
  6. Proje/Uygulamanın Deposu
  7. Uygulamanın bitmiş halini indirin

Yeni Başlayanlar İçin PHP’de MVC

Nedir bu MVC? Neden ihtiyaç duyulur? Nasıl ve nerelerde kullanılır? gibi birçok soru var kafada. Hele ki yeni bir PHP meraklısıysanız, bu sorular daha çok ve derinden gelir.

Ben, programlamayı vakit geçirmek için kullanan, ortaya bir şeyler çıkarmaya çalışan birisi olarak PHP ile makarna kod yazdığım zamanların ardından MVC ile tanıştım. Tanıştıktan sonra, bir daha görüşmedim çünkü yeterli türkçe kaynak yoktu/bulamamıştım ve tam manasıyla kavrayamamıştım. Buna istinaden şimdi aynı şekilde, bildiğim kadarıyla MVC’yi tarif edeceğim ve birkaç örnek vereceğim. Tariflerim hakkında bilgi alabilmek için, PHP’de biraz olsun OOP yani nesne yönelimli programlama bilginizin olması gerekiyor. Olmasa bile biraz zorlamanızı öneririm.

MVC, Model-View-Controller üçlüsünün baş harfleridir ve bunlar birer katmandır. Katman başka yerlere götürmesin sizi. Üç tane PHP dosyasının birleşimi gibi düşünebilirsiniz. Bu üç PHP dosyası ayrı işlemler yaparak, bizim işimizi kolaylaştırır. Model katmanı, veritabanından verileri çekmemizde bize yardımcı olur. Böylelikle verileri tek bir yerden kontrol etmiş oluruz. View katmanı, görünüm katmanıdır. Yani HTML, CSS, JavaScript gibi kodlarımızı tuttuğumuz katmandır. Controller katmanı ise sistemi kontrol ettiğimiz katmandır. Model katmanından verileri alır, View dosyasına göndeririz, View katmanında verileri listeledikten sonra kullanıcıya View dosyasını sunarız.

Bu yazı ve sonraki yazılarda basit bir MVC sistemi yazacağız. Bu sistemi kodlarken, üzerine basit bir blog sistemi de ekleyeceğiz. Her şeyi en basitinden vurgulayarak anlatacağım bu basit uygulamada, her satırın açıklamasını da yapmaya çalışacağım. Böylelikle ne yapmış olduğumuzu anlayabileceğiz. Sınıf ve metod isimlendirmelerinde Türkçe yerine İngilizce kullanacağım. Böyle yapmamın sebebi, MVC’yi anlamlandırırken İngilizce olarak anlamlandırmamızın daha sonrası için yararlı olacağı.

Yapacaklarımız:

  1. Çekirdek dizininde app sınıfının oluşturulması
    Yani, sistemin bel kemiğini ana klasörde yazacağız.
  2. Çekirdek dizininde model, view ve controller sınıflarının oluşturulması
    Yani, ana klasörde, veri, görünüm ve kontrol işlemlerini yapan kodları yazacağız.
  3. Models dizininde yeni bir model üzerinde çalışmak
    Yani, verileri yöneteceğimiz PHP kodlarında, örnek bir dosya oluşturacağız.
  4. Controllers dizininde yeni bir controller dosyası
    Yani, yeni bir kontrol dosyası oluşturup, adres barındaki URL’ye göre cevap vereceğiz.
  5. Views dizininde görünüm dosyaları yaratmak ve kullanmak
    Yani, kullanıcıların ne göreceğini, CSS ve JavaScript ayarlamalarını yapacağız.

Kabaca bu şekilde.

Projenin/uygulamanın bitmiş ve çalışır halinin deposuna buradan
İndirmek içinse buradan alalım sizi.

Önümüzdeki yazılarda görüşmek üzere.

Yazı dizisi:

  1. Yeni Başlayanlar İçin PHP’de MVC (Şu an buradasınız)
  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
  6. Proje/Uygulamanın Deposu
  7. Uygulamanın bitmiş halini indirin

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

Birçok kod yazıyoruz ve bunları ortaya koyarken herkes farklı yöntemler kullanılıyor. Bu yazıda bazı PHP ipuçlarından bahsedeceğim. Tüm bunlar hem kod kalitemizi arttıracak, hem de bize zaman kazandıracak şeyler olacak.

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', 'foo@bar.com'];
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; // $pagination = 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 $isim\n'; // veya echo 'merhaba\nisim';

// 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[14]) ? '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.

PHP’de explode Fonksiyonu

PHP’de en çok karşıma çıkan fonksiyonlardan birisi olan explode() fonksiyonu hakkında bir kaç kelam etmek gerekiyor. Bu fonksiyonun kullanım amacı, belirli bir karakteri kullanarak bu karakteri içeren dizgeyi parçalara ayırıp dizge haline getirmek.

(PHP 4, PHP 5, PHP 7)
explode — Bir dizgeyi bir ayraca göre bölüp bir dizi haline getirir

Kullanımı

explode(dizge $ayraç, dizge $dizge)

Örneklemelerle kullanımını anlayalım. Örneğin bir formdan etiketler alanı gelmiş olsun. Bu etiketleri virgül yardımıyla parçalara ayırıp dize haline getirelim.

// Örnek POST alan içeriği: php dersleri, fonksiyonlar, nasıl yapılır, içerik
$etiketler = $_POST['etiketler'];

// , karakteriyle dizgeyi bölüyoruz
$etiketlerDizesi = explode(',', $etiketler);
// Her dizge öğesine trim() fonksiyonu uyguluyoruz
// bu fonksiyon, sağdaki ve soldaki boşlukları temizler
$etiketlerDizesi = array_map('trim', $etiketlerDizesi);

// Dizge içeriğini ekrana yazdıralım:
print_r($etiketlerDizesi);

/* 
Sonuç olarak dönen değer şu olacaktır:
Array
(
    [0] => php dersleri
    [1] =>  fonksiyonlar
    [2] =>  nasıl yapılır
    [3] =>  içerik
)
*/

explode Fonksiyonunun Kullanım Alanları

Nerelerde kullanıyoruz bu fonksiyonu hemen söyleyelim. Örneğin, URL barındaki adresi anlamak için kullanıyoruz ya da belli karakterlerle listelenmiş bir yazıyı parçalara bölmek için kullanıyoruz. Ben, örnek olarak basit bir yöntemi anlatacağım. Bu anlatacağım yöntemde URL satırındaki adresi alıp, daha anlaşılabilir hale getireceğiz.

Bahsedeceğim örnekte, modül ve bölüm sistemi olacak. Modüller, anasayfa, haber, galeri gibi kök yöneticiler, bölümler ise alt yöneticileri olacak. Yani örneğin haber modülü için haberGoster, haberEkle gibi ek bölümler olacak.


// Eğer rota varsa rotadır, yoksa kök halindedir
$rota = isset($_GET['rota']) ? $_GET['rota'] : '/';
// Burada explode ile / karakterini kullanara dizgeyi böldük
$rotalar = explode('/', $rota);
// Eğer dize içindeki ilk rota boşsa diziden çıkaralım
// array_shift fonksiyonu dizideki ilk öğeyi çıkarır
if (empty($rotalar[0])) array_shift($rotalar);

// Burada modül eğer varsa ve boş değilse modüldir, değilse geçerli olarak anasayfa'dır
$modul = isset($rotalar[0]) && !empty($rotalar[0]) ? $rotalar[0] : 'anasayfa';
// Burada aynı şekilde modül gibi varsa vardır yoksa giriş'dir
$bolum = isset($rotalar[1]) && !empty($rotalar[1]) ? $rotalar[1] : 'giris';

// Yukarıdaki işlemin diğer bir kullanımı şöyledir:
$modul = null;
if (isset($rotalar[0]) && !empty($rotalar[0])) {
	$modul = $rotalar[0];
} else {
	$modul = 'anasayfa';
}

Yukarıdaki örneği olabildiğince basite indirgediğim için işimizi tam anlamıyla ve sürekli olarak görmeyebilir ama anlamak açısından iyi gelebilir. MVC çatıları sayfanın ne olduğunu explode fonksiyonu ile anlar ve işlemi buna göre yönlendirirler.

PHP’de array_sum Fonksiyonunu Alt Dizelerde Kullanmak

PHP’de array_sum fonksiyonu kullanmam gerekti. Ama normal kök dizide değilde, alt dizideki değerleri toplamam gerekiyordu.

array_sum fonksiyonu ne işe yarar?
Bu fonksiyon bir dize içerisindeki değerlerin toplamını döndürür.

Örnek senaryo, her müşteriye ait tutar/bakiye değerlerini listeleyip hepsinin toplamını en son göstermek.

['Müşteri' => ['Tutar' => 150.00, 'Bakiye' => 360.00]]

<?php
$amount = [];
$balance = [];

foreach ($result as $result) {
    $amount[] = $result['amount'];
    $balance[] = $result['balance'];
    # Listeleme yapacak kod bloğu
}

$totalAmount = array_sum($amount);
$totalBalance = array_sum($balance);

Şeklinde kullanılabilir, kolay gelsin.

PDO’nun En Kolay Hali

Genelde, mysql fonksiyonundan PDO’ya geçişte biraz zorlanılır. Bu yazıda, PHP ile veri çekerken ve eklerken en çok kullandığımız yöntemlerin PDO karşılığını yazacağım.

PDO ile MySQL Veritabanına Bağlanmak

$pdo = new PDO('mysql:host=localhost;dbname=veritabani;charset=utf8', 'kullaniciAdi', 'sifre');

Bu satır, mysql_connect() satırıyla aynı işi hatta daha fazlasını yapar. Hem belirttiğimiz veritabanına bağlanır hem de karakter setini belirler. Böylece türkçe karakter sıkıntısı yaşamayız.

PDO ile Veri Listelemek

$sorgu = $pdo->query('SELECT * FROM tablo');
$ogeler = $sorgu->fetchAll();

foreach ($ogeler as $oge) {
	echo $oge['baslik'] . '<br>';
}

Burada, query() ile sorguyu yazdık ve fetchAll() metodu ile verileri birden fazla yani hepsini çekecek biçimde aldık. Daha sonra foreach() ile sayfaya yazdırdık.

PDO ile Veri Çekmek

PHP’de PDO ile veri çekerken veya listelerken iki yöntemimiz var. Bunlardan birincisi query() metodu diğeriyse önce prepare() yani hazırlama metodu sonrasında execute() yani çalıştır metodu. İlk metodun örneğini veri listelemek bölümünde yaptık, şimdi tekil veri çekerken, ikinci yöntemi yani prepare() metodunu kullanacağız.

$id = $_GET['id'];
$sorgu = $pdo->prepare('SELECT * FROM table WHERE id=?');
$calistir = $sorgu->execute(array($id));
$oge = $calistir->fetch();
echo $oge['baslik'];

Neden ilkinden farkı bir yöntem kullandık?

Çünkü, veritabanı ile bilgi alışverişi yaparken güvenliğe önem vermemiz gerekiyor. Son örnekte, kullanıcıdan gelen bilgiye göre ($id = $_GET['id'] satırına dikkat) bilgi çekiyoruz. Önceleri, yani PDO’dan evvel değişkeni sorgu içine ekleyip de veriler çekiliyordu. Ama SQL Injection dediğimiz açıkları da beraberinde getiriyordu. Biz, bu örnekte önce sorguyu hazırladık, şart yerine ? işareti koyduk ve soru işareti gelen yere $sorgu->execute(array($id)); kısmında göründüğü üzere gelecek verinin $id değişkeni olduğunu söyledik.

Bunun gibi ve bundan farklı birçok örnek mevcut ve kullanıma açık. Mesele mantığı kavramak. Ama en basit hali bu hali. Kolay gelsin. Bir dahaki yazıda, PDO ile veri ekleme, silme ve güncelleme konularına değineceğim.

Esen kalın.

Deprecated: mysql_connect() Hatası Üzerine

PHP’ye yeni başlayanlar, öğrenmek için çaba harcayanlar muhtemelen bu hatayla karşılaşacaklardır. Kafa karıştırmadan hatanın çözümünü söyleyelim…

Hata tam olarak şöyle:

 Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in /dosya/yolu/bilmemne.php on line 1

Bu hatayı alıyorsanız, çok eski ve artık desteklenmeyen bir yöntem kullanıyorsunuz demektir. Hatanın türkçesi:

Kullanımı önerilmiyor: mysql_connect(): msyql uzantısının kullanımı önerilmiyor ve yakın gelecekte bu fonksiyon kaldırılacak. mysqli veya PDO kullanın

Peki neden artık bu fonksiyon desteklenmiyor? Çünkü, bu fonksiyon kullanışlı ve yönetilebilir bir fonksiyon değil. Bunun yerine mysqli ve PDO kullanmak gerekiyor.

mysqli nedir?

Bu da tıpkı mysql fonksiyonları gibi bir fonksiyondur ve PHP’nin mysql fonksiyonlarına alternatifidir, eğer illa ki fonksiyonlarla veri çekeceğim diyorsanız bunu kullanın ama bana kalırsa PDO kullanmanızı öneririm.

PDO nedir?

PDO, PHP’nin bir uzantısıdır. PHP Data Objeleri diye anlayabiliriz. Neden öneriyorum, çünkü bu yöntem daha bir nesne yönelimli ve öğrenince daha da kolayınıza gelecektir. Yakın zamanda PDO hakkında bir şeyler yazmak istiyorum, bakalım.

Sonuç olarak, artık bu fonksiyonu kullanmamanızı öneriyorum.