How to Create Pagination With PHP
When you have loads of data like blog posts, products or any listed item for that matter, it’s impractical to load all of them at once. It you have thousands of items, not only does it require loads of resources but most of the time it can simply take too long too load. Slow websites cause users to navigate away which will increase your bounce rate. There are two ways to overcome this. One way is to use lazy loading and the other is to use pagination. We’ll be going over the latter in this post and as a bonus, we’ll build a category filter that remains active while paging through the pagination. Find a working example of the project here.
Prerequisites
- An intermediate understanding of PHP
- Some basic Maths logic
To begin, I’ve created some basic HTML markup so we have some structure. I’ve also included Bootstrap so I have some predefined classes and styles to add a bit of design to the HTML. Luckily, Bootstrap includes styling for pagination. For the data, I’m going to use an array of products I’ve created manually, but you’ll probably be pulling the data from a database like SQL, or even from an external source, like a JSON or XML API. No matter what the case is, the data will most likely be an array.
Here’s the HTML:
<!DOCTYPE html>
<html>
<head>
<title>Pagination Demo</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<style>
body {
background-color: #eee;
min-height: 100vh;
display: flex;
align-items: center;
}
.container {
background-color: #fff;
border: 1px solid #ddd;
border-radius: 10px;
max-width: 800px;
padding: 20px;
}
.filter {
background-color: #eee;
padding: 10px 20px;
margin: 0 0 10px;
text-align: center;
border-radius: 5px;
vertical-align: middle;
}
form {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
label, input, select {
display: inline-block !important;
margin-right: 10px;
width: auto !important;
margin-bottom: 0;
}
.product {
padding: 20px;
}
.column-inner {
border: 1px solid #ddd;
}
.img-fluid {
width: 100%;
height: auto;
}
.pagination {
border-top: 1px solid #ddd;
padding-top: 10px;
margin-top: 20px;
width: 100%;
text-align: center;
justify-content: center;
}
.product-title, .product-price,.product-cat {
text-align: center;
}
.product-title {
padding-top: 10px;
font-size: 24px;
color: #454545;
}
.product-price {
font-size: 20px;
color: #0062cc;
}
.product-cat {
font-size: 16px;
color: #767676;
}
</style>
</head>
<body>
<div class="container">
<div class="filter row">
<form class="form" action="" method="GET">
<label>Filter Products</label>
<select name="category" class="form-control form-control-sm">
<option value="" ></option>
<!-- Category drop down filter will go here -->
</select>
<input type="submit" class="btn btn-primary btn-sm" />
</form>
</div> <!-- Filter -->
<div class="products row">
<!-- Products will go here -->
</div><!-- Products -->
<nav aria-label="Page navigation">
<ul class="pagination">
<!-- Pagination list items will go here -->
</ul>
</nav>
</div> <!-- Container -->
</body>
</html>
And here’s the array of products, each with an id, title, sku, price, category and image.
<?php
$products = array(
array(
'id' => '1',
'title' => 'Product 1',
'sku' => 'code-2',
'price' => '95.00',
'category' => 'Shirts',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '2',
'title' => 'Product 2',
'sku' => 'code-2',
'price' => '145.00',
'category' => 'Pants',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '3',
'title' => 'Product 3',
'sku' => 'code-3',
'price' => '895.00',
'category' => 'Shirts',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '4',
'title' => 'Product 4',
'sku' => 'code-4',
'price' => '295.00',
'category' => 'Pants',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '5',
'title' => 'Product 5',
'sku' => 'code-5',
'price' => '215.00',
'category' => 'Caps',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '6',
'title' => 'Product 6',
'sku' => 'code-6',
'price' => '365.00',
'category' => 'Shirts',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '7',
'title' => 'Product 7',
'sku' => 'code-7',
'price' => '95.00',
'category' => 'Caps',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '8',
'title' => 'Product 8',
'sku' => 'code-8',
'price' => '495.00',
'category' => 'Caps',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '9',
'title' => 'Product 9',
'sku' => 'code-9',
'price' => '95.00',
'category' => 'Caps',
'image' => 'https://via.placeholder.com/500x300.png'
)
);
I’ve only included nine products to demonstrate the concept of pagination.
Define the Variables
To easily identify and call the data, I’ve defined some variables which are as follows:
- $price_prefix : This is optional and doesn’t influence the outcome of the pagination or filter. It just adds a currency prefix to the price.
- $products : this is our array of products.
- $filtered_products : When the category filter is active, we’ll store products that match the query into this array.
- $categories : this will store our categories in an associative array with matching key/value pairs. We’ll loop through this array for the category filter options.
- $total_products : If there’s a category filter active, $total_products will count how many products are available in that category. If no filter is active, it will just count how many products are available. When defining this variable, it’s important that it come after the loop that pushes the categories into the $categories array. If it runs before the loop runs, it will be empty.
- $current_page : If the pagination query is active, this will store which page we’re currently on.
- $limit : This variable sets how many products to show per page. I’ve hard coded this here as 2, but you could make this dynamic by using another drop down or even pull it from user preferences set in your database.
- $offset : Offset calculates which post to start from using the $current_page and $limit values as reference. To prevent errors, if we’re on page 1, it will be explicitly set to 0.
- $total_pages : Calculates how many pages we have, using PHP’s ceil() function to round up to the nearest whole number.
- $filtered_category_query : Checks if there’s a filter active. If there is, it will save the query.
- $first_product_displayed : This will be the number of the first product in the range we’re showing when we’re paginate. It’s the next product after the others have been offset. We’ll use this to show the user which range of products they’re viewing.
- $last_product_displayed : Similar to $first_product_displayed, except this is the last product in the range, albeit a little more tricky. If the total products is 100 but our range ends at 91 it would be silly to print “Showing Products 80 to 100”. To prevent that we need to check which value is smaller and print that instead.
- $range : This variable will use the values of $first_product_displayed, $last_product_displayed and $total_products to let the user know where they are in the range of products.
We’re good to go. There are also some loop-specific variables, but those will have to be defined in the appropriate loops.
Creating the Category Filter
Creating the filter is as simple as creating a form. We can use asort() to sort the $categories array in alphabetical order by its values then use a foreach loop to loop through the sorted $categories array and create an HTML <option> for each category. To keep the selection when the filter is active, I’ve defined a $select variable which will add a “selected” HTML attribute if the option value matches the $_GET parameter in the query. If it doesn’t match, $select will be empty.
We’ll also use the $range variable we define earlier to get the range of products we’re viewing and print out “Showing $range in $_GET[‘category’]” where $_GET[‘category’] is the currently queried category.
<form class="form" action="" method="GET">
<label>Filter Products</label>
<select name="category" class="form-control form-control-sm">
<option value="" ></option>
<?php
//Sort categories alphabetically by value
asort($categories);
// List categories available to filter by
foreach ($categories as $category) {
// check if we are in a filter already ('name' from <select>) to make the filter the selected option
if ( isset($_GET['category']) && $_GET['category'] == $category ) {
$selected = ' selected="selected" '; // or just selected will do as well
}
else {
$selected = '';
}
// Add the category option in the <select>
echo '<option value="' . $category . '" ' . $selected . '>' . $category . '</option>';
} // End categories for each
?>
</select>
<input type="submit" class="btn btn-primary btn-sm" />
<span>Showing <?php echo $range;
if (isset($_GET['category']) && !empty($_GET['category'])){
echo ' in ' . $_GET['category'];
} ?>
</span>
</form>
Display the Products
In the code below, array_slice() will do the magic of displaying the correct range of products based on our pagination. This built-in PHP function takes three arguments; our array we want to slice, an offset value and a limit value. Well good for us, we’ve already defined all of those so we just need to insert the array_slice() function into our conditional statement. If there is an active filter, we should slice the $filtered_products array, but if there isn’t a filter set, we’ll slice our original $products array. Notice, I’m redefining the original $products variable here depending on which sliced array we’re getting our data from. It will just make it easier to use one variable when we actually call the data in the foreach loop.
<div class="products row">
<?php
// Redefine $products array if there are filters set
if( isset($_GET['category']) && !empty($_GET['category']) ) {
// Array Slice allows us to offset and limit array output
$products = array_slice($filtered_products, $offset, $limit);
}
// or leave as is
else {
$products = array_slice($products, $offset, $limit);
}
// Loop through $products array
foreach ($products as $product) { ?>
<div class="col-md-6 product">
<div class="column-inner">
<img src="<?php echo $product['image']; ?>" class="img-fluid" />
<h2 class="product-title"><?php echo $product['title']; ?></h2>
<h3 class="product-price"><?php echo $price_prefix . $product['price']; ?></h3>
<p class="product-cat">Category: <?php echo $product['category']; ?></p>
</div>
</div>
<?php } // End Products foreach ?>
</div><!-- Products -->
Creating Pagination With Active Filters in Place and Limiting the Amount of Pages
Now, one issue with pagination is that it can get out of hand, and by that, I mean if you have too many pages it can go against the reason you created pagination in the first place. Ideally, we want to avoid an unattractively lengthy list of pages. So we’ll make sure to only display a certain amount of pages on either side of the current page.
Before we add the HTML for the pagination, we should first check that we have more than page to avoid rendering useless HTML.
<?php
if ($total_pages > 1) {
// Pagination goes here
}
?>
I’d like for the pagination to have links to the beginning and end of all the pages. However, if we’re on the first page, it’s pointless having the link to the beginning, and likewise, the last page shouldn’t have a link to the end.
Pagination Link Back To The Start of The Array
<?php
if ($current_page > 1 ) { ?>
<li class="page-item">
<a class="page-link" href="<?php echo '?page=1' . $filtered_category_query; ?>" >First</a>
</li>
<?php }
Using our $current_page variable we set earlier, we check if the page we’re currently on is more than 1. If it is we’ll add an HTML list item with a link back to the beginning. The anchor’s href value will add a $_GET parameter to the current URL of the page which includes two queries; page 1 and the filtered category query if one is active. Remember, we defined the $filtered_category_query variable earlier.
Pagination Link To The End of The Array
<?php }
if ($current_page < $total_pages) { ?>
<li class="page-item">
<a class="page-link" href="<?php echo '?page=' . $total_pages . $filtered_category_query; ?> ">Last</a>
</li>
<?php }
This code checks if the current page we’re on is less than the total pages. If that is the case we can show a list item for a link to the last page.
Just like the first list item, our anchor href will send some $_GET parameter queries when it’s clicked. This time we can’t hard code the last page because we don’t know what that will be at any given time. Instead, the page number will be equal to the total amount of pages. The filter query remains the same.
So far this is what we have:
<?php
if ($current_page > 1 ) { ?>
<li class="page-item">
<a class="page-link" href="<?php echo '?page=1' . $filtered_category_query; ?>" >First</a>
</li>
<!-- The rest of our page list items will go here
<li>1</li>
<li>2</li>
etc.....
-->
<?php }
if ($current_page < $total_pages) { ?>
<li class="page-item">
<a class="page-link" href="<?php echo '?page=' . $total_pages . $filtered_category_query; ?> ">Last</a>
</li>
<?php }
Adding Pagination With a PHP For Loop
For the middle pages, we need to use a for loop to cycle through the page numbers and create an HTML list item for each one. However, depending on the amount of pages, we only want to show a few that are adjacent to the current page.
Let’s have a look at the code below and figure out what’s going on.
<?php
// Loop through page numbers
for ($page_in_loop = 1; $page_in_loop <= $total_pages; $page_in_loop++) {
if ($total_pages > 3) {
// 5 pages to, 5 page to the left, 5 pages to the right
if ( ($page_in_loop >= $current_page - 5 && $page_in_loop <= $current_page ) || ( $page_in_loop <= $current_page + 5 && $page_in_loop >= $current_page) ) { ?>
<li class="page-item <?php echo $page_in_loop == $current_page ? 'active disabled' : '' ; ?>">
<a class="page-link" href="<?php
// The URL
echo '?page=' . $page_in_loop . $filtered_category_query;
?> " ><?php
// The Page Number (HTML)
echo $page_in_loop;
?>
</a>
</li>
<?php }
}
// if the total pages doesn't look ugly, we can display all of them
else { ?>
<li class="page-item <?php echo $page_in_loop == $current_page ? 'active disabled' : '' ; ?>">
<a class="page-link" href="<?php echo '?page=' . $page_in_loop . $filtered_category_query; ?> " ><?php echo $page_in_loop; ?></a>
</li>
<?php } // end for loop
The for loop will set $page_in_loop as the expression starting at 1 ( $page_in_loop = 1 ). For every loop we’ll add 1 to $page_in_loop ( $page_in_loop++ ) but we’ll only loop as long as $page_in_loop is less than or equal to the total amount of pages ( $page_in_loop <= $total_pages ).
While we’re in the for loop we need to check if $total_pages is more than our acceptable limit for display purposes. If it is more and the pagination will be all screwy, we’ll only display the page numbers that meet certain criteria. Now, I know the if statement might look complicated but I’ll break it down.
Side note: To prevent too many page numbers from being rendered in the view, I’m limiting them to 5 pages on either side of the current page. How many you want is up to you.
Every time the loop runs, the first set of parentheses in the if statement checks if the page in the loop ( $page_in_loop ) is more than or equal to the current page minus 5 and is less than or equal to the current page. For example, if we’re on page 15 then 15 – 5 = 10. The only valid values will 10, 11, 12, 13, 14 and 15 itself. If the loop is on page 2 it will be false because it doesn’t meet the criteria.
The second set of parentheses is similar, except this time, it checks for page numbers that are less than our current page plus 5 and that those page numbers are more than or equal to our current page.
If either of the statements is true, the page in the loop will be echoed along will its li and anchor because I’ve use the OR operator between them ( || ).
The list item in the for loop also has a ternary operator that checks if the value of the current looped item is the same as our current page.
<?php echo $page_in_loop == $current_page ? 'active disabled' : '' ; ?>
This allows us to add some functionality and styling to our currently active page. I’ve opted to disable clicks on the current page but this is optional.
Here’s the whole document with all of the code.
<?php
// Optional, Define the price prefix, can be pulled from database or hardcoded
$price_prefix = 'R';
$products = array(
array(
'id' => '1',
'title' => 'Product 1',
'sku' => 'code-2',
'price' => '95.00',
'category' => 'Shirts',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '2',
'title' => 'Product 2',
'sku' => 'code-2',
'price' => '145.00',
'category' => 'Pants',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '3',
'title' => 'Product 3',
'sku' => 'code-3',
'price' => '895.00',
'category' => 'Shirts',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '4',
'title' => 'Product 4',
'sku' => 'code-4',
'price' => '295.00',
'category' => 'Pants',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '5',
'title' => 'Product 5',
'sku' => 'code-5',
'price' => '215.00',
'category' => 'Caps',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '6',
'title' => 'Product 6',
'sku' => 'code-6',
'price' => '365.00',
'category' => 'Shirts',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '7',
'title' => 'Product 7',
'sku' => 'code-7',
'price' => '95.00',
'category' => 'Caps',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '8',
'title' => 'Product 8',
'sku' => 'code-8',
'price' => '495.00',
'category' => 'Caps',
'image' => 'https://via.placeholder.com/500x300.png'
),
array(
'id' => '9',
'title' => 'Product 9',
'sku' => 'code-9',
'price' => '95.00',
'category' => 'Caps',
'image' => 'https://via.placeholder.com/500x300.png'
),
);
// Declare $filtered_products array to store products in the filter $_GET params
$filtered_products = array();
// Declare $categories array to store categories
$categories = array();
// Loop through products and store categories into categories array. Also store filtered products into an array
foreach ($products as $product) {
// Make sure item has a category key and a value, assigned as key and value to prevent duplicates
if ($product['category'] && !empty($product['category'])) {
$categories[$product['category']] = $product['category'];
}
//Store filtered products if set
if (isset($_GET['category']) && !empty($_GET['category']) && $product['category'] == $_GET['category']) {
$filtered_products[] = $product;
}
}
// Get the total products based on filter must come AFTER the loop above
$total_products = isset($_GET['category']) && !empty($_GET['category']) ? count($filtered_products) : count($products);
// Declare a variable for our current page. If no page is set, the default is page 1
$current_page = isset($_GET['page']) ? $_GET['page'] : 1;
// Declare $limit value this can either be another filter option, a value pulled from the database (like user preferences) or a fixed value
$limit = 2;
// Declare an offset based on our current page (if we're not on page 1).
if (!empty($current_page) && $current_page > 1) {
$offset = ($current_page * $limit) - $limit;
/*
for example, if we are on page 3 and we're only showing 2 items per page, we've already seen four items, two on page 1 and two on page 2. So our next number should be 5.
We need to offset by 4.
(3 * 2) - 2
3 * 2 = 6, 6 - 2 = 4 (so we offset the value by 4 )
*/
} else {
$offset = 0;
}
// Get the total pages rounded up the nearest whole number
$total_pages = ceil($total_products / $limit);
// Declare active category filter to use when we paginate so we don't lose the filter
$filtered_category_query = isset($_GET['category']) ? '&category=' . $_GET['category'] : '';
// When we filter, we want to know the range of products we're viewing
$first_product_displayed = $offset + 1;
//example : if we're on page 3, our offset is 4 ( limit(page 1) + limit(page2) ) so 4 + 1 = 5 (first product in view is 5)
// if the total products is more than the current offset x 2 + 2 then our last product is the offset + 2 or else it should be the total
$last_product_displayed = $total_products >= ($offset * $limit) + $limit ? $offset + $limit : $total_products;
// example 1 : if we're on page 3, our offset is 4 ( limit(page 1) + limit(page2) ) so 4 x 2 = 8, + 2 = 10 (last product in view is 10)
// example 2 : if we're on page 2, our offset is 2 ( limit(page 2) ) so 2 x 2 = 4, + 2 = 6 (last product in view is 6)
// Display the current range in view
if ($first_product_displayed === $last_product_displayed) {
$range = 'the Last of ' . $total_products . ' Products';
} else {
$range = $first_product_displayed . ' - ' . $last_product_displayed . ' of ' . $total_products . ' Products';
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Pagination Demo</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<style>
body {
background-color: #eee;
min-height: 100vh;
display: flex;
align-items: center;
}
.container {
background-color: #fff;
border: 1px solid #ddd;
border-radius: 10px;
max-width: 800px;
padding: 20px;
}
.filter {
background-color: #eee;
padding: 10px 20px;
margin: 0 0 10px;
text-align: center;
border-radius: 5px;
vertical-align: middle;
}
form {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
label,
input,
select {
display: inline-block !important;
margin-right: 10px;
width: auto !important;
margin-bottom: 0;
}
.product {
padding: 20px;
}
.column-inner {
border: 1px solid #ddd;
}
.img-fluid {
width: 100%;
height: auto;
}
.pagination {
border-top: 1px solid #ddd;
padding-top: 10px;
margin-top: 20px;
width: 100%;
text-align: center;
justify-content: center;
}
.product-title,
.product-price,
.product-cat {
text-align: center;
}
.product-title {
padding-top: 10px;
font-size: 24px;
color: #454545;
}
.product-price {
font-size: 20px;
color: #0062cc;
}
.product-cat {
font-size: 16px;
color: #767676;
}
</style>
</head>
<body>
<div class="container">
<div class="filter row">
<form class="form" action="" method="GET">
<label>Filter Products</label>
<select name="category" class="form-control form-control-sm">
<option value=""></option>
<?php
//Sort categories alphabetically by value
asort($categories);
// List categories available to filter by
foreach ($categories as $category) {
// check if we are in a filter already ('name' from <select>) to make the filter the selected option
if (isset($_GET['category']) && $_GET['category'] == $category) {
$selected = ' selected="selected" '; // or just selected will do as well
} else {
$selected = '';
}
// Add the category option in the <select>
echo '<option value="' . $category . '" ' . $selected . '>' . $category . '</option>';
} // End categories for each
?>
</select>
<input type="submit" class="btn btn-primary btn-sm" />
<span>Showing <?php
echo $range;
if (isset($_GET['category']) && !empty($_GET['category'])) {
echo ' in ' . $_GET['category'];
}
?>
</span>
</form>
</div> <!-- Filter -->
<div class="products row">
<?php
// Redefine $products array if there are filters set
if (isset($_GET['category']) && !empty($_GET['category'])) {
// Array Slice allows us to offset and limit array output
$products = array_slice($filtered_products, $offset, $limit);
}
// or leave as is
else {
$products = array_slice($products, $offset, $limit);
}
// Loop through $products array
foreach ($products as $product) { ?>
<div class="col-md-6 product">
<div class="column-inner">
<img src="<?php echo $product['image']; ?>" class="img-fluid" />
<h2 class="product-title"><?php echo $product['title']; ?></h2>
<h3 class="product-price"><?php echo $price_prefix . $product['price']; ?></h3>
<p class="product-cat">Category: <?php echo $product['category']; ?></p>
</div>
</div>
<?php } // End Products foreach
?>
</div><!-- Products -->
<?php
if ($total_pages > 1) { ?>
<nav aria-label="Page navigation">
<ul class="pagination">
<?php
// When we're not on the first page, we'll have a paginator back to the beginning
if ($current_page > 1) { ?>
<li class="page-item"><a class="page-link" href="<?php echo '?page=1' . $filtered_category_query; ?>">First</a></li>
<?php
}
// Loop through page numbers
for ($page_in_loop = 1; $page_in_loop <= $total_pages; $page_in_loop++) {
// if the total pages is more than 2, we can limit the pagination. We'll also give the current page some classes to disable and style it in css
// if the page in the loop is more between
if ($total_pages > 3) {
if (($page_in_loop >= $current_page - 5 && $page_in_loop <= $current_page) || ($page_in_loop <= $current_page + 5 && $page_in_loop >= $current_page)) { ?>
<li class="page-item <?php echo $page_in_loop == $current_page ? 'active disabled' : ''; ?>">
<a class="page-link" href="<?php echo '?page=' . $page_in_loop . $filtered_category_query; ?> "><?php echo $page_in_loop; ?></a>
</li>
<?php }
}
// if the total pages doesn't look ugly, we can display all of them
else { ?>
<li class="page-item <?php echo $page_in_loop == $current_page ? 'active disabled' : ''; ?>">
<a class="page-link" href="<?php echo '?page=' . $page_in_loop . $filtered_category_query; ?> "><?php echo $page_in_loop; ?></a>
</li>
<?php } // End if
?>
<?php } // end for loop
// and the last page
if ($current_page < $total_pages) { ?>
<li class="page-item"><a class="page-link" href="<?php echo '?page=' . $total_pages . $filtered_category_query; ?>">Last</a></li>
<?php } ?>
</ul>
</nav>
<?php } // End if total pages more than 1
?>
</div> <!-- Container -->
</body>
</html>
Taking Pagination Further
This is a simple pagination but it covers the most important parts of how it works. To go even further, you could include “previous” and “next” links or even render a few adjacent pages along with the last few pages. You could also write a reusable function that takes parameters to make your pagination template reusable across a variety of data sources. No matter what you choose to do, the principles of the for loop, limits and offsets will be worth knowing.
Let me know how you come along in the comments below. If something is not clear or needs further explaining, you’re welcome to let me know as well.
Where To Use This Code
Copy and paste the code into the page that displays your posts, products or data. Ideally, you’d want to figure out how to make this object-oriented instead of procedural, but I’ll leave that for another post. 😉
Is this still valid in 2024? Please let me know in the comments below.