Sunday, May 27, 2007

A PDOIterator class using SPL

While writing about the scalability issues of using function calls like file_get_contents and PDOStatement::fetchAll, I wrote the following PDOIterator class, which I have every intention of using for a while. It makes it very easy to keep queries in the application logic while keeping the retrieval of the results in the output, without using any database driver calls. Because it implements the Iterator interface from SPL, the template can use a foreach loop just like it would if the application had passed the results from PDOStatement::fetchAll.

class PDOIterator implements Iterator {
 /**
  * The PDO connection object
  */
 protected $database;
 protected $statement;
 /**
  * The query to run on the first iteration
  */
 protected $query;
 /**
  * Optional parameters to use for prepared statements
  */
 protected $parameters;
 /**
  * The current record in the results
  */
 protected $current;
 /**
  * The row number of the current record
  */
 protected $key;
 /**
  * A boolean as to whether it has more results
  */
 protected $valid;

 /**
  * Forward-only cursor assumed and enforced
  */
 public function rewind() {
  return false;
 }
 
 public function current() {
  if ($this->key === -1) {
   if (!$this->runQuery()) {
    $this->valid = false;
    return false;
   } else {
    $this->next();
   }
  }
  return $this->current;
 }
 
 public function key() {
  return $this->key;
 }
 
 public function next() {
  $this->current = $this->statement->fetch(PDO::FETCH_ASSOC);
  if ($this->current) {
   $this->key++;
   if (!$this->valid) {
    $this->valid = true;
   }
   return true;
  } else {
   $this->statement = null;
   $this->valid = false;
   return false;
  }
 }
 
 protected function runQuery() {
  $this->statement = $this->database->prepare($this->query);
  $this->statement->execute($this->parameters);
 }
 
 public function valid() {
  return $this->valid;
 }
 
 public function setParameters($params) {
  $this->parameters = $params;
 }
 
 public function __construct($database, $query) {
  $this->database = $database;
  $this->query = $query;
  $this->parameters = null;
  $this->current = null;
  $this->key = -1;
  $this->valid = true;
 }
}

The following two code chunks show how to use this class in the application logic, and then later on when displaying the results of the query, assuming an already open connection in $database, an instance of the MySQL flavor of PDO:

// First, run the query and get the list
$query = 'SELECT `id`, `name` FROM `users` ORDER BY `name`';
$users = new PDOIterator($database, $query);

<!-- Later in the application, output the list -->
<ol>
<?php foreach ($users as $user) { ?>
 <li><a href="?id=<?php echo (int)$user['id']; ?>">
  <?php echo Utilities::escapeXMLEntities($user['name']); ?>
 </a></li>
<?php } ?>
</ol>

The query doesn't actually run until the first iteration, so the database connection does not have any open statement until it starts the iterations. Once complete, it sets the statement to null, freeing up the connection for other queries, without having to clutter any of the application code with PDO-specific calls.

This could easily extend to other database drivers (like the mysqli-specific driver, for example), but since PDO stands to have a large rate of adoption (and since I like it), I used it instead.

Labels: ,

0 Comments:

Post a Comment

Links to this post:

Create a Link

<< Home