The Iterator Pattern: OOP Techniques in PHP

Posted on 04. Mar, 2009 by Will Fitch in PHP

The iterator pattern is one of the most useful, yet unused patterns defined. It provides a way for class users to count and iterate over a set of objects related to the class. This is very useful in MVC (Model-View-Controller) models as they handle data and the logic that pertains to it.

PHP provides two interfaces that already define what you should include in an iterator pattern: Iterator and Countable. Below are their definitions:

Iterator Interface

PHP
  1. <?php
  2. interface Iterator implements Traversable
  3. {
  4.     public mixed current();
  5.     public scalar key();
  6.     public void next();
  7.     public void rewind();
  8.     public bool valid();
  9. }
  10. ?>

Countable Interface

PHP
  1. <?php
  2. interface Countable
  3. {
  4.     public int count();
  5. }
  6. ?>

You can already iterate over class properties using the foreach() loop, so the Iterator and Countable interfaces are domain specific — meaning the iteration logic is specific to the functionality of the class.

As an example, let’s assume we have a domain model that manages users (CRUD). We have an additional class that acts as a structure (you don’t have to do this, but it’s easier to read in a blog) for each individual user. You also have a method that retrieves an arbitrary number of users, assigns each to a User class (acting as a struct), and gives back an array. While this is certainly doable, the iterator pattern defines an easier way to process the data.

Example Before Iterator Pattern

PHP
  1. <?php
  2.  
  3. class User
  4. {
  5.         public $email;
  6.         public $address;
  7.         public $city;
  8.         public $state;
  9.         public $zip;
  10. }
  11.  
  12. class User_Management
  13. {
  14.         /**
  15.          * List of users
  16.          */
  17.         private $users=array();
  18.        
  19.         public function getUserByID($id)
  20.         {
  21.                 // go get a user and assign it an instance of User
  22.         }
  23.        
  24.         public function getAllUsers()
  25.         {
  26.                 // go get all users, assign each to User class, and add to the self::users array
  27.         }
  28.        
  29. }
  30.  
  31. ?>

The above example is pretty typical for domain models without the use of iterator. Adding the iterator, we provide an interface to bi-directionally traverse, get key information and count records.

With the Iterator Pattern Implemented

PHP
  1. <?php
  2.  
  3. class User
  4. {
  5.         public $email;
  6.         public $address;
  7.         public $city;
  8.         public $state;
  9.         public $zip;
  10. }
  11.  
  12. class User_Management implements Iterator, Countable
  13. {
  14.         /**
  15.          * List of users
  16.          */
  17.         private $users=array();
  18.        
  19.         /**
  20.          * Position of the iterator
  21.          */
  22.         private $position=0;
  23.        
  24.         /**
  25.          * Retrieve the current record
  26.          */
  27.         public function current()
  28.         {
  29.                 return $this->users[$this->position];          
  30.         }
  31.        
  32.         /**
  33.          * Return the current key index
  34.          */
  35.         public function key()
  36.         {
  37.                 return $this->position;
  38.         }
  39.        
  40.         /**
  41.          * Increment the iterator index
  42.          */
  43.         public function next()
  44.         {
  45.                 ++$this->position;
  46.         }
  47.        
  48.         /**
  49.          * Reset the position
  50.          */
  51.         public function rewind()
  52.         {
  53.                 $this->position = 0;
  54.         }
  55.        
  56.         public function getUserByID($id)
  57.         {
  58.                 // go get a user and assign it an instance of User
  59.         }
  60.        
  61.         /**
  62.          * Validate whether a record at current
  63.          * position exists
  64.          */
  65.         public function valid()
  66.         {
  67.                 return (isset($this->users[$this->position]));
  68.         }
  69.        
  70.         /**
  71.          * Return the total number of users
  72.          */
  73.         public function count()
  74.         {
  75.                 return count($this->users);
  76.         }
  77.        
  78.         public function getAllUsers()
  79.         {
  80.                 // go get all users, assign each to User class, and add to the self::users array
  81.         }
  82.        
  83. }
  84.  
  85. ?>

The domain model now implements Countable and Iterator. The below example shows a couple of uses for this:

PHP
  1. <?php
  2.  
  3. $iterator = new User_Management();
  4. foreach ($iterator as $index => $value)
  5. {
  6.         // The $index will contain the current key
  7.         // while the $value will contain the user
  8.         // at the current position
  9.         echo $value->address;
  10. }
  11. // Rewind the iterator
  12. $iterator->rewind();
  13.  
  14. while ($iterator->valid())
  15. {
  16.         $value = $iterator->current();
  17.         echo $value->address;
  18.         $iterator->next();
  19. }
  20.  
  21. // Rewind the iterator
  22. $iterator->rewind();
  23.  
  24. for ($iterator->key(); $iterator->valid(); $iterator->next())
  25. {
  26.         $value = $iterator->current();
  27.         echo $value->address;
  28. }
  29.  
  30. ?>

Conclusion
This is a much cleaner, easier approach to iterating over objects. I hope you find this usable in your domain models!

Tags: , , ,

Related Articles

2 Responses to “The Iterator Pattern: OOP Techniques in PHP”

  1. Adaptor

    26. Jul, 2009

    Thanks for the clear explanation!

  2. Will Fitch

    10. Feb, 2010

    No problem! Glad it helped.

Leave a Reply