View Source

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[{zone-template-instance:ZFDEV:Zend Proposal Zone Template}

{zone-data:component-name}
Zend_Db_Table Enhancements
{zone-data}

{zone-data:proposer-list}
[~fab]
[~eugenep]
[~peptolab]
[Darby Felton|mailto:darby@zend.com], Zend liaison
{zone-data}

{zone-data:revision}
0.1 - 14 October 2006: Summary of community feedback on ZF Mailing list.
{zone-data}

{zone-data: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.
{zone-data}

{zone-data:references}
* [ZF General Mailing List|http://framework.zend.com/wiki/display/ZFMLGEN/mail/1625]
* [ZF Issue Tracker|http://framework.zend.com/issues/browse/ZF-439]
{zone-data}

{zone-data:requirements}
* 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)
{zone-data}

{zone-data:dependencies}
* Zend_Db_Table
* Zend_Db_Exception
{zone-data}

{zone-data: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.
{zone-data}

{zone-data:milestones}
* 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.
{zone-data}

{zone-data:class-list}
* Zend_Db_Table
* Zend_Db_Table_Rowset
* Zend_Db_Table_Rowset_Abstract
* Zend_Db_Table_Row
* Zend_Db_Table_Row_Abstract
{zone-data}

{zone-data:use-cases}
{composition-setup}
{deck:id=Usecase}
{card:label=UC 01 : Domain logic}
h3. UC 01
From: http://framework.zend.com/wiki/display/ZFMLGEN/mail/1625

We have a "user" table with following fields :
=> id (int) PK
=> firstName (string)
=> familyName (string)

{code}
class UserFactory extends Zend_Db_Table {
protected $_name = 'user';
protected $_row = 'User';
protected $_rowset = 'UserSet';
}

class UserSet extends Zend_Db_Table_Rowset {}

class User extends Zend_Db_Table_Row_Abstract {

// Add domain logic
public function completeName() {
return $this->firstName . ' ' . $this->familyName;
}

}

$userFactory = new UserFactory();
$user = $userFactory->find(1);

echo($user->firstName); // same behaviour than with Zend_Db_Table_Row
echo($user->familyName); // [...]
echo($user->completeName()); // we can use our domain logic here !!!
{code}
{card}
{card:label=UC 02 : Fluent queries}
h3. Allow chaining/joins for more fluent queries
{code}
class Myclient_Order extends Zend_Db_Table
{
$this->_name = 'order_list';
$this->_row = 'Myclient_Order_Row';
}

class Myclient_Order_Row extends Zend_Db_Table_Row_Abstract
{
public function fetchItem()
{
$item = new Myclient_Order_Item;
return $item->find($this->order_item_id);
}

public function fetchContact()
{
$contact = new Myclient_Contact;
return $contact->find($this->contact_list_id);
}
}

class Myclient_Contact extends Zend_Db_Table
{
$this->_name = 'contact_list';
$this->_row = 'Myclient_Contact_Row';
}

class Myclient_Contact_Row extends Zend_Db_Table_Row_Abstract
{
public function getAddress()
{
return array_filter(array($this->address_1,
$this->address_2,
$this->suburb,
$this->city,
$this->postcode));
}
}

$order = new Myclient_Order;
echo join('<br />', $order->find(1)->fetchContact()->getAddress());

{code}
{card}
{card:label=UC 03 : Filtered row data for insert/update}
h3. UC 03 - Insert/Update callbacks
{code}
class Myclient_Transaction_Row extends Zend_Db_Table_Row_Abstract
{
public function _insert()
{
$this->modified = date('Y-m-d H:i:s');
$this->created = date('Y-m-d H:i:s');
$this->ip = $_SERVER['REMOTE_ADDR'];
}

public function _update()
{
$this->modified = date('Y-m-d H:i:s');
$this->ip = $_SERVER['REMOTE_ADDR'];
}
}
{code}
{card}
{deck}
{zone-data}

{zone-data:skeletons}
{composition-setup}
{deck:id=Source}
{card:label=Zend_Db_Table}
{code}
abstract class Zend_Db_Table
{
const CASE_NONE = 0;
const CASE_LOWER = 1;
const CASE_UPPER = 2;
const CASE_CAMEL = 3;

static protected $_defaultDb;
static protected $_inflector;
protected $_db;
protected $_name;
protected $_cols;
protected $_case = self::CASE_NONE;
protected $_primary = 'id';
protected $_row = 'Zend_Db_Table_Row';
protected $_rowset = 'Zend_Db_Table_Rowset';
protected $_criteria = array();
protected $_order = array();

public function __construct($config = null)
public final function getAdapter()
public function info()
public function insert($data)
public function update($data, $where)
public function delete($where)
public function find($val)
public function fetchAll($where = null, $order = null, $count = null, $offset = null)
public function fetchRow($where = null, $order = null)
public function fetchNew()
public function getCriteria()
public function addCriteria()
public function clearCriteria()
public function setColumnCase()

protected function _fetch($type, $where = null, $order = null, $count = null, $offset = null)
protected function _setup()
protected final function _getDefaultAdapter()
}
{code}
{card}
{card:label=Zend_Db_Table_Row_Abstract}
{code}
abstract class Zend_Db_Table_Row_Abstract
{
protected $_table;
protected $_data = array();
protected $_info = array();

public function __construct($config = array())
public function __get($column)
public function __set($column, $value)
public function __sleep()
public function __wakeup()
public function save()
public function toArray()
public function setFromArray($data)

protected function _update()
protected function _insert()
protected function _refresh()
}

class Zend_Db_Table_Row extends Zend_Db_Table_Row_Abstract
{
}
{code}
{card}
{card:label=Zend_Db_Table_Rowset_Abstract}
{code}
abstract class Zend_Db_Table_Rowset_Abstract implements Iterator
{
protected $_table;
protected $_row;
protected $_count;
protected $_pointer = 0;
protected $_data = array();
protected $_rows = array();

public function __construct($config = array())
public function rewind()
public function current()
public function key()
public function next()
public function valid()
public function count()
public function exists()
public function toArray()
}

class Zend_Db_Table_Rowset extends Zend_Db_Table_Rowset_Abstract
{
}
{code}
{card}
{deck}
{zone-data}

{zone-template-instance}]]></ac:plain-text-body></ac:macro>