Skip to content

Commit 16bf661

Browse files
committed
Improve pagination with maximum button counts, allowing forced number.
1 parent 9c7ec67 commit 16bf661

File tree

4 files changed

+113
-23
lines changed

4 files changed

+113
-23
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ $labels = [ // optional. Change button labels
4444
```php
4545
// Define inline keyboard pagination.
4646
$ikp = new InlineKeyboardPagination($items, $command);
47-
$ikp->setMaxButtons(6);
47+
$ikp->setMaxButtons(7, true); // Second parameter set to always show 7 buttons if possible.
4848
$ikp->setLabels($labels);
4949

5050
// Get pagination.
@@ -58,16 +58,16 @@ $pagination = $ikp->getPagination();
5858
### Result
5959
```php
6060
if (!empty($pagination['keyboard'])) {
61-
$pagination['keyboard'][0]['callback_data']; // testCommand?currentPage=10&nextPage=1
62-
$pagination['keyboard'][1]['callback_data']; // testCommand?currentPage=10&nextPage=9
61+
$pagination['keyboard'][0]['callback_data']; // command=testCommand&currentPage=10&nextPage=1
62+
$pagination['keyboard'][1]['callback_data']; // command=testCommand&currentPage=10&nextPage=7
6363
...
6464

6565
$response = [
66-
'reply_markup' => json_encode([
66+
'reply_markup' => [
6767
'inline_keyboard' => [
6868
$pagination['keyboard'],
6969
],
70-
]),
70+
],
7171
];
7272
}
7373
```

src/InlineKeyboardPagination.php

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ class InlineKeyboardPagination implements InlineKeyboardPaginator
2121
*/
2222
private $max_buttons = 5;
2323

24+
/**
25+
* @var bool
26+
*/
27+
private $force_button_count = false;
28+
2429
/**
2530
* @var integer
2631
*/
@@ -52,12 +57,13 @@ class InlineKeyboardPagination implements InlineKeyboardPaginator
5257
* @inheritdoc
5358
* @throws InlineKeyboardPaginationException
5459
*/
55-
public function setMaxButtons(int $max_buttons = 5): InlineKeyboardPagination
60+
public function setMaxButtons(int $max_buttons = 5, bool $force_button_count = false): InlineKeyboardPagination
5661
{
5762
if ($max_buttons < 5 || $max_buttons > 8) {
5863
throw new InlineKeyboardPaginationException('Invalid max buttons, must be between 5 and 8.');
5964
}
60-
$this->max_buttons = $max_buttons;
65+
$this->max_buttons = $max_buttons;
66+
$this->force_button_count = $force_button_count;
6167

6268
return $this;
6369
}
@@ -187,7 +193,7 @@ public function getPagination(int $selected_page = null): array
187193

188194
return [
189195
'items' => $this->getPreparedItems(),
190-
'keyboard' => [$this->generateKeyboard()],
196+
'keyboard' => $this->generateKeyboard(),
191197
];
192198
}
193199

@@ -244,7 +250,8 @@ protected function generateKeyboard(): array
244250
$button['text'] = sprintf($label, $page);
245251
}
246252

247-
return array_filter($buttons);
253+
// Pagination has 1 single row of buttons, so wrap in (row) array.
254+
return [array_values(array_filter($buttons))];
248255
}
249256

250257
/**
@@ -259,20 +266,26 @@ protected function generateRange(): array
259266

260267
if ($this->selected_page === 1) {
261268
$from = 2;
262-
$to = $from + $number_of_intermediate_buttons;
269+
$to = $this->max_buttons;
263270
} elseif ($this->selected_page === $number_of_pages) {
264271
$from = $number_of_pages - $number_of_intermediate_buttons;
265272
$to = $number_of_pages;
266273
} else {
267-
if (($this->selected_page + $number_of_intermediate_buttons) > $number_of_pages) {
268-
$from = $number_of_pages - $number_of_intermediate_buttons;
269-
$to = $number_of_pages;
270-
} elseif (($this->selected_page - 2) < 1) {
274+
if ($this->selected_page < 3) {
271275
$from = $this->selected_page;
272276
$to = $this->selected_page + $number_of_intermediate_buttons;
277+
} elseif (($number_of_pages - $this->selected_page) < 3) {
278+
$from = $number_of_pages - $number_of_intermediate_buttons;
279+
$to = $number_of_pages;
273280
} else {
274-
$from = $this->selected_page - 1;
275-
$to = $this->selected_page + 2;
281+
// @todo: Find a nicer solution for page 3
282+
if ($this->force_button_count) {
283+
$from = $this->selected_page - floor($number_of_intermediate_buttons / 2);
284+
$to = $this->selected_page + ceil($number_of_intermediate_buttons / 2) + ($this->selected_page === 3 && $this->max_buttons > 5);
285+
} else {
286+
$from = $this->selected_page - 1;
287+
$to = $this->selected_page + ($this->selected_page === 3 ? $number_of_intermediate_buttons - 1 : 2);
288+
}
276289
}
277290
}
278291

@@ -303,7 +316,7 @@ protected function generateButton(int $page): array
303316
*/
304317
protected function generateCallbackData(int $page): string
305318
{
306-
return "{$this->command}?currentPage={$this->selected_page}&nextPage={$page}";
319+
return "command={$this->command}&currentPage={$this->selected_page}&nextPage={$page}";
307320
}
308321

309322
/**
@@ -323,4 +336,18 @@ protected function getOffset(): int
323336
{
324337
return $this->items_per_page * ($this->selected_page - 1);
325338
}
339+
340+
/**
341+
* Get the parameters from the calback query.
342+
*
343+
* @param string $data
344+
*
345+
* @return array
346+
*/
347+
public static function getParametersFromCallbackData($data): array
348+
{
349+
parse_str($data, $params);
350+
351+
return $params;
352+
}
326353
}

src/InlineKeyboardPaginator.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ public function __construct(array $items, string $command, int $selected_page, i
2222
/**
2323
* Set the maximum number of keyboard buttons to show.
2424
*
25-
* @param int $max_buttons
25+
* @param int $max_buttons
26+
* @param bool $force_button_count
2627
*
2728
* @return InlineKeyboardPagination
2829
*/
29-
public function setMaxButtons(int $max_buttons = 5): InlineKeyboardPagination;
30+
public function setMaxButtons(int $max_buttons = 5, bool $force_button_count = false): InlineKeyboardPagination;
3031

3132
/**
3233
* Set command for this pagination.

tests/InlineKeyboardPaginationTest.php

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ public function testValidConstructor()
5050
$this->assertArrayHasKey('items', $data);
5151
$this->assertCount($this->items_per_page, $data['items']);
5252
$this->assertArrayHasKey('keyboard', $data);
53-
$this->assertArrayHasKey(1, $data['keyboard'][0]);
54-
$this->assertArrayHasKey('text', $data['keyboard'][0][1]);
55-
$this->assertStringStartsWith($this->command, $data['keyboard'][0][1]['callback_data']);
53+
$this->assertArrayHasKey(0, $data['keyboard'][0]);
54+
$this->assertArrayHasKey('text', $data['keyboard'][0][0]);
55+
$this->assertStringStartsWith("command={$this->command}", $data['keyboard'][0][0]['callback_data']);
5656
}
5757

5858
/**
@@ -75,6 +75,20 @@ public function testEmptyItemsConstructor()
7575
$ikp->getPagination();
7676
}
7777

78+
public function testCallbackDataParser()
79+
{
80+
$ikp = new InlineKeyboardPagination($this->items, $this->command, $this->selected_page, $this->items_per_page);
81+
$data = $ikp->getPagination();
82+
83+
$callback_data = $ikp::getParametersFromCallbackData($data['keyboard'][0][0]['callback_data']);
84+
85+
self::assertSame([
86+
'command' => $this->command,
87+
'currentPage' => "$this->selected_page",
88+
'nextPage' => '1', // because we're getting the button at position 0, which is page 1
89+
], $callback_data);
90+
}
91+
7892
public function testValidPagination()
7993
{
8094
$ikp = new InlineKeyboardPagination($this->items, $this->command, $this->selected_page, $this->items_per_page);
@@ -105,6 +119,54 @@ public function testSetMaxButtons()
105119
self::assertTrue(true);
106120
}
107121

122+
public function testForceButtonsCount()
123+
{
124+
$cbdata = 'command=%s&currentPage=%d&nextPage=%d';
125+
$command = 'cbdata';
126+
$ikp = new InlineKeyboardPagination(range(1, 10), $command, 1, 1);
127+
128+
// testing with 8 flexible buttons
129+
$ikp->setMaxButtons(8, false);
130+
131+
self::assertAllButtonPropertiesEqual([
132+
['· 1 ·', '2', '3', '4 ›', '5 ›', '6 ›', '7 ›', '10 »'],
133+
], 'text', $ikp->getPagination(1)['keyboard']);
134+
135+
self::assertAllButtonPropertiesEqual([
136+
['« 1', '‹ 4', '· 5 ·', '6 ›', '10 »'],
137+
], 'text', $ikp->getPagination(5)['keyboard']);
138+
139+
// testing with 8 fixed buttons
140+
$ikp->setMaxButtons(8, true);
141+
142+
self::assertAllButtonPropertiesEqual([
143+
['· 1 ·', '2', '3', '4 ›', '5 ›', '6 ›', '7 ›', '10 »'],
144+
], 'text', $ikp->getPagination(1)['keyboard']);
145+
146+
self::assertAllButtonPropertiesEqual([
147+
['· 1 ·', '2', '3', '4 ›', '5 ›', '6 ›', '7 ›', '10 »'],
148+
], 'text', $ikp->getPagination(1)['keyboard']);
149+
150+
self::assertAllButtonPropertiesEqual([
151+
['« 1', '‹ 2', '‹ 3', '‹ 4', '· 5 ·', '6 ›', '7 ›', '10 »'],
152+
], 'text', $ikp->getPagination(5)['keyboard']);
153+
154+
// testing with 7 fixed buttons
155+
$ikp->setMaxButtons(7, true);
156+
157+
self::assertAllButtonPropertiesEqual([
158+
['· 1 ·', '2', '3', '4 ›', '5 ›', '6 ›', '10 »'],
159+
], 'text', $ikp->getPagination(1)['keyboard']);
160+
161+
self::assertAllButtonPropertiesEqual([
162+
['« 1', '‹ 3', '‹ 4', '· 5 ·', '6 ›', '7 ›', '10 »'],
163+
], 'text', $ikp->getPagination(5)['keyboard']);
164+
165+
self::assertAllButtonPropertiesEqual([
166+
['« 1', '‹ 5', '‹ 6', '‹ 7', '8', '9', '· 10 ·'],
167+
], 'text', $ikp->getPagination(10)['keyboard']);
168+
}
169+
108170
/**
109171
* @expectedException \TelegramBot\InlineKeyboardPagination\Exceptions\InlineKeyboardPaginationException
110172
* @expectedExceptionMessage Invalid max buttons, must be between 5 and 8.
@@ -145,7 +207,7 @@ public function testInvalidItemsPerPage()
145207

146208
public function testButtonLabels()
147209
{
148-
$cbdata = '%s?currentPage=%d&nextPage=%d';
210+
$cbdata = 'command=%s&currentPage=%d&nextPage=%d';
149211
$command = 'cbdata';
150212
$ikp1 = new InlineKeyboardPagination(range(1, 1), $command, 1, $this->items_per_page);
151213
$ikp10 = new InlineKeyboardPagination(range(1, $this->items_per_page * 10), $command, 1, $this->items_per_page);

0 commit comments

Comments
 (0)