Symfony2: Framework independent controllers part 2: Don't use annotations
In the previous part of this series we decreased coupling of a Symfony controller to the Symfony2 framework by removing its dependency on the standard Controller
class from the FrameworkBundle.
Now we take a look at annotations. They were initially introduced for rapid development (no need to create/modify some configuration file, just solve the issues inline!):
namespace Matthias\ClientBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* @Route("/client")
*/
class ClientController
{
/**
* @Route('/{id}')
* @Method("GET")
* @ParamConverter(name="client")
* @Template
*/
public function detailsAction(Client $client)
{
return array(
'client' => $client
);
}
}
When you use these annotations, the details
action will be executed when the URL matches /client/{id}
. A param converter will be used to fetch a Client
entity from the database based on the id
parameter which is extracted from the URL by the router. The return value of the action is an array. These are used as template variables for rendering the Resources/views/Client/Details.html.twig
template.
Symfony2: How to create framework independent controllers?
Part I: Don’t use the standard controller
The general belief is that controllers are the most tightly coupled classes in every application. Most of the time based on the request data, they fetch and/or store persistent data from/in some place, then turn the data into HTML, which serves as the response to the client who initially made the request.
So controllers are “all over the place”, they glue parts of the application together which normally lie very far from each other. This would make them highly coupled: they depend on many different things, like the Doctrine entity manager, the Twig templating engine, the base controller from the FrameworkBundle, etc.
Inject a repository instead of an entity manager
It appears that I didn’t make myself clear while writing about entity managers and manager registries yesterday. People were quick to reply that instead you should inject entity repositories. However, I wasn’t talking about entity repositories here. I was talking about classes that get an EntityManager
injected because they want to call persist()
or flush()
. The point of my previous post was that in those cases you should inject the manager registry, because you don’t know beforehand which entity manager manages the entities you are trying to persist. By injecting a manager registry you also make your code useful in contexts where another Doctrine persistence library is used.
Inject the ManagerRegistry instead of the EntityManager
Or: how to make your persistence-related code more reusable
You probably know that you should trim your controllers as much as possible. This usually means extracting some classes from the semi-procedural code inside your controller class. You create services for the extracted classes and inject the necessary dependencies into them. You can no longer use the convenient $this->getDoctrine()->getManager()
. Instead you inject the doctrine.orm.default_entity_manager
service into every service that needs the EntityManager
to persist data in your relational database:
Book review: Modernizing Legacy Applications in PHP
Legacy code
I’m happy that I’ve discovered the work of P.M. Jones recently. His and mine interests seem to align at several interesting points. Though I don’t personally enjoy “putting .308 holes in targets at 400 yards” (as quoted by Phil Sturgeon), I do care a great deal about package coupling (and cohesion for that matter) and I’m also lightly inflammable when it comes to the use of service locators. It also appears that Paul, just like myself and many others in this business, has felt the pain of working on a legacy PHP application, trying to add features to it, or change existing behavior. This is a particularly hard thing to do and because so many incompetent developers have been creating PHP applications since the dawn of PHP, chances are that a big part of the job of competent PHP developers nowadays consists of maintaining these dumpsites of include statements and superglobals.
Principles of PHP Package Design - First part of the book is now available
Seven months, two presentations and three blog posts later, I’ve published the first installment of my new book, Principles of PHP Package Design.
From the introduction of the book:
Naturally the biggest part of this book covers package design principles. But before we come to that, we first take a close look at what constitutes packages: classes and interfaces. The way you design them has great consequences for the characteristics of the package in which they will eventually reside. So before considering package design principles themselves, we first need to take a look at the principles that govern class design. These are the so-called SOLID principles. Each letter of this acronym stands for a different principle, each of which we will briefly (re)visit in the first part of this book.
There's no such thing as an optional dependency
On several occasions I have tried to explain my opinion about “optional dependencies” (also known as “suggested dependencies” or “dev requirements”) and I’m doing it again:
There’s no such thing as an optional dependency.
I’m talking about PHP packages here and specifically those defined by a composer.json
file.
What is a dependency?
First let’s make sure we all agree about what a dependency is. Take a look at the following piece of code:
Test Symfony2 commands using the Process component and asynchronous assertions
A couple of months ago I wrote about console commands that create a PID file containing the process id (PID) of the PHP process that runs the script. It is very usual to create such a PID file when the process forks itself and thereby creates a daemon which could in theory run forever in the background, with no access to terminal input or output. The process that started the command can take the PID from the PID file and use it to determine whether or not the daemon is still running.
About coding dojos, the Symfony meetup and my new book
Last week was a particularly interesting week. I organised three coding dojos, on three different locations. The first one was at the IPPZ office (one of the companies I’ve worked for), the second one was at the SweetlakePHP meetup in Zoetermeer. The last one was for a group of developers from NOS, a large Dutch news organization. Although that would have been enough, I finished the week with my revised talk “Principles of PHP Package Design” at a meeting of the Dutch Symfony Usergroup in Utrecht…
A Year With Symfony: Bonus chapter is now available!
My first book, A Year With Symfony, has been available since September 4th last year. That’s not even six months ago, but right now it has almost 800 readers already (digital and printed edition combined).
During the past few days I’ve taken the time to write an extra chapter for the book. Consider it as a big thank you to everybody who bought the book! I feel very much supported by the Symfony/PHP community and this really keeps me going.