При разработке сайта с использованием паттерна «модель-представление-контроллер», существует риск непонимания роли контроллеров, представлений и моделей. Это приводит к созданию огромных контроллеров и маленьких моделей, что в свою очередь делает крайне сложным тестировать и поддерживать ваше приложение. Целью этой главы является ознакомить вас с тем, какой код можно размещать в классе контроллера, какой - в шаблоне представления, а какой - в классе модели.
Смысл термина "тонкий контроллер" в том, что, как правило, в ваших классах контроллера вы размещаете только код, который:
$_GET
, $_POST
, $_FILES
и другие PHP-переменные);ViewModel
.Классу контроллера не следует:
Ниже, в качестве примера "тонкого" контроллера представлен класс CurrencyConverterController
.
Контроллер имеет метод действия "convert", который конвертирует определенное количество денег
из евро (EUR) в доллары (USD). Пользователь передает количество денег через GET-переменную "amount".
class CurrencyConverterController extends AbstractActionController
{
// Модель конвертера валюты
private $currencyConverter;
// Конструктор. Его цель - "внедрение" зависимостей.
public function __construct($currencyConverter)
{
$this->currencyConverter = $currencyConverter;
}
// Действие "convert" отображает количество конвертированных денег
public function convertAction()
{
// Получаем кол-во денег от GET
$amount = (float)$this->params()->fromQuery('amount', -1);
// Проверяем входящие данные
if($amount<0) {
// Money amount is missing
$this->getResponse()->setStatusCode(404);
return;
}
// Передаем данные модели CurrencyConverter
$convertedAmount = $this->currencyConverter->convertEURtoUSD($amount);
return new ViewModel([
'amount'=>$amount,
'convertedAmount'=>$convertedAmount
]);
}
}
Метод действия контроллера, представленный выше, делает следующее:
Принимает данные, переданные посетителем сайта (строка 16). Данные обычно являются частью
объекта Request
и могут быть извлечены с использованием метода контроллера getRequest()
или плагина контроллера Params
.
Проводит базовую проверку данных, переданных пользователем (строка 19), и, если данные отсутствуют (или недействительны), устанавливает код HTTP-ошибки (строка 21).
Передает количество денег модели CurrencyConverter
(строка 26), вызывая ее метод convertEURtoUSD()
. Этот
метод затем возвращает конвертированное количество денег.
Создает переменную-контейнер ViewModel
и передает ей полученные данные (строка 28).
Эта переменная-контейнер позже может использоваться в соответствующем шаблоне представления.
Так как вам нужно оставлять ваши контроллерами настолько тонкими, насколько это возможно, большинство бизнес-логики вашего приложения следует размещать в классах моделей. В грамотно написанном приложении «модель-представление-контроллер», модели получаются огромными. Класс модели может содержать код, который:
Осуществляет сложную фильтрацию и проверку данных. Так как данные, которые вы извлекаете, в контроллере, передается в ваше приложение из "внешнего мира", вам необходимо приложить немало усилий, чтобы проверить данные и убедиться, что они не сломают вашу систему. Результатом будет безопасный вебсайт, защищенный от хакерских атак.
Осуществляет управление данными. Ваши модели должны управлять данными: например, загружать данные из базы данных, сохранять в базу данных и изменять данные. Модели являются подходящим местом для хранения запросов БД, чтения файлов и их записи.
В классе модели не рекомендуется:
Обращаться к данным через HTTP-запрос, $_GET
, $_POST
и другие PHP-переменные. Распаковка этих
данных и передача их модели - это работа контроллера.
Создавать HTML или другой код для конкретного представления. Такой код может отличаться в зависимости от запроса пользователя, и лучше расположить его в шаблоне представления.
Если вы будете следовать этим принципам, вы заметите, что ваши модели легко тестируются, потому что у них есть четко идентифицируемые ввод и вывод данных. Вы можете написать юнит-тест, передающий некие данные на вход модели, извлекающий выходные данные и подтверждающий, что данные корректны.
Если вы не уверены в том, располагать определенный код в контроллере или модели, спросите себя: является ли это важной бизнес-логикой, которую нужно тщательно протестировать? Если ответ "да", вам следует расположить код в модели.
По причине того, что большинство алгоритмов хранятся в ваших моделях, ваши шаблоны представлений должны быть максимально просты, чтобы создать представление данных, полученных через переменную-контейнер. В шаблоне представления вы можете:
Хранить статическую HTML-разметку.
Извлекать данные из переменной-контейнера и выводить их в поток вывода PHP.
Если контроллер передал определенную модель через контейнер переменных, запросить данные у модели (например, вы можете извлечь строки таблицы из таблицы базы данных и вывести их).
Содержать простые PHP-операции управления потоком выполнения, такие как if
, foreach
, switch
и так далее.
Это позволяет изменять представление в зависимости от переменных, переданных контроллером.
Шаблону представления не рекомендуется:
Обращаться к данным HTTP-запроса и суперглобальным PHP-переменным.
Создавать модели, управлять ими и изменять форму приложения.
Если вы будете следовать этим принципам, вы заметите, что ваши представления легко могут быть заменены без необходимости изменять бизнес-логику вашего приложения. Например, вы легко сможете изменять дизайн ваших веб-страниц и даже использовать меняющуюся тему (theme).