PHP-使用MVC构建Slim3 Web应用程序并了解模型的作用 [英] PHP - Structuring a Slim3 web application using MVC and understanding the role of the model

查看:97
本文介绍了PHP-使用MVC构建Slim3 Web应用程序并了解模型的作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Slim3框架以及Twig模板系统在php中创建一个身份验证系统,并且对于数据库,我正在将MySQL与PDO结合使用.我也在尝试使用模型视图控制器设计模式来实现它.但是,我很难理解如何在Web应用程序中使用MVC结构.我在网络上查看了过多的说明,似乎没有明确的答案.很多人说要使用PHP框架,例如Laravel,Symfony或CodeIgniter,因为他们显然采用了类似MVC的结构.但是,我宁愿保持简单并手动编写代码,而不要使用框架.

I’m trying to create an authentication system in php with the Slim3 framework along with the Twig template system, and for the database I’m using MySQL with PDO. I’m also trying to implement it using a model view controller design pattern. However I’m having difficulty understanding how to use an MVC structure for a web application. I’ve looked at a plethora of explanations on the web and there doesn’t seem to be a clear cut answer. A lot of people say to to use a php framework such as Laravel, Symfony or CodeIgniter as they apparently employ an MVC like structure. However I would much rather keep things simple and to write the code manually rather than using a framework.

目前,我看到MVC的两种解释.此图中描绘了第一个:

Currently there are two interpretations of MVC that I see. The first one being depicted in this diagram:

我看到的另一种解释是:(摘自此YouTube视频)

The other interpretation I’ve seen is this: (which is taken from this YouTube video)

我已经完成了研究.问题和答案,例如

I have done my research. Questions and answers such as this and this have been helpful. But I’m still not sure how I might structure my own applications, specifically indentifying and understanding the model aspect of MVC. I’ll now explain the register process of my authentication app. So you have an idea how my code is works.

首先,我有一个SQLQueries类,该类将一系列SQL语句简单地放入函数中.然后,我有一个SQLWrapper类,该类具有的功能可以例如在数据库中存储新用户的详细信息.该类还调用SQLQueries类中的函数.我还有一个ValidateSanitize类,该类具有清除用户输入以及检查用户输入在表单中是否有效的功能.我认为这三个类是MVC模型方面的一部分,但我不确定.我看到了许多其他的使用用户模型类"的教程,但我的应用程序中没有必要.

Firstly I have an SQLQueries class that simply puts a series of SQL statements into functions. I then have a SQLWrapper class that has functions that can for example store a new users details inside the database. This class also calls functions from the SQLQueries class. I also have a ValidateSanitize class that has functions that cleans user input as well as checking if user input is valid in the form. These three classes I think are part of the model aspect of MVC but I'm not sure. I see a lot of other tutorials using a ‘User Model class’ but I can’t find the need for one in my application.

我的视图只是显示HTML的Twig模板,例如主页,注册,登录等.然后我有了控制器.我打算让多个控制器执行不同的操作.目前,我仅实现了负责注册和登录用户的AuthController.

My views are simply Twig templates that display html, such as the homepage, register, login etc. I then have controllers. I intend to have multiple controllers that do different things. For now I’ve only implemented the AuthController which is responsible for Registering and Signing a user in.

因此AuthController要做的第一件事是在名为getRegisterForm的函数中显示寄存器表单.用户提交表单后,postRegisterForm函数将获取该用户输入并将其分配给受污染的变量.

So the first thing the AuthController does is to display the register form in a function called getRegisterForm. Once the user has submitted the form the postRegisterForm function takes that user input and assigns it to tainted variables.

public function postRegisterForm($request, $response)  
{
   $arr_tainted_params = $request->getParsedBody(); 

   $tainted_email = $arr_tainted_params['email'];  it a variable
   $tainted_username = $arr_tainted_params['username'];
   $tainted_password = $arr_tainted_params['password'];
   $tainted_password_confirm = $arr_tainted_params['password_confirm'];

接下来将实例化所有前面三个类以及数据库详细信息,以便可以在AuthController中使用它们的功能:

Next all of the three previous classes as well as the database details are instantiated so their functions can be used in the AuthController:

$sanitizer_validator = $this->container->ValidateSanitize;
$sql_wrapper = $this->container->SQLWrapper;
$sql_queries = $this->container->SQLQueries;
$db_handle = $this->container->get('dbase');

然后,使用sanitize_input函数清除受污染的用户详细信息.然后,将清理后的用户详细信息输入到验证功能中,以确保它们不会触发任何验证违规行为.密码也在此处散列:

The tainted user details are then cleaned with the sanitize_input function. The cleaned user details are then fed into the validate functions to make sure they don’t trigger any validation violations. The password is also hashed here:

$cleaned_email = $sanitizer_validator->sanitize_input($tainted_email, FILTER_SANITIZE_EMAIL); 
$cleaned_username = $sanitizer_validator->sanitize_input($tainted_username, FILTER_SANITIZE_STRING);
$cleaned_password = $sanitizer_validator->sanitize_input($tainted_password, FILTER_SANITIZE_STRING);
$cleaned_password_confirm = $sanitizer_validator->sanitize_input($tainted_password_confirm, FILTER_SANITIZE_STRING);

$hashed_cleaned_password = password_hash($cleaned_password, PASSWORD_DEFAULT); 

$sanitizer_validator->check_email_exists($cleaned_email);
$sanitizer_validator->validate_email($cleaned_email);
$sanitizer_validator->validate_username($cleaned_username);
$sanitizer_validator->validate_password($cleaned_password);
$sanitizer_validator→validate_password_confirm($cleaned_password_confirm);

最后,有一个if语句检查所有验证错误消息是否为空.如果是的话,我们为SQLWrapper类提供数据库详细信息以及SQLQueries类对象.然后,我们通过调用SQLWrapper类store-details函数将用户详细信息插入数据库.最后,我们将用户定向到登录页面,以便用户可以登录其新注册的帐户.

Finally there is an if statement that checks to see if all validation error messages are empty. If they are we provide the SQLWrapper class with the database details as well as a SQLQueries class object. We then insert the users details into the database by calling the SQLWrapper classes store-details function. Finally we direct the user to the login page, so the user can sign into their newly registered account.

if ($sanitizer_validator->get_validate_messages('email_error') == ' ' && $sanitizer_validator->get_validate_messages('username_error') == ' '
    && $sanitizer_validator->get_validate_messages('password_error') == ' ' && $sanitizer_validator->check_passwords_match($cleaned_password, $cleaned_password_confirm ) == true
    && $sanitizer_validator->check_email_exists($cleaned_email) == false)
{   

    $sql_wrapper->set_db_handle($db_handle); 
    $sql_wrapper->set_sql_queries($sql_queries); 

     $sql_wrapper->store_details($cleaned_email, $cleaned_username, $hashed_cleaned_password);
     return $response→withRedirect($this→container→router→pathFor('login'));

}

但是,如果任何验证错误消息都不为空,那么我们将调用SanitiseValidate display_validate_messages,该消息仅将消息设置为要显示在寄存器树枝模板上的会话.然后,我们重定向回注册页面,以便用户可以看到验证错误消息.

However if any of the validate error messages are not blank, then we call the SanitiseValidate display_validate_messages which simply sets the messages into a session to be displayed on the register twig template. We then redirect back to the register page so the user can see the validation error messages.

else
  {
      $sanitizer_validator->display_validate_messages();
      return $response->withRedirect($this->container->router->pathFor('register'));
  }
}

因此,基于用户注册帐户的这种结构.这是否遵循干净的简单MVC结构,还是需要进行一些更改?我的任何课程都扮演过模型的角色吗?关于我的结构的任何建议和技巧将不胜感激.

So based on this structure of a user registering an account. Does this adhere to a clean simple MVC structure or do some changes need to be made? Do any of my classes take the role of a model? Any suggestions and tips regarding my structure will be appreciated.

可以在我的GitHub上看到完整的应用程序如果那会有所帮助.请注意,此版本比我在此问题中使用的示例代码稍旧.

The full application can be seen on my GitHub if that would be helpful. Note that this version is slightly older than the sample code I used in this question.

推荐答案

的确,关于在Web应用程序中应如何应用MVC模式有多种方法.众多变体是一个简单事实的结果,即原始的MVC模式(为桌面应用程序开发(由Trygve Reenskaug,在1979年开发))无法原样应用于Web应用程序. 此处是一个简短说明.但是,从这套方法中,您可以选择最符合您的要求的一种.也许您会在下定决心之前尝试更多的方法.不过,在某个时候,您会知道哪一个适合您的视野.

Indeed, there are multiple approaches regarding how the MVC pattern should be applied in web applications. This multitude of variants is the result of the simple fact, that the original MVC pattern - developed for desktop applications (by Trygve Reenskaug, in 1979) - can not be applied as is to the web applications. Here is a little description. But, from this set of approaches, you can choose one which best complies with your requirements. Maybe you'll try more of them before you'll make your mind. Though, at some point, you'll know which one fits to your vision.

在下面的图中,我试图在Web MVC工作流中展示我选择的方法-主要是受 Robert的启发马丁的演示主题演讲:迷失的年代的建筑(根据 Creative Commons Attribution ShareAlike 3.0 许可).

In the following diagrams I tried to present my chosen approach on the web MVC workflow - mainly inspired by Robert Martin's presentation Keynote: Architecture the Lost Years (licensed under a Creative Commons Attribution ShareAlike 3.0).

通常,您可以将Web MVC应用程序视为由以下部分组成:

In general, you could think of a web MVC application as composed of the following parts:

  1. 域模型(例如模型,例如模型层);
  2. 服务层(可选);
  3. 投放机制
  4. 其他组件(例如自己的库等).
  1. Domain model (e.g. model, e.g. model layer);
  2. Service layer (optional);
  3. Delivery mechanism;
  4. Other components (like own libraries, etc).

1) 域模型应包含以下组件:

  • 实体(例如域对象)和值对象.他们根据属性和行为对业务规则进行建模,并且与应用程序无关,可以被多个(类型)应用程序使用.
  • (数据)映射器 ,以及可选的 存储库 .这些组件负责持久性逻辑.
  • 外部服务.它们用于执行涉及使用外部/自有库的不同任务(例如,发送电子邮件,解析文档等).
  • Entities (e.g. domain objects) and value objects. They model the business rules in terms of properties and behavior and, being application-independent, can be used by multiple (types of) applications.
  • (Data) mappers and, optional, repositories. These components are responsible with the persistence logic.
  • External services. They are used to perform different tasks involving the use of external/own libraries (like sending emails, parsing documents, etc).

此外,领域模型可以分为两部分:

Further, the domain model could be split into two parts:

a) 域模型抽象 .如果实现了,这将是交付机制的组件或服务层的服务访问的模型层的唯一空间:

a) Domain model abstraction. This would be the only space of the model layer accessed by the components of the delivery mechanism, or by the services of the service layer - if one is implemented:

  • 实体和价值对象;
  • (数据)映射器抽象以及可选的存储库抽象;
  • 外部服务的抽象.

  • Entities and value objects;
  • (Data) mapper abstractions and, optional, repository abstractions;
  • Abstractions of external services.

注意:抽象是指接口和抽象类.

b) 域模型实现 .该空间将驻留不同域模型抽象的实现(请参见 a ).依赖项注入容器(作为传递机制的一部分)将负责将这些具体类的实例作为依赖项(例如,作为构造函数的参数)传递给应用程序的其他组件(如控制器,视图,服务等).

b) Domain model implementation. This space would be the one in which the implementations of the different domain model abstractions (see a) would reside. The dependency injection container (as part of the delivery mechanism) will be responsible with passing instances of these concrete classes as dependencies - as constructor arguments, for example - to the other components of the application (like controllers, views, services, etc).

2):服务层(可选):从技术上讲,交付机制的组件可以直接与域模型的元素进行交互.尽管此类交互涉及(很多)操作,但这些操作仅特定于模型,而不特定于交付机制.因此,一个不错的选择是将这些操作的执行推迟到服务类(例如 services )上,作为所谓的

2) Service layer (optional): Technically, the components of the delivery mechanism could directly interact with the elements of the domain model. Though such interactions involve (a lot of) operations, specific only to the model, not to the delivery mechanism. Therefore, a good choice is to defer the execution of these operations to service classes (e.g. services), as part of the so-called service layer. The delivery mechanism components will then use only these services to access the domain model components.

注意:实际上,服务层可以视为模型层的一部分.在下面的图表中,我更喜欢将其显示为位于模型外部的图层.但是,在文件系统示例中,我将相应的文件夹放在域空间中.

3) 交付机制总结了用于确保用户与模型层的组件之间交互的构造.对于用户,我不是指一个人,而是一个人可以与之交互的界面,例如浏览器,控制台(例如CLI),桌面GUI等.

3) The delivery mechanism sums up the constructs used to assure the interaction between the user and the model layer's components. By user I don't mean a person, but an interface with which a person can interact - like a browser, a console (e.g. CLI), a desktop GUI, etc.

  • Web服务器:通过单个入口点(index.php)解析用户请求.

  • Web server: parses the user request through a single point of entry (index.php).

依赖性注入容器:为应用程序的不同组件提供适当的依赖性.

Dependency injection container: provides the proper dependencies to the different components of the application.

HTTP消息(例如HTTP请求和HTTP响应)抽象(请参见 PSR-7:HTTP消息接口).

路由器:在预定义的路由列表中将请求组件(HTTP方法和URI路径)与每个路由组件(HTTP方法和模式)进行匹配,并返回匹配的路由,如果找到了.

Router: matches the request components (HTTP method and URI path) against the components of each route (HTTP method and pattern) in a predefined list of routes and returns the matched route, if found.

前端控制器:将用户请求与路线进行匹配,并将其分配给特定的控制器和/或查看操作.

Front controller: matches the user request against a route and dispatches it to a certain controller and/or view action.

控制器.他们向模型层写入(例如执行创建,更新和删除操作),并且(应该)没有结果.这可以通过直接与域模型中定义的组件进行交互,或者最好仅与服务类进行交互来实现.

Controllers. They write (e.g. perform create, update and delete operations) to the model layer and (should) expect no results. This can happen by directly interacting with the components defined in the domain model, or, preferably, by only interacting with the service classes.

观看次数.它们应该是类,而不是模板文件.他们可以接收模板引擎作为依赖项.它们仅从模型层获取数据(例如执行读取操作).通过直接与域模型中定义的组件进行交互,或者最好仅通过与服务类进行交互.同样,他们决定向用户显示哪个结果(如字符串)或模板文件内容.视图操作应始终返回HTTP响应对象(可能由PSR-7规范定义),该对象的主体将使用上述结果或模板文件内容进行预先更新.

Views. They should be classes, not template files. They can receive a template engine as dependency. They only fetch data (e.g. perform read operations) from the model layer. Either by directly interacting with the components defined in the domain model, or, preferably, by only interacting with the service classes. Also, they decide which result (like a string), or template file content, will be displayed to the user. A view action should always return a HTTP response object (maybe as defined by the PSR-7 specification), whose body will be before-hand updated with the mentioned result or template file content.

模板文件.应保持尽可能简单.整个表示逻辑应仅在视图实例中发生.因此,模板文件应仅包含变量(它们是纯PHP的变量,或使用使用的模板引擎语法呈现的变量),并且可能仅包含一些简单的条件语句或循环.

Template files. Should be kept as simple as possible. The whole presentation logic should happen only in the view instances. So, the template files should contain only variables (be they pure PHP ones, or presented with the used template engine syntax) and, maybe, some simple conditional statements, or loops.

响应发射器:读取视图返回的HTTP响应实例的正文并进行打印.

Response emitter: reads the body of the HTTP response instance returned by the view and prints it.

4) 其他组件.如所希望的.例如,一些由您自己开发的库.类似于 PSR-7 抽象的实现.

4) Other components. As wished. For example some libraries developed by your own. Like an implementation of the PSR-7 abstraction.

我如何选择发送用户请求:

如您在上图中所看到的,前端控制器不仅将用户请求分派给控制器操作(以更新域模型),而且还分派给视图操作(以读取和显示更新的状态) /来自模型层的数据).一种分派的调度.通过将控制器动作和视图动作分配给每个路线(例如波纹管),并告诉前端控制器依次调用它们,可以相对容易地实现:

As you see in the diagrams above, the front controller dispatches the user request not only to a controller action (in order to update the domain model), but also to a view action (in order to read and display the updated state/data from the model layer). Kind of a splitted dispatch. This can be relatively easy achieved by assigning the controller action and the view action to each route (like bellow), and telling the front controller to call them successively:

<?php

use MyApp\UI\Web\Application\View;
use MyApp\UI\Web\Application\Controller;

// Note: $this specifies a RouteCollection to which the route is added. 
$this->post('/upload', [
    'controller' => [Controller\Upload::class, 'uploadFiles'],
    'view' => [View\Upload::class, 'uploadFiles'],
]);

此方法为用户请求分发提供了灵活性.例如,视图动作的名称可以与控制器动作的名称不同.或者,为了仅获取模型层数据,您无需将用户请求分派给控制器,而仅分派给视图.因此,您根本不需要在路由中分配控制器操作:

This approach gives flexibility in regard to the user request dispatch. For example, the name of the view action can be different from the name of the controller action. Or, in order to only fetch model layer data, you don't need to dispatch the user request to a controller, but only to a view. Therefore you don't need to assign a controller action in the route at all:

<?php

use MyApp\UI\Web\Application\View;

$this->get('/upload', [View\Upload::class, 'listFiles']);


文件系统结构示例:

myapp/domain :包含域模型类和服务的文件夹.该目录可以带到"myapp/web/src"文件夹中,但不可以,因为模型层和服务层不是传递机制的一部分.

myapp/domain: folder containing the domain model classes and the services. This directory could be brought into the "myapp/web/src" folder, but it shouldn't, because the model layer and the service layer are not part of the delivery mechanism.

myapp/web :包含传递机制类的文件夹.它的名称描述了应用程序的类型-可以是Web应用程序,CLI应用程序等.

myapp/web: folder containing the delivery mechanism classes. Its name depicts the type of application - can be a web app, a cli app, etc.

myapp/web/src :

myapp/web/src:

资源:

*) Sandro Mancuso:交互驱动设计简介

*)在 *)亚历杭德罗·格瓦西奥(Alejandro Gervasio)展示的教程:

*) The tutorials presented by Alejandro Gervasio:

  • Building a Domain Model – An Introduction to Persistence Agnosticism
  • Building a Domain Model – Integrating Data Mappers
  • Handling Collections of Aggregate Roots – the Repository Pattern
  • An Introduction to Services

*)Slim 3页面上的示例:操作Slim的-Domain-Responder .

*) The example on the Slim 3 page: Action-Domain-Responder with Slim.

这篇关于PHP-使用MVC构建Slim3 Web应用程序并了解模型的作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆