请提供以下创建模型层的方法的反馈,该模型层由业务规则组成,这些业务规则使Doctrine用于数据访问。
我目前的方法是基于这样一种观念,即Model是一个ContainerAware类/对象,其中包含所有非库,业务特定的域逻辑。
我发现我必须锤击框架以这种方式做事,这就是为什么我的大脑部分质疑我的方法。
我目前正在使用Symfony 2,它与所有现代PHP MVC框架一样,使用像Doctrine 2这样的ORM层,并且不可避免地将其视为模型层。我猜测情况与ZF2类似,所以尽管我的例子是用SF2编写的,但我认为这是一个与框架无关的问题。
具体示例
作为一个具体示例,请考虑以下情形:
消息要求
控制器
在Symfony2中,这些要求在控制器层中编码为Actions。 我将跳过检查消息是否实际属于用户的无关代码,但显然,这也应该是域逻辑的一部分。在方法“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.
}
}
修订控制器
我的修订控制器现在看起来像这样:
// 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);
// ...
}
另外两个要求也将在模型上实施。
我的方法简而言之
简而言之,这是我目前的做法:
我的问题
提前谢谢。
答案 0 :(得分:3)
首先,您可以使用ParamConverter来简化控制器。如果找不到实体,则会自动抛出异常。
我会调用消息方法archive
和restore
,但这是一个偏好问题。
JMSSecurityExtraBundle提供了一种集成安全检查的好方法。使用ACLs检查当前用户是否可以存档邮件。
您的MessageModel
类似于您在FOSUserBundle中可以找到的Manager
(interface/abstract和教义ODM / ORM implementation)模式。
这些经理正在构建存储层和控制器之间的桥梁,并且所有管理器共享相同的接口(即UserManagerInterface
)。通过这种方式,您可以轻松地与推动交换,即学说。
通过将您的控制器变为服务并注入Manager
服务而不是注入整个容器(即通过扩展Symfony\Bundle\FrameworkBundle\Controller\Controller
)并从那里获取来改进。
您应该只将所需的依赖项注入服务以简化测试。 在doctrine orm / odm的示例中,管理器服务将检索classname参数,entity- / documentmanager服务和存储库服务。 (service definition)
您可以在Benjamin Eberlei的博客文章“Extending Symfony2: Controller Utilities”中找到创建控制器实用程序服务的其他灵感。
另一种常用的技术是为最常用的控制器依赖项创建一个抽象parent service。 (example)
我的最后一个快速提示是将flashmessages的创建移至event listeners/subscribers。只需检查该方法是否返回Message
或更好MessageInterface
类型的对象,并添加success-flash消息。如果找不到实体,则可以捕获异常并添加flashmessage并显示错误消息。
当使用像FOSRestBundle's这样的视图响应侦听器时,你甚至可以在方法结束时省略return
,如果方法没有返回任何内容,它会自动假设你返回原始参数 - 阅读它here
最后,您可以在一个可测试的控制器服务中找到一个方法,如下所示:
/**
* @PreAuthorize("hasPermission(#message, 'ARCHIVE')")
*/
public function archiveAction(Message $message)
{
$message->archive();
$this->messageManager->update($message, true);
}
manager->update()
方法的行为类似于我的示例中FOS\UserBundle\Doctrine\UserManager中的方法。