Much has been said this days about how modern web frameworks interpret the MVC architectural pattern. I'm my case those articles made me rethink how I use a framework, specially Symfony.
This lead me to start a study on which patterns come into play while we develop a Symfony application. So while adding new features or refactoring existing ones I will know which class is in charge off doing the job.
Some of the patterns involved in Symfony are:
- Front Controller
- Intercepting Filter
- Context Object
- Two Step View
- Helper Object or View Helper
- Table Data Gateway (i.e.: ArticlePeer.php)
- Row Data Gateway (i.e.: Article.php)
- Active Record
- Single Table Inheritance
How do they interoperate? The Front Controller act as an entry point for our application. After it processes the request it will choose which Command should handle the request, in our case, one of our defined actions, that's why we have the execute prefix as a convention for the action
While the request is being processed Symfony filter chain will launch modifying the results accordingly. The idea of the Intercepting Filter pattern is to be possible to pre and post modify the request and the response without the need of change existing code. What's is a nice feature in Symfony is that we can add them directly on the filters.yml file making really convenient to use. Also we can specify filters that only apply for certain modules, making the process really flexible.
On the View side Symfony uses the Two Step View, so generally the result of our request will be decorated with a common layout to add consistency to our website. Here we can also use helpers, as they are explained in the Symfony documentation or add Helper classes. The Helpers classes can retrieve data from the Model, format data, or anything our application needs to work on the View side. With them we make our templates clear and free off script code, so it's easy to non developers to work on them.
Also Symfony allows to use specific view classes by module, adding extra flexibility in the way we can handle the response, let's say, to return JSON data.
As a way to keep a reference to the context, Symfony implement the -some guess please- Context Object pattern, which I won't talk about here.
Then we have the DAL, which in my case is powered by Propel. This ORM implements the Table Data Gateway and the Row Data Gateway patterns. As they name implies they act as gateways to our tables and rows, but because generally we add some domain logic inside them they start to act as Active Records.
A cool feature of Propel is that it implements the Single Table Inheritance pattern. This pattern is useful when we have a table for publications where we store Magazines and Books. If our table has a field acting as the type of the row, and in our application we treat them differently, whether if they are books or magazines, then this pattern let us work with Publication, Book and Magazine classes.
After this research I came with the following conclusion:
- Use the controllers to handle user input data and choose which view to display.
- If there, remove all business logic from the controllers.
- Let the Model do it's job with the provided data.
- When handling data to the model, do it as an array of normalized data -or proper objects pertinent to our application-, the model shouldn't know about the request.
- Let helpers or Helper Objects handle the data formating, avoiding script code inside templates.
- Use Propel as a DAL and refactor towards a Model. This means to remove complex business logic from the Propel classes.
What helped my during this research where the following books, which I highly recommend.