【问题标题】:MVC concept - who's responsabillity is to load a model?MVC 概念——谁的职责是加载模型?
【发布时间】:2013-01-19 10:28:02
【问题描述】:

好的,我现在了解 MVC 概念的基础知识,我知道有人问过类似的问题,但仍然没有找到明确的答案。在阅读 MVC 时,我发现了一些相互矛盾的例子,所以我想找出哪个概念更好。

我应该使用我的控制器从模型中加载数据,然后将该数据传递给视图,还是应该让视图从模型中加载数据并使用控制器来选择合适的视图。

对我来说更自然(正确)的方式是控制器应该加载模型,但如果我需要具有 2 个不同视图的相同内容,例如:

  1. 视图显示简单的文章文本
  2. 视图显示相同的文章文本,但也显示带有文章作者信息的框。

让我感到困惑的是我有一个请求,给我看 ID 为 33 的文章。在第一种情况下,一切都很清楚,但现在我的第二个视图使用显示附加数据(关于作者)的不同模板进行渲染,所以应该我让视图从模型(关于作者)请求数据还是整个逻辑应该由控制器完成?

这很令人困惑,因为现在控制器应该根据视图应该呈现的模板从模型中请求适当的数据。

希望我有道理:)

【问题讨论】:

    标签: php model-view-controller model


    【解决方案1】:

    简答:将模型传递给控制器​​和视图。

    长答案:在 MVC 中,控制器不会“从模型加载数据,然后将该数据传递给视图”。视图与模型有直接关系,并从中请求数据。请参阅:How is MVC supposed to work in CodeIgniterhttp://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller:“视图向模型请求生成输出表示所需的信息。”

    因此,要启用松散耦合,请完全独立地启动模型视图和控制器并将它们相互传递。

    这允许严格分离 MVC 提倡的关注点,并允许您将任何组件与任何其他组件重用,因为没有任何东西是硬编码的。

    类似:

    $model = new Model;
    $controller = new Controller($model);
    $view = new View($model, $controller);
    echo $view->output();
    

    控制器不应选择视图,也不应选择模型。通过将此责任放在控制器中,控制器不能与其他视图或模型重用。

    编辑:我已更新此内容以回答 tresko 关于视图为何需要了解其控制器的评论。

    视图需要控制器以避免将控制器硬编码到视图中。这是为了让视图知道它在当前上下文中与哪个控制器配对并可以回发给它。

    事件(用户操作)在视图中触发,需要由控制器处理。

    有3种方法:

    1) 硬编码 view:controller 关系,在 web 上这是通过使用<a href="/some/hardcoded/route" 来实现的;或<a href="' . $this->router->create('someController, 'action') . '>' 这消除了将视图与任何其他不可取的控制器一起使用的可能性。

    2) 将控制器传递给视图并让视图知道其事件将被触发到哪个控制器。在 Web 上,使用这种方法,视图还需要一个路由器,它将控制器操作转换为路由。例如<a href="' . $this->router->getRoute($this->controller, 'action') . '>'

    3) 将视图传递给控制器​​并让控制器在视图上设置操作:(控制器代码)$this->view->setEvent('buttonClick', $this->router->getRoute($this, 'action'))...(视图代码)<a href="' . $this->getEvent('buttonClick') . '>'

    其中,

    1) 是最不可取的,因为它严重影响了灵活性。视图只能在非常特定的控制器上调用操作。

    2) 对开发人员来说是最少的工作量,但每个控制器都需要一个特定的接口。

    3) 这提供了最大的技术灵活性,但开发人员需要做更多的工作,并且控制器需要了解很多关于其视图的信息,除了 API 之外,它还必须知道视图中可用的事件。如果一个视图被更新并且有一个新的动作,每个控制器都需要被更新来解释它。这也适用于 2),但是因为 2 可以使用接口轻松处理,所以跟踪使用它的每个类要容易得多。

    在我看来,2 和 3 都是很好的方法,但 2 更好,因为它允许更强大的系统并允许最多的重用,缺点是控制器必须实现特定的接口。 3 允许控制器有任何接口,但它必须知道很多关于它的视图。

    CakePHP 和其他流行的框架在他们的示例中倾向于硬编码关系(例如 http://book.cakephp.org/2.0/en/views.html ),echo $this->Html->link('edit', array( 'action' => 'edit', $post['Post']['id'])); ?> 链接只能转到“编辑”控制器。这会严重影响重用。

    【讨论】:

    • 谢谢。但是这样看,我问自己谁需要控制器?我的意思是在应用程序启动后,我通常可以根据请求参数调用适当的视图,这将加载视图,视图会从模型中询问数据并输出它:)
    • upload.wikimedia.org/wikipedia/commons/f/fd/MVC-Process.png 这是来自维基百科的“MVC 流程”图形。它显示用户“使用”控制器。然而,一个“控制器”对用户来说毫无意义,他们“使用”视图。来自 Java 蓝图,模型视图控制器:briggs.myweb.port.ac.uk/WEBP/notes/images/…“视图 - 将用户手势发送到控制器”。控制器从视图中调用,这是用户真正与之交互的唯一事物。
    • st-www.cs.illinois.edu/users/smarch/st-docs/mvc.html "视图的实例变量控制器指向其控制器,控制器的实例变量视图指向其关联视图" 用户输入是来自视图中的函数还是通过控制器HTTP 请求是一个无关紧要的实现细节,架构是一样的。简单示例:用户单击按钮。用户与视图交互,按钮是视图的一部分而不是控制器。单击该按钮时会发生什么由控制器处理 - 但该事件由视图触发。
    • @TomB 您在其他帖子中提供的链接非常有用。还有一个简单的语句:“控制器接收用户输入,必要时更新模型,并通知视图模型已更改。”让事情变得更加清晰。
    • 请记住,关于在模型发生更改时通知视图的最后一部分在 Web 上是无关紧要的,因为无论如何视图都必须始终刷新,因此它需要请求当前状态模型。
    【解决方案2】:

    我的建议是,将数据源与视图结合起来的逻辑应该完全在控制器中进行。视图不应绑定到特定的数据源。

    例如,如果您有一个使用 Smarty 语法(或类似语法)和命名占位符的视图,那么您可以使用任何数据源、文本、模型等来提供信息以呈现到模板中。如果视图与模型相关联,您需要修改模型和视图,同时了解对对方的影响。

    与松散的耦合相比,这样的紧密耦合会导致更多的问题,因为松耦合会减少意外破坏某些东西的机会。

    示例:

    class Page_Controller extends Controller {
    
      // __construct/__destruct/__callStatic/__call/etc, whatever you need in your implementation
    
      // -------------------------------------------------------
      // Adjust to suit your situation for passing data
      // This controller doesn't care where objSource comes from
      // -------------------------------------------------------
      private function pageSpecificImplementation($objSource = null){
    
        // using a factory class - but assume a view is created in whatever way works for you
        // the key thing here is that the view could be anything that can be returned as a string - but use whatever works for you
        $tplMain = make::view( 'template-url-or-path' )->assign(array(
           'placeholder1' => $objSource->value1,
           'placeholder2' => $objSource->value2
        ));
    
        $tplSub = make::view( 'template-url-or-path' )->assign( $objSource );
    
        $tplMain->assign('sub',$tplSub->render())->render();
    
        // $tpl is some form of html? csv?, who knows - not relevant at THIS stage
    
        // okay - now I know what I want to do!
        // decide what to do with it here - output headers for html
        // save to a file
        // output and cache the output, whatever works for you here
        // output to pdf?
        // send as an email?
        output::html( $tplMain, $cacheable, $cachetime... );
        // output::email( $tplMain, $extra_params );
        // output::pdf( $tplMain, $extra_params );
      }
    
    }
    

    在这里,您使用的是视图,但没有将其与输出紧密耦合。您的控制器可以在运行时根据正在运行的任何业务规则修改输出,但数据源不绑定到视图,输出也不绑定到视图。

    我建议以遵循类似原则的方式进行分离的“一些”实现。 YMMV 取决于您正在做什么以及您希望如何实现它,但请尝试在 MVC 中保持每个元素分开。

    在某些实现中,您会看到在视图中“替换”事物的逻辑,而无需提及“什么”视图。这通常在 Smarty 中完成。然后可以由控制器的流程确定视图。数据可以从多个模型或其他来源中提取,这可能会或可能不会影响哪个视图是合适的。

    因此,您绝对应该将数据加载与视图分开。将它保存在控制器中,这是应该做出决定的地方。视图不应与模型连接,除非您考虑到特定的用例,例如具有紧密耦合主题视图的主题模型,其中不涉及额外的业务逻辑(不太可能但可能?)。

    【讨论】:

    • 投反对票应附有解释,以便发帖者可以删除或修改以更有用,而不是让他们留在这里无法为其他人改进。
    • 我没有投反对票,但这显然是因为你实现了接近MVP 的东西并打破了SRP
    • @DaveJust - 你所说的违反 SRP 规则是什么意思?
    猜你喜欢
    • 2019-04-09
    • 2013-11-29
    • 2021-12-31
    • 2013-04-03
    • 1970-01-01
    • 2011-07-18
    • 1970-01-01
    • 2013-12-09
    • 1970-01-01
    相关资源
    最近更新 更多