Introduction to Zend_Db
Modelling Tables
Zend_Db_Table is a class that represents a table. You instantiate one of these and call query functions against it. The actual code for my classes is really minimal, here’s an example:
class UserTable extends Zend_Db_Table
{
protected $_name = 'users';
protected $_rowClass = 'User';
}
The two properties that I’m setting here are all I use for basic tables. $_name tells Zend Framework which database table to use. $_rowClass tells it which class it should instantiate for the results from your select query. By default you’ll get Zend_Db_Table_Row objects but you can have it use any object which extends this class instead.
Getting Results
To get results from a table, you instantiate the relevant table class, and then fetch some results. You can restrict the results you get but Zend_Db_Select is a whole other kettle of fish that perhaps I’ll post about some other day. So a controller action might look something like this:
function viewAction()
{
$users = new UserTable();
$user_list = $users->fetchAll();
}
The $user_list variable will then hold an instance of Zend_Db_Rowset.
Working with Rowsets
The rowsets are iterators, you can loop over them and they will return objects which represent the rows in the resultset of the query. By default these are objects of type Zend_Db_Table_Row but if you set the table object’s rowClass, either in the object declaration or by calling setRowClass() before you fetch the results, then you can have any class which extends Zend_Db_Table_Row used. Its pretty common to pass the rowset straight to the view and iterate over it there – you can put any functionality you need for this object into the class you use. Let’s look at my user class for this example.
The Row Class
class User extends Zend_Db_Table_Row
{
public function getName()
{
return $this->first_name . ' '.$this->last_name;
}
public function getProfileUrl()
{
return '/users/profile/id/' . $this->id;
}
}
These are overly simple examples, but giving this kind of functionality to the user model allows it to be re-used any place you need it. I’ve also found it useful to have an intermediate table row class that contains functionality that will be used by data from more than one table – for example for formatting dates in a consistent manner.
Zend Framework and Models
This is a pretty simple overview but it took me a while to get to this point – the framework does have documentation but its quite case-specific and doesn’t really show how it ties together. Now I understand these classes and their relationships, my development project is going a lot more smoothly.
thx lorna, nice and short introduction. i wasn’t aware of the table-row-class… this will make my devlife a lot easier!
Yuck :) I really can’t see the point of designing an ORM that ends up looking like that.
I don’t think it’s meant to be an ORM
butters: glad to hear it helped :)
Stuart: The very nature of what I do means I work with whatever I need to, and I don’t get to choose! I’d be interested to see examples of how it works in other systems though
Thank you for this post. It’s very clear and helped solidify my thinking of the workflow from the database to read an example that is linearly stated.
Zend_Db_Table is not a model class and never should be used strictly as a model class. It may seem convenient to work directly with the table classes in your controller but will only get you in trouble in the end. You should either construct your own model classes which use Zend_Db_Table or you can read this: http://www.zacharysnow.com/2008/06/20/zend_db_table-as-a-model/ for a semi work around.
Interesting site on ORM:
http://ifacethoughts.net/2008/11/06/on-misusing-orms/
diamondTears: glad to have cleared things up a bit.
Zachary: I am now further into my project than I was when I wrote this post and I repent entirely the decision to inherit from the table_row classes!! I’ll be writing another post when I’ve settled a bit more with the whole concept but suffice to say I’ve got ordinary models with SQL or use of Zend_Db stuff in the various methods and that is working much better! They do say “you live and learn” and I did on this one :)
Simon: Thanks for sharing the link. I agree that ORMs can be more hazard than help – especially since I know a lot more about databases than about PHP frameworks.
I have made the same mistake on my project(before I even read this post, so this is not your fault :-) ). I inherit EVERY classes from the table_row classes.
Now I can see there is something wrong with the whole concept but I’m too deep in the projetc to change that. Anyway I would be glad to hear more from you about that.
Tanks for this Post
The thing that gets me is this: in any non-trivial project, a model doesn’t just interact with MySQL. Models end up in caching layers, in sessions, and interacting with users through forms, query parameters, and of course APIs.
Given all that, any sort of model that is designed around tables and rows just seems ultimately to be missing the point :)
Best regards,
Stu
Great post. I love it when someone simplifies things like this. thank you
Thanks for this posting. I would like to question your assertion that you should have an intermediate class functions such as date formatting. Surely this kind of function should be implemented as a method on an independent helper class. Do you agree?
Karim: You make a good point. In general, if I’m writing formatting code that is specific to a particular object, I’ll make it a method of the object, just to keep everything together. But if its something like formatting a date which will happen in lots of places, then yes I would want to use a helper. The examples in my post were really too simple – just because I wanted to give an outline without any distractions
Therein lies the rub. When you do ORM at this level it seems almost mechanical until you realize that it introduces a lot of application layer cruft in your more complex models that could have been done by queries.
I’m pretty good at queries, and while I ordinarily say don’t worry about preformance at first, the thing will preform better ultimately if we let the db server do complex queries rather than selecting a whole bunch of stuff and filtering it through a lot of these table objects in the app. When you do it this way you hardwire it into your model at a low level and it is not so easy to optimize.
hey Lorna – found this quite helpful.
cheers,
Matt