如何处理 MVC 网站的模板?

How to handle templates for MVC websites?

提问人:Andrew G. Johnson 提问时间:12/1/2008 最后编辑:hakreAndrew G. Johnson 更新时间:6/6/2013 访问量:6638

问:

我将其标记为 PHP,但这只是因为我将使用 PHP 代码来显示我的问题。

所以我为控制器准备了一些这样的代码:

switch ($page)
{
    case "home":
        require "views/home.php";
        break;
    case "search":
        require "views/search.php";
        break;
}

显然有更多的页面,但这应该说明我的问题。这两个页面(以及网站上的所有页面)都有一个通用的页眉、导航和页脚。我应该使用多个 require 语句吗?我的第一个猜测是:

switch ($page)
{
    case "home":
        require "templates/header.php";
        require "templates/navigation.php";
        require "views/home.php";
        require "templates/footer.php";
        break;
    case "search":
        require "templates/header.php";
        require "templates/navigation.php";
        require "views/search.php";
        require "templates/footer.php";
        break;
}

不知何故,我的直觉告诉我这是不正确的。

php -控制器视图

评论


答:

5赞 tvanfosson 12/1/2008 #1

控制器应该只为视图设置数据,并选择要显示的视图。视图应负责页面的布局,包括共享页面。我喜欢你的第一个样本而不是第二个样本。

评论

2赞 Andrew G. Johnson 12/1/2008
这并不能回答我的问题,我不认为在智能中对两个文件具有相同的页眉/页脚/导航。特别是因为它公然违背了 DRY 原则。
0赞 tvanfosson 12/1/2008
不一定。ASP.NET MVC 提供了母版页,它允许维护关注点分离(这就是我所说的)和 DRY 原则。对 php 框架了解不够,不知道是否有具有类似功能的框架。对我来说,保持 SOC 比 DRY 更重要
0赞 CupOfJava 10/1/2021
@tvanfosson 为什么控制器有责任将数据传达给视图,而不仅仅是被视为验证者或复杂的事件处理?通过控制器从模型而不是模型 -> 视图 -> HTML 模板路由信息实际上有优势吗?
1赞 genehack 12/1/2008 #2

是的,您应该将页眉、页脚等拆分出来。

对于您展示的特定示例,这不是更好吗?

    require "templates/header.php";
    require "templates/navigation.php";
    require "views/$page.php";
    require "templates/footer.php";

(其中$page是“主页”、“搜索”等)

-2赞 Alarion 12/2/2008 #3

如果你使用直接的PHP页面作为模板,你基本上可以设置一个全局/会话变量来保存你想要的页面。您将拥有一个“主模板”php 页面,其中包含页眉和页脚元素,然后为$page调用 include。控制器中的内容如下所示:

$_SESSION['page'] = sanitize_input($_GET['page']);
require "templates/main.php";

然后在 main.php 模板文件中:

require "templates/header.php";
require "templates/navigation.php";
require "views/{$_SESSION['page']}.php";
require "templates/footer.php";

评论

0赞 Alarion 12/2/2008
有点好奇为什么当它直接回答 OP 的问题时会被否决。如果他不想使用这种格式,请使用模板引擎或框架。任何其他方式都会导致重复代码。
0赞 Preston 12/2/2008
此处不应使用 _SESSION 美元。我们不会将数据转移到下一个请求。否则,你就朝着正确的方向前进。
0赞 Alarion 12/3/2008
我只是用它作为一个简化的例子。将其存储在某种数据结构中,以便包含的页面可以引用它。我不喜欢继承常规的“全局”变量。耸肩
0赞 user42092 12/2/2008 #4

以下是我如何使用当前项目制作模板的简化版本,如果有任何用处:

class Template {
    var $pagename = 'index';

    function __construct() {
        $this->pagename = basename($_SERVER['SCRIPT_NAME'], '.php');
        register_shutdown_function(array($this, 'do_output'));
    }

    function do_output() {
        $this->header();
        $this->display($this->pagename);
        $this->footer();
    }

    function __call($template, array $params) {
        call_user_func(array($this, 'display'), $template, params);
    }

    function display($template, array $params = null) {
        include "templates/$template.php";
    }
}

它背后的想法是,你可以写“包括'Template.inc';new Template;“,它安排 do_output() 在脚本末尾自动运行。它遗漏了一些东西,例如用于将变量传递给模板的方法。

你已经提到你没有使用 PHP,其中有一些 PHP isms:register_shutdown_function() 确保模板在对象析构函数之前但在主脚本之后调用,而对 $this->header()/footer() 的调用是神奇的函数调用,只执行 display('header') 和 display('footer'),它们应该被覆盖。

当然,使用像您发布的示例这样的开关并没有错,但是您不需要每个 case 语句中的页眉/页脚。像这样的东西会做同样的事情:

require "templates/header.php";
require "templates/navigation.php";
switch ($page)
{
    case "home":
        require "views/home.php";
        break;
    case "search":
        require "views/search.php";
        break;
}
require "templates/footer.php";

...或者你可以用基于文件名的东西替换switch(),就像我上面使用的那样,如果它适用于你的页面设置方式。不过,如果您打算通过 URL 参数进行切换,则切换是最安全的方法。

0赞 nem 12/3/2008 #5

你在重复代码。这几乎不是一个好主意。为了接近您的初始示例,类似这样的东西肯定是可取的:

require "templates/header.php";
require "templates/navigation.php";

switch ($page) {
    case "home":
        require "views/home.php";
        break;
    case "search":
        require "views/search.php";
        break;
}

require "templates/footer.php";

如果不更多地了解您的架构方法,就很难提供更多建议。例如,建议将控制器的这一部分(仅准备输出)放在一个非常中心的位置,并在包含视图模板之前启动输出缓冲。这样,您可以将输出存储在一个变量中,您可能希望在 HTTP 响应中返回其内容之前进一步处理该变量。

0赞 Jim Nelson 12/9/2008 #6

我同意 tvanfosson 的观点,并想解释一下它与 MVC 的关系和关系。

第二个示例的问题在于,控制器暴露于视图的构造方式。从严格意义上讲,控制器对视图的输入进行封送,并将它们传递给视图,仅此而已。

一种实用的思考方式是,如果视图根据应用程序要求或输入本身而变化。例如,如果生成的视图是针对 JavaScript 弹出窗口的,它可能会(并且可能会)使用一组不同的页眉、页脚、CSS、元等。在第二个示例中,所有内容都向控制器公开。在你的第一个中,是视图知道如何生成视图 -- 这正是重点。

为了进一步举例,假设 JavaScript 弹出窗口被重新设计为整页视图,或者针对 AJAX 进行了重构(或者弹出窗口/页面/AJAX 问题由输入确定,例如字段中的隐藏元素)。现在你正在拆开控制器,因为视图已经改变。与其说你违反了 MVC,不如说你一开始就不应该为它而烦恼。

0赞 Brock Hensley 6/6/2013 #7

如果所有文件名都与示例中所示的视图/页面请求匹配,则只需要一行,无需语句:switch

require "templates/header.php";
require "templates/navigation.php";
require 'views/' . $page . '.php'; // <-- one-liner
require "templates/footer.php";