Модель-Вид-Контроллер в PHP

Шаблон проектирования MVC (model – view – controller) самый распространенный на сегодняшнем день в web. Большинство современных PHP-фреймворков использует данный паттернн. Но большинство новичков отталкивает невозможность найти хорошего материала для начала изучения. В этой заметке я попробую это сделать.
Паттерн MVC разделяет приложение на 3 составляющее: Модель (Model), Вид или Презентация (View) и Контроллер (Controller).

Модель используется для того, чтобы управлять данными: записывать и считывать данные из базы данных и многое другое. Модель содержит логическую составляющую приложения.

Вид или как ее еще называют Представление – нужен для того, чтобы отображать данные, полученные от Модели в нужном формате. В основу популярных web-приложений заложена возможность использовать шаблоны для вывода информации.

Контроллер – делает так, чтобы Модель и Вид работали совместно. Контроллер получает запрос от клиента, «пинает» Модель, чтобы выполнила требуемые операции и посылает данные в Вид.
Ниже представлена картинка все это дело отображающая.mvc паттерн

Наш PHP пример имеет несложную структуру, поэтому легко все уместится в одной папке.
mvc паттерн

Контроллер

Контроллер первым получает запрос, обрабатывает его, запускает и «пинает» модель, принимая от нее ответ, который направляет в уровень представления (Вид). Это практически канат между Моделью и Представлением.
В нашем простецком приложении PHP будет только один контроллер, который будет объявлен всего одним классом, который так и будет назван Controller. Точка входа в приложение находится в файле index.php. Он уже сам будет делегировать запросы к контроллеру:

1
2
3
4
5
   //index.php
   include_once('controller/Controller.php');
   
   $controller = new Controller;
   $controller->invoke;

В нашем контроллере всего одна функция помимо конструктора. Конструктор инициирует класс Модели и когда запрос будет обработан принимает решение какие данные ей нужны. Затем «пинает» Модель для получения от нее данных. Код очень прост, поэтому новичкам не составит труда в нем разобраться. И еще хотелось бы сказать, что контроллер ничего не знает о существовании какой-либо базы данных и не знает о том, как генерировать страницу.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   include_once('model/Model.php');
   class Controller
   {
      public $model;
     
      public function __contruct()
      {
         $this->model = new Model();
      }
     
      public function invoke()
      {
         if(!isset($_GET['book')) {
            $books = $this->model->getBookList();
            include 'view/booklist.php'
         } else {
            $book = $this->model->getBook($_GET['book']);
            include 'view/viewbook.php';
         }
      }
   }

На нижеследующей картинке вы можете лицезреть что происходит при запросе от клиента

Модель и сущность классов

Модель представляет собой данные и логику приложения, это многие называют бизнес-логикой. Модель обычно отвечает за:

  • хранение, удаление и обновление данных приложения. Обычно включает работу с базой данных
  • изолировать логику приложения и взять ее всю на себя. На этом уровне большинство ошибок это объявление бизнес-логики в Контроллере или в Презентации. (не делайте этого)

В нашем простом примере модель представлена 2 классами: «Model» и «Book». Больше Модели и не надо. Класс «Book» должен передавать Презентации форматированные данные, полученные от Модели. В идеале хорошая реализации паттерна MVC предполагает, что организация класса Модели не включает в себя никакой бизнес-логики. Это означает, что организация сущности объектов может легко быть заменена XML, JSON или другим куском данных. В коде чуть ниже вы можете посмотреть как кусок кода сообщает Моделе вывести либо определенную книгу, либо список доступных книг

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
include_once('model/Book.php');

class Model
{
   public function getBookList()
   {
      return array(
         "PHP для начинающих"=> new Book('PHP для начинающих', 'Котеров', 'Книга-руководство'),
         "PHP для начинающих"=> new Book('PHP для начинающих', 'Котеров', 'Книга-руководство'),
         "PHP для начинающих"=> new Book('PHP для начинающих', 'Котеров', 'Книга-руководство')
      );
   }
   
   public function getBook($title)
   {
      $allBooks = $this->getBookList();
      return $allBooks[$title];
   }
}

В данном примере уровень модели включает Book класс. В реальном сценарии, модель будет включать все сущности и все классы, чтобы сохранить все данные в базе данных, отделив тем самым бизнес-логику.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Book
{
   public $title;
   public $author;
   public $description;
   
   public function _contruct($title, $author, $description)
   {
      $this->title = $title;
      $this->author = $author;
      $this->description = $description;
   }
}

Вид или Презентация

Презентация ответственна за получение данных из Модели в форме доступной для пользователя. Данные могут поступать в разных форматах от модели: объектах, xml структурах и т.д

Презентация не надо путать с механизмом шаблонов, хотя иногда она работает по тому же принципу. И презентация и шаблоны уменьшают зависимость Презентационного уровня от системы и отделяет HTML от кода.

В нашем примере Презентация состоит из 2х файлов: один для отображения одной книги и другой для вывода списка.

viewbook.php

1
2
3
4
5
6
7
8
9
10
<html>  
   <head></head>  
   <body>  
      <?php  
         echo 'Название:' . $book->title . '<br/>';  
         echo 'Автор:' . $book->author . '<br/>';  
         echo 'Описание:' . $book->description . '<br/>';  
      ?>  
   </body>  
</html>

и booklist.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
   <head></head>  
   <body>  
      <table>  
         <tbody><tr><td>Название</td><td>Автор</td><td>Описание</td></tr></tbody>  
         <?php  
         foreach ($books as $title => $book)  
         {  
            echo '<tr><td><a href="index.php?book='.$book->title.'">'.$book->title.'</a></td><td>'.$book->author.'</td><td>'.$book->description.'</td></tr>';  
         }  
         ?>  
      </table>  
   </body>  
</html>

Данный пример показывает упрощенную реализацию. Большинство PHP фреймворков базирующихся на MVC имееют похожую структуру, немного в лучшей реализации. Как бы то ни было возможности MVC паттерна безграничны. К примеру AJAX приложение может реализовать Презентационный уровень напрямую в JavaScript в браузере.

Этот пост был бы незаконченным, если бы не перечисление основных преимущество данного паттерна:

  • Модель и Презентация отделены друг от друга, делая приложения более гибкими;
  • Модель и Презентация могут быть изменены или заменены независимо друг от друга. К примеру, приложение может использовать сервисы в админке вместо базы данных, просто поменяя модель Модуль.
  • Каждый модуль может быть протестирован независимо.