• Введение в MVC паттерн

    by  • 05.03.2010 • Zend Framework, программирование • Комментарии [6]

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

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

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

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

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

    Наш PHP пример имеет несложную структуру, поэтому легко все уместится в одной папке.
    mvc structure Введение в 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 в браузере.

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

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

    Комментарии к "Введение в MVC паттерн"

    1. Денис Кызыл-оол
      27.06.2010 at 07:22

      Спасибо за статью, читать было приятно (в отличие от остальных, которые до этого нашел в гугле).

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

      Остались еще вопросики, но пока надо все осмыслить.

      • 28.06.2010 at 10:46

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

        • Денис Кызыл-оол
          04.07.2010 at 04:59

          Точно пока даже не могу мысли сформулировать *еще блуждаю тут в потемках*, но, если в общих чертах, то хотелось бы прочитать какую-нибудь отличную статью, посвященную MVC и XForms.

          Проблема в том, что сейчас все еще нельзя расчитывать на то, что XForms будет поддерживаться у пользователей. В то же время, элементы форм из HTML5 поддерживаются еле-еле и во всех браузерах совершенно по-разному. А тем не менее выгода от их использования неоспорима, к тому же чуть ли не все веб-приложения нуждаются в формах. Это очень нужно.

          На данный момент я бы хотел в своем проекте реализовать все так, чтобы xforms преобразовывались на сервере (PHP, XSLT, DOM), т.к. хотелось бы избавиться от зависимости от JavaScript и Ajax (навязывать пользователю их бы не хотелось, ведь приложение теоретически могло бы обойтись и без них).

          Также хотелось бы, чтобы приложения было очень гибким. Предполагается, что XForms могут быть использованы для преобразования в самые разные форматы и использоваться приложениями на разных платформах (приложения для Windows, сайты; в различных браузерах, различных версий, с использованием разных технологий). Поэтому Представлению (Виду) передается все в формате XML (XForms), а он их дальше сам преобразует в нужный формат по какому-нибудь шаблону, и смотрит при этом, что браузер поддерживает, чтобы реализовать это как можно в лучшем виде и с поддержкой последних технологий (к примеру, если браузер поддерживает XForms, то нет необходимости в JavaScript и Ajax; если это форма приложения, то нет необходимости в CSS и т.д.).

          Еще пока остается вопрос, где контроллер и что он делает. Сама модель должна получать (уж, наверное) тоже данные в формате XML, а запрос может быть послан и в формате GET. Этим-то он и мог заниматься. А саму модель при этом можно перенести вообще на отдельный сервер (что было бы, очень кстати, т.к. не хочется самое главное кому-то там отдавать не на своем сервере). Главное, только сюда не приплетать SOAP для работы с моделью, а то мой моск взорвется.

          Сама технология XForms буквально создана для реализации MVC, но все никак не могу представить себе всю картину такого приложения. Надо бы схемку такую на UML что-ли. При этом любой из M, V и C может находиться на отдельной машине. И БД тоже может быть отдельно. А может случиться и так, что они окажутся вместе, поэтому могут быть Виды/Контроллеры.

          Больше всего пугает необходимость максимально поддерживать работу с XForms, а описывать все это самому (проще было свой формат сделать, но я не хочу изобретать велосипед). Может, есть для PHP какие-нибудь инструменты, это ж нереально сделать в одиночку.

          Вот если б только все это сделать, это ж сколько всего можно было бы реализовать. Приложения любой сложности.

    2. Дмитрий
      06.07.2010 at 11:22

      Огромное спасибо за статью!!!
      Для новичка в самый раз

    3. Саня
      11.12.2011 at 01:24

      Интересно как работает это:
      public function __contruct()
      {
      $this->model = new Model();
      }

      ведь так выдаст ошибку на этой строке:
      $books = $this->model->getBookList();

      Ошибка такая будет:
      Fatal error: Using $this when not in object context

      у меня такая проблема возникла когда я попытался обратиться из метода одного класса к методу другого через экземпляр созданный в конструкторе первого!!!
      если интересно, можете посмотреть, я проблему покачто и нерешил, вот ссылка на мою тему в форуме:
      http://www.cyberforum.ru/php-oop/thread401209-page4.html#post2270719

      • 11.12.2011 at 18:20

        единственное предположение: у вас версия php стоит моложе, чем используется в примере, хотя и для 5+ должно работать.

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

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