*/ #[Autoconfigure(public: true, shared: false)] class QueryResult implements QueryResultInterface { protected DataMapper $dataMapper; protected PersistenceManagerInterface $persistenceManager; /** * @var int|null */ protected $numberOfResults; /** * @phpstan-var QueryInterface|null */ protected ?QueryInterface $query = null; /** * @var array|null * @phpstan-var list|null */ protected $queryResult; public function __construct( DataMapper $dataMapper, PersistenceManagerInterface $persistenceManager ) { $this->dataMapper = $dataMapper; $this->persistenceManager = $persistenceManager; } /** * @phpstan-param QueryInterface $query */ public function setQuery(QueryInterface $query): void { $this->query = $query; $this->dataMapper->setQuery($query); } /** * Loads the objects this QueryResult is supposed to hold */ protected function initialize() { if (!is_array($this->queryResult)) { $this->queryResult = $this->dataMapper->map($this->query->getType(), $this->persistenceManager->getObjectDataByQuery($this->query)); } } /** * Returns a clone of the query object * * @return QueryInterface * @phpstan-return QueryInterface */ public function getQuery() { return clone $this->query; } /** * Returns the first object in the result set * * @return object * @phpstan-return TValue|null */ public function getFirst() { if (is_array($this->queryResult)) { $queryResult = $this->queryResult; reset($queryResult); } else { $query = $this->getQuery(); $query->setLimit(1); $queryResult = $this->dataMapper->map($query->getType(), $this->persistenceManager->getObjectDataByQuery($query)); } $firstResult = current($queryResult); if ($firstResult === false) { $firstResult = null; } return $firstResult; } /** * Returns the number of objects in the result * * @return int The number of matching objects */ public function count(): int { if ($this->numberOfResults === null) { if (is_array($this->queryResult)) { $this->numberOfResults = count($this->queryResult); } else { $this->numberOfResults = $this->persistenceManager->getObjectCountByQuery($this->query); } } return $this->numberOfResults; } /** * Returns an array with the objects in the result set * * @return array * @phpstan-return list */ public function toArray() { $this->initialize(); return iterator_to_array($this); } /** * This method is needed to implement the ArrayAccess interface, * but it isn't very useful as the offset has to be an integer * * @param mixed $offset */ public function offsetExists($offset): bool { $this->initialize(); return isset($this->queryResult[$offset]); } /** * @param mixed $offset * @return TValue|null */ public function offsetGet($offset): mixed { $this->initialize(); return $this->queryResult[$offset] ?? null; } /** * This method has no effect on the persisted objects but only on the result set * * @param mixed $offset * @param mixed $value * @phpstan-param TValue $value */ public function offsetSet($offset, $value): void { $this->initialize(); $this->numberOfResults = null; $this->queryResult[$offset] = $value; } /** * This method has no effect on the persisted objects but only on the result set * * @param mixed $offset */ public function offsetUnset($offset): void { $this->initialize(); $this->numberOfResults = null; unset($this->queryResult[$offset]); } /** * @return mixed * @see Iterator::current() * @return TValue|false */ public function current(): mixed { $this->initialize(); return current($this->queryResult); } /** * @return mixed * @see Iterator::key() * @return int|null */ public function key(): mixed { $this->initialize(); return key($this->queryResult); } /** * @see Iterator::next() */ public function next(): void { $this->initialize(); next($this->queryResult); } /** * @see Iterator::rewind() */ public function rewind(): void { $this->initialize(); reset($this->queryResult); } /** * @see Iterator::valid() */ public function valid(): bool { $this->initialize(); return current($this->queryResult) !== false; } /** * Ensures that the persistenceManager and dataMapper are back when loading the QueryResult * from the cache * @internal only to be used within Extbase, not part of TYPO3 Core API. */ public function __wakeup() { $this->persistenceManager = GeneralUtility::makeInstance(PersistenceManagerInterface::class); $this->dataMapper = GeneralUtility::makeInstance(DataMapper::class); } /** * @return array * @internal only to be used within Extbase, not part of TYPO3 Core API. */ public function __sleep() { return ['query']; } }