我正在开展个人HMVC项目:
static
或global
),没有单身人士。所有需要的依赖项都被注入抽象控制器的构造函数中。如果我想覆盖这个构造函数,那么我也必须在子控制器的构造函数中传递所有这些依赖项。
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...
}
}
依赖项列表会进一步增长。这需要改变。所以我在考虑:
Response
将是视图的依赖。Request
,Session
,Logger
等相似; Request
,Session
,Logger
等相似; AbstractController
中。我正在努力寻找一种优雅的方式来处理这项任务,我会感激任何建议。谢谢。
答案 0 :(得分:4)
我会回答我自己的问题。当我编写它时,我已经很好地概述了许多有经验的开发人员推荐的关于MVC和MVC结构中依赖注入的内容。
因此,无论我做什么,这些解决方案似乎都不适合我的HMVC项目的结构。所以,我进一步挖掘,直到我意识到缺少的链接是什么。为此,我完全赞赏以下伟大文章的创建者 Tom Butler :
他的作品基于对MVC概念的深入,充分论证的分析。它们不仅非常容易理解,而且还通过不言自明的例子来维持。总之:对MVC和开发者社区的贡献很棒。
我要进一步写下的内容只是用他自己的话语来表达他的原则,以某种方式完成它们,提供一个更紧凑的视角,并展示我所遵循的步骤。我在我的项目中实现了它们。所有关于这里描述的主题,想法,原则和工作流程的信用都归 Tom Butler 所有。
那么,我的HMVC项目缺少什么链接?它被命名为分离关注。
为简单起见,我将尝试通过仅将一个控制器,一个控制器操作,一个视图,一个模型(域对象)和一个模板(文件)引用自己来解释这一点,并将它们引入{{1} } context。
最常在网络上描述的MVC概念 - 也是我研究过的一些流行框架实现的 - 主要围绕让控制器控制视图和模型的原则。为了在屏幕上显示某些内容,您必须告诉控制器 - 他还会通知视图加载和呈现模板。如果此显示过程也意味着使用某些模型数据,那么控制器也会操纵模型。
以传统方式,控制器创建和动作调用过程包括两个步骤:
代码:
User
这意味着,控制器负责一切。因此,难怪为什么必须为控制器注入如此多的依赖项。
但是,控制器是否应该参与或负责在屏幕上有效地显示任何类型的信息?不,这应该是观点的责任。为了实现这一目标,我们开始将视图与控制器分开 - 从不需要任何模型的前提出发。涉及的步骤是:
$controller = new UserController(/* Controller dependencies */);
$controller->{action}(/* Action dependencies */);
方法,用于在屏幕上显示信息
图。output
方法:代码:
output
通过完成五个上面的步骤,我们设法将控制器与视图完全分离。
但是,有一个方面,我们先前假设:我们没有使用任何模型。那么控制器在这个星座中的作用是什么呢?答案是:没有。控制器应仅作为某个存储位置(数据库,文件系统等)与视图之间的中间存在。否则,例如,只有在屏幕上以某种格式输出一些信息,视图的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();
方法才足够。
如果一个模型出现在现场,情况会发生变化。但是应该在哪里注射?在控制器或视图中?同时。它们共享相同的模型实例。在这一刻,控制器凭借自己的权利获得了中间人 - 存储和视图之间的角色。理论形式是:
output
这样,控制器可以更改模型的状态并确保它已保存在存储系统中。视图读取并显示相同的模型实例及其状态。控制器通过模型将显示逻辑信息传递给视图。问题是,这些信息不属于业务逻辑,模型应该是唯一拥有的。它们只是显示逻辑参与者。
为了避免向模型赋予显示逻辑职责,我们必须在图片中引入一个新组件:视图模型。控制器和视图将共享视图模型实例,而不是共享模型对象。只有这一个将接收模型作为依赖。实施:
$model = new UserModel;
$controller = new UserController($model, /* Other controller dependencies */);
$view = new UserView($model, /* Other view dependencies */);
$controller->{action}(/* Action dependencies */);
echo $view->output();
工作流程可以这样描述:
$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();
方法中,视图会从中读取值
视图模型并请求模型查询其存储
基础。视图模型不属于域模型,所有域对象都驻留在该域模型中,并且发生了真正的业务逻辑。它也不属于服务层,它操纵域对象,存储库和数据映射器。它属于应用程序模型,例如应用程序逻辑发生的位置。视图模型自行负责从控制器获取显示逻辑状态并将其传送到控制器。
可以看出,只有视图模型&#34;触及&#34;该模型。控制器和视图两者不仅完全脱离,而且还与模型完全分离。这种方法最重要的方面是,所涉及的所有组件中的每一个都只能获得它应该获得的责任。
通过利用这种组件分离和依赖注入容器,控制器中依赖性太多的问题消失了。并且可以以非常灵活的方式应用我的问题中提出的所有选项的组合。没有考虑到其中一个组件(模型,视图或控制器)获得了太多的责任。