如何处理MVC网站的模板?

时间:2022-06-09 08:22:50

I have this marked as PHP but only because I'll be using PHP code to show my problem.

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

So I have some code like this for the controller:

我有一些像这样的控制器代码:

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

Obviously there's more pages but this should illustrate my issue. There is a common header, navigation and footer for both of these pages (and for all pages on the site for that matter). Should I be using multiple require statements? My first guess would be:

显然还有更多的页面,但这应该能说明我的问题。这两个页面都有一个通用的页眉、导航和页脚(以及站点上的所有页面)。我应该使用多个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;
}

Somehow my gut tells me this isn't correct.

我的直觉告诉我这是不对的。

8 个解决方案

#1


5  

The controller should just set up the data for the view and choose which view to display. The view should be responsible for the layout of the page, including shared pages. I like your first sample over the second.

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

#2


1  

Yes, you should have the header, footer, etc. split out.

是的,你应该有页眉,页脚等等。

For the particular example you show, wouldn't this work better?

对于你展示的这个例子,这不是更好吗?

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

(Where $page is 'home', 'search', etc.)

($page是“home”、“search”等)

#3


0  

Here's a simplified version of how I do templates with my current project, if it's any use:

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

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";
    }
}

The idea behind it is that you can write "include 'Template.inc'; new Template;" and it arranges for do_output() to run at the end of the script automatically. There's a few things left out from it like the method used to pass variables to the template.

它背后的想法是,你可以写“包括‘Template.inc’;它将do_output()安排在脚本的末尾自动运行。这里还有一些东西,比如将变量传递给模板的方法。

You've mentioned you're not using PHP, and there's a few PHP-isms in there: register_shutdown_function() makes sure the templates get called before object destructors but after the main script, and the calls to $this->header()/footer() are magic function calls that just do display('header') and display('footer'), they're meant to be overridden.

您提到过您没有使用PHP,其中有一些PHP风格:register_shutdown_function()确保在对象析构函数之前调用模板,但是在主脚本之后调用模板,对$this的调用——>header()/footer()是神奇的函数调用,它只执行显示('header')和显示('footer'重载'),它们是命中的。

Of course there's nothing wrong with using a switch like the example you've posted, but you don't need the headers/footers inside every case statement. Something like this would do the same thing:

当然,使用一个类似于你发布的例子的开关并没有什么错,但是你不需要在每个case语句中使用header /footer。类似这样的事情也会发生:

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";

...or you could replace the switch() with something based on the filename like I've used above, if it works for the way your pages are set up. A switch is the safest way if you plan on doing it through URL parameters though.

…或者,您也可以根据上面使用的文件名替换switch(),如果它适用于页面的设置方式。如果您打算通过URL参数进行切换,那么切换是最安全的方式。

#4


0  

You're repeating code. This is next to never a good idea. To stay close to your initial example, something like this would surely be preferable:

你重复的代码。这绝不是一个好主意。为了更接近你最初的例子,类似这样的事情肯定是更好的:

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";

It's hard to give more advice without knowing more about your architectural approach. For instance it would be advisable to have this part of the controller, which simply prepares the output, in a very central place and to start output buffering before including the view templates. That way you could store the output in a variable which you might want to process further before returnin its contents in the HTTP response.

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

#5


0  

I agree with tvanfosson, and want to explain why and how it relates to MVC.

我同意tvanfosson的观点,并想解释它为什么和如何与MVC关联。

The problem with your second example is that the controller is exposed to how the view is constructed. In a strict sense, the controller marshals the inputs for the view and passes them to it, and nothing more.

第二个示例的问题是,控制器公开了视图的构造方式。严格地说,控制器对视图的输入进行编组并将它们传递给视图,仅此而已。

A practical way of thinking of this is if the view shifts depending on application requirements or the inputs themselves. For example, if the view being generated is for a JavaScript pop-up, it might (and probably will) use a different set of headers, footers, CSS, meta, etc. With your second example, all that is exposed to the controller. In your first, it's the view who knows how to generate the view -- which is exactly the point.

考虑这个问题的一种实际方法是,如果视图根据应用程序需求或输入本身发生变化。例如,如果生成的视图是用于一个JavaScript弹出窗口的,那么它可能(也可能)使用一组不同的header、footer、CSS、meta等等。首先,视图知道如何生成视图——这正是关键所在。

To take my example further, imagine that the JavaScript pop-up is redesigned to be a full page view, or is refactored for AJAX (or the pop-up/page/AJAX question is determined by inputs, such as a hidden element in a field). Now you're ripping apart the controller because the view has changed. It's not so much that you've violated MVC, but you shouldn't have bothered with it in the first place.

进一步举例来说,假设JavaScript弹出框被重新设计成一个完整的页面视图,或者为AJAX重构(或者弹出/页面/AJAX问题是由输入决定的,比如字段中的隐藏元素)。现在你把控制器拆开了因为视图变了。这并不是说你违反了MVC,但是你本来就不应该为它烦恼。

#6


0  

If all of your file names match your view/page request as shown in your example then you just need one line and no switch statement:

如果您的所有文件名与您的示例中所示的视图/页面请求相匹配,那么您只需要一行,并且没有switch语句:

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

#7


-2  

Is there any particular reason why you're not using an MVC framework such as Symfony or CakePHP?

您是否有什么特别的理由不使用MVC框架,比如Symfony或CakePHP?

I know this does not answer the question directly, but it might be helpful.

我知道这并不能直接回答问题,但它可能会有帮助。

#8


-2  

If you are using straight PHP pages as your templates, you could essentially set a global/session variable to hold the page you want. You would have a "main template" php page which includes the header and footer elements, then calls a include for the $page. Something like this in the controller:

如果您正在使用直接的PHP页面作为模板,您可以设置一个全局/会话变量来保存您想要的页面。您将拥有一个包含页眉和页脚元素的“主模板”php页面,然后调用$页面的include。控制器里有这样的东西:

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

and then in the main.php template file:

然后总体来说。php模板文件:

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

#1


5  

The controller should just set up the data for the view and choose which view to display. The view should be responsible for the layout of the page, including shared pages. I like your first sample over the second.

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

#2


1  

Yes, you should have the header, footer, etc. split out.

是的,你应该有页眉,页脚等等。

For the particular example you show, wouldn't this work better?

对于你展示的这个例子,这不是更好吗?

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

(Where $page is 'home', 'search', etc.)

($page是“home”、“search”等)

#3


0  

Here's a simplified version of how I do templates with my current project, if it's any use:

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

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";
    }
}

The idea behind it is that you can write "include 'Template.inc'; new Template;" and it arranges for do_output() to run at the end of the script automatically. There's a few things left out from it like the method used to pass variables to the template.

它背后的想法是,你可以写“包括‘Template.inc’;它将do_output()安排在脚本的末尾自动运行。这里还有一些东西,比如将变量传递给模板的方法。

You've mentioned you're not using PHP, and there's a few PHP-isms in there: register_shutdown_function() makes sure the templates get called before object destructors but after the main script, and the calls to $this->header()/footer() are magic function calls that just do display('header') and display('footer'), they're meant to be overridden.

您提到过您没有使用PHP,其中有一些PHP风格:register_shutdown_function()确保在对象析构函数之前调用模板,但是在主脚本之后调用模板,对$this的调用——>header()/footer()是神奇的函数调用,它只执行显示('header')和显示('footer'重载'),它们是命中的。

Of course there's nothing wrong with using a switch like the example you've posted, but you don't need the headers/footers inside every case statement. Something like this would do the same thing:

当然,使用一个类似于你发布的例子的开关并没有什么错,但是你不需要在每个case语句中使用header /footer。类似这样的事情也会发生:

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";

...or you could replace the switch() with something based on the filename like I've used above, if it works for the way your pages are set up. A switch is the safest way if you plan on doing it through URL parameters though.

…或者,您也可以根据上面使用的文件名替换switch(),如果它适用于页面的设置方式。如果您打算通过URL参数进行切换,那么切换是最安全的方式。

#4


0  

You're repeating code. This is next to never a good idea. To stay close to your initial example, something like this would surely be preferable:

你重复的代码。这绝不是一个好主意。为了更接近你最初的例子,类似这样的事情肯定是更好的:

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";

It's hard to give more advice without knowing more about your architectural approach. For instance it would be advisable to have this part of the controller, which simply prepares the output, in a very central place and to start output buffering before including the view templates. That way you could store the output in a variable which you might want to process further before returnin its contents in the HTTP response.

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

#5


0  

I agree with tvanfosson, and want to explain why and how it relates to MVC.

我同意tvanfosson的观点,并想解释它为什么和如何与MVC关联。

The problem with your second example is that the controller is exposed to how the view is constructed. In a strict sense, the controller marshals the inputs for the view and passes them to it, and nothing more.

第二个示例的问题是,控制器公开了视图的构造方式。严格地说,控制器对视图的输入进行编组并将它们传递给视图,仅此而已。

A practical way of thinking of this is if the view shifts depending on application requirements or the inputs themselves. For example, if the view being generated is for a JavaScript pop-up, it might (and probably will) use a different set of headers, footers, CSS, meta, etc. With your second example, all that is exposed to the controller. In your first, it's the view who knows how to generate the view -- which is exactly the point.

考虑这个问题的一种实际方法是,如果视图根据应用程序需求或输入本身发生变化。例如,如果生成的视图是用于一个JavaScript弹出窗口的,那么它可能(也可能)使用一组不同的header、footer、CSS、meta等等。首先,视图知道如何生成视图——这正是关键所在。

To take my example further, imagine that the JavaScript pop-up is redesigned to be a full page view, or is refactored for AJAX (or the pop-up/page/AJAX question is determined by inputs, such as a hidden element in a field). Now you're ripping apart the controller because the view has changed. It's not so much that you've violated MVC, but you shouldn't have bothered with it in the first place.

进一步举例来说,假设JavaScript弹出框被重新设计成一个完整的页面视图,或者为AJAX重构(或者弹出/页面/AJAX问题是由输入决定的,比如字段中的隐藏元素)。现在你把控制器拆开了因为视图变了。这并不是说你违反了MVC,但是你本来就不应该为它烦恼。

#6


0  

If all of your file names match your view/page request as shown in your example then you just need one line and no switch statement:

如果您的所有文件名与您的示例中所示的视图/页面请求相匹配,那么您只需要一行,并且没有switch语句:

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

#7


-2  

Is there any particular reason why you're not using an MVC framework such as Symfony or CakePHP?

您是否有什么特别的理由不使用MVC框架,比如Symfony或CakePHP?

I know this does not answer the question directly, but it might be helpful.

我知道这并不能直接回答问题,但它可能会有帮助。

#8


-2  

If you are using straight PHP pages as your templates, you could essentially set a global/session variable to hold the page you want. You would have a "main template" php page which includes the header and footer elements, then calls a include for the $page. Something like this in the controller:

如果您正在使用直接的PHP页面作为模板,您可以设置一个全局/会话变量来保存您想要的页面。您将拥有一个包含页眉和页脚元素的“主模板”php页面,然后调用$页面的include。控制器里有这样的东西:

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

and then in the main.php template file:

然后总体来说。php模板文件:

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