問題描述
我正在嘗試使用 Slim3 框架和 Twig 模板系統在 php 中創建一個身份驗證系統,對于數據庫,我使用帶有 PDO 的 MySQL.我也在嘗試使用模型視圖控制器設計模式來實現它.但是,我很難理解如何將 MVC 結構用于 Web 應用程序.我在網上看了很多解釋,似乎沒有一個明確的答案.很多人說要使用 php 框架,例如 Laravel、Symfony 或 CodeIgniter,因為它們顯然采用了類似 MVC 的結構.但是,我更愿意保持簡單并手動編寫代碼,而不是使用框架.
目前我看到的 MVC 有兩種解釋.此圖中描繪的第一個:
我看到的另一種解釋是:(取自
我已經完成了我的研究.問題和答案,例如
通常,您可以將 Web MVC 應用程序視為由以下部分組成:
- 領域模型(例如模型,例如模型層);
- 服務層(可選);
- 交付機制;
- 其他組件(例如自己的庫等).
1)領域模型應包含以下組件:
- 實體(例如域對象)和值對象.它們根據屬性和行為對業務規則進行建模,并且獨立于應用程序,可供多種(類型的)應用程序使用.
myapp/domain:包含域模型類和服務的文件夾.這個目錄可以放到myapp/web/src"文件夾中,但不應該,因為模型層和服務層不是交付機制的一部分.
myapp/web:包含傳遞機制類的文件夾.它的名稱描述了應用程序的類型 - 可以是網絡應用程序、cli 應用程序等.
myapp/web/src:
資源:
*) Sandro Mancuso:交互驅動設計簡介
*) 我的舊答案.
*) Alejandro Gervasio 提供的教程:
- 構建領域模型——持久性不可知論簡介莉>
- 構建領域模型——集成數據映射器
- 處理聚合根的集合——存儲庫模式
- 服務簡介
*) Slim 3 頁面上的例子:Action-Slim 域響應器.
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.
Currently there are two interpretations of MVC that I see. The first one being depicted in this diagram:
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.
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.
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.
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'];
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');
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);
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')); }
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')); } }
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.
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.
解決方案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.
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).
In general, you could think of a web MVC application as composed of the following parts:
- Domain model (e.g. model, e.g. model layer);
- Service layer (optional);
- Delivery mechanism;
- Other components (like own libraries, etc).
1) The domain model should consist of the following components:
- 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) 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.
Note: By abstractions I mean interfaces and abstract classes.
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) 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.
Note: The service layer can, actually, be seen as part of the model layer. In my diagrams bellow I preferred to display it as a layer residing outside the model. But, in the file system example, I put the corresponding folder in the domain space.
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 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 message (e.g. HTTP request and HTTP response) abstraction (see PSR-7: HTTP message interfaces).
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.
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.
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.
Response emitter: reads the body of the HTTP response instance returned by the view and prints it.
4) Other components. As wished. For example some libraries developed by your own. Like an implementation of the PSR-7 abstraction.
How I chose to dispatch the user request:
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 MyAppUIWebApplicationView; use MyAppUIWebApplicationController; // Note: $this specifies a RouteCollection to which the route is added. $this->post('/upload', [ 'controller' => [ControllerUpload::class, 'uploadFiles'], 'view' => [ViewUpload::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 MyAppUIWebApplicationView; $this->get('/upload', [ViewUpload::class, 'listFiles']);
File system structure example:
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: 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:
Resources:
*) Sandro Mancuso : An introduction to interaction-driven design
*) The ones listed in an older answer of mine.
*) 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
*) The example on the Slim 3 page: Action-Domain-Responder with Slim.
這篇關于PHP - 使用 MVC 構建 Slim3 Web 應用程序并理解模型的作用的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!
【網站聲明】本站部分內容來源于互聯網,旨在幫助大家更快的解決問題,如果有圖片或者內容侵犯了您的權益,請聯系我們刪除處理,感謝您的支持!