提问人:Andrew G. Johnson 提问时间:12/1/2008 最后编辑:hakreAndrew G. Johnson 更新时间:6/6/2013 访问量:6638
如何处理 MVC 网站的模板?
How to handle templates for MVC websites?
问:
我将其标记为 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;
}
不知何故,我的直觉告诉我这是不正确的。
答:
控制器应该只为视图设置数据,并选择要显示的视图。视图应负责页面的布局,包括共享页面。我喜欢你的第一个样本而不是第二个样本。
评论
是的,您应该将页眉、页脚等拆分出来。
对于您展示的特定示例,这不是更好吗?
require "templates/header.php";
require "templates/navigation.php";
require "views/$page.php";
require "templates/footer.php";
(其中$page是“主页”、“搜索”等)
如果你使用直接的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";
评论
以下是我如何使用当前项目制作模板的简化版本,如果有任何用处:
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 参数进行切换,则切换是最安全的方法。
你在重复代码。这几乎不是一个好主意。为了接近您的初始示例,类似这样的东西肯定是可取的:
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 响应中返回其内容之前进一步处理该变量。
我同意 tvanfosson 的观点,并想解释一下它与 MVC 的关系和关系。
第二个示例的问题在于,控制器暴露于视图的构造方式。从严格意义上讲,控制器对视图的输入进行封送,并将它们传递给视图,仅此而已。
一种实用的思考方式是,如果视图根据应用程序要求或输入本身而变化。例如,如果生成的视图是针对 JavaScript 弹出窗口的,它可能会(并且可能会)使用一组不同的页眉、页脚、CSS、元等。在第二个示例中,所有内容都向控制器公开。在你的第一个中,是视图知道如何生成视图 -- 这正是重点。
为了进一步举例,假设 JavaScript 弹出窗口被重新设计为整页视图,或者针对 AJAX 进行了重构(或者弹出窗口/页面/AJAX 问题由输入确定,例如字段中的隐藏元素)。现在你正在拆开控制器,因为视图已经改变。与其说你违反了 MVC,不如说你一开始就不应该为它而烦恼。
如果所有文件名都与示例中所示的视图/页面请求匹配,则只需要一行,无需语句:switch
require "templates/header.php";
require "templates/navigation.php";
require 'views/' . $page . '.php'; // <-- one-liner
require "templates/footer.php";
下一个:如何抓取HTML标签的内容?
评论