How to easily create Models and Table Relationships in Zend Framework

Matt McCormick

2010/04/24

When I first started using Zend Framework, I was so frustrated when trying to figure out how to get information from the database. I fought against the framework for the longest time. Instead of working with it, I would write the SQL and then simply query the database to get the data back as an array of objects.

Later on I learned at just how powerful Zend Framework can be when it comes to retrieving models. It is actually easier and more fun to do things in the “Zend” way.

This is the post I wish I had read before spending hours going about things the wrong way.

I’m using Zend Framework for a project which has videos, users (each user can have many videos) and tags (many tags to many videos). This example will show you how to setup your models correctly:

/application/models/DbTable/Videos.php

class Model_DbTable_Videos extends Zend_Db_Table_Abstract
{
	protected $_name = 'videos';	// database table name
	protected $_rowClass = 'Model_Row_Video';	// row class for extending
	protected $_dependentTables = array('Model_DbTable_VideoTag');	// videos depends on the many-to-many join table for tags

	protected $_referenceMap = array(
		'User' => array(
			'columns' => 'user_id',	// the column in the 'videos' table which is used for the join
			'refTableClass' => 'users',	// the users table name
			'refColumns' => 'id'	// the primary key of the users table
		)
	);
}

/application/models/DbTable/User.php

class Model_DbTable_Users extends Zend_Db_Table_Abstract
{
	protected $_name = 'users';
	protected $_rowClass = 'Model_Row_User';
	protected $_dependentTables = array('Model_DbTable_Videos');
}

/application/models/DbTable/Tags.php

class Model_DbTable_Tags extends Zend_Db_Table_Abstract
{
    protected $_name = 'tags';
    protected $_rowsetClass = 'Model_Rowset_Tags';
}

/application/models/DbTable/VideoTag.php (the join table for many-to-many relationship)

class Model_DbTable_VideoTag extends Zend_Db_Table_Abstract
{
    protected $_name = 'video_tag';
    protected $_referenceMap = array(
		'Video' => array(
			'columns' => 'video_id',
			'refTableClass' => 'Model_DbTable_Videos',
			'refColumns' => 'id'
   		),
   		'Tag' => array(
   			'columns' => 'tag_id',
   			'refTableClass' => 'Model_DbTable_Tags',
   			'refColumns' => 'id'
   		)
	);
}

The protected fields allow you to tell the framework the setup of the tables. You can also extend the Row on Rowset by simply setting the $_rowClass or $_rowsetClass field.

In Model_DbTable_Videos, you see I have specified the $_rowClass field. This means any row returned will be an object of Model_Row_Video. In the class Model_Row_Video, I have added extra methods for easily retrieving the user of the video and tags belonging to that video:

/application/models/Row/Video.php

class Model_Row_Video extends Zend_Db_Table_Row_Abstract
{
	private $tags = null;
	private $user = null;

	/**
	 * @return Model_Row_User
	 */
	public function getUser()
	{
		if (!$this->user) {
			$this->user = $this->findParentRow('Model_DbTable_Users');
		}

		return $this->user;
	}

	/**
	 * @return Model_Rowset_Tags
	 */
	public function getTags()
	{
		if (!$this->tags) {
			$this->tags = $this->findManyToManyRowset(
				'Model_DbTable_Tags',	// match table
				'Model_DbTable_VideoTag');	// join table
		}

		return $this->tags;
	}
}

Because I have all the relationships setup, I simply need to call findParentRow() or findManyToManyRowset() to get the Row or Rowset of a related record.

Here is an example for the Rowset:

/application/models/Rowset/Tags.php

class Model_Rowset_Tags extends Zend_Db_Table_Rowset_Abstract
{
	/**
	 * @return array the tags in an array
	 */
	public function getAsArray()
	{
		$tags = array();

		while ($this->valid()) {
			$tag = $this->current();
			$tags[] = $tag->name;  // the actual tag name
			$this->next();
		}

		$this->rewind();

		return $tags;
	}
}

So to get the tags of the video with ID 23 all I need to do now is simply:

$tblVideo = new Model_DbTable_Videos();
$video = $tblVideo->find(23)->current();  // returns Model_Row_Video
$tagsArr = $video->getTags()->getAsArray();

Working with Zend Framework becomes very easy and pleasurable once you follow their setup.

Refer to the Zend_Db section of the manual for more information about working with the database.