背景
这是一个漫长而复杂的问题。我使用 php/MySQL 作为示例(因为它是一个实际示例),但这在理论上可以适用于其他语言。忍受我。
没有 ORM 的 MVC 框架
我正在创建一个使用自己的框架的应用程序。由于各种原因,不接受以下对此问题的回答:
- 为什么不直接使用
X
框架? - 为什么不直接使用
X
ORM?
该应用程序执行自己的查询(由我编写),这就是我现在希望它保留的方式。
商业逻辑?
“业务逻辑”似乎是一个毫无意义的流行词,但我认为它本质上是指
查询和基于这些查询构建结果集的逻辑
我还读到 MVC 中的模型应该执行所有业务逻辑。
User.php
是 884 行
现在我已经让我的应用程序运行得很好,我想对其进行重构,以免出现这种可憎的情况。 User.php
本质上是用户的模型(显然)。我在其中看到了一些我可以轻松摆脱的责任,但我遇到的一个主要障碍是:
如何协调 SOLID 与 MVC?
User.php
增长如此之大的原因是因为我执行的任何查询都需要该文件中的用户成员。用户用于大量操作(它需要做的不仅仅是CRUD
),因此任何需要userid
,username
等的查询都由该文件中的函数运行。显然查询应该在模型中(而不是控制器),但我觉得这绝对应该以某种方式分开。我需要完成以下工作:
- 创建一个 API,以划分的方式涵盖所有这些必要的查询
- 避免在不必要时授予对 DB 连接类的访问权限
User
向视图添加数据(User.php
现在正在这样做——视图对象是由 setter 注入的,我认为这也很糟糕)。
...所以我可以做的是创建其他对象,例如UserBranchManager
, UserSiteManager
,UserTagManager
等,每个对象都可以有相关的查询和注入的 DB 对象来运行这些查询,但是他们如何获得User::$userid
他们需要运行的令人垂涎的东西这些查询?不仅如此,我怎么能通过Branch::$branchid
?这些成员不应该是私人的吗?为它们添加吸气剂也使这毫无意义。
我也不确定在哪里画出一个物体应该做多少的界限。许多操作相似但仍然不同。每个人的一个类将是巨大的矫枉过正。
可能的答案
如果我无法获得任何帮助,我会做(或至少尝试做)是为上面的对象(例如)构建某种依赖注入容器UserBranchManager
并将它们注入相关控制器。这些将有一个DB
和Query
对象。该Query
对象可以传递给低级模型(如User
)以根据需要绑定参数,而更高级别的模型或它们所调用的任何东西都会将结果返回给控制器,控制器也会根据需要将数据添加到模板中。我看到的一些可能的障碍是创建适当的合约(例如,UserController
最好依赖于用户模型的一些抽象),但不可避免地需要一些细节,尤其是在视图方面。
谁能提供一些智慧来回答我漫无边际的问题?
回复@tereško
他不仅在这里提供了一个很好的答案,而且还在How should a model be structure in MVC?
代码
根据要求,这里有一些极其精简的代码(基本上服务于一个请求)。一些重要的注意事项:
- 现在,控制器不是类,只是文件
- 控制器还处理很多路由
- 没有“视图”对象,只有模板
- 这可能看起来很糟糕
这些也是需要改进的地方,但我最担心的是模型(User
特别是因为它失控了):
#usr.php -- controller
$route = route();
$user = '';
$branch = '<TRUNK>';
if (count($route) > 0) {
if (count($route) > 1) {
list($user, $branch) = $route;
}
else {
list($user) = $route;
}
}
$dec = new Decorator('user');
$dec->css('user');
if (isset($_SESSION['user']) && $_SESSION['user']->is($user)) {
$usr = $_SESSION['user'];
}
else {
$usr = new User(new DB, $user);
}
$usr->setUpTemplate($dec, $branch);
return $dec->execute();
# User.php -- model
class User {
private $userid;
private $username;
private $db;
public function __construct(DB $db, $username = null) {
$this->username = $username;
$this->db = $DB;
}
public function is($user) {
return strtolower($this->username) === strtolower($user);
}
public function setUpTemplate(Decorator $dec, $branch) {
$dec->_title = "$this->username $branch";
// This function runs a moderately complicated query
// joining the branch name and this user id/name
$dec->branch = $this->getBranchDisplay($branch);
}
}
关于答案的问题
在这里回答:
- 您谈论离开缓存/身份验证/授权。这些重要吗?为什么他们不被覆盖?它们与模型/控制器/路由器有什么关系?
- 该
Data Mapper
示例具有具有诸如..之Person
类的方法的类getExemption
,这些是什么?isFlaggedForAudit
这些计算似乎需要数据库数据,那么它是如何获得它们的呢?Person Mapper
离开select
。这不重要吗? - 什么是“领域逻辑”?
回答 5863870(特别是代码示例):
- 这些工厂对象不应该是抽象(而不是依赖于通过创建
new
)还是这些是特殊的? - 您的 API 如何包含必要的定义文件?
- 我已经阅读了很多关于在构造函数中注入依赖项的最佳方式(如果它们是强制性的)。我假设您以这种方式设置工厂,但为什么不是对象/映射器/服务本身呢?抽象呢?
- 您是否担心代码重复(例如,大多数模型
_object_factory
在其类定义中需要一个成员)?如果是这样,你怎么能避免这种情况? - 你正在使用
protected
. 为什么?
如果您可以提供任何特定的代码示例,那将是最好的,因为这样我更容易拿起东西。
我理解你的回答所说的理论,这很有帮助。我仍然感兴趣(但不完全确定)的是确保以最佳方式处理此 API 中对象的依赖关系(最糟糕的方式new
无处不在)。