The core of any modern web application is interaction with the database. If the Zend Framework is chosen for the implementation of the application, we will inevitably encounter the use of the Zend_Db_Table_Abstract class. This class successfully implements the CRUD functions necessary for working with the database, but, nevertheless, this construction contains one drawback.
Consider the following database structure:

')
In this database two entities are presented - news and comments to the news. Obviously, a lot of comments can be left to the news.
We set ourselves the task - to select all the news and comments to them and pass the information into the presentation. How to approach it from the point of view of Zend_Db_Table_Abstract? Suppose we have the appropriate models - News and Comments.
Select all the news is quite simple:
$News= new News();
$news=$News->fetchAll();
* This source code was highlighted with Source Code Highlighter .
Then, following the logic, it is necessary in a cycle to bypass all the news and select for each news all comments belonging to it. This is where the main question arises - where to write an array of objects with comments?
I really want to do this:
foreach ($news as $record)
{
$comments=$Comments->fetchAll(“news_id=”.$record->news_id);
$record->comments=$comments;
}
$ this ->view->news=$news;
* This source code was highlighted with Source Code Highlighter .
But we get an exception saying the
column “comments” is not in the row . The fact is that each $ record variable is an object of type Zend_Db_Table_Row_Abstract. Trying to assign its comments property to an array (in fact, an object of type Zend_Db_Table_Rowset_Abstract) we call the __set () method. Inspect it:
public function __set($columnName, $ value )
{
$columnName = $ this ->_transformColumn($columnName);
if (!array_key_exists($columnName, $ this ->_data)) {
require_once 'Zend/Db/Table/Row/Exception.php' ;
throw new Zend_Db_Table_Row_Exception( "Specified column \"$columnName\" is not in the row" );
}
$ this ->_data[$columnName] = $ value ;
$ this ->_modifiedFields[$columnName] = true ;
}
* This source code was highlighted with Source Code Highlighter .
It becomes clear that the assignment is possible only if the property is registered in the $ _data array. Well, it gets to the date array by studying the meta information of the tables involved in the query. Obviously, in our case, the comments field will never be there.
Finally we got to solve the problem. The worst thing that can be done is to correct the problem directly in the code of the Zend_Db_Table_Row_Abstract class and throw out the check for registration in the $ _data array. Naturally, we will not do this; we will prefer OOP. All you need to do is create your own Row and Rowset classes.
Here is the code for the new Row class (put it in the
/library/Main/Db/Table/Row.php file):
<?php
class Main_Db_Table_Row extends Zend_Db_Table_Row
{
public function newDataProperty($ value )
{
if (!array_key_exists($ value ,$ this ->_data))
$ this ->_data[$ value ]= "" ;
}
}
* This source code was highlighted with Source Code Highlighter .
Here is the code for the new Rowset class (
/library/Main/Db/Table/Rowset.php file):
<?php
class Main_Db_Table_Rowset extends Zend_Db_Table_Rowset
{
function init()
{
$ this ->_rowClass= 'Main_Db_Table_Row' ;
}
}
* This source code was highlighted with Source Code Highlighter .
That's not all, now let's make our models use these classes. On the example of the News model:
<?php
class News extends Zend_Db_Table_Abstract
{
protected $_name= 'news' ;
protected $_primary= 'news_id' ;
function init()
{
$ this ->setRowClass( 'Main_Db_Table_Row' );
$ this ->setRowsetClass( 'Main_Db_Table_Rowset' );
}
}
* This source code was highlighted with Source Code Highlighter .
Everything is almost ready. The final touch, change the code for adding comments to the news item:
foreach ($news as $record)
{
$comments=$Comments->fetchAll(“news_id=”.$record->news_id);
$record->newDataProperty('comments');
$record->comments=$comments;
}
$ this ->view->news=$news;
* This source code was highlighted with Source Code Highlighter .
It works now!