Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: Make IsFirst and IsLast work as expected for PaginatedList (fixes #11465) #11471

Merged
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
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