35

我似乎在掌握 MVC 中的视图概念时遇到了问题,根据我所阅读的内容,它们是管理应用程序中表示的层,但我一直在阅读的许多材料似乎与此不同这件事来自PHP Master.com

View 是一个具有返回一些 HTML 代码的函数的类,我的 HTML 的其余部分在哪里?是否应该将其放在访问此视图代码的独立 .html 页面中?

在本文中,来自php-html.net的 View 是一个带有 .php 扩展名的简单 HTML 文件,但他们如何访问该数据?我看不到require()第一个教程中的实例化或任何类似的实例化。

4

7 回答 7

81

注意: MVC 和受 MVC 启发的模式是高级构造。它们旨在用于普通面向对象(遵循SOLID和其他准则)代码开始变得难以管理的代码库中。通过引入这种模式,您将施加额外的约束,从而使您能够包含非常复杂的应用程序。MVC不适用于“hello world”应用程序。


让我们从头开始 ...

MVC 和受 MVC 启发的设计模式背后的核心思想是关注点分离。所述分离是双重的:

  • 模型层与 UI 层是分开的:
  • 视图与控制器分离

在此处输入图像描述

模型层(不是“类”或“对象”)将包含几组结构,每组都作为业务逻辑的不同方面处理。主要部分是:

  • 领域对象:验证、业务规则
  • 存储抽象:来自域对象的数据的持久性和缓存
  • 服务:应用逻辑

也可能混合在存储库工作单元和其他中。

UI 层主要由视图和控制器组成。但它们都利用服务与模型层进行交互。服务为控制器提供了改变模型层状态的方法,并为视图提供了基于新状态收集信息的方法。

在 Web 上下文中,由于 Web 应用程序表现出的请求-响应特性,视图和控制器形成了松散的一对。

需要注意的是,虽然控制器可以直接改变当前视图的状态,但更常见的是这些改变是通过模型来实现的。直接更改视图的一个原因是,例如,当您需要使用 JSON 来响应而不是 XML 时。

尽管也可以争辩说,可以简单地为每种输出格式实例化不同的视图并利用多态性。


什么不是视图?

人们普遍认为视图只是美化的模板文件。这个错误在 RubyOnRails 原型框架发布后变得非常流行。

视图不是模板。如果你这样使用它们,你就打破了 MVC 和 MVC 启发模式背后的核心原则。

如果你假装模板是视图,它会对你的架构产生巨大的影响。视图中没有表示逻辑的位置,因此您可以在控制器或模型层中推送表示逻辑。通常的选择是“控制器”,因为大多数人都明白表示逻辑在模型层没有位置。

本质上,这会导致视图和控制器的合并。


视图在做什么?

视图的职责是处理表示逻辑。在网络环境中,视图的目标是对用户产生响应(顺便说一句,这是浏览器而不是人类)

从技术上讲,可以创建客户端视图,即用户 Web 套接字来观察模型层,但实际上几乎不可能实现。尤其是在 PHP 环境中。

要创建此响应视图,需要从模型层获取信息,并根据收集到的数据,通过将数据分发到模板并呈现或有时简单地发送 HTTP 位置标头来组装响应。

当使用Post/Redirect/Get时,重定向部分由视图执行,而不是人们经常做的控制器。


高度主观的位

最近我更喜欢使用以下方法与 MVC 交互:

  // the factory for services was injected in constructors
  $controller->{ $method.$command }($request);
  $response = $view->{ $command }();
  $response->send();

$method是当前的REQUEST_METHOD,它已经被调整为一个类似 REST 的 API,这$command就是人们通常所说的“动作”。GET控制器对请求和POST(另一个)请求具有单独的例程。这有助于避免if在每个“动作”中都出现相同的情况。

在视图中,我调用了类似命名的方法,它准备发送给客户端的响应。

警告:我怀疑此设置包含SRP违规。把它当作你自己的可能是个坏主意。


干呢?

您可能已经注意到,将视图作为实例存在一个小问题。你最终会得到重复的代码。例如:菜单或分页。

让我们看看分页.. 分页包含逻辑,但这个逻辑与模型层无关。该模型没有“页面”的概念。相反,这部分逻辑将驻留在 UI 层中。但是,如果您的每个视图都包含或继承了分页,那么这将明显违反 SRP(实际上也违反了其他几项原则)。

为了避免这个问题,您可以(并且应该,恕我直言)在您的视图中引入演示对象。

注意:虽然 Fowler 称它们为“演示模型”,但我认为这个名称只会增加整个“什么是模型”的混乱。因此,我建议将它们称为“演示对象”。

表示对象处理重复的逻辑片段。这使得视图“更轻”,并且在某些方面开始反映模型层的服务结构。

表示对象和模板之间的交互变得类似于域对象和数据映射器之间的交互。


我总是需要这一切吗?

,这种特定方法主要针对代码,其中 UI 层具有很多复杂性,您需要将输入处理与表示分开,以保持理智。

如果您的应用程序具有非常简单的 UI,例如 .. emm .. 您正在为更大的集成项目制作 REST API。在这样的实用选项中,可以将每个控制器视图对合并到单个类中。

在重构遗留代码库时,这也是一个很好的步骤,因为这种约束较少的方法可以让您移动整个旧代码块。当您隔离了这些旧代码并检查了一切是否仍然有效(因为遗留代码从来没有任何测试......这就是它成为“遗留”的方式),然后您可以开始进一步拆分它,同时专注于分离业务逻辑从用户界面。


PS我自己仍在努力寻找一种如何最好地处理观点的方法。这篇文章不是一个答案,更像是我目前理解的快照。

于 2013-05-16T20:18:44.657 回答
11

第二个教程是 Code Igniter 框架的工作方式,也是我习惯的。即使根本不使用框架,我也会遵循它。

事实上,开发者必须将类似MVC的原则付诸实践,否则即使使用最类似MVC的框架,他/她也可以做出千层面或意大利面。

使用“PHP 文件作为视图模板”的方法,理想情况下人们会使用最少的 PHP 语句,基本上只用于重复结构 ( foreach ($array as $item))、基本条件 ( if ($boolean)) 和echo- 就好像它确实是一种模板语言,仅此而已。

因此,<?php ?>视图模板文件中的标签应该只是占位符,没有别的。

不应在视图模板文件中执行数据库查询、对模型的访问、计算等。基本上,它应该被视为带有占位符的 HTML 文件。(及其关联的 CSS 和 JavaScript。当应用程序大量依赖 JavaScript / AJAX 时,事情可能会变得更加复杂......)

遵循这些简单的原则,我们有效地将表示与业务逻辑进行了一些分离。即使听起来很简单,我也厌倦了处理不遵循它的 Code Igniter 代码。有些人使用“辅助函数”来伪装模型/数据库调用——并认为这是一个好习惯!:-)

require在这些 PHP 视图模板文件中看不到 a,因为它们是必需的,而是来自“视图构建”方法。

当然,一个不应该echo和/或print来自控制器和模型函数的内部。这也很简单,但我也厌倦了看到从 CI 控制器方法内部回显 HTML 的意大利面条代码。

在实践中,控制器调用模型方法,为视图构建所有必要的数据,作为最后一步,调用视图(即构建并输出它),将之前已经获得的数据传递给它。

说得通?我不知道我是否回答了你的问题。至少,这些是我的“2 美分”。

于 2013-05-16T19:12:00.730 回答
1

您应该在视图类中传递一个方法,以构建独立于输出格式的视图所需的一切。大多数开发人员将使用某种模板引擎来构建大部分页面,然后在正文中填写请求特定信息。有很多方法可以做到这一点。拥有一个为常见元素(如表单和输入)定义辅助方法的抽象视图类也很好。

该层是抽象的,因此如果您决定以任何方式更改设计或输出格式,则不必更改应用程序的逻辑。

编辑: 由 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);
    }
}

这只是一个简单的示例,让您了解简单的视图方法可能是什么样子。

于 2013-05-16T18:38:25.917 回答
1

不同的框架使用不同的逻辑来分配变量以查看和获取其内容。下面是一个使用 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>

希望这能回答你的问题...

于 2013-05-16T18:51:37.073 回答
0

当浏览器调用一个页面时,会为此页面加载一个控制器。控制器管理您的应用程序的生命周期。他将从模型中获取数据,该模型仅用于获取数据(可能来自数据库)。视图只是 HTML,控制器将回显视图,如果需要,传递一些参数给它。

于 2013-05-16T18:39:18.450 回答
0
<?php

类视图 {

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

}

于 2017-09-18T17:09:40.050 回答
0

检查此代码:

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

}
于 2017-09-18T17:14:07.210 回答