Понимание взглядов MVC в PHP
Мне кажется, проблемы с пониманием концепции Views в MVC, они, в соответствии с тем, что я прочитал, являются уровнем, который управляет представлением в приложении, но многие материалы, которые я читал, кажутся различными в этом отношении. дело в этом от PHP Master.com.
Представление - это класс с функциями, которые возвращают некоторый HTML-код. Где остальная часть моего HTML-кода? Должен ли он быть помещен в независимые HTML-страницы, которые обращаются к этому коду представления?
В этой статье с php-html.net View представляет собой простой HTML-файл с расширением.php, но как они получают доступ к этим данным? Не вижу require()
или что-то вроде экземпляров в первом уроке.
7 ответов
Примечание. Шаблоны, основанные на MVC и MVC, являются продвинутыми конструкциями. Они предназначены для использования в кодовых базах, где обычный объектно-ориентированный (который следует SOLID и другим рекомендациям) код становится неуправляемым. Введя этот шаблон, вы наложите дополнительные ограничения, которые затем позволят вам содержать очень сложные приложения. MVC не предназначен для приложений "Hello World".
Давайте начнем с самого начала...
Основная идея шаблонов проектирования, основанных на MVC и MVC, - это разделение проблем. Указанное разделение состоит из двух частей:
- Уровень модели отделен от уровня пользовательского интерфейса:
- представления отделены от контроллеров
Слой модели (не "класс" или "объект") будет содержать несколько групп структур, каждая из которых имеет дело с различным аспектом бизнес-логики. Основные части будут:
- доменные объекты: валидация, бизнес-правила
- абстракция хранилища: постоянство и кэширование данных из доменных объектов
- услуги: логика приложения
Также могут быть смешаны репозитории, единицы работы и другие.
Уровень пользовательского интерфейса в основном состоит из представлений и контроллеров. Но они оба используют сервисы для взаимодействия с уровнем модели. Сервисы предоставляют контролерам возможность изменять состояние уровня модели и представлениям собирать информацию на основе этого нового состояния.
В контексте сети представления и контроллеры образуют свободную пару из-за природы запроса-ответа, которую демонстрируют веб-приложения.
Следует отметить, что хотя контроллеры могут напрямую изменять состояние текущего представления, чаще всего эти изменения осуществляются через модель. Одна из причин прямого изменения представления - это, например, когда вместо XML вам нужно ответить JSON.
Хотя можно также утверждать, что можно просто создать другое представление для каждого выходного формата и воспользоваться преимуществами полиморфизма.
Что не вид?
Существует распространенное заблуждение, что представления представляют собой просто прославленный файл шаблона. Эта ошибка стала чрезвычайно популярной после выпуска фреймворка для прототипов RubyOnRails.
Представления не являются шаблонами. Если вы используете их как таковые, вы нарушаете основной принцип, лежащий в основе шаблонов, основанных на MVC и MVC.
Если вы делаете вид, что шаблоны являются представлениями, это оказывает огромное влияние на вашу архитектуру. В представлении нет места для логики представления, поэтому вы продвигаете логику представления либо на уровне контроллера, либо на уровне модели. Обычный выбор - "контроллер", потому что большинство людей понимают, что логике представления нет места на уровне модели.
По сути, это вызывает слияние представлений и контроллеров.
Что делает представление?
Ответственность за представление заключается в том, чтобы иметь дело с логикой представления. В контексте сети цель просмотра - создать ответ для пользователя (который, между прочим, является браузером, а не человеком).
Технически было бы возможно создать представления на стороне клиента, эти пользовательские веб-сокеты для наблюдения уровня модели, но на практике это практически невозможно реализовать. Особенно в среде PHP.
Для создания этого представления отклика получает информацию из уровня модели и на основе собранных данных либо собирает отклик, распределяя данные по шаблонам и обрабатывая, либо иногда просто отправляя заголовок местоположения HTTP.
При использовании Post / Redirect / Get часть перенаправления выполняется представлением, а не контроллером, как это часто делают люди.
Очень субъективный бит:
В последнее время я предпочел взаимодействовать с MVC, используя следующий подход:
// the factory for services was injected in constructors
$controller->{ $method.$command }($request);
$view->{ $command }();
$view->respond();
$method
текущее значение REQUEST_METHOD, которое было настроено для REST-подобного API, и $command
это то, что люди обычно называют "действием". Контроллер имеет отдельные процедуры для GET
а также POST
(другие) запросы. Это помогает избежать того же if
в каждом "действии".
На вид я вызываю два метода. Во-первых, это динамический вызов для сбора данных. И второе направлено на создание определенного типа ответа.
Предупреждение: я подозреваю, что эта настройка содержит нарушение SRP. Принятие этого как своего собственного могло бы быть плохой идеей.
Что насчет СУХОГО?
Как вы уже могли заметить, существует небольшая проблема с использованием представлений в качестве экземпляров. В итоге вы получите повторяющиеся фрагменты кода. Например: меню или нумерация страниц.
Давайте посмотрим на нумерацию страниц. Нумерация страниц содержит логику, но эта логика не связана со слоем модели. Модель не имеет понятия "страница". Вместо этого эта часть логики будет находиться на уровне пользовательского интерфейса. Но если каждое из ваших представлений содержит или наследует нумерацию страниц, то это будет явным нарушением ПСП (и фактически нескольких других принципов).
Чтобы избежать этой проблемы, вы можете (и должны, IMHO) представить объекты представления в ваших представлениях.
Примечание: хотя Фаулер называет их "моделями презентаций", я думаю, что это имя лишь добавляет путаницы "что такое модель". Поэтому я бы рекомендовал называть их "объектами презентации".
Объекты презентации имеют дело с повторяющимися кусочками логики. Это делает представления намного "более легкими", а в некоторых аспектах начинает отражать структуру сервисов на уровне модели.
Взаимодействие между объектами представления и шаблонами становится аналогичным взаимодействию между объектами домена и сопоставителями данных.
Мне всегда нужно все это?
Нет. Этот конкретный подход в значительной степени ориентирован на код, где уровень пользовательского интерфейса имеет большую сложность, и вам необходимо отделить обработку ввода от представления только для здравого смысла.
Если ваше приложение имеет очень простой пользовательский интерфейс, например... эмм... вы создаете REST API для более крупного интегрированного проекта. В таком прагматическом варианте можно просто объединить каждую пару контроллера-представления в один класс.
Это также может быть хорошим шагом при рефакторинге устаревшей кодовой базы, потому что этот менее ограниченный подход позволяет перемещать целые куски старого кода. Когда вы изолировали такие фрагменты старого кода и проверили, что все по-прежнему работает (поскольку унаследованный код никогда не имеет никаких тестов… именно так он становится "унаследованным"), вы можете начать разделять его дальше, сосредоточившись на разделении бизнес-логики. из интерфейса
PS Я сам все еще пытаюсь найти способ, как лучше всего иметь дело с представлениями. Этот пост - не просто ответ, а скорее снимок моего понимания.
Второе руководство - это то, как работает платформа Code Igniter, и то, к чему я привык. Я следую этому, даже когда не использую фреймворк вообще.
Фактически, разработчик должен применить принципы, подобные MVC, на практике, в противном случае он / она может сделать лазанью или спагетти, даже используя самую ориентированную на MVC среду.
Используя подход "PHP файл как шаблон представления", в идеале можно использовать минимальные операторы PHP, в основном только для структур повторения (foreach ($array as $item)
), основные условия (if ($boolean)
) а также echo
- как если бы это был действительно шаблонный язык, и ничего более.
Итак <?php ?>
теги в файлах шаблона представления должны быть просто заполнителями, и ничем иным.
Запросы к базе данных, доступ к модели, вычисления и т.п. не должны выполняться в файле шаблона представления. В основном это следует рассматривать как HTML-файл с заполнителями. (Со связанными с ним CSS и JavaScript. Все может стать более сложным, когда приложение сильно зависит от JavaScript / AJAX...)
Следуя этим простым принципам, мы эффективно отделяем презентацию от бизнес-логики. Даже если это звучит так просто, я устала иметь дело с кодом Code Igniter, который ему не следует. Некоторые используют "вспомогательные функции" для маскировки вызовов модели / базы данных - и считают это хорошей практикой!:-)
Вы не видите require
внутри этих PHP-файлов шаблонов представления, потому что они требуются взамен методов "построения представления".
Конечно, не следует echo
и / или print
изнутри функции контроллера и модели. Это также очень просто, но я также устал видеть, как спагетти-код выводит HTML из методов контроллера CI.
На практике контроллер вызывает методы модели, собирает все необходимые данные для представления и, в качестве последнего шага, вызывает представление (т. Е. Создает и выводит его), передавая ему ранее полученные данные.
Имеет смысл? Я не знаю, ответил ли я на ваш вопрос. По крайней мере, это мои "2 цента".
Предполагается, что вы передаете метод в классе представления все, что нужно для построения представления независимо от формата вывода. Большинство разработчиков используют какой-то шаблонизатор для создания основной части страницы, а затем заполняют тело информацией, специфичной для запроса. Есть так много способов сделать это. Также хорошо иметь класс абстрактного представления, который определяет вспомогательные методы для общих элементов, таких как формы и входные данные.
Этот слой абстрагирован, поэтому логику вашего приложения не нужно менять, если вы решите каким-либо образом изменить дизайн или формат вывода.
Редактировать: каждый модуль, представленный набором MVC, будет иметь свое собственное представление, которое имеет коллекцию методов, отвечающих за отправку вашего вывода в браузер. Есть много способов, которыми вы можете пойти, но вот один пример:
class testModule_view extends viewAbstract {
public function showTestData($title, $subtitle, $data) {
$XHTML = '<h1>' . $title . '</h1>'
. '<h2>' . $subtitle . '</h2>'
. parent::data2table($data);
parent::outputToBrowser(DEFAULT_TEMPLATE, $XHTML);
}
}
Это просто быстрый пример, чтобы дать вам представление о том, как может выглядеть простой метод представления.
Различные структуры используют различную логику для назначения переменных для просмотра и получения их содержимого. Ниже приведен простой пример использования функции ob_start().
<?php
$title = 'Hello...';
ob_start();
file_get_contents('view.phtml');
$viewContents = ob_get_clean();
echo $viewContents;
?>
//view.phtml
<b>Hello the title is <?php echo $title; ?></b>
Надеюсь, что это ответ на ваш вопрос...
Проверьте этот код:
include_once(ROOT.'/'.'config/config.php');
function __autoload($class_name){
$lib_path = ROOT . '/' . 'lib/class.'.$class_name . '.php';
$controller_path = ROOT . '/' . 'controllers/'.str_replace("controller", "", strtolower($class_name)) . '.controller.php';
$model_path = ROOT . '/' . 'models/'.strtolower($class_name) . '.php';
if(file_exists($lib_path)){
require_once ($lib_path);
} else if (file_exists($controller_path)){
require_once ($controller_path);
} else if(file_exists($model_path)){
require_once ($model_path);
} else {
throw new Exception("File {$class_name} cannot be found!");
}
}
<?php
Класс View {
protected $data;
protected $path;
protected static function getDefaultViewPath() {
$router = App::getRouter();
if(!$router){
return false;
}
$controller_path = $router->getController();
$method_path = ($router->getMethodPrefix() !== "" ? $router->getMethodPrefix() . '_' : '') . $router->getAction();
return ROOT . "/views/" . $controller_path . "/" . $method_path . ".phtml";
}
public function __construct($data = array(), $path = null) {
if(!$path){
//default
$path = $this->getDefaultViewPath();
}
if(!file_exists($path)){
throw new Exception("Error view file!");
}
$this->data = $data;
$this->path = $path;
}
public function render(){
$data = $this->data;
ob_start();
include ($this->path);
$content = ob_get_clean();
return $content;
}
}
Когда браузер вызывает страницу, для нее будет загружен контроллер. Контроллер управляет жизненным циклом вашего приложения. Он будет получать данные из модели, которая используется только для получения данных (возможно, из базы данных). Представление является только HTML, контроллер будет отображать представление и, если необходимо, передать ему несколько параметров.