Abdel5
Abdel5

Reputation: 1120

Code separation in symfony 2 - Controller vs Service vs entity

I am using symfony 2 and I have question about code separation. I would like to make sure that I correctly understand what elements should be in a controller, what in a service and what in a entity.

Let's imagine that I have list of documents that I need to display. On each document before displaying I have to also perform some logic operation (e.g. add two variables).

As in understand entity class takes care only on data retrieval and operation on single entity. I should not input there any custom code. As I understand this should be done by a service.

But should I:

I would rather think that the first approach is appropriate to keep controller thin (thin controllers, big models) but is this approach right? What code should be in entity, what in controller and what in a service?

In particular where should I relate to entity manager - in a controller or rather in service?

Let's also pretend that in many place in my app I need to check if document is finalized before allowing user to perform any action (e.g. edit it). This definitely should be either in a service, as another service would be required to check this. Should I however load the document entity object in controller, send it to service to verify whether it may be finalized or rather load document in service and there perform a check?

Upvotes: 8

Views: 4558

Answers (3)

Alex
Alex

Reputation: 1183

But should I:

use a service to pass to controller list of documents based on some criteria after performing the required logic, or use a controller to download list of documents, and than pass document to service to perform some logic?

Rather, second. Just because it lets you to understand what's going on looking at your controller code. The way all web-frameworks work is supposed to map each request to controller action. Following this logic expecting a request you go to the controller. I'd suggest you to call service directly in controller to do some custom logic on data. But if you need to retrieve data from database it's better to implement in your repository class not in service.

If you need to reorganize your data after retrieving maybe it's necessary to rethink your database structure. It's much easier and much more convenient to have ability to retrieve what you need without any action after retrieving.

I would rather think that the first approach is appropriate to keep controller thin (thin controllers, big models) but is this approach right? What code should be in entity, what in controller and what in a service?

To keep controller thin it's enough to not put into it queries and other difficult logic: like sorting, filtering, etc.

In particular where should I relate to entity manager - in a controller or rather in service?

It's also pretty easy. You should relate to em there where you need to. There is nothing bad if your logic is not complex and the only need you have before show result to a user is to do very tiny stuff (like setting some virtual property to retrieved entity) its absolutely ok to put this logic into controller. But don't forget refactor your code with complexity growing. If it becomes a code which should be applied in different places (repeatable code) - get in out from controller into service.

Upvotes: 2

user5192753
user5192753

Reputation:

My Symfony 2 architecture is (with Doctrine ORM):

  1. Thin controllers with just the routing logic
  2. A service (a.k.a. "Manager") for each entity (all the business logic is here)
  3. Custom services for my other needs (ie, for using external tools like Amazon S3 or Mandrill mailing system)
  4. A repository for each entity (just methods to read entities from the DB)

Each action inside a controller calls one or more methods from the entity's manager; I always try to avoid using directly the respository's "magic methods" in favor of custom made methods: inside the action, instead of calling

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

I create this method inside the repository:

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

And inside the action I use:

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

Of course this is a simple example, but it works quite well with more complex findBy or findOneBy queries.

Upvotes: 10

Absalon Valdes
Absalon Valdes

Reputation: 910

In Symfony2 is super easy decouple logic using repositories and services. For example:

A entity repository with aditional custom finder

use Doctrine\ORM\EntityRepository;

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

A fat service to handle the main business logic

// 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
    }
}

To define services:

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

In your controller:

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: sorry for my english

Upvotes: 7

Related Questions