*/ class ResourceCollection implements Iterator, Countable { use ServiceLocatorAwareTrait; use OntologyAwareTrait; const CACHE_SIZE = 100; /** * @var string[] */ private $resources; /** * @var int */ private $index = 0; /** * @var Filter */ private $filter; /** * @var bool */ private $endOfClass; /** * @var core_kernel_classes_Class */ private $class; /** * @var int */ private $cacheSize; /** * @var int */ private $lastId = 0; /** * @var bool */ private $classFilterSet = false; /** * @var int */ private $limit; /** * ResourceCollection constructor. * * @param null|string|core_kernel_classes_Class $class * @param int $cacheSize */ public function __construct($class = null, $cacheSize = self::CACHE_SIZE) { if ($class !== null) { $class = $this->getClass($class); } $this->class = $class; $this->filter = new Filter(); $this->cacheSize = $cacheSize; } /** * Load a collection of resources * * @return bool */ protected function load() { if ($this->resources !== null) { return $this->count() > 0; } $this->resources = null; $this->index = 0; $this->loadResources(); return $this->count() > 0; } /** * Load resources from storage */ private function loadResources() { if ($this->class !== null && $this->classFilterSet === false) { $this->addClassFilter(); } /** @var common_persistence_SqlPersistence $persistence */ $persistence = $this->getModel()->getPersistence(); $platform = $persistence->getPlatForm(); $query = $platform->getQueryBuilder() ->select('*') ->from('statements') ->andWhere('id > ' . $this->lastId) ->orderBy('id'); if ($this->cacheSize > 0) { $query->setMaxResults($this->cacheSize); } $query = $this->filter->applyFilters($query); $results = $query->execute(); if ($this->cacheSize > 0) { $this->endOfClass = $results->rowCount() < $this->cacheSize; } foreach ($results->fetchAll() as $result) { $this->resources[] = $result; $this->lastId = $result['id'] > $this->lastId ? $result['id'] : $this->lastId; } } /** * Add a filter to filter the resources we should iterate over. * * @param string $column * @param string $operator * @param mixed $value * @return ResourceCollection */ public function addFilter($column, $operator, $value) { $this->filter->addFilter($column, $operator, $value); return $this; } /** * Add a type resource filter. * * @param string $type * @return ResourceCollection */ public function addTypeFilter($type) { $this->filter->eq('predicate', OntologyRdf::RDF_TYPE) ->eq('object', $type); return $this; } /** * @inheritdoc */ public function count() { if (!is_array($this->resources) && !$this->resources instanceof Countable) { return 0; } return count($this->resources); } /** * @inheritdoc */ public function current() { return $this->resources[$this->index]; } /** * @inheritdoc */ public function next() { $this->index++; } /** * @inheritdoc */ public function key() { return $this->index; } /** * @inheritdoc */ public function valid() { if ($this->resources === null) { return $this->load(); } if ($this->endOfClass === false && !isset($this->resources[$this->index])) { if ($this->isLimitReached()) { $this->resources = null; return false; } $this->resources = null; return $this->load(); } return isset($this->resources[$this->index]); } /** * @inheritdoc */ public function rewind() { $this->index = 0; } /** * Activate a limitation of how many resources should be processed. This is different from cache size, since it * limits the amount of items that are processed in e.g. a foreach loop without loading remaining resources. */ public function useLimit() { $this->limit = $this->cacheSize; } /** * Check if we reached the limit of the amount of items we should process. * * @return bool */ private function isLimitReached() { return $this->limit !== null && $this->count() >= $this->limit; } /** * Check if the end of the current class is reached (no more records available) * @return bool */ public function getEndReached() { return $this->endOfClass; } /** * Adds a filter for a class */ private function addClassFilter() { $this->addTypeFilter($this->class->getUri()); $this->classFilterSet = true; } }