rvd*_*vid 6 php oop zend-framework symfony doctrine-orm
请提供以下创建模型层的方法的反馈,该模型层由业务规则组成,这些规则使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.');
// ...
}
Run Code Online (Sandbox Code Playgroud)
该模型
如果我将它放入模型中,它将如下所示:
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.
}
}
Run Code Online (Sandbox Code Playgroud)
修订控制器
我的修订控制器现在看起来像这样:
// 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);
// ...
}
Run Code Online (Sandbox Code Playgroud)
另外两个要求也将在模型上实施.
简而言之,我的方法
简而言之,这是我目前的做法:
我的问题
先感谢您.
首先,您可以使用ParamConverter来简化您的控制器。如果未找到实体,则会自动引发异常。
我会调用消息方法archive,restore但这是一个偏好问题。
JMSSecurityExtraBundle提供了一种集成安全检查的好方法。使用ACL检查是否允许当前用户存档邮件。
您的模式MessageModel类似于您可以在 FOSUserBundle 中找到的Manager(接口/抽象和即学说 ODM/ORM实现)模式。
这些管理器正在存储层和控制器之间构建桥梁,并且都共享相同的接口(即UserManagerInterface)。这样你就可以轻松地与 propel 交换 ie 主义。
通过将控制器转变为服务并注入服务来改进,Manager而不是注入整个容器(即通过扩展Symfony\Bundle\FrameworkBundle\Controller\Controller)并从那里获取它。
您应该仅将所需的依赖项注入到服务中以简化测试。在 orm/odm 的示例中,管理器服务将检索类名参数、实体/文档管理器服务和存储库服务。(服务定义)
您可以在 Benjamin Eberlei 的博客文章“扩展 Symfony2:控制器实用程序”中找到创建控制器实用程序服务的更多灵感。
另一种常用的技术是为最常用的控制器依赖项创建抽象父服务。(例子)
我的最后一个快速提示是将 flashmessages 的创建移至事件侦听器/订阅者。只需检查该方法是否返回一个类型Message或更好类型的对象MessageInterface,然后添加 success-flash 消息。如果未找到实体,您可以捕获异常并添加带有错误消息的闪现消息。
当使用像FOSRestBundle这样的视图响应侦听器时,您甚至可以return在方法末尾省略,如果该方法不返回任何内容,该侦听器会自动假定您正在返回原始参数 - 请在此处阅读相关内容。
最后,您可以在一个可很好测试的控制器服务中得到一个方法,如下所示:
/**
* @PreAuthorize("hasPermission(#message, 'ARCHIVE')")
*/
public function archiveAction(Message $message)
{
$message->archive();
$this->messageManager->update($message, true);
}
Run Code Online (Sandbox Code Playgroud)
该manager->update()方法的行为类似于我的示例中FOS\UserBundle\Doctrine\UserManager中找到的方法。