* @implements Collection */ class SthCollection implements Collection, IteratorAggregate, Countable { private string $entityType; private ?SelectQuery $query = null; private ?PDOStatement $sth = null; private ?string $sql = null; private function __construct(private EntityManager $entityManager) {} private function executeQuery(): void { if ($this->query) { $this->sth = $this->entityManager->getQueryExecutor()->execute($this->query); return; } if (!$this->sql) { throw new LogicException("No query & sql."); } $this->sth = $this->entityManager->getSqlExecutor()->execute($this->sql); } public function getIterator(): Traversable { return (function () { if (isset($this->sth)) { $this->sth->execute(); } while ($row = $this->fetchRow()) { $entity = $this->entityManager->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); } /** * Get count. Can be slow. Use EntityCollection if you need count. */ 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 iterator_count($this); } protected function prepareEntity(Entity $entity): void {} /** * @deprecated As of v6.0. Use `getValueMapList`. * @todo Remove in v9.0. * @return array>|stdClass[] */ public function toArray(bool $itemsAsObjects = false): array { $arr = []; foreach ($this as $entity) { $item = $entity->getValueMap(); if (!$itemsAsObjects) { $item = get_object_vars($item); } $arr[] = $item; } return $arr; } /** * {@inheritDoc} */ 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; } /** * Create from a query. * * @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; } /** * Create from an SQL. * * @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; } }