ZF-4692: Create new function Zend_Db_Table_Row->getDependentSelect()
Description
Zend_Db_Table_Row->findDependentRowset() returns a rowset from a linked table constrained by a foreign key. It does this by creating a Zend_Db_Table_Select object and passing it to Zend_Db_Table->fetchAll.
findDependentRowset() accepts a Zend_Db_Table_Select as an optional parameter, which we can use to further restrict the records returned.
At present when we find the dependent rowset we must query all rows at once (fetchAll). It would be better if we could just retrieve the Zend_Db_Table_Select representing the query and then execute it later. In particular this will allow us to set the ->limit() clause before running the query and thus only retrieve the rows we are interested in.
My use case is that I want to pass the child rows of a table to Zend_Paginator to display in a master-detail view:
// Get the child rows for display
$childTab = $this->_request->getParam('childtab', $table->getDefaultChild());
if (isset($childTab)) {
// Send variables to the view
$this->view->childTab = $childTab;
$select = $row->getDependentSelect($childTab);
$paginator = Zend_Paginator::factory($select, 'DbTableSelect');
$paginator->setCurrentPageNumber($this->_request->getParam('page'));
$this->view->childPaginator = $paginator;
}
Looking at the documentation, using Zend_Db_Table_Select is the preferred way of integrating Zend_Db with Zend_Paginator, because we can use the limit() clause to avoid returning all the rows when we only want to display 10 of them. Zend_Paginator_Adapter_DbTable returns a Zend_Db_Table_Rowset from getItems().
This function would allow the use of Zend_Db_Table_Row and Zend_Db_Table_Rowset functions for handling relationships while retaining the performance benefits of a SELECT ... LIMIT statement.
The code changes required are trivial and do not alter existing functionality, the existing findDependentRowset is split into two portions: 1) build the select statment, 2) execute the fetchall. E.g.
/**
* Generate a select statement to query a dependent table to retrieve rows matching the current row.
*
* @param string|Zend_Db_Table_Abstract $dependentTable
* @param string OPTIONAL $ruleKey
* @param Zend_Db_Table_Select OPTIONAL $select
* @return Zend_Db_Table_Select Query result from $dependentTable
* @throws Zend_Db_Table_Row_Exception If $dependentTable is not a table or is not loadable.
*/
public function getDependentSelect($dependentTable, $ruleKey = null, Zend_Db_Table_Select $select = null)
{
$db = $this->_getTable()->getAdapter();
if (is_string($dependentTable)) {
try {
@Zend_Loader::loadClass($dependentTable);
} catch (Zend_Exception $e) {
require_once 'Zend/Db/Table/Row/Exception.php';
throw new Zend_Db_Table_Row_Exception($e->getMessage());
}
$dependentTable = new $dependentTable(array('db' => $db));
}
if (! $dependentTable instanceof Zend_Db_Table_Abstract) {
$type = gettype($dependentTable);
if ($type == 'object') {
$type = get_class($dependentTable);
}
require_once 'Zend/Db/Table/Row/Exception.php';
throw new Zend_Db_Table_Row_Exception("Dependent table must be a Zend_Db_Table_Abstract, but it is $type");
}
if ($select === null) {
$select = $dependentTable->select();
} else {
$select->setTable($dependentTable);
}
$map = $this->_prepareReference($dependentTable, $this->_getTable(), $ruleKey);
for ($i = 0; $i < count($map[Zend_Db_Table_Abstract::COLUMNS]); ++$i) {
$parentColumnName = $db->foldCase($map[Zend_Db_Table_Abstract::REF_COLUMNS][$i]);
$value = $this->_data[$parentColumnName];
// Use adapter from dependent table to ensure correct query construction
$dependentDb = $dependentTable->getAdapter();
$dependentColumnName = $dependentDb->foldCase($map[Zend_Db_Table_Abstract::COLUMNS][$i]);
$dependentColumn = $dependentDb->quoteIdentifier($dependentColumnName, true);
$dependentInfo = $dependentTable->info();
$type = $dependentInfo[Zend_Db_Table_Abstract::METADATA][$dependentColumnName]['DATA_TYPE'];
$select->where("$dependentColumn = ?", $value, $type);
}
return $select;
}
/**
* Query a dependent table to retrieve rows matching the current row.
*
* @param string|Zend_Db_Table_Abstract $dependentTable
* @param string OPTIONAL $ruleKey
* @param Zend_Db_Table_Select OPTIONAL $select
* @return Zend_Db_Table_Rowset_Abstract Query result from $dependentTable
* @throws Zend_Db_Table_Row_Exception If $dependentTable is not a table or is not loadable.
*/
public function findDependentRowset($dependentTable, $ruleKey = null, Zend_Db_Table_Select $select = null)
{
$select = $this->getDependentSelect($dependentTable, $ruleKey, $select);
return $dependentTable->fetchAll($select);
}
Comments
Posted by Roger Hunwicks (rhunwicks) on 2009-04-29T00:22:45.000+0000
There is a small(!) error in my findDependentRowset - it breaks if you pass in a string $dependentTable instead of a Zend_Db_Table_Abstract one. The correct code should be:
Posted by Roger Hunwicks (rhunwicks) on 2009-04-29T00:25:39.000+0000
Sorry - I created a new issue ZF-6461 because I forgot I had already logged it as ZF-4692.