PHP Frameworks: comparing CakePHP and symfonyTuesday, December 30, 2008
I've futzed with PHP for a fair number of years, but never really seriously looked at PHP frameworks. Some of the applications I write are fairly simple, but have to deal with a metric truckload of data. I like data, it's like plasticine, you can mould it in a variety of different ways.
Over the last 6 months I've become more and more frustrated about having to write essentially the same database code over and over to deal with storage and retrieval. This eats up so much time and effort, and leaves me with very little, and practically no enthusiasm or motivation for then writing the actual application and business logic.
So I thought I'd take a look at a few PHP frameworks and find one that works with me. The main essential feature was to alleviate the core SQL shenanigans I seem to get stuck doing over and over again, give me a framework that allows me to focus more on real business and application logic, and less on the monotonous plumbing. The second essential feature was to be able to process data inside the data Model from a CLI PHP script so it could run regularly as well as by hand.
When it comes to web frameworks, the only one I've used in anger is Struts, back in my time with Legal & General. It's decent, but gets a little convoluted when you need nice URLs on your web apps. But Struts is no good to me because I'd need a Java environment - which rules out any decent shared hosting solution.
Asking around in work (well, the extended group of web developers that used to be the phenomenal Yahoo Web Dev team in London, and Munich) for PHP framework suggestions, the most frequently recommended one was Django (go figure!), followed by symfony, Code Igniter, and one solitary suggestion of Cake PHP.
I started looking at CakePHP because of one single idea: Cake's default approach is to start with an existing database schema and semi-automatically generates the Model classes for you. No SQL. Right out of the box, Cake generates a simple interface that allows you to browse and update data in the existing data. (The main developer work was to associate each table together using class level variables - took a bit of thought to translate the database scheme relations into what Cake wanted).
I was absolutely hooked on that one feature alone. I spent a weekend creating a CakePHP application; well, application is an overstatement, it was the scaffolding code. Most of the time was populating the database with data through the scaffolding code. With very little effort you had a tidy, although not amazing, editing application for your data.
Unfortunately, that's how far I got; playing with the scaffolding. I tried to work within the Cake conventions, trying to disregard things like clean URLs, but I kept wanting to look elsewhere.
The MVC felt forced, where the Controller felt too tied to the Model. Each table in a database had it's Model class, and its Controller class. That's fine for writing simple construct web applications: one table = one facet or view. Which is just about as far away from the types of applications I was wanting to build.
I think the problem I ran into was that Cake is too helpful, and the developer is shown a set of voodoo steps and incantations that magically cause Cake to do something splendid. The only problem was when that didn't work, I wasn't in a position to figure out what went wrong, or how to approach diagnosing the problem through to fixing it. It felt like I was treading a very fine line, as long as I followed the developer examples carefully, things worked, but with no real understanding why.
The documentation of Cake is decent. Both the online documentation as well as the Cake Book proved useful as introductory and basic material. I wanted a better insight into Cake, and I felt at a loss of how to build a real application (as opposed to a one-table todo list, and a blogging application).
I also really disliked the view helper methods, particularly how the Beginning CakePHP from Novice to Professional uses them in the code examples. Examples that generate invalid markup do not inspire confidence in me; wrapping list items in an anchor is not kosher. The form helpers do not encourage the development of well structured forms (with fieldsets and legends). The chapter on adding Ajax I found just appalling, no progressive enhancement or even graceful degradation. That all left me cold.
Symfony isn't a framework that initially appealed to me. With the barest knowledge, it feels limited and forces you to work in a specific way, and that way felt at odds with how I wanted to work. But I decided to spent some of my offline time in December having a serious look at it and see what it was really about.
I grabbed a paper copy of the Definitive Guide to Symfony, and the sandbox version of symfony 1.2, as well as the Askeet tutorial.
The book itself covers symfony well (but version 1.0). I found the introduction sections about the advantages of an MVC framework a compelling argument for the use of symfony. The following chapters explains the features of symfony very well, perhaps too well as I found myself overwhelmed quite quickly in its detail. Intrigued but slightly overwhelmed.
Unfortunately, I couldn't really get started with the Askeet tutorial, none of the symfony commands seemed to work, and the development environment instructions seemed quite at odds with the sandbox archive I was using. I figured out that using the sandbox was part of the problem, so I downloaded the "other zip file", the one that wasn't a sandbox. (The main installation methods of SVN and PEAR installs were not practical over a really slow dial-up modem in far flung South Africa.)
In downloading the main symfony zip file in early December, I noticed the Jobeet tutorial written especially for version 1.2 of symfony. Comparing the symfony CLI commands I realised that there's enough of a difference between versions 1.2 and 1.0 which was also causing problems with the Askeet tutorial.
So with a proper copy of symfony, plus the first parts of the Jobeet tutorial, I tried again with good success. Allied with the correct version of the Definitive guide documentation, things started moving very well.
Symfony's approach to data storage is to define a database schema in YAML - a simple text format. With that YAML definition the SQL statements are written to generate the actual database, and the Model classes are created from the very same definition. So symfony, by convention, takes control of the creation of MySQL data tables. I initially thought that was too limiting, but it turns out to be a very useful feature.
Because symfony controls the table structures it offers more flexibility and power than Cake can. Changing the database structure is as simple as changing the YAML file and getting symfony to rebuild both the Model classes and the database itself. Symfony's class structure for the Model means that additional custom methods you add don't get deleted on each model build. So changing the database structure works in a nice agile fashion.
Data fixtures are a killer feature of symfony. A fixture is a way of adding default data to a database. As part of building the database tables and Model, symfony gives you a way of populating it with start data. So no more backup/restore or tedious re-entering data, symfony has it all taken care of.
Out of the box, symfony has three environments: development, test, and production; along with the capability of creating more. This offers three different setup configurations within one single codebase. The production environment is optimised for performance (so caching, and limited logging), the development environment bypasses the caching and logs loads of useful stuff, and the test environment allows test data to be used without impacting the production data.
Symfony has been well thought-out framework that succeeds in agile-type development. It's easy to iterate and play with various approaches without compromising the application.
What struck me as impressive was the way the Model is separated from the Controllers, and the Propel ORM itself. There is one data model for the entire project. A project can contain multiple applications, which itself can contain multiple modules. This is so logical, and so fundamental to making symfony a joy to work with. That one point makes CLI scripts straightforward to do in symfony, and I had one running within minutes of defining a database schema in YAML.
Initially, it felt like symfony has too many files. Loads of YAML configuration files, loads of generated Model class (each table generates four PHP classes); and what seems like a complicated directory structure. But, where symfony succeeds, where Cake fails, is to explain to the developer what all these files are. Symfony's documentation inspires developer confidence, because it is not afraid to explain what all those files are for, and why things are done in this particular way.
Clean URLs using the symfony routing system just made sense. I couldn't get my head around Cake's approach. With symfony I got the clean URL structure I wanted at the first attempt. Although, I haven't figured out how a project with two applications is going to work in one URL space, since each application has its own front-end controller.
Symfony's use of Propel is brilliant. The generated model classes offer useful methods of dealing with join-table type queries: many-to-many relationships using an intermediary table results are handled very naturally. Although you'd have to look at the generated code to find these useful helper methods; but symfony does generate decently formatted code, with useful comments.
The one thing about symfony's Model classes I found strange was that the Criterion object (created for each added Criteria rule), the value attribute is private with no setter method. That makes it impossible to create a Criteria object, and then change the value on the fly. A simple public setValue() method on the Criterion object would solve that (the Criterion->value is only set on the constructor). I don't like to create classes all the time when I know I can modify an existing one - sure, it does work.
When the right version of symfony is used in conjunction with the right version of the documentation, symfony is excellent. Although, I would have preferred offline copies of the documentation - like a PDF. Not having a PDF of the documentation was the main root cause of my initial difficulties with symfony.
With the documentation available, the online Definitive guide to symfony in conjunction with the Jobeet tutorial are very useful sets of documentation, and allow a developer to quickly get to grips with the framework.
And, symfony encourages test-driven development. Unit test aren't an after-thought.
Comparing Cake to symfony
Symfony is clearly an enterprise capable framework. It feels solidly built, and it feels logical and intuitive. Many times when I wished that symfony could do something, they were one step ahead of me and it was already there. I like symfony because of the thought that's been put into it for building real-world applications. There's a lot of functionality that's not obvious at first until you go digging around. I found that compelling and confidence-building.
Cake feels well thought-out, but still a bit of a black hole, and its documentation didn't cover what I felt I needed to know. Cake is impressive. Its scaffolding code generation is remarkable. I struggled with how to take that code further and adapt it to my own needs. I felt uncomfortable changing the code because I was scared to break what was already working. I guess I felt the generated code was brittle - this may be an unfair categorisation.
But, Cake's documentation lets itself down by producing appalling markup - particularly the helper functions and the Ajax examples.
Simply, symfony did what I expected, and it seemed to know what Model helper methods I would need. It certainly is more complicated than Cake, but the complications are, for me, well worth it.
I'm left with the feeling that Cake does the simple things very very well, and the difficult things are not obvious. With symfony, the simple things are a little more complicated, but that makes the difficult things much easier and logical.
In terms of trust and confidence, and control over code quality, symfony wins hands-down. And this is my choice of framework to use for the applications I'm building for myself.
Update 5 January: Chris Hartjes has has rebutted an number of criticisms I made about CakePHP in an excellent blog post CakePHP Mythbusters. I'm glad to see this; I really wanted to like CakePHP, so his explanations give me some fresh hope that I might be able to use CakePHP in the future.