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 7914af9
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 6 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
25 changes: 25 additions & 0 deletions tests/php/View/SSViewerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ class SSViewerTest extends SapphireTest
*/
protected $oldServer = [];

protected static $fixture_file = 'SSViewerTest/SSViewerTestModel.yml';

protected static $extra_dataobjects = [
SSViewerTest\TestObject::class,
SSViewerTest\SSViewerTestModel::class,
];

protected function setUp(): void
Expand Down Expand Up @@ -1480,6 +1483,28 @@ public function testSSViewerBasicIteratorSupport()
$this->assertEquals("", $result, "Only numbers that are multiples of 11 are returned. I.e. nothing returned");
}

public function testSSViewerBasicIteratorSupportWithPaginatedList()
{
$paginatedList = PaginatedList::create(SSViewerTestModel::get())->setPageLength(2);
$data = new ArrayData([
'PaginatedList' => $paginatedList
]);

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

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

$paginatedList->setCurrentPage(2);

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

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

/**
* Test $Up works when the scope $Up refers to was entered with a "with" block
*/
Expand Down
4 changes: 3 additions & 1 deletion tests/php/View/SSViewerTest/SSViewerTestModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@

class SSViewerTestModel extends DataObject implements TestOnly
{

private static array $db = [
'Name' => 'Varchar',
];
}
9 changes: 9 additions & 0 deletions tests/php/View/SSViewerTest/SSViewerTestModel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
SilverStripe\View\Tests\SSViewerTest\SSViewerTestModel:
test1:
Name: steven
test2:
Name: bonny
test3:
Name: sam
test4:
Name: bob

0 comments on commit 7914af9

Please sign in to comment.