mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merged revisions 50683 via svnmerge from
http://svn.silverstripe.com/open/modules/sapphire/branches/2.2.2 ........ r50683 | aoneil | 2008-03-07 11:05:27 +1300 (Fri, 07 Mar 2008) | 2 lines #2295 - DataObjectSets cannot be iterated over multiple times concurrently ........ git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@50871 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
a0be727cc3
commit
05dc1eee2c
@ -18,7 +18,7 @@
|
||||
* @package sapphire
|
||||
* @subpackage view
|
||||
*/
|
||||
class ViewableData extends Object implements Iterator {
|
||||
class ViewableData extends Object implements IteratorAggregate {
|
||||
/**
|
||||
* The iterator position.
|
||||
* @var int
|
||||
@ -89,6 +89,18 @@ class ViewableData extends Object implements Iterator {
|
||||
parent::defineMethods();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a "1 record iterator"
|
||||
* Views <%control %> tags operate by looping over an item for as many instances as are
|
||||
* available. When you stick a single ViewableData object in a control tag, the foreach()
|
||||
* loop still needs to work. We do this by creating an iterator that only returns one record.
|
||||
* This will always return the current ViewableData object.
|
||||
* @return ViewableData_Iterator A 1 record iterator
|
||||
*/
|
||||
function getIterator() {
|
||||
return new ViewableData_Iterator($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor overloader.
|
||||
* Allows default getting of fields via $this->getVal(), or mediation via a
|
||||
@ -798,63 +810,6 @@ class ViewableData extends Object implements Iterator {
|
||||
function Top() {
|
||||
return SSViewer::topLevel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of a "1 record iterator"
|
||||
* Views <%control %> tags operate by looping over an item for as many instances as are
|
||||
* available. When you stick a single ViewableData object in a control tag, the foreach()
|
||||
* loop still needs to work. We do this by creating an iterator that only returns one record.
|
||||
* This will always return the current ViewableData object.
|
||||
*/
|
||||
public function current() {
|
||||
if($this->show) {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of a "1 record iterator"
|
||||
* Views <%control %> tags operate by looping over an item for as many instances as are
|
||||
* available. When you stick a single ViewableData object in a control tag, the foreach()
|
||||
* loop still needs to work. We do this by creating an iterator that only returns one record.
|
||||
* Rewinds the iterator back to the start.
|
||||
*/
|
||||
public function rewind() {
|
||||
$this->show = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of a "1 record iterator"
|
||||
* Views <%control %> tags operate by looping over an item for as many instances as are
|
||||
* available. When you stick a single ViewableData object in a control tag, the foreach()
|
||||
* loop still needs to work. We do this by creating an iterator that only returns one record.
|
||||
* Return the key for the current object.
|
||||
*/
|
||||
public function key() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of a "1 record iterator"
|
||||
* Views <%control %> tags operate by looping over an item for as many instances as are
|
||||
* available. When you stick a single ViewableData object in a control tag, the foreach()
|
||||
* loop still needs to work. We do this by creating an iterator that only returns one record.
|
||||
* Get the next object.
|
||||
*/
|
||||
public function next() {
|
||||
return $this->show = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of a "1 record iterator"
|
||||
* Views <%control %> tags operate by looping over an item for as many instances as are
|
||||
* available. When you stick a single ViewableData object in a control tag, the foreach()
|
||||
* loop still needs to work. We do this by creating an iterator that only returns one record.
|
||||
* Check if there is a current object.
|
||||
*/
|
||||
public function valid() {
|
||||
return $this->show;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -879,11 +834,7 @@ class ViewableData extends Object implements Iterator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal state toggler for the "1 record iterator"
|
||||
* @var bool
|
||||
*/
|
||||
private $show;
|
||||
|
||||
|
||||
/**
|
||||
* Object-casting information for class methods
|
||||
@ -1148,4 +1099,61 @@ class ViewableData_Debugger extends ViewableData {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of a "1 record iterator"
|
||||
* Views <%control %> tags operate by looping over an item for as many instances as are
|
||||
* available. When you stick a single ViewableData object in a control tag, the foreach()
|
||||
* loop still needs to work. We do this by creating an iterator that only returns one record.
|
||||
* This will always return the current ViewableData object.
|
||||
*/
|
||||
class ViewableData_Iterator implements Iterator {
|
||||
function __construct($viewableData) {
|
||||
$this->viewableData = $viewableData;
|
||||
$this->show = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal state toggler
|
||||
* @var bool
|
||||
*/
|
||||
private $show;
|
||||
|
||||
/**
|
||||
* This will always return the current ViewableData object.
|
||||
*/
|
||||
public function current() {
|
||||
if($this->show) {
|
||||
return $this->viewableData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewinds the iterator back to the start.
|
||||
*/
|
||||
public function rewind() {
|
||||
$this->show = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key for the current object.
|
||||
*/
|
||||
public function key() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next object.
|
||||
*/
|
||||
public function next() {
|
||||
return $this->show = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is a current object.
|
||||
*/
|
||||
public function valid() {
|
||||
return $this->show;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -11,7 +11,7 @@
|
||||
* @package sapphire
|
||||
* @subpackage model
|
||||
*/
|
||||
class DataObjectSet extends ViewableData implements Iterator {
|
||||
class DataObjectSet extends ViewableData implements IteratorAggregate {
|
||||
/**
|
||||
* The DataObjects in this set.
|
||||
* @var array
|
||||
@ -90,7 +90,7 @@ class DataObjectSet extends ViewableData implements Iterator {
|
||||
}
|
||||
}
|
||||
|
||||
$this->current = $this->prepareItem(current($this->items));
|
||||
|
||||
}
|
||||
parent::__construct();
|
||||
}
|
||||
@ -440,70 +440,6 @@ class DataObjectSet extends ViewableData implements Iterator {
|
||||
$this->push($item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the iterator to the beginning of the set.
|
||||
* @return DataObject The first item in the set.
|
||||
*/
|
||||
public function rewind() {
|
||||
$this->current = $this->prepareItem(reset($this->items));
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current object of the iterator.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function current() {
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key of the current object of the iterator.
|
||||
* @return mixed
|
||||
*/
|
||||
public function key() {
|
||||
return key($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next item in this set.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function next() {
|
||||
$this->current = $this->prepareItem(next($this->items));
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next item in this set without progressing the iterator.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function peekNext() {
|
||||
return $this->getOffset(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the prvious item in this set, without affecting the iterator.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function peekPrev() {
|
||||
return $this->getOffset(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object in this set offset by $offset from the iterator pointer.
|
||||
* @param int $offset The offset.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function getOffset($offset) {
|
||||
$keys = array_keys($this->items);
|
||||
foreach($keys as $i => $key) {
|
||||
if($key == key($this->items)) break;
|
||||
}
|
||||
$requiredKey = $keys[$i + $offset];
|
||||
return $this->items [$requiredKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a specific slice of an existing set.
|
||||
@ -521,28 +457,14 @@ class DataObjectSet extends ViewableData implements Iterator {
|
||||
}
|
||||
return $set;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare an item taken from the internal array for
|
||||
* output by this iterator. Ensures that it is an object.
|
||||
* @param DataObject $item Item to prepare
|
||||
* @return DataObject
|
||||
* Returns an Iterator for this DataObjectSet.
|
||||
* This function allows you to use DataObjectSets in foreach loops
|
||||
* @return DataObjectSet_Iterator
|
||||
*/
|
||||
protected function prepareItem($item) {
|
||||
if(is_object($item)) {
|
||||
$item->iteratorProperties(key($this->items), sizeof($this->items));
|
||||
}
|
||||
// This gives some reliablity but it patches over the root cause of the bug...
|
||||
// else if(key($this->items) !== null) $item = new ViewableData();
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the iterator is pointing to a valid item in the set.
|
||||
* @return boolean
|
||||
*/
|
||||
public function valid() {
|
||||
return $this->current !== false;
|
||||
public function getIterator() {
|
||||
return new DataObjectSet_Iterator($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1038,4 +960,103 @@ function column_sort_callback_basic($a, $b) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* An Iterator for a DataObjectSet
|
||||
*/
|
||||
class DataObjectSet_Iterator implements Iterator {
|
||||
function __construct($items) {
|
||||
$this->items = $items;
|
||||
|
||||
$this->current = $this->prepareItem(current($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare an item taken from the internal array for
|
||||
* output by this iterator. Ensures that it is an object.
|
||||
* @param DataObject $item Item to prepare
|
||||
* @return DataObject
|
||||
*/
|
||||
protected function prepareItem($item) {
|
||||
if(is_object($item)) {
|
||||
$item->iteratorProperties(key($this->items), sizeof($this->items));
|
||||
}
|
||||
// This gives some reliablity but it patches over the root cause of the bug...
|
||||
// else if(key($this->items) !== null) $item = new ViewableData();
|
||||
return $item;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the current object of the iterator.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function current() {
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key of the current object of the iterator.
|
||||
* @return mixed
|
||||
*/
|
||||
public function key() {
|
||||
return key($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next item in this set.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function next() {
|
||||
$this->current = $this->prepareItem(next($this->items));
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the iterator to the beginning of the set.
|
||||
* @return DataObject The first item in the set.
|
||||
*/
|
||||
public function rewind() {
|
||||
$this->current = $this->prepareItem(reset($this->items));
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the iterator is pointing to a valid item in the set.
|
||||
* @return boolean
|
||||
*/
|
||||
public function valid() {
|
||||
return $this->current !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next item in this set without progressing the iterator.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function peekNext() {
|
||||
return $this->getOffset(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the prvious item in this set, without affecting the iterator.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function peekPrev() {
|
||||
return $this->getOffset(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object in this set offset by $offset from the iterator pointer.
|
||||
* @param int $offset The offset.
|
||||
* @return DataObject
|
||||
*/
|
||||
public function getOffset($offset) {
|
||||
$keys = array_keys($this->items);
|
||||
foreach($keys as $i => $key) {
|
||||
if($key == key($this->items)) break;
|
||||
}
|
||||
$requiredKey = $keys[$i + $offset];
|
||||
return $this->items [$requiredKey];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -11,7 +11,7 @@
|
||||
* @package sapphire
|
||||
* @subpackage model
|
||||
*/
|
||||
class SQLMap extends Object implements Iterator {
|
||||
class SQLMap extends Object implements IteratorAggregate {
|
||||
/**
|
||||
* The query used to generate the map.
|
||||
* @var SQLQuery
|
||||
@ -49,32 +49,11 @@ class SQLMap extends Object implements Iterator {
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterator functions - necessary for foreach to work
|
||||
* Iterator - necessary for foreach to work
|
||||
*/
|
||||
public function rewind() {
|
||||
public function getIterator() {
|
||||
$this->genItems();
|
||||
return $this->items->rewind() ? $this->items->rewind()->Title : null;
|
||||
}
|
||||
|
||||
public function current() {
|
||||
$this->genItems();
|
||||
return $this->items->current()->Title;
|
||||
}
|
||||
|
||||
public function key() {
|
||||
$this->genItems();
|
||||
return $this->items->current()->ID;
|
||||
}
|
||||
|
||||
public function next() {
|
||||
$this->genItems();
|
||||
$next = $this->items->next();
|
||||
return isset($next->Title) ? $next->Title : null;
|
||||
}
|
||||
|
||||
public function valid() {
|
||||
$this->genItems();
|
||||
return $this->items->valid();
|
||||
return $this->items->getIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,4 +87,4 @@ class SQLMap extends Object implements Iterator {
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
?>
|
||||
|
@ -495,7 +495,7 @@ JS;
|
||||
return null;
|
||||
}
|
||||
|
||||
$item = $this->unpagedSourceItems->getOffset($_REQUEST['ctf']['start'] + 1);
|
||||
$item = $this->unpagedSourceItems->getIterator()->getOffset($_REQUEST['ctf']['start'] + 1);
|
||||
|
||||
$start = $_REQUEST['ctf']['start'] + 1;
|
||||
return Convert::raw2att($this->PopupBaseLink() . "&methodName={$_REQUEST['methodName']}&ctf[childID]={$item->ID}&ctf[start]={$start}");
|
||||
@ -506,7 +506,7 @@ JS;
|
||||
return null;
|
||||
}
|
||||
|
||||
$item = $this->unpagedSourceItems->getOffset($_REQUEST['ctf']['start'] - 1);
|
||||
$item = $this->unpagedSourceItems->getIterator()->getOffset($_REQUEST['ctf']['start'] - 1);
|
||||
|
||||
$start = $_REQUEST['ctf']['start'] - 1;
|
||||
return Convert::raw2att($this->PopupBaseLink() . "&methodName={$_REQUEST['methodName']}&ctf[childID]={$item->ID}&ctf[start]={$start}");
|
||||
@ -532,7 +532,7 @@ JS;
|
||||
}
|
||||
for($i = $offset;$i <= $offset + $this->pageSize && $i <= $this->totalCount;$i++) {
|
||||
$start = $i - 1;
|
||||
$item = $this->unpagedSourceItems->getOffset($i-1);
|
||||
$item = $this->unpagedSourceItems->getIterator()->getOffset($i-1);
|
||||
$links['link'] = Convert::raw2att($this->PopupBaseLink() . "&methodName={$_REQUEST['methodName']}&ctf[childID]={$item->ID}&ctf[start]={$start}");
|
||||
$links['number'] = $i;
|
||||
$links['active'] = $i == $currentItem ? false : true;
|
||||
|
Loading…
Reference in New Issue
Block a user