Создание mvc. MVC: что это такое и какое отношение имеет к пользовательскому интерфейсу

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

Вы можеть быть даже слышали о шаблонах проектирования и даже листали эти прекрасные книги:

  • Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидесс «Приемы объектно ориентированного проектирования. Паттерны проектирования»;
  • М. Фаулер «Архитектура корпоративных программных приложений».
А многие, не испугавшись огромных руководств и документаций, пытались изучить какой-либо из современных фреймворков и столкнувшись со сложностью понимания (в силу наличия множества архитектруных концепций хитро увязанных между собой) отложили изучение и применение современных интсрументов в «долгий ящик».

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

Прожженные PHP-программисты вряд ли найдут в данной статье что-то новое для себя, но их замечания и комментарии к основному тексту были бы очень кстати! Т.к. без теории практика невозможна, а без практики теория бесполезна, то сначала будет чуть-чуть теории, а потом перейдем к практике. Если вы уже знакомы с концепцией MVC, можете пропустить раздел с теорией и сразу перейти к практике.

1. Теория Шаблон MVC описывает простой способ построения структуры приложения, целью которого является отделение бизнес-логики от пользовательского интерфейса. В результате, приложение легче масштабируется, тестируется, сопровождается и конечно же реализуется.

Рассмотрим концептуальную схему шаблона MVC (на мой взгляд - это наиболее удачная схема из тех, что я видел):

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

Типичную последовательность работы MVC-приложения можно описать следующим образом:

  • При заходе пользователя на веб-ресурс, скрипт инициализации создает экземпляр приложения и запускает его на выполнение.
    При этом отображается вид, скажем главной страницы сайта.
  • Приложение получает запрос от пользователя и определяет запрошенные контроллер и действие. В случае главной страницы, выполняется действие по умолчанию (index ).
  • Приложение создает экземпляр контроллера и запускает метод действия,
    в котором, к примеру, содержаться вызовы модели, считывающие информацию из базы данных.
  • После этого, действие формирует представление с данными, полученными из модели и выводит результат пользователю.
  • Модель - содержит бизнес-логику приложения и включает методы выборки (это могут быть методы ORM), обработки (например, правила валидации) и предоставления конкретных данных, что зачастую делает ее очень толстой, что вполне нормально.
    Модель не должна напрямую взаимодействовать с пользователем. Все переменные, относящиеся к запросу пользователя должны обрабатываться в контроллере.
    Модель не должна генерировать HTML или другой код отображения, который может изменяться в зависимости от нужд пользователя. Такой код должен обрабатываться в видах.
    Одна и та же модель, например: модель аутентификации пользователей может использоваться как в пользовательской, так и в административной части приложения. В таком случае можно вынести общий код в отдельный класс и наследоваться от него, определяя в наследниках специфичные для подприложений методы.

    Вид - используется для задания внешнего отображения данных, полученных из контроллера и модели.
    Виды cодержат HTML-разметку и небольшие вставки PHP-кода для обхода, форматирования и отображения данных.
    Не должны напрямую обращаться к базе данных. Этим должны заниматься модели.
    Не должны работать с данными, полученными из запроса пользователя. Эту задачу должен выполнять контроллер.
    Может напрямую обращаться к свойствам и методам контроллера или моделей, для получения готовых к выводу данных.
    Виды обычно разделяют на общий шаблон, содержащий разметку, общую для всех страниц (например, шапку и подвал) и части шаблона, которые используют для отображения данных выводимых из модели или отображения форм ввода данных.

    Контроллер - связующее звено, соединяющее модели, виды и другие компоненты в рабочее приложение. Контроллер отвечает за обработку запросов пользователя. Контроллер не должен содержать SQL-запросов. Их лучше держать в моделях. Контроллер не должен содержать HTML и другой разметки. Её стоит выносить в виды.
    В хорошо спроектированном MVC-приложении контроллеры обычно очень тонкие и содержат только несколько десятков строк кода. Чего, не скажешь о Stupid Fat Controllers (SFC) в CMS Joomla. Логика контроллера довольно типична и большая ее часть выносится в базовые классы.
    Модели, наоборот, очень толстые и содержат большую часть кода, связанную с обработкой данных, т.к. структура данных и бизнес-логика, содержащаяся в них, обычно довольно специфична для конкретного приложения.

    1.1. Front Controller и Page Controller В большинстве случае, взаимодействие пользователя с web-приложением проходит посредством переходов по ссылкам. Посмотрите сейчас на адресную строку браузера - по этой ссылке вы получили данный текст. По другим ссылкам, например, находящимся справа на этой странице, вы получите другое содержимое. Таким образом, ссылка представляет конкретную команду web-приложению.

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

    Рассмотрим два варианта адресной строки, по которым показывается какой-то текст и профиль пользователя.

    Приблизительный код обработки в таком случае:
    switch($_GET["action"]) { case "about" : require_once("about.php"); // страница "О Нас" break; case "contacts" : require_once("contacts.php"); // страница "Контакты" break; case "feedback" : require_once("feedback.php"); // страница "Обратная связь" break; default: require_once("page404.php"); // страница "404" break; }
    Думаю, почти все так раньше делали.

    С использованием движка маршрутизации URL вы сможете для отображения той же информации настроить приложение на прием таких запросов:
    http://www.example.com/contacts/feedback

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

    Также стоит знать, что маршрутизаторы многих веб-фреймворков позволяют создавать произвольные маршруты URL (указать, что означает каждая часть URL) и правила их обработки.
    Теперь мы обладаем достаточными теоретическими знаниями, чтобы перейти к практике.

    2. Практика Для начала создадим следующую структуру файлов и папок:


    Забегая вперед, скажу, что в папке core будут храниться базовые классы Model, View и Controller.
    Их потомки будут храниться в директориях controllers, models и views. Файл index.php это точка в хода в приложение. Файл bootstrap.php инициирует загрузку приложения, подключая все необходимые модули и пр.

    Будем идти последовательно; откроем файл index.php и наполним его следующим кодом:
    ini_set("display_errors", 1); require_once "application/bootstrap.php";
    Тут вопросов возникнуть не должно.

    Следом, сразу же перейдем к фалу bootstrap.php :
    require_once "core/model.php"; require_once "core/view.php"; require_once "core/controller.php"; require_once "core/route.php"; Route::start(); // запускаем маршрутизатор
    Первые три строки будут подключать пока что несуществующие файлы ядра. Последние строки подключают файл с классом маршрутизатора и запускают его на выполнение вызовом статического метода start.

    2.1. Реализация маршрутизатора URL Пока что отклонимся от реализации паттерна MVC и займемся мрашрутизацией. Первый шаг, который нам нужно сделать, записать следующий код в .htaccess :
    RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule .* index.php [L]
    Этот код перенаправит обработку всех страниц на index.php , что нам и нужно. Помните в первой части мы говорили о Front Controller?!

    Маршрутизацию мы поместим в отдельный файл route.php в директорию core. В этом файле опишем класс Route, который будет запускать методы контроллеров, которые в свою очередь будут генерировать вид страниц.

    Содержимое файла route.php

    class Route { static function start() { // контроллер и действие по умолчанию $controller_name = "Main"; $action_name = "index"; $routes = explode("/", $_SERVER["REQUEST_URI"]); // получаем имя контроллера if (!empty($routes)) { $controller_name = $routes; } // получаем имя экшена if (!empty($routes)) { $action_name = $routes; } // добавляем префиксы $model_name = "Model_".$controller_name; $controller_name = "Controller_".$controller_name; $action_name = "action_".$action_name; // подцепляем файл с классом модели (файла модели может и не быть) $model_file = strtolower($model_name).".php"; $model_path = "application/models/".$model_file; if(file_exists($model_path)) { include "application/models/".$model_file; } // подцепляем файл с классом контроллера $controller_file = strtolower($controller_name).".php"; $controller_path = "application/controllers/".$controller_file; if(file_exists($controller_path)) { include "application/controllers/".$controller_file; } else { /* правильно было бы кинуть здесь исключение, но для упрощения сразу сделаем редирект на страницу 404 */ Route::ErrorPage404(); } // создаем контроллер $controller = new $controller_name; $action = $action_name; if(method_exists($controller, $action)) { // вызываем действие контроллера $controller->$action(); } else { // здесь также разумнее было бы кинуть исключение Route::ErrorPage404(); } } function ErrorPage404() { $host = "http://".$_SERVER["HTTP_HOST"]."/"; header("HTTP/1.1 404 Not Found"); header("Status: 404 Not Found"); header("Location:".$host."404"); } }


    Замечу, что в классе реализована очень упрощенная логика (несмотря на объемный код) и возможно даже имеет проблемы безопасности. Это было сделано намерено, т.к. написание полноценного класса маршрутизации заслуживает как минимум отдельной статьи. Рассмотрим основные моменты…

    В элементе глобального массива $_SERVER["REQUEST_URI"] содержится полный адрес по которому обратился пользователь.
    Например: example.ru/contacts/feedback

    С помощью функции explode производится разделение адреса на составлющие. В результате мы получаем имя контроллера, для приведенного примера, это контроллер contacts и имя действия, в нашем случае - feedback .

    Далее подключается файл модели (модель может отсутствовать) и файл контроллера, если таковые имеются и наконец, создается экземпляр контроллера и вызывается действие, опять же, если оно было описано в классе контроллера.

    Таким образом, при переходе, к примеру, по адресу:
    example.com/portfolio
    или
    example.com/portfolio/index
    роутер выполнит следующие действия:

  • подключит файл model_portfolio.php из папки models, содержащий класс Model_Portfolio;
  • подключит файл controller_portfolio.php из папки controllers, содержащий класс Controller_Portfolio;
  • создаст экземпляр класса Controller_Portfolio и вызовет действие по умолчанию - action_index, описанное в нем.
  • Если пользователь попытается обратиться по адресу несуществующего контроллера, к примеру:
    example.com/ufo
    то его перебросит на страницу «404»:
    example.com/404
    То же самое произойдет если пользователь обратится к действию, которое не описано в контроллере.2.2. Возвращаемся к реализации MVC Перейдем в папку core и добавим к файлу route.php еще три файла: model.php, view.php и controller.php


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

    Содержимое файла model.php
    class Model { public function get_data() { } }
    Класс модели содержит единственный пустой метод выборки данных, который будет перекрываться в классах потомках. Когда мы будем создавать классы потомки все станет понятней.

    Содержимое файла view.php
    class View { //public $template_view; // здесь можно указать общий вид по умолчанию. function generate($content_view, $template_view, $data = null) { /* if(is_array($data)) { // преобразуем элементы массива в переменные extract($data); } */ include "application/views/".$template_view; } }
    Не трудно догадаться, что метод generate предназначен для формирования вида. В него передаются следующие параметры:

  • $content_file - виды отображающие контент страниц;
  • $template_file - общий для всех страниц шаблон;
  • $data - массив, содержащий элементы контента страницы. Обычно заполняется в модели.
  • Функцией include динамически подключается общий шаблон (вид), внутри которого будет встраиваться вид
    для отображения контента конкретной страницы.

    В нашем случае общий шаблон будет содержать header, menu, sidebar и footer, а контент страниц будет содержаться в отдельном виде. Опять же это сделано для упрощения.

    Содержимое файла controller.php
    class Controller { public $model; public $view; function __construct() { $this->view = new View(); } function action_index() { } }
    Метод action_index - это действие, вызываемое по умолчанию, его мы перекроем при реализации классов потомков.

    2.3. Реализация классов потомков Model и Controller, создание View"s Теперь начинается самое интересное! Наш сайт-визитка будет состоять из следущих страниц:
  • Главная
  • Услуги
  • Портфолио
  • Контакты
  • А также - страница «404»
  • Для каждой из страниц имеется свой контроллер из папки controllers и вид из папки views. Некоторые страницы могут использовать модель или модели из папки models.


    На предыдущем рисунке отдельно выделен файл template_view.php - это шаблон, содержащий общую для всех страниц разметку. В простейшем случае он мог бы выглядеть так:
    Главная
    Для придания сайту презентабельного вида сверстаем CSS шаблон и интегририруем его в наш сайт путем изменения структуры HTML-разметки и подключения CSS и JavaScript файлов:

    В конце статьи, в разделе «Результат», приводится ссылка на GitHub-репозиторий с проектом, в котором проделаны действия по интеграции простенького шаблона.

    2.3.1. Создадаем главную страницу Начнем с контроллера controller_main.php , вот его код:
    class Controller_Main extends Controller { function action_index() { $this->view->generate("main_view.php", "template_view.php"); } }
    В метод generate экземпляра класса View передаются имена файлов общего шаблона и вида c контентом страницы.
    Помимо индексного действия в контроллере конечно же могут содержаться и другие действия.

    Файл с общим видом мы рассмотрели ранее. Рассмотрим файл контента main_view.php :
    Добро пожаловать!

    ОЛОЛОША TEAM - команда первоклассных специалистов в области разработки веб-сайтов с многолетним опытом коллекционирования мексиканских масок, бронзовых и каменных статуй из Индии и Цейлона, барельефов и изваяний, созданных мастерами Экваториальной Африки пять-шесть веков назад...


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

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

    2.3.2. Создадаем страницу «Портфолио» В нашем случае, страница «Портфолио» - это единственная страница использующая модель.
    Модель обычно включает методы выборки данных, например:
  • методы нативных библиотек pgsql или mysql;
  • методы библиотек, реализующих абстракицю данных. Например, методы библиотеки PEAR MDB2;
  • методы ORM;
  • методы для работы с NoSQL;
  • и др.
  • Для простоты, здесь мы не будем использовать SQL-запросы или ORM-операторы. Вместо этого мы сэмулируем реальные данные и сразу возвратим массив результатов.
    Файл модели model_portfolio.php поместим в папку models. Вот его содержимое:
    class Model_Portfolio extends Model { public function get_data() { return array(array("Year" => "2012", "Site" => "http://DunkelBeer.ru", "Description" => "Промо-сайт темного пива Dunkel от немецкого производителя Löwenbraü выпускаемого в России пивоваренной компанией "CАН ИнБев"."), array("Year" => "2012", "Site" => "http://ZopoMobile.ru", "Description" => "Русскоязычный каталог китайских телефонов компании Zopo на базе Android OS и аксессуаров к ним."), // todo); } }

    Класс контроллера модели содержится в файле controller_portfolio.php , вот его код:
    class Controller_Portfolio extends Controller { function __construct() { $this->model = new Model_Portfolio(); $this->view = new View(); } function action_index() { $data = $this->model->get_data(); $this->view->generate("portfolio_view.php", "template_view.php", $data); } }
    В переменную data записывается массив, возвращаемый методом get_data , который мы рассматривали ранее.
    Далее эта переменная передается в качестве параметра метода generate , в который также передаются: имя файла с общим шаблон и имя файла, содержащего вид c контентом страницы.

    Вид содержащий контент страницы находится в файле portfolio_view.php .
    Портфолио

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


    Здесь все просто, вид отображает данные полученные из модели.

    2.3.3. Создаем остальные страницы Остальные страницы создаются аналогично. Их код досутпен в репозитории на GitHub, ссылка на который приводится в конце статьи, в разделе «Результат».3. Результат А вот что получилось в итоге:

    Скриншот получившегося сайта-визитки



    Ссылка на GitHub: https://github.com/vitalyswipe/tinymvc/zipball/v0.1

    А вот в этой версии я набросал следующие классы (и соответствующие им виды):

    • Controller_Login в котором генерируется вид с формой для ввода логина и пароля, после заполнения которой производится процедура аутентификации и в случае успеха пользователь перенаправляется в админку.
    • Contorller_Admin с индексным действием, в котором проверяется был ли пользователь ранее авторизован на сайте как администратор (если был, то отображается вид админки) и действием logout для разлогинивания.
    Аутентификация и авторизация - это другая тема, поэтому здесь она не рассматривается, а лишь приводится ссылка указанная выше, чтобы было от чего оттолкнуться.4. Заключение Шаблон MVC используется в качестве архитектурной основы во многих фреймворках и CMS, которые создавались для того, чтобы иметь возможность разрабатывать качественно более сложные решения за более короткий срок. Это стало возможным благодаря повышению уровня абстракции, поскольку есть предел сложности конструкций, которыми может оперировать человеческий мозг.

    Но, использование веб-фреймворков, типа Yii или Kohana, состоящих из нескольких сотен файлов, при разработке простых веб-приложений (например, сайтов-визиткок) не всегда целесообразно. Теперь мы умеем создавать красивую MVC модель, чтобы не перемешивать Php, Html, CSS и JavaScript код в одном файле.

    Данная статья является скорее отправной точкой для изучения CMF, чем примером чего-то истинно правильного, что можно взять за основу своего веб-приложения. Возможно она даже вдохновила Вас и вы уже подумываете написать свой микрофреймворк или CMS, основанные на MVC. Но, прежде чем изобретать очередной велосипед с «блекджеком и шлюхами», еще раз подумайте, может ваши усилия разумнее направить на развитие и в помощь сообществу уже существующего проекта?!

    P.S.: Статья была переписана с учетом некоторых замечаний, оставленных в комментариях. Критика оказалась очень полезной. Судя по отклику: комментариям, обращениям в личку и количеству юзеров добавивших пост в избранное затея написать этот пост оказалось не такой уж плохой. К сожалению, не возможно учесть все пожелания и написать больше и подробнее по причине нехватки времени… но возможно это сделают те таинственные личности, кто минусовал первоначальный вариант. Удачи в проектах!

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

    Теги:

    • php
    • framework
    • cmf
    • mvc
    • site
    Добавить метки

    В этой статье мы рассмотрим архитектурный паттерн MVC (Model, View, Controller) в применении к веб-разработке, «в чистом виде», без привлечения каких-то дополнительных, не относящихся к MVC структур и паттернов. Мы будем продвигаться от простого к сложному, поэтому пока не станем рассматривать, например, дальнейшее развитие MVC – паттерн HMVC (Hierarchical MVC). Хотя HMVC, несомненно, намного более интересен для разработки веб-приложений, но его применение не отменяет необходимости понимания «обычного» MVC.

    Статья в Википедии (а именно туда, видимо, чаще всего попадают те, кто только начинает изучать MVC), изобилует неточностями и туманными формулировками, само определение, по сути, является неверным, а приведенная схема просто напросто не соответствует той, которая применяется в веб вообще и при разработке на PHP – в особенности.

    Наиболее корректное определение паттерна MVC я обнаружил :

    Шаблон проектирования MVC предполагает разделение данных приложения, пользовательского интерфейса и управляющей логики на три отдельных компонента: Модель, Представление и Контроллер – таким образом, что модификация каждого компонента может осуществляться независимо.
    Уточним, что термин «компонент» в данном случае не имеет никакой связи с компонентами некоторых популярных CMS или фреймворков, а компоненты Битрикса, например вообще строятся из всех трёх составляющих MVC.
    В приведённом определении под компонентом следует понимать некую отдельную часть кода, каждая из которых играет одну из ролей Контроллера, Модели или Представления, где Модель служит для извлечения и манипуляций данными приложения, Представление отвечает за видимое пользователю отображение этих данных (то есть, в применении к вебу, формирует отдаваемый сервером браузеру пользователя HTML/CSS), а Контроллер управляет всем этим оркестром.

    Давайте рассмотрим классическую схему веб-приложения:

    Рисунок 1

    На этом и последующем рисунках пунктирными линиями показана управляющая информация (такая, например, как ID запрашиваемой записи блога или товара в магазине), а сплошными – собственно данные приложения (которые могут храниться в БД, или в виде файлов на диске, или даже, возможно, в оперативной памяти – этот вопрос лежит за пределами паттерна MVC). В применении к вебу запрос и ответ ходят по HTTP, поэтому можно условно считать, что на этом рисунке пунктиром обозначены заголовки HTTP-запроса и ответа, а сплошными линиями – их тела.

    Получив Запрос 1, Контроллер его анализирует, и в зависимости от результатов обработки может выдать следующие варианты ответа (почему ответ имеет номер 4, станет понятно из следующих рисунков):

    1. Сразу выдать ответ об ошибке (например, при запросе несуществующей страницы отдать только HTTP-заголовок «404 Not found»)

    2. Если поступивший Запрос 1 признан корректным, то, в зависимости от того, является он запросом на просмотр или на модификацию данных, Контроллер вызывает соответствующий метод Модели, такой как Save или Load (Запрос 2 на Рис.2).

    Рисунок 2

    Важное замечание: концепция MVC не только не привязана к какому-то конкретному языку программирования, она также не привязана и к используемой парадигме программирования. То есть, вы вполне можете проектировать своё приложение по MVC, при этом не применяя ООП, и спроектировать Модель Товар для интернет-магазина таким образом:

    Итак, в зависимости от полученного от Модели Ответа 2 Контроллер решает, какое из Представлений вызвать для формирования итогового ответа на изначальный Запрос 1:

    3.1. В случае неудачи – Представление для сообщения об ошибке
    3.2. В случае успеха – Представление для отображения запрашиваемых данных либо сообщения об их успешном сохранении (если Запрос 1 был на изменение данных).

    Рисунок 3

    Вопрос о том, кто должен проверять на валидность и права доступа входные данные (Контроллер или Модель), является предметом достаточно многочисленных споров, поскольку паттерн MVC не описывает таких деталей. Это значит, что в этом вопросе выбор за вами (или за вас его сделали авторы вашего любимого фрейvворка или CMS).
    Мы в своей практике придерживаемся такого подхода:
    Контроллер проверяет входные данные на предмет «общей» (т.е. независящей от конкретного запроса) корректности, соответствие требованиям Модели к валидности сохраняемых данных проверяет соответствующий метод Модели, а права доступа – метод Access отдельного класса User .

    Для вызова Представления в PHP иногда проектируется специальный класс (а то и несколько классов), например, View (это часто встречается в описаниях MVC в реализации того или иного фреймворка), однако это не является требованием MVC.
    Также файлы Представлений часто называют шаблонами, а при использовании так называемых шаблонизаторов роль Представления играет сам шаблонизатор, а шаблоны (т.е. файлы, содержащие непосредственно HTML-разметку) в некоторых фреймворках называют layouts .

    Не вполне понятен предыдущий абзац? Забейте, поскольку у нас каждое Представление – это просто отдельный PHP-файл, а сам PHP устроен так, что в случае, если мы используем для выполнения Запроса 3 инструкцию include, Ответ 3 и Ответ 4 (помните, что это ответ на Запрос 1?) отдаются браузеру автоматически, средствами самого PHP.

    Давайте рассмотрим пример.

    У нас есть два варианта Представления (шаблоны), в которых

    будет означать HTML-код, предворяющий в формируемом веб-документе основной контент (т.е. содержит тег doctype, контейнер head, код шапки страницы, и т.п.), а – примерно то же, только для подвала страницы.

    Листинг 1. Шаблон product.tpl.php отображает данные о Товаре (которые к моменту его вызова уже содержит объект $product ):

    Цена:

    Листинг 2. Шаблон error.tpl.php отображает сообщение об ошибке (которое содержится в переменной $error ):

    Ошибка:

    Листинг 3. Контроллер product.php , служащий для отобоажения Товара, будет выглядеть примерно так:

    Те, кто любит красивый и оптимизированный код могут заметить, что блоки HTML.header и HTML.footer дублируются в обоих шаблонах (они же Представления) error.tpl.php и product.tpl.php , и наверняка захотят вынести их в Контроллер product.save.php :

    …. и нарушить таким образом основное правило MVC – разделяйте Контроллер, Модель и Представление.

    Тем не менее, дублирующийся код – однозначное Зло. Что же делать?
    Мы должны перейти от MVC к HMVC!
    Но это – тема отдельной статьи.

    Фреймворк Bootstrap: быстрая адаптивная вёрстка

    Пошаговый видеокурс по основам адаптивной верстки в фреймворке Bootstrap.

    Научитесь верстать просто, быстро и качественно, используя мощный и практичный инструмент.

    Верстайте на заказ и получайте деньги.

    Бесплатный курс "Сайт на WordPress"

    Хотите освоить CMS WordPress?

    Получите уроки по дизайну и верстке сайта на WordPress.

    Научитесь работать с темами и нарезать макет.

    Бесплатный видеокурс по рисованию дизайна сайта, его верстке и установке на CMS WordPress!

    *Наведите курсор мыши для приостановки прокрутки.

    Назад Вперед

    Удобный подход к веб-разработке: Модель MVC

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

    Расшифровывается MVC как "Модель-Вид-Контроллер" (Model-View-Controller). Давайте поговорим об этом подробнее.

    На сегодняшний день можно выделить два наиболее типичных способа создания веб-приложений (сайтов).

    Первый способ , назовем его "Классическим", предполагает то, что в одном файле может содержаться код различных языков программирования и разметки.

    Скажем, в начале файла производится запрос к базе данных для получения из нее какой-либо информации. Здесь мы имеем дело с языком SQL - специальным языком запросов, предназначенным для взаимодействия с базой данных.

    После этого, как правило, начинается html-разметка страницы (куда же без нее?). Причем внутри html-разметки в нужных местах производятся вставки PHP-кода, которые производят управление сайтом, являются его логикой. Итого мы имеем в одном файле: SQL, (X)HTML и PHP. Это уже - сборная солянка. Не забудьте добавить сюда еще CSS и немного Javascript для полноты картины, и, в итоге мы получим такую кашу, что в этом файле сам черт ногу сломит.

    Конечно, первое время вы будете помнить, что и как у вас в нем происходит, зачем что нужно, и где нужно вносить изменения для добавления / удаления / модификации определенного функционала. Однако я гарантирую вам, что уже через несколько месяцев вы будете смотреть на свой код с недоумением, силясь вспомнить, что же с чем связано, какие изменения "потянутся" после изменения какого-либо файла и т.п.

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

    Второй способ связан как раз-таки с применением схемы "Модель-Вид-Контроллер" .

    В чем суть данного подхода, и как его использование может помочь вам в работе?

    Основная идея данного подхода заключается в необходимости разделения однородных элементов по разным файлам . Если говорить очень упрощенно: один файл - один язык. Но это очень грубый пример. Такое встречается крайне редко.

    Помимо идеи разнесения разных языков в разные файлы, ключевой концепцией является также разделение файлов на группы в соответствии с теми функциями, которые они выполняют в приложении .

    Здесь мы начинаем с вами подходить к более подробному рассмотрению модели MVC.

    Данная модель подразумевает разделение всех файлов, задействованных при разработке сайта на три группы:

    1. Файлы группы "модель"
    2. Файлы группы "контроллер"
    3. Файлы группы "вид"

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

    Итак, давайте посмотрим на сравнительную схему модели MVC и "классического" способа разработки .


    В левой части вы видите как раз то, о чем мы говорили выше. Вверху страницы - SQL-запросы к базе. Затем разметка плюс вставки PHP.

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

    В контроллере находится логика приложения , т.е. то, что определяет его функционал.

    Вид же предназначен для показа конечному пользователю .

    Двунаправленные стрелки на схеме показывают то, что в парах "Модель - Контроллер" и "Контроллер - Вид" существует взаимосвязь. Рассмотрим эту взаимосвязь подробнее на примере следующей схемы.


    На этой схеме у нас добавилось два новых элемента: браузер пользователя и база данных. Рассмотрим в общих чертах весь цикл: от обращения браузера к определенному url-адресу до момента отображения страницы для пользователя:

    1. Пользователь вводит адрес, и браузер обращается к контроллеру.

    2. Контроллер обращается к модели.

    3. Модель обращается к базе данных (к примеру, для получения необходимой для вывода информации)

    4. Информация из базы попадает обратно в модель.

    5. Из модели информация передается в контроллер.

    6. Контроллер передает эту информацию в вид.

    7. Вид выводится в браузер с помощью контроллера.

    Такова общая схема работы данной модели. Как вы можете видеть, некоторым особняком на данной схеме стоят браузер и база данных. Действительно, браузер может обращаться только к контроллеру, так как контроллер является частью url-адреса . Посетитель не может обратиться к чему бы то ни было, кроме контроллера. Это важно понимать. Человек не может через адресную строку обращаться к видам или моделям. Он взаимодействует только с контроллером.

    В связи с этим можно говорить о контроллере как о своеобразном "распределительном центре". Смотрите сами: контроллер обрабатывает запросы пользователя, контроллер обращается к модели, контроллер же является посредником для вывода вида в браузер.

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

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

    Модель , кстати сказать, является необязательным элементом в схеме MVC . Вполне можно реализовать все, что нужно, не используя моделей в принципе. Естественно, в этом случае вы будете взаимодействовать с базой данных из файлов контроллера и вида. Как вы уже поняли, это не очень хороший тон. Коль скоро вы решили работать в рамках данной концепции, рекомендуется использовать модели и делать это по их прямому назначению.

    "Крайних" мы рассмотрели, а в центре схемы так и осталась наша троица, где происходят взаимодействия "Модель - Контроллер" и "Контроллер - Вид".

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

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

    Ведь что из себя представляет фреймворк? Если отбросить иностранное слово, то это просто каркас, определенная структура, по которой вам предлагается разрабатывать сайт. Данная структура является достаточно универсальной, чтобы создать с ее помощью практически любой сайт. При этом, что очень важно, фреймворк одновременно является и очень гибким, позволяя добиваться именно того, что вам нужно.

    Говоря другим языком, фреймоворк - это гибкие рамки, которые ограничивают вас в плане структуры, но не ограничивают в плане функционала.

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

    Среди других преимуществ модели MVC можно отметить разделение кода по функциональному признаку . Вам больше не нужно будет копаться в "каше" из SQL-запросов, разметки и PHP-кода. Если вам нужно что-то подправить или изменить, вы точно будете знать, какой именно файл вам надо править.

    Ниже вы можете видеть часть файла, относящегося к группе "виды":


    А вот кусок кода из модели:


    Так может выглядеть контроллер:


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

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

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

    Очевидны преимущества применения модели MVC в рамках фреймворка , например, того же CodeIgniter.

    Не секрет, что на каждом сайте есть большое количество похожих, а то и вовсе идентичных функций. Достаточно вспомнить форму обратной связи или постраничную навигацию. Это лишь наиболее яркие, "внешние" моменты. Еще больше сходств вы найдете в коде, который не виден обычному пользователю, в коде, который выполняется на сервере.

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

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

    И все это происходит в рамках концепции MVC, позволяя вам добиваться практически любых результатов средствами фреймворка. При этом вы получаете высокую степень читаемости кода. А что еще нужно для комфортной и результативной работы?

    Послесловие: Не забывайте о том, что любая структура, которая была создана для облегчения выполнения определенных задач, была создана именно для облегчения работы.

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

    Дмитрий Науменко

    P.S. Думаете, какой бы PHP-фреймворк освоить? Обратите внимание на CakePHP - он реализует рассмотренный выше паттерн MVC, и прямо сейчас вы можете получить небольшой вводный видеокурс, чтобы получить общее представление о возможностях этого фреймворка:

    Понравился материал и хотите отблагодарить?
    Просто поделитесь с друзьями и коллегами!


    Шаблон проектирования Модель-Представление-Контроллер (MVC) — это шаблон программной архитектуры, построенный на основе сохранения представления данных отдельно от методов, которые взаимодействуют с данными.

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

    В этой статье я опишу основные принципы, а также рассмотрю определение схемы построения и простой MVC PHP пример.

    Что такое MVC

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


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

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

    При этом «Модель » не имеет никакой связи или информации о том, что происходит с данными, когда они передаются компонентам «Представление » или «Контроллер ». Единственная задача «Модели » — обработка данных в постоянном хранилище, поиск и подготовка данных, передаваемых другим составляющим MVC .

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

    Представление

    Представление — это часть системы, в которой данным, запрашиваемым у «Модели », задается окончательный вид их вывода. В веб-приложениях, созданных на основе MVC , «Представление » — это компонент, в котором генерируется и отображается HTML -код.

    Представление также перехватывает действие пользователя, которое затем передается «Контроллеру ». Характерным примером этого является кнопка, генерируемая «Представлением ». Когда пользователь нажимает ее, запускается действие в «Контроллере».

    Существует несколько распространенных заблуждений относительно компонента «Представление ». Например, многие ошибочно полагают, что «Представление » не имеет никакой связи с «Моделью », а все отображаемые данные передаются от «Контроллера ». В действительности такая схема потока данных не учитывает теорию, лежащую в основе MVC архитектуры. В своей статье Фабио Чеваско описывает этот некорректный подход на примере одного из нетрадиционных MVC PHP фреймворков:

    «Чтобы правильно применять архитектуру MVC, между «Моделью» и «Представлением» не должно быть никакого взаимодействия: вся логика обрабатывается «Контроллером».

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

    Компоненту «Представление » никогда не передаются данные непосредственно «Контроллером ». Между «Представлением » и «Контроллером » нет прямой связи — они соединяются с помощью «Модели ».

    Контроллер

    Его задача заключается в обработке данных, которые пользователь вводит и обновлении «Модели ». Это единственная часть схемы, для которой необходимо взаимодействие пользователя.

    «Контроллер » можно определить, как сборщик информации, которая затем передается в «Модель » с последующей организацией для хранения. Он не содержит никакой другой логики, кроме необходимости собрать входящие данные. «Контроллер » также подключается только к одному «Представлению » и одной «Модели ». Это создает систему с односторонним потоком данных с одним входом и одним выходом в точках обмена данными.

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

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

    MVC в PHP

    Напишем на PHP веб-приложение, архитектура которого основана MVC . Давайте начнем с примера каркаса:


    Просто, не так ли?. Данный контроллер будет сохранен как books_controller.php и размещён в /app/controllers . Он содержит список функций, которые выполняют действия для нашего примера, а также другие функции для выполнения связанных с книгами операций (добавить новую книгу, удалить книгу, и так далее).

    Рабочая среда предоставляет нам множество готовых решений и нужно только сформировать список книг. Есть базовый класс, в котором уже определено базовое функционирование контроллера, таким образом, надо унаследовать свойства и функции этого класса (AppController является наследником Controller ).

    Все что нужно сделать в списке действий — вызвать модель для получения данных и затем выбрать вид для представления их пользователю. Вот как это делается.

    this->Book - это наша модель, и часть кода:

    $this->Book->findAllByCategory($category)

    сообщает модели, что нужно вернуть список книг по выбранной теме (мы рассмотрим модель позже).

    Метод set в строке:

    $this->set("books", $this->Book->findAllByCategory($category));

    Контроллер передаёт данные виду. Переменная books принимает данные, возвращённые моделью, и они становятся доступными для вида.

    Теперь остаётся только вывести на экран вид, но эта функция выполняется автоматически в cakePHP, если мы используем вид по умолчанию. Если мы хотим использовать другой вид, то надо явно вызвать метод render .

    Модель

    Модель даже ещё проще:

    class Book extends AppModel {

    Почему она пустая? Потому что она является наследником базового класса, который обеспечивает необходимую функциональность и нам нужно использовать соглашение об именах в CakePHP для того, чтобы рабочая среда выполняла все другие задачи автоматически. Например, cakePHP известно на основании имени, что данная модель используется в BooksController , и что она имеет доступ к таблице базы данных с именем books.

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

    Код сохраняем как book.php в папке /app/models .

    Вид

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



    Название
    Автор
    Цена








    Как можно заметить, вид создаёт не полноценную страницу, а лишь фрагмент HTML (таблицу в данном случае). Потому, что CakePHP обеспечивает другой способ для определения шаблона страницы, и вид вставляется в данный шаблон. Рабочая среда также обеспечивает нас некоторыми вспомогательными объектами для выполнения общих задач во время создания частей HTML страницы (вставка форм, ссылок, Ajax или JavaScript).

    Сохраняем вид как list.ctp (list — это имя действия, а ctp означает шаблон CakePHP) в папке /app/views/books (потому, что это вид для действия контроллера).

    Вот так выполняются все три компонента с помощью рабочей среды CakePHP!