请就以下创建模型层的方法提供反馈,该模型层由使用 Doctrine 进行数据访问的业务规则组成。
我目前的方法基于模型是一个 ContainerAware 类/对象的概念,所有非库、特定于业务的域逻辑都在其中。
我发现我必须锤炼框架才能以这种方式做事,这就是为什么我的一部分大脑质疑我的方法。
我目前正在使用 Symfony 2,它与所有现代 PHP MVC 框架一样,使用像 Doctrine 2 这样的 ORM 层,并且不可避免地将其视为模型层。我猜 ZF2 的情况会类似,所以尽管我的示例是用 SF2 编写的,但请将其视为与框架无关的问题。
具体示例
作为具体示例,请考虑以下场景:
消息要求
- 作为用户,我可以创建属于我的消息。
- 作为用户,我可以更新属于我的消息。
- 作为用户,我可以存档属于我的消息。
Controller
在 Symfony2 中,这些要求被编码为 Controller 层中的操作。 我将跳过检查消息是否真正属于用户的无关代码,但显然,这也应该是域逻辑的一部分。在方法“belongsToUser”或类似方法中。
// Vendor\MessageBundle\DefaultController
public function archiveAction(Request $request) {
// ...
$em = $this->getDoctrine()
->getManager();
$message = $em->getRepository('MessageBundle:Message');
->getManager()
->getRepository('MessageBundle:Message')
->find($request->get('id'));
$message->setIsArchived(true);
$em->persist($entity);
$em->flush();
$this->flashMessage('Message has been archived.');
// ...
}
模型
如果我要将其放入模型中,它将如下所示:
class MessageModel
{
public function archive($messageId) {
// ...
$em = $this->getDoctrine()
->getManager();
$message = $em->getRepository('MessageBundle:Message')
->find($messageId);
$message->setIsArchived(true);
$em->persist($entity);
$em->flush();
// ... return true on success, false on fail.
}
}
修订后的 Controller
我修改后的 Controller 现在看起来像这样:
// Vendor\MessageBundle\DefaultController
public function archiveAction(Request $request) {
// ...
$model = new MessageModel(); // or a factory.
$result = $model->archive($request->get('id'));
if($result) {
$this->flashMessage('Message has been archived.');
} else {
$this->flashMessage('Message could not be archived due to a system error.');
}
return array('result'=>$result);
// ...
}
其他两个要求也将在模型上实现。
我的方法简而言之
简而言之,这是我目前的做法:
- Controller - 逻辑较少
- View - 保持不变
- 模型 - 容器感知并容纳所有业务逻辑,访问 Doctrine 作为数据访问。
- ORM - 被视为模型层的一部分,但不被视为模型层。
- 服务层 - 需要时,我可以使用服务层来处理多个层,但我发现由于简单,我只在少数情况下不得不使用它我必须构建的应用程序的性质。
我的问题
- 我的方法与其他人的做法一致吗?
- 我是否漏掉了一些非常明显的东西?
- 您是否尝试过类似的东西并发现它好/坏?
提前谢谢你。
最佳答案
首先你可以使用ParamConverter来简化你的 Controller 。如果找不到实体,则会自动抛出异常。
我会调用消息方法 archive
和 restore
但这是一个偏好问题。
JMSSecurityExtraBundle 提供了一种集成安全检查的好方法.使用 ACLs检查是否允许您当前的用户存档消息。
您的 MessageModel
类似于您可以在 FOSUserBundle 中找到的 Manager
(interface/abstract 和即学说 ODM/ORM implementation)模式。
这些管理器正在存储层和 Controller 之间架起桥梁,并且都共享相同的接口(interface)(即 UserManagerInterface
)。这样你就可以轻松地交换学说和推进。
通过将你的 Controller 变成一个服务并注入(inject)你的 Manager
服务而不是注入(inject)整个容器来改进(即通过扩展 Symfony\Bundle\FrameworkBundle\Controller\Controller
)并从那里获取它。
您应该只将需要的依赖项注入(inject)到您的服务中以简化测试。 在 doctrine orm/odm 的示例中,管理器服务将检索类名参数、实体/文档管理器服务和存储库服务。 ( service definition )
您可以在 Benjamin Eberlei 的博客文章“Extending Symfony2: Controller Utilities”中找到创建 Controller 实用程序服务的更多灵感。
另一种常用的技术是创建一个摘要 parent service对于您最常用的 Controller 依赖项。 ( example )
我的最后一个快速提示是将 flashmessages 的创建移动到 event listeners/subscribers .只需检查该方法是否返回类型为 Message
或更好的 MessageInterface
的对象,并添加成功提示。如果未找到该实体,您可以捕获异常并添加带有错误消息的闪现消息。
当使用像 FOSRestBundle's 这样的 View 响应监听器时,您甚至可以在方法末尾省略 return
如果该方法不返回任何内容,它会自动假定您返回原始参数 - 阅读它 here .
最后,您可以在一个可很好测试的 Controller 服务中得到一个方法,如下所示:
/**
* @PreAuthorize("hasPermission(#message, 'ARCHIVE')")
*/
public function archiveAction(Message $message)
{
$message->archive();
$this->messageManager->update($message, true);
}
manager->update()
方法的行为类似于 FOS\UserBundle\Doctrine\UserManager 中的方法。在我的示例中。
关于php - 对我的模型层方法的反馈 Symfony 2 模型层 + Doctrine,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19847141/