反馈我的模型层方法Symfony 2模型层+学说

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)

另外两个要求也将在模型上实施.

简而言之,我的方法

简而言之,这是我目前的做法:

  • 控制器 - 减少逻辑
  • 视图 - 保持不变
  • 模型 - 容器识别并容纳所有业务逻辑,访问Doctrine作为数据访问.
  • ORM - 被视为模型层的一部分,但不被视为模型层.
  • 服务层 - 如果需要,我可以使用服务层来处理多个层,但是我发现由于我必须构建的应用程序的简单性,我只需要在几个场合使用它.

我的问题

  • 我的方法是否符合其他人的做法?
  • 我错过了一些明显的东西吗?
  • 你有没有尝试过类似的东西,发现它好/坏?

先感谢您.

Nic*_*ich 4

首先,您可以使用ParamConverter来简化您的控制器。如果未找到实体,则会自动引发异常。

我会调用消息方法archiverestore但这是一个偏好问题。

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中找到的方法。