* @implements Collection */ class SthCollection implements Collection, IteratorAggregate, Countable { private EntityManager $entityManager; private string $entityType; private ?SelectQuery $query = null; private ?PDOStatement $sth = null; private ?string $sql = null; private function __construct(EntityManager $entityManager) { $this->entityManager = $entityManager; } private function getQueryComposer(): QueryComposer { return $this->entityManager->getQueryComposer(); } private function getEntityFactory(): EntityFactory { return $this->entityManager->getEntityFactory(); } private function getSqlExecutor(): SqlExecutor { return $this->entityManager->getSqlExecutor(); } private function executeQuery(): void { $sql = $this->getSql(); $sth = $this->getSqlExecutor()->execute($sql); $this->sth = $sth; } private function getSql(): string { if (!$this->sql) { $this->sql = $this->getQueryComposer()->composeSelect($this->getQuery()); } return $this->sql; } private function getQuery(): SelectQuery { /** @var SelectQuery */ return $this->query; } public function getIterator(): Traversable { return (function () { if (isset($this->sth)) { $this->sth->execute(); } while ($row = $this->fetchRow()) { $entity = $this->getEntityFactory()->create($this->entityType); $entity->set($row); $entity->setAsFetched(); $this->prepareEntity($entity); yield $entity; } })(); } private function executeQueryIfNotExecuted(): void { if (!$this->sth) { $this->executeQuery(); } } /** * @return array */ private function fetchRow() { $this->executeQueryIfNotExecuted(); assert($this->sth !== null); return $this->sth->fetch(PDO::FETCH_ASSOC); } public function count(): int { $this->executeQueryIfNotExecuted(); assert($this->sth !== null); $rowCount = $this->sth->rowCount(); // MySQL may not return a row count for select queries. if ($rowCount) { return $rowCount; } return count($this->getValueMapList()); } protected function prepareEntity(Entity $entity): void { } /** * @deprecated * @return array>|\stdClass[] */ public function toArray(bool $itemsAsObjects = false): array { $arr = []; foreach ($this as $entity) { if ($itemsAsObjects) { $item = $entity->getValueMap(); } else if (method_exists($entity, 'toArray')) { $item = $entity->toArray(); } else { $item = get_object_vars($entity->getValueMap()); } $arr[] = $item; } return $arr; } public function getValueMapList(): array { /** @var \stdClass[] */ return $this->toArray(true); } /** * Whether Is fetched from DB. SthCollection is always fetched. */ public function isFetched(): bool { return true; } /** * Get an entity type. */ public function getEntityType(): string { return $this->entityType; } /** * @return self */ public static function fromQuery(SelectQuery $query, EntityManager $entityManager): self { /** @var self $obj */ $obj = new self($entityManager); $entityType = $query->getFrom(); if ($entityType === null) { throw new RuntimeException("Query w/o entity type."); } $obj->entityType = $entityType; $obj->query = $query; return $obj; } /** * @return self */ public static function fromSql(string $entityType, string $sql, EntityManager $entityManager): self { /** @var self $obj */ $obj = new self($entityManager); $obj->entityType = $entityType; $obj->sql = $sql; return $obj; } }