ساخت یک فریم ورک mvc با php

Pensive-pith

Active Member
MVC چیست؟
MVC یک الگوی طراحی است. الگوی طراحی کدی ساختاریافته است که به شما اجازه می‌دهد کارهای معمول خود را به‌سرعت انجام دهید. شاید به الگوی طراحی همچون یک اسکلت یا چارچوبی که بر روی آن نرم‌افزارتان را خواهید ساخت، بنگرید.
در فریم‌ورک MVC ای که در این آموزش خواهیم ساخت چندین نکته را روشن می‌کنیم. نخست آنکه فریم‌ورک نیازمند یک مدخل ورودی است. همچون index.php؛ این مدخل جایی است که همه‌ی دسترسی‌ها به سایت باید از آن کنترل شود.
برای اطمینان یافتن از اینکه هر دسترسی به سایت محافظت شده است. از htaccess استفاده می‌کنیم تا فایل دیگری قابل دسترسی نباشد. و اینکه ما فایل index.php را از URL پنهان می‌کنیم تا URL های کارپسند(user-friendly) و SEO داشته باشیم.
نشان دادن چگونگی برپاسازی htaccess و mod_rewrite فراتر از محدوده‌ی این آموزش است و برای اطلاعات بیشتر در این باره به راهنمای Apache مراجعه کنید.
خود فایل htaccess اینچنین است:
RewriteEngine on
کد:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^(.*)$ index.php?rt=$1 [L,QSA]
فایل htacceess اجازه‌ی دسترسی به سایت را به URL های همچون این می‌دهد.

کد:
http://www.example.com/news/show
اگر Mod_rewrite را در دسترس ندارید ورود به سایت همانگونه خواهد بود جز اینکه Url تان مقادیر مورد نیاز را دربر خواهد داشت. همچون:.

کد:
http://www.example.com/index.php?rt=news/show
ساختار سایت
در این آموزش به چندین پوشه برای نگهداری اجزای گوناگونی که فریم‌ورک ما را می‌سازند نیاز داریم. فایل index.php و htaccess در بالاترین سطح جای دارند. ما همچنین به پوشه‌ای نیاز داریم که کد نرم‌افزامان را در آن نگهداری کنیم. و پوشه‌های برای Model، View و Controller. ساختار سایت باید همچون زیر باشد.

کد:
html
        application
        controller
        includes
        model
        views
 .htaccess
 index.php
فایل index
همانگونه که پیشتر گفتم، فایل index.php تنها نقطه‌ی یا مدخل ورودی ماست. بدین لحاظ، فضای ایده‌آلی برای تعریف متغیرها و پیکربندی‌های سراسری سایت فراهم می‌کند. در این فایل است که یک فایل کمک‌کننده (helper) را برای ارزش‌دهی‌آغازین (initialize ) برخی مقادیر فراخوانی می‌کنیم. این فایل را init.php می‌نامیم و آن را در پوشه‌ی includes جای می‌دهیم. (همانگونه که در ساختار سایت نشان داده شده است). بنابراین فایل index.php شبیه به کد زیر خواهد بود.

PHP:
<?php 

 /*** error reporting on ***/ 
 error_reporting(E_ALL); 

 /*** define the site path constant ***/ 
 $site_path = realpath(dirname(__FILE__)); 
 define ('__SITE_PATH', $site_path); 

 /*** include the init.php file ***/ 
 include 'includes/init.php'; 

?>
فایل index.php تاکنون تنها error_reporting را تنظیم و همچنین فایل init را شامل کرده (include) است. و ثابتی که مسیر سایت را مشخص می‌کند. با قراردادن این فایل‌ در جای خود می‌توانیم آغاز به ساخت شی registery یا نام‌نویس کنیم. شی registery به دیگر اشیا گذر می‌کند و متغیرهای سراسری را بدون به‌کارگیری از کلمه کلیدی global دربرمی‌گیرد. برای ساخت یک شی registery جدید، ما از init.php ای که در پوشه‌ی includes است سود می‌بریم.
البته، برای ساخت یک شی جدید نیازمند شامل کردن فایل ِ تعریف‌کلاس ِ registery هستیم. در مدت ساخت فریم‌ورک MVC به طور مستقیم فایل‌های کلاس نرم‌افزارمان را شامل خواهیم کرد. هر یک از فایل‌های تعریف کلاس PHP که به وسیله‌ی لایه‌ی Model به کار می‌رود به طور خودکار بارگذاری می‌شود صرف‌نظر از این کار می‌تواند در هنگام نوشتن نرم‌افزارهای بزرگ مایه‌ی زحمت فراوان شود. برای کاهش برخی از این مشکلات PHP تابع __autoload() را برای ما فراهم کرده است.
پس از شامل کردن نرم‌افزار، تابع __autoload() هنگام درخواست، توسط سیستم آنها را بارگذاری می‌کند. به عبارت دیگر هنگامی که کلمه‌ی کلیدی تازه‌ای به کار می‌رود تعاریف کلاس در پوشه‌ای با نام ‌Model ذخیره می‌شوند. فایل includes/init.php می‌بایست اینگونه باشد:

PHP:
<?php 

 /*** include the controller class ***/ 
 include __SITE_PATH . '/application/' . 'controller_base.class.php'; 

 /*** include the registry class ***/ 
 include __SITE_PATH . '/application/' . 'registry.class.php'; 

 /*** include the router class ***/ 
 include __SITE_PATH . '/application/' . 'router.class.php'; 

 /*** include the template class ***/ 
 include __SITE_PATH . '/application/' . 'template.class.php'; 

 /*** auto load model classes ***/ 
    function __autoload($class_name) { 
    $filename = strtolower($class_name) . '.class.php'; 
    $file = __SITE_PATH . '/model/' . $filename; 

    if (file_exists($file) == false) 
    { 
        return false; 
    } 
  include ($file); 
} 

 /*** a new registry object ***/ 
 $registry = new registry; 

?>
در اینجا باید توجه شود که تابع autoload از یک قرارداد نام‌گذاری برای فایل‌های تعریف کلاس، که باید شامل شوند استفاده می‌کنند.
همه‌ی آنها باید با .class.php پایان یابند و نام کلاس باید نام فایل .class.php را نداشته باشد. پس برای ساختن شی تازه‌ای با نام news فایل تعریف‌کلاس باید news.class.php و نام کلاس news باشد. با جای دادن این فایل‌ها نیمی از راه را رفته‌ایم. هر چند که فریم‌ورک‌مان چیز خاصی نیست. در واقع، اگر اکنون بخواهیم به فایل index.php دسترسی یابیم با خطاهای بسیاری درباره‌ی نبود فایل‌ها روبرو خواهیم شد. که عمدتا درون پوشه‌ی application هستند. پس بیاید شروع به ساخت این فایل‌ها می‌کنیم که هر کدام می‌توانند تهی از محتوا یا دارای کد زیر باشند.

PHP:
<?php 

?>
فایل‌های که باید در پوشه‌ی application ساخته شوند عبارتند از:

کد:
controller_base.class.php 
registry.class.php 
router.class.php 
template.class.php
توجه کنید اگر چه این فایل‌ها به طور خودکار بارگذاری نمی‌شوند اما هنوز همان قرارداد نام‌گذاری رعایت می‌شود.

شی نام‌نویس (Registry)

رجیستری شی است که مقادیر سراسری می‌توانند از طریق آن بدون نیاز به کلمه کلیدی global ذخیره شوند. با گذر دادن شی رجیستری به کنترلرهایی که آن را نیاز دارند از پیچیده کردن نام متغیرها دوری می‌جویم و متغیرهایمان را به طور امن منتقل می‌کنیم. ما نیازمند به تنظیم متغیرهای رجیستری هستیم تا بتوانیم آنها را به کنترلرها بدهیم. توابع جادویی __set() و __get() برای این منظور ایده‌آل هستند. پس فایل registry.class.php را در پوشه‌ی application باز کنید و کدهای زیر را در ان بنویسید.

PHP:
<?php 

Class Registry { 

 /* 
 * @the vars array 
 * @access private 
 */ 
 private $vars = array(); 


 /** 
 * 
 * @set undefined vars 
 * 
 * @param string $index 
 * 
 * @param mixed $value 
 * 
 * @return void 
 * 
 */ 
 public function __set($index, $value) 
 { 
        $this->vars[$index] = $value; 
 } 

 /** 
 * 
 * @get variables 
 * 
 * @param mixed $index 
 * 
 * @return mixed 
 * 
 */ 
 public function __get($index) 
 { 
        return $this->vars[$index]; 
 } 

} 

?>
با قرار دادن رجیستری در جای خود، سامانه‌ی ما آماده به کار است. این سامانه کاری را انجام یا نمایش نمی‌دهد. اما یک سامانه‌ی در حال کار داریم. توابع جادویی __set() و __get() اکنون به ما اجازه می‌دهند که متغیرهای درون رجیستری را تنظیم و ذخیره کنیم. حال زمان افزودن کلاس‌های رهیاب (router) و Model است.

مدل ( (Model

Model حرف M در سرنام MVC است. مدل جایی است که در آن بیزینس لوجیک (business logic) ذخیره شده است. بیزینس لوجیک به عنوان ارتباطات ِ پایگاه داده‌ی یا ارتباطاتی به منابع داده‌ای تعریف شده است. و داده‌هایی را برای کنترلر فراهم می‌کنند. اما چون من علاقه‌مند به CAV هستم.( Controller – Action -View) پیوند میان کنترلر و مدل را چندان آشکار نخواهم کرد. ارتباط پایگاه‌داده‌مان از طریق یک الگوی طراحی (Design pattern) با نام یکتا (Singleton) است. که در پوشه‌ی classes جای دارد و می‌تواند به طور ایستا از کنترلر فراخوانی شده و در رجیستری تنظیم شود. کد زیر را درinit.php که پیش از این ساختیم بیفزایید.

PHP:
<?php 

 /*** create the database registry object ***/ 
 $registry->db = db::getInstance(); 

?>
همچون همگی اعضای رجیستری، پایگاه‌داده نیز به طور سراسری در دسترس اسکریپت‌های‌مان است. چون کلاس یکتاست. ما همیشه همان نمونه را دریافت می‌کنیم. اکنون شی‌هایی رجیستری می‌تواند در هنگام نیاز متدی از کنترلر را بارگذاری کنند.

رهیاب (Router)

کلاس رهیاب وظیفه‌ی بارگذاری کنترلر صحیح را بر عهده دارد و کار دیگری انجام نمی‌دهد. مقدار کنترلر توسط URL مشخص می‌شود. آنURL شبیه به زیر است:
http://www.example.com/index.php?rt=news

یا اگر از htaccess یا mod_rewrite استفاده کنید شبیه به زیر می‌شود:

http://www.example.com/news

همانگونه که می‌بینید مسیر متغیر rt است که با news مقداردهی شده است. برای اجرای کلاس router برخی چیزها را باید تنظیم کنیم. کد زیر را به فایل router.class.php در پوشه‌ی application بیفزایید.

PHP:
<?php 

class router { 
 /* 
 * @the registry 
 */ 
 private $registry; 

 /* 
 * @the controller path 
 */ 
 private $path; 

 private $args = array(); 

 public $file; 

 public $controller; 

 public $action; 

 function __construct($registry) { 
        $this->registry = $registry; }
کار چندانی را انجام نمی‌دهد اما برای شروع به کار کافیست. می‌توانیم همچنین رهیاب را در رجیستری بارگذاری کنیم. کد زیر را به index.php بیفزایید.

حال که کلاس router را بارگذاری کردیم می‌توانیم کارمان را با افزودن متدی برای تنظیم ِ مسیر پوشه‌ی controller دنبال کنیم. کد زیر را به router.class.php بیفزایید.

PHP:
<?php 
 /** 
 * 
 * @set controller directory path 
 * 
 * @param string $path 
 * 
 * @return void 
 * 
 */ 
 function setPath($path) { 

        /*** check if path i sa directory ***/ 
        if (is_dir($path) == false) 
        { 
                throw new Exception ('Invalid controller path: `' . $path . '`'); 
        } 
        /*** set the path ***/ 
        $this->path = $path; 
}
و برای تنظیم مسیر کنترلر در رجیستری خط زیر را به index.php بیفزایید.
PHP:
/*** set the path to the controllers directory ***/ 
 $router->setPath (__SITE_PATH . 'controllers');
با تنظیم مسیر کنترلر می‌توانیم کنترلر را بارگذاری کنیم. متدی با نام loader() خواهیم ساخت که کنترلر می‌گیرد و بارگذاری می‌کند. این متد ، متد getController() را فراخوانی می‌کند. تا تصمیم بگیر که کدام کنترلر را بارگذاری کند. اگر کنترلری یافت نشد آنگاه به طور قراردادی به Index باز می‌گردد. متد loader اینچنین است:

PHP:
<?php 

 /** 
 * 
 * @load the controller 
 * 
 * @access public 
 * 
 * @return void 
 * 
 */ 
 public function loader() 
 { 
        /*** check the route ***/ 
        $this->getController(); 

        /*** if the file is not there diaf ***/ 
        if (is_readable($this->file) == false) 
        { 
                echo $this->file; 
                die ('404 Not Found'); 
        } 

        /*** include the controller ***/ 
        include $this->file; 

        /*** a new controller class instance ***/ 
        $class = $this->controller . 'Controller_'; 
        $controller = new $class($this->registry); 

        /*** check if the action is callable ***/ 
        if (is_callable(array($controller, $this->action)) == false) 
        { 
                $action = 'index'; 
        } 
        else 
        { 
                $action = $this->action; 
        } 
        /*** run the action ***/ 
        $controller->$action(); 
 }
متد getController ای که متد loader() فراخوانی کرده کار را انجام می‌دهد. با گرفتن مقادیر router از URL توسط $_GET[' rt'] می‌توان کنترلری را بارگذاریکرد یا در صورت نبود کنترلر آن را به کنترلر index تحویل داد. همچنین این متد بررسی می‌کند که آیا کُنشی (Action) بارگذاری شده بود یا خیر؟ یک کنش متدی است که درون کنترلر ِ مشخص‌شده وجود دارد. اگر کنش تعریف نشده باشد. به طور پیش‌فرض index است. متد getController را به فایل router.class.php بیفزاید:
PHP:
<?php 
 /** 
 * 
 * @get the controller 
 * 
 * @access private 
 * 
 * @return void 
 * 
 */ 
private function getController() { 

        /*** get the route from the url ***/ 
        $route = (empty($_GET['rt'])) ? '' : $_GET['rt']; 

        if (empty($route)) 
        { 
                $route = 'index'; 
        } 
        else 
        { 
                /*** get the parts of the route ***/ 
                $parts = explode('/', $route); 
                $this->controller = $parts[0]; 
                if(isset( $parts[1])) 
                { 
                        $this->action = $parts[1]; 
                } 
        } 

        if (empty($this->controller)) 
        { 
                $this->controller = 'index'; 
        } 

        /*** Get action ***/ 
        if (empty($this->action)) 
        { 
                $this->action = 'index'; 
        } 

        /*** set the file path ***/ 
        $this->file = $this->path .'/'. $this->controller . '.php'; 
} 
?>
کنترلر

Controller حرف C از سرنام MVC است. کنترلر پایه، یک کلاس انتزاعی (Abstract) ساده است که ساختار همه‌ی کنترلرها را تعریف می‌کند. با شامل کردن رجیستری در اینجا رجیستری در دسترس همه‌ی کلاس‌هایی که از کنترلر پایه گسترش (Extend) می‌یابند قرار دارد. یک متد index نیز در کنترلر پایه شامل شده است. بدین معنا که همه‌ی کلاس‌های کنترلری که از کلاس کنترلر پایه گسترش می‌یابند. باید یک متد Index در خود داشته باشند. کد زیر را به controller.class.php در پوشه‌ی Application بیفزایید:
PHP:
<?php 

Abstract Class baseController { 

/* 
 * @registry object 
 */ 
protected $registry; 

function __construct($registry) { 
        $this->registry = $registry; 
} 

/** 
 * @all controllers must contain an index method 
 */ 
abstract function index(); 
} 

?>
هنگام ساخت یک کنترلر می‌توانیم یک کنترلر index و یک کنترلر Blog بسازیم. کنترلر index پیش‌فرض سامانه‌ی ماست و از اینجاست که نخستین صفحه بارگذاری می‌شود. کنترلر Blog را برای ساخت یک کنترلر فرضی از ماژول Blog در نظر می‌گیریم. هنگامی که ماژول Blog در URL مشخص شد:



کد:
http://www.example.com/blog
آنگاه متد Index در کنترلر Blog فراخوانی می‌شود. متد نمایش (View) نیز در کنترلر Blog ساخته خواهد شد و اینگونه در URL تعریف می‌شود.


کد:
http://www.example.com/blog/view
سپس متد view در کنترلر Blog بارگذاری خواهد شد. نخست بگذارید کنترلر index را ببینیم. این کنترلر در پوشه‌ی controller جای دارد.

PHP:
<?php 

class indexController extends baseController { 

public function index() { 
    /*** set a template variable ***/ 
        $this->registry->template->set ('welcome', 'Welcome to PHPRO MVC'); 

    /*** load the index template ***/ 
        $this->registry->template->show('index'); 
} 

} 

?>
لاس indexController بالا نشان می‌دهد که IndexController از کلاس baseController گشترش یافته است. در نتیجه رجیستری می‌تواند بدون نیاز به متغیرهای سراسری در دسترس باشد. کلاس indexController همچنین دارای متد الزامی Index است که همگی کنترلرها باد آن را داشته باشند. درون متد index متغیری با نام Welcome در رجیستری تنظیم شده است. این متغیر هنگامی که توسط متد template->show بارگذاری شود در دسترس بخش template خواهد بود. کلاس blogController از همان فرمت پیروی می‌کند. اما یک ضمیمه‌ی کوچک دارد که آن متد View است. این متد نمونه‌ای است اینکه چگونه یک متد به جز متد index می‌تواند فراخوانی شود. متد View توسط URL بارگذاری می‌شود.

نما View

همانگونه که تاکنون حدس زده‌اید View حرف V در سرنام MVC است. View دارای کدهایی است که مربوط به نمایش و منطق نمایش هستند، همچون قالب‌گذاری و کش کردن. در کنترلر بالا متد Show را می‌بینیم. این متد View را فراخوانی می‌کند. جز اصلی در PHPROMVC (فریم‌ورکی که هم‌اکنون در حال ساخت آنیم) کلاس template است. فایل
template.class.php دارای تعریف کلاس است. همچون دیگر کلاس‌ها برای این بخش نیز رجیستری در دسترس است و دارای یک متد __set() است که در آن متغیرهای template می‌توانند تنظیم و یا ذخیره شوند. متد Show موتور لایه نمایش است. متدی است که خود، template را بارگذاری می‌کند. و متغیرهای template را در دسترس می‌سازد. برخی فریم‌ورک‌هایی MVC بزرگ، یک زبان برای Template پیاده‌سازی ‌می‌کنند که این کار لایه‌ای اضافی را ایجاد می‌کند. افزودن لایه‌ها برابر است با بار اضافی. ما در اینجا خواها سرعت ِ بارگذاری template هستیم. بنابراین از ساخت این زبان صرف‌نظر می‌کنیم. این کار سبب می‌شود که طراحان‌وب بدون کمترین نیاز به دانستن PHP یا یک زبان Template وب‌سایت را طراحی کنند. فایل template.class.php همانند زیر است:

PHP:
<?php 

Class Template { 

/* 
 * @the registry 
 * @access private 
 */ 
private $registry; 

/* 
 * @Variables array 
 * @access private 
 */ 
private $vars = array(); 

/** 
 * 
 * @constructor 
 * 
 * @access public 
 * 
 * @return void 
 * 
 */ 
function __construct($registry) { 
        $this->registry = $registry; 

} 


 /** 
 * 
 * @set undefined vars 
 * 
 * @param string $index 
 * 
 * @param mixed $value 
 * 
 * @return void 
 * 
 */ 
 public function __set($index, $value) 
 { 
        $this->vars[$index] = $value; 
 } 


function show($name) { 
        $path = __SITE_PATH . '/views' . '/' . $name . '.php'; 

        if (file_exists($path) == false) 
        { 
                throw new Exception('Template not found in '. $path); 
                return false; 
        } 

        // Load variables 
        foreach ($this->vars as $key => $value) 
        { 
                $$key = $value; 
        } 

        include ($path); 
} 


} 

?>
قالب (template)

قالب‌ها اصولا فایل‌های HTML هستند که اندکی کد PHP در آن نوشته شده است. بر این گمان نباشید که باید کاملا HTML و PHP را جداسازی کنیم. به یاد داشته باشید که PHP خو یک زبان اسکریپتی قابل‌جاسازی (embeddable ) است. در واقع، PHP مجمعه‌ی از وظایف است که برای ساخت یک زبان قالب‌گذاری طراحی شده است. فایل‌های Template متعلق به پوشه‌ی views است. کد زیر متعلق به index.php است.



PHP:
<h1><?php echo $welcome; ?></h1>

و کد زیر متعلق به blog_index.php است



PHP:
<h1><?php echo $blog_heading; ?></h1>

و سرانجام فایل blog_view.php



PHP:
<h1><?php echo $blog_heading; ?></h1>



PHP:
<p><?php echo $blog_content; ?></p>

توجه کنید که در فایل‌های Template بالا نام متغیرها در قالب‌ها مطابق است با متغیرهای Template ای که در کنترلر ساخته شده است.

منبع: http://www.phpro.org/tutorials/Model...oller-MVC.html
سورس: http://www.phpro.org/downloads/mvc-0.0.3.tar.gz
 

جدیدترین ارسال ها

بالا