I came across this Stack Overflow question a couple days ago, asking about the proper place to put business logic in a Symfony2 application: whether it belongs in the controller, a service, or elsewhere. Actually, that’s quite a common question that I see: what, exactly, belongs in the controller? The documentation has this to say:
A controller is a PHP function you create that takes information from the HTTP request and constructs and returns an HTTP response (as a Symfony2 Response object). The response could be an HTML page, an XML document, a serialized JSON array, an image, a redirect, a 404 error or anything else you can dream up. The controller contains whatever arbitrary logic your application needs to render the content of a page.
Now, let me sum that up for you: the controller is meant to construct and return a response. That’s… it, really. It’s not meant for heavy lifting. Way too often, I see controller actions that are completely loaded down with code, heavy with business logic that would be better-suited (not to mention more reusable) in a service, or even a custom entity repository. And frequently, I’ll see controller actions that try to deal with more than one thing (like a view action also handling search results — with the search code written out in the action, of course). As a result, I’ve come up with a short checklist for writing your controllers:
- Controllers are meant to be thin. Think of a controller as the glue of your application, a lightweight connection between a model and a view… emphasis on “lightweight.” That means that as much code as possible should be offloaded to services, repositories, etc. I’ll cover those topics in a future post.
- Controllers should only handle one thing. Symfony2 has a great feature that lets you render embedded controllers, meaning that any one controller should only perform a specific action: viewing or editing a resource, for example, or retrieving search results.
I did say short, didn’t I? Learn to love and live by the principles of separation of concerns and and DRY: if you’re copy-pasting code to more than one place in a controller, it’s probably time to start thinking about how you can refactor to bring all that duplicated work into one easy-to-maintain location.
Another thing: the base controller available in the Symfony2 FrameworkBundle provides a great collection of helpful methods to get you started, but you can feel free to extend it or ignore it as you please. Because controllers can be any PHP callable, you’re not required to use the FrameworkBundle’s controller at all. In fact, because the FrameworkBundle is technically an optional dependency, it’s considered good practice to provide your own self-contained controller classes instead of using the base controller if you intend to make your code available to other users in the community.
And as always, the community abounds with examples of great code. The FriendsOfSymfony projects are a good place to start looking if you’re wondering about best practices: just dive into any repository and start looking at how they’ve built their controllers. I’m also happy to answer any questions you might have; feel free to leave a comment or shoot me an email to firstname.lastname@example.org. Happy coding!
Edit: the link to wikipedia’s entry on “Don’t repeat yourself” was broken. It’s fixed now.