This article, the second of two parts, explains how to use the Iterator pattern to manipulate any collection of objects. It is excerpted from chapter eight of the book PHP|architect's Guide to PHP Design Patterns, written by Jason E. Sweat (PHP|architect, 2005; ISBN: 0973589825).
With Iterators,you can do more than just present each item of the collection. You can also select what items are presented. Let's modify the Library::getIterator() to allow two additional iterator types.
class Library { // ... function getIterator($type=false) { switch (strtolower($type)) { case 'media': $iterator_class = 'LibraryIterator'; break; case 'available': $iterator_class = 'LibraryAvailableIterator'; break; case 'released': $iterator_class = 'LibraryReleasedIterator'; break; default: $iterator_class = 'LibraryGofIterator'; } return new $iterator_class($this->collection); } }
The class LibraryAvailableIterator should only iterate over items that have a status of "library" (recall that the checkOut() method changes the status to "borrowed").
This test creates a new Media instance and stores it in the variable $dvd. The first highlighted assertEqual()assertion verifies that the new item is present when iterating with LibraryAvailableIterator. Next, the test uses the checkOut() method and verifies that the new item is missing from the display.
The code to implement filtering is very similar to LibraryIterator::next(), except filtering is done prior to returning the item. If the current item does not match the filter criteria, the code returns $this->next() instead.
class LibraryAvailableIterator { protected $collection = array(); protected $first=true; function __construct($collection) { $this->collection = $collection; } function next() { if ($this->first) { $this->first = false; $ret = current($this->collection); } else { $ret = next($this->collection); } if ($ret && 'library' != $ret->status) { return $this->next(); } return $ret; } }