<?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;
});
}
}
|