Başlarken;
Yakın zamanda ” MVC 5N1K? “ başlıklı bir makale yayınlamıştım. Şimdi ise MVC yapısını hayata geçirelim. Bu makalede MVC’nin temel yapısına ve PHP temelli bir MVC Framework’ü yazmaya odaklanacağız. Bu makalenin amacı OOP yardımı ile temel MVC yapısını öğretmektir.
Temel Gereksinimler;
- PHP OOP (Nesnesel Porgramlama)
İzlenecek Yol Haritası;
- MVC Nedir?
- Dizinlerin oluşturulması.
- Core > Application.php oluşturulması.
- Controller > HomeController.php oluşturulması.
- View > Home/Index.html oluşturulması.
- Model > Database.php oluşturulması.
- Kaynak Kodlar
MVC Nedir?
” MVC 5N1K? “ makalesinde buna ince ince değinmiştik. Burada da üzerinden kısaca bir geçelim. MVC; açılımı ile “Model View Controller” anlamına gelir. Bu yapılar birer katman görevi görür. Bu katmanları inceleyelim.
- Model: Veritabanı ilişkileri (Veri çekme, veri güncelleme, veri girişi v.s) bu katmanda gerçekleşir.
- View: Kullanıcının etkileşime geçeceği arayüz bu katmanda oluşturulur. Genel olarak HTML, CSS, JS dosyaları bu katmandadır.
- Controller: Model ve View arasındaki ilişkiyi sağlayan katmandır. Nesnesel programlamadan bilindiği üzere sınıflar metotlardan oluşur. Controller aslında bir sınıftır. içerisindeki metotlar ise “Action” olarak adlandırılır. Model katmanından bilgileri alır ve View katmanına yönlendirir ya da View katmanından bilgileri alır ve Model katmanına yönlendirir.
- Router: İstemcinin gönderdiği adreslerin gerekli Controller ve Action yönlendirilmesi bu katmanda yapılır.
Dizinlerin Oluşturulması;
İşlemlere başlamadan önce bir PHP IDE’si kullanmanızı önerebilirim. Dosya yolları ve syntax önerilerinde işinizi kolaylaştıracaktır. Ben “PHPStorm” kullanıyorum. Başlayalım.
Kalbimiz kadar temiz bir proje oluşturmak ilk işimiz olacaktır. Proje adımız “php_mvc”.
Çalışma alanımızın şu an ki hali aşağıdaki gibidir. ("*" içeren dosyalar/dizinler anlatılan bölümde oluşturulmuştur.)
/php_mvc
/App *
/Model *
/View *
/Controller *
/Core *
/Public *
Core > Application.php Oluşturulması;
Bilgilendirme: Bu kısımda işlemlere başlamadan önce kullandığınız server uygulamasına çalıştığımız “php_mvc” alanındaki “Public” dizininde çalıştırmalıyız. Bilgili arkadaşlar local server üzerinde bunu yapabilirler. Makalemde Terminal ya da CMD üzerinden bu işlemi yapacağız.
Core dizini bizim “çekirdek” olarak adlandırılacak dizinimiz. Bu dizin Framework’ümüzün temeli diyebiliriz. Ana işlemlerimizin hepsi bu dizinde gerçekleşecek.
Application.php dosyamızı yazmaya başlamadan önce “App” dizini içerisinde “Globals.init.php” adında bir dosya oluşturun. Bu dosya içerisinde tüm yapı içerisinde kullanacağımız “global değişkenler” bulunacak. Model, View, Controller, Application dizinlerine ulaşmamızı kolaylaştıracak.
Çalışma alanımızın şu an ki hali aşağıdaki gibidir. ("*" içeren dosyalar anlatılan bölümde oluşturulmuştur.)
/php_mvc
/App
/Model
/View
/Controller
/Core
Globals.init.php *
/Public
# Globals.init.php dosyası aşağıdaki gibidir.
<?php
define("ROOT", __DIR__.DIRECTORY_SEPARATOR);
define("MODEL", ROOT."Model".DIRECTORY_SEPARATOR);
define("VIEW", ROOT."View".DIRECTORY_SEPARATOR);
define("CONTROLLER", ROOT."Controller".DIRECTORY_SEPARATOR);
define("CORE", ROOT."Core".DIRECTORY_SEPARATOR);
?>
Application.php dosyamız MVC yapısındaki “Router” katmanı gibi düşünebiliriz. İstemciden aldığımız adresler bu kısımda Controller’a yönlendirilecek.
// Çalışma alanımızın şu an ki hali aşağıdaki gibidir. ("*" içeren dosyalar anlatılan bölümde oluşturulmuştur.)
/php_mvc
/App
/Model
/View
/Controller
/Core
Application.php *
Globals.init.php
/Public
// Application.php dosyası aşağıdaki gibidir.
<?php
class Application {
protected $controller = "HomeController";
protected $action = "IndexAction";
protected $parameters = array();
public function __construct(){
$this->ParseURL();
// Eğer Controller dosyası varsa dosyayı dahil et.
if(file_exists(CONTROLLER.$this->controller.".php")){
require_once (CONTROLLER.$this->controller.".php");
// Dahil edilen Controller sınıfından yeni bir Controller oluştur.
$this->controller = new $this->controller;
// Oluşturulan Controller içerisinde Action varsa Action çağır.
if(method_exists($this->controller, $this->action)){
call_user_func_array([$this->controller, $this->action], $this->parameters);
} else {
echo "Böyle Bir Action Yok.";
}
} else {
echo "Böyle Bir Controller Yok.";
}
}
/**
* ParseURL methodu genel mantığı ile şu işlemleri yapar;
*
* $_SERVER["REQUEST_URI"] yardımı ile istemci tarafından gönderilen URL yakalanır.
*
* trim() fonkisyonu ile URL sonunda bulunursa "/" karakteri temizlenir.
*
* explode() fonksiyonu ile URL "/" karakterine göre dizileştirilir.
*
* $url değişkeni bir dizi olur. [0] => Controller Adı, [1] => Action Adı, [2} ve Sonrası => Parametreler
*
* unset() fonksiyonu ile $url değişkeninde varsa [0] ve [1] indis numaralı elemenlar temizlenir.
* Geriye kalan değerler parametrelerdir.
*/
protected function ParseURL(){
$request = trim($_SERVER["REQUEST_URI"], "/");
if (!empty($request)){
$url = explode("/", $request);
$this->controller = isset($url[0]) ? $url[0]."Controller" : "HomeController";
$this->action = isset($url[1]) ? $url[1]."Action" : "IndexAction";
unset($url[0], $url[1]);
$this->parameters = !empty($url) ? array_values($url) : array();
} else {
$this->controller = "HomeController";
$this->action = "IndexAction";
$this->parameters = array();
}
}
}
?>
Application.php ile istemcinin gönderdiği adresteki Controller > Action ikilisini elde etmiş olduk. call_user_func_array() fonksiyonu ile bu ikiliyi çağırmış olduk. Sonuç olarak sistem çalışmaya başlayacak. Fakat nerede? Bir “Index.php” dosyasına ihtiyacımız var. “App” dizini içerisinde “Index.php” dosyası oluşturalım. Kullanıcılarımızın güvenlik sebebi ile “App” dizinine erişimi olmayacağını biliyoruz bu sebeple kullanıcının etkileşime geçeceği dizin olan “Public” içerisinde de “Index.php” oluşturalım.
Çalışma alanımızın şu an ki hali aşağıdaki gibidir. ("*" içeren dosyalar anlatılan bölümde oluşturulmuştur.)
/php_mvc
/App
/Model
/View
/Controller
/Core
Application.php
Globals.init.php
Index.php *
/Public
Index.php *
// App/Index.php dosyası aşağıdaki gibidir.
<?php
require_once("Globals.init.php");
require_once(CORE . "Application.php");
new Application();
?>
// Public/Index.php dosyası aşağıdaki gibidir.
<?php
require_once ("../App/Index.php");
?>
Artık “Application.php” dosyamız kullanıma uygun ve çalışır bir halde geldi. Sıradaki bölümde bir Controller oluşturarak devam edeceğiz.
Controller > HomeController.php Oluşturulması;
Kullanıcının girdiği URL adresini düşünelim;
URL: http://localhost:8080/Home/Index
URL: http://localhost:8080/Blog/Index
URL: http://localhost:8080/Home/Contact
Örnek birkaç URL yukarıda verdik. Biz biliyoruz ki bizim gözümüzde bu URL adresleri şu şekilde;
URL: http://localhost:8080/Controller/Action/Parameters
“Application.php” zaten bu Controller ve Action yönlendirmelerini yapıyordu. Bize şu an gerekli olan “BlaBlaController.php” şeklinde bir dosya.
Controller’ın ne yaptığını tekrar hatırlayalım. Kullanıcıya View sunmak veya View’dan gelen verileri Model’e iletmekti. Bu işlemleri yapacak bir çekirdek “View” ve çekirdek “Controller” sınıfına ihtiyacımız olacak.
Çalışma alanımızın şu an ki hali aşağıdaki gibidir. ("*" içeren dosyalar anlatılan bölümde oluşturulmuştur.)
/php_mvc
/App
/Model
/View
/Controller
/Core
Application.php
Contoller.php *
View.php *
Globals.init.php
Index.php
/Public
Index.php
// Conroller.php dosyası aşağıdaki gibidir.
<?php
require_once(CORE . "View.php");
class Controller {
protected $view;
public function View($view_name, $model = []){
$this->view = new View($view_name, $model);
return $this->view->Render();
}
public function Redirect($path)
{
header("Location: {$path}");
}
}
?>
// View.php dosyası aşağıdaki gibidir.
<?php
class View {
protected $view_file;
protected $view_model;
public function __construct($view_file, $view_model){
$this->view_file = $view_file;
$this->view_model = $view_model;
}
public function Render(){
if(file_exists(VIEW.$this->view_file.".php")){
extract($this->view_model);
ob_start();
ob_get_clean();
include_once (VIEW.$this->view_file.".php");
}
}
}
?>
Oluşturduğumuz Controller sınıfının methodları temel olarak şu işlemleri yapacaktır;
View($view_name, $model = []): Görüntülenecek dosya yolu $view_name ile istemciden gelen parametreler ise $modelile yakalanır. Bu parametreler ile yeni bir View sınıfı oluşturulur ve Render() methodu çalıştırılır. extract() fonksiyonu dizi içi indisleri değişken olarak yönlendirir. Bizde bu oluşan değişkenleri View dizinimizdeki HTML dosyalarında kullanırız. Örnek verecek olursam extract() şu işlemi yapar;
<?php
$dizi = ("adi" => "Yasin");
extract = ($dizi);
echo $adi;
// Çıktı olarak "Yasin" değerini görürüz.
?>
Çekirdek “Controller” ve Çekirdek “View” sınıflarımız hazır olduğuna göre “HomeController.php” dosyasını oluşturabiliriz.
Çalışma alanımızın şu an ki hali aşağıdaki gibidir. ("*" içeren dosyalar anlatılan bölümde oluşturulmuştur.)
/php_mvc
/App
/Model
/View
/Controller
HomeController.php *
/Core
Application.php
Contoller.php
View.php
Globals.init.php
Index.php
/Public
Index.php
// HomeController.php dosyası aşağıdaki gibidir.
<?php
require_once(CORE . "Controller.php");
class HomeController extends Controller {
public function IndexAction(){
$this->View("/Home/Index");
}
}
?>
View > Home/Index.html Oluşturulması;
Kullanıcının girdiği URL adresini aşağıdaki gibi olduğunda /Home/Index çalışacak;
URL: http://localhost:8080/Home/Index
URL: http://localhost:8080/
Peki kullanıcıya Controller neyi View edecek? Tabi ki bir View dosyasına ihtiyacımız var.
Çalışma alanımızın şu an ki hali aşağıdaki gibidir. ("*" içeren dosyalar anlatılan bölümde oluşturulmuştur.)
/php_mvc
/App
/Model
/View
/Home *
Index.php *
/Controller
HomeController.php
/Core
Application.php
Contoller.php
View.php
Globals.init.php
Index.php
/Public
Index.php
// View/Home/Index.php dosyası aşağıdaki gibidir.
<h1>Merhaba ben /Home/Index diziniyim!</h1>
Her şey hazır. İlk testimizi yapmak için “Windows Tuşu + R” ile “Çalıştır” penceresini açalım. “php_mvc” isimli ana çalışma klasörümüzün içindeki “Public” dizinine “cd” komutu ile ulaşalım. (Görsel tamamen örnektir. Dosya yolunuz farklı olabilir.). Bu klasör içerisinde bir local sunucu oluşturalım.
php -S localhost:8080
Herangi bir tarayıcımızın URL çubuğuna aşağıdaki adres ile bağlanırsak;
https://localhost:8080/Home/Index
Ta Daaaaaa! MVC yapımız çalışıyor. Mutlu son diyebiliriz.
Model > Database.php Oluşturulması;
Geriye kalan tek adım veri tabanı bağlantısı yapacak olan Model dosyamız. “Model” dizini altında “Database.php” adında bir dosya oluşturalım.
Çalışma alanımızın şu an ki hali aşağıdaki gibidir. ("*" içeren dosyalar anlatılan bölümde oluşturulmuştur.)
/php_mvc
/App
/Model
Database.php *
/View
/Home
Index.php
/Controller
HomeController.php
/Core
Application.php
Contoller.php
View.php
Globals.init.php
Index.php
/Public
Index.php
// Database.php dosyası aşağıdaki gibidir.
<?php
// Veritabanı bilgileri bu kısımdan güncellenebilir.
define("HOST", "localhost");
define("DBNAME", "database_adi");
define("UNAME", "kullanici_adi");
define("PASSWD", "sifre");
define("CHARSET", "utf8");
class Model extends PDO
{
public function __construct()
{
try {
parent::__construct("mysql:host=" . HOST . ";dbname=" . DBNAME, UNAME, PASSWD);
$this->query('SET CHARACTER SET ' . CHARSET);
$this->query('SET NAMES ' . CHARSET);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
} catch (PDOException $error) {
$error->getMessage();
}
}
public function fetchAll(){
return $this->query("SELECT * FROM `tablo_adi`", PDO::FETCH_ASSOC);
}
}
?>
Kaynak Kodlar;
PHP – MVC Framework konusu için oluşturduğum kaynak kodlar için tıklayınız. (Kaynak: GitHub – yasinerarslan – PHP-MVC).
Yasin Erarslan
Umarım ki “PHP – MVC Framework Oluşturma” makalesi hoşunuza gitmiştir. Keyifli ve Bol Bug’lu Günler Dileri 🙂
Valla ağzına eline sağlık abim ya çok sade ve güzel anlatmışsın. Senden php dersi almak isterdim valla 🙂
Değerli yorumun ve desteğin için çok teşekkür ederim 🙂
Merhaba hocam, elinize sağlık çok sade ve temiz bir yazı olmuş. Ancak resimler görünmüyor, içeriğinizi güncelleme şansınız var mı ?
Uzun süredir ilgilenme şansım olmamıştı. Hosting taşıma sırasında sanırım tüm görsellerim silinmiş ama bu yorumu görmek beni tekrardan elden geçirmem için dürtükledi. İvedilikle ilgileniyorum. Kıymetli yorumunuz için çok ve çok teşekkür ederim 🙂
Merhaba
Öncellikle hocam ellerinize sağlık çok güzel bir anlatım olmuş teşekkür ederim..
Benim bu konuyla ilgili aklımda çok sorular var.
1. Sorum: SEF linki htaccess ile sağlıyarak yapıyorduk. Ben örnek veriyorum, profile.php içerisinde GET ile 5 bölüm oluşturacağım örnek olarak (şifre değiştirme, adres, üye bilgiler, siparişler, iadeler) bunları mvc ile nasıl yapacağım hiç aklımda fikir oluşmuyor.
Mai ile ulaşırsanız çok seviniirim.
Merhaba, emeğine sağlık. Şöyle bir sorun var ; Application.php dosyasında require_once (CONTROLLER.$this->controller.”.php”); satırında .”.php” kısmında bulunamadı diye hata veriyor. Phpstorm ‘da üzerine gelince App// klasöründen sonra iki tane ” / ” işareti çıkıyor. Bunun sebebi nedir ?
PHPStorm’un dosya indexlemesinden kaynaklı bir sorun sadece. Yapınızın çalışmasına karşı bir sorun oluşturmuyor. Sebebine gelince ortada daha oluşturulmamış bir “$this->controller” var tabi doğal olarak böyle bir dosya bulamıyor 🙂
Peki Controller/HomeController.php dosyasını görmesi gerekmiyor mu ? Teşekkür ederim.
Yorumunu okurken aklıma ilk bu yapıyı anlamaya çalışırken ki halim geldi 😀 Kod yazarken altı çizili yerleri ve uyarıları görmeyi pek sevmem bende. Mantıken evet haklısın. Çünkü kodda da eklediğimiz gibi ilk ataması “HomeController” olarak alınıyor. Fakat anlamadığım bir şekilde sadce uyarı. Bir hata değil. Ben tamamen PHPStorm’dan kaynaklı olarak düşünüyorum. “Hacı bak burada bu olmaya bilir haberin olsun.” diyor. Biz zaten dosya kontrolünü sağladıktan sonra erişim yapmaya çalışıyoruz. Bu sebeple bir hata yaşamazsın. Eklediğin yorumda da dediğin gibi bu rehber temel seviyede MVC yapısını anlatmaya çalışıyor 🙂 Projelerde seni yarı yolda koymaz fakat üzerine eklenmesi gereken bolca yapılar bulunabilir. Bu tamamen sana kalmış 🙂 Tekrar eklemek isterim. Sadece bir uyarı dikkate alma 😀
Evet kod yazarken altı çizili yerleri ve uyarıları görmeyi sevmiyorum. Ama takılıp kalmamalı. Teşekkür ederim.