我正在做一个个人 HMVC 项目:
I'm working on a personal HMVC project:
static
或 global
),没有单例.static
or global
), no singletons.所有需要的依赖都被注入到抽象控制器的构造函数中.如果我想覆盖这个构造函数,那么我也必须在子控制器的构造函数中传递所有这些依赖项.
All needed dependencies are injected in the constructor of the abstract controller. If I want to override this constructor, then I have to pass all these dependencies in the child controller's constructor too.
class UsersController extends AbstractController {
private $authentication;
public function __construct(
Config $config
, Request $request
, Session $session
, View $view
, Response $response
, Logger $logger
, Authentication $authentication // Domain model service
) {
parent::__construct(/* All dependencies except authentication service */);
$this->authentication = $authentication;
}
// Id passed by routing.
public function authenticateUser($id) {
// Use the authentication service...
}
}
依赖项列表将进一步增长.这需要改变.所以我在想:
The dependencies list would further grow. This needs to change. So I was thinking about:
Response
将成为视图的依赖项.Request
、Session
、Logger
等;Request
、Session
、记录器
等;AbstractController
中.Response
would be a dependency of the views.Request
, Session
, Logger
, etc;Request
, Session
, Logger
, etc;AbstractController
anymore.我正在努力寻找一种优雅的方式来处理这项任务,我将不胜感激任何建议.谢谢.
I'm trying to find an elegant way to deal with this task and I'll appreciate any suggestions. Thank you.
我会回答我自己的问题.当我编写它时,我已经很好地概述了许多有经验的开发人员关于 MVC 和 MVC 结构中的依赖注入的建议.
I'll answer to my own question. When I wrote it I already had a good overview of what many experienced developers recommend regarding dependency injection in MVC and MVC structure.
因此,无论我做了什么,这些解决方案似乎都不适合我的 HMVC 项目的结构.所以,我进一步挖掘,直到我意识到缺失的环节是什么.为此,我非常感谢 Tom Butler,他撰写了以下精彩文章:
So, none of this solutions seemed to entirely fit into the structure of my HMVC project, whatever I did. So, i dug further, until I realised what the missing link was. For this I give my whole appreciation to Tom Butler, the creator of the following great articles:
他的作品基于对 MVC 概念的深入、充分论证的分析.它们不仅很容易理解,而且还可以通过不言自明的例子来支持.一句话:对 MVC 和开发者社区的精彩贡献.
His works are based on an in-depth, well argumented analyse of the MVC concepts. They are not only very easy to follow, but also sustained by self-explanatory examples. In a word: a wonderful contribution to the MVC and developer community.
我接下来要写的只是用我自己的话来介绍他的原则,考虑以某种方式完成它们,提供更简洁的视角,并展示我在实施时遵循的步骤他们在我的项目中.此处描述的主题、想法和原则以及工作流程的所有功劳均归功于 Tom Butler.
What I'll write further is meant to be just a presentation of his principles with my own words, having in mind to somehow complete them, to provide a compacter perspective of them and to show the steps followed by me when I implemented them in my project. All credit on the subject, ideas and principles and workflows depicted here goes to Tom Butler.
那么,我的 HMVC 项目中缺少的链接是什么?它被命名为关注分离.
So, what was the missing link in my HMVC project? It's named SEPARATION OF CONCERNS.
为简单起见,我将尝试通过仅将自己称为一个控制器、一个控制器操作、一个视图、一个模型(域对象)和一个模板(文件)来解释这一点,并将它们引入 User代码>上下文.
For simplicity I'll try to explain this by referring myself to only one controller, one controller action, one view, one model (domain object) and one template (file), introducing them into a User
context.
Web 上描述最多的 MVC 概念 - 也由我研究过的一些流行框架实现 - 以让控制器控制视图和模型的原则为中心.为了在屏幕上显示一些东西,你必须告诉控制器——他进一步通知视图加载和渲染模板.如果这个显示过程也暗示使用一些模型数据,那么控制器也会操纵模型.
The MVC concept most described on web - and also implemented by some popular frameworks that I studied - is centered around the principle of giving the controller the control over the view and the model. In order to display something on screen you'll have to tell that to the controller - he further notifies the view to load and render the template. If this display process implies the use of some model data too, then the controller manipulates the model too.
以经典的方式,控制器创建和动作调用过程包括两个步骤:
In a classical way, the controller creation and action calling process involve two steps:
代码:
$controller = new UserController(/* Controller dependencies */);
$controller->{action}(/* Action dependencies */);
这意味着,控制器负责一切.所以,难怪一个控制器必须注入这么多的依赖.
This implies, that the controller is responsible for, well, everything. So, no wonder why a controller must be injected with so many dependencies.
但是控制器是否应该参与或负责在屏幕上有效地显示任何类型的信息?不,这应该是视图的责任.为了实现这一点,让我们开始将视图与控制器分离——从不需要任何模型的前提出发.涉及的步骤是:
But should the controller be involved in, or responsible for effectively displaying any kind of information on the screen? No. This should be the responsibility of the view. In order to achieve this let's begin separating the view from the controller - going from the premise of not needing any model yet. The involved steps would be:
output
方法,用于在屏幕上显示信息查看.output
方法:output
method for displaying information on screen in the
view.output
method of the view:和代码:
class UserView {
//....
// Display information on screen.
public function output () {
return $this
->load('<template-name>')
->render(array(<data-to-display>))
;
}
//....
}
$controller = new UserController(/* (less) controller dependencies */);
$view = new UserView(/* View dependencies */);
$controller->{action}(/* Action dependencies */);
echo $view->output();
通过完成上面的五个步骤,我们设法将控制器与视图完全分离.
By accomplishing the five upper steps we managed to completely decouple the controller from the view.
但是,有一个方面,我们之前假设过:我们没有使用任何模型.那么控制器在这个星座中的作用是什么?答案是:没有.控制器应该仅作为某个存储位置(数据库、文件系统等)和视图之间的中间人存在.否则,例如只是在屏幕上输出某种格式的一些信息,视图的output
方法就完全足够了.
But, there is an aspect, that we hypothesised earlier: we did not made use of any model. So what's the role of the controller in this constellation then? The answer is: none. The controller should exist only as a middlemann between some storage place (database, file system, etc) and the view. Otherwise, e.g. only to output some information in a certain format on screen, is the output
method of the view fully sufficient.
如果模特被带到现场,事情就会改变.但是应该在哪里注射呢?在控制器中还是在视图中?同时.它们共享相同的模型实例.在这一刻,控制器凭借自己的权利获得了中间人的角色——介于存储和视图之间.一种理论形式是:
Things change if a model is beeing brought on the scene. But where should it be injected? In the controller or in the view? In both. They share the same model instance. In this moment the controller gains the role of the middleman - between storage and view - in his own right. A theoretical form would be:
$model = new UserModel;
$controller = new UserController($model, /* Other controller dependencies */);
$view = new UserView($model, /* Other view dependencies */);
$controller->{action}(/* Action dependencies */);
echo $view->output();
这样做,控制器可以改变模型的状态并确保它保存在存储系统中.视图读取并显示相同的模型实例及其状态.控制器通过模型将显示逻辑信息传递给视图.问题是,这些信息不属于模型应该专有的业务逻辑.他们只是显示逻辑参与者.
Doing so, the controller can change the state of the model and ensure that it's saved in the storage system. The same model instance, respective its state, is read by the view and displayed. The controller passes display logic informations to the view through the model. The problem is, that these informations don't belong to the business logic, which a model is supposed to exclusively have. They are only display logic participants.
为了避免将显示逻辑责任交给模型,我们不得不在图中引入一个新组件:view-model.控制器和视图将共享一个视图模型实例,而不是共享模型对象.只有这个将接收模型作为依赖项.一个实现:
In order to avoid giving display logic responsibilities to the model, we have to introduce a new component in the picture: the view-model. Instead of sharing a model object, the controller and the view will share a view-model instance. Only this one will receive the model as dependency. An implementation:
$model = new UserModel;
$viewModel = new UserViewModel($model, /* Other view-model dependencies */);
$controller = new UserController($viewModel /* Other controller dependencies */);
$view = new UserView($viewModel, /* Other view dependencies */);
$controller->{action}(/* Action dependencies */);
echo $view->output();
工作流程可以这样描述:
And the workflow can be described like this:
output
方法中,视图从视图模型并请求模型查询其上的存储output
method, the view reads the values from the
view-model and requests the model to query the storage on their
basis.视图模型不属于域模型,所有域对象都驻留在域模型中,真正的业务逻辑发生在域模型中.它也不属于操作域对象、存储库和数据映射器的服务层.它属于应用程序模型,例如应用程序逻辑发生的地方.视图模型获得从控制器获取显示逻辑状态并将其传递给控制器的唯一责任.
The view-model does not belong to the domain model, where all domain objects reside and the real business logic takes place. It does also not belong to the service layer, which manipulates the domain objects, repositories and data mappers. It belongs to the application model, e.g to the place where the application logic takes place. The view-model obtains the sole responsibility of gaining display logic state from the controller and conducting it to the controller.
可以看出,只有视图模型接触"了模型.控制器和视图不仅彼此完全解耦,而且还与模型完全解耦.这种方法最重要的方面是,所涉及的所有组件中的每一个都只获得它应该获得的职责.
As can be seen, only the view-model "touches" the model. Both, the controller and the view were not only completely decoupled from each other, but also from the model. The most important aspect of this approach is, that each of all the components involved gains only the responsibilities, that it is supposed to gain.
通过使用这种组件分离和依赖注入容器,控制器依赖过多的问题消失了.并且可以以一种非常灵活的方式应用我的问题中提出的所有选项的组合.无需考虑其中一个组件(模型、视图或控制器)承担了太多责任.
By making use of this kind of component separation and of a dependency injection container, the problem of too many dependencies in controllers vanishes. And one can apply a combination of all options presented in my question in a really flexible manner. Without having in mind that one of the components (model, view or controller) gains too many responsibilities.
这篇关于PHP MVC:控制器中的依赖项太多?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!