i'm a newbie in php especially on making pagination.
my question is, how to make paging like stackoverflow's pagination?
i mean paging like this :
1 ... 5 6 7 8 9 ... 25
(the first number and the last number is always appear, but in the middle only 5 numbers with the selected page absolutely in the middle)
in php i have tried making paging,
<?php
//Show page links
for($i=1; $i<=$pages; $i++)
{
echo '<li id="'.$i.'">'.$i.'</li>';
}
?>
but it will be shown all of pages like
1 2 3 4 5 6 7 8 9 10 etc
any body have simple logic example to solve this problem?
many thanks :)
This will generate the numbers as per above with current = 7, pages = 25. Replace the numbers with links to get an actual pagination index.
$current = 7;
$pages = 25;
$links = array();
if ($pages > 3) {
// this specifies the range of pages we want to show in the middle
$min = max($current - 2, 2);
$max = min($current + 2, $pages-1);
// we always show the first page
$links[] = "1";
// we're more than one space away from the beginning, so we need a separator
if ($min > 2) {
$links[] = "...";
}
// generate the middle numbers
for ($i=$min; $i<$max+1; $i++) {
$links[] = "$i";
}
// we're more than one space away from the end, so we need a separator
if ($max < $pages-1) {
$links[] = "...";
}
// we always show the last page
$links[] = "$pages";
} else {
// we must special-case three or less, because the above logic won't work
$links = array("1", "2", "3");
}
echo implode(" ", $links);
Output:
1 ... 5 6 7 8 9 ... 25
Below is a snippet from a general pagination class1 I wrote a few years ago. I have edited it to show the relevant parts only.
// cntAround is the number of pages to show before and after the current
function renderNavigation($cntAround = 1) {
$out = '';
$isGap = false; // A "gap" is the pages to skip
$current = // Current page
$cntPages = // Total number of pages
for ($i = 0; $i < $pages; $i++) { // Run through pages
$isGap = false;
// Are we at a gap?
if ($cntAround >= 0 && $i > 0 && $i < $cntPages - 1 && abs($i - $current) > $cntAround) { // If beyond "cntAround" and not first or last.
$isGap = true;
// Skip to next linked item (or last if we've already run past the current page)
$i = ($i < $current ? $current - $cntAround : $cntPages - 1) - 1;
}
$lnk = ($isGap ? '...' : ($i + 1)); // If gap, write ellipsis, else page number
if ($i != $current && !$isGap) { // Do not link gaps and current
$lnk = '' . $lnk . '';
}
$out .= "\t<li>" . $lnk . "</li>\n"; // Wrap in list items
}
return "<ul>\n" . $out . '</ul>'; // Wrap in list
}
Example 1
cntAround = 1, current = 5, cntPages = 9:
[1] ... [4] 5 [6] ... [9]
Example 2
cntAround = 3, current = 5, cntPages = 11:
[1] [2] [3] [4] 5 [6] [7] [8] ... [11]
1) Article is in Danish. Google Translate'd version is here.
Somewhat like this(pseudo-code):
pg = CurrentPageNo
low = 1
high = MAX_PAGES
if (pg-low <=5)
output 1 to pg-1 [with links]
else
output 1..3 [with links]
output "..."
output (pg-3) to (pg-1) [with links]
output pg
if (high - pg <=5)
output pg+1 to high [with links]
else
output (pg+1) to high-3 [with links]
output "..."
output (high-2) to high [with links]
You could use Zend_Paginator to do just that, and learn to use the Zend Framework while you're at it.
Below is php classes link from where you can download php class for pagination.
http://www.phpclasses.org/search.html?words=paging&x=0&y=0&go_search=1
If you (might potentially) have a large number of pages, consider using "logarithmic" page navigation, as described here (sample code included):
How to do page navigation for many, many pages? Logarithmic page navigation
(Note that it'll work just fine for small numbers of pages, too, of course!)
Related
I have a pagination which renders some pages. I have a setting where the user can define how many pages are to be displayed as numbers in the pagination nav. The first and last number are always visible. Now if the user wants to have 3 numbers on the nav and assuming that i am now in the page 7, then it should look like this:
1 ... 6 7 8 ... 12
If the user wants four items then it should look like that:
1 ... 6 7 8 9 ... 12
Up until now i have the following which gives me 3 before and after the current page
$maxLinks = 3;
$currentPageNumber = 7;
$pages = [];
$pages[] = 1;
for($i = max(2, $currentPageNumber - $maxLinks); $i <= min($currentPageNumber + $maxLinks, 12 - 1); $i++) {
$pages[] = $i;
}
$pages[] = 12;
foreach ($pages as $key => $page) {
$newPage = '...';
if (($key === 0) && $pages[1] !== $page + 1) {
array_splice( $pages, 1, 0, $newPage );
}
$itemBeforeLast = count($pages)-2;
if (is_numeric($pages[$itemBeforeLast]) && ($key === $itemBeforeLast) && $pages[$itemBeforeLast + 1] !== $pages[$itemBeforeLast] + 1) {
array_splice( $pages, $itemBeforeLast +1, 0, $newPage );
}
}
This gives me back the following:
But i only want to get 3 or 4 numbers between the dots (this changes based on the value that the user gives in the settings ($maxLinks variable))
Any help is deeply appreciated
Best regards
As you add them to both sides, you need to divide by 2. Also, remove 1 to account for the current page. Then you just need to account for the possibility of having a non-even number of links to the left&right by rounding (down for the left, up for the right).
And end up with:
for($i = max(2, $currentPageNumber - floor(($maxLinks-1)/2));
$i <= min($currentPageNumber + ceil(($maxLinks-1)/2), 12 - 1); $i++)
I have an pagination code:
//pagination
$result1=$mysqli->query("SELECT * FROM product");
$row_per_page=10 ;
$rows=$result1->num_rows;
if ($rows>$row_per_page) $page=ceil($rows/$row_per_page);
else $page=1;
if(isset($_GET['start']) && (int)$_GET['start'])
$start=$_GET['start'];
else
$start=0;
$result=$mysqli->query("SELECT * FROM product limit $start,$row_per_page");
//End pagination
while ($rows = $result->fetch_assoc()) {
echo $rows['name'];
}
$page_cr=($start/$row_per_page)+1;
for($i=1;$i<=$page;$i++)
{
if ($page_cr!=$i) echo "<div class='pagination'>"."<a href='index.php?go=product&start=".$row_per_page*($i-1)."'>$i </a>"."</div>";
else echo "<div class='pagination'>".$i." "."</div>";
}
This code create pagination like this
Yeah, i want to remove 6 7 8 9 ect... and replace with "...". When i click to page 6, it will remove 1 2 3 4 5 - 10 11 12 13 etc..,it only show 6 7 8 9 as image below
[![Pagination][2]][2]
I hope you can understand my ideal, i try the best to show you.
Thank you so much
Here's a solution to your pagination problem, the kind of solution Google uses to paginate it's search results. Basically, the idea behind this solution is this:
At any point of time, at most 5 pagination links will be displayed i.e. from the current page's perspective, display two predecessor and successor pages. Let me explain this thing using two scenarios,
Case(1): When the number of pages is equal to or less than 5 (Lets say we have 4 pages)
In this case, we have to display all pagination links without any preceding or succeeding dots, like this:
// User is on page 1
1 2 3 4
- - -
// User is on page 3
1 2 3 4
- - -
// User is on page 4
1 2 3 4
- - -
Case(2): When the number of pages is greater than 5 (Lets say we have 10 pages)
In this case, we have to display both the pagination links and dots accordingly, like this:
// User is on page 1
1 2 3 4 5 ...
- - - -
// User is on page 5
... 3 4 5 6 7 ...
- - - -
// User is on page 10
... 6 7 8 9 10
- - - -
So after getting the current page number using $page_cr=($start/$row_per_page)+1;, the algorithm of this custom pagination system would be like this:
Check whether the number of pages is greater than 5 or not. If it's greater than 5, go to step 2 otherwise go to step 5.
Find the superset range of pages, like 1-10, or 1-20 etc. For example, if $page = 10 then this superset range would be 1-10. The code for this step is this:
// Superset range of pages
$superset_range = range(1, $page);
Find the subset range of pages to display, like 1-5, or 3-7 etc. For example, if $page = 10 then this subset range would be 1-5, or 3-7, or 6-10 etc., it can be any consecutive five page between 1 and 10. Also, adjust this range whenever necessary. The code for this step is this:
// Subset range of pages to display
$subset_range = range($page_cr - 2, $page_cr + 2);
// Adjust the range
foreach($subset_range as $p){
if($p <= 0){
array_shift($subset_range);
$subset_range[] = $subset_range[count($subset_range) - 1] + 1;
}elseif($p > $page){
array_pop($subset_range);
array_unshift($subset_range, $subset_range[0] - 1);
}
}
Display the pagination links and dots accordingly. The code for this step is this:
// Display pagination links and dots
if($subset_range[0] > $superset_range[0]){
echo "<div class='pagination'>... </div>";
}
foreach($subset_range as $p){
if($page_cr != $p){
echo "<div class='pagination'>"."<a href='index.php?go=product&start=".$row_per_page*($p-1)."'>$p </a>"."</div>";
}else{
echo "<div class='pagination'>".$p." "."</div>";
}
}
if($subset_range[count($subset_range) - 1] < $superset_range[count($superset_range) - 1]){
echo "<div class='pagination'> ...</div>";
}
Display all pagination links using your old code, like this:
// Display all page links
for($i = 1; $i <= $page; $i++){
if($page_cr != $i){
echo "<div class='pagination'>"."<a href='index.php?go=product&start=".$row_per_page*($i-1)."'>$i </a>"."</div>";
}else{
echo "<div class='pagination'>".$i." "."</div>";
}
}
So here's the complete code:
$result1 = $mysqli->query("SELECT * FROM product");
$row_per_page= 10;
$rows=$result1->num_rows;
if ($rows > $row_per_page){
$page=ceil($rows/$row_per_page);
}else{
$page=1;
}
if(isset($_GET['start']) && (int)$_GET['start']){
$start=$_GET['start'];
}else{
$start=0;
}
$result=$mysqli->query("SELECT * FROM product limit $start,$row_per_page");
while ($rows = $result->fetch_assoc()) {
echo $rows['name'];
}
$page_cr=($start / $row_per_page) + 1; // Page number
if($page > 5){
// From the current page's perspective, display two predecessor and successor pages
// Superset range of pages
$superset_range = range(1, $page);
// Subset range of pages to display
$subset_range = range($page_cr - 2, $page_cr + 2);
// Adjust the range
foreach($subset_range as $p){
if($p <= 0){
array_shift($subset_range);
$subset_range[] = $subset_range[count($subset_range) - 1] + 1;
}elseif($p > $page){
array_pop($subset_range);
array_unshift($subset_range, $subset_range[0] - 1);
}
}
// Display pagination links and dots
if($subset_range[0] > $superset_range[0]){
echo "<div class='pagination'>... </div>";
}
foreach($subset_range as $p){
if($page_cr != $p){
echo "<div class='pagination'>"."<a href='index.php?go=product&start=".$row_per_page*($p-1)."'>$p </a>"."</div>";
}else{
echo "<div class='pagination'>".$p." "."</div>";
}
}
if($subset_range[count($subset_range) - 1] < $superset_range[count($superset_range) - 1]){
echo "<div class='pagination'> ...</div>";
}
}else{
// Display all page links
for($i = 1; $i <= $page; $i++){
if($page_cr != $i){
echo "<div class='pagination'>"."<a href='index.php?go=product&start=".$row_per_page*($i-1)."'>$i </a>"."</div>";
}else{
echo "<div class='pagination'>".$i." "."</div>";
}
}
}
I have the following array
$example=array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);
$limit=4 // 4 at the beginning only...
//it used to get incremented automatically to 8,12,16....
At first i want 1,2,3,4as an output for which i have done
foreach($example as $eg)
{
if($eg>$limit)
continue;
}
and i am easily getting 1,2,3,4 at the first then 1,2,3,4,5,6,7,8 then1,2,3,4,5,6,7,8,9,10,11,12
But now i what i want is 1,2,3,4 at the very beginning then 5,6,7,8 then 9,10,11,12 lyk this... how can i get that???
please do help me... :)
AS the
foreach($example as $eg)
{
if($eg>$limit)
continue;
}
is returning only 1,2,3,4 at $limit=4 and 1,2,3,4,5,6,7,8 at $limit=8
i need 1,2,3,4 at $limit=4 and 5,6,7,8 at $limit=8
Have you tried using the helpful built-in functions?
array_chunk($example,$limit);
Alternatively, for more page-like behaviour:
$pagenum = 2; // change based on page
$offset = $pagenum * $limit;
array_slice($example,$offset,$limit);
You would first chunk the array so each chunk has 4 elements, then loop through each chunk:
To change the numbers shown depending on which group, you could do:
$group = $_GET['group'];
$items = array_chunk($example, ceil(count($example)/4)[$group-1];
echo implode(", ", $items);
Then you can go to
yoursite.com/page.php?group=1
And it will output
1, 2, 3, 4
And when you go to
yoursite.com/page.php?group=2
It will output
5, 6, 7, 8
etc.
You can try something like this
$example=array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);
$limit = 8;
$limit -= 4;
for($i = $limit; $i < ($limit + 4); $i++)
{
echo $example[$i].' ';
}
Output
//for $limit 4 output 1 2 3 4
//for $limit 8 output 5 6 7 8
//for $limit 16 output 13 14 15 16
Have an $A is the data for pagination:
$A = array(
0=>array(
0=>1,
1=>2
),
1=>array(
0=>3,
1=>5,
2=>2
),
2=>array(
0=>3,
1=>1,
2=>6,
3=>6
)
);
Anybody could help me to get the expected ouput (output this "...." more) the most important ?
.... told that it is still more element need to display next page.
or it the remain element from previous page.
There are 09 elements of $A to display ,So
I set
$show_per_page = 3;
Output (for the first page):
1
2
Total:3
3
....//output this "...." more
Output (for the second page):
....//output this "...." continue from first page
5
2
Total:10
3
.... //output this "...." more
Output (for the third page):
.... //output this "...." continue from second page
1
6
6
Total:16
if I set
$show_per_page = 5;
Output (for the first page):
1
2
Total:3
3
5
2
Total:10
// .... //not output this "...." more now
Output (for the second page):
3
1
6
6
Total:16
if I set
$show_per_page = 9;
OUTPUT:
1
2
Total:3
3
5
2
Total:10
3
1
6
6
Total:16
Currently I am try to do with the function paging_from_multi_arr but I am stuck on how implement to got the expeted result:
// page to show (1-indexed)
// number of items to show per page
function paging_from_multi_arr($display_array, $page){
Global $show_per_page;
$start = $show_per_page * ($page-1);
$end = $show_per_page * $page;
$i = 0;
foreach($display_array as $main_order=>$section){
$total = 0;
foreach($section as $sub_order=>$value){
if($i >= $end){
break 2; // break out of both loops
}
$total += $value;
if($i >= $start){
echo $value.'<br>';
}
$i++;
}
if($i >= $start){
echo 'Total:'.$total.'<br>';
}
if($i >= $end){
break;
}
}
$total = count($display_array, COUNT_RECURSIVE);
// Total numbers of elements in $display_array array.
// See http://php.net/manual/en/function.count.php
if ($end < $total){
echo "...";
}
}
$show_per_page = 5;
paging_from_multi_arr($A,$_GET["page"]);
Do you have any idea with the function here? Or could give the better algorithm?
thanks
This should give you the output you're looking for:
function flatten_display_array($display_array){
$new_array = array();
$count = 0;
foreach($display_array as $main_order => $section){
$total = 0;
foreach($section as $sub_order => $value){
$new_array[] = array('main' => $main_order,
'sub' => $sub_order,
'value' => $value);
$total += $value;
$count++;
}
// Add section's total to final element in section
$new_array[$count-1]['total'] = $total;
}
return $new_array;
}
function paging_from_multi_array($display_array, $page = 1, $show_per_page = 3){
if(isset($_GET['page']) && is_numeric($_GET['page'])){
// Page number set externally
$page = $_GET['page'];
}
if(isset($_GET['per_page']) && is_numeric($_GET['per_page'])){
// Per page set externally
$show_per_page = $_GET['per_page'];
}
$start = $show_per_page*($page-1);
$end = $show_per_page*$page;
// Convert array to useable format
$new_array = flatten_display_array($display_array);
/* Formatting elements */
$top_string = '....'; // Indicator continues is on previous page
$bottom_string = '....'; // Indicator continues on next page
$br = '<br />'; // Line break
$indent = ' '; // Indent of value row
$count = 0;
$string = '';
for($i = $start; $i < $end; $i++){
// Loop through visible range
$string .= $indent.$new_array[$i]['value'].$br;
if(isset($new_array[$i]['total'])){
$string .= 'Total: '.$new_array[$i]['total'].$br;
}
}
// Check previous page
if($start > 0 && $start < count($new_array) && !isset($new_array[$start-1]['total'])){
// Started mid-way through section
$string = $top_string.$br.$string;
}
// Check next page
if($end-1 < count($new_array) && !isset($new_array[$end-1]['total'])){
// Stopped mid-way through section
$string .= $bottom_string.$br;
}
return $string;
}
To use it, just call the paging_from_multi_array() function:
echo paging_from_multi_array($A);
This way, if the page number or the amount to show per page is not set, it will default to the ones set in the first line of paging_from_multi_array() (currently page 1 and 3 per page).
Also, look at the lines under /* Formatting Elements */ to set the elements for the output (eg: the '...' before and after each segment.
Without the "total" calculation, it should be a simple pagination (by merging the sub-arrays and paginate on the big array). But actually, "total" has nothing to do with the pagination itself. So I recommending let's first forget the "total" for paginating, then insert it in approriate place later.
My idea begins with creating a merge-array like this:
$B = array(
0=>1,
1=>2
0=>3,
1=>5,
2=>2
0=>3,
1=>1,
2=>6,
3=>6
}
And another array to keep track of the beginning of each sub-array of $A in the merge-array $B:
$C = {0, 2, 5}
Then I can do the pagination pretty simply as usual. About the "total", we can use the original array A to calculate it, then insert into the approriate position based on C.
For a quick example, at page 2, max-per-page = 3
From B, I get the sub-array B1 : B(offset = 1, max-per-page=3, from B[3] to B[5])
$B1 = {
1=>5,
2=>2,
0=>3
}
Based on $C={0,2,5}, by a simple "for" loop, we can have $C[1] = 2 < 3 < 5 = 5 = $C[2] < length(B), so at here we know that we will show 2 sub-array (A[1] and a[2]); and we must calculate and return total(A[1]) as well.
That's my idea. I think it would make things easier to keep track.
I have the following method that creates and returns markup for my pagination links in PHP.
public function getPaginationLinks($options) {
if($options['total_pages'] > 1) {
$markup = '<div class="pagination">';
if($options['page'] > 1) {
$markup .= '< prev';
}
for($i = 1; $i <= $options['total_pages']; $i++) {
if($options['page'] != $i) {
$markup .= '' . $i . '';
}
else {
$markup .= '<span class="current">' . $i . '</span>';
}
}
if($options['page'] < $options['total_pages']) {
$markup .= 'next >';
}
$markup .= '</div>';
return $markup;
}
else {
return false;
}
}
I just recently discovered (to my surprise) that i had reached 70+ pages which means that there are now 70+ links showing up at the bottom..
I'm wondering if someone can help me break this up.. I'm not sure how most pagination works as far as showing the numbers if im on say.. page 30, ideas?
You just display the current page plus the previous and the following x (say 4) pages.
If you're on Page 1:
1 2 3 4 5
Page 35:
31 32 33 34 35 36 37 38 39
Page 70:
66 67 68 69 70
You could also add a quick link to the first and last page using « and » for instance.
Example:
$x = 4;
for ($i = $currentPage - $x; $i < $currentPage; $i++)
{
if ($i >= 1) { /* show link */}
else { /* show ellipsis and fix counter */ $i = 1; }
}
/* show current page number without link */
for ($i = $currentPage + 1; $i < $currentPage + $x; $i++)
{
if ($i <= $totalPages) { /* show link */}
else { /* show ellipsis and break */ break; }
}
You can also implement Infinite History / Pagination, which is uber cool. =)
UPDATE: A more elegant version of this # Codepad.
You could do (on page 15)
[View Previous] 12 13 14 [15] 15 17 18 [View More]
Where the [View More] link fetches the rest (or just a few more) page links. This keeps things uncluttered while allowing the user to navigate all the pages.
Example (after clicking View Previous)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [15] 15 17 18 [View More]
or (just show a few more)
[View More] 7 8 9 10 11 12 13 14 [15] 15 17 18 [View More]
When I say "fetch" I mean use javascript to create links to the other pages w/o reloading the page
Consider "logarithmic pagination", as described here (PHP code included):
How to do page navigation for many, many pages? Logarithmic page navigation
You may also look at Zend_Paginator, which handles lots of these types of things for you.