ZF-6461: Split Zend_Db_Table_Row_Abstract::findDependentRowset to allow direct access to the Zend_Db_Table_Select object

Description

Zend_Db_Table_Row_Abstract::findDependentRowset find the child rows for a particular table for a Zend_Db_Table_Row object.

It does this by building a Zend_Db_Table_Select object and then calling Zend_Db_Table_Abstract::fetchAll to return the rows:

    public function findDependentRowset($dependentTable, $ruleKey = null, Zend_Db_Table_Select $select = null)
    {
        $db = $this->_getTable()->getAdapter();

         Build Zend_Db_Table_Select object $select 

        return $dependentTable->fetchAll($select);
    }

Sometimes it is more useful to have direct access to the $select object than it is to the rowset. My use case is using Zend_Paginator to display a paginated list of child rows in a master-detail screen. This works best (for us at least) if we have a Zend_Db_Table_Select representing the whole rowset, and we then allow Zend_Paginator to change the offset and limit clauses as required to bring back the required rows.

This is easily accomplished if we split the existing findDependentRowset into a method to build the $select and a separate method to return the rowset.

The new getDependentSelect is identical to the existing findDependentRowset except for the last line which returns the $select instead of a rowset:

    /**
     * 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;
    }

findDependentRowset now becomes a call to getDependentSelect:

    /**
     * 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 $select->getTable()->fetchAll($select);
    }

This allows my use case to work easily:

            // Create the child paginator
            $select = $row->getDependentSelect($child);
            $page = $this->_request->getParam('page');
            $paginator = Zend_Paginator::factory($select, 'DbTableSelect');
            $paginator->setCurrentPageNumber($page);
            $this->view->childPaginator = $paginator;

Comments

Might be useful to refactor the many-to-many and parent-row support as well.

Duplicate of ZF-4692