Несмотря на то что с концепцией MVC знаком практически каждый веб-разработчик, её применение в реальных проектах часто вызывает затруднения. Главная идея MVC — повторное использование кода и разделение проблем. В данном разделе будут описаны общие принципы, которые помогут следовать MVC в вашем приложении.
Предположим, что веб-приложение состоит из нескольких подприложений, таких как:
Подприложения могут быть реализованы в виде модулей или как приложение, которое содержит код, общий для нескольких подприложений.
Модели представляют внутреннюю структуру данных приложения.
Они часто являются общими для нескольких подприложений.
Например, модель LoginForm
может быть использована как в пользовательской, так
и в административной части приложения. Модель News
может использоваться
консольными командами, API и front/back частями приложения. Поэтому модели
должны содержать свойства, представляющие конкретные данные;
должны включать в себя бизнес-логику (например, правила валидации), чтобы убедиться в том, что данные соответствуют предъявленным требованиям;
могут содержать код для работы с данными. К примеру, модель SearchForm
,
помимо хранения поисковых данных, может содержать метод search
, который этот поиск
осуществляет.
Иногда следование последнему правилу делает модель очень
толстой, то есть содержащей очень много кода в одном классе. Это может привести
к трудностям поддержки кода в том случае, если модель используется для выполнения
различных задач. К примеру, модель News
может содержать метод getLatestNews
,
который используется только пользовательской частью и метод getDeletedNews
,
который используется только административной частью. Для небольших и средних
приложений это допустимо. Для крупных же приложений в целях упрощения
дальнейшей поддержки кода можно сделать следующее:
Создать модель NewsBase
, содержащую только код, общий для подприложений
(пользовательской и административной частей).
В каждом подприложении создать модель News
, наследуемую от NewsBase
и
определить в ней специфичные для подприложения методы.
Таким образом, если применить это к рассмотренному выше примеру, необходимо
добавить модель News
с методом getLatestNews
в пользовательскую часть и
ещё одну модель News
с методом getDeletedNews
в административную часть.
В общем случае, модели не должны напрямую взаимодействовать с пользователем. То есть:
не должны использовать $_GET
, $_POST
или другие подобные переменные, напрямую
получаемые из запроса пользователя, так как модели могут использоваться в
совершенно других подприложениях (например, в модульных тестах или API), в
которых эти переменные недоступны. Все переменные, относящиеся к запросу
пользователя, должны обрабатываться в контроллере;
не должны генерировать HTML или другой код представления, так как он может изменяться в зависимости от нужд пользователя (то есть, пользовательская часть и административная часть могут показывать новости в совершенно разном формате). Такой код должен генерироваться в представлениях.
Представления отвечают за отображение моделей в необходимом пользователю формате. В общем случае представления
должны, главным образом, содержать разметку, такую как HTML, и простой PHP код, используемый для обхода, форматирования и отображения данных;
не должны напрямую обращаться к базе данных. Этим должны заниматься модели;
не должны напрямую обращаться к $_GET
, $_POST
и другим переменным, получаемым
из запроса пользователя. Эту задачу должен выполнять контроллер. Представления
должны использоваться только для оформления данных, полученных от контроллера и модели;
могут напрямую обращаться к свойствам и методам контроллера или моделей. Однако это должно делаться только в целях отображения данных.
Представления можно использовать повторно несколькими способами:
Общий шаблон: в него можно вынести разметку, общую для всех страниц. Например, шапку и подвал.
Части шаблона: используются внутри других шаблонов и, как правило, не
используются с общим шаблоном. К примеру, часть шаблона _form.php
можно
использовать для отображения формы ввода модели, которая будет использоваться
как при её создании, так и при редактировании.
Виджеты: используются в том случае, когда часть шаблона включает в себя слишком много логики. При этом логика переносится в класс виджета. Виджет, генерирующий большое количество разметки, может использовать свои шаблоны представлений.
Хелперы (помощники): в шаблонах часто требуется выполнять небольшие задачи, такие как форматирование данных или генерация HTML-тегов. Вместо того чтобы вставлять код напрямую в шаблоны, можно поместить его в класс-хелпер и использовать в шаблонах этот класс. Пример такого подхода можно найти в классе CHtml, который помогает генерировать часто используемый HTML код. Для того чтобы избежать явного подключения классов, хелперы можно разместить в отдельной директории, указанной в import.
Контроллеры — связующее звено, соединяющее модели, представления и другие компоненты в рабочее приложение. Контроллер отвечает за обработку запросов пользователя. Поэтому контроллер
может обращаться к $_GET
, $_POST
и другим переменным PHP, получаемым из
запроса пользователя;
может создавать экземпляры моделей и управлять ими. К примеру, в типичном действии
обновления модели контроллер может сначала создать экземпляр модели, затем
заполнить его данными из $_POST
и, в случае успешного сохранения модели, перенаправить
браузер пользователя на страницу созданной модели. Стоит отметить, что само сохранение
модели должно быть реализовано в классе модели, а не в контроллере;
не должен содержать SQL-запросы. Их лучше держать в моделях;
не должен содержать HTML и другую разметку. Её стоит вынести в представления.
В хорошо спроектированном MVC-приложении контроллеры обычно очень тонкие и содержат только несколько десятков строк кода. В то же время, модели очень толстые и содержат большую часть кода, связанную с обработкой данных, так как структура данных и бизнес-логика, содержащиеся там, обычно довольно специфичны для конкретного приложения. Логика контроллера, наоборот, довольно типична и может быть вынесена в базовые классы.
Found a typo or you think this page needs improvement?
Edit it on github !
Signup or Login in order to comment.