在MVC中,是否可以/应该在View中完成从模型的基本数据检索?


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的情况下至少有意义。当Controller本身只能检索数据时,为什么还要收集数据并将其传递给View?这使Controller保持打开状态,可以进行纯粹的“应用程序级”处理(例如,处理GET / POST请求,管理访问权限),以及保持模型可重用和所有其他优点。

如果扩展此示例以允许用户过滤结果,则控制器将只处理表单中的POST并将过滤器传递给View,然后该视图将再次请求数据,这次是使用过滤器。

这是开发MVC应用程序的有效方法吗?还是我忽略了控制器应扮演的重要角色?

Answers:


17

是的,在技术上可以做到。不,不应该这样做。是的,您缺少控制器的用途。

控制器在那里将视图与模型分离。去耦是有益的,因为您应该将View视为几乎一次性的代码。随着UI技术的变化,您希望最大程度地减少生成新View所需的返工。该控制器实现了这种解耦,并为您的代码提供了一个可以通过UI技术实现的地方。

如果您需要添加或更改模型,它也可以反向进行。所有上游更改都将包含在Controller中,并且您的视图将被保留。

另一个风险是,虽然观点很简单,现在,你有较少的保证,它将保留在其整个生命那么简单。通过直接从(非常简单的)视图调用模型,您已经打开了一扇门,以便在以后非常非常简单的视图变得不太简单时,可以引入其他不良做法。未来的开发人员将很想从不太简单的View中进行更多的Model调用,而不是重构代码并与Controller进行交互。


1
好答案,谢谢。稍微扩展您的“向前看”方案;如果页面上有与请求内容不同的通用信息(例如,用户正在查看特定产品,则在侧面显示“最新特别优惠”的一般列表)应如何/在何处拨打电话offers_model->get_latest()?将此添加到控制器中的每个方法中(就像我之前愚蠢地尝试的那样)似乎过大并且明显不干燥。
亚当·威斯布鲁克

2
@AdamWestbrook看一下MVVM。其中的ViewModel部分可以解决此特定问题。您可以将添加offers_model->get_latest()ProductViewModel基类或类似的东西。
Zachary Yates

1
太好了,我一定会研究MVVM,再次感谢。
亚当·威斯布鲁克

很好的答案,会挑剔地继续出演。我个人也是MVVM的忠实支持者:)
Benjamin Gruenbaum 2013年

@BenjaminGruenbaum您是否在PHP中使用MVVM?如果是这样,您是否正在使用特定的框架?
亚当·韦斯特布鲁克

6

鉴于“瘦控制器,胖模型”的概念,并且普遍认为Views可以在需要数据输出时直接调用Models

不,这是不正确的。视图不能直接调用模型。视图不应访问Model对象,除非出于某种原因程序员将这些对象暴露给View。

是否应该考虑在视图而不是控制器中处理请求的“获取和显示”部分?

基本上,这会删除控制器,并打消拥有它们的意义。

当Controller本身只能检索数据时,为什么还要收集数据并将其传递给View?

控制器不收集数据。该模型负责收集数据。控制器决定是否将此数据传递给视图。View只是显示数据。

如果扩展此示例以允许用户过滤结果,则控制器将只处理表单中的POST并将过滤器传递给View,然后该视图将再次请求数据,这次是使用过滤器。

没有。

控制器检查POST数据是否有效,然后将此数据作为选项传递给模型,模型随后将查询数据源并返回数据,然后控制器将其传递给视图。

这是开发MVC应用程序的有效方法吗?还是我忽略了控制器应扮演的重要角色?

控制器作为浏览器的处理程序进行操作。调度程序将请求发送到控制器的操作,控制器的操作又将请求分发到模型。模型包含所有业务逻辑(这是最重要的部分),并将数据返回给控制器。然后,控制器可以简化和调整数据,以便View可以更轻松地呈现数据。

视图的重点是使HTML表示与数据源之间的结构和依赖性脱钩。虽然这可能很困难。视图并不总是显示直接来自模型的数据。控制器通常会添加相关的额外数据。

我敢肯定,关于MVC的教程很多。我建议阅读其中一些。


谢谢马修。为了澄清起见,到目前为止,我一直按照阅读和建议将视图和模型与控制器分离。但是,自从开始学习保留“瘦”控制器以来,我一直在想应该/应该从中删除哪些内容,看来导致我提出这个问题的思考过程已经走了两三个步骤!
2013年

当您开始获得许多控制器使用的模型时。他们变得肥胖的需求变得非常明显。当View开始包含大量PHP时,您便知道控制器已精简。当您的控制器非常胖时。很难让其他控制器以相同的方式进行操作(例如,添加API服务)。
Reactgular 2013年

3

我发现您的问题非常有趣,因为最近在学习Python时遇到了同样的问题。

尽管给出的答案令人信服,但我想我会补充一下我遇到的另一种观点,即视图确实无需通过Controller就可以获取模型的状态。

MVC

重要的是要注意,视图和控制器都取决于模型。但是,模型既不依赖于视图也不依赖于控制器。这是分离的主要好处之一。这种分离允许独立于视觉呈现而构建和测试模型。视图和控制器之间的分离在许多富客户端应用程序中是次要的,实际上,许多用户界面框架将角色实现为一个对象。另一方面,在Web应用程序中,视图(浏览器)和控制器(处理HTTP请求的服务器端组件)之间的分隔非常明确。

Model-View-Controller是用于将用户界面逻辑与业务逻辑分离的基本设计模式。不幸的是,该模式的流行导致了许多错误的描述。特别地,术语“控制器”已用于表示不同上下文中的不同事物。幸运的是,Web应用程序的出现帮助解决了一些歧义,因为视图和控制器之间的分离非常明显。

在Steve Burbeck的Smalltalk-80应用程序编程:如何使用模型视图控制器(MVC)[Burbeck92]中,描述了MVC的两个变体:被动模型和主动模型。

当一个控制器专门操纵模型时,将采用被动模型。控制器修改模型,然后通知视图模型已更改并且应刷新(请参见图2)。在这种情况下,模型完全独立于视图和控制器,这意味着该模型无法报告其状态的变化。HTTP协议就是一个例子。浏览器中没有简单的方法可以从服务器获取异步更新。浏览器显示该视图并响应用户输入,但不会检测到服务器上数据的更改。仅当用户明确请求刷新时,服务器才会查询更改。

MVC-被动模型

我不能说哪种观点是“正确的”,老实说,在阅读了这里的答案和链接的文章后,我有些困惑了。

文章全文在这里


是的,另外一个使客户感到困惑的是客户端-服务器,原来的SmallTalk MVC并没有真正解决这个问题。在客户端-服务器(例如,使用javascript)中,客户端上有面向表示的模型,视图和控制器,服务器上有面向域的视图和控制器,尽管服务器还进行了一些面向表示的处理,增加了混乱。另外,有时我们希望域视图具有某种持久性,这意味着视图参数形成了自己的模型,而不一定是域模型的一部分。
Erik Eidt

谢谢您的链接,我知道我并没有发疯!从本质上讲,这就是我在想得太久之前一直想做的事情,只要模型不依赖于任何事物,无论如何访问/在何处都重要?我还没有决定我下一步的开发将采用哪种方法,但这肯定会有所帮助。
亚当·威斯布鲁克

1

要考虑的另一件事是,您似乎已经自动加载,user_modelinvoice_model允许视图访问它们。为了使它可靠地工作,您可能会自动加载所有模型(因为$this->load->model()在视图中看起来只是错误,不是吗...)

这样做不必要地通过加载一堆可能永远不会使用的东西而使您的堆栈膨胀。拥有多个模型的部分原因是允许您封装相关逻辑并仅加载给定任务所需的内容。

看起来像CodeIgniter。我已经做了很多CI的开发工作,并且可以从个人经验中分享您确实不想自动加载更多内容。尝试添加$this->output->enable_profiler(TRUE);控制器的构造函数,并自动加载(包括诸如之类的帮助器database):您可能会看到加载和执行时间发生了重大变化,但尤其是在内存分配方面。


1
很好,您是对的,它是基于CI的,尽管为了清楚起见,我删除了一些特定的语法。我钻进了“自动加载”为主要时间和DRY原因几乎所有的习惯,显得有点疯狂有很多相同的load->model大多数控制器和方法。我不喜欢CI的向后兼容性,这是我最不喜欢使用的功能之一,但这是另一个完整的讨论……
Adam Westbrook,2013年

0

简短的答案是代码示例的形式看似直观。看来这是一种“轻松自在”的方法。


问题1

ModelView对象将紧密耦合。

如果必须在中添加删除方法Model,则可能需要相应地进行更改View

从根本上说,MVC是从“ 命令”和“ 观察者”模式派生的。你想要一个独立的 “模式”是通过操纵接口/ APIController可以挂接到(即授权)。

通常,这意味着 ModelView实例注入Controller,并将其存储为的属性Controller。然后,使用的方法Controller(即,命令)作为工作区中,将数据传递到一个 View 从所述 Model后`模型完成更新应用程序的状态)。

传递数据(数组,可迭代的对象,等等)不断耦合之间ModelView实例松动。如果您将Model实例注入View,请参阅上面的问题1。

请记住,Views遵循表示状态传输方法(REST)的可能是HTML,JSON,Text,XML,HTTP标头,YAML或几乎任何其他内容

因此,关键是了解如何管理之间的关系,Model并且Views是看它是什么关系,一到多(可能)!这正是Observer模式旨在完成的任务。

虽然大多数设置一次只涉及一个视图,但是没有什么可以阻止MVC架构模式一次更新多个视图!使用传统的CRUD Web应用程序会使人们以一对一的方式进行思考,但这是观察者模式如何工作的最小示例(一对多是另一个)。

因此,如果您有一个Model或多个Views,那么由于您更改了API /方法中的某些内容,更新所有实现代码的潜在麻烦现在变得很严重Views'Model's

将数据传递到 Views而不是的实例 Models

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.