Archive
Models in frameworks
One of my biggest frustrations with most MVC frameworks is that their approach to models are generally broken. Models should hide the data from the controllers. A controller shouldn’t need to know anything about the data, including what kind of storage is used. Most frameworks don’t use this approach.
Suppose, for example, that I have a model for Users. The model should provide the ability to add a User, remove a User, and update a User. We’ll also probably need some methods for listing and other ways of manipulating the user information. However, the model should be written in such a way that if we start with the user information in a database, and later move it to a remote LDAP server, or some SOAP or REST service, or a text file it won’t matter. All of the controllers should be able to continue to call the same model methods that they were calling before, in the same way, and it shouldn’t matter to them where the data is coming from. Knowing where the data is stored and how to get it and work with it is the model’s job.
This means that the model should not extend some database class. The framework’s model structure should not assume that all models use databases. In general, most discussions of MVC frameworks try to isolate the actual database work from the controllers, but they still require the controller to know too much about the data repository. The effect is that you could probably move from a MySQL database to SQLite, but if you stop using a database entirely you are going to have some major work to do on your controllers that use that model. To me, a major measure of success for any MVC approach is how much your controllers have to know about the data. Less is better.
Zend Framework tries to accomplish this, but they basically do it by not giving you a start at any models at all. Even if it was a very abstract class it would be nice to have something to extend. At least that would give people a place to start that’s better than extending one of the DB classes, which just about all of the tutorials and articles on ZF models recommend doing. ZF’s approach to just leave it entirely out of the framework means most instructions tell people to do it wrong.
I’m not a purist. I’m a pragmatist. I don’t care a great deal about patterns for their own sake, in any sort of Platonic sense. I just want to be able to be lazy when I find out later that indeed change is normal and I need to adapt an application to shifting requirements. Because as predictable as the weather in Phoenix, just about the time that an inventory application is just about through a couple of iterations and really getting useful, they are going to want to integrate it with some remote POS system, and oh yeah can it get its user information from some place in the cloud rather than the local accounts we wanted to use last week. I’d much rather say “Sure, that’ll be ready day after tomorrow” than say “DOH!” That requires the controller to be ignorant about the data repository.
Getting more done
I was delighted to see the announcement that Zend Framework is partnering with Dojo. Life will be easier. I like Zend Framework. I like Dojo (although I’ve used jQuery on some recent projects). What could be better? Time from idea to working solution will be shorter. As a result, customers and users will say “Wow, how’d you do that?” and get back to working more efficiently and in ways they couldn’t before, quicker. That’s good stuff.
Building table based forms in Zend_Form
In web applications it is fairly common to want to edit multiple rows of the same kind of information, formatted into a table, kind of like you can in a spreadsheet. These forms are generally for editing existing information pulled from a database, which is usually already in an array format. Most of the examples I can think of at the moment need explanations of the context to make much sense, so I’ll pick an example that isn’t as likely to be useful but doesn’t need explanation.
Suppose I have a table in my database that lists contacts with title, first name, last name, and so on. When I fetch the data from the database, I’m likely to have an array that looks something like this:
$array[0]['id'] = 1005; $array[0]['title'] = 'Mr.'; $array[0]['first'] = 'Ted'; $array[0]['last'] = 'Smith'; $array[1]['id'] = 1006; $array[1]['title'] = 'Mr.'; $array[1]['first'] = 'Mark'; $array[1]['last'] = 'Jones'; $array[2]['id'] = 1007; $array[2]['title'] = 'Ms.'; $array[2]['first'] = 'Sally'; $array[2]['last'] = 'Adams';
I want to get this into a form structured as a table, that submits the whole thing as one form. So that the posted results like this:
$contacts[1005]['title'] = 'Mr.'; $contacts[1005]['first'] = 'Ted'; $contacts[1005]['last'] = 'Smith';
I want the array to look like this because its much easier to handle the results than if we used the actual field name or id as the variable name.
I couldn’t find anything in the Zend Framework documentation, lists, or various tutorials that explained how to do this very clearly, so I thought I’d post at least one way to accomplish this in Zend Framework. This is based on 1.5. I’m going to start by showing the whole thing together. In practice it might not appear this way in the context of your controller class, but hopefully this is clear enough to understand how to apply it. After I show the whole thing I’ll walk through it and explain the pieces.
$form = new Zend_Form(); $form->setMethod('post') ->setAttrib('id', 'contactForm'); $subForm = new Zend_Form_SubForm(); foreach($array as $rownum => $row){ $id = $row['id']; $rowForm = new Zend_Form_SubForm(); foreach($row as $key => $value){ if($key == 'id') continue; $rowForm->addElement( 'text', $key, array( 'value' => $value, ) ); } $rowForm->setElementDecorators(array( 'ViewHelper', 'Errors', array('HtmlTag', array('tag' => 'td')), )); $subForm->addSubForm($rowForm, $id); } $subForm->setSubFormDecorators(array( 'FormElements', array('HtmlTag', array('tag'=>'tr')), )); $form->addSubForm($subForm, 'contacts'); $form->setSubFormDecorators(array( 'FormElements', array('HtmlTag', array('tag' => 'tbody')), )); $form->setDecorators(array( 'FormElements', array('HtmlTag', array('tag' => 'table')), 'Form' )); $form->addElement( 'submit', 'submit', array('label' => 'Submit')); $form->submit->setDecorators(array( array( 'decorator' => 'ViewHelper', 'options' => array('helper' => 'formSubmit')), array( 'decorator' => array('td' => 'HtmlTag'), 'options' => array('tag' => 'td', 'colspan' => 4) ), array( 'decorator' => array('tr' => 'HtmlTag'), 'options' => array('tag' => 'tr')), ));
As you can see, this approach uses subforms, which allows us to automatically build the array in the form, as well as having a convenient structure for applying decorators. In order to get the array right, we use two layers of subforms (subForm for $contacts, and rowForm to create each row of the table). I couldn’t figure out a good way to not do that and still get the array I wanted in the html. setElementsBelongTo doesn’t quite do the trick.
$form = new Zend_Form(); $form->setMethod('post') ->setAttrib('id', 'contactForm'); $subForm = new Zend_Form_SubForm();
This just sets up the form, tells the form it should post and have an id of ‘contactForm’. You might note that I didn’t set an action for the form. Zend_Form does set the action, but leaves the value blank. This conveniently posts the form back to our action (supposing all the browsers handle blank actions as specified in the relevant RFCs). The $subForm line just creates our first subForm for use later.
foreach($array as $rownum => $row){ $id = $row['id']; $rowForm = new Zend_Form_SubForm(); foreach($row as $key => $value){ if($key == 'id') continue; $rowForm->addElement( 'text', $key, array( 'value' => $value, ) ); } $rowForm->setElementDecorators(array( 'ViewHelper', 'Errors', array('HtmlTag', array('tag' => 'td')), )); $subForm->addSubForm($rowForm, $id); }
For each row in our data we create a subForm, which we populate with an element for each field in our data, setting the value for each to the value from our data. We decorate this subForm in the loop, wrapping each element in a td tag. Note that we don’t need to worry about the name of the element. We just set it to our data field name, and the framework’s subForm handling deals with the rest. Of course in practice your fields might not all be simple text fields, so the actual code would get more complicated but the process is the same. I also should mention that in practice you almost certainly want to check that $array is actually an array before you do this, or you’ll be looking at lots of warnings (at least) in your logs.
$subForm->setSubFormDecorators(array( 'FormElements', array('HtmlTag', array('tag'=>'tr')), )); $form->addSubForm($subForm, 'contacts'); $form->setSubFormDecorators(array( 'FormElements', array('HtmlTag', array('tag' => 'tbody')), ));
Now that we have a rowForm for each data row added to our subForm, we can decorate each of these with a tr tag, and assign the whole thing back to the parent form. This is where we tell Zend_Form that the parent should be ‘contacts’, so that gets in our result array. Then we decorate the contacts subForm with the tbody tag to get it nicely fit into the table.
$form->setDecorators(array( 'FormElements', array('HtmlTag', array('tag' => 'table')), 'Form' ));
We need to decorate the whole form with a table tag. This likely needs a class and id, but we leave it plain in this example.
$form->addElement( 'submit', 'submit', array('label' => 'Submit')); $form->submit->setDecorators(array( array( 'decorator' => 'ViewHelper', 'options' => array('helper' => 'formSubmit')), array( 'decorator' => array('td' => 'HtmlTag'), 'options' => array('tag' => 'td', 'colspan' => 4) ), array( 'decorator' => array('tr' => 'HtmlTag'), 'options' => array('tag' => 'tr')), ));
Chances are we want a submit button, so here we add it to the bottom, and decorate it with its own row in the table.
The order of a surprising amount of this is arbitrary. You could set the table tag toward the top, for example, if it flows better for you. There are, of course, also completely different ways to do this. Several of the examples in the manual extend Zend_Form rather than do it the way that I have here. To me, this seems more clear.
[2010-02-06 I edited this slightly to fix some formatting issues introduced by a WordPress migration. After almost two years this post still gets visits, and I figured maybe it should be readable.]
Zend Framework
Well, I’m continuing to look at Zend Framework. It has a lot of nice features, and a not insignificant learning curve to get what I would need out of it. Add to this that I don’t have time in my current work schedule to play with it, so I’m doing it in my “free” time. I’d like it to be quicker to figure out. Not that any particular piece of it is hard, there’s just lots of it and each part has to be unraveled and then plugged into what I’ve learned of the whole. I am currently working with 1.5, which is actually a Release Candidate at the moment, but earlier versions don’t include everything I need. Since I’m not going to be ready to use this in anything like a production app for a little while at least (assuming I end up with this approach), hopefully it will be a stable release by then.
Since I’m exploring ZF, I want to get as much out of it using ZF as I can, rather than combining it with other approaches. It might turn out that using a combination of resources is the best approach (as Wil Sinclair suggested in response to my last post on this), but I don’t want to use that as a crutch not to figure out what I can get out of ZF.
Some of the things I want to be able to do include:
- Modular controller layout – I want to separate the controller code for various types of things. ZF allows for this pretty well. It took me a little fiddling to figure this part out, but most of that was me trying to get it to do variations. There are some pretty good articles about how to do this in addition to the documentation.
- Modular model layout – I need to support different data sources, often of very different types. For example, the same application might need to interact with Postgres, MySQL, and a SOAP service. For Drupal I wrote a dataconnector module, and then a library for each different data source that belongs to this module. Much of what I had to do to get Drupal to do this sort of thing already exists in ZF. This is going to take more investigation to do in a way that is maintainable and easy to manage, but I haven’t had a chance to spend much time on it. Its possible that the directory layout of putting the model files in a shared directory is ok. Just don’t know yet.
- Form and input validation – I’m probably going to write a post about this issue by itself soon. Its what I’m looking at now, and I’m not sure I’ve figured out the way that I think this would work best in the ZF context. Zend_Form has the server side validation nicely packaged. Actually, I’d like to do three levels of validation/filtering: at the client (for usability), at the controller for business logic and the first level of security filtering (this is what Zend_Form does), and at the model (for data integrity and injection avoidance). I need to investigate how I want to do model level filtering. That’s actually the easiest, I just haven’t looked at it yet in the ZF context. The database interaction layer isn’t sufficient for filtering by itself because at that level we don’t know all the uses we might be making of the fields. Just doing it at the controller layer isn’t sufficient because we don’t know that all data interactions will pass through any given controller, and we don’t want to miss anything. The next piece I’m going to investigate, though, is tying something like the jQuery validation plugin into Zend_Form.
- I still haven’t gotten to XML_RPC and SOAP. There actually isn’t a stand-alone SOAP component yet, but the StrikeIron service component has a fairly basic SOAP base class.
- Some core issues like logging and error (exception) handling. These are fairly boring, so I’ll put them off as long as I can.
- Zend_Translate and Zend_Locale. I haven’t looked at these at all, but I need to. Like the previous bullet, this isn’t my favorite piece. I’m putting it here in part to remind myself not to skip it.
- PDF creation. At FH (my day job) we have some stuff using PDFlib’s PPS libraries. I haven’t even thought about this yet.
- I haven’t tackled AJAX for forms yet, although I don’t expect this to be a big issue from what I’ve seen of Zend_Form so far. In forms that are an integrated part of business process, I like forms that save each field in the background as it is filled out. I’ve used Dojo to do this in the past, but I think I’ll move it all to jQuery (largely because of Drupal’s influence). Not that there’s anything wrong with Dojo. Its a great tool kit. Its great to have such good choices.
There’s lots more to put on this list, but that will do for now. Tune in next week, same bat time, same bat channel. Or some time, some where, maybe.
Frameworks
Over the years I’ve written a number of web applications basically from scratch. I almost always use PEAR and other libraries, but for the most part I’ve always used my own code as the basic framework. Last year I started building a number of modules for Drupal, including some that automate business processes (such as inventory and producing PDF documents for brochures based on live business data). This is convenient because these modules fit nicely into larger intranet type sites for the organizations that use them. But Drupal isn’t really designed to be an application framework. Since it isn’t intended to be that, they apparently feel free to completely redesign key parts of the API between versions, which really makes upgrading expensive.
So more recently I’ve been looking again at a couple of the frameworks that are available for PHP, this time Solar and Zend Framework. I’ve looked at these before, along with Cake. I didn’t care for Cake, and ZF and Solar weren’t ready yet last time I looked.
Solar and ZF are much closer to ready for real use now than they were last time I looked closely at them. Over the weekend I built a few simple prototype level things from both Solar and ZF. The short of it is that I really like Solar. It is very easy, and I found it very intuitive. I shouldn’t be surprised, Paul M Jones has a history of writing good stuff. I had a site running in very short order with Solar.
I think that the main problem with Solar is the size of the community. I need a number of the features that Zend Framework provides, but aren’t available for Solar yet, like SOAP, XMLRPC, RSS, support for MS SQL server connections (I’d rather not need that, but I do), and others. I could write them for Solar, but most of what I need is already available from ZF.
Zend Framework doesn’t have the conceptual consistency and elegance of Solar, and I’m having a hard time piecing together the bits of it that I need. Its sort of like building your own Linux from scratch, maybe something every Linux geek should do at least a couple of times but not something you want to do when all you need is a working computer to get stuff done. When you just need to get work done you want a Ubuntu or OpenSuse to package it for you so you can just get to work.
In the end, ZF is more like PEAR on steroids than it is the framework I want to save me time in piecing something together. Still, I think its the closest to what I need, and it has the momentum and support behind it to have a rosier future. So I guess the best approach would be to build my own blank application from the pieces I will commonly use, with all of the components I commonly will need bundled in already, and archive that as an application template.
Or maybe I’ll try Solar some more.
Or maybe I’ll just keep cooking my own.