Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions src/Query/IteratorBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,33 @@
abstract class IteratorBuilder extends Builder
{
protected $randomize = false;
private $itemsBeingPaginated;

public function count()
{
return $this->getFilteredAndLimitedItems()->count();
}

public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
{
// The total and the page are both taken from the same items, so they only need working out once.
$this->itemsBeingPaginated = $this->getFilteredItems();

try {
return parent::paginate($perPage, $columns, $pageName, $page);
} finally {
$this->itemsBeingPaginated = null;
}
}

protected function getCountForPagination()
{
return $this->getFilteredItems()->count();
return ($this->itemsBeingPaginated ?? $this->getFilteredItems())->count();
}

public function get($columns = ['*'])
{
$items = $this->getFilteredItems();
$items = $this->itemsBeingPaginated ?? $this->getFilteredItems();

if ($this->randomize) {
$items = $items->shuffle();
Expand Down Expand Up @@ -108,9 +121,16 @@ protected function intersectFromWhereClause($entries, $filteredEntries, $where)

private function intersectItems($entries, $filteredEntries)
{
return $entries->filter(function ($entry) use ($filteredEntries) {
return $filteredEntries->contains($entry);
});
if ($filteredEntries->contains(fn ($entry) => ! is_object($entry))) {
return $entries->filter(fn ($entry) => $filteredEntries->contains($entry));
}

// Both are taken from the same items, so an object can be looked up rather than compared with each of the others.
$filtered = $filteredEntries->mapWithKeys(fn ($entry) => [spl_object_id($entry) => true])->all();

return $entries->filter(fn ($entry) => is_object($entry)
? isset($filtered[spl_object_id($entry)])
: $filteredEntries->contains($entry));
}

protected function filterWhereIn($entries, $where)
Expand Down
29 changes: 29 additions & 0 deletions tests/Search/QueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -732,10 +732,37 @@ public function results_are_found_using_where_status()
$this->assertCount(1, $results);
$this->assertEquals(['b'], $results->map->reference->all());
}

#[Test]
public function paginating_works_the_results_out_once()
{
$items = collect(range(1, 25))->map(fn ($i) => ['reference' => 'item'.$i, 'status' => $i % 5 ? 'published' : 'draft', 'site' => $i % 2 ? 'en' : 'fr']);

$query = (new FakeQueryBuilder($items))->withoutData()->where('status', 'published')->where('site', 'en');

$paginator = $query->paginate(4, ['*'], 'page', 2);

$this->assertSame(1, $query->searches);
$this->assertSame(10, $paginator->total());
$this->assertEquals(['item11', 'item13', 'item17', 'item19'], collect($paginator->items())->map->reference->all());
}

#[Test]
public function a_query_can_be_used_again_after_paginating()
{
$items = collect(range(1, 6))->map(fn ($i) => ['reference' => 'item'.$i, 'site' => $i % 2 ? 'en' : 'fr']);

$query = (new FakeQueryBuilder($items))->withoutData()->where('site', 'en');

$this->assertSame(3, $query->paginate(2)->total());
$this->assertEquals(['item3'], $query->where('reference', 'item3')->get()->map->reference->all());
$this->assertSame(1, $query->paginate(2)->total());
}
}

class FakeQueryBuilder extends QueryBuilder
{
public $searches = 0;
protected $results;

public function __construct($results)
Expand All @@ -746,6 +773,8 @@ public function __construct($results)

public function getSearchResults($query)
{
$this->searches++;

return $this->results;
}
}
Loading