Tải bản đầy đủ (.docx) (18 trang)

Xây dựng mô hình MVC chuẩn cho ứng dụng web php

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (120.91 KB, 18 trang )

MVC là một mô hình chuẩn. Một mẫu thiết kế mà giúp tạo nên một cấu trúc mã cho phép
cho các khuôn khổ ứng dụng nhân rộng một cách nhanh chóng. Bạn có thể nghĩ đây như
một khung sườn, một khuôn khổ mà trên đó các ứng dụng của bạn sẽ được xây dựng.

Trích dẫn:Tại sao ta phải viết theo mô hình MVC? Mô hình MVC có những lợi ích gì? Và
hầu hết các CMS hiện đại như: joomla, e-commence, drupal, e-shop, open cart; và cả các
framework nổi tiếng như: CI (CodeIgniter), CakePHP, Zend, Yii đều phải tuân theo mô
hình MVC này?
Để không mất thời gian, tôi chỉ hướng dẫn các bạn tổ chức 1 project MVC, các bạn có thể
tham khảo video về mô sau để hiểu rõ thêm về mô hình
này: />Và các bạn sẽ hay đặt câu hỏi phân vân là tôi nên học framework gì? Joomla, Drupal, CI,
CakePHP hay Zend,...? Theo kinh nghiệm của tôi là bạn mà hãy bắt đầu với MVC chuẩn
mà tôi sẽ hướng dẫn sau đây để hiểu cơ chế làm việc sau đó có thể chọn 1 trong các
framework (FW) trên để học, vì các CMS hay FW này đều tuân theo mô hình MVC thì chỉ
khác nhau về các phương thức hỗ trợ thôi, chứ về bản chất và cấu trúc các phần (MVC)
đều giống nhau cả! Theo tôi thì nên chọn từ FW dễ đến khó để học được cái hay của mỗi
FW, hãy bắt đầu với CI hoặc CakePHP, mình đánh giá cao về CI (chạy nhanh, dễ phát
triển và đặt biệt đa số LTV ở VN hay sài FW này)

Giới thiệu: Đây là bài hướng dẫn cách xây dựng 1 MVC framework đơn giản từ mới
bắt đầu đến kết thúc.
Theo như hình vẽ ở trên các bạn sẽ thấy các lớp như: Controller, Model, View được tách
ra rõ rệt: Client request đến Controller, Controller sẽ quyết định có nên sử dụng Model
hay trực tiếp qua View

Trong loạt hướng dẫn sau, tôi sẽ liệt kê 1 số điểm quan trọng, như việc đầu tiên mà
framework cần làm là tạo điểm bắt đầu (vd: index.php), đó là nơi mà cho phép tất cả các
controller có thể thông qua và hoạt động.
Để làm điều này ta sử dụng mod_rewrite, 1 mặt giúp ta có thể tạo ra điểm bắt đầu để



các controller hoạt động, 1 mặt giúp ta tạo ra 1 đường dẫn thân thiện (SEO URL) giúp các
bộ máy tìm kiếm index nhanh hơn.
VD: File .htaccess sẽ viết lại đường dẫn cho ta có dạng:
/>Nếu như không dùng thì dưởng dẫn ra có dạng:
/>Đây là file .htaccess của ứng dụng:
Mã PHP: (SELECT ALL)
RewriteEngine on
# Cho nay la duong dan chinh cua thu muc lam viec (http://localhost/) => RewriteBase /
# Neu thuc muc la http://localhost/shop/ => RewriteBase /shop/
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?rt=$1 [L,QSA]

Cấu trúc của ứng dụng MVC:
Mã PHP: (SELECT ALL)
application
controller
includes
model
views
.htaccess
index.php

Phần Index:

/index.php
Như đã đề cập trước đó, tập tin index.php là điểm duy nhất để chúng ta truy cập ứng
dụng. Như vậy, nó cung cấp không gian lý tưởng để khai báo các biến và cấu hình trang
web rộng. Trong tập tin này tôi cũng sẽ gọi một hay một vài tập tin trợ giúp để khởi tạo

biến, cấu hình,.... Tập tin này sẽ được gọi là init.php và nó sẽ được đặt trong includes:
/index.php:
Mã PHP: (SELECT ALL)
/*** error reporting on ***/
error_reporting(E_ALL);


/*** define the site path ***/
$site_path = realpath(dirname(__FILE__));
define ('__SITE_PATH', $site_path);
/*** include the config.php file ***/
include 'config.php';
/*** include the init.php file ***/
include 'includes/init.php';
?>

index.php thiết lập các báo cáo lỗi (error_reporting), include tập tin init.php, và define
hằng số đường dẫn tĩnh của website (__SITE_PATH).
Và tại đây .htaccess sẽ bắt đầu xây dựng registry, các object registry này đã được xây
dựng thông qua những biến toàn cục mà ta đã xây dựng, để tạo ra đối tượng này ta tạo
ra init.php:
Dĩ nhiên là trước khi tạo ra tập tin này, chúng ta cần phải định nghĩa các lớp cần thiết cho
hệ thống như: controller, registry, router, template. Các lớp này sẽ tự động include vào
chương trình (bắt buộc để hệ thống hoạt động).
/includes/init.php
Mã PHP: (SELECT ALL)
/*** include the controller class ***/
include __SITE_PATH . '/application/' . 'controller_base.class.php';

/*** include the view class ***/
include __SITE_PATH . '/application/' . 'view_base.class.php';
/*** include the model class ***/
include __SITE_PATH . '/application/' . 'model_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';
/*** a new registry object ***/


$registry = new registry;
?>

Phần Registry:
Registry là 1 đối tượng (registry object) cho phép lưu __set() và truy xuất __get() các
biến mà ta không cần phải sử dụng biến toàn cục (global variable).
Mã PHP: (SELECT ALL)
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];
}
}
?>
[php]
Chúng ta thấy có 2 phương thức tự động: __set() và __get() để tạo ra các biến sử dụng xuyên suốt t

rong chương trình như global variable. Cách sử dụng:
[php] $registry->newval = 'This is value of variable name newval';
echo $registry->newval;

Phần Router:
Đây là lớp giúp mô hình MVC chọn đúng controller làm việc:
Ví dụ ta có 1 router sử dụng controller "new":
/>Nếu ta sử dụng mod_rewrite thì url có dạng:
/>/application/router.class.php:
Mã PHP: (SELECT ALL)
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;
}


Phương thức khởi tạo class router sẽ giúp ta tạo mới 1 object router thông qua object
$registry mà ta đã tạo sẵn trong init.php. Thêm đoạn mã sau để thực hiện tạo mới 1
router trong index.php:
Mã PHP: (SELECT ALL)
*** load the router ***/
$registry->router = new router($registry);

Để chọn được thư mục controller làm việc ta viết thêm phương thức setPath trong
router.class.php:
Mã PHP: (SELECT ALL)
/**
*
* @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;
}

...Và bây giờ ta truyền thư mục controller làm việc cho lớp router trong index.php:

Mã PHP: (SELECT ALL)


/*** set the path to the controllers directory ***/
$router->setPath (__SITE_PATH . 'controller');

Sau khi router kiểm tra tính hợp lệ của controller truyền vào qua phương thức setPath(),
ta tiếp tục load controller:
Mã PHP: (SELECT ALL)
/**
*
* @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)
{
$this->file = $this->path.'/error404.php';
$this->controller = 'error404';
}
/*** 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 ***/
if(!empty($this->args))
$controller->$action($this->args);
else
$controller->$action();
}

Phương thức loader() sẽ triệu gọi phương thức getController() với ý nghĩa getController()
sẽ thông qua query string: $_GET['rt'] mà ta truyền (http://example/index.php?
rt=news/view/3) để xác định controller mà ta thực hiện.
Trong trường hợp với controller "news" với url (http://example/index.php?rt=news) thì
controller sẽ sử dụng lớp news controller với phương thức index().
Trường hợp với controller "news/view" với url (http://example/index.php?rt=news/view/3)
thì controller sẽ sử dụng lớp news controller với phương thức view($arg) với $arg[1] = 3.
Chúng ta sẽ hiểu rõ về router hơn ở phần viết 1 ứng dụng blog nho nhỏ!
Mã PHP: (SELECT ALL)

/**
*
* @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(isset( $parts[2]))
{
$count_args = count($parts);

$k = 1;
$args = array();
for($i = 2; $i < $count_args; $i++)
$args[$k++] = $parts[$i];
$this->args = $args;
}
}
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 . 'Controller.php';
}

Phần Controller:
Controller (C trong MVC) là 1 lớp trừu tượng định nghĩa ra tất cả các controller của ứng
dụng, nó bao gồm: object $registry hỗ trợ truyền dữ liệu, object $model để lấy dữ liệu từ
Model (M), object $view để truyền data và xuất View (V) phương thức mặc định index()
cho controller không có action (task) như ví dụ truyền vào router với 2 tham số "news và
news/view/id" ở ví dụ ở phần Router
Mã PHP: (SELECT ALL)
Abstract Class baseController {
/*

* @registry object
*/
protected $registry;


protected $model;
protected $view;
function __construct($registry) {
$this->registry = $registry;
$this->model = &baseModel::getInstance();
$this->view

= &baseView::getInstance();

}

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

Phần Model: Model (M trong MVC) Thành phần xử lý các dữ liệu từ textfile, database,
objects,.... baseModel sẽ được triệu gọi từ Controller khi cần đến, baseModel có nhiệm vụ
thao tác với Database
Mã PHP: (SELECT ALL)
class baseModel {
private static $instance;

function __construct() {
}
public static function getInstance() {
if (!self::$instance)
{
self::$instance = new baseModel();
}
return self::$instance;
}
public function get($name){
$file = __SITE_PATH.'/model/'.str_replace("model","",strtolower($name))."Model.php";
if(file_exists($file))
{
include ($file);
$class = str_replace("model","",strtolower($name))."Model";
return new $class;


}
return NULL;
}
function __destruct() {
}
}

Phần View:
View (V trong MVC) là phần định dạng dữ liệu, gởi và nhận dữ liệu từ form, hiển thị html
& css,...
View ngoài phương thức set dùng để gởi data từ controller sang, view còn phương thức
show() để giúp controller

chọn lựa tên view cần sử dụng:
/application/view_base.class.php
Mã PHP: (SELECT ALL)
Class baseView {

/*
* @Variables array
* @access public
*/
public $data = array();
private static $instance;
/**
*
* @constructor
*
* @access public
*
* @return void
*
*/
function __construct() {
}
public static function getInstance() {
if (!self::$instance)


{
self::$instance = new baseView();
}

return self::$instance;
}
/**
*
* @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->data as $key => $value)
{
$$key = $value;
}

include ($path);
}

}


?>

Viết ứng dụng:
Viết 1 ứng dụng blog có chức năng như sau:
1/ Nếu người dùng truy cập thì sẽ xuất ra màn hình dòng chữ
"Welcome to My Blog!"
Mặc định nếu người dùng không gõ Controller, thì hệ thống sẽ chọn lấy indexController để
làm việc (nếu không có thì sẽ dùng /controller/error404.php)
Ta tạo: /controller/error404.php khi không tìm thấy Controller thì sẽ hiển thị ra nội dung:
Mã PHP: (SELECT ALL)
Class error404Controller Extends baseController {
public function index()
{
$this->view->data['blog_heading'] = 'This is the 404';
$this->view->show('error404');
}
}
?>

Với error404Controller có sử dụng View "error404".
Ta tạo /views/error404.php có nội dung sau:
Mã PHP: (SELECT ALL)

<?php echo $blog_heading; ?>



This is a custom 404 error page.


You can put whatever content you like here such as search for your site



Tiếp theo ta tạo Controller Index:
Ta tạo: /controller/indexController.php với phương thức index() là phương thức mặc định:
Mã PHP: (SELECT ALL)
Class indexController Extends baseController {
public function index() {
/*** set a template variable ***/
$this->view->data['welcome'] = 'Welcome to My Blog!';
/*** load the index template ***/
$this->view->show('index');
}


}
?>

Với indexController có sử dụng View "index".
Ta tạo /views/index.php có nội dung sau:
Mã PHP: (SELECT ALL)

<?php echo $welcome; ?>



Như vậy là xong, bây giờ ta test kết quả:
Gõ thì sẽ xuất ra màn hình dòng chữ "Welcome to My Blog!"
Gõ thì sẽ xuất ra thông báo lỗi không tìm thấy trang.
2/ Nếu người dùng truy cập thì sẽ xuất ra màn hình tất cả các
bài Blog
Đầu tiên ta tạo 1 database đơn giản có tên "framework" và 1 table "blog" gồm 3 trường

id, name và content.
Mã PHP: (SELECT ALL)
CREATE TABLE IF NOT EXISTS `blog` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`content` varchar(500) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB

DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

--- Dumping data for table `blog`
-INSERT INTO `blog` (`id`, `name`, `content`) VALUES
(1, 'MVC Introduction', 'This is content of MVC Introduction'),
(2, 'PHP framework', 'This is a content of PHP framework');

Tiếp theo ta cấu hình kết nối trong: /config.php
Mã PHP: (SELECT ALL)
define('DB_NAME', 'framework'); // Database name
define('DB_USER', 'root'); // User database
define('DB_PASSWORD', 'vertrigo'); // Password database
define('DB_HOST', 'localhost'); // IP host
?>

Tương tự như index Controller ta tạo ra blog Controller với action index() cho chức năng


này:
/controller/blogController.php

Mã PHP: (SELECT ALL)
Class blogController Extends baseController {
public function index()
{
$this->view->data['blogs'] = $this->model->get('blogModel')->get_blogs();
$this->view->data['blog_heading'] = 'This is the blog Index';
$this->view->show('blog_index');
}
}
?>

Với blogController có sử dụng model "blogModel" với phương thức get_blogs().
Ta tạo /model/blogModel.php có chứa phương thức get_blogs() lấy tất cả các blog trong
CSDL:
Mã PHP: (SELECT ALL)
Class blogModel Extends baseModel {
public function get_blogs()
{
global $db;
$blog = $db->query('SELECT id, name, content FROM blog');
return $db->fetch_object();
}
}
?>

Với blogController có sử dụng view "blog_index".
Ta tạo /views/blog_index.php có nội dung sau:
Mã PHP: (SELECT ALL)

<?php echo $blog_heading; ?>


<ul>
<?php foreach($blogs as $blog) : ?>
<li>Blog id <?php echo $blog->id; ?>:
<?php echo $blog->name; ?></a></li>
<?php endforeach; ?>
</ul>


Như vậy là xong, chúng ta test kết quả:
Gõ thì sẽ xuất ra màn hình tất cả các bài Blog:
Mã PHP: (SELECT ALL)
This is the blog Index
Blog id 1: MVC Introduction
Blog id 2: PHP framework

3/ Nếu người dùng truy cập thì sẽ xuất ra màn hình chi
tiết của bài blog có id là "id"
Tương tự như blog Controller ta bổ sung blogController với action view() cho chức năng
này:
/controller/blogController.php
Mã PHP: (SELECT ALL)
Class blogController Extends baseController {
public function index()
{
$this->view->data['blogs'] = $this->model->get('blogModel')->get_blogs();
$this->view->data['blog_heading'] = 'This is the blog Index';
$this->view->show('blog_index');
}

public function view($args){
$id_blog = $args[1];
$blog_detail = $this->model->get('blogModel')->get_blog_detail($id_blog);
$this->view->data['blog_heading'] = $blog_detail->name;
$this->view->data['blog_content'] = $blog_detail->content;
$this->view->show('blog_view');
}
}
?>

Với action view() có sử dụng model "blogModel" với phương thức get_blog_detail().
Ta bổ sung /model/blogModel.php phương thức get_blog_detail($id) cho phép lấy thông
tin blog có id là $id:
Mã PHP: (SELECT ALL)


Class blogModel Extends baseModel {
public function get_blogs()
{
global $db;
$blog = $db->query('SELECT id, name, content FROM blog');
return $db->fetch_object();
}
public function get_blog_detail($id)
{
global $db;
$blog = $db->query('SELECT id, name, content FROM blog where id = '.$db->sqlQuote($id));
return $db->fetch_object($first_row = true);
}

}
?>

Với blogController có sử dụng view "blog_view".
Ta tạo /views/blog_view.php có nội dung sau:
Mã PHP: (SELECT ALL)

<?php echo $blog_heading; ?>


<?php echo $blog_content; ?>



Như vậy là xong, chúng ta test kết quả:
Gõ thì sẽ xuất ra màn hình chi tiết của bài blog có id là 1
Mã PHP: (SELECT ALL)
MVC Introduction
This is content of MVC Introduction

Kết thúc: Qua bài tutorial khá là dài, mình cố gắng xây dựng rất chi tiết mô hình MVC
này, mong các bạn đóng góp ý kiến để bài viết hoàn thiện hơn.

Lưu ý:
- Đây là mô hình MVC cốt lõi rất đơn giản, đối với 1 mô hình hoàn thiện cần phải có thêm
1 số xử lý khác như: helper (chứ các function làm việc), library (các thư viện hỗ trợ, có


thể mở rộng), languages (đa ngôn ngữ), cache (giúp ứng dụng chạy nhanh hơn),... và
còn nhiều thành phần khác mà bạn hãy tự khám phá nhé!
- Với mô hình này bạn có thể phát triển thêm rất nhiều tùy ý bạn, nhưng mình khuyên
sau khi chạy và hiểu rõ sự hoạt động mô hình này, hãy bắt đầu ngay với 1 framework như
CI (CodeIgniter ) để bạn hiểu sâu hơn, đồng thời nó đã xây dựng tất cả các xử lý mà tôi
vừa trình bày, khi bạn viết bạn sẽ tập trung vào ứng dụng của mình hơn so với xây dựng
1 framework rất mất thời gian!

- Nếu đơn giản cho bạn phát triển thêm theo mô hình MVC hãy chọn ngay CMS với
joomla, với shop hãy chọn Open cart: đã xây dựng đầy đủ
trang quản trị và các ngôn ngữ, cùng với các chức năng tin tức và sản phẩm, bạn chỉ viết
thêm 1 số tính năng như giá vàng, thanh toán ngân lượng, quảng cáo,...



×