Skip to content

Commit

Permalink
FIX: Make IsFirst and IsLast work as expected for PaginatedList (fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
kinglozzer committed Nov 19, 2024
1 parent dae7320 commit 0abd282
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 5 deletions.
19 changes: 14 additions & 5 deletions src/View/SSViewer_Scope.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use SilverStripe\ORM\FieldType\DBFloat;
use SilverStripe\ORM\FieldType\DBInt;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\PaginatedList;

/**
* This tracks the current scope for an SSViewer instance. It has three goals:
Expand Down Expand Up @@ -305,8 +306,8 @@ public function next()
}

if (!$this->itemIterator) {
// Note: it is important that getIterator() is called before count() as implemenations may rely on
// this to efficiency get both the number of records and an iterator (e.g. DataList does this)
// Note: it is important that getIterator() is called before count() as implementations may rely on
// this to efficiently get both the number of records and an iterator (e.g. DataList does this)

// Item may be an array or a regular IteratorAggregate
if (is_array($this->item)) {
Expand All @@ -319,11 +320,19 @@ public function next()
$this->itemIterator->rewind();
}

// If the item implements Countable, use that to fetch the count, otherwise we have to inspect the
// iterator and then rewind it.
if ($this->item instanceof Countable) {
// Special case: we *don't* want to use count() on PaginatedList. This is because it'll call
// PaginatedList::count(), which currently returns the full list count rather than the count of items
// on the current page (which is what we need for the iterator count)
if ($this->item instanceof PaginatedList) {
// We have to re-fetch the iterator before calling getInnerIterator(): we need to count a copy of the
// inner iterator because it's a generator so can't be rewound or cloned
$innerIterator = $this->item->getIterator()->getInnerIterator();
$this->itemIteratorTotal = iterator_count($innerIterator);
} elseif ($this->item instanceof Countable) {
// If the item implements Countable, use that to fetch the count
$this->itemIteratorTotal = count($this->item);
} else {
// Otherwise we have to inspect the iterator and then rewind it
$this->itemIteratorTotal = iterator_count($this->itemIterator);
$this->itemIterator->rewind();
}
Expand Down
31 changes: 31 additions & 0 deletions tests/php/View/SSViewerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1480,6 +1480,37 @@ public function testSSViewerBasicIteratorSupport()
$this->assertEquals("", $result, "Only numbers that are multiples of 11 are returned. I.e. nothing returned");
}

public function testSSViewerBasicIteratorSupportWithPaginatedList()
{
$list = new ArrayList([
['Val' => 1],
['Val' => 2],
['Val' => 3],
['Val' => 4],
['Val' => 5],
['Val' => 6],
]);
$paginatedList = new PaginatedList($list);
$paginatedList->setPageLength(2);
$data = new ArrayData([
'PaginatedList' => $paginatedList
]);

$result = $this->render('<% loop PaginatedList %><% if $IsFirst %>$Val<% end_if %><% end_loop %>', $data);
$this->assertEquals("1", $result, "Only the first item on the first page is rendered");

$result = $this->render('<% loop PaginatedList %><% if $IsLast %>$Val<% end_if %><% end_loop %>', $data);
$this->assertEquals("2", $result, "Only the last item on the first page is rendered");

$paginatedList->setCurrentPage(2);

$result = $this->render('<% loop PaginatedList %><% if $IsFirst %>$Val<% end_if %><% end_loop %>', $data);
$this->assertEquals("3", $result, "Only the first item on the second page is rendered");

$result = $this->render('<% loop PaginatedList %><% if $IsLast %>$Val<% end_if %><% end_loop %>', $data);
$this->assertEquals("4", $result, "Only the last item on the second page is rendered");
}

/**
* Test $Up works when the scope $Up refers to was entered with a "with" block
*/
Expand Down

0 comments on commit 0abd282

Please sign in to comment.