<?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>Wiffu &#187; data storage</title>
	<atom:link href="http://wiffu.com/category/data-storage/feed/" rel="self" type="application/rss+xml" />
	<link>http://wiffu.com</link>
	<description>sam keen&#039;s corner of the web</description>
	<lastBuildDate>Sun, 02 May 2010 18:06:22 +0000</lastBuildDate>
	
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Amazon Simple Storage Service [S3]</title>
		<link>http://wiffu.com/amazon-simple-storage-service-s3/</link>
		<comments>http://wiffu.com/amazon-simple-storage-service-s3/#comments</comments>
		<pubDate>Sun, 06 May 2007 22:09:00 +0000</pubDate>
		<dc:creator>sam keen</dc:creator>
				<category><![CDATA[data storage]]></category>

		<guid isPermaLink="false">http://dabbling.in/uncategorized/amazon-simple-storage-service-s3</guid>
		<description><![CDATA[What is S3??
Simple Storage Service: Is a web service that allows any developer to gain access to highly scalable, very reliable, inexpensive storage space. Your data is replicated to multiple servers at multiple data centers.
How to get started
Go to Amazon’s AWS page, then to the S3 page and sign up (check out the other services [...]]]></description>
			<content:encoded><![CDATA[<h2>What is S3??</h2>
<p>Simple Storage Service: Is a web service that allows any developer to gain access to highly scalable, very reliable, inexpensive storage space. Your data is replicated to multiple servers at multiple data centers.</p>
<h2>How to get started</h2>
<p>Go to Amazon’s <a href="http://www.amazon.com/gp/browse.html?node=3435361">AWS</a> page, then to the <a href="http://www.amazon.com/S3-AWS-home-page-Money/b/ref=sc_fe_l_2/102-3892210-9249704?ie=UTF8&amp;node=16427261&amp;no=3435361&amp;me=A36L942TSJ2AJA">S3</a> page and sign up (check out the other services while you are there)</p>
<p>Pricing (From Amazon’s <a href="http://www.amazon.com/S3-AWS-home-page-Money/b/ref=sc_fe_l_2/102-3892210-9249704?ie=UTF8&amp;node=16427261&amp;no=3435361&amp;me=A36L942TSJ2AJA">site</a>…)</p>
<blockquote><p><em>Pricing</em></p>
<p><em>…</em></p>
<p><em>New Pricing (effective June 1st, 2007)</em></p>
<p><em><strong>Storage</strong><br />$0.15 per GB-Month of storage used</em></p>
<p><em><strong>Data Transfer</strong><br />$0.10 per GB &#8211; all data uploaded</em></p>
<p><em>$0.18 per GB &#8211; first 10 TB / month data downloaded<br />$0.16 per GB &#8211; next 40 TB / month data downloaded<br />$0.13 per GB &#8211; data downloaded / month over 50 TB</em></p>
<p><em>Data transferred between Amazon S3 and Amazon EC2 is free of charge</em></p>
<p><em><strong>Requests</strong><br />$0.01 per 1,000 PUT or LIST requests<br />$0.01 per 10,000 GET and all other requests*<br />* No charge for delete requests</em></p>
<p><em>Storage and bandwidth size includes all file overhead</em></p>
</blockquote>
<p>I looked around the web for similar services (hard to find someone that posts prices), and for 180 gig’s of reduntantly stored data, it was in the $200/month price range.</p>
<p>The same 180Gb on S3 would be</p>
<p>$27 to store for 30 days</p>
<p>$18 to xmit (the entire 180Gb) to S3</p>
<p>In addition to price, with S3, you are in full control of <strong>how</strong> and <strong>when</strong> you put and/or get your data.</p>
<h2>Only pay for what you actually use</h2>
<p>One of the niceties about S3 (and the other Amz web services) is that you pay just for what you use.</p>
<p><em>Didn’t use that service last month:</em> <strong>Pay $0.</strong></p>
<p><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_-U2xCN0FOWI/SG5ao9W5UZI/AAAAAAAAABE/rNNV_RZeMPE/s1600-h/usage-report-11.png"><img style="cursor: pointer;" src="http://bp0.blogger.com/_-U2xCN0FOWI/SG5ao9W5UZI/AAAAAAAAABE/rNNV_RZeMPE/s320/usage-report-11.png" alt="" id="BLOGGER_PHOTO_ID_5219208677880844690" border="0" /></a></p>
<p>This allows you to ‘tinker’ all you want for mere pennies</p>
<h2>Amazon S3 &#8211; Objects</h2>
<h2>    What is an Object?</h2>
<p><strong><em>Object</em></strong> is the term we use in S3 for the ‘thing’ (file/data) you want to store.<br />Once an object is stored in S3, it contains the original data (contents of the file), plus a given amount of meta-data (name/value pairs).</p>
<p>You can add your own metadata but some of the standards are ‘<em>Last-Modified</em>‘ and ‘<em>Content-Type</em>‘</p>
<p>A given Object can be from 1byte to 5GBs</p>
<h2>Amazon S3 &#8211; Buckets</h2>
<h2>Why Buckets?</h2>
<p>Buckets provides a unique namespace for management of objects contained in the bucket</p>
<p>Bucket namespaces are Global across all of S3 (all users of S3.  Similar concept as ‘<em>domain names</em>‘)</p>
<p>An S3 account is allowed 100 buckets</p>
<h2>Amazon S3 &#8211; Keys</h2>
<h2>Key</h2>
<p>A key is the unique identifier for an <strong><em>object</em></strong> within a <strong><em>bucket</em></strong></p>
<h2>Locating an object</h2>
<p>Any Object can be located by its [<em>bucket + key</em>] using a <a href="http://www.xfront.com/REST-Web-Services.html">RESTful</a> formatted URL</p>
<pre>   <em>http://s3.amazonaws.com/foo-products/2006/may/1845.prd</em></pre>
<p><em><br /></em></p>
<p><em>foo-products</em> is the <strong>bucket</strong> &amp; <em>2006/may/1845.prd</em> is the <strong>Key</strong></p>
<h3>S3 &#8211; Authentication</h3>
<p>Most requests to S3 require authentication, this ensures that you don’t get charged for operations you didn’t authorize, and that nobody else sees your private data.</p>
<p>You can grant various access models (acl) for an <strong>Object</strong> or an entire <strong>Bucket</strong></p>
<ul>
<li>private</li>
<li>public-read</li>
<li>public-read-write</li>
<li>authenticated-read</li>
</ul>
<p>To set the ACL, when you PUT the Object to S3, you set a x-amz-acl header.  For example…</p>
<pre>x-amz-acl: public-read</pre>
<p>ACl defaults to private if not set on the PUT</p>
<h2>S3: Putting it all Together</h2>
<h2>How do you speak to S3?</h2>
<p>At this point, all interaction is done with the HTTP protocol (the current exception is that you can retrieve objects using http or BitTorrent).</p>
<p>So, creating a program to interact with S3 is just a matter of creating HTTP requests and reading HTTP responses. Something PHP is quite capable of (especially with a little help from PEAR <a href="http://pear.php.net/package/HTTP_Request">HTTP_Request</a> and <a href="http://pear.php.net/package/Crypt_HMAC/">Crypt_HMAC</a>)</p>
<h2>First get an account and your Keys</h2>
<ul>
<li><strong>Access Key ID:</strong> You add this to any requests to S3. Essentially it is your unique identifier that tells S3 a given request is targeted for your account.</li>
<li><strong>Secret Access Key:</strong> For requests to S3 for Objects with acl’s that require authentication (i.e. <em>private</em>, <em>authenticated-read</em>), you ’sign’ your request with this <strong>secret</strong> key.</li>
</ul>
<p>The following code examples are based on what is in the Amazon S3 developer <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/gsg/">docs</a></p>
<h2>Create a Bucket</h2>
<pre class="aws-code"># create bucket request

PUT /[bucket-name] HTTP/1.0Date: Wed, 08 May 2007 08:45:09 GMTAuthorization: AWS [aws-access-key-id]:[header-signature]Host: s3.amazonaws.com

# create bucket response

HTTP/1.1 200 OKx-amz-id-2: VjzdTviQorQtSjcgLshzCZSzN+7CnewvHA+6sNxR3VRcUPyO5fmSmo8bWnIS52qax-amz-request-id: 91A8CC60F9FC49E7Date: Wed, 08 Mar 2006 04:06:15 GMTLocation: /[bucket-name]Content-Length: 0Connection: keep-aliveServer: AmazonS3</pre>
<h2>Put Objects in your Bucket</h2>
<pre class="aws-code"># put object request

PUT /[bucket-name]/[key-name] HTTP/1.0Date: Wed, 08 Mar 2006 04:06:16 GMTAuthorization: AWS [aws-access-key-id]:[header-signature]Host: s3.amazonaws.comContent-Length: 14x-amz-meta-title: my titleContent-Type: text/plain

this is a test

# put object response

HTTP/1.1 200 OKx-amz-id-2: wc15E1LUrjDZhNtT4QZtsbtadnOMKGjw5QTxkRDVO1owwbA6YoiqJJEuKShopufwx-amz-request-id: 7487CD42C5CA7524Date: Wed, 08 Mar 2006 04:06:16 GMTETag: "54b0c58c7ce9f2a8b551351102ee0938"Content-Length: 0Connection: keep-aliveServer: AmazonS3</pre>
<h2>Retrieve Objects from your bucket</h2>
<pre># get object request</pre>
<pre>GET /[bucket-name]/[key-name] HTTP/1.0Date: Wed, 08 Mar 2006 04:06:18 GMTAuthorization: AWS [aws-access-key-id]:[header-signature]Host: s3.amazonaws.com</pre>
<pre># get object response</pre>
<pre>HTTP/1.1 200 OKx-amz-id-2: FbGpiykb9oJEdJd0bcfwkL6S3lc06X0y7XSeA/GWyRdvlNEZ0irthljxKoeGFfB6x-amz-request-id: 9298531013923634Date: Wed, 08 Mar 2006 04:06:18 GMTLast-Modified: Wed, 08 Mar 2006 04:06:16 GMTETag: "54b0c58c7ce9f2a8b551351102ee0938"x-amz-meta-title: my titleContent-Type: text/plainContent-Length: 14Connection: keep-aliveServer: AmazonS3</pre>
<pre>this is a test</pre>
<h2>S3 &#8211; The PHP way</h2>
<h2>Implementing an API to S3 with PHP</h2>
<h2>Prerequisites</h2>
<p>You’ll need the <strong>PEAR</strong> libraries <strong>Crypt_HMAC</strong> &amp; <strong>HTTP_Request</strong> (<em>at least things are much easier if you have these</em>)</p>
<pre># sudo pear install Crypt_HMACpear.php.net" to updatedownloading Crypt_HMAC-1.0.1.tgz ...Starting to download Crypt_HMAC-1.0.1.tgz (2,149 bytes)....done: 2,149 bytesinstall ok: channel://pear.php.net/Crypt_HMAC-1.0.1</pre>
<pre>sam$ sudo pear install HTTP_Requestpear.php.net" to updatedownloading HTTP_Request-1.4.0.tgz ...Starting to download HTTP_Request-1.4.0.tgz (15,262 bytes).....done: 15,262 bytesdownloading Net_URL-1.0.14.tgz ...Starting to download Net_URL-1.0.14.tgz (5,173 bytes)...done: 5,173 bytesdownloading Net_Socket-1.0.7.tgz ...Starting to download Net_Socket-1.0.7.tgz (5,419 bytes)...done: 5,419 bytesinstall ok: channel://pear.php.net/Net_URL-1.0.14install ok: channel://pear.php.net/Net_Socket-1.0.7install ok: channel://pear.php.net/HTTP_Request-1.4.0</pre>
<h2>Creating the API</h2>
<p>At this point all you really need to do is create a function for each needed interaction with S3 (or better yet, a PHP Object with a method for each). So something like…</p>
<pre>createBucket()</pre>
<pre>putObject()</pre>
<pre>getObject()</pre>
<pre>getBucketListing()</pre>
<pre>   ...</pre>
<p>These functions are going to be creating http requests and reading http responses. Sometimes this can be a bit tricky (one missing ‘\n’ and you’re screwed), so leverage what what other have done befor you. The Amazon web services <a href="http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=46">site</a> has some good examples but in particular, I would recommend you look at ‘<a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=482">Test Utility for Amazon S3 in PHP</a>‘ which does a good job of demo’ing most of the S3 functionality using PHP.</p>
<p><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_-U2xCN0FOWI/SG5bLLsu_1I/AAAAAAAAABM/nrBpOrUD5zM/s1600-h/example-php-s3-1.png"><img style="cursor: pointer;" src="http://bp2.blogger.com/_-U2xCN0FOWI/SG5bLLsu_1I/AAAAAAAAABM/nrBpOrUD5zM/s320/example-php-s3-1.png" alt="" id="BLOGGER_PHOTO_ID_5219209265846091602" border="0" /></a></p>
<p>I used this code as a starting point to develop a very simple ‘Rsync’ type application for Amazon S3.<span style="text-decoration: underline;"><br /></span></p>
<p><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_-U2xCN0FOWI/SG5bhMBVlOI/AAAAAAAAABU/9Vbe9Pc_FUY/s1600-h/s3-sync-1.png"><img style="cursor: pointer;" src="http://bp1.blogger.com/_-U2xCN0FOWI/SG5bhMBVlOI/AAAAAAAAABU/9Vbe9Pc_FUY/s320/s3-sync-1.png" alt="" id="BLOGGER_PHOTO_ID_5219209643889628386" border="0" /></a><span style="text-decoration: underline;"></span></p>
<p> <span style="text-decoration: underline;"></span><br />
<h2>Resources</h2>
<ul>
<li><a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=123&amp;categoryID=48">Documentation from Amazon</a></li>
<li><a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=482&amp;categoryID=47">Example PHP implementation</a></li>
<li>Who’s Using S3 <a href="http://www.amazon.com/Success-Stories-AWS-home-page/b/ref=sc_fe_c_0_16427261_3/102-3892210-9249704?ie=UTF8&amp;node=182241011&amp;no=16427261&amp;me=A36L942TSJ2AJA">here</a> and <a href="http://solutions.amazonwebservices.com/connect/kbcategory.jspa?categoryID=66">here</a></li>
<li>My <a href="http://code.google.com/p/s3-sync/">Rsync App</a> (Proof of Concept at this point)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://wiffu.com/amazon-simple-storage-service-s3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Find and Replace in Mysql</title>
		<link>http://wiffu.com/find-and-replace-in-mysql/</link>
		<comments>http://wiffu.com/find-and-replace-in-mysql/#comments</comments>
		<pubDate>Sat, 17 Feb 2007 04:20:00 +0000</pubDate>
		<dc:creator>sam keen</dc:creator>
				<category><![CDATA[data storage]]></category>

		<guid isPermaLink="false">http://dabbling.in/uncategorized/find-and-replace-in-mysql</guid>
		<description><![CDATA[Simple but very useful MySQL trick&#8230;
UPDATE `table`  SET `field` = REPLACE (
`field` ,
&#8216;{string to find}&#8217;,
&#8216;{replacement string}&#8217;
)
]]></description>
			<content:encoded><![CDATA[<p>Simple but very useful MySQL trick&#8230;</p>
<p>UPDATE `table`  SET `field` = REPLACE (<br />
`field` ,<br />
&#8216;{string to find}&#8217;,<br />
&#8216;{replacement string}&#8217;<br />
)</p>
]]></content:encoded>
			<wfw:commentRss>http://wiffu.com/find-and-replace-in-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP Data Objects</title>
		<link>http://wiffu.com/php-data-objects/</link>
		<comments>http://wiffu.com/php-data-objects/#comments</comments>
		<pubDate>Sat, 27 Jan 2007 05:41:00 +0000</pubDate>
		<dc:creator>sam keen</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[data storage]]></category>

		<guid isPermaLink="false">http://dabbling.in/uncategorized/php-data-objects</guid>
		<description><![CDATA[DataObject is a design pattern that incapsualtes the database with a simple to use interface. This alows the Controller to interact with the database layer in an SQL&#8217;less way. To acomplish this you build a base DataObject abstract class that implements the base functionality and sets up the interface for a Data Object. Then for [...]]]></description>
			<content:encoded><![CDATA[<p>DataObject is a design pattern that incapsualtes the database with a simple to use interface. This alows the Controller to interact with the database layer in an SQL&#8217;less way. To acomplish this you build a base DataObject abstract class that implements the base functionality and sets up the interface for a Data Object. Then for each of your &#8216;Business Objects&#8217; (such as customer), you extend the DataObject class and implement the various methods provided by DataObject. (<em>see attached code for details</em>).</p>
<p>You&#8217;ll in the posted PHP files below that the code gets more &#8216;complex&#8217; as we move down the stack (Controller->BusinessObject->DataObject). This is the beauty of OOP since we have implemented the DataObject only once in our application (it may be used by many Business Objects). As long as we maintain its interface, we are free to refactore its implementation details and add functionallity. And there is only one file to change.</p>
<h4>Retrival of object from the database</h4>
<p>In the simple case of the controller needing to retrieve a customer. Traditionally this might involve building a query of something like&#8230;</p>
<p><textarea name="code" class="php"><br />$query = &#8220;Select * from customer where customer_id = 1&#8243;<br />$result = $db->execute($query);<br />$customer = $result->fetch();<br /></textarea>
<p></p>
<p>This process involes the Controller having far too much knowelge of the Database layer.  This tight coupling should be avoided.</p>
<p>The same process using a database object would go something like this&#8230;</p>
<p><textarea name="code" class="php"><br />$customer = new CustomerDO($db_handle);<br />$customer->get(1);<br /></textarea><br />
<h4>Search the database for an Object</h4>
<p>This is quite elegant with DO&#8217;s. Just simply create a new DO, set what you do know about what you are seaching for then execute the object find method.</p>
<p><textarea name="code" class="php"><br />$customerDO = new CustomerDO($db);<br />$customerDO->last_name = &#8220;smith&#8221;;<br />$customers = $customerDO->find();<br /></textarea><br />
<h4>Saving and Updating Objects</h4>
<p>Again this is very simple also. For a populated customer DO object, just issue its insert method and to update, use the update method.</p>
<p><textarea name="code" class="php"><br />$customer->insert();<br />$customer->first_name = &#8220;Sir Charles&#8221;;<br />$customer->update();<br /></textarea><br />
<h3>Here is a Proof of Concept Controller PHP script</h3>
<p><textarea name="code" class="php"><br />/**<br /> * This is just a Proof Of Concept, this is not Production code.<br /> *<br /> * This is the controller script, that in a very simple<br /> * way allows you to Insert/update and delete customers<br /> * using the Data Object Class.<br /> */<br />require(&#8220;../includes/bootstrap.php&#8221;);<br />require_once(&#8216;CustomerDO.php&#8217;);<br />$feedback = &#8220;&#8221;;<br />// collect the id of the image to edit<br />$customer_id = isset($_GET['customer_id']) ? hd($_GET['customer_id']) : null;<br />/**<br /> * Create the CustomerDO object.  The DB connection<br /> * is an argument of the constructor (Dependency Injection)<br /> */<br />$customer = new CustomerDO($db);<br />/**<br /> * If there is a customer id provided, retrieve it from the DB<br /> */<br />if ($customer_id) {<br />  $customer->get($customer_id);<br />}<br />if (isset($_GET['submit'])) {<br />  $customer->first_name = hd($_GET['first_name']);<br />  $customer->last_name = hd($_GET['last_name']);<br />  if($customer->customer_id){<br />    $customer->update();<br />    $feedback = &#8220;Update Complete&#8221;;<br />  } else {<br />    $customer->insert();<br />    $feedback = &#8220;Creation Complete&#8221;;<br />  }<br />} else if(isset($_GET['delete']) &amp;&amp; $customer->customer_id) {<br />  $customer->delete();<br />  $feedback = &#8220;Delete Complete&#8221;;<br />}<br />// Set values for the template<br />$first_name = $customer->first_name;<br />$last_name = $customer->last_name;<br />$customer_id = $customer->customer_id;<br />// Just using simple include rather than template engine(i.e. Smarty)<br />include(&#8220;customer/index.tpl&#8221;);<br /></textarea><br />
<h3>Here is the Customer Data Object that extends DataObject</h3>
<p><textarea name="code" class="php"><br />require_once(&#8216;DataObject.php&#8217;);<br />/**<br /> * This is the Business Object class for Customer.<br /> * It extends the DataObject class and implemets<br /> * the needed interface methods of DataObject<br /> *<br /> */<br />class CustomerDO extends DataObject {<br />  /**<br />   * Specific C.R.U.D. queries for Customer<br />   *<br />   */<br />  const QUERY_GET = &#8220;<br />    SELECT *<br />    FROM customer<br />    WHERE<br />    customer_id = ?&#8221;;<br />  const QUERY_INSERT = &#8220;<br />    INSERT INTO customer<br />    SET<br />    `customer_id` = ?,<br />    `first_name` = ?,<br />    `last_name` = ?,<br />    `create_date` = NOW(),<br />    `modified_date` = NOW()&#8221;;<br />  const QUERY_UPDATE = &#8220;<br />    UPDATE customer<br />    SET<br />    `first_name` = ?,<br />    `last_name` = ?,<br />    `modified_date` = NOW()<br />    WHERE<br />    customer_id = ?&#8221;;<br />  const QUERY_DELETE = &#8220;<br />    DELETE FROM customer<br />    WHERE<br />    customer_id = ?&#8221;;<br />  const QUERY_FIND = &#8220;<br />    SELECT *<br />    FROM customer&#8221;;<br />  /**<br />   * Allow for an optional Database handle param.  This<br />   * Inversion Of Control allows for ease of unit testing. i.e. you<br />   * can supplay a mock database object<br />   *<br />   * @param Object $db DatabaseHandle<br />   */<br />  public function __construct($db=null) {<br />    $this->init();<br />    $this->db = $db;<br />  }<br />  /**<br />   * Set up the initial values for this objects<br />   * attributes.<br />   *<br />   */<br />  private function init() {<br />    $this->attributes = array(<br />      &#8216;customer_id&#8217; => &#8221;,<br />      &#8216;first_name&#8217; => &#8221;,<br />      &#8216;last_name&#8217; => &#8221;<br />    );<br />  }<br />  /**<br />   * Retrieve a specific object by its primary key<br />   *<br />   * @param mixed $data_object_id<br />   */<br />  public function get($data_object_id) {<br />    parent::get($data_object_id, self::QUERY_GET);<br />  }<br />  /**<br />   * Persist a new object to the DB<br />   *<br />   */<br />  public function insert() {<br />    $this->customer_id = $this->db->nextId(parent::DB_SEQUENCE_TABLE);<br />    $params = array(<br />      $this->customer_id,<br />      $this->first_name,<br />      $this->last_name<br />    );<br />    $result = $this->db->query(self::QUERY_INSERT ,$params);<br />    db_check_error($result);<br />  }<br />  /**<br />   * Update an existing Object in the DB<br />   *<br />   */<br />  public function update() {<br />    $params = array(<br />      $this->first_name,<br />      $this->last_name,<br />      $this->customer_id<br />    );<br />    $result = $this->db->query(self::QUERY_UPDATE ,$params);<br />    db_check_error($result);<br />  }<br />  /**<br />   * Permanently remove an object from persistence<br />   *<br />   */<br />  public function delete() {<br />    $params = array($this->customer_id);<br />    $row = $this->db->query(self::QUERY_DELETE ,$params);<br />    db_check_error($row);<br />  }<br />  /**<br />   * Query for all records matching the state of the current<br />   * Object.<br />   *<br />   * @param boolean $as_detached_recordset<br />   * @param string $order_by_field &#8220;{fieldname} {ASC | DESC}&#8221;<br />   * @param string $group_by_fields &#8220;{fieldname1, fieldname2&#8230;}&#8221;<br />   * @return ReadOnlyResultSet or array if $as_detached_recordset = true<br />   */<br />  public function find($as_detached_recordset=false, $order_by_field=false,<br />    $group_by_fields=false, $return_structure=false) {<br />    return parent::find(self::QUERY_FIND, $as_detached_recordset,<br />    $order_by_field, $group_by_fields,$return_structure);<br />  }<br />  /**<br />   * Utility function to retrieve the number of objects in persistance<br />   * matching a given &#8216;find&#8217;<br />   *<br />   * @return unknown<br />   */<br />  public function count() {<br />    return count($this->find(true));<br />  }<br />}<br /></textarea><br />
<h3>And here is the Base Data Object Class</h3>
<p><textarea name="code" class="php"><br />/**<br />* This is the base DataObject Class.<br />*<br />* @package default<br />* @author Sam<br />**/<br />abstract class DataObject {<br /> protected $attributes;<br /> protected $db;<br /> // for debugging use<br /> public $sql;<br /> protected $attrib_to_dbfield_mapping = array();<br /> /**<br />     * Default get implementation<br />     *<br />     * @param unknown_type $newsletter_id<br />     */<br /> protected function get($do_id, $get_query) {<br />  $params = array($do_id);<br />  $row = $this->db->getRow($get_query ,$params,DB_FETCHMODE_ASSOC);<br />  db_check_error($row);<br />  if ($row) {<br />   foreach ($this->attributes as $class_attribute => $value) {<br />    if (key_exists($class_attribute,$row)) {<br />     $this->$class_attribute = $row[$class_attribute];<br />    }<br />   }<br />  }<br /> }<br /> protected abstract function insert();<br /> protected abstract function update();<br /> protected abstract function delete();<br /> protected abstract function count();<br /> const DB_SEQUENCE_TABLE = &#8217;sequence_ids&#8217;;<br /> /**<br />  * @todo implement OR, right now only AND is supported&#8230;<br />  */<br /> const CONJUNCTION_AND = &#8216;AND&#8217;;<br /> const CONJUNCTION_OR = &#8216;OR&#8217;;<br /> public function __get ($attribute) {<br />  if (! key_exists($attribute,$this->attributes)) {<br />   throw new Exception(&#8220;Attribute: [$attribute], not defined for &#8220;.__CLASS__);<br />  }<br />  return $this->attributes[$attribute];<br /> }<br /> public function __set ($attribute, $value) {<br />  if (! key_exists($attribute,$this->attributes)) {<br />   throw new Exception(&#8220;Attribute: [$attribute], not defined for &#8220;.__CLASS__);<br />  }<br />  $this->attributes[$attribute] = $value;<br /> }<br /> public function getAttributeList() {<br />  return array_keys($this->attributes);<br /> }<br /> /**<br />  * Used to build clauses for sql queries.<br />  * Assumptions:<br />  * &#8211; All date values are supplied as UNIX timestamps<br />  *<br />  * @param string $field_name<br />  * @param string $clause The expected format of the $clause param is<br />  * &#8211; &#8216;{ <= | <|>= |> } {value}&#8217;<br />  * &#8211; i.e. &#8220;>= 17645329&#8243;<br />  * &#8211; i.e. &#8220;!= Y&#8221;<br />  * &#8211; i.e. &#8220;Y&#8221;  // = (equals) is inferred by default<br />  * a space is required to seperate the boolean clause and<br />  * the value.  If no boolean clause is given, &#8216;=&#8217; is<br />  * assumed.<br />  */<br /> protected function buildWhereClause($field_name, $clause) {<br />  $clause_boolean = &#8216;=&#8217;; //default is equals &#8216;=&#8217;<br />  $clause_righthand_value = $clause;<br />  $hack_booleans = array(&#8216;<=','>=&#8217;,&#8217;<>&#8216;,&#8217;=',&#8217;!=&#8217;,&#8217;<','>&#8216;);<br />  foreach ($hack_booleans as $boolean) {<br />   if (strpos($clause,$boolean)===0) {<br />    $clause_boolean = substr($clause,0,strlen($boolean));<br />    $clause_righthand_value = trim(substr($clause,strlen($boolean)+1));<br />    break;<br />   }<br />  }<br /> }<br /> /**<br />  * check date special case (righthand value should be a timestamp)<br />  * &#8211; be sure to use the attribute name if it exists (rather than the db field name).  That way we can<br />  * rely on the &#8216;_date&#8217; naming convention.<br />  */<br /> $date_attribute_name = array_search($field_name,$this->attrib_to_dbfield_mapping)<br />  ? array_search($field_name,$this->attrib_to_dbfield_mapping)<br />  : $field_name;<br /> if (strstr($date_attribute_name,&#8217;_date&#8217;)) {<br />  if (! is_numeric($clause_righthand_value)) {<br />   throw new Exception(&#8220;The righthand value for [{$field_name}]: not in correct format.  Should be {boolean} {unix timestamp}.  (&#8220;<br />    .&#8221;i.e. usage: &#8216;expire_date&#8217; => &#8216;>= &#8216;.time())&#8221;);<br />  }<br />  $where = &#8220;`{$field_name}` {$clause_boolean} FROM_UNIXTIME(&#8216;&#8221; . mysql_escape_string($clause_righthand_value) . &#8220;&#8216;)&#8221;;<br />  //  Not date, so put clause together normally<br /> } else {<br />  $where = &#8220;`{$field_name}` {$clause_boolean} &#8216;&#8221; . mysql_escape_string($clause_righthand_value) . &#8220;&#8216;&#8221;;<br /> }<br /> return $where;<br /> }<br /> /**<br />  * Maps DO attribute names to DB field names.  If a mapping for<br />  * the given attribute does not exist, the attribute name is simply<br />  * returned<br />  *<br />  * @param string $attribute_name<br />  * @return string<br />  */<br /> protected function mappedAttributeName($attribute_name) {<br />  return isset($this->attrib_to_dbfield_mapping[$attribute_name])<br />   ? $this->attrib_to_dbfield_mapping[$attribute_name]<br />   : $attribute_name;<br /> }<br /> /**<br />  * Query for all records matching the state of the current<br />  * Object.<br />  *<br />  * @param strinh $find_query The sql query (w/out the where clause)<br />  * @param boolean $as_detached_recordset<br />  * @param string $order_by_field &#8220;{fieldname} {ASC | DESC}&#8221;<br />  * @param string $group_by_fields &#8220;{fieldname1, fieldname2&#8230;}&#8221;<br />  * @return ReadOnlyResultSet or array if $as_detached_recordset = true<br />  */<br /> public function find($find_query, $as_detached_recordset=false, $order_by_field=false, $group_by_fields=false, $return_structure=false, $limit_terms=false) {<br />  if (! $as_detached_recordset &amp;&amp; $return_structure) {<br />   throw new Exception(&#8216;Usage: If you requests a $return_structure, you must specify $as_detached_recordset=true&#8217;);<br />  }<br />  $where_clauses = array();<br />  // Loop through the properties build individual where clauses<br />  foreach ($this->attributes as $attrib_key=>$attrib_values) {<br />   /*<br />    * attribute values can have multi value so force to array &amp;<br />    * deal only with the array case.<br />    * This allows you to define something like<br />    * WHERE<br />    * username != &#8216;Anonymous&#8217; AND<br />    * username != &#8221;<br />    */<br />   $attrib_values = is_array($attrib_values) ? $attrib_values : array($attrib_values);<br />   foreach ($attrib_values as $attrib_value) {<br />    if (trim($attrib_value)!=&#8221;) {<br />     $where_clauses[] = $this->buildWhereClause($this->mappedAttributeName($attrib_key), $attrib_value);<br />    }<br />   }<br />  }<br />  // If we have a where clause, build it<br />  $this->sql = $find_query;<br />  if (count($where_clauses)> 0){<br />   $this->sql .= &#8221; WHERE &#8221; . implode(&#8216; &#8216;.DataObject::CONJUNCTION_AND.&#8217; &#8216;, $where_clauses);<br />  }<br />  if ($group_by_fields) {<br />   $group_by_fields = explode(&#8216;,&#8217;,$group_by_fields);<br />   $group_by_fields = array_map(&#8216;trim&#8217;,$group_by_fields);<br />   $this->sql .= &#8221; GROUP BY `&#8221;.implode(&#8220;`, `&#8221;,$group_by_fields).&#8221;`&#8221;;<br />  }<br />  if ($order_by_field) {<br />   $order_by_field = explode(&#8216;,&#8217;,$order_by_field);<br />   foreach ($order_by_field as &amp;$order_by) {<br />    $direction = stristr(&#8216;DESC&#8217;,$order_by)<br />     ? &#8220;DESC&#8221;<br />     : &#8220;ASC&#8221;;<br />    $order_by = str_ireplace(array(&#8220;DESC&#8221;,&#8221;ASC&#8221;),&#8221;,$order_by);<br />    $order_by = &#8220;`&#8221;.trim($order_by).&#8221;`&#8221;.&#8221; {$direction} &#8220;;<br />   }<br />   $this->sql .= &#8221; ORDER BY &#8220;.implode(&#8220;`, `&#8221;,$order_by_field);<br />  }<br />  if ($limit_terms) {<br />   $this->sql .= &#8221; LIMIT &#8220;.$limit_terms;<br />  }<br />  $results = null;<br />  if ($as_detached_recordset) {<br />   $results = $this->db->getAll($this->sql,array(),DB_FETCHMODE_ASSOC);<br />   db_check_error($results);<br />   if($return_structure) {<br />    $results = $this->mapReturnStructure($results,$return_structure);<br />   }<br />   return $results;<br />  } else {<br />   $result_set = $this->db->query($this->sql);<br />   db_check_error($result_set);<br />   require_once(&#8216;ReadOnlyResultSet.php&#8217;);<br />   return new ReadOnlyResultSet($result_set);<br />  }<br /> }<br /> /**<br />  * Takes the indexed array result of a query and re-structures it to<br />  * the supplied assocc array.<br />  * &#8211; ie<br />  * <code><br />  * if $result_data is<br />  *     [0]=>array('name'=>'foo','age'=>10, 'height'=>6.5, 'weight'=>100),<br />  *     [1]=>array('name'=>'bar','age'=>13, 'height'=>6.5, 'weight'=>120)<br />  * and $return_structure is<br />  *     'name'=>array('age','weight')<br />  * then the final returned array would be...<br />  *     'foo'=>array('age'=>10,'weight'=>100),<br />  *     'bar'=>array('age'=>13,'weight'=>120)<br />  *<br />  * if there is only 1 array value in the return structure..<br />  *     i.e. 'name'=>'age' (or 'name'=>array('age')<br />  * then the final returned array would be...(no keys on value)<br />  *     'foo'=>10,<br />  *     'bar'=>13<br />  * </code><br />  *<br />  * @param array $result_data<br />  * @param array $return_structure<br />   */<br /> private function mapReturnStructure($result_data, $return_structure) {<br />  $result = array();<br />  foreach ($result_data as $datum) {<br />   $key = key($return_structure);<br />   $struct_values = is_array($return_structure[$key]) ? $return_structure[$key] : array($return_structure[$key]);<br />   foreach ($datum as $datum_key => $datum_value) {<br />    if (in_array($datum_key,$struct_values)) {<br />     if (count($struct_values)>1) {<br />      $result[$datum[$key]][$datum_key] = $datum_value;<br />     } else {<br />      $result[$datum[$key]] = $datum_value;<br />     }<br />    }<br />   }<br />  }<br />  return $result;<br /> }<br />}<br /></textarea></p>
]]></content:encoded>
			<wfw:commentRss>http://wiffu.com/php-data-objects/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
