Symfony2 reusable functions in controllers

2019-06-05 00:03发布

After some time without knowing how to do this properly, and avoid duplicating code in several controllers, I searched and again to seek, but can not find a clear answer.

One case in particular, need to calculate several statistics data completed of an entity. This calculation will use in 3 different controllers. In two of them, I'll show broken down into different layouts, and on the third, I will use this data to make a global calculation. business logic for each calculation, involves more than 100 lines of code, I would have to triple in the different controllers.

A scheme could be:

  • Calculation fields completed "Data A"
  • Calculation fields completed "Data B"
  • Calculation fields completed "Data C"

With these 3 values, I make a later total calculation.

The options that I could find are:

Any idea how to solve this scenario?

thanks a lot

1条回答
萌系小妹纸
2楼-- · 2019-06-05 00:31

The Services seem to be much corresponding with the described usage.

Recently, I worked on an accounting solution. I used a lot of complex algorithms and I soon had very long methods, even trying to optimise code.

Services are easily callable, and their usage can make controllers so much sexier, lighter, and make big methods readable and more cuttable by using separated services corresponding to specific actions.

And they can be used in other components, as long as DependencyInjection is present, it's a sufficient raison to move your logic if you may need apply it in another context that a controller.

It's simple to declare it :

services:
    acmeapp.calculation:
        class: Acme\AppBundle\Services\CalculationService
        arguments: ["@doctrine.orm.entity_manager", "@acmeapp.anotherservice"]

And the service

class CalculationService {

    protected $em;
    protected $another;

    public function __constructor(EntityManager $entityManager, AnotherService $anotherService)
    {
        $this->em = $entityManager;
        $this->another = $anotherService;
    }

    //...
}

The Controller-Service approach is primarily a service, with all its advantages.
A method of your service can render a view and have a route associated using the _controller attribute, like this :

display_data_a:
    path:     /data/A
    methods: GET
    defaults: { _controller: acmeapp.calculation:dealWithData }

Without extending your service from the Symfony\Bundle\FrameworkBundle\Controller\Controller, but, of course, you can use it.

Also, an abstract BaseController can be a very clean alternative, if you have many duplicated code with a few different characters between your controllers.
It's what I'm doing before use services, if the methods are corresponding with my definition of a controller, which is corresponding to what @fabpot says in your link.

The DIC mostly helps manage "global" objects. Controllers are not global objects. Moreover, a controller should be as thin as possible. It's mainly the glue between your Model and the View/Templates. So, if you need to be able to customize then, it probably means that you need to refactor them and extract the business logic from them.

More about BaseController in OOP,

The way is simple, if you have a line of code that repeats itself two or three times in a method, you use a variable.
Same for a block of code, you will use a method. For controllers it's the same, if you have two or more objects of the same type (here a controller), you should use an AbstractController (or BaseController), move your duplicated methods in it (only once, of course), and remove them from the child controllers.

BaseController :

class BaseController extends Controller
{
    /**
     * Shortcut for get doctrine entity manager.
     */
    public function getEntityManager()
    {
        return $this->getDoctrine->getEntityManager();
    }

    /**
     * Shortcut for locate a resource in the application.
     */
    public function locateResource($path)
    {
        return $this->get('kernel')->locateResource($path);
    }

    // ...
}

And use it as part of your child controllers

class ChildController extends BaseController
{
    public function helloAction()
    {
        $em = $this->getEntityManager();
        $file = $this->locateResource('@AcmeAppBundle/Resources/config/hello.yml');
        // ...
    }

}

Hope this helps you to avoid a lot of a duplicated code.

查看更多
登录 后发表回答