symfony 2中的代码分离 - Controller与Service vs实体

时间:2015-08-06 07:13:27

标签: symfony model-view-controller

我正在使用symfony 2,我对代码分离有疑问。我想确保我正确理解控制器中应该包含哪些元素,服务中的内容以及实体中的内容。

让我们想象一下,我有需要显示的文档列表。在显示之前的每个文档上,我还必须执行一些逻辑操作(例如,添加两个变量)。

理解实体类只关注单个实体上的数据检索和操作。我不应该输入任何自定义代码。据我所知,这应该由一项服务来完成。

但我应该:

  • 使用服务根据某些文件传递给文档的控制器列表 执行所需逻辑后的标准,
  • 或使用控制器下载文件列表,然后通过 文件服务执行一些逻辑?

我宁愿认为第一种方法适合保持控制器薄(瘦控制器,大型模型),但这种方法是否正确? 实体中应包含哪些代码,控制器中的内容以及服务中的内容?

特别是我应该在哪里与实体经理联系 - 在控制器中还是在服务中?

我们还假装在我的应用中的许多地方,我需要在允许用户执行任何操作(例如编辑它)之前检查文档是否已完成。这绝对应该是在服务中,因为需要另一个服务来检查这个。但是,我是否应该将文档实体对象加载到控制器中,将其发送给服务以验证它是否可以最终确定,或者更确切地说是在服务中加载文档并执行检查?

3 个答案:

答案 0 :(得分:9)

我的Symfony 2架构(使用Doctrine ORM):

  1. 仅具有路由逻辑的精简控制器
  2. 每个实体的服务(a.k.a。"经理")(所有业务逻辑都在这里)
  3. 根据我的其他需求定制服务(即使用Amazon S3或Mandrill邮件系统等外部工具)
  4. 每个实体的存储库(只是从数据库中读取实体的方法)
  5. 控制器内的每个动作都会调用实体管理器中的一个或多个方法;我总是试图避免直接使用存储库的魔法 方法"支持自定义方法:在动作内部,而不是调用

    $this->getDoctrine()->getRepository(<entity>)->findBy(array('parent' => null));
    

    我在存储库中创建了这个方法:

    public function findParents()
    {
        return $this->findBy(array('parent' => null));
    }
    

    在我使用的动作中:

    $this->getDoctrine()->getRepository(<entity>)->findParents();
    

    当然,这是一个简单的示例,但对于更复杂的findByfindOneBy查询,它可以很好地运行。

答案 1 :(得分:7)

在Symfony2中,使用存储库和服务是超级简单的解耦逻辑。例如:

具有aditional自定义查找程序的实体存储库

use Doctrine\ORM\EntityRepository;

class MyEntityRepository extends EntityRepository
{
    public function findAllWithX($parameter)
    {
         // your DQL. manipule own data. filters.
    }
}

处理主要业务逻辑的胖服务

// could be a listener 
class MyFatService 
{
    public function __construct(MyEntityRepository $mer,
                                AnotherRepository $aor,
                                MisteriousService $mis)
    {
        $this->mer = $mer;
        $this->aor = $aor;
        $this->mis = $mis;
    }

    public function someBigAction($paramX, $paramY)
    {
         $foo = $this->mer->findAllWithX($paramX);
         $bar = $this->aor->findBy(....);

         // manipule data. complex operations. 
         // call related services. 
         // manipule data related to many repositories
    }
}

定义服务:

services:
    my_entity_repository:
        class: AppBundle\Repository\MyEntityRepository
        factory: [@doctrine, getRepository]
        arguments:
            - %entity.my_entity%

    my_another_repository:
        class: AppBundle\Repository\AnotherRepository
        factory: [@doctrine, getRepository]
        arguments:
            - %entity.my_another_entity% 

    my_fat_service:
        class: AppBundle\MyFatService
        arguments:
            - @my_entity_repository
            - @my_another_repository
            - @misterious_service

在您的控制器中:

public function blaAction($x, $y)
{
   // leave the heavy work to services. 
   // just handle request and send the response
   $data = $this->get('my_fat_service')
                ->someBigAction($x, $y);

   return $this->render('template.twig', ['data' => $data]);
}

ps:抱歉我的英文

答案 2 :(得分:1)

  

但我应该:

     

使用服务根据某些内容传递给控制器​​文档列表   执行所需逻辑后的标准,   或使用控制器   下载文档列表,并将文档传递给服务   执行一些逻辑?

相反,第二。只是因为它让您了解查看控制器代码的过程。所有Web框架的工作方式应该是将每个请求映射到控制器操作。按照这个逻辑期望你去控制器的请求。我建议你直接在控制器中调用服务来对数据做一些自定义逻辑。但是,如果您需要从数据库中检索数据,最好在您的存储库类中实现,而不是在服务中。

如果您需要在检索后重新组织数据,可能需要重新考虑数据库结构。在检索后无需任何操作即可检索所需内容,这样更容易,也更方便。

  

我宁愿认为第一种方法适合保留   控制器薄(瘦控制器,大型号)但这种方法   对?什么代码应该在实体中,什么在控制器中以及在什么中   服务?

为了保持控制器的精简,它不足以将查询和其他困难的逻辑放入其中:如排序,过滤等。

  

特别是我应该在哪里与实体经理联系 - 在a   控制器还是服务器?

这也很容易。你应该在那里与你联系。如果你的逻辑并不复杂并且在向用户显示结果之前你唯一需要做的就是做一些非常小的事情(比如将一些虚拟属性设置为检索到的实体),这绝对没有把这个逻辑放到控制器中。 但是,不要忘记重构代码的复杂性。如果它成为应该在不同地方应用的代码(可重复的代码) - 从控制器进入服务。