Previous or Next? Paginating Records with PHP – Part 2

In the first article in this series, we learned how to do simple pagination of records taken from a text file. In this article, we will look at pagination for a larger group of records, using an Objected Oriented rather than a procedural approach to creating the application.

Introduction

Welcome to Part II of the article “Previous or Next? Paginating Records with PHP.” I really hope that you enjoyed reading the first article as much as I did writing it. For those faithful readers who spent some time reading over the first part, I have good news. In this second part, we’re going to head toward the OOP approach, defining a class for paginating records. In this way we will discover a better solution, useful for applying to larger Web projects.

However, for those catching up right now, don’t be disappointed! Before we go more deeply into any attempt to define a PHP class, we’ll review the concepts previously explained, looking briefly at the function originally created to paginate records, which used a procedural orientation.

Having examined the preliminaries, it’s time to go more deeply into the area of record paging in PHP. Are you ready to move on? Fine, because it’s going to be really fun. Let’s get started.

{mospagebreak title=Taking the procedural approach: the friendly “paginateRecords()” function}

In the first article, we defined a simple text file for storing information, where each line is considered a single record, and each column is a different field. In programming environments, this is known as a flat database. We went as far as setting up a rather basic function to paginate data retrieved from a text file. The function’s source code was originally defined as shown below:

function paginateRecords($dataFile,$page,$numRecs=5){

$output=”;

// validate data file

(file_exists($dataFile))?$data=array_reverse(file
($dataFile)):die(‘Data file not valid.’);

// validate number of records per page

(is_int($numRecs)&&$numRecs>0)?$numRecs=$numRecs:die
(‘Invalid number of records ‘.$numRecs);

// calculate total of records

$numPages=ceil(count($data)/$numRecs);

// validate page pointer

if(!preg_match(“/^d{1,2}$/”,$page)
||$page<1||$page>$numPages){

$page=1;

}

// retrieve records from flat file

$data=array_slice($data,($page-1)*$numRecs,$numRecs);

// append records to output

foreach($data as $row){

$columns=explode(‘|’,$row);

foreach($columns as $column){

$output.=$column.’&nbsp;';

}

$output.='<br />';

}

// create previous link

if($page>1){

$output.='<a href=”‘.$_SERVER['PHP_SELF'].’?page=’.
($page-1).'”>&lt;&lt;Previous</a>&nbsp;';

}

// create intermediate links

for($i=1;$i<=$numPages;$i++){

($i!=$page)?$output.='<a href=”‘.$_SERVER
['PHP_SELF'].’?
page=’.$i.'”>’.$i.'</a>&nbsp;':$output.=$i.’&nbsp;';

}

// create next link

if($page<$numPages){

$output.=’&nbsp;<a href=”‘.$_SERVER['PHP_SELF'].’?
page=’.($page+1).'”>Next&gt;&gt;</a> ‘;

}

// return final output

return $output;

}

Although the function is basic, it’s really flexible and portable, because it allows us to specify a data file to read from, and the number of records (in this case we’re referencing file lines and not true database records) to be displayed at a time. Also, it’s powerful enough to generate the classic <previous> – 1, 2, 3 …<next> navigational links. It’s an elemental format, but with minor modifications, it might be changed to present a more polished look, just by tweaking the original code.

One possible implementation for the function may be defined in the following example:

<?php

require_once(‘pager.php’);

$page=$_GET['page'];

echo paginateRecords(‘data1.dat’,$page);

?>

In the above example, we’ve specified a data file “data1.dat” from which to read, and a default value of five records to be displayed at a time. Because of its flexibility, another case may be coded as follows:

<?php

require_once(‘pager.php’);

$page=$_GET['page'];

echo paginateRecords(‘data2.dat’,$page,10);

?>

The previous case will display data from a “data2.dat” file and show ten records per page, reordering the paging links according to the new parameters. Definitely, we have to agree that our function is very versatile and extendable. Just play around with the code to make it fit your needs.

While the function is handy for small projects, it lacks some of the complexities found in more sophisticated applications. Therefore, as I previously stated, we’re going to develop a PHP class that will paginate records from a data file. It will keep the functionality of the initial procedural method, but offer the wide advantages inherent to Object Oriented Programming. So, let’s start defining the PHP class.

{mospagebreak title=Taking the OOP approach: defining the “Pager” PHP class}

There are still many heated discussions about the pros and cons of Procedural and Object Oriented Programming in PHP. Regardless of the different opinions voiced about the topics, generally developers seem to agree on one point: OOP is best suited for medium or larger projects, while a procedural approach works better for small applications. Nothing stops you from applying the method that best fits your particular needs. However, with an extremely versatile language such as PHP, as Web applications evolve from simple scripts to full-fledged programming structures, the OOP approach is definitely the right way to go.

Keeping in mind the concepts explained above, we’ll tackle the record paging issue, creating a PHP class to handle records in an efficient manner. Our “Pager” class will present only two methods: the corresponding class constructor and the “displayRecords()” method, which, not surprisingly, will display the records and create the paging links. Let’s start by defining the blueprints for the class:

class Pager {

var $output;

var $totalRecs;

var $numRecs;

function Pager($dataFile,$numRecs){

// code for parameters setup

}

function displayRecords($page){

// code to display records and paging links

}

}

As you can see, the structure of the class is very simple, and displays three properties: $output, $totalRecs and $numRecs, respectively. The first property will store progressively the output generated, including record values and paging links. The second one represents the total  records retrieved from the data file, and finally the third argument shows the number of records per page being displayed. Indeed, the class structure is pretty self-explanatory.

Once we’ve defined the skeleton of the class, it’s time to add some functionality to the constructor. Let’s take a look at its complete definition:

function Pager($dataFile,$numRecs=5){

// validate data file

(file_exists($dataFile)&&count(file($dataFile))>0)?
$this->totalRecs=array_reverse(file($dataFile)):die
(‘Data file not valid.’);

// validate number of records per page

(is_int($numRecs)&&$numRecs>0)?$this-
>numRecs=$numRecs:die(‘Invalid number of records
‘.$numRecs);

}

As you can deduce from the code for the constructor, it accepts only two parameters: the data file and the number of records to be shown per page. In this particular case I’ve decided to give a default value of five records, but as usual, could be changed by passing another value to the method.

Next, the constructor checks to see whether the data file is valid and  not empty. If the file is considered valid, then the constructor gets the complete records from the data file, assigning them as a class property. Otherwise, the class is stopped with a die() statement.

What is the constructor’s next task? It validates the number of records per page, by checking to see whether the value passed is a positive integer. If the condition is meet, the method assigns the value as another class property. If the value is not valid, the class is simply killed using a regular die() statement.

Notice the simplistic nature of the constructor, which performs the validation of parameters and sets up the corresponding properties. Once the method has completed its tasks, we need to define the other method of the class: “displayRecords()”. Let’s take a deep breath and jump straight into its code definition.

{mospagebreak title=Defining the “displayRecords()” method}

The “displayRecords()” method is the workhorse of the class, and accepts one parameter, $page, which determines which page of the retrieved records we want to see. So, let’s start by defining its signature and then explain how it works:

function displayRecords($page){

// calculate number of pages

$numPages=ceil(count($this->totalRecs)/$this->numRecs);

// validate page pointer

if(!preg_match(“/^d{1,2}$/”,$page)
||$page<1||$page>$numPages){

$page=1;

}

// retrieve corresponding records from flat file

$records=array_slice($this->totalRecs,($page-1)*$this-
>numRecs,$this->numRecs);

foreach($records as $row){

$columns=explode(‘|’,$row);

foreach($columns as $column){

$this->output.=$column.’&nbsp;';

}

$this->output.='<br />';

}

// create previous link

if($page>1){

$this->output.='<a href=”‘.$_SERVER['PHP_SELF'].’?
page=’.($page-1).'”>&lt;&lt;Previous</a>&nbsp;';

}

// create intermediate links

for($i=1;$i<=$numPages;$i++){

($i!=$page)?$this->output.='<a href=”‘.$_SERVER
['PHP_SELF'].’?page=’.$i.'”>’.$i.'</a>&nbsp;':$this-
>output.=$i.’&nbsp;';

}

// create next link

if($page<$numPages){

$this->output.=’&nbsp;<a href=”‘.$_SERVER
['PHP_SELF'].’?page=’.($page+1).'”>Next&gt;&gt;</a> ‘;

}

// return final output

return $this->output;

}

As I said previously, the method accepts the $page parameter, which is the page pointer to determine which page we wish to view. However, before any validation process takes place, it calculates the number of pages needed to display the complete number of records. This value is obtained with the following lines:

// calculate number of pages

$numPages=ceil(count($this->totalRecs)/$this->numRecs);

Here we’re dividing the total number of records read from the file by the number of records per page. Using ceil(), we round a decimal number up; in this way 3.25 becomes 4. Doing so, we get an integer value as the number of pages to be used.

Having obtained the number of pages, the method is now capable of validating the $page parameter. Maybe you’re asking… why should I check out the validity of this parameter? The answer is that we don’t want this value to be tampered with by external code. We’re using the same technique shown in the first article. It’s really funny to see how many websites fail to validate page pointers, and end up modifying paging links according to code injected through the querystring, even if they’re using a URL rewriter. We don’t want this to happen, so the method checks if the $page pointer is valid, like this:

if(!preg_match(“/^d{1,2}$/”,$page)
||$page<1||$page>$numPages){

$page=1;

}

Any attempt to tamper with the page pointer will result in a value of 1. Doing so, the method will display the first page. Simple and straightforward, right?

Are you getting tired of long explanations and want to see the class in action? So am I! But there are a few lines of code that still need to be explained. Just be a bit patient and keep reading.

{mospagebreak title=Source code ahead: completing the “displayRecords()” method}

Let’s jump directly into the code to complete the method definition. Once the page pointer has been properly validated, it’s time to fetch the exact number of records specified for being displayed on each page. Since we’ve already stored the total number of records as a class property $this->totalRecs, the process of extracting the corresponding records is a breeze. We just use the array_slice() PHP function to position the array internal pointer at the right place, and retrieve the correct number of records. This simple line extracts the records from the $this-> totalRecs array, as listed below:

$records=array_slice($this->totalRecs,($page-1)*$this-
>numRecs,$this->numRecs);

Now, if the page pointer $page is equal to 1, and assuming that the default number of records is 5, the $records variable will store the first five elements from the $this->totalRecs array. Similarly, if $page is equal to 2, the second set of five records will be stored. As you can see, the expression is simple but extremely powerful for extracting chunks of records, closely resembling the LIMIT SQL statement extensively used with MySQL.

At this point, the method only needs to loop over the extracted records and progressively generate the output to be displayed in the browser. Using a couple of “foreach” loops, the task is nicely performed:

foreach($records as $row){

$columns=explode(‘|’,$row);

foreach($columns as $column){

$this->output.=$column.’&nbsp;';

}

$this->output.='<br />';

}

By now, we’ve applied very basic formatting to the records. However, this might be changed with minor modifications. Before I hear someone complaining about this, in Part III of this article, we’ll cover in-depth record formatting to suit real-world needs. So, you don’t have any excuses to skip the next part of the series!

The rest of the code is nearly identical to the example shown in the first part. The methods take care of creating the paging links, first checking whether it’s possible to insert a <previous> link. If it’s possible, the corresponding link is generated. Then, the numbered links are created using a loop, and appending the $page variable to the querystring, providing the necessary navigational links.

Finally, the methods verifies whether it’s feasible to generate a <next> link, to completely display the paging links.

Here are the lines used to create the paging links:

// create previous link

if($page>1){

$this->output.='<a href=”‘.$_SERVER['PHP_SELF'].’?
page=’.($page-1).'”>&lt;&lt;Previous</a>&nbsp;';

}

// create numerated links

for($i=1;$i<=$numPages;$i++){

($i!=$page)?$this->output.='<a href=”‘.$_SERVER
['PHP_SELF'].’?page=’.$i.'”>’.$i.'</a>&nbsp;':$this-
>output.=$i.’&nbsp;';

}

// create next link

if($page<$numPages){

$this->output.=’&nbsp;<a href=”‘.$_SERVER
['PHP_SELF'].’?page=’.($page+1).'”>Next&gt;&gt;</a> ‘;

}

We’ve retrieved the records to be displayed and built the paging links with a single method. Definitely, the method shows its power and simplicity with some lines of code. Maybe we’re not ready to paginate thousands of records, but this attempt is fairly valid!

Well, our job is almost done by now. But, am I forgetting anything? Sure, we need to put the class into action and see its actual functionality. Therefore, let’s write a bit more of code to implement our “Pager” class.

{mospagebreak title=Pager class implementation}

With the class completely defined, it’s easy to implement a couple of examples to show how it can used. However, let’s first show the complete list for our Pager class. This is the full source code:

class Pager {

var $output=”;

var $totalRecs;

var $numRecs;

function Pager($dataFile,$numRecs=5){

// validate data file

(file_exists($dataFile)&&count(file($dataFile))>0)?
$this->totalRecs=array_reverse(file($dataFile)):die
(‘Data file not valid.’);

// validate number of records per page

(is_int($numRecs)&&$numRecs>0)?$this-
>numRecs=$numRecs:die(‘Invalid number of records
‘.$numRecs);

}

function displayRecords($page){

// calculate number of pages

$numPages=ceil(count($this->totalRecs)/$this->numRecs);

// validate page pointer

if(!preg_match(“/^d{1,2}$/”,$page)
||$page<1||$page>$numPages){

$page=1;

}

// retrieve corresponding records from flat file

$records=array_slice($this->totalRecs,($page-1)*$this-
>numRecs,$this->numRecs);

foreach($records as $row){

$columns=explode(‘|’,$row);

foreach($columns as $column){

$this->output.=$column.’&nbsp;';

}

$this->output.='<br />';

}

// create previous link

if($page>1){

$this->output.='<a href=”‘.$_SERVER['PHP_SELF'].’?
page=’.($page-1).'”>&lt;&lt;Previous</a>&nbsp;';

}

// create intermediate links

for($i=1;$i<=$numPages;$i++){

($i!=$page)?$this->output.='<a href=”‘.$_SERVER
['PHP_SELF'].’?page=’.$i.'”>’.$i.'</a>&nbsp;':$this-
>output.=$i.’&nbsp;';

}

// create next link

if($page<$numPages){

$this->output.=’&nbsp;<a href=”‘.$_SERVER
['PHP_SELF'].’?page=’.($page+1).'”>Next&gt;&gt;</a> ‘;

}

// return final output

return $this->output;

}

}

Having listed the complete class code, we can show our first example, which is presented in the following way:

require_once(‘pagerclass.php’);

$pager=&new Pager(‘data.dat’);

$page=$_GET['page'];

echo $pager->displayRecords($page);

That’s it. With four lines of code, we’re displaying records from the text file “data.dat” and displaying the corresponding paging links, using a default value of five records per page.

If that’s not good enough for you, here’s another example, showing records from a text file “data2.dat”, in sets of 10 records at a time:

require_once(‘pagerclass.php’);

$pager=&new Pager(‘data2.dat’,10);

$page=$_GET['page'];

echo $pager->displayRecords($page);

Our newly developed class is very flexible and portable, and easy to implement in different situations, simply by changing the parameters passed to it. As usual, feel free to play with the code and tweak it to fit your particular needs. Certainly, the possibilities are numerous.

Summary

In this second part, we’ve developed in a step-by-step process, a PHP class to paginate records in websites. Noticeably, we’ve branched out to an OOP approach to illustrate the great advantages that classes offer to PHP programmers. However, before you start clamoring for more complex paginating solutions, we’re not done yet.

In its current incarnation, the class lacks many desirable features that should be present in real-world applications. The first clear issue is that we’re working with text files as a data source. If we’re going to work with medium to large sites, we’ll quickly encounter a relational database system. The second problem is the basic mechanism presented to apply some kind of visual format to the records. Thus, all of these relevant topics should be properly addressed without modifying the inner structure of the class, while maintaining the basics of the original code.

In the third part of this series, we’ll apply some interesting improvements to the class, making it possible to work directly with MySQL, offering a flexible mechanism for defining the records’ visual presentation. What more can we ask for? In the meantime, have some fun playing with the class. See you soon!

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri