Page 1 of 1

Understanding models (zend framework) [long]

Posted: Fri Jun 26, 2009 5:53 am
by Stryks
Hey all. I'm not new to PHP, but I am very new to frameworks and any non homemade MVC setups. Of course, my home made ones are shoddy things at best, hence the decision to learn a tried and tested package.

Anyhow .... I'm wondering if anyone can help me out with understanding models.

I've gone through a few tutorials and I've seen two basic approaches to models. This may apply mainly to zend framework here, so bear with me.

The first method is what I more or less expected to find (code clipped from Akra’s DevNotes).

Code: Select all

<?php
 
// application/models/DbTable/Albums.php
 
class Model_DbTable_Albums extends Zend_Db_Table
{
    protected $_name = 'albums';
 
    public function getAlbum($id)
    {
        $id = (int)$id;
        $row = $this->fetchRow('id = ' . $id);
        if (!$row) {
                throw new Exception("Count not find row $id");
        }
        return $row->toArray();
    }
    
    public function addAlbum($artist, $title)
    {
        $data = array(
        'artist' => $artist,
        'title' => $title,
        );
        $this->insert($data);
    }
 
    function updateAlbum($id, $artist, $title)
    {
        $data = array(
        'artist' => $artist,
        'title' => $title,
        );
        $this->update($data, 'id = '. (int)$id);
    }
 
    function deleteAlbum($id)
    {
        $this->delete('id =' . (int)$id);
    }
}
I look at that and I think "Ahhh ... so THATS how it's supposed to work". I still have questions about how this works ... but more on that later.

Anyhow ... I then rock on up to the official zend quickstart, and I see the model like this ...

Code: Select all

<?php
// application/models/DbTable/Guestbook.php
 
/**
 * This is the DbTable class for the guestbook table.
 */
class Default_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
{
    /** Table name */
    protected $_name    = 'guestbook';
}

Code: Select all

<?php
// application/models/GuestbookMapper.php
 
class Default_Model_GuestbookMapper
{
    protected $_dbTable;
 
    public function setDbTable($dbTable)
    {
        if (is_string($dbTable)) {
            $dbTable = new $dbTable();
        }
        if (!$dbTable instanceof Zend_Db_Table_Abstract) {
            throw new Exception('Invalid table data gateway provided');
        }
        $this->_dbTable = $dbTable;
        return $this;
    }
 
    public function getDbTable()
    {
        if (null === $this->_dbTable) {
            $this->setDbTable('Default_Model_DbTable_Guestbook');
        }
        return $this->_dbTable;
    }
 
    public function save(Default_Model_Guestbook $guestbook)
    {
        $data = array(
            'email'   => $guestbook->getEmail(),
            'comment' => $guestbook->getComment(),
            'created' => date('Y-m-d H:i:s'),
        );
 
        if (null === ($id = $guestbook->getId())) {
            unset($data['id']);
            $this->getDbTable()->insert($data);
        } else {
            $this->getDbTable()->update($data, array('id = ?' => $id));
        }
    }
 
    public function find($id, Default_Model_Guestbook $guestbook)
    {
        $result = $this->getDbTable()->find($id);
        if (0 == count($result)) {
            return;
        }
        $row = $result->current();
        $guestbook->setId($row->id)
                  ->setEmail($row->email)
                  ->setComment($row->comment)
                  ->setCreated($row->created);
    }
 
    public function fetchAll()
    {
        $resultSet = $this->getDbTable()->fetchAll();
        $entries   = array();
        foreach ($resultSet as $row) {
            $entry = new Default_Model_Guestbook();
            $entry->setId($row->id)
                  ->setEmail($row->email)
                  ->setComment($row->comment)
                  ->setCreated($row->created)
                  ->setMapper($this);
            $entries[] = $entry;
        }
        return $entries;
    }
}

Code: Select all

<?php
// application/models/Guestbook.php
 
class Default_Model_Guestbook
{
    protected $_comment;
    protected $_created;
    protected $_email;
    protected $_id;
    protected $_mapper;
 
    public function __construct(array $options = null)
    {
        if (is_array($options)) {
            $this->setOptions($options);
        }
    }
 
    public function __set($name, $value)
    {
        $method = 'set' . $name;
        if (('mapper' == $name) || !method_exists($this, $method)) {
            throw new Exception('Invalid guestbook property');
        }
        $this->$method($value);
    }
 
    public function __get($name)
    {
        $method = 'get' . $name;
        if (('mapper' == $name) || !method_exists($this, $method)) {
            throw new Exception('Invalid guestbook property');
        }
        return $this->$method();
    }
 
    public function setOptions(array $options)
    {
        $methods = get_class_methods($this);
        foreach ($options as $key => $value) {
            $method = 'set' . ucfirst($key);
            if (in_array($method, $methods)) {
                $this->$method($value);
            }
        }
        return $this;
    }
 
    public function setComment($text)
    {
        $this->_comment = (string) $text;
        return $this;
    }
 
    public function getComment()
    {
        return $this->_comment;
    }
 
    public function setEmail($email)
    {
        $this->_email = (string) $email;
        return $this;
    }
 
    public function getEmail()
    {
        return $this->_email;
    }
 
    public function setCreated($ts)
    {
        $this->_created = $ts;
        return $this;
    }
 
    public function getCreated()
    {
        return $this->_created;
    }
 
    public function setId($id)
    {
        $this->_id = (int) $id;
        return $this;
    }
 
    public function getId()
    {
        return $this->_id;
    }
 
    public function setMapper($mapper)
    {
        $this->_mapper = $mapper;
        return $this;
    }
 
    public function getMapper()
    {
        if (null === $this->_mapper) {
            $this->setMapper(new Default_Model_GuestbookMapper());
        }
        return $this->_mapper;
    }
 
    public function save()
    {
        $this->getMapper()->save($this);
    }
 
    public function find($id)
    {
        $this->getMapper()->find($id, $this);
        return $this;
    }
 
    public function fetchAll()
    {
        return $this->getMapper()->fetchAll();
    }
}
After a fair amount of head scratching ... I think I get how this works.

application/models/Guestbook.php is pretty much an object version of the table, independent of the table itself.

application/models/GuestbookMapper.php is along the lines of the previous model style, but basically acts as an interface to convert the object to ...

application/models/DbTable/Guestbook.php - the straight Zend_Db_Table_Abstract object.

Some fumbling around has turned up that this is Object Relational Mapping.

My questions on this are basically ....

1. Why do it the second way? What benefits does it provide?

2. What about working with multiple tables .... such as displaying a SELECT with a JOIN. I've been told to create a model just pulling the SELECT data and then use have the controller open multiple models (i.e. the view for display, and whatever tables need changing for actions). I'm assuming that with either method I use I still have to open multiple models this way. This kind of leads me back to question one.

3. Well ... many more ... but let's stop here for the moment.

Any help anyone could give here would be great.

Cheers

Re: Understanding models (zend framework) [long]

Posted: Sat Jun 27, 2009 7:32 am
by Stryks
(24 hour bump)

*bump*

Re: Understanding models (zend framework) [long]

Posted: Tue Jun 30, 2009 12:31 pm
by allspiritseve
Sorry I don't have much time, but the first one is a TableDataGateway and the second is a DataMapper. A big difference between them is that TDG generally works with arrays, whereas DM works with objects. TDG (or ActiveRecord) are good for when your objects/properties map 1:1 to tables/columns. They essentially give you an object oriented way of working with database tables. They are simple and easy, but tend to fall apart when working with relations and objects that don't map 1:1 to tables. Hope that helps.

Re: Understanding models (zend framework) [long]

Posted: Tue Jun 30, 2009 12:52 pm
by BornForCode
DataMapper moves data between objects and a database while keeping them independent of each other and the mapper itself. The desired loosely coupling is achieved.

A TableDataGateway is a 'Gateway' (object that encapsulates access to an external system or resource) to a database table. One instance handles all the rows in the table.

The Table Data Gateway is used mainly for a single table or view. It contains all the selects, inserts, updates, and deletes. So Customer is a table or a view in your case. So, one instance of a table data gateway object handles all the rows in the table. Usually this is related to one object per database table.

While Data Mapper is more independent of any domain logic and is less coupled (although I believe either there is coupling or not coupling). It is merely a intermediary layer to transfer the data between objects and a database while keeping them independent of each other and the mapper itself.

So, typically in a mapper, you see methods like insert, update, delete and in table data gateway you will find getcustomerbyId, getcustomerbyName, etc.

Now shortly said first method is preferred but due the deadlines i often choose the second one :twisted:

Re: Understanding models (zend framework) [long]

Posted: Tue Jun 30, 2009 2:10 pm
by Christopher
BornForCode wrote:So, typically in a mapper, you see methods like insert, update, delete and in table data gateway you will find getcustomerbyId, getcustomerbyName, etc.
Actually in all three patterns you will see all of those methods. This difference is:

- The ActiveRecord object is the database record, so you tell the object to load and save itself.

- TableDataGateway is a data gateway to the table, so it loads and saves an array containing one or more row records.

- DataMapper is a gateway between one or more objects and the database (not just a single table), so it is responsible for moving data back and forth between the objects and the database.

Re: Understanding models (zend framework) [long]

Posted: Wed Jul 01, 2009 1:12 am
by lotus
How can i get to know the way Db autoloader is registered in framework. If i want to get a new autoloader like Db how can i register?
Urghhh !!! is there any other reference to get understanding on zf version 1.8.4. I went through the quick start but i'm still confusing.
Thanks.

Re: Understanding models (zend framework) [long]

Posted: Wed Jul 01, 2009 3:34 am
by BornForCode
If you would like to add autoload for you models, welcome to the ZF nightmare.

Anyway, if you are using a modular approach for zend framework you will have to do add this in your bootstrap file:

Code: Select all

 
protected function _initAutoload()
    {
        $moduleLoader = new Zend_Application_Module_Autoloader(array(
            'namespace' => 'Default',
            'basePath' => APPLICATION_PATH.'/modules/default'));
        return $moduleLoader;
    }
 
The above code fits for Default module located in application/modules/default.
Your models class naming strategy should be: Default_Model_User for example.