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

Авто 24.04.2019
Авто

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

MVC - не только архитектура программного обеспечения, но и концепция разработки. Я не говорю, что данная статья вам расскажет все секреты MVC, но первоначальное понимание получить точно можно. А дальше http://pogugli.com/?74600

Концепция MVC (Model-View-Controller: модель-вид-контроллер) очень часто упоминается в мире веб программирования в последние годы. Каждый, кто хоть как-то связан с разработкой веб приложений, так или иначе сталкивался с данным акронимом. Сегодня мы разберёмся, что такое - концепция MVC, и почему она стала популярной.

Древнейшая история

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

Впервые она была описана в 1979 году, конечно же, для другого окружения. Тогда не существовало концепции веб приложения. Tim Berners Lee (Тим Бернерс Ли) посеял семена World Wide Web (WWW) в начале девяностых и навсегда изменил мир. Шаблон, который мы используем сегодня, является адаптацией оригинального шаблона к веб разработке.

Бешеная популярность данной структуры в веб приложениях сложилась благодаря её включению в две среды разработки, которые стали очень популярными: Struts и Ruby on Rails. Эти две среды разработки наметили пути развития для сотен рабочих сред, созданных позже.

MVC для веб приложений

Идея, которая лежит в основе конструкционного шаблона MVC, очень проста: нужно чётко разделять ответственность за различное функционирование в наших приложениях:

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

Контроллер (Controller)

Контроллер управляет запросами пользователя (получаемые в виде запросов HTTP GET или POST, когда пользователь нажимает на элементы интерфейса для выполнения различных действий). Его основная функция - вызывать и координировать действие необходимых ресурсов и объектов, нужных для выполнения действий, задаваемых пользователем. Обычно контроллер вызывает соответствующую модель для задачи и выбирает подходящий вид.

Модель (Model)

Модель - это данные и правила, которые используются для работы с данными, которые представляют концепцию управления приложением. В любом приложении вся структура моделируется как данные, которые обрабатываются определённым образом. Что такое пользователь для приложения - сообщение или книга? Только данные, которые должны быть обработаны в соответствии с правилами (дата не может указывать в будущее, e-mail должен быть в определённом формате, имя не может быть длиннее Х символов, и так далее).

Модель даёт контроллеру представление данных, которые запросил пользователь (сообщение, страницу книги, фотоальбом, и тому подобное). Модель данных будет одинаковой, вне зависимости от того, как мы хотим представлять их пользователю. Поэтому мы выбираем любой доступный вид для отображения данных.

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

Вид (View)

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

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

Разберём пример

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

У нас есть определённый контроллер для обработки всех действий, связанных с книгами (просматривать, редактировать, создавать и так далее). Давайте назовем его books_controller.php в нашем примере. Также нам нужна модель, например, book_model.php , которая обрабатывает данные и логику, связанные с позицией в магазине. В заключение, нам нужно несколько видов для представления данных, например, список книг, страница для редактирования и так далее.

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

Контроллер (books_controller.php) получает запрос пользователя (запрос HTTP GET или POST). Мы можем организовать центральный контроллер, например, index.php, который получает запрос и вызывает books_controller.php.

Контроллер проверяет запрос и параметры, а затем вызывает модель(book_model.php), запрашивая у неё список доступных книг по теме фэнтези .

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

Контроллер использует подходящий вид для представления данных пользователю . Если запрос приходит с мобильного телефона, используется вид для мобильного телефона; если пользователь использует определённое оформление интерфейса, то выбирается соответствующий вид, и так далее.

В чем преимущества?

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

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

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

А зачем использовать рабочую среду?

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

Рассмотрим cakePHP в качестве примера рабочей среды MVC. После установки у вас будет три основных директории:

  • cake/
  • vendors/

Папка app является местом размещения ваших файлов. Это место для разработки вашей части приложения.

В папке cake размещаются файлы cakePHP (функциональность рабочей среды).

Папка vendors служит для хранения библиотек PHP сторонних разработчиков.

Ваше рабочее пространство (директория app) имеет следующую структуру:

  • app/
    • config/
    • controllers/
    • locale/
    • models/
    • plugins/
    • tests/
    • vendors/
    • views/
    • webroot/

Вам нужно размещать ваши контроллеры в директории controllers , модели в директории models и виды в директории views !

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

Использование рабочей среды для нашего примера

Так как данный урок не имеет целью показать процесс создания приложения с помощью cakePHP, то мы покажем только код для модели, контроллера и вида с комментариями о преимуществах использования рабочей среды MVC. Код специально упрощён и непригоден для использования в реальном приложении.

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

Итак, как только пользователь нажимает кнопку, браузер запрашивает данный url:

1 www.ourstore.com/books/list/fantasy

CakePHP форматирует URL по шаблону /controller/action/param1/param2 , где action - это функция, которая вызывается контроллером. В старом классическом виде url будет выглядеть так:

1 www.ourstore.com/books_controller.php?action=list&category=fantasy

Контроллер

В рабочей среде cakePHP, наш контроллер будет выглядеть так:

1
class BooksController extends AppController {

function list($category) {

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

}

function add() { ... ... }

function delete () { ... ... }

... ... } ?>

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

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

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

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

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

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

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

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

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

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

Модель

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

1
class Book extends AppModel {

}

?>

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

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

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

Вид

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

1














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

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

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

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

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

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

Архитектура программы или компьютерной системы - это структура или структуры системы, которые включают элементы программы, видимые извне свойства этих элементов и связи между ними [Басс (Bass)].

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

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

Так что же такое архитектура программы?

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

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

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

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

Отношения между компонентами системы также определяются ее архитектурой.

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

Уровни абстракции

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

Модули низкого уровня максимально автономны, они не зависят от других частей программы. Хорошо спроектированные модули изолируют "внешний мир" от тонкостей решения поставленной перед ними задачи. Вызывающая сторона знает лишь интерфейс модуля (внешние функции), внутренняя часть для нее закрыта.

Рассмотрим в качестве примера галерею фотографий. Информация об изображениях и пользователях хранится в БД, пользовательский интерфейс разделен на клиентскую часть и панель администратора.

Структура программы могла бы быть такой, как на рисунке ниже:


В этом примере прослеживаются три уровня абстракции .

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

Архитектура MVC

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

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

Что же предлагает нам MVC для отделения логики приложения от пользовательского интерфейса?

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

  1. Модель (Model) . Модель предоставляет данные (обычно для Представления), а также реагирует на запросы (обычно от Контроллера), изменяя свое состояние;
  2. Представление (View) . Отвечает за отображение информации (пользовательский интерфейс);
  3. Контроллер (Controller) . Интерпретирует данные, введенные пользователем, и информирует модель и представление о необходимости соответствующей реакции.

На рисунке ниже показаны отношения между компонентами каркаса. Проиллюстрируем рисунок небольшим примером.


Представьте форму, где можно ввести текст, нажать кнопку Edit и получить его транслитерацию:


Повторим шаги, изображенные на схеме:

  1. Пользователь нажимает кнопку Edit, при этом Представление (View) посылает сообщение Контроллеру (Controller): "Команда: edit"
  2. Контроллер принимает сообщение и обращается к Модели (Model), вызывая метод Edit() .
  3. В результате модель меняет свое состояние (запомненный в ней транслитерированный текст) и оповещает об этом представление: "Событие: changed".
  4. Представление принимает сигнал и обращается к модели за новым значением результата, вызывая ее метод Get() .

Реализация MVC

Реализация MVC предполагает объектно-ориентированный подход (ООП). Однако шаблон проектирования - это всего лишь набор решений. Адаптируем их для PHP без применения ООП. Упрощение делается для того, чтобы сконцентрироваться на сути разделения логики, а также для того, чтобы материал смог применить читатель, не знакомый с ООП.

Рассмотрим снова пример с галереей фотографий .
У нее есть два режима просмотра:

  1. Режим просмотра уменьшенных изображений (всех сразу);
  2. Режим просмотра фотографии полного размера (одной).

Также есть возможность загружать фотографии на сервер. Дополнительно реализуем поддержку типов визуализации, чтобы оценить гибкость каркаса.

На сайте будут две точки входа :

  1. index.php (просмотр галереи);
  2. photo.php (просмотр полноразмерной фотографии).
Эти два файла будем считать Контроллерами .

В качестве Модели будет выступать модуль, обеспечивающий работу с хранилищем изображений. Назовем его gallery.php и поместим в папку model .

В роли Представления будут выступать HTML-шаблоны, они будут находиться в папке templates . Что такое шаблоны и для чего они нужны - будет видно дальше.

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

Просмотр галереи будет иметь два типа визуализации:

  1. В виде таблицы (по умолчанию);
  2. В виде списка.
Нам потребуются четыре шаблона:
  1. main.php (каркас страницы);
  2. content_index_table.php (табличный вид содержимого галереи);
  3. content_index_list.php (списочный вид содержимого галереи);
  4. content_photo.php (содержимое страницы просмотра фотографии).

Получается следующая структура сайта:


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

Модель

Начнем с реализации Модели. В коде ниже приведен не полностью для минимизации и лучшей наглядности примера.

Мы определили лишь интерфейс Модели, оставив реализацию пропущенной. Однако для примера реализации каркаса MVC она вовсе и не нужна.

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

Теперь рассмотрим шаблоны. Начнем с общего каркаса страницы:

<?=$title?>

Вас не должно смущать, что в шаблоне используются непонятно откуда взявшиеся переменные $title и $content . Их подставит Контроллер. Но об этом позже.

- это сокращенный вариант записи .

Его удобно использовать в шаблонах. Также в шаблонах удобнее использовать альтернативные варианты записи конструкций if-else , foreach , for , while . Выглядят они так:

If (<условие>): <тело> endif; foreach (<инициализация цикла>): <тело> endforeach;

Остальные шаблоны будут подставляться в main.php таким образом:

В примерах ниже, приведен их код:

Код templates/content_index_table.php

Таблица | Список

"> " />



Код templates/content_index_list.php

Таблица | Список

"> " />



templates/content_photo.php: Назад

" />

Контроллер

И, наконец, соберем все вместе, описав наши два Контроллера. Их задача заключается в обработке запроса, выборе шаблона и подстановке нужных шаблону данных. Данные берутся, как правило, из модели.

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

Код index.php

Контроллер просмотра фотографии еще проще:

Код photo.php

В заключение

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

Для реализации модели MVC лучше выбрать объектно-ориентированный подход .

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

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

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

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

Что такое MVC

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

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

Модель

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

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

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

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

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

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

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

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

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

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

Контроллер

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

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

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

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

MVC в PHP

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

string = "MVC + PHP = Awesome!"; } } controller = $controller; $this->model = $model; } public function output(){ return "

" . $this->model->string . "

"; } } model = $model; } }

У нас есть проект с несколькими основными классами для каждой части шаблона. Теперь нужно настроить взаимосвязь между ними:

output();

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

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

string = “MVC + PHP = Awesome, click here!”; } } controller = $controller; $this->model = $model; } public function output() { return "

model->string . "

"; } } model = $model; } public function clicked() { $this->model->string = “Updated Data, thanks to MVC and PHP!”; } }

Мы расширили программу базовым функционалом. Настройка взаимодействий между компонентами теперь выглядит следующим образом:

{$_GET["action"]}(); } echo $view->output();

В этом руководстве Вы узнаете, как построить простую систему по архитектуре MVC (Model-View-Controller, Модель-Отображение-Контроллер) на PHP 5.1 с использованием возможностей библиотеки SPL (Standard PHP Library, Стандартная Библиотека PHP).

Введение

Добро пожаловать в первое полноценное руководство для PHP 5. Вам понадобится PHP 5.1 с установленной библиотекой SPL, так как мы воспользуемся некоторыми из самых последних возможностей PHP 5.

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

Одна точка входа

Одной из важных вещей в MVC является одна точка входа в приложение вместо кучи PHP-файлов, делающих примерно следующее:

У нас будет один файл, обрабатывающий все запросы. Это значит, что нам не придётся мучиться с подключением global.php каждый раз, когда нам нужно создать новую страницу. Эта «одна точка входа» будет называться index.php и на данный момент будет такой:

Как Вы можете заметить, этот скрипт пока ещё ничего не делает, но погодите минутку.

Чтобы направить все запросы на главную страницу, мы воспользуемся mod_rewrite и установим в.htaccess директиву RewriteRule. Вставим следующий код в файл.htaccess и сохраним его в той же директории, что и index.php:

RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?route=$1

Сперва мы проверяем, существует ли запрашиваемый файл, используя директиву RewriteCond, и, если нет, то перенаправляем запрос на index.php. Такая проверка на существование файла необходима, так как иначе index.php будет пытаться обрабатывать все запросы к сайту, включая запросы на изображения. А это нам как раз и не надо.

Если у Вас нет возможности использовать.htaccess или mod_rewrite, то Вам придётся вручную адресовать все запросы к index.php. Другими словами, все ссылки должны будут иметь вид «index.php?route=[здесь-идёт-запрос]». Например, «index.php?route=chat/index».

Теперь, когда все запросы идут через одну точку входа, мы можем начать написание скрипта index.php. Первая вещь, которую мы должны сделать, это инициализация системы. Создадим директорию includes, а в ней файл startup.php (он будет у нас файлом инициализации). Вставим следующий код в index.php:

В этом примере мы объявляем некоторую константу, узнаём, где лежат файлы системы, а также проверяем, что версия PHP, ну, хотя бы, 5.1.

Следующая вещь, которую необходимо сделать, это объект Registry (журнал, реестр) для хранения глобальных значений. Он будет передаваться в отдельные объекты системы и использоваться для доступа к глобальным значениям, причём без необходимости обозначать переменные как «global» или обращаться к массиву $GLOBALS. Почитайте статью «Использование глобальных значений в PHP» для более подробной информации об объекте реестра.

Добавьте следующий код в файл startup.php после того кода, что приведён в предыдущем примере:

$registry = new Registry;

Если сейчас попробовать запустить систему, то можно увидеть следующую ошибку:

Fatal error: Class "Registry" not found in g:\Projects\PHP\content\simple mvc php5\demo\includes\startup.php on line 12

Это, конечно, не большой сюрприз для нас, ведь мы ещё не написали сам класс Registry. Файл с классом можно было бы просто подключить, используя функцию include() (Прим. пер.: кстати говоря, include() не такая уж и функция, а всё-таки выражение языка, управляющая структура, если смотреть по ману), но давайте воспользуемся одной из новых возможностей PHP 5: __autoload().

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

Вставьте этот код перед кодом из предыдущего примера:

// Загрузка классов «на лету» function __autoload($class_name) { $filename = strtolower($class_name) . ".php"; $file = site_path . "classes" . DIRSEP . $filename; if (file_exists($file) == false) { return false; } include ($file); }

Наша функция __autoload() берёт имя класса, переданное ей как аргумент, и проверяет, существует ли файл с похожим именем в директории с классами. Если файла нет, то функция просто вернёт false и выскочит фатальная ошибка. Но если файл существует, он будет загружен. Т.е. объявится необходимый класс, и никакой ошибки не будет.

Мы ещё не создали сам класс Registry, поэтому ошибка всё ещё будет появляться. Давайте же займёмся этим.

Создание класса Registry

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

Для начала создадим директорию classes и в ней файл registry.php. Вставим следующий код в registry.php:

Теперь у нас есть «скелет» класса Registry и нужно нагрузить его методами. Напишем 2 метода: set(), чтобы устанавливать значения и get(), чтобы значения получать. Также можно написать метод remove() для удаления значений. Добавим эти методы в класс Registry:

Function set($key, $var) { if (isset($this->vars[$key]) == true) { throw new Exception("Unable to set var `" . $key . "`. Already set."); } $this->vars[$key] = $var; return true; } function get($key) { if (isset($this->vars[$key]) == false) { return null; } return $this->vars[$key]; } function remove($var) { unset($this->vars[$key]); } ?>

Эти методы простые, они устанавливают, получают и удаляют элементы из массива $vars, который является атрибутом класса. В методе set() мы заодно проверяем, не существует ли уже значение с указанным ключом, и, если существует, то мы генерируем исключение. Это нужно, чтобы избежать случайной перезаписи значений.

Теперь у нас есть полноценный класс Registry, но мы не будем останавливаться на этом. Воспользуемся одной из возможностей библиотеки SPL: ArrayAccess. SPL (сокращённо от Standard PHP Library, Стандартная Библиотека PHP) - это коллекция интерфейсов и классов, предназначенных для решения стандартных проблем. Один из интерфейсов SPL, ArrayAccess, может быть использован, чтобы предоставить доступ к объекту, как к обычному массиву. Посмотрим на такой пример:

set ("name", "Dennis Pallett"); // Получаем значение, используя get() echo $registry->get ("name"); // Получаем значение, используя доступ как к массиву echo $registry["name"] ?>

Фокус в том, что $registry становится как бы массивом, хотя на самом деле это объект. Конечно, ArrayAccess не даёт никаких особых преимуществ, но он позволяет сократить объём кода, так как не придётся каждый раз писать «->get()». Чтобы воспользоваться этим интерфейсом, нужно исправить первую строчку класса («Class Registry») таким образом:

Class Registry Implements ArrayAccess {

Ключевое слово «Implements» говорит интерпретатору, что этим классом мы реализуем интерфейс, чем на самом деле ArrayAccess и является.

Класс, реализующий интерфейс ArrayAccess, должен иметь следующие методы:

Function offsetExists($offset) { return isset($this->vars[$offset]); } function offsetGet($offset) { return $this->get($offset); } function offsetSet($offset, $value) { $this->set($offset, $value); } function offsetUnset($offset) { unset($this->vars[$offset]); }

Эти методы должны быть понятны сами по себе. Дополнительную информацию можно найти в документации SPL.

Теперь, реализовав интерфейс ArrayAccess, мы можем обращаться к объекту, как к обычному массиву. Это наглядно продемонстрировано, как в предыдущем примере, так и в этом:

get ("name"); // Получаем значение, используя доступ как к массиву echo $registry["name"] ?>

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

Модель

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

Нам нужно написать код, необходимый для установки соединения с БД и поместить его в index.php. Существует множество замечательных библиотек для работы с БД (включая мою собственную, AutoCRUD), но в PHP 5 уже есть такая библиотека – PDO. Поэтому нет нужды использовать какую-либо другую.

Вставим следующий код в файл index.php (после подключения файла инициализации):

# Соединяемся с БД $db = new PDO("mysql:host=localhost;dbname=demo", "", ""); $registry->set ("db", $db);

В этом примере мы сначала создаём новый экземпляр библиотеки PDO и соединяемся с нашей БД MySQL. Потом делаем переменную $db доступной глобально при помощи нашего класса Registry.

Модельная компонента нашей системы готова, поэтому давайте перейдём к написанию контроллера.

Написание контроллера подразумевает также и написание класса Router, ответственного за загрузку нужного контроллера в зависимости от запроса (вспомните, в index.php через URL передаётся переменная $route).

Класс Router

Класс Router будет разбирать запрос, а потом загружать требуемый контроллер. Создадим «скелет» класса:

registry = $registry; } } ?>

Затем добавим следующие строки в index.php:

# Загружаем router $router = new Router($registry); $registry->set ("router", $router);

Первая вещь, которую мы напишем, это метод setPath() для установки директории, где будут лежать все наши контроллеры. Метод выглядит следующим образом и должен быть добавлен в класс Router:

Function setPath($path) { $path = trim($path, "/\\"); $path .= DIRSEP; if (is_dir($path) == false) { throw new Exception ("Invalid controller path: `" . $path . "`"); } $this->path = $path; }

Потом добавим следующие строки в index.php:

$router->setPath (site_path . "controllers");

Теперь, когда мы установили путь до наших контроллеров, напишем сам метод, ответственный за загрузку контроллера. Этот метод будет называться delegate(), он и будет анализировать запрос. Первый кусочек этого метода такой:

Function delegate() { // Анализируем путь $this->getController($file, $controller, $action, $args);

Как Вы можете видеть, он использует ещё один метод, getController(), чтобы получить название контроллера и несколько других переменных. Этот метод выглядит так:

Private function getController(&$file, &$controller, &$action, &$args) { $route = (empty($_GET["route"])) ? "" : $_GET["route"]; if (empty($route)) { $route = "index"; } // Получаем раздельные части $route = trim($route, "/\\"); $parts = explode("/", $route); // Находим правильный контроллер $cmd_path = $this->path; foreach ($parts as $part) { $fullpath = $cmd_path . $part; // Есть ли папка с таким путём? if (is_dir($fullpath)) { $cmd_path .= $part . DIRSEP; array_shift($parts); continue; } // Находим файл if (is_file($fullpath . ".php")) { $controller = $part; array_shift($parts); break; } } if (empty($controller)) { $controller = "index"; }; // Получаем действие $action = array_shift($parts); if (empty($action)) { $action = "index"; } $file = $cmd_path . $controller . ".php"; $args = $parts; }

Пробежимся по этому методу. Сначала он берёт значение переменной $route из запроса, потом разбивает его на части с помощь функции explode(). Например, запрос «members/view» преобразуется в такой массив: array(‘members’, ‘view’).

Потом при помощи цикла foreach он проходит по каждой части и проверяет, является ли эта часть директорией. Если является, то он приписывает её к пути до файла и проверяет следующую часть. Это позволяет поместить контроллеры в поддиректориях и, таким образом, получить иерархию контроллеров. Если же текущая часть запроса не является директорией, но является файлом, она сохраняется в переменную $controller, и мы выходим из цикла, так как нашёлся контроллер, который нам нужен.

После цикла мы проверяем переменную с именем контроллера. Если она пустая, то используем контроллер «index», который будет у нас контроллером по умолчанию. Потом метод определяет действие, которое необходимо выполнить. Контроллер – это класс, который состоит из нескольких методов. Действие же указывает на конкретный метод. Если действие не указано, будем использовать «index» - действие по умолчанию.

И, наконец, получаем полный путь до файла контроллера, объединяя три переменные: путь, имя контроллера и расширение «php».

Теперь, когда мы проанализировали запрос, пора вызывать метод delegate() для загрузки контроллера и выполнения действия. Полностью метод delegate() выглядит так:

Function delegate() { // Анализируем путь $this->getController($file, $controller, $action, $args); // Файл доступен? if (is_readable($file) == false) { die ("404 Not Found"); } // Подключаем файл include ($file); // Создаём экземпляр контроллера $class = "Controller_" . $controller; $controller = new $class($this->registry); // Действие доступно? if (is_callable(array($controller, $action)) == false) { die ("404 Not Found"); } // Выполняем действие $controller->$action(); }

Проанализировав запрос при помощи метода getController(), мы проверяем, существует ли в действительности файл, и, если нет, то возвращаем простое сообщение об ошибке.

После этого мы подключаем файл с контроллером и создаём экземпляр его класса, называться который должен «Controller_[имя]». Чуть позже мы поговорим о контроллерах более подробно.

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

Написав полностью метод delegate(), добавим следующую строчку в файл index.php:

$router->delegate();

Если попробовать сейчас запустить систему, то мы увидим следующую ошибку (разумеется, если директории controllers ещё нет):

Fatal error: Uncaught exception "Exception" with message "Invalid controller path: `g:\Projects\PHP\content\simple mvc php5\demo\controllers\`" in g:\Projects\PHPit\content\simple mvc php5\demo\classes\router.php:18 Stack trace: #0 g:\Projects\PHP\content\simple mvc php5\demo\index.php(13): Router->setPath("g:\Projects\PHP...") #1 {main} thrown in g:\Projects\PHP\content\simple mvc php5\demo\classes\router.php on line 18

Или же мы увидим ошибку «404 Not Found», так как ещё нет ни одного контроллера. Но этим-то мы сейчас и займёмся.

Контроллер

Контроллеры в нашей MVC-системе будут достаточно простыми и потребуют совсем немного времени. Во-первых, удостоверимся, что директория controllers существует. Создадим файл controller_base.php в директории classes и вставим в него следующий код:

registry = $registry; } abstract function index(); } ?>

Этот абстрактный класс будет родительским классом для всех наших контроллеров. Он будет делать всего лишь две вещи: сохранять локальную копию класса Registry и при помощи абстрактного метода index() заставлять все дочерние контроллеры реализовывать этот метод.

Напишем наш первый контроллер. Создадим файл index.php в директории controllers и вставим в него такой код:

Только что мы создали наш первый контроллер и, если попробовать запустить систему, то можно увидеть следующее:

Это означает, что класс Router выполнил свою работу и запустил требуемое действие из требуемого контроллера. Давайте напишем ещё один контроллер, который будет соответствовать запросу «members/view». Создадим файл members.php в директории контроллеров и вставим в него такой код:

Теперь зайдём в нашу MVC-систему по запросу «members/view» или же «index.php?route=members/view». Мы должны увидеть такой результат:

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

Теперь, когда у нас есть контроллеры, осталась лишь одна вещь: «V» или «View» («Отображение»).

Отображение

Как и в случае с моделями, есть несколько различных вариантов создания компоненты View в MVC-системе. Мы могли бы научить класс Router автоматически загружать ещё один файл, названный как-нибудь так: «view_{имя}.php». Но чтобы сделать руководство более понятным, напишем класс Template, который будет заниматься выводом шаблонов.

Сначала создадим файл template.php в директории classes и вставим в него следующий код:

registry = $registry; } } ?>

Теперь у нас есть основная структура нашего класс Template. Следующим шагом добавим такой код в файл index.php прямо перед строками, связанными с классом Router:

# Создаём объект шаблонов $template = new Template($registry); $registry->set ("template", $template);

Так как нам понадобится использовать значения из моделей и контроллеров, то напишем метод set() для установки переменных, доступных в шаблонах. Посмотрим на пример:

Function set($varname, $value, $overwrite=false) { if (isset($this->vars[$varname]) == true AND $overwrite == false) { trigger_error ("Unable to set var `" . $varname . "`. Already set, and overwrite not allowed.", E_USER_NOTICE); return false; } $this->vars[$varname] = $value; return true; } function remove($varname) { unset($this->vars[$varname]); return true; }

Методы set() и remove() достаточно простые и используются, соответственно, для установки и удаления переменных.

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

Голосов: 745 | Просмотров: 8082

В паттерне "Модель - представление - контроллер " модель представляет данные приложения и связанную с ними бизнес-логику. Модель может быть представлена одним объектом или сложным графом связанных объектов. В приложении для плат­формы Java ЕЕ данные инкапсулируются в объектах предметной области, часто раз­вертываемых в EJB-модуле. Данные передаются в БД и из нее в объектах передачи данных (ОТО), и к ним обращаются с помощью объектов доступа к данным (ОАО).

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

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

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

Контроллер и представление расположены на веб­-уровне. Представление, вероятнее всего, будет создано из JavaServer Faces (JSF) или JavaServer Pages (JSP) с помощью языка выражений (EL). Контроллер обыч­но представляет собой сервлет, получающий НТТР-запросы от пользователя.

Часто MVC сочетается с другими паттернами, такими как "Команда" (или "Действие"), "Стратегия", "Компоновщик" и " ".

Впервые этот паттерн упоминался еще до создания Интернета в современном виде, в статье, опубликованной в декабре 1979 года работавшим тогда в компании Xeror SmallTalk-программистом Трюгве Реенскауг .

И хотя элементы MVC этого паттерна были описаны более 35 лет назад, они удивительно точно соответствуют современному их использованию в веб-прило­жениях.

Следующий рисунок показывает пользователя, выполняющего запрос к контроллеру. Контроллер обрабатывает запрос путем обновления модели и визуализации ново­го представления, которое затем отправляется пользователю.

Диаграмма паттерна MVC

Паттерн MVC существует в множестве разных форм. Две наиболее известные обычно называются тип I и тип II .

Типы паттерна MVC:

  • MVC тип I . Этот тип представляет собой странично-ориентированный подход,при котором представление и контроллер существуют в виде одной сущности, именуемой "представление - контроллер". При этом подходе логика контрол­лера реализуется в представлении, таком кaк JSF. Все выполняемые контрол­лером задания, включая извлечение атрибутов и параметров НТТР-запроса, вызов бизнес-логики и управление НТТР-сеансом, встроены в представление с помощью скриптлетов и библиотек тегов. Тип I сильно связывает формирование представления с последовательностью выполняемых приложением дей­ствий, затрудняя тем самым сопровождение.
  • MVC тип II . Проблемы с сопровождением в типе I преодолены в типе II благо­даря вынесению логики контроллера из представления в сервлет, при этом визуализация данных остается представлению.

Главное различие между типом I и типом II - в местонахождении логики кон­троллера: в типе I она в представлении, а в типе II - в сервлете.

Многие фреймворки, такие как Spring MVC , Struts , Grails и Wicket , реализуют свою собственную версию паттерна MVC типа II. Например, Spring MVC включает концепцию сервлета-диспетчера, взаимодействующего с НТТР-запросами и выпол­няющего делегирование контроллеру, а также содержит представление (и преобра­зователь представления) и обработчики.

Следующий рисунок демонстрирует диаграмму реализации паттерна MVC в Spring.

Диаграмма реализации паттерна MVC в Spring



Рекомендуем почитать

Наверх