I’ve been thinking a lot about Modeling in a MVC application, particularly in the Zend Framework. Obviously each application is different, and any Model is going to be fairly unique to your application. That’s why ZF doesn’t provide a base Model class. That said, there are some design patterns that a lot of people are using nowadays, and applications could use some base functionality to facilitate those patterns.
Zend Framework’s project lead, Matthew Weier O’Phinney, has a lot of great thoughts about Modeling that I’ve been trying to stick to. In implementing those ideas, I’ve started thinking out some base classes to build my Models on top of. Obviously these classes won’t work for everyone. But they should work for a lot of “typical” web applications.
That said, here are some notes that I’ve been putting together. I’m posting them now (very early) in hopes that I get some feedback.
The 6 Classes
Galahad_Model
This is the base class that all my others extend. It is there mostly to provide some helper functionality that all my other classes use (mostly for guessing class names based on a naming convention—more on that later).
Galahad_Model_Entity
The Entity is the base for all things. For example, a User would extend the Entity class. The Galahad_Model_Entity class has some basic methods for getting other objects. I haven’t thought this entirely through yet, but an example would be the Entity’s “Parent” Service (see below) or maybe a Form associated with that model or something similar.
Galahad_Model_Collection
The Collection class is just a wrapper for an array of Entities. I like this because it allows for type hinting/etc. This is pretty much a generic wrapper for an array that implements Iterator and Countable. I can’t think of much more that it needs.
Galahad_Model_Service
This probably doesn’t belong as part of the “Model”—I need to think that out. The Service Layer is where your application logic happens that’s not strictly part of a particular Model (for example, an interaction between two Models). An example might be authentication. The service layer will often map pretty closely to a public API (although obviously there will be things that your application can do that shouldn’t be exposed to the public).
Galahad_Model_DataMapper
The DataMapper maps your Entity to the appropriate DAO (see below) and makes sure the DAO gets the data it’s expecting. The way I think about this is that the Data Mapper expects an Entity as its input, but passes an array to the DAO.
Galahad_Model_Dao
The Data Access Object (DAO) is what takes the actual data in your entity and persists it. The most common DAO is going to be a database, but a web service could be a DAO as could a filesystem or any other method of persisting data. The DAO is going to have similar methods as the DataMapper, but it expects just the data—nothing else (that’s why you need the DataMapper to fetch and process the data in your Entity). An easy way to show this is in code:
class Default_Model_DataMapper_User
extends Galahad_Model_DataMapper
{
public function save(User $user)
{
$dao = $this->_getDao();
$dao->save(array(
'name' => $user->getName(),
'email' => $user->getEmail(),
'date_modified' => time(),
));
}
}
class Default_Model_Dao_DbTable_User
extends Zend_Db_Table_Abstract
implements Galahad_Model_Dao_Interface
{
protected $_name = 'user';
public function save(Array $data)
{
$this->insert($data);
}
}
Notes
I’m going to reiterate one more time that this is a very early concept and that I’m looking for feedback. It could be that I’m thinking about things completely backwards and it all needs to be thrown away. At this point I certainly don’t recommend people running too far with these ideas until they’ve been discussed/thought out a little more.
About the Galahad_Model base class—since all the different pieces of your model will likely follow the same naming conventions, the Galahad_Model class provides some helper functionality to guess the name of classes. For example, inside of Default_Model_DataMapper_User you’ll probably need to get an instance of Default_Model_Dao_DbTable_User. So Galahad_Model_DataMapper provides a nice getDao() method to do this for you. If you’ve set the DAO, it uses that, but if you didn’t, it assumes you want a DbTable, guesses the name for you based on the DataMapper’s class name (so that if you choose My_Model_DataMapper_Person it’ll know to return a My_Model_Dao_DbTable_Person) and instantiates it for you.
Thoughts?
What do you think? Does this make any sense? Or am I trying to make a simple thing more complicated than it needs to be? I think a lot of this would make more sense in code, so maybe I’ll try to get what I have started cleaned up a little and attach it to this post. In the meantime, I’d love feedback (in the comments below, or to @inxilpro.
Related posts:
- Installing Zend Server & Zend Framework on OS X This post is mostly a reminder to myself, but I...
- More PHP Modeling (w/ video demo) [Updated with follow-up video] About a month ago I posted...
- Zend Framework: Using separate layouts per module Someone was recently asking on ZFTalk about how to use...
- Better Zend Framework Documentation If you’ve every tried to navigate the Zend Framework documentation’s...
- Review: Zend Framework 1.8 Web Application Development At the beginning of February PACKT Publishing sent me a...
Pingback: "PHP Modeling (in Zend Framework)" by Chris Morrell | Source code bank
Pingback: "More PHP Modeling (w/ video demo)" by Chris Morrell