View Source

<ac:macro ac:name="info"><ac:parameter ac:name="title">New Proposal Template</ac:parameter><ac:rich-text-body>
<p>This page has been created from a template that uses &quot;zones.&quot; To proceed:</p>

<ol>
<li>Edit the page</li>
<li>Replace sample content within each zone-data tag with your own content</li>
<li>Remove this notice</li>
<li>Save the page</li>
<li>When you are ready for community review, move this page to the <ac:link><ri:page ri:content-title="Ready for Review" /></ac:link> section on the edit page.</li>
</ol>


<ac:macro ac:name="note"><ac:parameter ac:name="title">No placeholders allowed!</ac:parameter><ac:rich-text-body>
<p>Please do not create placeholders. Wait until you have sufficient content to replace all sample data in the proposal template before creating your proposal document.</p></ac:rich-text-body></ac:macro></ac:rich-text-body></ac:macro>

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

{zone-data:component-name}
Zend_Db_Adapter_Alias
{zone-data}

{zone-data:proposer-list}
[Joe Lee|~joeworks]
{zone-data}

{zone-data:liaison}
TBD
{zone-data}

{zone-data:revision}
0.1 - 20 October 2009: Incomplete WIP
{zone-data}

{zone-data:overview}
Zend_Db_Adapter_Alias is a simple component that allows multiple aliases for Zend_Db_Adapter. The main usage of this component is to minimise code repetition when writing Table Data Gateways that need to connect to multiple databases.

h4.Why?
Currently, to write a Table Data Gateway that needs to connect to a non-default database adapter, we have to code this way:
{code}
class MyTable1 extends Zend_Db_Table_Abstract
{
protected $_name = "MyTable1";

protected function _setupDatabaseAdapter()
{
$this->_db = Zend_DB::factory( ..... );
}

}
{code}

A better way is to preset the Db Adapter in the Registry at Bootstrap first, which I think is still not efficient because of an instance of Zend_DB_Adapter is created and set into Registry at every page load, even when it is not used:
{code}
// Bootstrap
Zend_Registry::set( "database2", Zend_DB::factory( ..... ) );

// Db Table
class MyTable1 extends Zend_Db_Table_Abstract
{
protected $_name = "MyTable1";

public function __construct($config = array())
{
$this->_setAdapter( "database2" );
parent::__construct($config);
}

}
{code}

Zend_Db_Adapter_Alias is designed to:

* Create the Database Adapter and set into Registry when it is required
* Maintain a list of connection information in a Zend_Config file
* Simplify writing Table Data Gateways that need to connect to multiple databases:
{code}

class MyTable1 extends Zend_Db_Table_Abstract
{
protected $_dbAlias = "database2";
protected $_name = "MyTable1";
}
{code}
* Allows Zend_Application to handle multiple database adapters through a new Resource plug-in, Zend_Application_Resource_DbAlias. See [Issue Tracker ZF-7997|http://framework.zend.com/issues/browse/ZF-7997]
{zone-data}

{zone-data:references}
* [Issue Tracker ZF-7997|http://framework.zend.com/issues/browse/ZF-7997]
{zone-data}

{zone-data:requirements}
||ToDo||
Most requirements take the form of "foo will do ...." or "foo will not support ...", although different words and sentence structure might be used. Adding functionality to your proposal is requirements creep (bad), unless listed below. Discuss major changes with your team first, and then open a "feature improvement" issue against this component.

* This component *will* correctly reads a developers mind for intent and generate the right configuration file.
* The generated config file *will not* support XML, but will provide an extension point in the API.
* This component *will* use no more memory than twice the size of all data it contains.
* This component *will* include a factory method.
* This component *will not* allow subclassing. (i.e. when reviewed, we expect to see "final" keyword in code)
* This component *will* only generate data exports strictly complying with RFC 12345.
* This component *will* validate input data against formats supported by ZF component Foo.
* This component *will not* save any data using Zend_Cache or the filesystem. All transient data *will be* saved using Zend_Session.
{zone-data}

{zone-data:dependencies}
* Zend_Db
* Zend_Db_Exception
* Zend_Config
{zone-data}

{zone-data:operation}
||ToDo||
The component is instantiated with a mind-link that ...
{zone-data}

{zone-data:milestones}
||ToDo||
Describe some intermediate state of this component in terms of design notes, additional material added to this page, and / code. Note any significant dependencies here, such as, "Milestone #3 can not be completed until feature Foo has been added to ZF component XYZ." Milestones will be required for acceptance of future proposals. They are not hard, and many times you will only need to think of the first three below.
* Milestone 1: [design notes will be published here|http://framework.zend.com/wiki/x/sg]
* Milestone 2: Working prototype checked into the incubator supporting use cases #1, #2, ...
* Milestone 3: Working prototype checked into the incubator supporting use cases #3 and #4.
* Milestone 4: Unit tests exist, work, and are checked into SVN.
* Milestone 5: Initial documentation exists.

If a milestone is already done, begin the description with "\[DONE\]", like this:
* Milestone #: \[DONE\] Unit tests ...
{zone-data}

{zone-data:class-list}
* Zend_Db_Adapter_Alias
* Zend_Application_Resource_DbAlias
* Zend_Db_Table_Abstract
{zone-data}

{zone-data:use-cases}
||UC-1 Setup using Bootstrap||
|*database.ini*
{code}
[maindb]
adapter = "PDO_MYSQL"
params.dbname = "myMainDb"
params.host = "host1.example.com"
params.username = "dbuser1"
params.password = "password1"

[legacydb]
adapter = "PDO_MSSQL"
params.dbname = "myLegacyDb"
params.host = "host2.example.com"
params.username = "dbuser2"
params.password = "password2"
{code}|
|*Bootstrap*
{code}
Zend_Db_Adapter_Alias::setConfig(
new Zend_Config_Ini( APPLICATION_PATH."/configs/database.ini" )
);
Zend_Db_Table_Abstract::setDefaultAlias( 'maindb' );
{code}|
||UC-2 Setup using Zend_Application||
|*application.ini*
{code}
...
resources.dbAlias.alias.maindb.adapter = "PDO_MYSQL"
resources.dbAlias.alias.maindb.params.dbname = "myMainDb"
resources.dbAlias.alias.maindb.params.host = "host1.example.com"
resources.dbAlias.alias.maindb.params.username = "dbuser1"
resources.dbAlias.alias.maindb.params.password = "password1"

resources.dbAlias.alias.legacydb.adapter = "PDO_MSSQL"
resources.dbAlias.alias.legacydb.params.dbname = "myLegacyDb"
resources.dbAlias.alias.legacydb.params.host = "host2.example.com"
resources.dbAlias.alias.legacydb.params.username = "dbuser2"
resources.dbAlias.alias.legacydb.params.password = "password2"

resources.dbAlias.defaultTableAlias = "maindb"
...
{code}|
||Writing Table Data Gateway||
|{code}
class MySite_Model_DbTable_MyMainTable1 extends Zend_Db_Table_Abstract
{
protected $_dbAlias = "maindb";
protected $_name = "MyTable1";
}

class MySite_Model_DbTable_MyMainTable2 extends Zend_Db_Table_Abstract
{
// uses the defaultTableAlias, i.e. maindb
protected $_name = "MyTable2";
}

class MySite_Model_DbTable_MyLegacyTable1 extends Zend_Db_Table_Abstract
{
protected $_dbAlias = "legacydb";
protected $_name = "MyTable1";
}
{code}|

{zone-data}

{zone-data:skeletons}
||New Class Zend_Db_Adapter_Alias||
|{code}
class Zend_Db_Adapter_Alias
{
/**
* Configuration of aliases
*
* @var Zend_Config
*/
protected static $_config = NULL;

/**
* Set the configuration of aliases
*
* @param $config Zend_Config
* @return void
*/
public static function setConfig( Zend_Config $config ) {
self::$_config = $config;
}

/**
* Get the configuration of aliases
*
* @return Zend_Config
*/
public static function getConfig() {
return self::$_config;
}

/**
* Get Zend_Db_Adapter_Abstract from alias
*
* @param $alias string
* @return Zend_Db_Adapter_Abstract
*/
public static function getAdapter( $alias ) {

$reg_key = __CLASS__."_".$alias;
if ( Zend_Registry::isRegistered( $reg_key ) ) {
return Zend_Registry::get( $reg_key );
}

if ( isset( self::$_config->$alias ) ) {

$db = Zend_Db::factory( self::$_config->$alias );

Zend_Registry::set( $reg_key, $db );
return $db;

} else {

throw new Zend_Db_Exception("Alias '{$alias}' not found.");

}

}

}
{code}|
||Changes to Zend_Db_Table_Abstract ||
|{code}
abstract class Zend_Db_Table_Abstract
{

// ... <snip!> ...

// 2 new properties...

/**
* Default Db Alias.
*
* @var string
*/
protected static $_defaultDbAlias = null;

/**
* The database alias.
*
* @var string
*/
protected $_dbAlias = null;

// ... <snip!> ...

// 4 new methods...

/**
* Set default database alias.
*
* @param $alias string
* @return void
*/
public static function setDefaultAlias( $alias = null )
{
self::$_defaultDbAlias = $alias;
self::setDefaultAdapter(
Zend_Db_Adapter_Alias::getAdapter( $alias )
);
}

/**
* Get default database alias.
*
* @return string
*/
public static function getDefaultAlias( )
{
return self::$_defaultDbAlias;
}

/**
* Set database alias.
*
* @param $alias string
* @return void
*/
public function setAlias( $alias )
{
$this->_dbAlias = $alias;
}

/**
* Get database alias.
*
* @return string
*/
public function getAlias( )
{
return $this->_dbAlias;
}

// add code to _setupDatabaseAdapter()

/**
* Initialize database adapter.
*
* @return void
*/
protected function _setupDatabaseAdapter()
{
// Start of added code
if ( $this->_dbAlias ) {
$this->_db = Zend_Db_Adapter_Alias::getAdapter( $this->_dbAlias );
}
// End of added code

if (! $this->_db) {
$this->_db = self::getDefaultAdapter();
if (!$this->_db instanceof Zend_Db_Adapter_Abstract) {
require_once 'Zend/Db/Table/Exception.php';
throw new Zend_Db_Table_Exception(
'No adapter found for ' . get_class($this)
);
}
}
}

// ... <snip!> ...

}
// TODO diff file for Zend_Db_Table_Abstract
{code}|
||ToDo Zend_Application_Resource_DbAlias||
|{code}
class Zend_Application_Resource_DbAlias extends Zend_Application_Resource_ResourceAbstract
{

}
{code}|

{zone-data}

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