理解 PHP 中的 MVC 视图
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16594907/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Understanding MVC Views in PHP
提问by Eduardo Naveda
I have to seem problems grasping the concept of Views in MVC, they are, according to what I've read, the layer that manages the presentation in the aplication, but many of the material I've been reading seem to be different regarding this matter in this one from PHP Master.com.
我在掌握 MVC 中的视图概念时似乎有问题,根据我所阅读的内容,它们是管理应用程序中的表示的层,但是我一直在阅读的许多材料似乎对此有所不同此事来自PHP Master.com。
The View is a class with functions that return some HTML code, where is the rest of my HTML? should it be put in independent .html pages that access this View code?
View 是一个具有返回一些 HTML 代码的函数的类,我的 HTML 的其余部分在哪里?它应该放在访问此视图代码的独立 .html 页面中吗?
In this article, from php-html.netthe View is a simple HTML file with a .php extension, but how are they accessing that data? I see no require()
or anything like the instantiations in the first tutorial.
在本文中,来自php-html.net的视图是一个带有 .php 扩展名的简单 HTML 文件,但它们如何访问该数据?我看不到require()
第一个教程中的实例化或类似的东西。
回答by tere?ko
Note:the MVC and MVC-inspired patterns are advanced constructs. They are meant to be used in codebases where ordinary object-oriented (that follows SOLIDand other guidelines) code starts to become unmanageable. By introducing this pattern you would impose additional constraints, which then lets you to contain very complex applications. MVC is notmeant for "hello world" apps.
注意:MVC 和 MVC-inspired 模式是高级构造。它们旨在用于普通面向对象(遵循SOLID和其他准则)代码开始变得难以管理的代码库中。通过引入这种模式,您将施加额外的约束,从而让您能够包含非常复杂的应用程序。MVC不适用于“hello world”应用程序。
Let's start from the beginning ...
让我们从头开始 ...
The core idea behind MVC and MVC-inspired design patterns is Separation of Concerns. Said separation is two-fold:
MVC 和受 MVC 启发的设计模式背后的核心思想是关注点分离。所说的分离有两个方面:
- model layer is separate from UI layer:
- views are separated from controllers
- 模型层与 UI 层是分开的:
- 视图与控制器分离
Model layer(not "class" or "object") would contain several groups of structures, each dealing with as different aspect of business logic. The major parts would be:
模型层(不是“类”或“对象”)将包含几组结构,每组作为业务逻辑的不同方面进行处理。主要部分将是:
- domain objects: validation, business rules
- storage abstraction: persistence and caching of data from domain objects
- services: application logic
- 域对象:验证、业务规则
- 存储抽象:域对象数据的持久化和缓存
- 服务:应用逻辑
Also there might be mixed in repositories, units of workand others.
UI layermostly consists of views and controllers. But they both utilize services to interact with the model layer. Services provide the way for controllers to change the state of model layer and for the views to gather information based on that new state.
UI 层主要由视图和控制器组成。但它们都利用服务与模型层进行交互。服务为控制器提供了更改模型层状态的方法,并为视图提供了基于新状态收集信息的方法。
In context of web the views and controllers form a loose pair, because of the request-response nature that web applications exhibit.
在 web 上下文中,视图和控制器形成松散的一对,因为 web 应用程序表现出的请求-响应性质。
It should be noted that although controllers canalter the state of the current view directly, it's more common that these changes are effected through the model. One reason to alter the view directly is, for example, when instead of XML you need to respond with JSON.
需要注意的是,虽然控制器可以直接改变当前视图的状态,但更常见的是这些改变是通过模型来实现的。直接更改视图的原因之一是,例如,当您需要用 JSON 响应而不是 XML 时。
Though it also could be argued that one could simple instantiate a different view for each output format and take advantage of polymorphism.
尽管也有人认为可以为每种输出格式简单地实例化不同的视图并利用多态性。
What is not view?
什么不是视图?
There is a widespread misconception that views are simply glorified template file. This mistake became extremely popular after release of RubyOnRails prototyping framework.
有一种普遍的误解,认为视图只是美化的模板文件。这个错误在 RubyOnRails 原型框架发布后变得非常流行。
Views are not templates. If you use them as such, you break the core principle behind MVC and MVC-inspired patterns.
视图不是模板。如果你这样使用它们,你就破坏了 MVC 和 MVC 启发模式背后的核心原则。
If you pretend that templates are views, it has an enormous impact on your architecture. There is no place for presentation logic in the view, therefore you push the presentation logic either in controller or model layer. The usual choice is "controller", because most of people understand that presentation logic has no place in model layer.
如果你假装模板是视图,它会对你的架构产生巨大的影响。视图中没有表示逻辑的位置,因此您可以在控制器或模型层中推送表示逻辑。通常的选择是“控制器”,因为大多数人都明白表示逻辑在模型层没有位置。
Essentially, this causes a merger of views and controllers.
本质上,这会导致视图和控制器的合并。
What is view doing?
视图在做什么?
The responsibility of the view is to deal with presentation logic. In context of web the goal for view is to produce a response to the user (which, btw, is the browser not the human).
视图的职责是处理表示逻辑。在 web 上下文中,视图的目标是对用户产生响应(顺便说一句,这是浏览器而不是人类)。
Technically it would be possible to create client side views, that user web sockets to observe model layer, but in practice it's virtually impossible to implement. Especially not in PHP environment.
从技术上讲,可以创建客户端视图,即用户 Web 套接字来观察模型层,但实际上几乎不可能实现。尤其不是在 PHP 环境中。
To create this response view acquires information from model layer and, based on gathered data, either assembles response by distributing data to templates and rendering or sometimes simple sending a HTTP location header.
创建这个响应视图需要从模型层获取信息,并根据收集到的数据,通过将数据分发到模板和渲染来组装响应,或者有时简单地发送一个 HTTP 位置标头。
When using Post/Redirect/Get, the redirect part is performed by the view, not the controller as often people tend to do.
当使用Post/Redirect/Get 时,重定向部分是由视图执行的,而不是像人们经常做的那样由控制器执行。
Highly subjective bit:
高度主观的位:
Lately I have preferred to interact with MVC using following approach:
最近我更喜欢使用以下方法与 MVC 交互:
// the factory for services was injected in constructors
$controller->{ $method.$command }($request);
$response = $view->{ $command }();
$response->send();
The $method
is the current REQUEST_METHOD, that has been adjusted fake a REST-like API, and the $command
is what people usually call "action". The controller has separate routines for GET
and POST
(an other) requests. This helps to avoid having same if
in every "action".
该$method
是当前REQUEST_METHOD,已调整过假类似REST的API,而$command
就是人们通常所说的“行动”。控制器具有用于GET
和POST
(另一个)请求的单独例程。这有助于避免if
在每个“动作”中都相同。
And on the view I call similarly named method, that prepares a response that is sent to the client.
在视图上,我调用了类似命名的方法,它准备了一个发送给客户端的响应。
Warning:I suspect that this setup contains an SRPviolation. Adopting it as your own might be a bad idea.
警告:我怀疑此设置包含SRP违规。把它当作你自己的可能是个坏主意。
What about DRY?
干呢?
As you might have noticed already, there is a slight problem with having views as instances. You would end up with repeating pieces of code. For example: menu or pagination.
您可能已经注意到,将视图作为实例存在一个小问题。你最终会得到重复的代码片段。例如:菜单或分页。
Lets look at pagination .. The pagination contains logic, but this logic is not related to the model layer. The model has no concept of "page". Instead this bit of logic would reside in the UI layer. But if each of your views contains or inherits pagination, then it would be a clear violation of SRP (and actually several other principles too).
让我们看看分页..分页包含逻辑,但此逻辑与模型层无关。该模型没有“页面”的概念。相反,这一点逻辑将驻留在 UI 层中。但是,如果您的每个视图都包含或继承了分页,那么这将明显违反 SRP(实际上还有其他几个原则)。
To avoid this issue you can (and should, IMHO) introduce presentation objectsin your views.
为了避免这个问题,你可以(并且应该,恕我直言)在你的视图中引入表示对象。
Note:while Fowler calls them "presentation models", I think that name just adds to the whole 'what is model' confusion. Therefore I would recommend to call them "presentation objects" instead.
注意:虽然 Fowler 称它们为“演示模型”,但我认为这个名字只会增加整个“什么是模型”的混乱。因此,我建议将它们称为“演示对象”。
The presentation objects deal with repeated pieces of logic. This makes the views much "lighter", and in some aspects starts to mirror the structure of services from the model layer.
表示对象处理重复的逻辑片段。这使得视图更“轻”,并且在某些方面开始从模型层镜像服务的结构。
The interaction between presentation objects and templatesbecomes similar to the interaction between domain objects and data mappers.
表示对象和模板之间的交互变得类似于域对象和数据映射器之间的交互。
Do I always need all of this?
我总是需要所有这些吗?
No.This specific approach is heavily geared towards code, where the UI layer has a lot of complexity and you need to separate the handling of input from presentation just to sane sane.
不。这种特定的方法非常适合代码,其中 UI 层具有很多复杂性,您需要将输入处理与表示分开,只是为了保持理智。
If your application has very simple UI, like .. emm .. you are making REST API for a larger integrated project. In such the pragmaticoption can be to just merge every controller-view pair into single class.
如果您的应用程序具有非常简单的 UI,例如 .. emm .. 您正在为更大的集成项目制作 REST API。在这种情况下,实用的选择可以是将每个控制器-视图对合并到单个类中。
It also can be a good step, when refactoring a legacy codebase, because this less-constrained approach lets you move entire chunks of old code. When you have isolated such pieces of older code and checked, that everything still works (since legacy code never has any tests .. that's how it becomes "legacy"), you then can start splitting it up further, while focusing on separating business logic from UI.
在重构遗留代码库时,这也是一个很好的步骤,因为这种约束较少的方法可以让您移动整个旧代码块。当您隔离了这些旧代码并进行检查时,一切仍然有效(因为遗留代码从来没有任何测试......这就是它成为“遗留”的方式),然后您可以开始进一步拆分它,同时专注于分离业务逻辑从用户界面。
P.S.I myself am still struggling with figuring out a way how best to deal with views. This post is less of an answer and more like a snapshot of my current understanding.
PS我自己仍在努力找出一种如何最好地处理视图的方法。这篇文章不是一个答案,更像是我目前理解的快照。
回答by J. Bruni
The second tutorial is the way that Code Igniter framework works, and the one which I am used to. I follow it even when using no framework at all.
第二个教程是 Code Igniter 框架的工作方式,也是我习惯的方式。即使根本不使用框架,我也遵循它。
In fact, the developer must put the MVC-like principles in practice, otherwise he/she can make the lasagne or spaghetti even using the most MVC-like oriented framework.
事实上,开发者必须将类似MVC的原则付诸实践,否则即使使用最类似MVC的框架,他/她也可以制作千层面或意大利面条。
Using the "PHP file as view template" approach, ideally one would use minimum PHP statements, basically only for repeat-structures (foreach ($array as $item)
), basic conditionals (if ($boolean)
) and echo
- as if it was, indeed, a template language, and nothing more.
使用“PHP 文件作为视图模板”方法,理想的情况是使用最少的 PHP 语句,基本上只用于重复结构 ( foreach ($array as $item)
)、基本条件 ( if ($boolean)
) 和echo
- 就好像它确实是一种模板语言,仅此而已。
So, the <?php ?>
tags in the view template files should be merely placeholders, and nothing else.
所以,<?php ?>
视图模板文件中的标签应该只是占位符,没有别的。
No database queries, access to the model, calculations, and the like should be performed in the view template file. It should be treated, basically, as an HTML file with placeholders. (With its associated CSS and JavaScript. Things may got more complex when the application relies on JavaScript / AJAX a lot...)
不应在视图模板文件中执行数据库查询、模型访问、计算等操作。基本上,它应该被视为带有占位符的 HTML 文件。(使用其关联的 CSS 和 JavaScript。当应用程序大量依赖 JavaScript / AJAX 时,事情可能会变得更加复杂......)
Following these simple principles, we effectively do some separation of the presentation from the business logic. Even sounding so simple, I'm tired of dealing with Code Igniter code which does not follow it. Some use "helper functions" to disguise the model/database calls - and think it is a good practice! :-)
遵循这些简单的原则,我们有效地将表示与业务逻辑进行了一些分离。即使听起来如此简单,我也厌倦了处理不遵循它的 Code Igniter 代码。有些人使用“辅助函数”来伪装模型/数据库调用 - 并认为这是一个很好的做法!:-)
You don't see a require
inside these PHP view template files because they are required instead, from the "view building" methods.
您看不到require
这些 PHP 视图模板文件的内部,因为它们是“视图构建”方法所必需的。
Of course, one should not echo
and/or print
from inside the controller and model functions. This is also very simple, but I am also tired to see spaghetti code echoing out HTML from inside CI controller methods.
当然,不应该echo
和/或print
来自控制器和模型函数内部。这也很简单,但我也厌倦了看到意大利面条式代码从 CI 控制器方法内部回显 HTML。
In practice, the controller calls the model methods, build all the necessary data for the view, and as a last step, calls the view (i.e., builds and output it), passing the already previously obtained data to it.
在实践中,控制器调用模型方法,为视图构建所有必要的数据,作为最后一步,调用视图(即构建并输出它),将之前已经获得的数据传递给它。
Makes sense? I don't know if I answered your question. At least, these are my "2 cents".
说得通?我不知道我是否回答了你的问题。至少,这些是我的“2 美分”。
回答by Jay Bhatt
Different frameworks use different logic to assign variables to view and get its content. Below is a simple example using ob_start() function.
不同的框架使用不同的逻辑来分配变量来查看和获取其内容。下面是一个使用 ob_start() 函数的简单示例。
<?php
$title = 'Hello...';
ob_start();
file_get_contents('view.phtml');
$viewContents = ob_get_clean();
echo $viewContents;
?>
//view.phtml
<b>Hello the title is <?php echo $title; ?></b>
Hope this answer your question...
希望这能回答你的问题...
回答by km6zla
You are supposed to pass a method in the view class everything it needs to build a view independent of the output format. Most developers will use some sort of templating engine to build the bulk of the page then fill in the body with request specific information. There are so many ways you can go about doing this. It is also good to have an abstract view class that defines helper methods for common elements like forms and inputs.
你应该在视图类中传递一个方法,它需要构建一个独立于输出格式的视图。大多数开发人员会使用某种模板引擎来构建页面的大部分内容,然后用请求特定的信息填充正文。有很多方法可以做到这一点。有一个抽象视图类也很好,它定义了表单和输入等常见元素的辅助方法。
This layer is abstracted so that the logic of your application doesn't have to change if you decide to change the design or output format in any way.
该层是抽象的,因此如果您决定以任何方式更改设计或输出格式,您的应用程序逻辑就不必更改。
Edit:Each module represented by an MVC set would have its own view that has a collection of methods responsible for sending your output the the browser. There are many ways you can go but here is one example:
编辑:由 MVC 集表示的每个模块都有自己的视图,该视图具有一组负责将输出发送到浏览器的方法。有很多方法可以去,但这里是一个例子:
class testModule_view extends viewAbstract {
public function showTestData($title, $subtitle, $data) {
$XHTML = '<h1>' . $title . '</h1>'
. '<h2>' . $subtitle . '</h2>'
. parent::data2table($data);
parent::outputToBrowser(DEFAULT_TEMPLATE, $XHTML);
}
}
This is just a quick example to give you an idea of what a simple view method might look like.
这只是一个简单的例子,让您了解一个简单的视图方法可能是什么样子。
回答by hice3000
When the browser calls a page, a controller will be loaded for this one. The controller manages the lifecycle of your app. He'll grab data from the model, which is only used to get data (maybe from a database). The View is only HTML, the controller will echo the view, and if neccessary, pass a few parameters to it.
当浏览器调用一个页面时,会为这个页面加载一个控制器。控制器管理您的应用程序的生命周期。他将从模型中获取数据,该数据仅用于获取数据(可能来自数据库)。视图只是 HTML,控制器会回显视图,如果需要,向它传递一些参数。
回答by David
<?php
class View {
类视图{
protected $data;
protected $path;
protected static function getDefaultViewPath() {
$router = App::getRouter();
if(!$router){
return false;
}
$controller_path = $router->getController();
$method_path = ($router->getMethodPrefix() !== "" ? $router->getMethodPrefix() . '_' : '') . $router->getAction();
return ROOT . "/views/" . $controller_path . "/" . $method_path . ".phtml";
}
public function __construct($data = array(), $path = null) {
if(!$path){
//default
$path = $this->getDefaultViewPath();
}
if(!file_exists($path)){
throw new Exception("Error view file!");
}
$this->data = $data;
$this->path = $path;
}
public function render(){
$data = $this->data;
ob_start();
include ($this->path);
$content = ob_get_clean();
return $content;
}
}
}
回答by David
Check this code:
检查此代码:
include_once(ROOT.'/'.'config/config.php');
function __autoload($class_name){
$lib_path = ROOT . '/' . 'lib/class.'.$class_name . '.php';
$controller_path = ROOT . '/' . 'controllers/'.str_replace("controller", "", strtolower($class_name)) . '.controller.php';
$model_path = ROOT . '/' . 'models/'.strtolower($class_name) . '.php';
if(file_exists($lib_path)){
require_once ($lib_path);
} else if (file_exists($controller_path)){
require_once ($controller_path);
} else if(file_exists($model_path)){
require_once ($model_path);
} else {
throw new Exception("File {$class_name} cannot be found!");
}
}