Painless Data Traversal with PHP FilterIterators

PHP 
Painless Data Traversal with PHP FilterIterators

There are a lot of ways to traverse data in PHP, including while, do while, for and foreach loops. These are fine in a lot of cases. But what if you want to reuse looping logic or have somewhat complex logic? Enter the FilterIterator.


Say you started out with a structure similar to the following:

<?php

$dataList = [
    [
        'name' => 'John Citizen',
        'email' => 'johnc@citizen.org',
        'dob' => 1350910989,
        'location' => 'Brisbane, Qld, Australia',
        'active' => true
    ],
    [
        'name' => 'Jane Citizen',
        'email' => 'janec@citizen.org',
        'dob' => 1350910989,
        'location' => 'Townsville, Qld, Australia',
        'active' => false
    ],
    [
        'name' => 'Peter Walker',
        'email' => 'peterw@citizen.org',
        'dob' => 1350910989,
        'location' => 'Sydney, Nsw, Australia',
        'active' => false
    ],
    [
        'name' => 'Wendy Hardworker',
        'email' => 'wendyh@citizen.org',
        'dob' => 1350910989,
        'location' => 'Melbourne, Vic, Australia',
        'active' => true
    ],
    [
        // ...
    ],
);

You can see that you could quickly and simple filter the information using any of the previously mentioned loops. The following is a rather trivial example of how you could do just this.

<?php

foreach ($dataList as $userDetail) {
    printf(
        "Name: %s | Email: %s | Date Of Birth: %s | Location: %s",
        $userDetail["name"],
        $userDetail["email"],
        $userDetail["dob"],
        $userDetail["location"]
    );
}

This would display output similar to the following:

Name: John Citizen | Email: johnc@citizen.org | DOB: 1350910989 | Location: Brisbane, Qld, Australia
Name: Jane Citizen | Email: janec@citizen.org | DOB: 1350910989 | Location: Townsville, Qld, Australia
Name: Peter Walker | Email: peterw@citizen.org | DOB: 1350910989 | Location: Sydney, Nsw, Australia
Name: Wendy Hardworker | Email: wendyh@citizen.org | DOB: 1350910989 | Location: Melbourne, Vic, Australia

But say, over time, the the data structure grew in complexity and you wanted to filter out some records, but not others. I’m sure that one of your first reactions would, naturally, be to change the SQL query - assuming that we’re talking to a datasource that is an RDBMS. But:

  • What if it wasn’t?
  • What if your only access was an API?
  • What if you were not allowed to change the underlying code?
  • What if you didn’t know all the ramifications of what a change may be?

It’s not quite so easy is it? Let’s say that one or more of these constraints is in effect. Is there an answer? Yes, there most certainly is — so long as you’re using PHP >= 5.1.0 that is.

Enter the FilterIterator

That’s right, in PHP 5.1.0 as part of the SPL (Standard PHP Library), Filter Iterators were introduced. If you’ve not heard of them or used them, the PHP manual describes them as:

This abstract iterator filters out unwanted values. This class should be extended to implement custom iterator filters. The FilterIterator::accept() must be implemented in the subclass.

Iterators and the FilterIterator are a wonderful addition to the PHP language as they allow us, as developers, to apply the existing looping constructs to our custom data structures.

Recently in a Malt Blue project, we were tasked with building a project that provides data, that is largely similar in nature, which needed to be viewed across a range of different parts of the application. Given that the data was so similar in nature, it made no sense to write a series of different, utility, functions for it - which would then need to be maintained over time.

So we chose to instead implement a series of FilterIterators so that we could make one data request call and display the same information in a multitude of, slightly, different ways.

For the purposes of this example, let’s assume that the data structure is similar to the above. Have a look at the code below and we’ll work through it, showing how to implement a FilterIterator:

<?php
 
class MyIterator_Filter_InActive extends FilterIterator
{
    public function accept()
    {
        $value = $this->current();
        if (
          array_key_exists('active', $value) 
          && ! empty($value['active'])
        ) 
        {
            return $value['active'];
        }
        return false;
    }
}

In the code above, we define the class MyIterator\_Filter\_InActive which extends FilterIterator. The FilterIterator interface requires us to only implement one method: accept. If accept returns true, then the current record is displayed. If it returns false, then the record is skipped.

So, we retrieve the current record, with $value = $this->current();. We then check whether it has an active value and if that value is set. If it is, then we return it. If one of these conditions is not met, then we automatically return false.

So let’s instantiate the FilterIterator with our existing dataset and see what the result is.

<?php

$dataIterator = new MyIterator_Filter_InActive(
    new ArrayIterator($dataList)
);

As our data is already in an array, we start by using it to instantiate an ArrayIterator, which makes it painless to iterate over the whole dataset - not what we want. This instantiated ArrayIterator is then passed to our custom FilterIterator.

With this instantiated FilterIterator, we can then loop over the data as before, but now only with records that have active set as true - as the below code does:

<?php
foreach ($dataIterator as $userDetail) {
    printf(
        "Name: %s | Email: %s | Date Of Birth: %s | Location: %s",
        $userDetail["name"],
        $userDetail["email"],
        $userDetail["dob"],
        $userDetail["location"]
    );
}

The updated output will be as below:

Name: John Citizen | Email: johnc@citizen.org | DOB: 1350910989 | Location: Brisbane, Qld, Australia
Name: Wendy Hardworker | Email: wendyh@citizen.org | DOB: 1350910989 | Location: Melbourne, Vic, Australia

Pretty simple, hey? Now yes, this was a simple example. But you can see how, through creating a custom FilterIterator, you can quickly and simply use the existing PHP looping constructs to iterate through your custom data sets returning only the information that you specifically need.

Questions

Do you use FilterIterators already? What’s your experience with them? Do they help you build more maintainable code? Share your experience in the comments. If you liked this post, then check out the follow up to it: filter by date of birth easily with a filteriterator.


You might also be interested in these tutorials too...

Mon, Dec 3, 2012

Why Errors Are Your Best Friend

As developers, we can believe errors are negative reflections on our applications. But are they really so? In today’s post, I show you how they’re anything but negative, they’re actually very very positive, when interpreted correctly.

Mon, Oct 22, 2012

Make Freelance Business Administration Easy - Use FreeAgent

Whether you’re a casual freelance php developer or running a professional freelance business, you’ll know just how much work can be involved. Not only do you have applications to design, develop and support. But you’ve also got business functions such as marketing, advertising, finances and networking as well. In this post we show you a tool to massively reduce your workload - FreeAgent.


Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.

Join the discussion

comments powered by Disqus