Why have Controllers as Services in Symfony?
The concept of a services is simple too, it's technically just a PHP object that performs a task over and over again somewhere in your application. Old school PHP developers can think of it as a fancy include to include statement you can use in different files to get access to the same exact function without replicating code.
Obviously there's quite a bit more going on underneath the hood and you may want to familiarise yourself with the Symfony Service Container if you want to know more of the details. In a nutshell, though the service allows importing (or injecting) other services or settings to the scope of the service.
Using services for tasks repeating in multiple locations of your application undoubtedly makes sense, but why should you shrinkwrap your controllers into a service? If you look at the official Symfony Demo Application does not do this. So why should yours?
Controllers as Services in eZ Platform
There is a whole section in the Symfony documentation dedicated to how to define Controllers as Services in Symfony. This is without a doubt a great resource for learning the mechanisms and benefits of doing so.
However, as a practical example we'll now take a look at how eZ Platform Demo, a complete example installation of a site built with eZ Platform, a CMS built on the Symfony Framework, leverages controllers as services.
The Blog Controller
The blog controller includes an action listing blog articles to render a simple list of them:
This is quite straightforward to read, even if you are unaware of how the eZ Platform Public API works, as it is yet another service and as such familiar to any Symfony Framework developer.
The Blog Controller Service Definition
If you have worked with services in Symfony in the past, the definition of the blog controller service is clearcut. First it's a common practise to define the service classes as parameters in the beginning of the YAML file, and from there on you move on to passing arguments:
In this case many of the arguments passed to the blog service are other services. There are a few parameters and there might as well be siteaccess aware dynamic settings too. Adding additional injections in the future is possible as well.
Injecting dependencies in the Blog Controller
In this case, the controller is done with constructor injection, so upon the initialization the services passed are set to be the properties of the specific object:
Now all the actions have access to whatever services or parameters were injected here. Allowing for easy access to services provided by eZ Platform domain services or any other services accessible to in your Symfony application.
Calling the Controller Actions
We've now set up a controller as a service, but obviously we need to call it somewhere. eZ Platform uses the view API and YAML configurations to match controllers and templates to dynamic routes provided by the content engine:
Calling a controller turned into a service follows the format
where as a regular controller would be, in this case, called with
Calling with the controller tag routes the call internally to the service mechanism, instead of calling the controller directly.
In the case of building a single site wrapping controllers into services might be of limited use, but if you want to reuse your blog functionality in the future it becomes more clear with the structure. And if your application contains a number of bundles with controllers, then the entrypoint of debugging is more straightforward as you can utilise the service debugging tooling.
In addition to easier extension and reuse of functionalities across different projects, services as containers also simplify modifying the application through configuration, rather than code. Or being locked into proprietary CMS extension methods, instead opening up opportunities for a Hexagonal Architecture.
If you would have originally built a blog service that provides a generic interface then it would be (relatively) simple to switch the site's blog functionality from the eZ Platform content repository to one using the Medium API, for example - allowing the use Symfony as an Integration Platform.
Wrapping controllers as services comes with little overhead, but offers increased flexibility and code readability along the road. Just do it.