7

请就以下创建模型层的方法提供反馈,该模型层由利用 Doctrine 进行数据访问的业务规则组成。

我目前的方法是基于这样一个概念,即模型是一个 ContainerAware 类/对象,所有非库、特定于业务的域逻辑都在其中。

我发现我必须锤击框架才能以这种方式做事,这就是为什么我的一部分大脑会质疑我的方法。

我目前正在使用 Symfony 2,它与所有现代 PHP MVC 框架一样,使用像 Doctrine 2 这样的 ORM 层,并且不可避免地将其视为模型层。我猜 ZF2 的情况会很相似,所以虽然我的例子是用 SF2 写的,但把这看作是一个与框架无关的问题。

具体例子

作为一个具体的例子,考虑以下场景:

消息要求

  • 作为用户,我可以创建一个属于我的消息。
  • 作为用户,我可以更新属于我的消息。
  • 作为用户,我可以归档属于我的消息。

控制器

在 Symfony2 中,这些需求被编码为控制器层中的动作。 我将跳过检查消息是否确实属于用户的无关代码,但显然,这也应该是域逻辑的一部分。在“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);
    // ...
}

其他两个要求也将在模型上实现。

简而言之我的方法

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

  • 控制器- 更少的逻辑
  • 查看- 保持不变
  • 模型- 容器感知并容纳所有业务逻辑,访问 Doctrine 作为数据访问。
  • ORM - 被视为模型层的一部分,但不被视为模型层。
  • 服务层- 需要时,我可以使用服务层来处理多个层,但我发现由于我必须构建的应用程序的简单性质,我只需要在少数情况下使用它.

我的问题

  • 我的方法与其他人的做法一致吗?
  • 我错过了一些明显的东西吗?
  • 您是否尝试过类似的事情并发现它好/坏?

先感谢您。

4

1 回答 1

4

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

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

JMSSecurityExtraBundle提供了一种集成安全检查的好方法。使用ACL检查您的当前用户是否被允许归档消息。

MessageModel类似于您可以在 FOSUserBundle 中找到的Manager接口/抽象和即学说 ODM/ORM实现)模式。

这些管理器正在构建存储层和控制器之间的桥梁,并且都共享相同的接口(即UserManagerInterface)。这样您就可以轻松地用推进器交换即学说。

通过将您的控制器变成服务并注入您的Manager服务而不是注入整个容器(即通过扩展Symfony\Bundle\FrameworkBundle\Controller\Controller)并从那里获取它来改进。

您应该只将您需要的依赖项注入到您的服务中以简化测试。在 orm/odm 学说的示例中,管理器服务将检索类名参数、实体/文档管理器服务和存储库服务。(服务定义

您可以在 Benjamin Eberlei 的博客文章“扩展 Symfony2:控制器实用程序”中找到创建控制器实用程序服务的更多灵感。

另一种常用的技术是为您最常用的控制器依赖项创建一个抽象的父服务。示例

我的最后一个快速提示是将 flashmessages 的创建转移到事件侦听器/订阅者。只需检查该方法是否返回类型Message或更好的对象MessageInterface并添加成功闪烁消息。如果未找到实体,您可以捕获异常并添加带有错误消息的 flashmessage。

return当使用像FOSRestBundle 之类的视图响应侦听器时,您甚至可以省略方法末尾

最后,您可以在一个可很好测试的控制器服务中得到一个方法,如下所示:

/** 
 * @PreAuthorize("hasPermission(#message, 'ARCHIVE')")
 */
public function archiveAction(Message $message) 
{
    $message->archive();
    $this->messageManager->update($message, true);
}

在我的示例中,该manager->update()方法的行为类似于在FOS\UserBundle\Doctrine\UserManager中找到的方法。

于 2013-11-07T23:23:02.240 回答