Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Zend Framework: Zend_Db_Table Enhancements Component Proposal

Proposed Component Name Zend_Db_Table Enhancements
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Db_Table Enhancements
Proposers Fabien MARTY
Eugene Panaitov
Simon Mundy
Darby Felton, Zend liaison
Revision 0.1 - 14 October 2006: Summary of community feedback on ZF Mailing list. (wiki revision: 8)

Table of Contents

1. Overview

This proposal summarises several requests on the mailing list and the issue tracker to provide enhancements to the Zend_Db_Table component. It is a response to Gavin Vess's request to formalise these enhancements and not meant to override or overlap any existing Db work already being performed. It's been posted here so that more focussed feedback can be given and to ensure these enhancements follow the established procedures for accepting ZF components.

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • This component will allow developers to extend the capability of Zend_Db_Table rows and rowsets.
  • It will allow developers to specify a custom class for rowsets / rows in a Zend_Db_Table instance.
  • It will provide better ORM capabilities by allowing the possibility of chained queries and domain logic outside of the Db itself.
  • It will allow Zend_Db_Table_Row objects to be serialized (removing dependency on stored DB connections)
  • It will provide methods to allow custom pre-filtering of data for insertion/updates to the database.
  • It will allow arrays for primary keys (for composite keys on tables)

4. Dependencies on Other Framework Components

  • Zend_Db_Table
  • Zend_Db_Exception

5. Theory of Operation

The component allows developers to override the default class used for rowsets and rows, meaning that result sets for single rows or collections of rows may be instantiated with user-defined classes.

Custom rows can also have hooks to perform user-transformations to data before rows are inserted or updated. The empty _insert() and _update() methods are called before data is committed and operate directly on row data using transformed columns (e.g. if you had camelCaps enabled, you would set $this->clientListId = 3 rather than $this->client_list_id = 3).

To allow rows and rowsets to be serialized (as they can contain domain logic rather than simply hold database values), the database is not stored within the row or rowset instance and is instead accessed by querying an instance of the parent table. A reference to the parent table is provided as a classname so that a row can take advantage of lazy-loading.

Because tables can also have composite keys, the primary key will allow arrays, and this will be enforced during a 'find()' operation - if the primary key is an associative array, all keys must be assigned values in the find() method or an exception will be thrown.

The enforcement of 'camelCaps' and other case transformations will not be set by default, however the option to add these will remain. The method setColumnCase() will create a new column filter for a Zend_Db_Table and all rows/rowsets will inherit this transformation.

6. Milestones / Tasks

  • Milestone 1: Community discussion and initial acceptance by Zend Db team.
  • Milestone 2: Working prototype checked into the incubator.
  • Milestone 3: Unit tests exist, work, and are checked into SVN.
  • Milestone 4: Update existing documentation and use-cases.

7. Class Index

  • Zend_Db_Table
  • Zend_Db_Table_Rowset
  • Zend_Db_Table_Rowset_Abstract
  • Zend_Db_Table_Row
  • Zend_Db_Table_Row_Abstract

8. Use Cases

9. Class Skeletons

]]></ac:plain-text-body></ac:macro>

]]></ac:plain-text-body></ac:macro>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Oct 14, 2006

    <p>Hi,</p>

    <p>I wonder if this is the right place for some thoughts to add some joining functionalities to Zend_Db. I just add them here anyway.</p>

    <p>a) I would like to suggest to split the current _fetch() method up into two methods like this (stripped off the comments):</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    protected function _buildSelect($where = null, $order = null, $count = null, $offset = null)
    {
    $select = $this->_db->select();

    $select->from($this->_name, array_keys($this->_cols));

    $where = (array) $where;
    foreach ($where as $key => $val) {
    if (is_int($key))

    Unknown macro: { $select->where($val); }

    else

    Unknown macro: { $select->where($key, $val); }

    }

    $order = (array) $order;
    foreach ($order as $val)

    Unknown macro: { $select->order($val); }

    $select->limit($count, $offset);

    return $select;
    }

    protected function _fetch($type, $where = null, $order = null, $count = null, $offset = null)

    Unknown macro: { $select = $this->_buildSelect($where, $order, $count, $offset); $method = "fetch$type"; return $this->_db->$method($select); }

    ]]></ac:plain-text-body></ac:macro>

    <p>Now I could easily add a join in my classes which extend Zend_Db:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    class User extends Zend_Db_Table
    {
    protected $_name = 'user';

    // do whatever you need to do to extend Zend_Db_Table

    protected function _buildSelect($where = null, $order = null, $count = null, $offset = null)

    Unknown macro: { $select = parent}

    }
    ]]></ac:plain-text-body></ac:macro>

    <p>b) Nothing to do with joiningh, but I would also like to add a fetchCount() method to Zend_Db_Table to get the number of all records or the number for a specific where clause?</p>

    <p>What do you think about these suggestions?</p>

    <p>Best Regards,</p>

    <p>Ralf</p>

  2. Oct 19, 2006

    <p>My implementation is:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    class Zend_Db_Table_Orm extends Zend_Db_Table {

    //per http://framework.zend.com/wiki/display/ZFMLGEN/mail/1625
    protected $_rowClassName = 'Zend_Db_Table_Row';
    protected $_rowsetClassName = 'Zend_Db_Table_Rowset';
    protected $_dirs = null; // to support db_row classes in different directories

    /**

    • Fetches all rows.
      *
    • Honors the Zend_Db_Adapter fetch mode.
      *
    • @param string|array $where An SQL WHERE clause.
    • @param string|array $order An SQL ORDER clause.
    • @param int $count An SQL LIMIT count.
    • @param int $offset An SQL LIMIT offset.
    • @return mixed The row results per the Zend_Db_Adapter fetch mode.
      */
      public function fetchAll($where = null, $order = null, $count = null,
      $offset = null)
      Unknown macro: { $className = $this->_rowsetClassName; Zend}

    /**

    • Fetches one row.
      *
    • Honors the Zend_Db_Adapter fetch mode.
      *
    • @param string|array $where An SQL WHERE clause.
    • @param string|array $order An SQL ORDER clause.
    • @return mixed The row results per the Zend_Db_Adapter fetch mode.
      */
      public function fetchRow($where = null, $order = null)
      Unknown macro: { $className = $this->_rowClassName; Zend}

    /**

    • Fetches a new blank row (not from the database).
    • @return _rowClassName class
      */
      public function fetchNew()
      Unknown macro: { $keys = array_keys($this->_cols); $vals = array_fill(0, count($keys), null); $className = $this->_rowClassName; Zend}

    }
    ]]></ac:plain-text-body></ac:macro>

    <p>We need to point to the loadClass function a list of directories where each successor of Zend_Db_Table_Row or Zend_Db_Table_RowSet can be found. I added <br />
    protected $_dirs = null;<br />
    variable</p>

  3. Nov 13, 2006

    <p>Unofficial comments ...</p>

    <p>This "KISS" style of ORM has been a popular suggestion over the last few months on the mail lists. The team is focused on marching towards a 1.0. Due to time constraints, and the difficulty in obtaining approval for "incubator" with a proposal that is not required for ZF 1.0, I am recommending that this proposal at least be granted lab access to help get more exposure and make it easier for others to test, use, and hopefully improve it.</p>

    1. Nov 14, 2006

      <p>That's great Gavin - I have a version with some updated Zend_Db_Table_xxx components that I could upload immediately. They're working quite well in a production environment but could do with some refactoring to allow caching of table structures, etc. Will publish when (if?) we get the lab account.</p>

  4. Nov 22, 2006

    <ac:macro ac:name="unmigrated-wiki-markup"><ac:plain-text-body><![CDATA[Hello.

    We need a count method able to retrieve the number of records available in a table that will receive a $where parameter. If a user is filtering the list making a search then is not difficult to recreate pagination on the search results.

    Something like:

    /**

    • Count the available records that match a search criteria
      *
    • @param string $where
      */
      public function count($where)
      {
      $select = $this->_db->select();
      $select->from($this->_name,'COUNT('.$this->_primary.') AS number');

    // the WHERE clause
    $where = (array) $where;
    foreach ($where as $key => $val) {
    if (is_int($key))

    Unknown macro: { $select->where($val); }

    else

    Unknown macro: { $select->where($key, $val); }

    }
    return $this->_db->fetchRow($select);
    }]]></ac:plain-text-body></ac:macro>

  5. Dec 11, 2006

    <ac:macro ac:name="note"><ac:parameter ac:name="title">Zend Feedback</ac:parameter><ac:rich-text-body><p>This proposal has been approved for laboratory development. Please continue to work together as a group on this project, discussing ideas together during development, and raise any issues in the ZF fw-db mail list.</p></ac:rich-text-body></ac:macro>

  6. Jan 03, 2007

    <p>I'm trying to think why the Zend_Db_Table inherited classes are not singletons or even static. I don't see any reason to instantiate more than one (or even not instantiate at all) a "table".</p>

  7. Jan 29, 2007

    <p>I think it might be a good idea to use a method instead of a variable to determine what type should be returned by the fetch methods. This would allow you to use the fetch methods with STI (single table inheritance), much like Ruby On Rails polymorphic finds. For example:</p>

    <p>Zend_Db_Table.php</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    public function fetchRow($where = null, $order = null)
    {
    $data = $this->_fetch('Row', $where, $order, 1);
    $className = $this->_determineClassName($data);
    Zend::loadClass($className, $this->_dirs);
    return new $className(array(
    'db' => $this->_db,
    'table' => $this,
    'data' => $data,
    ));
    }
    ]]></ac:plain-text-body></ac:macro>
    <p>Zend_Db_Table_RowSet.php</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    public function current() {
    // is the pointer at a valid position?
    if (! $this->valid())

    Unknown macro: { return false; }

    // do we already have a row object for this position?
    if (empty($this->_rows$this->_pointer))

    Unknown macro: { $className = $this->_table->_determineClassName($this->_data[$this->_pointer]); // create a row object $this->_rows[$this->_pointer] = new $className(array( 'db' => $this->_db, 'table' => $this->_table, 'data' => $this->_data[$this->_pointer] )); }

    // return the row object
    return $this->_rows[$this->_pointer];
    }
    ]]></ac:plain-text-body></ac:macro>
    <p>Example.php</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    abstract class STITable extends Zend_Db_Table_Row {
    protected function _determineClassName($data)

    Unknown macro: { return 'STITable_' . $data['type']; }

    public function doSomethingOne()

    Unknown macro: { return 'ok!'; }

    abstract public function doSomethingTwo();
    }

    class STITable_One extends STITable {
    public function doSomethingTwo()

    Unknown macro: { return 'one behavior'; }

    }

    class STITable_Two extends STITable {
    public function doSomethingTwo()

    Unknown macro: { return 'another behavior'; }

    }

    $table = new STITable();
    foreach($table->fetchAll() as $sti_table) {
    $sti_table->doSomethingOne();
    $sti_table->doSomethingTwo();
    }
    ]]></ac:plain-text-body></ac:macro>

    <p>It would probably be a good idea to implement a similar change in Zend_Db_Table::fetchAll() for getting a different type of Zend_Db_Table_RowSet object back as well, though this would not need to be polymorphic based on the return type.</p>

    <p>Food for thought.</p>

    1. Jan 29, 2007

      <ac:macro ac:name="unmigrated-wiki-markup"><ac:plain-text-body><![CDATA[Quick bug catch:

      protected function _determineClassName($data)

      Unknown macro: {{/code}

      This needs to be public, obviously.]]></ac:plain-text-body></ac:macro>

  8. Feb 07, 2007

    <p>May I ask how's the proposal going? Is there any possibility for the code being accepted and thrown into incubator anytime soon?</p>

    1. Feb 07, 2007

      <p>Hi Martel - I'll be uploading a set of classes to the Laboratory soon. Nothing yet in the incubator, though - I understand Bill's making a lot of other improvements and they may or may not have a similar implementation.</p>

  9. May 14, 2008

    <p>It looks like this proposal hasn't been active for quite a while. Has it been promoted or abandoned? I'm moving to the archived section. Please reparent to the correct section if the status has changed.</p>

    <p>,Wil</p>