Понимание MVC-просмотров в PHP

Мне кажется, что проблемы, рассматривающие концепцию Views в MVC, в соответствии с тем, что я прочитал, являются слоем, который управляет презентацией в приложении, но многие из материалов, которые я читал, кажутся разные по этому вопросу в этом вопросе от PHP Master.com.

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

В этой статье из php-html.net представление представляет собой простой HTML файл с расширением .php, но как они получают доступ к этим данным? Я не вижу require() или что-то вроде создания экземпляров в первом уроке.

Ответ 1

  Примечание: шаблоны, основанные на MVC и MVC, являются продвинутыми конструкциями. Они предназначены для использования в кодовых базах, где обычный объектно-ориентированный (который следует SOLID и другим рекомендациям) код становится неуправляемым. Введя этот шаблон, вы наложите дополнительные ограничения, которые затем позволят вам содержать очень сложные приложения. MVC не предназначен для приложений "Hello World".


Давайте начнем с самого начала...

Основная идея шаблонов проектирования, основанных на MVC и MVC, - Разделение проблем. Указанное разделение состоит из двух частей:

  • Уровень модели отделен от уровня пользовательского интерфейса:
  • представления отделены от контроллеров

enter image description here

Слой модели (не "класс" или "объект") будет содержать несколько групп структур, каждая из которых имеет дело с различным аспектом бизнес-логики. Основные части будут:

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

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

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

В контексте Интернета представления и контроллеры образуют неплотную пару из-за природы запроса-ответа, которую демонстрируют веб-приложения.

Следует отметить, что хотя контроллеры могут напрямую изменять состояние текущего представления, чаще всего эти изменения осуществляются через модель. Одна из причин прямого изменения представления - это, например, когда вместо XML вам нужно ответить JSON.

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


Что не вид?

Существует распространенное заблуждение, что представления представляют собой просто прославленный файл шаблона. Эта ошибка стала чрезвычайно популярной после выпуска среды прототипирования RubyOnRails.

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

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

По сути, это вызывает слияние представлений и контроллеров.


Что делает представление?

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

Технически было бы возможно создать виды на стороне клиента, чтобы пользовательские веб-сокеты наблюдали за уровнем модели, но на практике это практически невозможно реализовать. Особенно в среде PHP.

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

При использовании Post/Redirect/Get часть перенаправления выполняется представлением, а не контроллером, как это часто делают люди.


Субъективный бит:

В последнее время я предпочел взаимодействовать с MVC, используя следующий подход:

  // the factory for services was injected in constructors
  $controller->{ $method.$command }($request);
  $response = $view->{ $command }();
  $response->send();

$method - это текущий REQUEST_METHOD, который был настроен под REST-подобный API, а $command - это то, что люди обычно называют "действием". Контроллер имеет отдельные подпрограммы для запросов GET и POST (другой). Это помогает избежать одинакового if в каждом "действии".

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

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


Что насчет СУХОГО?

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

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

Чтобы избежать этой проблемы, вы можете (и должны, ИМХО) представлять объекты презентации в своих представлениях.

Примечание:, хотя Фаулер называет их "моделями презентаций", я думаю, что это имя лишь добавляет путаницы "что такое модель". Поэтому я бы рекомендовал называть их "объектами презентации".

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

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


Мне всегда нужно все это?

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

Если ваше приложение имеет очень простой пользовательский интерфейс, например... эмм... вы создаете REST API для более крупного интегрированного проекта. В таком случае прагматичный вариант может состоять в том, чтобы просто объединить каждую пару контроллер-представление в один класс.

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


П.С. Я сам все еще пытаюсь найти способ, как лучше всего иметь дело с представлениями. Этот пост - скорее не ответ, а скорее снимок моего текущего понимания.

Ответ 2

Второй учебник - это то, как работает среда Code Igniter, и тот, к которому я привык. Я следую за ним даже при отсутствии фреймворка вообще.

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

Используя подход "PHP файл как шаблон представления", в идеале можно использовать минимальные PHP-инструкции, в основном только для повторных структур (foreach ($array as $item)), базовых условных выражений (if ($boolean)) и echo - как если бы это было, действительно, язык шаблонов и ничего более.

Итак, теги <?php ?> в файлах шаблона представления должны быть просто заполнителями, и ничего больше.

Никакие запросы к базе данных, доступ к модели, вычисления и т.п. не должны выполняться в файле шаблона представления. Его следует рассматривать, в основном, как файл HTML с заполнителями. (С его связанными CSS и JavaScript. Что может быть сложнее, когда приложение полагается на JavaScript/AJAX много...)

Следуя этим простым принципам, мы фактически делаем некоторое разделение презентации от бизнес-логики. Даже звучание настолько простое, что мне надоело иметь код Code Igniter, который не следует за ним. Некоторые используют "вспомогательные функции" для маскировки вызовов модели/базы данных и считают, что это хорошая практика!: -)

Вы не видите require внутри этих файлов шаблонов представления PHP, потому что они необходимы вместо этого из методов "view building".

Конечно, изнутри контроллера и функций модели не следует echo и/или print. Это тоже очень просто, но я также устал видеть, что код спагетти эхом выводит HTML из методов контроллера CI.

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

Имеет смысл? Я не знаю, ответил ли я на ваш вопрос. По крайней мере, это мои "2 цента".

Ответ 3

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

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

Edit: Каждый модуль, представленный набором 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);
    }
}

Это просто быстрый пример, который даст вам представление о том, как выглядит простой метод просмотра.

Ответ 4

В разных структурах используется другая логика для назначения переменных для просмотра и получения содержимого. Ниже приведен простой пример с использованием функции 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>

Надеюсь, что ответ на ваш вопрос...

Ответ 5

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

Ответ 6

<?php

class 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;
}

}

Ответ 7

Проверьте этот код:

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!");
}

}