mirror of
https://github.com/processwire/processwire.git
synced 2025-08-08 15:57:01 +02:00
Initial commit to new repo (carried over from: https://github.com/ryancramerdesign/ProcessWire/tree/devns)
This commit is contained in:
189
wire/core/PageArrayIterator.php
Normal file
189
wire/core/PageArrayIterator.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php namespace ProcessWire;
|
||||
|
||||
/**
|
||||
* PageArrayIterator for iteration of Page objects in a lazy-loaded fashion
|
||||
*
|
||||
* The custom Iterator that finds real Pages in chunks (in advance, and on demand), enabling memory
|
||||
* safety while maintaining reasonable speeds when iterating over a large set of Pages.
|
||||
*
|
||||
* Thanks to Avoine and @sforsman for this.
|
||||
*
|
||||
*/
|
||||
class PageArrayIterator extends Wire implements \Iterator {
|
||||
|
||||
/**
|
||||
* Placeholder objects for pages
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
*/
|
||||
protected $lazypages;
|
||||
|
||||
/**
|
||||
* Current buffer of real pages
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
*/
|
||||
protected $pages;
|
||||
|
||||
/**
|
||||
* Holds the options originally given by the user for Pages::find()
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
*/
|
||||
protected $options = array();
|
||||
|
||||
/**
|
||||
* Current position
|
||||
*
|
||||
* @var int
|
||||
*
|
||||
*/
|
||||
protected $position = 0;
|
||||
|
||||
/**
|
||||
* Current chunk
|
||||
*
|
||||
* @var
|
||||
*
|
||||
*/
|
||||
protected $currentChunk;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
*/
|
||||
protected $pagesPosition = 0;
|
||||
|
||||
/**
|
||||
* Number of pages in current chunk
|
||||
*
|
||||
* @var int
|
||||
*
|
||||
*/
|
||||
protected $pagesCount = 0;
|
||||
|
||||
/**
|
||||
* Determines how many pages to load in advance.
|
||||
*
|
||||
* Could be adjusted or suggested automatically based on memory_limit, for an example.
|
||||
*
|
||||
* @var int
|
||||
*
|
||||
*/
|
||||
protected $chunkSize = 1000;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param array $lazypages
|
||||
* @param array $options Options provided to $pages->find()
|
||||
*
|
||||
*/
|
||||
public function __construct(array $lazypages, array $options = []) {
|
||||
$this->lazypages = $lazypages;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the next chunk of real pages
|
||||
*
|
||||
*/
|
||||
protected function loadChunk() {
|
||||
|
||||
$this->pagesPosition = 0;
|
||||
$start = $this->currentChunk++ * $this->chunkSize;
|
||||
|
||||
// If the starting position exceeds the amount of placeholder objects, we just issue an empty
|
||||
// PageArray, which causes the loop to stop (because valid() will return false)
|
||||
if(!isset($this->lazypages[$start])) {
|
||||
|
||||
$this->pages = $this->wire('pages')->newPageArray();
|
||||
|
||||
} else {
|
||||
|
||||
// Check if the user gave options for the loading
|
||||
$options = isset($this->options['loadOptions']) ? $this->options['loadOptions'] : array();
|
||||
|
||||
// Always disable the cache
|
||||
$options['cache'] = false;
|
||||
|
||||
// Here we retrieve a chunk of Page objects and loop over them to retrieve the IDs of the Pages.
|
||||
$lazypages = array_slice($this->lazypages, $start, $this->chunkSize);
|
||||
$ids = array();
|
||||
|
||||
foreach($lazypages as $page) {
|
||||
// Grab the ID from the placeholder object. We are using the internal method here which does
|
||||
// not cause the real Page to be loaded. We only need to collect the IDs to request a chunk
|
||||
// of real Page-objects from Pages::getById()
|
||||
$ids[] = $page->id;
|
||||
}
|
||||
|
||||
$debug = $this->wire('pages');
|
||||
if($debug) $this->wire('pages')->debug(false);
|
||||
$this->pages = $this->wire('pages')->getById($ids, $options);
|
||||
if($debug) $this->wire('pages')->debug(true);
|
||||
}
|
||||
|
||||
$this->pagesCount = count($this->pages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind to beginning
|
||||
*
|
||||
*/
|
||||
public function rewind() {
|
||||
$this->pagesPosition = 0;
|
||||
$this->position = 0;
|
||||
$this->currentChunk = 0;
|
||||
$this->pagesCount = 0;
|
||||
$this->pages = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current Page
|
||||
*
|
||||
* @return Page
|
||||
*
|
||||
*/
|
||||
public function current() {
|
||||
return $this->pages[$this->pagesPosition];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current key/position
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
*/
|
||||
public function key() {
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update current position to next
|
||||
*
|
||||
*/
|
||||
public function next() {
|
||||
$this->pagesPosition++;
|
||||
$this->position++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether or not there are more items after current position
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
*/
|
||||
public function valid() {
|
||||
if($this->position === 0 || $this->pagesPosition >= $this->pagesCount) {
|
||||
// If we have just been rewound or if we have reached the end of the buffer,
|
||||
// we will load the next chunk.
|
||||
$this->loadChunk();
|
||||
}
|
||||
// We have reached the end when no Pages were loaded
|
||||
return ($this->pagesCount > 0);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user