It’s a persistent statement: controllers should have as little code as possible because they’re difficult, nay impossible, to test. Developers should force most of their code into the models instead, where business, validation and other logic can take place. This way, the models are reusable and the code is easily tested in isolation. After all, if the controller can’t be adequately tested, then the controller can’t be expected to contain very much crucial logic. The controller becomes just a data and information traffic cop.
But this is not true. Controllers are no more or less testable than any other kind of code. What’s more, the fact that people believe controllers are largely untestable is an excuse for writing untestable code, not a valid design decision.
About a year ago, I was introduced to Zend Framework as the framework I was going to be working with almost every day. And for nearly a year now, every day I have worked closely with Zend Framework, learning it’s intricacies and dealing with its warts. I sat down in March of last year and wrote a case study about learning Zend Framework. A year after adopting it seemed like a good time to reevaluate the framework and reflect.
Learning Zend Framework was a daunting, challenging experience that tested myself and those I worked with. I learned a few lessons that I think are important, and I think are worth sharing.
In November of 2009, I wrote about why developers should write their own frameworks. I pointed out at the time that often developing a framework forces developers to make the kinds of architectural choices that frameworks require, which helps them better understand the architectural choices in the most popular frameworks.
I haven’t stopped believing in the power of doing as a learning tool. But in the past few months I’ve had an opportunity to move into more of an understanding of frameworks like Zend Framework, and I’ve come to another realization:
Until last week, I had never experienced what must have been incredibly frustrating to most developers: the fact that the self keyword in PHP refers to the class it is located in, and not necessarily a class that extends it. I personally ran into this problem when trying to extend Zend_Auth. Being a singleton, the constructor in Zend_Auth is protected, and the static method Zend_Auth::getInstance() instantiates itself. The problem is, when extended, My_Auth::getInstance() still returns an instance of Zend_Auth. The solution was to duplicate the static method in my My_Auth class, which worked properly. For example:
One of the things I’m always looking for is ways to improve performance with the applications I write. While a few applications are write-heavy, most are read-heavy: that is, reading the database is the predominant behavior (for example, this WordPress blog reads the database far more often than it writes to the database). Additionally, Zend Framework is (comparatively) slow at handling requests, offering a throughput of about 67 requests per second on my machine, while loading static pages came in at a whopping 750 requests per second.*
So, given this performance difference, how do we improve the performance of Zend Framework while still retaining its functionality and ease-of-use? Well, we employ caching, of course!
In the last two entries, we examined creating a navigation structure with Zend_Navigation, and then we examined using that structure with the Zend Navigation View Helper. In both discussions, we focused on creating navigation items and menus, and inherently these items were available to all users regardless of access controls. But what happens when you have special areas of your site, say for subscribers or administrators? Controlling access is something that all web developers must do at some point. This is where integration between Zend_Navigation and Zend_Acl comes in.
Some important points about Zend_Acl
Zend_Acl doesn’t follow any particular paradigm with regards to implementation of access control. Instead, much like Zend_Navigation, it works as a standalone component, allowing you to determine when, where and how to implement access control. Personally, I implement access control at the module/controller/action level, but you can choose to do it any way you like.