Шаблон проектирования MVC

Шаблон проектирования MVC

От автора: в данном уроке я хотел бы поговорить с Вами о шаблоне проектирования MVC. Это очень популярный, а главное полезный шаблон проектирования, который предусматривает полное разделение логики скрипта от его представления (дизайна). Мы разберем его теоретические основы и в качестве практики переделаем ранее созданную CMS из мини курса, который ранее публиковался у нас на сайте, таким образом, чтобы полностью отделить ее логику от дизайна.

скачать исходникискачать урок

Что такое MVC

Это достаточно сложный шаблон проектирования и по моему мнению это не сколько шаблон — сколько метод или идея того как можно организовать наше веб-приложение. И идея эта заключается в том, что бы отделить логику программирования от представления (то есть от вывода на экран).

Итак, как следует их названия MVC состоит из трех компонентов модель (Model) – вид (View) или представление и контроллер (controller). Давайте разберем каждый элемент в отдельности.

Фреймворк YII2. Быстрая разработка с современным PHP фреймворком

Узнай тонкости современной веб-разработки с помощью фреймворка YII2

Узнать подробнее

Вид или представление — эта часть отвечает за вывод информации на экран — это уже дизайнерская часть нашего веб — приложения с минимальным количеством логики. То есть попросту говоря, этот блок отвечает за внешний вид нашего приложения. Задача представления хранить дизайн скрипта.

Контроллер — блок, который получает данные от пользователя, обрабатывает, нормализует их, также выполняет проверку правильности ввода и передает эти обработанные данные в нужную модель. Также он принимает данные от модели, затем выбирает нужное представление, наполняет его данными и отображает на экране браузера. Но при этом, еще раз уточню, контроллер не должен содержать в себе никакой информации о внешнем виде веб-приложения. Контроллер можно рассмотреть как связующее звено между представлением и моделью.

Модель — это основа логики нашего веб-приложения — она отвечает за расчеты, выборку информации из базы данных, изменение информации в БД и т.д. Модель можно представить как библиотеку различных функций, позволяющих реализовывать функционал нашего приложения. То есть — это блок, который получает данные от контроллера, далее на основе этих данных производит необходимые преобразования, либо опять же выбирает данные из БД или изменяет их, а затем передает результат своей работы назад контроллеру.

Если приложение сложное то моделей может быть несколько, например модель для работы со статьями и модель для работы с пользователями. Графически работу шаблона MVC можно представить по такой схеме:

То есть на данной схеме мы с Вами видим взаимодействие между Моделью Контроллером Представлением и браузером пользователя. Зелеными стрелками я обозначил запросы данных или передачу управления элементу, а серыми стрелками обозначено получение результирующих данных.

Смотрите, когда пользователь попадает на страничку, то он совершает какое то действие, к примеру, отправляет форму. То есть, выполняет запрос к контроллеру, контроллер в свою очередь, проверяет эти данные и передает запрос к модели. Модель к примеру сохраняет полученные данные в базе данных и возвращает контроллеру ответ об успешности проведенной операции. Затем контроллер выбирает, какой шаблон отобразить, посылает ему данные и получает ответ, который и направляется в браузер пользователю.

По диаграмме видно, что контроллер может использовать несколько различных шаблонов для отображения страницы скрипта. Далее, как Вы видите, шаблоны напрямую не работают с моделью, они могут получать данные только через контроллер. Аналогично модель не может воздействовать на шаблоны. То есть контроллер выступает связующим звеном между моделью и представлением. Пользователи в свою очередь, также не могут напрямую работать с шаблонами и с моделью, все, что они видят — это страницы предоставляемые контроллером.

Правка точки входа на сайт

На мой взгляд, наиболее интересно будет перевести уже рабочий готовый проект на структуру MVC, нежели работать с выдуманным примером. Поэтому в данном уроке мы будем работать с простой CMS, которая была создана в мини курсе, что публиковался ранее на нашем сайте. На всякий случай продублирую ссылку на данный мини курс. Вот ее вид в браузере:

Думаю, Вы ее прекрасно помните. Данная CMS довольно неплоха и прекрасно выполняет свои задачи. Но у нее есть один существенный недостаток. В ее коде перемешана логика с внешним видом. А это не очень хорошо с точки зрения читаемости кода, да и в дальнейшем, очень трудно будет, что то изменить в коде, особенно если это касается дизайна. Так как не получится изменить дизайн не затрагивая логику работы данной CMS.

Поэтому в данному уроке мы переделаем часть данной CMS на структуру шаблона MVC и если данная идея Вам понравится, то оставшуюся часть Вы доделаете самостоятельно. Итак, первым делом изменим главный файл данной CMS index.php:

<?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, для хранения модели и шаблонов соответственно.

Фреймворк YII2. Быстрая разработка с современным PHP фреймворком

Узнай тонкости современной веб-разработки с помощью фреймворка YII2

Узнать подробнее

Создание модели

Если мы посмотрим, к примеру, на файл ACore.php мы увидим, что в его методах очень много кода, который отправляет запросы к базе данных. Нам необходимо весь данный код, перенести в модель. При этом в модели необходимо создать отдельные методы, которые будут возвращать массивы данных подученных из базы данных. Поэтому в папке model создаем файл со следующим содержимым:

<?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:

<?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:

<?php
class main extends ACore {
 
 public function get_content() {
 
  $result = $this->m->get_main_content();
  return $result;
 
 }
}
?>

Файл menu.php:

<?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:

<?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 кода). Итак, главный файл шаблона, который собирает в единое целое все части шаблона:

<?
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):

<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):

<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):

<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):

<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):

<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):

<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.

На этом данный урок завершен. Всего Вам доброго и удачного Вам кодирования!!!

Фреймворк YII2. Быстрая разработка с современным PHP фреймворком

Узнай тонкости современной веб-разработки с помощью фреймворка YII2

Узнать подробнее
Самые свежие новости IT и веб-разработки на нашем Telegram-канале

Курс по программированию на языке PHP

Изучите PHP с нуля до результата!

Смотреть курс

Метки: ,

Похожие статьи:

Комментарии Вконтакте:

Комментарии Facebook:

Комментарии (13)

  1. Sam

    Еще не видел , но идея интересная о «модернизации» старых проэктов и может увиличение функционала может даже довидение этой CMS в портал, тоже идея!!!

  2. Sam

    Как Я люблю MVC это как раз то что нужно!!! побольше бы на эту теме.

    • Виктор Гавриленко

      Здравствуйте!
      Да, MVC, это очень популярный шаблон проектирования и главное очень полезный. Рад, что Вам понравился урок.

  3. SkyNoSky

    Признаться, Я действительно в восторге от Ваших курсов. Неторопливо, одно и тоже по нескольку с немного другими «словооборотами» — расширяют круг слушателей курсов с различным восприятием.
    Собственно есть вопрос по МИНИ CMS: во втором уроке начали наполнять главный класс и в методах по выводу левого и верхнего меню почему-то применялись различные циклы при выборке результата (for для get_left_bar() и foreach для get_menu()), а метод get_menu() вообще раздробили. По мне просто и понятно применять тот же механизм вывода левого меню и для верхнего меню. Почему Вы используете другой механизм для вывода верхнего?
    Курс полностью ещё не просмотрел.

    • Виктор Гавриленко

      Здравствуйте!
      Спасибо Вам за комментарий, по Вашему вопросу ответил в теме форума.

  4. Денис

    Начало понравилось, а вот код надо комментировать, особенно если он предназначен кого-то чему-то научить.

    • Виктор Гавриленко

      Здравствуйте, Денис!
      Да согласен комментарии в коде нужны. Но в уроках я их не использую — для экономии времени.

      • Denis

        Для экономии Вашего времени, так как код можно прокомментировать заранее.

  5. Sergei

    А что такое класс?

  6. Ольга

    Спасибо! Не знаю, насколько пример создания MVC безупречен, но благодаря этому уроку, я, наконец, поняла суть вопроса. Билась год с темой проектирования, выслушав и прочитав с дюжину источников, понимание так и не приходило. Вы совершили чудо ) Я поняла )) Дальше просто просто буду совершенствовать. Спасибо автору урока!

    • Виктор Гавриленко

      Здравствуйте, Ольга!
      Спасибо, за теплые слова. Очень рад, что урок для Вас полезен!

  7. Vyacheslav

    Здравствуйте, Виктор!
    У меня два вопроса, если можно. Какую нагрузку может выдержать такая CMS. При объявлении объекта инициализируется с БД MySQL и нигде не закрывается. Сколько может предоставить соединения MySQL и что делать если их не хватит? И второй вопрос — как выводить лучше заголовок Last-Modified для страницы?

  8. айнур

    Почитай здесь
    phpjs.ru/2017/03/06/mvc-приложение-на-php-часть-6-crud-mapping/

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree