![]() |
![]() |
|
|
|||||||
| Web Framework Giới thiệu và cùng thử các Framework |
![]() |
|
|
LinkBack | Công Cụ | Dạng sắp xếp bài |
|
|||
|
Như đã viết trong bài roadmap from newbie to professional, hôm nay 5s bắt đầu series về xây dựng một MVC framework cho ứng dụng web bằng PHP.
Trong bài viết này, 5s sẽ walk through nhiều concept quan trọng của một ứng dụng để tạo nên một MVC framework thực sự hữu dụng cho các bạn mới bắt đầu. Có được kíên thức MVC cơ bản, các bạn sẽ thấy thoải mái khi sử dụng những framework "pro" như Zend hay Symfony, CakePHP,... MVC cơ bản này cũng sẽ giúp bạn nếu đam mê xây dựng application architect phát triển bổ sung vào nó những công cụ hữu ích khác nếu muốn, tạo nên một framework hoàn chỉnh có thể dùng trong công việc hàng ngày mà ở đó bạn có thể kiểm soát hoàn toàn các đoạn code của mình. MVC là mô hình gắn liền với mô hình ứng dụng 3 lớp mà chúng ta đã quen thuộc. Xử lý tập trung Xét với 1 ứng dụng web, do bản chất của nó là giao tiếp giữa client (web browser của người dùng) và server (web server) quan các lời gọi Request và trả lời Response, một ứng dụng web có thể cho user truy cập nhiều trang web khác nhau như index.php, user.php, view_product.php,... Việc này dẫn đến nhiều khó khăn khi hệ thống có những biến chia sẻ chung như setting, database access,... Thường thì chúng ta sẽ phải viết code như sau: Code: <?php include ('global.php'); //Các biến dùng chung // Các xử lý code khác ?> Mô hình MVC sẽ thay đổi việc này bằng cách tập trung tất cả các request của user về một trang duy nhất: index.php sau đó sẽ xử lý bằng cách phân tích request và gọi đúng trang php cần thiết để xử lý. Việc tập trung này sẽ giúp các bạn tiến hành các bước kết nối DB, logging, error handling,... hết sức hiệu quả sau này. Tuy nhiên, trong serie này 5s không thể đề cập hết các chi tiết đó. Trước tiên, ta sẽ tạo một file .htaccess để thực hiện rewrite URL, thay đổi request về file index.php sẽ có tham số route trong query string như sau: file .htacess ngang cấp với index.php Code: RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?route=$1 [L,QSA] Chú ý, nếu bạn chưa load module mod_rewrite của Apache, phải vào httpd.conf để sửa đổi cấu hình và restart Apache. Ở đây, route là tham số dùng để thay đổi cách gọi request của user, nó giúp file index.php phân tích nội dung request để chuyển tới file php khác thực hiện xử lý thích hợp. Cũng giống như thông tin cấu hình ở ví dụ trên, chúng ta sẽ làm vài việc gọi là initialize ứng dụng web của mình: File index.php Code: require 'includes/init.php'; File includes/init.php Code: <?php error_reporting (E_ALL); //Thông báo tất cả các lỗi if (version_compare(phpversion(), '5.1.0', '<') == true) { die ('PHP5.1 Only'); } //Framework của chúng ta làm việc trên PHP5 // Constants: define ('DIRSEP', DIRECTORY_SEPARATOR); //Tương thích với môi trường Win hay Unix, ta tìm dúng dấu phân cách dir là \ hay / // Get site path $site_path = realpath(dirname(__FILE__) . DIRSEP . '..' . DIRSEP) . DIRSEP; define ('site_path', $site_path); Tiếp theo, tạo một biến registry để chứa các biến toàn cục trong ứng dụng. Sau này ta sẽ lưu vào đây các biến DB, Logger, Cache, Template,... Code: $registry = new Registry; Vì chúng ta sẽ rất lười nhác phải viếc các hàm include file và vấn đề đang gặp phải là sẽ phải tạo file Registry.php (chú ý case sensitve nhé) cho class Registry ta đang dùng ở trên nên ta sẽ viết một hàm __autoload() cho PHP5 để nó tự load các file php ứng với class ta cần dùng: Code: function __autoload($class_name) { $filename = strtolower($class_name) . '.php'; $file = site_path . 'classes' . DIRSEP . $filename; if (file_exists($file) == false) { return false; } include ($file); } Hàm này PHP5 tự động sẽ gọi khi không tìm thấy class cần dùng trong hệ thống, chúng ta cứ viết và để đó - không cần quan tâm gì thêm. Một điều 5s muốn các bạn chú ý là trong một framework, cách đặt tên các file, class, variable,... là rất quan trọng và ảnh hưởng tới cơ chế xây dựng framework, do đó, hãy đế ý tới tên họ các biến, class cho cẩn thận, nhất là chữ hoa/thường vì sau này upload lên host Linux sẽ gặp nhiều vấn đề. Tạo class Registry Chúng ta sẽ dùng object của Registry để pass dữ liệu lòng vòng trong ứng dụng tương tự như việc dùng Application['abc'] của ASP hay biến $GLOBALS của PHP. Code: <?php Class Registry { private $vars = array(); function set($key, $var) { if (isset($this->vars[$key]) == true) { throw new Exception('Unable to set var `' . $key . '`. Already set.'); } $this->vars[$key] = $var; return true; } function get($key) { if (isset($this->vars[$key]) == false) { return null; } return $this->vars[$key]; } function remove($var) { unset($this->vars[$key]); } } ?> Chúng ta vừa tạo một Dictionary class. Ý nghĩa của một từ điển là cứ cho tôi một từ khoá (key) tôi sẽ cho ý nghĩa của nó (value). Value-key pair cũng là khái niệm hay dùng nên bạn cũng nên nắm bắt luôn ở đây. Ví dụ sử dụng Registry: Code: <?php $registry = new Registry; // Set some data $registry->set ('name', 'hung 5s'); // Get data, using get() echo $registry->get ('name'); // Get data, using array access echo $registry['name'] ?> Do tôi cũng lười typing và muốn nhấn mạnh một chút về OOP, ta sẽ implement một interface để biến đối tượng $registry mới tạo thành một array của PHP cho dễ dùng hơn. Đầu tiên sửa lại class Registry: Code: class Registry Implements ArrayAccess { Sau đó implement 4 method của interface ArrayAccess như sau: Code: function offsetExists($offset) { return isset($this->vars[$offset]); } function offsetGet($offset) { return $this->get($offset); } function offsetSet($offset, $value) { $this->set($offset, $value); } function offsetUnset($offset) { unset($this->vars[$offset]); } Bây giờ bạn có thể viết $registry['name'] = 'abc' thay vì $registry->set('name', 'abc'); khoái chí không nào. Nếu bạn muốn ứng dụng cái này ở class nào đó cho riêng mình để có thêm vòng lặp foreach chẳng hạn thì bạn thử implement thêm interface Iterator nữa nhé. |
|
|||
|
Tiếp tục tập 2, tập hóc búa nhất của MVC. Bây giờ chúng ta đi xây dựng Router - bộ phân tích và xử lý request như đã nói ở tập 1.
Mọi thứ đều đã tập trung về file index.php nên router của chúng ta phải đặt tại đây để xử lý request, add code sau vào index.php: Code: # Load router $router = new Router($registry); $registry->set ('router', $router); Tạo class Router.php: Code: <?php class Router { private $registry; private $path; private $args = array(); function __construct($registry) { $this->registry = $registry; } } ?> Ở đây, chúng ta đã truyền registry vào router để nó có thể truyền tiếp cho các file xử lý reuqest. Tiếp tục, để Router tìm ra file xử lý, ta đặt cho nó một folder mặc định làm gốc để bắt đầu chuyện mò kim đáy bể: Code: function setPath($path) { $path = trim($path, '/\\'); $path .= DIRSEP; if (is_dir($path) == false) { throw new Exception ('Invalid controller path: `' . $path . '`'); } $this->path = $path; } Trong MVC, các xử lý logic gọi là Controller và nhiệm vụ của router là tìm đúng controller để gửi gắm việc xử lý yêu cầu của client, vì thế, tạm gọi folder chức các controller của chúng ta là "controllers" luôn. Thêm dòng sau vào index.php Code: $router->setPath (site_path . 'controllers'); Bạn cũng cần tạo luôn foldere controller ngang cấp với index.php. Tiếp theo, ta viết một hàm để tìm kiếm controller dựa trên thông tin nhận được từ query string qua tham số route (xem bài 1). Code: private function getController(&$file, &$controller, &$action, &$args) { $route = (empty($_GET['route'])) ? '' : $_GET['route']; if (empty($route)) { $route = 'index'; } // Get separate parts $route = trim($route, '/\\'); $parts = explode('/', $route); // Find right controller $cmd_path = $this->path; foreach ($parts as $part) { $fullpath = $cmd_path . $part; // Is there a dir with this path? if (is_dir($fullpath)) { $cmd_path .= $part . DIRSEP; array_shift($parts); continue; } // Find the file if (is_file($fullpath . '.php')) { $controller = $part; array_shift($parts); break; } } if (empty($controller)) { $controller = 'index'; }; // Get action $action = array_shift($parts); if (empty($action)) { $action = 'index'; } $file = $cmd_path . $controller . '.php'; $args = $parts; } Hàm lằng nhằng này chỉ phục vụ ý tưởng đơn giản là tìm hai tham số của route, được phân cách bởi / trong đó tham số thứ nhất là controller, tham số thứ 2 là action. Nếu chúng ta không tìm ra gì hết thì controller mặc định là index.php và action mặc định là 'index'. Quá trình tìm kiếm bắt đầu từ root folder mà ta đã định nghĩa là /controller. Chúng ta sẽ đi qua các sub-folder chỉ ra như là một đường dẫn trong route Với mỗi điểm trên đường dẫn ta sẽ kiểm tra đó là folder hay file. Nếu là folder thì đi tiếp, file thì coi đó là file controller. Khi đã tìm ra controller thì điểm tiếp theo trên đường dẫn là tên action. Ở đây có một chút phức tạm mà tôi vẫn thực hiện là việc đi qua nhiều sub-folder. Việc này giúp tổ chức file trong một web application được dễ hơn vì ta có thể chia application ra thành nhiều module con, mỗi module là 1 folder và trong folder đó mới có các controller. Nếu route của chúng ta là: index.php?route=member/view thì chúng ta cần có member.php trong /controllers. Trong member.php, chúng ta sẽ viết 1 function có tên là view để thực hiện xử lý action 'view' như được yêu cầu. Nói thêm về tham số $args. Ở đây ta dùng rewrite_url và cơ chế thực hiện là mọi thứ tham số đều truyền qua route. Vì vậy, bạn thấy rằng cuối hàm getController, $args được gán bằng $parts là phần còn lại của route sau khi ta loại bỏ controller và action. Phần $args này thường sẽ có 1 số chẵn các phần tử trong đó cứ mỗi cặp thì ta có 1 phần tử là parameter name, phần tử kia là parameter value. Tạm thời ta không đi sâu vào xử lý $args. Trên thực tế để có một MVC "xịn" hơn thì ta sẽ tiếp tục phân tích $args bằng một rewrite engine nữa do ta tự quy định. Ví dụ, $args chuẩn để view một member dựa trên username sẽ là: route=member/view/name/hung5s. Rewrite engine mà ta tự viết có thể căn cứ vào controller và action để đưa ra các luật riêng của mình, khi đó ta tự hiểu rằng với controller member và action view thì chỉ cần 1 tham số là username. Lúc này, route viết lại là: route=member/view/hung5s <<<< Bạnt thấy URL như vậy có đẹp hơn không ? Trở lại với class Router của chúng ta, sau khi đã phân tích xong request, việc cuối cùng là thực hiện reuqest theo "lệnh" của client: Code: function delegate() { // Analyze route $this->getController($file, $controller, $action, $args); // File available? if (is_readable($file) == false) { die ('404 Not Found'); } // Include the file include ($file); // Initiate the class $class = $controller; $controller = new $class($this->registry); // (1) // Action available? if (is_callable(array($controller, $action)) == false) { die ('404 Not Found'); } // Run action $controller->$action($args); // (2) } Nếu bạn đọc hàm này, lại chú ý về cái tôi đã nói trước đó về quy cách đặt tên ảnh hưởng tới việc xây dựng 1 cơ chế cho MVC framework của chúng ta. Ở đây, tôi dùng tên class trùng với tên controller nên trong ví dụ trên, member.php phải khai báo class Member{}. Trong đoạn này, bạn cũng cần chú ý hai điểm cực mạnh của PHP là gọi class và hàm bằng tham số, xem điểm (1) và (2) như tôi đánh dấu. Cuối cùng, ta thêm dòng gọi delegate trong index.php: Code: $router->delegate(); Đã tới đoạn kết của phần 2, bây giờ ta có thể xây dựng các controller để thử nghiệm năng lực của router của chúng ta. Nhưng đầu tiên, nhớ lại rằng khi không có action nào được yêu cầu trong request, ta mặc định xử lý một action tên là 'index'. Do vậy, tôi sẽ lại viết tiếp về khả năng sử dụng OOP trong thiết kế ứng dụng để ép người coder phải tuân theo một vài quy định cụ thể. Các controller của ta sẽ đều thừa kế 1 abstract class là ControllerBase trong đó định nghĩa sẵn function index(). Sau này, controller của bạn sẽ phải implement function này. Code: <?php Abstract Class ControllerBase { protected $registry; function __construct($registry) { $this->registry = $registry; } abstract function index($args); } ?> Bây giờ, ta tạo index.php là file index thực sự theo cách viết web application thông thường không dùng MVC để xử lý các request. File /controllers/index.php Code: <?php class index Extends ControllerBase { function index($params) { echo 'Hello from my MVC system'; } } ?> Tới đây, bạn có thể run thử nghiệm ứng dụng của mình: http://localhost/mymvc. Do không có tham số nên route của chúng ta sẽ phân tích ra các thông tin mặc định là dùng controller có tên index và gọi action tên là index. Nếu muốn, bạn có thể tạo thêm controller member để test ví dụ ở trên: route=member/view/username/hung5s Code: <?php Class Members Extends ControllerBase { function index($params) { echo 'Default action of member is executed.'; } function view($params) { echo 'You are viewing the members'.$params[1]; } } ?> Pheewwww! tới đây, chúng ta chỉ còn 2 phần là model và view để cài đặt cho MVC hoàn chỉnh. Hãy theo dõi bài tiếp theo nhé. |
|
|||
|
Qua được phần 2, chắc anh em vẫn còn chưa hứng thú lắm vì framework này mới chỉ show off một phần nội lực nho nhỏ Grin. Hiển thị dữ liệu tới người dùng (View) mới là một trong những phần vô cùng hấp dẫn và gần như là có thể mở rộng vô bờ bến.
Trên thực tế thì phần Controller, mỗi người thiết kế ứng dụng có những cách tiếp cận khác nhau, nhiều cách tiếp cận sử dụng chain processing hay một vài design pattern phức tạp. Tuy nhiên quanh đi quẩn lại thì design 1 hồi xong rồi nó cũng sẽ fix cứng ở đó, không thay đổi gì nữa. Phần mở rộng nhiều nhất có lẽ là phần View, vì vậy 5s ngồi thêm 1 ngày design phần này tương đối "rộng cửa" một chút để khi sử dụng sau này, mọi người có thể viết thêm nhiều helper hữu ích. Let's go... Wink Đầu tiên ta đi làm một cái class chuẩn để xử lý dữ liệu truyền vào từ Controller. Tại sao lại nó là truyền vào từ controller ? đây là cái các bạn nên để ý vì nó là cơ bản của kiến trúc ứng dụng 3 tier: Giao diện (View) - Xử lý (Controller) - Dữ liệu (Model). Phần xử lý, chúng ta đã thực hiện ở các controller và nhiệm vụ của View (giao diện) là phải thể hiện những kết quả đã được xử lý cho user. Để tuân thủ chặt chẽ quy định của mô hình 3 tier, trong phần giao diện tuyệt đối cấm coder không viết các đoạn code có ngữ nghĩa cụ thể để giải quyết các vấn đề logic về chức năng. Sau đây là 1 ví dụ để bạn nắm bắt ý nghĩa của 3 tier rõ ràng hơn, sau này lập trình tránh không bị nhầm lẫn giữa code hiển thị dữ liệu và code xử lý logic: Code: Yêu cầu: Xem thông tin của một member trong forum, hiển thị số post của người đó và show ra link tới 5 latest post. Xử lý: - Gọi model để lấy thông tin user - Xử lý đếm tổng số bài post của user, đây là 1 bước xử lý logic trong đó có gọi Model để truy vấn dữ liệu - Xử lý lấy subject 5 bài post cuối cùng của user (tương tự trên) - Lưu số bài post vào biến $totalPosts, 5 objects của 5 bài latest post vào 1 array $latest5Posts - Truyền cho View 3 thông số: $user, $totalPosts và $latest5Posts - View sẽ truy cập các property của $user để hiện thông tin user, có format ngày sinh, điện thoại,... tương tự format $totalPost là $totalPosts. ' bài viết', và dùng vòng lặp foreach để show ra 5 latest posts. Đã hết lý thuyết Grin giờ thực hành tiếp: Code: <?php class Template { private $registry; private $vars = array(); function __construct($registry) { $this->registry = $registry; } public function set($varname, $value, $overwrite=false) { if (isset($this->vars[$varname]) == true AND $overwrite == false) { trigger_error ('Unable to set var `' . $varname . '`. Already set, and overwrite not allowed.', E_USER_NOTICE); return false; } $this->vars[$varname] = $value; return true; } public function remove($varname) { unset($this->vars[$varname]); return true; } } ?> Như đã nói, $vars chính là biến để chứa các thông tin truyền vào từ Controller. Tới đây, ta cần tạo ra 1 object của class Template để xử dụng. Chú ý rằng do mỗi request từ user tới server đều cần trả về một HTML page, nên để đỡ mất công trong phần controller ta phải new ra một biến $template, việc new này sẽ làm ngay tại file index.php để tiết kiểm coding Cần thêm vào phía trứơc các dòng về route vì trong route khi gọi delegate thì yêu cầu đã được xử lý xong. Code: # Load template object $template = new Template($registry); $registry->set ('template', $template); Quay lại trang /controllers/index.php, ta thêm code vào function index($params) để test thử: Code: function index($params) { $this->registry['template']->set ('message', 'Hello, I am passed from index controller.'); $this->registry['template']->show('index'); } Do ta chưa viết hàm show() nên bạn mà hăm hở chạy là sẽ bị lỗi Grin bổ sung hàm show($templateName) vào class Template Code: function show($templateName) { $path = site_path . 'templates' . DIRSEP . $templateName . '.php'; if (file_exists($path) == false) { trigger_error ('Template `' . $templateName . '` does not exist.', E_USER_NOTICE); return false; } // Load variables for easy access foreach ($this->vars as $key => $value) { $$key = $value; } include ($path); } Ở đây có vài điểm đáng chú ý: - Chúng ta quy định rằng các file template sẽ đặt trong folder /templates ngang cấp với index.php - Đoạn code tôi comment là load variables for easy access tạo ra các biến $$key lấy ra từ $vars của object $template. Từ đó, bạn có thể dễ dàng sử dụng $message truyền vào từ controller trong file template của mình thay vì dùng $vars['message'] - Bạn phải đi tạo ra template (như đã nói là lưu trong /templates) để thực sự hiển thị dữ liệu Ví dụ /templates/index.php Code: <h1><?php echo $message ?></h1> Tới đây, bạn đã có cơ chế xử lý view hoàn chỉnh. Để chi tiết hơn, các bạn cần nắm được rằng với cách tổ chức View như thế này ta thực sự đã làm được 2 chuyện: - Viết ứng dụng theo cách code behind của ASP.NET - Bạn có thể tạo ra vô số các view, mỗi view bạn hãy coi như một form như khi làm ứng dụng Windows form. - Một form tương tự với một action bạn muốn xử lý hoặc bạn có thể gom nhiều xử lý dùng chung 1 form. Điểm thứ 3 là điểm rất hay mà MVC mang lại cho bạn. Với cách thiết kế như thế này, bạn rất dễ dàng quản lý các view, mỗi view không quá dài, không quá phức tạp về thiết kế giao dịên. Với cách thiết kế class Template và load file template bằng đoạn include($path) trên, thực tế ta luôn chỉ có 1 class Template trong khi view là một đoạn php/html được viết rất tự do mà công việc thiết kế có thể giao cho designer không biết về code thực hiện. Mô hình này cũng có thể dùng để sử dụng chung với Smarty template là một bộ template engine có nhiều tool và formatter rất hữu dụng. Tuy nhiên, tôi không làm nhiều lắm về Smarty nên các bạn sau khi tìm hiểu sơ về Smarty có thể thay thế class Template phần show() để tích hợp Smarty - third party component đầu tiên của chúng ta vào framework MVC này. Tôi sẽ kết thúc bài này ở đây với đề nghị các bạn tích hợp Smarty vào MVC. Sau khi tích hợp xong, xin đảm bảo tinh thần bạn sẽ phấn khích hơn vài phần khi cảm nhận được một cấu trúc ứng dụng mà luôn sẵn sàng cho chúng ta mở rộng và tích hợp nhiều thành phần vào hệ thống. Phần 4, tôi vẫn sẽ tiếp tục với View để minh hoạ thêm về cách mở rộng class Template này, viết các helper/formatter class và thử múa may một chút để tạo ra Master Template tương tự như của ASP.NET, giúp các bạn "tinh lọc" toàn bộ những thứ HTML code phức tạp ra khỏi template của mình. |
|
|||
|
Trong phần này, 5s tiếp tục với việc upgrade class Template để xây dựng 1 master template cho toàn bộ website, giúp dễ dàng tiến hành các bước customize tiếp theo như thay đổi theme, layout của website.
Sơ lược khái niệm về Master template: Master template là khung giao diện của một website, trên đó gồm có những phần có thể thay đổi được và nhiều phần fix cứng. Các phần thay đổi được tạm gọi là place holder trong đó, nội dung cụ thể của place holder chỉ có khi người dùng browse tới 1 website cụ thể. Giả sử 1 website tổ chức 4 phần (như trong demo) thì header, footer ta muốn fix cứng, phần content và sidebar ta muốn thay đổi nội dung tuỳ vào từng trang. sidebar có thể gồm những mục nội dung lặt vặt, bổ sung thông tin cho content như login form, calendar, recent entries, tags,... trong khi content thì thay đổi nội dung như: - List ra tóm tắt các entries - Show 1 entry đầy đủ - Hiện form đăng ký user - ... Ta sẽ gọi mỗi một loại nội dung hiển thị trong content là một view, mỗi mục lặt vặt show trên sidebar là widget. Về cách hiện thực việc reuse lại các phần fix cứng như header, footer, cách thông thường nhất là ta dùng include(), đưa vào từng trang .php cụ thể. Vấn đề bắt đầu khó khăn hơn khi trong site của ta có nhiều widget và bạn muốn inlcude các widget đó vào từng trang. Vì có quá nhiều widget, việc nhiều dòng include đặt vào page sẽ làm code trở nên rắc rối vô cùng. Với cách sử dụng master template dưới đây, ta sẽ tùy ý cấu hình các widget nào show ở page nào một cách tự động, được kiểm soát bởi class template và 1 file cấu hình widgets.xml. File /widgets.xml ngang cấp với Code: <?xml version="1.0" encoding="UTF-8"?> <pages> <page path="default"> <widget id="login" /> <widget id="rss" /> </page> </pages> Đây là file cấu hình mà ta sẽ dựa vào đó để biết có những widget nào trong hệ thống và trang nào (path) thì dùng widget nào. Ở đây ta có 1 trang gọi là "defaut" dùng để cấu hình các widget mặc định sẽ hiển thị trên page mà chúng ta không có cấu hình gì cho nó. Bổ sung một hàm sẽ phân tích file cấu hình và lấy ra những widget cần hiển thị vào hệ thống: Code: protected function getWidgets($page) { $widgetConfig = SitePath . 'widgets.xml'; $pages = simplexml_load_file($widgetConfig); foreach($pages->page as $pageElement) { if ($pageElement['path'] == $page) return $pageElement->xpath('//widget'); } return $pages->page[0]->xpath('//widget'); } phần này tôi sử dụng SimpleXmlElement để phân tích file XML, bạn có thể tham khảo 1 topic khác về sử dụng SimpleXmlElement. Trong demo này, tôi không muốn làm cho hệ thống thêm phức tạp. tất cả các widgets sẽ coi như chỉ load vào một chỗ là sidebar của page. Thực tế, bạn có thể configure thêm file .xml để <widget> có thêm các thuộc tính như Region="SideBar", add thêm các child element như <param type="" name="" value="" /> để với mỗi page bạn có thể có các cách cấu hình một widget thay đổi khác nhau nữa. Bây giờ, trong phần show() của template, ta cần bổ sung code để get các widgets cần show: Code: // Get widgets to show $widgets = $this->getWidgets($name); Tới đây, ta có đủ thông tin các biến (từ phần 3) và widget cần hiển thị. Bước tạo ra master template thay cho hiện từng template riêng rẽ không có gì khó lắm. Sửa tiếp phần code của function show để hoàn thiện nó như sau: Code: <?php function show($name) { $path = SitePath . 'templates' . DIRSEP . $name . '.php'; $layout = SitePath . 'includes/layout.php'; if (file_exists($path) == false) { trigger_error ('Template `' . $name . '` does not exist.', E_USER_NOTICE); return false; } // Load variables foreach ($this->vars as $key => $value) { $$key = $value; } // Get widgets to show $widgets = $this->getWidgets($name); include($layout); } ?> Chú ý ở dòng thứ 3, $layout là file layout.php lưu trong /includes. Sở dĩ đặt tên layout vì file này sẽ tổ chức bố cục hệ thống. Hãy phân biệt layout và theme. Theme chỉ là màu sắc và các chi tiết thiết kế hình ảnh, flash,... không liên quan tới layout. Layout chính là master template của chúng ta. Thay vì load vào từng template cụ thể ở đây, ta sẽ làm nó trong layout. Ví dụ file layout: Code: <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <script type="text/javascript" src="<?php echo SiteUrl ?>/includes/js/prototype.js" /> <script type="text/javascript" src="<?php echo SiteUrl ?>/includes/js/utilities.js" /> <link rel="stylesheet" type="text/css" href="<?php echo SiteUrl ?>/themes/default/standard.css" /> <title><?php echo Application::getParameter('SiteTitle') ?></title> </head> <body> <div id="header"> <h2><a href="<?php echo SiteUrl ?>">I-blog!</a></h2> Another little space in phpvn.org </div> <div id="form"> <form id="form1" name="form" method="post" action="index.php"> <div id="Content"> <?php include($path); ?> </div> <div id="SideBar"> <?php $this->loadWidgets($widgets); ?> </div> </form> </div> <div style="clear:both" /> <div id="footer"> A simple MVC framework - by Hung5s (c) 1/2008<br /> <a href="http://artemis.com.vn">Artemis Software LLC.</a> </div> </body> </html> ở đây, tôi dùng <div> làm các place holder. Div content là place holder cho các view, vì vậy, trong phần này mới thực sự load các view (như ở phần 3 ta load trực tiếp trong show() ). Bằng cách này, bạn thấy là có nhiều code của trang HTML đã được bỏ đi, trong từng view chỉ có những phần code của view đó là cần viết mới. Ví dụ về view liệt kê các bài post Code: <?php echo $post->Subject.'<br />'; echo $post->Intro.'<br />'; echo $post->Content.'<br />'; echo "Total views: $post->Views <br />"; ?> Về các widget cũng tương tự, ta sẽ có code rất ngắn gọn cho phần widget: Code: <div class="field_label">Username:</div> <div class="field_input"><input type="text" name="username" id="username" value="" /></div> <div class="field_label">Password:</div> <div class="field_input"><input type="password" name="password" id="password" value="" /></div> <div class="field_input"><input type="button" name="login" id="login" value="Login" onClick="javascript:doSubmit('members/login')" /></div> Cuối cùng trong phần này, các bạn chú ý rằng Master template của chúng ta có 1 form dùng chung, do đó, trong toàn bộ các view, widget, chúng ta không bao giờ dùng tag form. Ngược lại, mọi xử lý đều dùng 1 form duy nhất. Về việc này, sự thuận lợi và bất lợi đều ngang nhau, trong đó có 1 phần bất lợi lớn là submit data luôn truyền lên server toàn bộ data trong 1 page, có thể làm chậm ứng dụng. Đổi lại, chỉ với cách submit như vậy mới có thể xử lý tốt ứng dụng có nhiều thành phần view và widget khác nhau, nếu không sẽ gây nhiều rắc rối khi debug ứng dụng. |
![]() |
| Tags |
| ứng dụng web, framework, web, web framework |
| Công Cụ | |
| Dạng sắp xếp bài | |
|
|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |