在PHP中了解MVC视图
我不得不在MVC中理解Views的概念,根据我所读到的,它是在应用程序中pipe理演示文稿的层,但是我一直在阅读的许多材料似乎与此不同在这个从PHP Master.com重要 。
View是一个带有返回一些HTML代码的函数的类,我的HTML的其余部分在哪里? 应该把它放在独立的.html页面访问这个视图代码?
在本文中,来自php-html.net的View是一个扩展名为.php的简单HTML文件,但是他们如何访问这些数据呢? 我没有看到require()
或类似于第一个教程中的实例。
注意: MVC和MVC启发模式是高级构造。 它们是用在普通的面向对象(遵循SOLID和其他准则)代码开始变得难以pipe理的代码库中的。 通过引入这个模式,你会施加额外的约束,然后让你包含非常复杂的应用程序。 MVC 不适用于“hello world”应用程序。
让我们从头开始 …
MVC和MVCdevise模式背后的核心思想是分离关注点 。 所述分离是双重的:
- 模型层与表示层分离:
- 视图与控制器分离
模型层 (不是“类”或“对象”)将包含几组结构,每个结构处理业务逻辑的不同方面。 主要部分是:
- 域对象 :validation,业务规则
- 存储抽象:来自域对象的数据的持久性和caching
- 服务:应用程序逻辑
在仓库 , 工作单位和其他方面也可能有所不同。
表示层主要由视图和控制器组成。 但是他们都利用服务来与模型层进行交互。 服务为控制器提供了改变模型层状态的方法,以及基于新状态收集信息的视图。
在networking环境下,由于Web应用程序所具有的请求响应特性,视图和控制器形成一对松散的对。
应该注意的是,虽然控制器可以直接改变当前视图的状态,但更常见的是这些改变是通过模型来实现的。 直接改变视图的一个原因是,例如,当您不需要XML时,您需要使用JSON进行响应。
虽然也可以争辩说,可以简单地为每个输出格式实例化不同的视图,并利用多态性。
什么不是观点?
有一个普遍的误解,认为只是荣耀的模板文件。 这个错误在RubyOnRails原型框架发布后变得非常stream行。
视图不是模板 。 如果你这样使用它们,你就会违背MVC和MVC模式背后的核心原则。
如果您假装模板是视图,则会对您的架构产生巨大影响。 视图中没有UI逻辑的位置,因此您可以在控制器或模型层中推送UI逻辑。 通常的select是“控制器”,因为大多数人都明白,UI逻辑在模型层中没有地位。
实质上,这会导致视图和控制器的合并。
什么是观点在做什么?
该观点的责任是处理UI逻辑。 在networking环境下,观看的目标是产生对用户的响应(顺便说一句,浏览器不是人) 。
从技术上来说,创build客户端视图是可能的,用户可以通过web套接字来观察模型层,但是在实践中实际上不可能实现。 特别是在PHP环境下。
要创build此响应视图,需要从模型层获取信息,并根据收集到的数据通过向模板分发数据和渲染来组装响应,或者有时简单地发送HTTP位置标头。
当使用Post / Redirect / Get时 ,redirect部分是由视图执行的,而不是像往常人们往往那样执行的。
非常主观的一点 :
最近我更喜欢使用以下方法与MVC进行交互:
// the factory for services was injected in constructors $controller->{ $method.$command }($request); $view->{ $command }(); $view->respond();
$method
是当前的REQUEST_METHOD ,它被调整为一个类似REST的API,而$command
通常被称为“action”。 控制器具有GET
和POST
(另一个)请求的单独例程。 这有助于避免在每个“行动”中都有相同的if
。
在视图上我称之为两种方法。 首先是收集数据的dynamic调用。 其次旨在创造一些types的回应。
警告 :我怀疑这个设置包含SRP违规。 采用它作为你自己可能是一个坏主意。
干什么?
正如您可能已经注意到的那样,将视图作为实例存在一个小问题。 你会最终重复代码段。 例如:菜单或分页。
让我们看看分页..分页包含逻辑,但是这个逻辑与模型层无关。 该模型没有“页面”的概念。 相反,这一点的逻辑将驻留在表示层。 但是,如果你的每个观点都包含或inheritance了分页,那么这将明显违反SRP(实际上还有其他几个原则)。
为了避免这个问题,你可以(也应该,imho)在你的视图中引入表示对象 。
注意:当福勒称他们为“演示模型”时,我认为这个名字只是增加了整个“模型”的混淆。 因此,我build议将其称为“演示文稿对象”。
表示对象处理重复的逻辑。 这使得视图更“轻” ,并在某些方面开始反映模型层的服务结构。
表示对象和模板之间的交互变得类似于域对象和数据映射器之间的交互。
PS我自己还在努力想办法如何最好地处理意见。 这篇文章不是一个答案,更像是我目前理解的一个快照。
第二个教程是Code Igniter框架的工作方式,以及我习惯的方式。 即使不使用框架,我也会遵循它。
事实上,开发者必须把MVC类的原则付诸实践,否则他/她甚至可以使用最类似MVC的框架来制作千层面或意大利面条。
使用“PHP文件作为视图模板”的方法,理想情况下,人们会使用最小的PHP语句,基本上只用于重复结构( foreach ($array as $item)
),基本条件( if ($boolean)
)和echo
-它确实是一种模板语言,只不过是一种模板语言。
所以,视图模板文件中的<?php ?>
标签应该只是占位符 ,没有别的。
视图模板文件中不应该执行数据库查询,访问模型,计算等。 基本上,它应该被视为一个带有占位符的HTML文件。 (通过关联的CSS和JavaScript,当应用程序依赖于JavaScript / AJAX时,情况可能会变得更复杂…)
遵循这些简单的原则,我们有效地将演示文稿与业务逻辑分开。 即使听起来如此简单,我厌倦了处理Code Igniter代码,它不遵循它。 有些人使用“辅助函数”来掩饰模型/数据库调用 – 并认为这是一个好习惯! 🙂
您不会在这些PHP视图模板文件中看到require
因为它们是“视图构build”方法所必需的。
当然,不应该从控制器和模型function内部echo
和/或print
。 这也是非常简单的,但是我也厌倦了从CI控制器方法里面看到意大利面代码回显HTML。
在实践中,控制器调用模型方法,为视图构build所有必要的数据,并作为最后一步调用视图(即构build和输出视图),将之前获得的数据传递给它。
说得通? 我不知道我是否回答你的问题。 至less,这是我的“2美分”。
不同的框架使用不同的逻辑来分配variables来查看和获取其内容。 下面是一个使用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>
希望这个答案你的问题…
你应该在视图类中传递一个方法来创build独立于输出格式的视图。 大多数开发人员将使用某种模板引擎来构build页面的大部分,然后用请求特定的信息填充正文。 有这么多的方法可以去做这个。 拥有一个抽象的视图类,为像表单和input这样的常见元素定义辅助方法也是很好的做法。
这个图层是抽象的,这样如果您决定以任何方式更改devise或输出格式,您的应用程序的逻辑不必改变。
编辑:由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); } }
这只是一个简单的例子,让你了解一个简单的视图方法的样子。
当浏览器调用一个页面时,这个控制器将被加载。 控制器pipe理您的应用程序的生命周期。 他将从模型中获取数据,这只是用来获取数据(可能来自数据库)。 视图只是HTML,控制器会回显视图,如果需要的话,传递一些参数给它。
<?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; }
}
检查这个代码:
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!"); } }