PhpRiot
Become Zend Certified

Prepare for the ZCE exam using our quizzes (web or iPad/iPhone). More info...


When you're ready get 7.5% off your exam voucher using voucher CJQNOV23 at the Zend Store

Advanced OOP With SPL In PHP 5

Handling Returned Data: UnbufferedAssociativeResultSet

You may have noticed that the PostgreSQL class did not return the query result variable directly. This is because it would defeat the purpose of the interface because, as you likely know, in PHP all the different databases have different ways of working with their result resources.

The PostgreSQL class used an UnbufferedAssociativeResultSet object.

Listing 4 UnbufferedAssociativeResultSet.php
<?php
    /**
     * UnbufferedAssociativeResultSet
     *
     * Allows for unified access to a database result set
     *
     * @implements  ArrayAccess, Iterator, Countable
     * @remarks     If not using 5.1, implementing countable can be and count() used directly.
     * @see         SPL Documentation For Interface Declarations.
     */
    class UnbufferedAssociativeResultSet implements ArrayAccess, Iterator, Countable
    {
        private $currentIndex, $result;
 
        function __construct($result)
        {
            $this->currentIndex = 0;
            $this->result = $result;
        }
 
        //Region ArrayAccess
        function offsetExists($offset)
        {
            switch($configuration['databasetype']) {
                case 'PostgreSQL':
                    if(pg_fetch_assoc($this->result, $offset)) {
                        return true;
                    } else {
                        return false;
                    }
                default:
                    throw new Exception("No Database Handler");
            }
        }
 
        function offsetGet($offset)
        {
            switch($configuration['databasetype']) {
                case 'PostgreSQL':
                    if($row = pg_fetch_assoc($this->result, $offset)) {
                        return $row;
                    } else {
                        throw new Exception("No row at ". $offset);
                    }
                default:
                    throw new Exception("No Database Handler");
            }
        }
 
        function offsetSet($offset,$value)
        {
            throw new Exception("This collection is read only.");
        }
 
        function offsetUnset($offset)
        {
            throw new Exception("This collection is read only.");
        }
        //EndRegion
 
        //Region Countable
        function count()
        {
            switch($configuration['databasetype']) {
                case 'PostgreSQL':
                    if($rows = pg_num_rows($this->result)) {
                        return $rows;
                    } else {
                        throw new Exception("Could not fetch the number of rows in resultset");
                    }
                default:
                    throw new Exception("No Database Handler");
            }
        }
        //EndRegion
 
        //Region Iterator
        function current()
        {
            return $this->offsetGet($this->currentIndex);
        }
 
        function key()
        {
            return $this->currentIndex;
        }
 
        function next()
        {
            return $this->currentIndex++;
        }
 
        function rewind()
        {
            $this->currentIndex = 0;
        }
 
        function valid()
        {
            if($this->offsetExists($this->currentIndex)) {
                return true;
            } else {
                return false;
            }
        }
 
        function append($value)
        {
            throw new Exception("This collection is read only");
        }
 
        function getIterator()
        {
            return $this;
        }
        //EndRegion
    }
?>

The above object creates an array from a result set without requiring the data to be loaded until its needed.

You can use this array just as a numerical array and each index will return an associative array containing the keys in the result set.

For compactness I have left the code commenting off this class and used simple inline exceptions. You will want to use Exception derived objects to throw exceptions like ReadOnlyException.

This class uses a switch in each database dependent function. It is also possible to use a second factory class to spin up a database-specialized class for this result set, then provide an interface for its use in the application. However, this adds a lot of extra files the fray and offers no real benefits. Whether to do it as a single class or another factory is entirely up to you.

Usage Examples:

Listing 5 listing-5.php
<?php
    $uid = 12345;
    $db = DatabaseFactory::factory();
    $ubars = $db->getCustomerDetails($uid);
 
    if($ubars[0]['property'] == "Name") {
    echo "The first property was 'name'";
    }
 
    foreach($ubars as $values) {
        foreach($values as $key=>$value) {
            echo $key . '=' . $value;
        }
    }
 
    $rows = count($ubars);
?>

In This Article