PHP Modeling (in Zend Framework)

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:

  1. Installing Zend Server & Zend Framework on OS X This post is mostly a reminder to myself, but I...
  2. More PHP Modeling (w/ video demo) [Updated with follow-up video] About a month ago I posted...
  3. Zend Framework: Using separate layouts per module Someone was recently asking on ZFTalk about how to use...
  4. Better Zend Framework Documentation If you’ve every tried to navigate the Zend Framework documentation’s...
  5. Review: Zend Framework 1.8 Web Application Development At the beginning of February PACKT Publishing sent me a...
This entry was posted in Web Development, Zend Framework and tagged , , , , , . Bookmark the permalink.
  • jazz

    Hi,I’m so interesting in your modeling in ZF, but can you support a simple code ?
    Many thanks!

    • http://cmorrell.com Chris Morrell

      Yeah—I’ll put some sample code up once I have things figured out a little more.

  • http://twitter.com/cyberpet Peter

    I use an similar approach (even the entity and collection thing) and I really like your thoughts on it.
    But how do you handle relations between models/mappers? e.g. I use a posts model that depends on users, artists, comments, votings, images… That drives me nuts ;-)

    • http://cmorrell.com Chris Morrell

      Once you’re dealing with relatively complex relationships, I think any general modeling system is going to start to fail for you. At that point you’re going to have to give up on the shortcuts and develop models that work exactly the way you need them to. That said, I think a simple solution to relationships is to just have your models contain the other models they’re related to and deal with the performance hit with caching. For example, if your Post object needs to know about the post’s author, why not just have a setAuthor(User $author) method on your Post object? Or maybe you don’t want to force a User object as the parameter, and let it lazy load that relationship later if its needed (for example, your setAuthor() method could either accept a User object or an integer (the user_id)—If it gets an integer, then your getAuthor() method would ask your Service_User object to fetch it a User object based on the user_id provided before returning it). Sure, you could get better performance out of a carefully tweaked SQL query for each situation, but once you’re really worrying about performance you’re going to have to refactor everything anyway…

  • http://twitter.com/cyberpet Peter

    It seems we share the same ideas so can’t be that wrong ;-) Thanks for your thoughts.
    Right now I use lazy loading dependent objects via getUser() getArtist() and so on. Every method of those returns a single entity of e.g. a user (which has an getProfil() itself for a profile object and so on).
    I’m always afraid of having too many dependencies and the lack of optimized sql queries.
    You’re probably right, there won’t be the one perfect modeling system for complex real world scenarios.

  • http://lcf.name lcf

    Check out the Doctrine 2.0 it is domain driven and really flexible. It provides as well lazy loading without involving domain objects themselves (as you both do – wrong) by Proxy classes.
    There is a couple of ‘BUT’ though – it’s in alpha yet (but actively developed) and it’s for php 5.3 only.

    • Chris Morrell

      Doctrine 2 seems to be the best ORM option out there, and I’ll definitely take a closer look at it when it’s released. It’s also worth mentioning that Doctrine is likely to become the “preferred” ORM for use with Zend Framework, and they’ll probably work on ensuring that they work together well. Probably when ZF 2.0 and Doctrine 2.0 are out they will be a killer combination.

      My system is much simpler than Doctrine, and might make more sense when you don’t need a full-blown ORM.

  • Pingback: "PHP Modeling (in Zend Framework)" by Chris Morrell | Source code bank

  • Pingback: "More PHP Modeling (w/ video demo)" by Chris Morrell

  • http://twitter.com/3ngineers ClarionDoor

    I really like you’re ideas about DAO in Zend Framework. We use classes with a naming convention of Application_Model_DataAccess_* which all implement a common interface and extend Zend_Db_Table_Abstract or do their own logic for implementing persistence (filesystem as an example). One tip is that Zend Framework already provides autoloading support for classes named Application_Service_Example in /application/services/Example.php and Application_Model_Mapper_Example in /application/models/mappers/Example.php.