【问题标题】:Code separation in symfony 2 - Controller vs Service vs entitysymfony 2 中的代码分离 - 控制器 vs 服务 vs 实体
【发布时间】:2017-04-30 13:46:31
【问题描述】:

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

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

正如理解实体类只关心数据检索和单个实体上的操作。我不应该在那里输入任何自定义代码。据我了解,这应该由服务完成。

但是我应该:

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

我宁愿认为第一种方法适合保持控制器精简(瘦控制器,大型模型),但这种方法是否正确? 实体中应该有什么代码,控制器中应该有什么,服务中应该有什么?

特别是我应该在哪里与实体管理器相关 - 在控制器中还是在服务中?

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

【问题讨论】:

  • Symfony2 的理念是:瘦控制器,胖服务。您与实体存储库类中的实体管理器相关。如果您想使用服务修改数据,请将存储库服务传递给它。
  • 所以我应该主要为每个实体创建服务(“实体存储库类”)来处理所有请求。我理解正确吗?

标签: symfony model-view-controller


【解决方案1】:

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

  1. 只有路由逻辑的瘦控制器
  2. 每个实体的服务(也称为“经理”)(所有业务逻辑都在这里)
  3. 满足我其他需求的定制服务(即,使用 Amazon S3 或 Mandrill 邮件系统等外部工具)
  4. 每个实体的存储库(只是从数据库中读取实体的方法)

控制器中的每个操作都会调用实体管理器的一个或多个方法;我总是尽量避免直接使用存储库的“魔法 方法”支持自定义方法:在动作内部,而不是调用

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

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

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

在我使用的动作中:

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

当然,这是一个简单的示例,但它非常适用于更复杂的 findByfindOneBy 查询。

【讨论】:

  • 你能详细解释一下 findParents()
  • 完成:我已经扩展了我的示例 :)
  • 感谢您的回复。
【解决方案2】:

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

带有额外自定义查找器的实体存储库

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:对不起我的英语

【讨论】:

    【解决方案3】:

    但是我应该:

    使用服务传递给控制器​​的文档列表基于一些 执行所需逻辑后的标准, 或使用控制器 下载文件列表,然后将文件传递给服务 执行一些逻辑?

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

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

    我宁愿认为第一种方法适合保留 控制器瘦(瘦控制器,大模型)但是这种方法 正确的?什么代码应该在实体中,什么在控制器中,什么在 服务?

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-27
      • 2019-04-13
      • 1970-01-01
      • 2014-12-27
      • 2023-01-31
      • 1970-01-01
      • 2021-10-19
      • 2018-12-08
      相关资源
      最近更新 更多