<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ralph Schindler &#187; Zend_Db</title>
	<atom:link href="http://ralphschindler.com/tag/zend_db/feed" rel="self" type="application/rss+xml" />
	<link>http://ralphschindler.com</link>
	<description>Ralph Schindler</description>
	<lastBuildDate>Mon, 19 Sep 2011 21:38:12 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Composite Rowsets For Many-To-Many Relationships Via Zend_Db_Table</title>
		<link>http://ralphschindler.com/2010/11/15/composite-rowsets-for-many-to-many-relationships-via-zend_db_table</link>
		<comments>http://ralphschindler.com/2010/11/15/composite-rowsets-for-many-to-many-relationships-via-zend_db_table#comments</comments>
		<pubDate>Tue, 16 Nov 2010 01:30:24 +0000</pubDate>
		<dc:creator>Ralph Schindler</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[Zend_Db]]></category>

		<guid isPermaLink="false">http://ralphschindler.com/?p=120</guid>
		<description><![CDATA[One of the hardest problems to solve when developing an ORM of any complexity is in deciding how to handle the retrieval of rows that satisfy a many-to-many relationship, also known as a M:N relationship. From the perspective of an object, there is no such thing as a many to many relationship. There are only [...]]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop --><!-- End Shareaholic LikeButtonSetTop --><p>One of the hardest problems to solve when developing an ORM of any complexity is in deciding how to handle the retrieval of rows that satisfy a <a href="http://en.wikipedia.org/wiki/Many-to-many_(data_model)">many-to-many relationship</a>, also known as a M:N relationship.  From the perspective of an object, there is no such thing as a many to many relationship.  There are only two relationships an object understands.  The first is the relationship of itself to another object, which is a one to one (1:1) relationship. The second is the relationship of itself to a group of other objects, or a one-to-many (1:N) relationship.  It&#8217;s not until you look at the relationship of all objects in a system that the many-to-many relationship pattern emerges.</p>
<p>In RDBM systems, rows and their relationships are modeled through the use of foreign keys and foreign key constraints between a left table and a right table.  Foreign key constraints, by themselves, can only model 1:1 and 1:N relationship of rows.  To model M:N relationships, database developers must get creative.  By employing the use of a &#8220;3rd party&#8221;, and by utilizing foreign keys that model a 1:N relationship, database developers can model a M:N relationship.  This 3rd party comes in the form of another table that may or may not have any data model specific information attached to it.  This table is generally known as a <a href="http://en.wikipedia.org/wiki/Junction_table">junction table</a>, but has also been known as a cross-reference table, bridge table, join table, map table, intersection table, linking table, many-to-many resolver, link table, or association table.</p>
<h3><tt>Zend_Db_Table_Row</tt> And Junction Tables</h3>
<p><tt>Zend_Db_Table</tt> is a component in Zend Framework that implements the <a href="http://martinfowler.com/eaaCatalog/tableDataGateway.html">Table Data</a> and <a href="http://martinfowler.com/eaaCatalog/rowDataGateway.html">Row Data Gateway</a> patterns. In short, a row object attempts to create a single PHP object per actual row in the database table.  Furthermore, <tt>Zend_Db_Table_Row</tt> objects can go as far as to describe, understand, and interrogate these various 1:1, 1:N and M:N relationships.  This allows row objects to be able to find and return related row objects in the form of a <i>rowset</i>.</p>
<p>One of the primary tenets of <tt>Zend_Db_Table</tt> and <tt>Zend_Db_Table_Row</tt> is to be able to produce consistent row objects.  This means that the properties of these row objects should be a <i>complete</i> and logical representation of how the row might look inside the table of the RDBMS.</p>
<p>Some time ago an issue (<a href="http://framework.zend.com/issues/browse/ZF-6232">ZF-6232</a>) was filed against <tt>Zend_Db_Table</tt> to report that columns from the junction table were being included in the resulting rowset&#8217;s row objects.  This was causing issues for people who then attempted to <tt>save()</tt> the row object back to the database.  If a developer mistakenly altered one of the junction table values that was accidentally included in the row, <tt>Zend_Db_Table_Row</tt> would throw an exception since the row object had more columns than the actual row in the database.  Given that we want to create consistent, complete and logical row objects, a solution was devised to ensure that the junction table&#8217;s row information was not included in the resulting rowset&#8217;s rows.  Consequently, this meant that anyone relying on this undocumented behavior would no longer be able to get data stored inside the junction table as part of the result set&#8217;s row object.  This fix was incorporated into the 1.10.2 release.</p>
<p>Over the past several years of working on Zend Framework, I&#8217;ve noticed the developer population at large is <em>really</em> good at finding undocumented and previously unthought-of use-cases of Zend Framework components.  These use-cases, while sometimes &#8220;inventive&#8221; to say the least- are also sometimes blatant misuses of a component.  It suffices to say that these use-cases are not captured in a unit test and consequently are not protected by backwards compatibility.</p>
<p>Relying on <tt>Zend_Db_Table_Row</tt> to include junction data is not only an unintended use case but also a misuse of the <tt>findManyToManyRowset()</tt> functionality provided by <tt>Zend_Db_Table_Row</tt>.  That said, I do want to provide a solution for developers that relied on this behavior of Zend_Db_Table_Row in Zend Framework previous to 1.10.2.</p>
<h3>A Solution</h3>
<p>While the motivation for creating this class is based on providing a solution to developers who relied on utilizing junction table data in Zend_Db_Table_Row&#8217;s many-to-many rowsets, this same technique can be utilized with any ORM or database abstraction layer that handles many-to-many result sets.</p>
<p>Basically, I&#8217;ve created a single class that effectively take the place of <tt>Zend_Db_Table_Row::findManyToManyRowset()</tt> for the purposes of creating an iterable rowset that allows access to both the target many-to-many rowset as well as the junction rowset.  This solution is called a <em>Composite Rowset</em>.  In this solution, both rowsets (iterators) are kept in sync with one another.  This proves to be an ideal solution in a couple of ways.  First, it will produce consistent row objects that are explicitly tied to a row in a database.  Second, the cost of creating this composite rowset is at the expense of 2 queries: the original many-to-many query and a similar query to retrieve the junction rowset.  This is ideal since previously, to get the junction data, <tt>findDependentRowset()</tt> would have had to been called on each row within the rowset produced by the <tt>Zend_Db_Table_Row::findManyToManyRowset()</tt>.</p>
<p>The API for this Composite Rowset looks like this:</p>
<pre class="brush: php; title: ; notranslate">

/**
 * @link https://github.com/gooeylabs/Gooey-PHP-5.2-Components/blob/master/library/Gooey/Db/Table/ManyToManyCompositeRowset.php
 */
class Gooey_Db_Table_ManyToManyCompositeRowset implements SeekableIterator, ArrayAccess, Countable
{

    public function __construct(Zend_Db_Table_Row_Abstract $row, $matchTableName, $junctionTableName, $matchRefRule = null);
    public function seek($position);
    public function current();
    public function currentJunction();
    public function next();
    public function rewind();
    public function key();
    public function valid();
    public function offsetSet($offset, $value);
    public function offsetGet($offset);
    public function offsetExists($offset);
    public function offsetUnset($offset);
    public function count();
    public function getRow($position, $seek = false);
    public function getJunctionRow($position, $seek = false);
    public function toArray();
    public function junctionRowsetToArray();
}
</pre>
<p>NOTE: Full class located <a href="https://github.com/gooeylabs/Gooey-PHP-5.2-Components/blob/master/library/Gooey/Db/Table/ManyToManyCompositeRowset.php">here.</a></p>
<p>As you can see, the API mirrors that of <tt>Zend_Db_Table_Rowset</tt> to provide a something that is immediately recognizable. Below is an example of sample usage.  For this example, assume there is a typically artist/genre data model that demonstrates a many-to-many relationship.  Inside of the junction table we are attempting to track the date that the relationship was created.  This examples shows this usage:</p>
<pre class="brush: php; title: ; notranslate">

$aTable = new ArtistTable();
$artist1 = $aTable-&gt;find(1)-&gt;current();
echo 'Artist: ' . $artist1-&gt;name . PHP_EOL;
// instead of $genres = $a-&gt;findManyToManyRowset('GenreTable', 'ArtistGenreTable');
$genres = new Gooey_Db_Table_ManyToManyCompositeRowset($artist1, 'GenreTable', 'ArtistGenreTable');

// iterate
foreach ($genres as $genre) {
    echo '  Genre ' . $genre-&gt;name . ' added on ' . $genres-&gt;currentJunction()-&gt;added_on . PHP_EOL;
}

/**
 * Sample Output:
 *
 *    Artist: Foo Artist
 *      Genre Rock &amp; Roll added on 2010-11-10
 *      Genre Hiphop added on 2010-11-11
 *
 */
</pre>
<h3>Where To Get It &amp; Conclusions</h3>
<p>This code is available on my <a href="https://github.com/gooeylabs">GooeyLabs</a> github account, specifically inside of the <a href="https://github.com/gooeylabs/Gooey-PHP-5.2-Components">Gooey-PHP-5.2-Components</a> repository.  (Gooey is my namespace and moniker for my open source code contributions.)  Hopefully, those who have found they&#8217;ve had issues with the above mentioned fix for <tt>Zend_Db_Table_Row::findManyToManyRowset()</tt> and junction table data might find value in this class.</p>
<div class="shr-publisher-120"></div><!-- Start Shareaholic LikeButtonSetBottom --><!-- End Shareaholic LikeButtonSetBottom -->]]></content:encoded>
			<wfw:commentRss>http://ralphschindler.com/2010/11/15/composite-rowsets-for-many-to-many-relationships-via-zend_db_table/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Database Abstraction Layers Must Live!</title>
		<link>http://ralphschindler.com/2009/07/15/database-abstraction-layers-must-live</link>
		<comments>http://ralphschindler.com/2009/07/15/database-abstraction-layers-must-live#comments</comments>
		<pubDate>Wed, 15 Jul 2009 17:46:22 +0000</pubDate>
		<dc:creator>Ralph Schindler</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Best Practices]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Software Architecture]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[Zend_Db]]></category>

		<guid isPermaLink="false">http://ralphschindler.com/?p=26</guid>
		<description><![CDATA[I come preaching true hope, against the fallacies. I&#8217;ve heard the arguments for and against database abstraction layers (DALs) time and time again. I must say first, I agree with them all, both sides, equally. Interestingly, I can put the vocal proponents of each side of the argument in one of two boxes: a programmer [...]]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop --><!-- End Shareaholic LikeButtonSetTop --><p>I come preaching true hope, <a href="http://jeremy.zawodny.com/blog/archives/002194.html">against the fallacies</a>.</p>
<p>I&#8217;ve heard the arguments for and against <a href="http://en.wikipedia.org/wiki/Database_abstraction_layer">database abstraction layers (DALs)</a> time and time again.  I must say first, I agree with them all, both sides, equally.  Interestingly, I can put the vocal proponents of each side of the argument in one of two boxes: a <em>programmer guy</em> box, or a <em>database guy</em> box.  For some unknown reason though, they never seem to see eye to eye.</p>
<p>Honestly though, I like to put myself in the middle of that argument.  I see both sides.  I think fine tuning an application&#8217;s core business with vendor specific features is tremendously important, after all, that is why there are so many competing database vendors.  Generally speaking of database driven projects, I feel like planning to use a specific vendor up front, knowing its pro&#8217;s and con&#8217;s, and tailoring an application to the chosen database&#8217;s strengths can only help in the long run.  Also, I feel that building a <em>database model first</em> before any code, offers many performance and scalability advantages than does <em>code first</em> development.</p>
<p>That said, I also see value in using a database as a simple data-store when the actual database is not a key component of the overall application.  That&#8217;s right, it is completely valid to say that the data-storage &amp; database component of an application sometimes is not the key component; a database guy probably will never agree with you there.  Just as there are programmers who swear by this <em>code first, database later</em> mantra, there are database developers that will swear by the <em>database first, code later</em> mantra.</p>
<p>The fact is, each project is unique.  It&#8217;s this uniqueness of projects and their execution that ultimately shapes the perspectives of developers as well as the tools they write and consume.  To say that one mantra is clearly a better choice over another is simply being ignorant.</p>
<h3>The Use Case of Abstraction Layers</h3>
<p>To be honest, I don&#8217;t really buy the &#8220;I might switch database vendors at some point&#8221; argument either, as <a href="http://jeremy.zawodny.com/">Jeremy Zawodny</a> points out.  For larger projects (on the scale of the facebooks, the twitters, etc), switching the database underneath after a project has been in production is a monumental task- regardless if you have an abstraction layer or not.  Chances are, you used some of the database specific features, not to mention, you now have a large set of mission critical <em>data</em> that also has to be ported.  Long story short, its never as easy as swapping the abstraction layers database adapter out.</p>
<p>What I will buy though, is there are some problems that fall in thicker end of the <a href="http://en.wikipedia.org/wiki/Pareto_principle">Pareto Principle</a> that can be solved with a database abstraction layer.  For the uninitiated, the Pareto Principle is effectively the 80/20 rule.  In software use cases, when applying this term- the 80% use case is the majority of use cases.  These use cases are generally <em>not that interesting</em> in terms of database interaction.  To give it a label, we can call these the <a href="http://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a>, BREAD, or  &lt;&lt;insert your favorite terminology here&gt;&gt; operations.  That is not to say that these operations are not important, but they are not special.  In fact, they are so un-special, that we can just about apply a standard query syntax <a href="http://en.wikipedia.org/wiki/SQL-92">(SQL 92)</a> to them, and expect that the query is both portable between databases and common across applications that wish to use them.</p>
<p>This is where database abstraction fits in.  As a developer, you&#8217;ll come across this problem time and time again.  A large portion of an application are CRUD screens and the smaller more interesting part of your application is your reporting screens.  With an abstraction layer, we are able to code against both a unified API as well as have a layer that will produce consistent and vendor compatible queries.  This allows us to build more specialized <a href="http://en.wikipedia.org/wiki/Data_access_layer">data access layers</a> (patterns) for multiple database vendors with great ease.  You want <a href="http://martinfowler.com/eaaCatalog/tableDataGateway.html">Table Gateway</a>- done, you want <a href="http://martinfowler.com/eaaCatalog/rowDataGateway.html">Row Gateway</a>- done, you want <a href="http://martinfowler.com/eaaCatalog/activeRecord.html">Active Record</a>- done.   Each can be implemented to tackle the 80% part of the 80/20 rule when applied to the database centric business code of an application.</p>
<h3>The Slow Path &amp; The Fast Path</h3>
<p>When I talk about this 80/20 rule in terms of the applications we write, I like to further refine the terminology so that it easier to visualize.  The most prominent terms that helps developers visualize the 80/20 rule in their application is the <strong>slow path</strong> of your application, and the <strong>fast path</strong> of your application.  Each of these terms has a set of characteristics that set each apart from one another:</p>
<h4>Slow Path:</h4>
<ul>
<li>Performance is not of primary importance</li>
<li>Has an interactive nature</li>
<li>Validation and verification of data are of high priority</li>
<li>Application to data-store interactions are fairly trivial</li>
<li>Does not comprise applications core business logic</li>
</ul>
<h4>Fast Path:</h4>
<ul>
<li>Performance is of importance</li>
<li>Limited interactive nature, information flow is fairly static (non-interactive)</li>
<li>Flow of information consist of already verified and validated data (originates from the databsae)</li>
<li>Application to data-store interaction can become complex (JOINs, SUB-SELECTS, VIEWS)</li>
<li>Is the core business of the application</li>
</ul>
<p>To get a better understanding of how the terms are applied, lets look at a typical web application.  Generally speaking, there are a few web based forms that users interact with.  These forms are the entry point of a code path that does <em>not</em> get a lot of throughput.  This is generally because forms are submitted by people, and people can only type and submit forms so fast.  In addition to this being a less traveled code path, it also has a few checks along the way- validation of data, and verification of data.  Typically, the problems of verification and validation of data are not too unique to the application being executed.  In fact, the web forms, validation and verification problems have been solved over and over again by various libraries.</p>
<p>On the other side of the equation, there is the aggregation and merging of the stored data (which inevitably came from the aforementioned web forms.)  Since the unique aggregation and processing of this data is the core aspect of business of said application, it stands to reason that this code path will be more well traveled by users.  This, is the fast path.  The problems solved in this code path are generally unique and since they are unique, it&#8217;s hard to find an off the shelf solution to these problems.</p>
<p>Since this is where the money is to be made, it also stands to reason that developers should concentrate their efforts in the fast path of their application.  This means they should solve the slow path problems of their application with existing tried and tested solutions- this includes generic forms solutions, validation and verification libraries and yes, database abstraction layers.</p>
<h3>Getting Cozy With Zend_Db, a Database Abstraction Layer</h3>
<p>Not that we&#8217;ve made a use case for DAL&#8217;s, what would one look like?  Well, I&#8217;ll use Zend Frameworks Zend_Db as my use case.</p>
<p>The connection code:</p>
<pre class="brush: php; title: ; notranslate">
$dbAdapter = Zend_Db::factory(array(
    'adapter' =&gt; 'Pdo_Mysql', // could be Pdo_Sqlite, Mysqli, Pdo_Mysql, Db2, or even Oracle
    'params' =&gt; array(
        'username' =&gt; 'test_user',
        'password' =&gt; 'test_pwd',
        'dbname' =&gt; 'test'
        )
    ));
</pre>
<p>You&#8217;ll note that since this factory takes a standardized array, it makes it trivial to swap out various connection information for different adapters.</p>
<p>Simple queries:</p>
<pre class="brush: php; title: ; notranslate">
$data = array(
    'name'        =&gt; 'Remember the Milk',
    'description' =&gt; '2% Milk'
    'due_on'      =&gt; '2009-07-15',
    );
$dbAdapter-&gt;insert('todo_list', $data); // insert that data

// or
$lastInsertId = $dbAdapter-&gt;lastInsertId('todo_list');
$dbAdapter-&gt;update('todo_list', array('completed' =&gt; 'YES'), 'id = ' . $lastInsertId);

$dbAdapter-&gt;delete('todo_list', 'id = ' . $lastInsertId);
</pre>
<p>Here you&#8217;ll notice the generic and abstracted nature of this API.  Since there are several tasks in database interaction that are consistent across the board, those such as INSERT, UPDATE and DELETE, it makes sense that we can create a generic API for handling such interactions.  These interactions (INSERT, UPDATE and DELETE) represent the mutation methods of a database and as such, represent the most predominant way of getting data into a system.</p>
<p>For all intents and purposes though, simple SELECTs are fairly standardized too.  They are standardized enough as to compliment the INSERT, UPDATE, and DELETE abstractions so that we can find actual rows to do these mutation operations.</p>
<p>Now that we have a simple and consistent API for doing simple SELECTs, INSERTs, UPDATEs, and DELETEs; we can implement something a little more interesting: the <a href="http://framework.zend.com/manual/en/zend.db.table.html">table &amp; row gateway</a>:</p>
<pre class="brush: php; title: ; notranslate">
Zend_Db_Table_Abstract::setDefaultAdapter($dbAdapter);
$userTable = new Zend_Db_Table('user'); // ZF 1.9 feature
$userRow = $table-&gt;find(5); // find user by id 5 (primary key);
echo $userRow-&gt;username;
</pre>
<p>Immediately, you should see the inherent value in the above example.  Rudimentary and common tasks can now be handled with a consistent and simple API.  But what happens when you&#8217;ve started using this DAL, and you want to use a vendor specific feature?  Well..</p>
<pre class="brush: php; title: ; notranslate">
// assuming what you want is really REPLACE or INSERT IGNORE from mysql
$dbAdapter-&gt;query('INSERT IGNORE INTO configuration (name, value) VALUES (?, ?)', array($name, $value));

// OR
$dbAdapter-&gt;query('REPLACE INTO configuration (name, value) VALUES (?, ?)', array($name, $value));
</pre>
<p>As you can see, the query method of our database adapter will allow us to pass custom SQL into the database thus taking advantage of vendor specific features.</p>
<p>What if you want to combine both paradigms for ultimate flexibility?</p>
<pre class="brush: php; title: ; notranslate">

// assuming Zend_Db_Table_Row, with a FriendshipReference rule
$friendRowset = $currentUserRow-&gt;findDependentRowset('User', 'FriendshipReference');

// collect friend id's
foreach ($friendRowset as $friendRow) {
    $friendIds[] = $friendRow-&gt;related_user_id;
}

$inClause = ' IN (' . implode(',', $friendIds) . ')';

$select = $dbAdapter-&gt;select();
$select
    -&gt;from('user', array(
        'user_id',
        'related_user_id',
        'became_friends_on'
        ))
    -&gt;where('user_id ' . $inClause);

// interact with driver directly
$mysqli = $dbAdapter-&gt;getConnection();
$mysqli-&gt;query('CREATE TEMPORARY TABLE friend ('
        . ' `user_id` int(11) NOT NULL,'
        . ' `related_user_id` int(11) NOT NULL,'
        . ' `became_friends_on` DATE NOT NULL'
        . ' ) ENGINE=MEMORY;'
    );
$mysqli-&gt;query('INSERT INTO friend ' . (string) $select);

// query new friend view
$friendTable = new Zend_Db_Table('friend');
$rows = $friendTable-&gt;fetchAll(
    'became_friends_on &gt; DATE_SUB(CURDATE(), INTERVAL 6 MONTH)',
    'became_friends_on'
    );
</pre>
<p>While that above example is &#8220;a bit out there&#8221;, it does show that even with a DAL, if it&#8217;s flexible enough, you can code as close to or as far away from the database as you like.  Ultimately the mantra here is: lets get the job done in the most effective, efficient and sound way possible.</p>
<h3>Conclusions</h3>
<p>Simply put, a database abstraction layer is just another tool in the toolbox.  You don&#8217;t have to completely change your paradigm of programming, nor do you have to apply an all-or-none approach to using a DAL.  When applied correctly, you can build out the slow path of your application in little to no time, while leaving extra time for developing and fine-tuning the fast path of your application.  And to keep code from becoming unruly, simply apply some <a href="http://ralphschindler.com/2009/05/24/php-environments-libraries-and-applications-oh-my">best-practices code organization</a> to your project.</p>
<div class="shr-publisher-26"></div><!-- Start Shareaholic LikeButtonSetBottom --><!-- End Shareaholic LikeButtonSetBottom -->]]></content:encoded>
			<wfw:commentRss>http://ralphschindler.com/2009/07/15/database-abstraction-layers-must-live/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

