理解 PHP 中的 MVC 视图

2021-12-21 00:00:00 coding-style php model-view-controller

我在理解 MVC 中的视图概念时似乎有问题,根据我所读的内容,它们是管理应用程序中的表示的层,但我阅读的许多材料似乎都是与 :验证、业务规则

  • 存储抽象:域对象数据的持久化和缓存
  • 服务:应用逻辑
  • 也可能在存储库、工作单元 和其他.

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

    在 Web 上下文中,视图和控制器形成松散的一对,因为 Web 应用程序表现出请求-响应性质.

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

    尽管也有人认为可以为每种输出格式简单地实例化不同的视图并利用多态性.


    什么不是视图?

    有一种普遍的误解,认为视图只是美化的模板文件.这个错误在 RubyOnRails 原型框架发布后变得非常流行.

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

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

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


    视图在做什么?

    视图的职责是处理表示逻辑.在 Web 上下文中,视图的目标是对用户产生响应(顺便说一句,用户是浏览器而不是人).

    <块引用>

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

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

    <块引用>

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

    <小时>

    高度主观的部分:

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

    //服务工厂被注入到构造函数中$controller->{ $method.$command }($request);$response = $view->{ $command }();$response->send();

    $method 是当前的 REQUEST_METHOD,它已经被调整为一个 REST-like API,而 $command 是人们通常称为行动".控制器具有用于 GETPOST(另一个)请求的单独例程.这有助于避免在每个动作"中使用相同的 if.

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

    <块引用>

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


    干式呢?

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

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

    为了避免这个问题,你可以(也应该,恕我直言)在你的视图中引入展示对象.

    <块引用>

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

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

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


    我总是需要所有这些吗?

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

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

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

    <小时><块引用>

    P.S. 我自己仍在努力寻找一种最好的处理视图的方法.这篇文章不是一个答案,更像是我目前理解的快照.

    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.

    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?

    In this article, from php-html.net the 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.

    解决方案

    Note: the MVC and MVC-inspired patterns are advanced constructs. They are meant to be used in codebases where ordinary object-oriented (that follows SOLID and 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 not meant for "hello world" apps.


    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:

    • model layer is separate from UI layer:
    • views are separated from controllers

    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 work and others.

    UI layer mostly 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.

    In context of web the views and controllers form a loose pair, because of the request-response nature that web applications exhibit.

    It should be noted that although controllers can alter 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.

    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.

    Views are not templates. If you use them as such, you break the core principle behind MVC and MVC-inspired patterns.

    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).

    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.

    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.

    When using Post/Redirect/Get, the redirect part is performed by the view, not the controller as often people tend to do.


    Highly subjective bit:

    Lately I have preferred to interact with MVC using following approach:

      // 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".

    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 SRP violation. Adopting it as your own might be a bad idea.


    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).

    To avoid this issue you can (and should, IMHO) introduce presentation objects in 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.

    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 templates becomes 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.

    If your application has very simple UI, like .. emm .. you are making REST API for a larger integrated project. In such the pragmatic option can be to just merge every controller-view pair into single class.

    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.

    相关文章