В MVC можно / нужно выполнять базовый поиск данных из модели в представлении?

10

Учитывая концепцию «тощих контроллеров, толстых моделей» и общее признание того, что представления могут напрямую вызывать модели, когда требуются данные для вывода, следует ли рассматривать обработку частей «получить и отобразить» запросов в представлениях, а не контроллер? Например (попытка сделать код достаточно общим):

контроллер

<?php

class Invoice extends Base_Controller {

    /**
     * Get all the invoices for this month
     */

    public function current_month() {

        // as there's no user input let's keep the controller very skinny,
        // DON'T get data from the Model here, just load the view

        $this->load->view('invoice/current_month');

    }

}

Посмотреть

<?php

// directly retrieve current month invoices here

$invoices = $this->invoice_model->get_current_month();

// get some other display-only data, e.g. a list of users for a separate list somewhere on the page

$users = $this->user_model->get_users();

?>

<h1>This month's invoices</h1>

<ul>
<?php foreach ($invoices as $invoice) { ?>

<li><?php echo $invoice['ref']; ?></li>

<?php } ?>
</ul>

Для меня это имеет некоторый смысл в тех случаях, когда запрос по сути является просто представлением. Почему Контроллер должен собирать и передавать данные в View, если он может просто извлечь их сам? Это оставляет контроллер открытым только для обработки на уровне приложения (например, обработка запросов GET / POST, управление правами доступа и разрешениями и т. Д.), А также для сохранения возможности многократного использования моделей и других полезных вещей.

Если этот пример был расширен, чтобы позволить пользователю фильтровать результаты, контроллер просто обработает POST из формы и передаст фильтры представлению, которое затем снова запросит данные, на этот раз с фильтрами.

Это правильный подход к разработке приложения MVC? Или я упускаю из виду важную роль, которую должен играть Контроллер?

Адам Уэстбрук
источник

Ответы:

17

Да, технически это можно сделать. Нет, это не должно быть сделано. И да, вам не хватает того, для чего предназначен контроллер.

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

Он работает и в обратном порядке, если вам нужно добавить или изменить вашу модель. Все вышестоящие изменения будут содержаться в Контроллере, а ваши Представления будут оставлены в покое.

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


источник
1
Отличный ответ, спасибо. Немного расширив свой сценарий «смотреть в будущее»; если на странице есть общая информация, отдельная от запрашиваемой (например, пользователь просматривает конкретный продукт, общий список «последних специальных предложений» показан сбоку), как / где должен offers_model->get_latest()быть сделан вызов ? Добавление этого в каждый метод в контроллере (как я глупо пытался раньше) кажется излишним и явно не СУХИМЫМ.
Адам Вестбрук
2
@AdamWestbrook Взгляните на MVVM. Часть ViewModel, которая может решить эту конкретную проблему. Вы можете добавить offers_model->get_latest()к ProductViewModelбазовому классу или что - то подобное.
Захари Йейтс
1
Отлично, я обязательно посмотрю MVVM, еще раз спасибо.
Адам Уэстбрук
Очень хороший ответ, будет демонстративно держать это в главных ролях. Лично я также большой поклонник MVVM :)
Бенджамин Грюнбаум
@BenjaminGruenbaum Вы используете MVVM в PHP? Если да, то используете ли вы для этого конкретный фреймворк?
Адам Вестбрук
6

Учитывая концепцию «тощих контроллеров, толстых моделей» и общее признание того, что представления могут напрямую вызывать модели, когда требуются данные для вывода

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

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

Это в основном стирает Контроллер, и побеждает смысл их наличия.

Почему Контроллер должен собирать и передавать данные в View, если он может просто извлечь их сам?

Контроллер не собирает данные. Модель осуществляет сбор данных. Контроллер решает , следует ли передавать эти данные в представление. Представление только делает представление данных.

Если этот пример был расширен, чтобы позволить пользователю фильтровать результаты, контроллер просто обработает POST из формы и передаст фильтры представлению, которое затем снова запросит данные, на этот раз с фильтрами.

Нет.

Контроллер проверяет, являются ли данные POSTed действительными, затем передает эти данные в качестве параметров в Модель, которая затем запрашивает источник данных и возвращает данные, а Контроллер передает их в представление.

Это правильный подход к разработке приложения MVC? Или я упускаю из виду важную роль, которую должен играть Контроллер?

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

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

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

Reactgular
источник
Спасибо Мэтью. Для пояснения, до сих пор я всегда отделял представление и модель с контроллером, как прочитал и предложил. Тем не менее, с тех пор, как я начал читать о том, как держать «тощих» контроллеров, мне просто стало интересно, что из них следует / можно удалить, похоже, мыслительный процесс, который привел меня к этому вопросу, был на шаг или два слишком далеко!
Адам Уэстбрук
Когда вы начинаете получать модели, используемые многими контроллерами. Потребность в том, чтобы они были толстыми, становится очень ясной. Когда представление начинает содержать много PHP, вы знаете, что ваш контроллер слишком тонкий. Когда ваши контроллеры очень толстые. Трудно заставить другие контроллеры работать таким же образом (например, добавить службу API).
Reactgular
3

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

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

MVC

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

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

В Прикладном программировании в Smalltalk-80: Как использовать Model-View-Controller (MVC) [Burbeck92] Стив Бурбек описывает два варианта MVC: пассивную модель и активную модель.

Пассивная модель используется, когда один контроллер управляет исключительно моделью. Контроллер изменяет модель, а затем сообщает представлению, что модель изменилась и должна быть обновлена ​​(см. Рисунок 2). Модель в этом сценарии полностью независима от представления и контроллера, что означает, что у модели нет средств для сообщения об изменениях в ее состоянии. Протокол HTTP является примером этого. В браузере нет простого способа получать асинхронные обновления с сервера. Браузер отображает представление и реагирует на ввод пользователя, но не обнаруживает изменений в данных на сервере. Только когда пользователь явно запрашивает обновление, сервер запрашивает изменения.

MVC - пассивная модель

Я не в состоянии сказать, какое из мнений является «правильным», и, честно говоря, я немного запутался после прочтения ответов здесь и связанной статьи.

Полный текст статьи здесь .

alnafie
источник
Правильно, и еще одна вещь, которая добавляет путаницу, это клиент-сервер, который на самом деле не учитывался в оригинальном SmallTalk MVC. В клиент-сервере (например, с javascript) существуют клиент-ориентированные модели, представления и контроллеры на клиенте, и доменно-ориентированные представления и контроллеры на сервере, хотя сервер также выполняет некоторую обработку, ориентированную на представление, добавляя путаницу. Кроме того, иногда мы хотим, чтобы представления домена имели некоторую постоянность, что означает, что параметры представления формируют свою собственную модель, что не обязательно является частью модели домена.
Эрик Эйдт
Спасибо за ссылку, я знал, что я не злюсь, думая об этом! Это, по сути, то, о чем я говорил, прежде чем углубиться в идею, пока Модель не зависит от чего-либо, что имеет значение, как / где к ней обращаются? Я еще не решил, какой подход я выберу в своей следующей разработке, но это определенно помогает.
Адам Уэстбрук
1

Еще одна вещь, которую следует учитывать, - это то, что вы, кажется, автоматически загрузили user_modelи invoice_modelразрешили представлению доступ к ним. Для того, чтобы это работало надежно, вы, вероятно, автоматически загружаете все свои модели (потому что $this->load->model()просто выглядит неправильно в представлении, не так ли ...)

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

Это похоже на CodeIgniter. Я много занимался разработкой CI, и я могу поделиться своим личным опытом, что вы действительно не хотите загружать больше, чем нужно. Попробуйте добавить $this->output->enable_profiler(TRUE);конструктор контроллера и возиться с автозагрузкой (включая вспомогательные функции, например database): вы, вероятно, увидите значительное изменение во времени загрузки и выполнения, но особенно в распределении памяти.

msanford
источник
1
Хорошие моменты, вы правы, это основано на CI, хотя я убрал некоторые специфические синтаксис для ясности. У меня есть привычка «автозагрузки» почти всего по большому количеству времени и из-за СУХОГО, казалось немного сумасшедшим из-за того, что load->modelво многих контроллерах и методах есть то же самое. Отсутствие надлежащей функции автозагрузки - одна из вещей, которые мне больше всего не нравятся в обратной совместимости CI, но это совсем другое обсуждение ...
Адам Вестбрук,
0

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


Проблема № 1

Ваши Modelи Viewобъекты будут тесно связаны.

Если вам когда-либо придется добавлять или удалять методы в Model, то вам, возможно, придется изменить их Viewсоответствующим образом.

По сути, MVC основан на шаблонах Command и Observer . Вам нужна независимая «Модель», которой манипулируют через интерфейс / API, который Controllerможет подключаться (т.е. делегировать).

Зачастую это означает внедрение Model и Viewэкземпляры в Controllerи сохранение их как свойств указанного Controller. Затем, используя метод Controller(то есть команду) в качестве рабочей области, передайте данные в a View из Model ( после того, как `Модель закончила обновление состояния приложения ).

Попутный данные (массивы, Iterable объектов, безотносительно) продолжает сшивающий между Modelи Viewслучаями потерять . Если вы внедрили Modelэкземпляр в View, см. Проблему № 1 выше.

Помните, что это Viewsможет быть HTML, JSON, Text, XML, заголовки HTTP, YAML или почти что угодно, в соответствии с методологией передачи состояния представления (REST) .

Таким образом, ключ к пониманию того, как управлять отношениями между Modelи, Viewsсостоит в том, чтобы видеть отношения такими, какие они есть, один ко многим (потенциально)! Это именно то, для чего был разработан шаблон Observer .

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

Таким образом, если у вас было одно Modelи несколько Views, потенциальная головная боль обновления всего Views'кода реализации, потому что вы изменили что-то в Model'sAPI / методах, теперь становится острой .

Передавать данные Views , а не экземпляры Models .

Энтони Ратледж
источник