Skip to content
Open
81 changes: 70 additions & 11 deletions src/ResultSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,84 @@
namespace ipl\Orm;

use ArrayIterator;
use BadMethodCallException;
use Generator;
use Iterator;
use Traversable;

/**
* Dataset containing database rows
*
* @implements Iterator<int, mixed>
*/
class ResultSet implements Iterator
{
/** @var ArrayIterator<int, mixed> */
protected $cache;

/** @var bool Whether cache is disabled */
protected $isCacheDisabled = false;

/** @var Generator<int, mixed, mixed, mixed> */
protected $generator;

/** @var ?int */
protected $limit;

/** @var ?int */
protected $position;

public function __construct(Traversable $traversable, $limit = null)
/** @var ?int */
protected $offset;

/** @var ?int */
protected $pageSize;

/**
* @param Traversable<int, mixed> $traversable
* @param ?int $limit
* @param ?int $offset
*/
public function __construct(Traversable $traversable, ?int $limit = null, ?int $offset = null)
{
$this->cache = new ArrayIterator();
$this->generator = $this->yieldTraversable($traversable);
$this->limit = $limit;
$this->offset = $offset;
}

/**
* Create a new result set from the given query
*
* @param Query $query
*
* @return static
* @return ResultSet
*/
public static function fromQuery(Query $query)
public static function fromQuery(Query $query): ResultSet
{
return new static($query->yieldResults(), $query->getLimit());
return new static($query->yieldResults(), $query->getLimit(), $query->getOffset());
}

/**
* Do not cache query result
*
* ResultSet instance can only be iterated once
*
* @return $this
* @return ResultSet
*/
public function disableCache()
public function disableCache(): ResultSet
{
$this->isCacheDisabled = true;

return $this;
}

public function hasMore()
public function hasMore(): bool
{
return $this->generator->valid();
}

public function hasResult()
public function hasResult(): bool
{
return $this->generator->valid();
}
Expand Down Expand Up @@ -86,7 +109,7 @@ public function next(): void
}
}

public function key(): int
public function key(): ?int
{
if ($this->position === null) {
$this->advance();
Expand Down Expand Up @@ -117,7 +140,7 @@ public function rewind(): void
}
}

protected function advance()
protected function advance(): void
{
if (! $this->generator->valid()) {
return;
Expand All @@ -137,10 +160,46 @@ protected function advance()
}
}

protected function yieldTraversable(Traversable $traversable)
/**
* @param Traversable<int, mixed> $traversable
* @return Generator
*/
protected function yieldTraversable(Traversable $traversable): Generator
{
foreach ($traversable as $key => $value) {
yield $key => $value;
}
}

/**
* Sets the amount of items a page should contain (only needed for pagination)
*
* @param ?int $size
* @return void
*/
public function setPageSize(?int $size): void
{
$this->pageSize = $size;
}

/**
* Returns the current page calculated from the {@see ResultSet::$offset} and the {@see ResultSet::$pageSize}
*
* @return int
* @throws BadMethodCallException if no {@see ResultSet::$pageSize} has been provided
*/
protected function getCurrentPage(): int
{
if ($this->pageSize) {
if ($this->offset && $this->offset > $this->pageSize) {
// offset is not on the first page anymore
return intval(floor($this->offset / $this->pageSize));
}

// no offset defined or still on page 1
return 1;
}

throw new BadMethodCallException(`The 'pageSize' property has not been set. Cannot calculate pages.`);
}
}