0

I'm running into problems breaking up a complex Controller.

The action has many different conditions and the code inside each block uses different dependencies. What's the most logical way to break this up into separate controllers so I have a better handle on my growing list of constructor dependencies?

The reason it's all in one action is because it's serving a single URL /report which renders different templates based on permissions and other conditions.

PS. The code is not technically correct, was made quickly to visualise my question.

<?php

class ExampleController
{
    protected $dependency1;

    protected $dependency2;

    protected $dependency3;

    protected $user;

    /**
     * ExampleController constructor.
     *
     * @param $dependency1
     * @param $dependency2
     * @param $dependency3
     * @param $user
     */
    public function __construct($dependency1, $dependency2, $dependency3, $user)
    {
        $this->dependency1 = $dependency1;
        $this->dependency2 = $dependency2;
        $this->dependency3 = $dependency3;
        $this->user = $user;
    }

    public function exampleAction()
    {
        if ($this->user->hasRole('a')) {
            $this->dependency1->something();
        } elseif ($this->user->hasRole('b')) {
            $this->dependency2->something();
        } elseif ($this->user->hasRole('c')) {
            $this->dependency3->something();
        }

    }

}
The Pixel Developer
  • 13,282
  • 10
  • 43
  • 60

4 Answers4

2

You have to create a Service (that you call in your controller) to define all your business logic. Controller should only intercept a Request to render a Response.

EDIT: Have a look to official documentation and more particulary the Service Container section: http://symfony.com/doc/current/book/service_container.html

scoolnico
  • 3,055
  • 14
  • 24
  • 1
    Might consider adding a bit of detail to your answer as it does not seem to address the question. How, for example, would defining another service solve the role based dependency issue? How would the controller access the service? – Cerad Oct 21 '15 at 14:30
  • 1
    The controller should have the one service injected, or should be ContainerAware; in this way, the service can handle separately the business logic, away from the controller itself – Alessandro Lai Oct 21 '15 at 15:09
2

What you can use is Chain.

You need someone responsible for handling the dependencies. If you're using Symfony DI container (I know you do) you can you easily add dependencies into Chain via tags in service definition.

Have a look here http://symfony.com/doc/current/components/dependency_injection/tags.html

If it doesn't help you can go and kill me IRL

Michael Czolko
  • 2,698
  • 2
  • 15
  • 25
2

I don't know of a single best approach. Some suggestions:

  1. Revert back to using the dependency injection container as a service locator and just pull the dependencies out of it as needed. I'm a big proponent of defining controllers as services and injecting dependencies but sometime using a service locator makes the most sense.

  2. Create a kernel.controller listener http://symfony.com/doc/current/cookbook/event_dispatcher/event_listener.html and use it to select a container based on the user role or whatever other criteria you might have. This is a well documented approach though it does hide some of the dependency information in the listener.

  3. Extend the ControllerResolver http://api.symfony.com/2.0/Symfony/Component/HttpKernel/Controller/ControllerResolver.html and select the controller based on roles or other criteria. Basically the same approach as using a listener but works a bit upstream. This is probably the cleanest approach though there are not many examples out there. Extending Symfony2 Controller Resolver

Community
  • 1
  • 1
Cerad
  • 48,157
  • 8
  • 90
  • 92
  • 1
    With point 1, how are you unit testing controllers? That's the only thing that bothers me with this approach. I'm not sure even sure why it bothers me, unit testing controllers seems like a brittle approach to making sure a controller gives me the correct output. – The Pixel Developer Oct 22 '15 at 09:42
  • 1
    I don't unit test controllers, only the various dependencies. Approach 1 is the "standard" way of writing S2 controllers. – Cerad Oct 22 '15 at 12:34
0

a other possibility is by extend a other Controller In the example you create a other Controller, ToolsController.php by example

Class ToolsController
 public function exampleAction()
    {
        if ($this->user->hasRole('a')) {
            $this->dependency1->something();
        } elseif ($this->user->hasRole('b')) {
            $this->dependency2->something();
        } elseif ($this->user->hasRole('c')) {
            $this->dependency3->something();
        }

    }

And in your Controller,

use App\Controller\ToolsController;

Class ExampleController extends ToolsController
{
...
}
Cadot.eu
  • 316
  • 1
  • 3
  • 15