Reputation: 1120
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
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
Reputation:
My Symfony 2 architecture is (with Doctrine ORM):
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
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