Issues

ZF-5906: Multi rowclass support in Zend_Db_Table_Abstract and Zend_Db_Table_Rowset_Abstract

Issue Type: New Feature Created: 2009-02-27T03:29:02.000+0000 Last Updated: 2012-11-20T21:37:32.000+0000 Status: Open Fix version(s): Reporter: Tommy Mattila (lup0) Assignee: None Tags: - Zend_Db_Table

Related issues: Attachments:

Description

Rows from a database table should have the possibility to be instantiated as different types of objects depending on the values in the database row. Currently Zend_Db_Table_Abstract and Zend_Db_Table_Rowset_Abstract do not enable it.

I suggest that a protected method called getRowClass(array $data = array()) is added to both the _Table and Rowset abstract classes.

The methods to be modified are the following (changes made against ZF 1.7.2): -Zend_Db_Table_Abstract -- public function createRow(array $data = array(), $defaultSource = null) Replace lines

<pre class="highlight">
@Zend_Loader::loadClass($this->_rowClass);
$row = new $this->_rowClass($config);

With

<pre class="highlight">
$className = $this->_getRowClass($data);
$row = new $className($config);

-- public function fetchRow($where = null, $order = null) Replace lines

<pre class="highlight">
@Zend_Loader::loadClass($this->_rowClass);
return new $this->_rowClass($data);

With

<pre class="highlight">
$className = $this->_getRowClass($rows[0]);
return new $className($data);

-Zend_Db_Table_Rowset_Abstract -- public function current() Replace lines

<pre class="highlight">
$this->_rows[$this->_pointer] = new $this->_rowClass(
  array(
    'table'    => $this->_table,
    'data'     => $this->_data[$this->_pointer],
    'stored'   => $this->_stored,
    'readOnly' => $this->_readOnly
  )
);

With

<pre class="highlight">
$className = $this->_getRowClass($this->_data[$this->_pointer]); // Find the correct class name
$this->_rows[$this->_pointer] = new $className(
  array(
    'table'    => $this->_table,
    'data'     => $this->_data[$this->_pointer],
    'stored'   => $this->_stored,
    'readOnly' => $this->_readOnly
  )
);

Both Zend_Db_Table_Abstract and Zend_Db_Table_Rowset_Abstract should contain the following method, which then can be overridden in inheriting classes.

<pre class="highlight">
/**
 * Returns a string matching the class name for this data. Default implementation returns the classname
 * given in ROW_CLASS configuration option.
 * @param array $data
 * @return string Name of the class matching the given data.
*/
protected function _getRowClass(array $data = array())
{
  @Zend_Loader::loadClass($this->_rowClass);
  return $this->_rowClass;
}

Comments

Posted by Tommy Mattila (lup0) on 2009-02-27T04:12:21.000+0000

Example usage:

<pre class="highlight">
class My_Entity_Rowset extends Zend_Db_Table_Rowset_Abstract
{
  protected $_myClassMap = array("default" => "My_Entity_Default", "nice" => "My_Entity_Nice");

  protected function _getRowClass(array $data = array())
  {
    $ret = $_this->_rowClass;
    if(isset($data["type"]) {
      $type = $data["type"];
      if(isset($this->_myClassMap[$type]) {
        $ret = $this->_myClassMap[$type];
      }
  }
  return $ret;
}

Posted by Martin Mayer (martin.mayer) on 2009-03-05T13:34:33.000+0000

Imho this feature should be left for userland code only where it's easy to implement. This is useful just for a few special cases.

Posted by Tommy Mattila (lup0) on 2009-03-05T22:19:20.000+0000

The trouble with implementing this feature in userland code is that we lose compatibility with Zend Framework. Because PHP doesn't allow casting of objects we cannot call for example the fetchRow or createRow methods of the Zend_Db_Table_Abstract. We need to directly instantiate the correct implementing class. Between ZF 1.5 and 1.7 the implementation of Zend_Db_Table_Abstract::createRow() and its dependencies were modified so much that it broke the compatibility between my createRow() implementation and the rest of Zend_Db_Table_Abstract.

Posted by Martin Mayer (martin.mayer) on 2009-03-06T02:43:33.000+0000

I still think it easy to do in user code and keep "compatibility" with ZF, simply by overriding Zend_Db_Table_Abstract::createRow() like this:

<pre class="highlight">
public function createRow(array $data = array(), $defaultSource = null)
{
    $oldRowClass = $this->_rowClass;
    if ($data['attr'] == 'foo') {
        $this->_rowClass = 'My_New_Row_Class';
    }
    $row = parent::createRow($data, $defaultSource);
    $this->_rowClass = $oldRowClass;
    return $row;
}

Though the implementation of Zend_Db_Table_Abstract::createRow() changed, it still works.

Posted by Tommy Mattila (lup0) on 2009-03-10T01:48:49.000+0000

You are right Martin that the code you showed enables us to keep Zend_Db_Table_Abstract's createRow() intact. But it doesn't work for fetchRow() because the data is retrieved inside the method the object is instantiated. Similar problems occur with the Zend_Db_Table_Rowset_Abstract::current() method.

Posted by Gerard Brouwer (gerard.brouwer1) on 2010-04-20T12:28:00.000+0000

I've the exact same need for this feature. It would be nice if it could be implement as suggested by Tommy!

Have you found an issue?

See the Overview section for more details.

Copyright

© 2006-2016 by Zend, a Rogue Wave Company. Made with by awesome contributors.

This website is built using zend-expressive and it runs on PHP 7.

Contacts