The other day I had a nice meeting in my basement lair with my co-workers. We laughed. We cried. We had some BBQ’d burgers, and nobody got food poisoning. As we discussed the implementation of our new fantasy gaming platform, I managed to figure out that one requirement was for an admin to upload a header and footer file for a game. “The idea here is to be able to set up a new game for someone without programmer intervention”.
After I got over being insulted that a programmer was not required for every tiny tweak to the site (I’m kidding) I tried to figure out how I was going to accomplish this. I’m not a big fan of storing actual HTML content in a database. Call me old-fashioned, but I feel my HTML content belongs on the filesystem, and nowhere else. Besides, it makes it harder to do deployments of code on multiple servers if all the templates aren’t there to begin with.
The solution was, of course, obvious: create a custom Zend_View and override stuff until you get it behaving the way you want! The logic for doing this is actually quite simple:
Check to see if the script in question exists in the file system
If not, figure out what template we need to pull from the database.
Read in contents of that script from the database
Write contents to the filesystem
NOTE: I am not sure if this is the most elegant solution for solving this particular problem. I am open to a different solution so long as it meets the same needs.
Since I’ve made a commitment to doing TDD for this project, naturally I started out with a test:
The “Template” is a Doctrine (1.2) model that represents the table where I’m storing information about the templates. Yes, it has unit tests too. That pass.
So how do we accomplish this? First, I hit up my peeps on Twitter and the advice from Matthew Weier-O’Phinney himself was to simply create my own Zend_View and override what needed to be overriden. Also, he was very wise to tell me to look at Zend/View.php itself for guidance. That and some helpful messages from exceptions during testing.
Here’s the initial implementation, for which all tests pass.
* Includes the view script in a scope with only public $this variables.
*
* @param string The view script to execute.
*/
protected function _run()
{
if ($this->_useViewStream && $this->useStreamWrapper()) {
include 'zend.view://' . func_get_arg(0);
} else {
include func_get_arg(0);
}
}
}
So, what did I learn from this process?
I needed to create my own private _file and _filter variables to match what exists in so that if I create any custom filters they can be applied to my custom view *and* to keep compatibility with existing code.
Because the _run method is protected AND abstract, I needed to implement my own _run method in my custom view, or else it would spit out errors. Even if it doesn’t do anything special.
You can see that the logic in my render method is pretty much as I outlined before. I realize I will take a slight hit the first time we go to load a particular template. I’m assuming at some point I will want to run a test to see just how long it does take to pull in the template from the database and write it to the file system. I’m guessing this will not be a serious performance hit, but you never know. All those file_exists() calls can’t be that good for performance,
I look forward to seeing other potential solutions to this problem