От автора: в данном уроке я хотел бы поговорить с Вами о шаблоне проектирования MVC. Это очень популярный, а главное полезный шаблон проектирования, который предусматривает полное разделение логики скрипта от его представления (дизайна). Мы разберем его теоретические основы и в качестве практики переделаем ранее созданную CMS из мини курса, который ранее публиковался у нас на сайте, таким образом, чтобы полностью отделить ее логику от дизайна.
Что такое MVC
Это достаточно сложный шаблон проектирования и по моему мнению это не сколько шаблон — сколько метод или идея того как можно организовать наше веб-приложение. И идея эта заключается в том, что бы отделить логику программирования от представления (то есть от вывода на экран).
Итак, как следует их названия MVC состоит из трех компонентов модель (Model) – вид (View) или представление и контроллер (controller). Давайте разберем каждый элемент в отдельности.
Вид или представление — эта часть отвечает за вывод информации на экран — это уже дизайнерская часть нашего веб — приложения с минимальным количеством логики. То есть попросту говоря, этот блок отвечает за внешний вид нашего приложения. Задача представления хранить дизайн скрипта.
Контроллер — блок, который получает данные от пользователя, обрабатывает, нормализует их, также выполняет проверку правильности ввода и передает эти обработанные данные в нужную модель. Также он принимает данные от модели, затем выбирает нужное представление, наполняет его данными и отображает на экране браузера. Но при этом, еще раз уточню, контроллер не должен содержать в себе никакой информации о внешнем виде веб-приложения. Контроллер можно рассмотреть как связующее звено между представлением и моделью.
Модель — это основа логики нашего веб-приложения — она отвечает за расчеты, выборку информации из базы данных, изменение информации в БД и т.д. Модель можно представить как библиотеку различных функций, позволяющих реализовывать функционал нашего приложения. То есть — это блок, который получает данные от контроллера, далее на основе этих данных производит необходимые преобразования, либо опять же выбирает данные из БД или изменяет их, а затем передает результат своей работы назад контроллеру.
Если приложение сложное то моделей может быть несколько, например модель для работы со статьями и модель для работы с пользователями. Графически работу шаблона MVC можно представить по такой схеме:
То есть на данной схеме мы с Вами видим взаимодействие между Моделью Контроллером Представлением и браузером пользователя. Зелеными стрелками я обозначил запросы данных или передачу управления элементу, а серыми стрелками обозначено получение результирующих данных.
Смотрите, когда пользователь попадает на страничку, то он совершает какое то действие, к примеру, отправляет форму. То есть, выполняет запрос к контроллеру, контроллер в свою очередь, проверяет эти данные и передает запрос к модели. Модель к примеру сохраняет полученные данные в базе данных и возвращает контроллеру ответ об успешности проведенной операции. Затем контроллер выбирает, какой шаблон отобразить, посылает ему данные и получает ответ, который и направляется в браузер пользователю.
По диаграмме видно, что контроллер может использовать несколько различных шаблонов для отображения страницы скрипта. Далее, как Вы видите, шаблоны напрямую не работают с моделью, они могут получать данные только через контроллер. Аналогично модель не может воздействовать на шаблоны. То есть контроллер выступает связующим звеном между моделью и представлением. Пользователи в свою очередь, также не могут напрямую работать с шаблонами и с моделью, все, что они видят — это страницы предоставляемые контроллером.
Правка точки входа на сайт
На мой взгляд, наиболее интересно будет перевести уже рабочий готовый проект на структуру MVC, нежели работать с выдуманным примером. Поэтому в данном уроке мы будем работать с простой CMS, которая была создана в мини курсе, что публиковался ранее на нашем сайте. На всякий случай продублирую ссылку на данный мини курс. Вот ее вид в браузере:
Думаю, Вы ее прекрасно помните. Данная CMS довольно неплоха и прекрасно выполняет свои задачи. Но у нее есть один существенный недостаток. В ее коде перемешана логика с внешним видом. А это не очень хорошо с точки зрения читаемости кода, да и в дальнейшем, очень трудно будет, что то изменить в коде, особенно если это касается дизайна. Так как не получится изменить дизайн не затрагивая логику работы данной CMS.
Поэтому в данному уроке мы переделаем часть данной CMS на структуру шаблона MVC и если данная идея Вам понравится, то оставшуюся часть Вы доделаете самостоятельно. Итак, первым делом изменим главный файл данной CMS index.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?php session_start(); header("Content-Type:text/html;charset=UTF-8"); require_once("config.php"); function __autoload($c) { if(file_exists("controller/".$c.".php")) { require_once "controller/".$c.".php"; } elseif(file_exists("model/".$c.".php")) { require_once "model/".$c.".php"; } } if($_GET['option']) { $class = trim(strip_tags($_GET['option'])); } else { $class = 'main'; } if(class_exists($class)) { $obj = new $class; $obj->get_body($class); } else { exit("<p>Нет данные для входа</p>"); } ?> |
Обратите внимание – я убрал ручное подключение необходимых файлов с классами и добавил функцию __autoload() – функция автоматической загрузки классов. Которая будет выполнять подключение недостающих файлов с классами. И убрал проверку наличия файла, перед созданием его объекта. Так как это выполняется в функции __autoload(). Да и папку classes я переименовал в controller (в ней будут содержаться контроллеры). Также создал две пустых папки: model и tpl, для хранения модели и шаблонов соответственно.
Создание модели
Если мы посмотрим, к примеру, на файл ACore.php мы увидим, что в его методах очень много кода, который отправляет запросы к базе данных. Нам необходимо весь данный код, перенести в модель. При этом в модели необходимо создать отдельные методы, которые будут возвращать массивы данных подученных из базы данных. Поэтому в папке model создаем файл со следующим содержимым:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
<?php class model { protected $db; public function __construct() { $this->db = mysql_connect(HOST,USER,PASSWORD); if(!$this->db) { exit("Ошибка соединения с базой данных".mysql_error()); } if(!mysql_select_db(DB,$this->db)) { exit("Нет такой базы данных".mysql_error()); } mysql_query("SET NAMES 'UTF8'"); } public function get_left_bar(){ $query = "SELECT id_category,name_category FROM category"; $result = mysql_query($query); if(!$result) { exit(mysql_error()); } for($i = 0;$i < mysql_num_rows($result); $i++) { $row[] = mysql_fetch_array($result, MYSQL_ASSOC); } return $row; } public function menu_array() { $query = "SELECT id_menu,name_menu FROM menu"; $result = mysql_query($query); if(!$result) { exit(mysql_error()); } $row = array(); for($i = 0;$i < mysql_num_rows($result); $i++) { $row[] = mysql_fetch_array($result, MYSQL_ASSOC); } return $row; } public function get_main_content() { $query = "SELECT id,title,discription,date,img_src FROM statti ORDER BY date DESC"; $result = mysql_query($query); if(!$result) { exit(mysql_error()); } for($i = 0; $i < mysql_num_rows($result);$i++) { $row[] = mysql_fetch_array($result,MYSQL_ASSOC); } return $row; } public function get_cat($id_cat) { $query = "SELECT id,title,discription,date,img_src FROM statti WHERE cat='$id_cat' ORDER BY date DESC"; $result = mysql_query($query); if(!$result) { exit(mysql_error()); } $row = array(); for($i = 0; $i < mysql_num_rows($result);$i++) { $row[] = mysql_fetch_array($result,MYSQL_ASSOC); } return $row; } public function get_menu($id_menu) { $query = "SELECT id_menu,name_menu,text_menu FROM menu WHERE id_menu='$id_menu'"; $result = mysql_query($query); if(!$result) { exit(mysql_error()); } $row = mysql_fetch_array($result,MYSQL_ASSOC); return $row; } } ?> |
Думаю, Вы увидите здесь знакомые строки кода. Так как они просто скопированы из классов ACore, main, category и menu.
Редактируем ядро – класс ACore
Весь лишний код мы из него убрали (перенесли в модель), остались только фрагменты дизайна. Который, нам нужно обязательно исключить, и перенести в отдельный шаблон. Вот исправленный код ACore.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<?php abstract class ACore { protected $m; public function __construct() { $this->m = new model();; } protected function get_header() { return TRUE; } protected function get_left_bar() { $result = $this->m->get_left_bar(); return $result; } protected function get_menu() { $row = $this->m->menu_array(); return $row; } protected function get_footer() { $row = $this->m->menu_array(); return $row; } public function get_body($tpl) { if($_POST || $_GET['del']) { $this->obr(); } $this->get_header(); $left_bar = $this->get_left_bar(); $menu_top = $this->get_menu(); $content = $this->get_content(); $footer = $this->get_footer(); //подключение шаблона include "tpl/index.php"; } abstract function get_content(); } ?> |
Обратите внимание, какой вид приобрел класс ACore – согласитесь такой код намного удобнее читать и редактировать.
Да хочу обратить внимание на метод get_body(). Все методы которые вызываются в нем – должны возвращать данные. Которые, в последствии, сохраняются в переменной. Это нужно для того что бы вывести данные в шаблонах. Так как в конце данного метода мы подключаем главный файл шаблона. А значит в подключаемых файлах, будут доступны переменные с данными, что вернут методы get_header(), get_left_bar(), get_menu() и т.д.
Так же обратите внимание, что метод get_body(). Теперь должен принимать параметр $tpl – это имя загружаемого класса (текущего контроллера). Данный параметр будет передаваться из файла index.php. И он необходимо для формирования динамических вложенных шаблонов. Все шаблоны сохранены в отдельных файлах с одноименными именами с контроллерами.
Редактирование классов: main, menu и category
Аналогичным образом изменяем код контроллеров main, menu и category. Файл main.php:
1 2 3 4 5 6 7 8 9 10 11 |
<?php class main extends ACore { public function get_content() { $result = $this->m->get_main_content(); return $result; } } ?> |
Файл menu.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php class menu extends ACore { public function get_content() { if(!$_GET['id_menu']) { echo 'Не правильные данные для вывода меню'; } else { $id_menu = (int)$_GET['id_menu']; if(!$id_menu) { echo 'Не правильные данные для вывода меню'; } else { $row = $this->m->get_menu($id_menu); return $row; } } } } ?> |
Файл category.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php class category extends ACore { public function get_content() { if(!$_GET['id_cat']) { echo 'Не правильные данные для вывода статьи'; } else { $id_cat = (int)$_GET['id_cat']; if(!$id_cat) { echo 'Не правильные данные для вывода статьи'; } else { $result = $this->m->get_cat($id_cat); return $result; } } } } ?> |
Как Вы видите никаких запросов к базе данных и кода внешнего вида.
Создание шаблонов
Шаблоны – это файлы с кодом, которые отвечают за внешний вид скрипта. Весь код следующих файлов взят из исходников CMS, то есть из трех контроллеров main, menu и category и класса ACore (простое копирование HTML кода). Итак, главный файл шаблона, который собирает в единое целое все части шаблона:
1 2 3 4 5 6 7 |
<? include "header.php"; include "left_bar.php"; include "menu_top.php"; include $tpl.".php"; include "footer.php"; ?> |
Обратите внимание центральная часть – это динамическая часть и ее шаблон формируется при помощи переменной $tpl – то есть это имя загруженного в данный момент контроллера. Файл шапки остается без изменений – его попросту копируем в папку tpl (header.php). Все фалы шаблона содержатся в папке tpl! Шаблон левой колонки (файл left_bar.php):
1 2 3 4 5 6 7 8 9 10 |
<div class="quick-bg"> <div id="spacer" style="margin-bottom:15px;"> <div id="rc-bg">Menu</div> </div> <?php foreach($left_bar as $row) :?> <div class='quick-links'> » <a href='?option=category&id_cat=<?php echo $row['id_category']?>'><?php echo $row['name_category']?></a> </div> <?php endforeach;?> </div> |
Шаблон верхнего горизонтального меню (файл menu_top.php):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<div id="mainarea"> <div class="heading"> <div class="toplinks" style="padding-left:30px;"> <a href="?option=main">Главная</a></div> <div class="sap2">::</div> <?php $i = 1;?> <?php foreach($menu_top as $item) :?> <div class='toplinks'><a href='?option=menu&id_menu=<?php echo $item['id_menu']?>'><?php echo $item['name_menu']?></a></div> <?php if($i != count($menu_top)) :?> <div class='sap2'>::</div> <?php endif;?> <?php $i++;?> <?php endforeach; ?> </div> |
Шаблон футера (файл footer.php):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<div id='bottom'> <div class="toplinks" style="padding-left:127px;"> <a href="?option=main">Главная</a></div> <div class="sap2">::</div> <?php $i = 1;?> <?php foreach($menu_top as $item) :?> <div class='toplinks'><a href='?option=menu&id_menu=<?php echo $item['id_menu']?>'><?php echo $item['name_menu']?></a></div> <?php if($i != count($menu_top)) :?> <div class='sap2'>::</div> <?php endif;?> <?php $i++;?> <?php endforeach; ?> </div> <div class="copy"><span class="style1"> Copyright 2010 Название сайта </span> </div> </div> </center></body></html> |
И три динамических шаблона. Шаблон главной страницы (main.php):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<div id="main"> <?php foreach($content as $row) :?> <div style='margin:10px;border-bottom:2px solid #c2c2c2'> <p style='font-size:18px'><?php echo $row['title']?></p> <p><?php echo $row['date']?></p> <p><img style='margin-right:5px' width='150px' align='left' src='<?php echo $row['img_src']?>'><?php echo $row['discription']?></p> <p style='color:red'><a href='?option=view&id_text=<?php echo $row['id']?>'>Читать далее...</a></p> </div> <?php endforeach;?> </div> </div> |
Шаблон страницы категорий (category.php):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<div id="main"> <?php foreach($content as $row) :?> <div style='margin:10px;border-bottom:2px solid #c2c2c2'> <p style='font-size:18px'><?php echo $row['title'];?></p> <p><?php echo $row['date'];?></p> <p><img style='margin-right:5px' width='150px' align='left' src='<?php echo $row['img_src'];?>'><?php echo $row['discription'];?></p> <p style='color:red'><a href='?option=view&id_text=<?php echo $row['id'];?>'>Читать далее...</a></p> </div> <?php endforeach;?> </div> </div> |
Шаблон для вывода страниц верхнего меню (menu.php):
1 2 3 4 5 6 |
<div id="main"> <p style='font-size:18px'><?php echo $content['name_menu'];?></p> <p><?php echo $content['text_menu'];?></p> </div> |
Как Вы видите, шаблоны содержат практически чистый html, за исключением не большой количества PHP кода необходимого для работы с данными, переданными в данный шаблон. Вот теперь наша мини CMS построена по очень гибкой и удобной для редактирования структуре. Согласитесь, что шаблоны настолько просты, что их может редактировать человек, не знающий PHP, а это большой плюс. Если Вам понравилась данная идея, то доработайте CMS до конца и полностью переведите ее логическую структуру на структуру шаблона MVC.
На этом данный урок завершен. Всего Вам доброго и удачного Вам кодирования!!!
Комментарии (13)