<?php
 
 
// multiRecordsetItorator class
 
//
 
// This is free software: you can redistribute it and/or modify
 
// it under the terms of the GNU General Public License as published by
 
// the Free Software Foundation, either version 2 of the License, or
 
// (at your option) any later version.
 
//
 
// This is distributed in the hope that it will be useful,
 
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
// GNU General Public License for more details.
 
//
 
// See <http://www.gnu.org/licenses/>.
 
 
class multiPDOStatementIterator implements Iterator {
 
    /*
 
     * Holds all the passed in recordset
 
     */
 
    protected $pdostatements;
 
 
    /*
 
     * Database field that all recordsets are ordered by and contain.
 
     */
 
    protected $keyfield;
 
 
    /*
 
     * This is an incrementor so that we have a unique key in the loop
 
     */
 
    protected $externalkey = 0;
 
 
    /*
 
     * This is the set we are looking (eg. userid)
 
     */
 
    protected $internalkey;
 
 
    /*
 
     * So that we know where we are
 
     */
 
    protected $currentcache;
 
 
    /*
 
     * So that we know where we have been
 
     */
 
    protected $previouscache;
 
 
    /*
 
     * cache the order
 
     */
 
    protected $order;
 
 
    const ASC = 'asc';
 
    const DESC = 'desc';
 
 
    /**
 
     * Allows you to iterate over ordered recordsets by the keyfield that is passed.
 
     * 
 
     * If you have a multiple sets of data and they all have a user field you can use this 
 
     * to loop over them starting with the elements in the first recordset that match the key
 
     * and moving on to the elements in each passed set.
 
     * 
 
     * @param string $keyfield
 
     * @param PDOStatement $var [,PDOStatement $...]
 
     */
 
    public function __construct($keyfield, $sortorder=self::ASC) {
 
        $this->pdostatements = func_get_args();
 
 
        $this->keyfield = array_shift($this->pdostatements);
 
        $this->order = array_shift($this->pdostatements);
 
 
        // populate the first cache
 
        $this->latestRecordCache = array_map(function ($recordset) {
 
            return $recordset->fetchObject();
 
        }, $this->pdostatements);
 
 
        $this->setInternalKeyToNext();
 
 
        $this->setCurrentToNextForInternalKey();
 
    }
 
 
 
   public function valid() {
 
       // is there at least one recordset that still has unsent records?
 
       $key = $this->keyfield; // php < 5.4 doesn't have access to $this in array_reduce
 
       return array_reduce($this->latestRecordCache, function ($nonemptyrecordset, $currentRecord) use ($key) {
 
           return $nonemptyrecordset || !empty($currentRecord->$key) ? true : false;
 
       });
 
    }
 
 
    public function key() {
 
        return $this->externalkey;
 
    }
 
 
    public function current() {
 
        return $this->currentcache;
 
    }
 
 
    public function previous()  {
 
        return $this->previouscache;
 
    }
 
 
    public function next() {
 
        if (!$this->valid()) {
 
            return false;
 
        }
 
 
        // First try to return anything with the current internal key and move the cache to next record
 
        if ($this->setCurrentToNextForInternalKey() !== false) {
 
            $this->externalkey++;
 
            return $this->currentcache;
 
        }
 
 
        // nothing with the current internal key so we need to move on to next set
 
        $this->setInternalKeyToNext();
 
 
        // since we've moved the internal key then recursively call in to get the next record
 
        return $this->next();
 
    }
 
 
    public function rewind() {
 
        // can only rewind this the first time
 
        if (!empty($this->externalkey)) {
 
            throw new Exception('Cannot rewind more than once.');
 
        }
 
    }
 
 
    /**
 
     * Get the next record from the various recordset that belongs to the current key
 
     * 
 
     * @return boolean true if record found else false
 
     */
 
    protected function setCurrentToNextForInternalKey() {
 
        foreach ($this->latestRecordCache as $recordsetkey => $record) {
 
            $recordkeyval = !empty($record->{$this->keyfield}) ? $record->{$this->keyfield} : false;
 
 
            if (!empty($recordkeyval) && $recordkeyval == $this->internalkey) {
 
                $nextrecord = $this->pdostatements[$recordsetkey]->fetchObject();
 
 
                $this->previouscache = $this->currentcache;
 
                $this->currentcache = $nextrecord;
 
 
                $this->latestRecordCache[$recordsetkey] = $nextrecord;
 
 
                return $nextrecord != false;
 
            }
 
        }
 
 
        return false;
 
    }
 
 
    /**
 
     * Set the internal pointer to the lowest keyfield id of the recordsets.
 
     * 
 
     * This is necessary so we know which set of records we are on
 
     * 
 
     */
 
    protected function setInternalKeyToNext() {
 
        $this->internalkey = array_reduce($this->latestRecordCache, function ($nextSmallest, $record) {
 
            $recordkeyval = !empty($record->{$this->keyfield}) ? $record->{$this->keyfield} : false;
 
 
            if (empty($nextSmallest) && !empty($recordkeyval)) {
 
                return $recordkeyval;
 
            }
 
 
            if (!empty($recordkeyval)) {
 
                if ($this->order == self::ASC && $recordkeyval < $nextSmallest) {
 
                    return $recordkeyval;
 
                } elseif ($this->order == self::DESC && $recordkeyval > $nextSmallest) {
 
                    return $recordkeyval;
 
                }
 
            }
 
            
 
            return $nextSmallest;
 
        });
 
    }
 
}
 
 |