Home    posts    php pagination class

Posted on: February 14, 2018

Pagination class, introduction to OOP

I would like to begin with a statement that page navigation or pagination was planned to have its own post but since it was necessary for the gallery at the time, I have decided that there will still be a post dedicated to pagination. However, to avoid duplicate content, the plan is to rewrite it in object-oriented programming(OOP) style which brings a whole different paradigm and mindset to the table which again makes me wonder whether or not I want to have an opening post for OOP alone but the decision has been made that it will be done gradually anyway. Don't worry, we'll still be doing procedural code for a while, at least until we cover some of the most used stuff often needed to build websites or other minor web applications. It is important to say that re-using and compiling the code from existent scripts for further projects, especially when things will have to get done on a bigger scale, will lead us to OOP in the process anyway.

So what is object-oriented programming and how to approach it? This is the most relevant question you will have to ask yourself. But more importantly, no matter what you read and what anyone tells you, you will have to find out for yourself because until you get it on your own, words will have little meaning. The only thing that works or at least worked for me was actually coding, changing parts of code and refreshing the output until an over-head light bulb comes on. Sometimes reinventing the wheel is the best way to learn I suppose but now moving on and trying to put it into words, I would begin with the following.

Think of your code as being a blueprint for what you are trying to accomplish. There are no values put into it until you use this blueprint so everthing is just, in theoretical-practical analogy, in theory. Practice comes in later when an object is created. So a class is a theoretical part and a blueprint and this blueprint wants to know everthing that is possible to construct in practice but certain things are not dependant on each other as a whole.

For example, a blueprint for a house may contain schematics for a house itself, a balcony and a garage. Now, constructing a house does not depend on constructing a balcony or garage. These two can come later or not at all, same for reverse, you can build a balcony and a garage but not a house. It's possible, in theory, but not very practical. In OOP terms, all three are called methods which are basically functions that are grouped together in a class to make it practical. While they may not necessarily depend on each other, they might depend on the same properties used inside a blueprint. Properties are basically variables that need to be accessed by different methods inside a class. In the house analogy, it would be equivalent to materials, dimensions, quantities, basically fundamental blocks used in a blueprint to construct the whole. It is not necessary for a variable to become a property. For example there may be materials used for a garage that won't be used for balcony or house and that can stay just a variable. However, if this material is to be used elsewhere, then a property is required, unless the method itself returns a variable. Then you would just have to call the method inside other's method, just like calling a function whenever needed.

PART 1 - Pagination class

To put all the words into code, we will create a pagination class, a blueprint that you'll be able to use whenever in a need to paginate stuff. The pagination in the gallery was designed to paginate an array of elements without database so we'll recreate that. It will be done step by step that will hopefully help you understand what is going on. Before we start, we are going to make it a mission to encapsulate a concept of creating specific methods that will return only specific parts of what we want, such as a method to return only pages, a method for next button, a method for previous button and methods for first and last buttons most of which will be optional whenever an object is created. We'll start by creating a constructor, which is a special method of a class that can also accept arguments at the moment an object is created. These arguments will also be turned into properties because other methods will need them.


<php

class pagination {

	// Set visibility of properties
	private $value;
	private $perpage;
	private $range;
	private $total;
	private $offset;

	// create a constructor that accepts arguments, turn arguments to properties
	// Construct will expect array, int, int
	public function __construct($values, $perpage, $range) {

		$this->value = $values;
		$this->perpage = $perpage;
		$this->range = $range;
	}

You might be wondering what the visibility is all about? Well, whenever having properties inside a class, you can set whether the property can be called from outside the class when an object is created or the parent class is inherited. Don't worry, we'll partially explain this with an example when we create an object in PART 2. This could also be omitted in the first place and the code would still work but it is a good practice to stick to because sooner or later it will be the key to why a class or an object is misbehaving in terms of being able to access information that it shouldn't have access to. A thing to note here is that if visibility is not set, it is considered public as default.

Anyway, to continue, we are going to create a method for pages that will need all three properties from the constructor. It will be a public method, just like the constructor.


	public function pages() {

		// Count all elements from an array which is now a property
		// Get the pages with ceil function and save it to a property so it can be accessed elsewhere in the class but not outside of a class because it's private (see above)
		// Save offset to property, note that a method is being called for calculation, this method will be created later on.
		$count = count($this->value);
		$this->total = ceil($count / $this->perpage);
		$this->offset = ($this->currentpage() - 1) * $this->perpage;

		$pages = '';

		// Loop through the range of pages and append to $pages variable based on conditions.
		// This will get all the pages within a range being provided in a constructor when an object is created
		for($page = ($this->currentpage() - $this->range); $page < ((this->currentpage() + $this->range)+1); $page++) {

			if(($page <= $this->total) && ($page > 0)) {

				if($count <= $this->perpage) {$pages .= "";}

				elseif($page == $this->currentpage()) {$pages .= '<span class="pagina">'.$page.'</span>'."\n";}

				else {$pages .= '<a class="pagina" href="'.$_SERVER['PHP_SELF'].'?page='.$page.'">'.$page.'</a>'."\n";} 
			}
		}		

		return $pages;
	}

This is now a method that will return all the pages within the desired range for the object created. It will be an independant method despite being dependant on another method but that method, currentpage(), will be private anyway and will only be executed within other methods. It does, however, need a property defined in pages() method. These two methods basically need each other to function properly. Of course this dependancy can be broken by creating another method that would return $this->total property but that is a task for you to do after reading this post. Enough talk for now, let's create currentpage() method.


	private function currentpage() { 

		if(isset($_GET['page'])) {$currentpage = htmlentities($_GET['page']);} 

		else {$currentpage = 1;}

		if($currentpage < 1) {$currentpage = 1;}

		if($currentpage > $this->total) {$currentpage = $this->total;} 

		return $currentpage;
	}

So this is now a private function and cannot be called upon in child classes or objects. It will also get the current page from the url parameter if there is one provided, else it will know it's the first page. It cannot go beyond first page or the total pages. It's fixed between first and total pages. Next we want slice the values per pages and return the result.


	public function results() {

		$result = array_slice($this->value, $this->offset, $this->perpage);

		return $result;
	}

This method depends on the constuctor and the offset property to know how to slice the input. Note that offset property is independant because it only uses other properties, unlinke total that is bound to a local variable. It means that results() method works independant of any other methods. Anyway, it will, when called upon, return the sliced values per page. Also, this class is now ready for use, basically we can call results() and pages() methods which will display the results and all the pages within the range. It achieves the basic pagination effect but we still want to display next and previous button as well as first and last buttons. That's why we are going to create four more methods for each of the buttons.


	public function previouspage() {

		if($this->currentpage() > 1) {$prev = '<a class="pagina" href="'.$_SERVER['PHP_SELF'].'?page='.($this->currentpage()-1).'">< Prev </a>';}

		return $prev;
	}

	public function nextpage() {

		if($this->currentpage() < $this->total) {$next = '<a class="pagina" href="'.$_SERVER['PHP_SELF'].'?page='.($this->currentpage()+1).'"> Next ></a>';}

		return $next;
	}

	public function firstpage() {

		if($this->currentpage() != 1) {$first = '<a class="pagina" href="'.$_SERVER['PHP_SELF'].'?page=1"><< First</a>';}

		return $first;
	}

	public function lastpage() {

		if($this->currentpage() != $this->total) {$last = '<a class="pagina" href="'.$_SERVER['PHP_SELF'].'?page='.($this->total).'">Last >></a>';}

		return $last;
	}

// Closing the class
}
?>

All these methods depend on the currentpage() which depends on pages() to work properly. Why I'm saying this? Because now whenever an object is created, as you'll be able to see in the next part, the pages() method will have to be called upon before we can call any of these methods. I mean we could call them but they won't work properly, just like a garage and a balcony can be built but without a house being built first, it wouldn't be very practical. Again, it was designed this way on purpose to fit into the context being presented.

PART 2 - Creating instances(objects) of a pagination class

Now that the class is completed, we will create two instances of this class, also called objects. Each will be using results() and pages() methods but both won't be using next, previous, last and first page buttons. We will also touch visibility where you'll be now able to see how it behaves in objects. So to begin, we will paginate an array of names of European captials. For this demonstration the code will be included just below the class in the same file.


<php

$arrayofcities = array('Ljubljana', 'Paris', 'Rome', 'Madrid', 'Lisbon', 'London', 'Edinburgh', 'Dublin', 'Befast', 'Cardiff', 'Berlin', 'Amsterdam', 'Brussels', 'Luxemburg City' 'Oslo', 'Helsinki', 'Stockholm', 'Copenhagen', 'Reykjavik', 'Riga', 'Vilnus', 'Tallinn', 'Warsaw', 'Vienna', 'Prague', 'Bern', 'Vaduz', 'Bratislava', 'Kiev', 'Minsk', 'Moscow', 'Bucharest', 'Budapest', 'Sofia', 'Zagreb', 'Belgrade', 'Sarajevo', 'Skopje', 'Podgorica', 'Tirana', 'Pristina' 'Athens', 'Chisinau', 'Nicosia', 'Valleta', 'Torshavn', 'Andorra la Vella');

// Remember that the constructor expects (array, int, int)
// We want 10 capitals per page and the range of 2
// Create object and save it to variable
$capitals = new pagination($arrayofcities, 10, 2)

// Call the mandatory methods, save them to variables
$results = $capitals->results();
$pages = $capitals->pages();

// Now display the results and pages
foreach($results as $capital) {echo $capital.','."\n";}
echo '<br/><br/>';
echo $pages;

?>

It will output the following:

Just results and pages for the range of 2, which means 2 to the left and 2 to the right but because it is the first page it shows only 2 to the right. Let us now try to access the total property from the object.


echo 'Total pages: '.$capitals->total;

This will result with the following error:


Fatal error: Uncaught Error: Cannot access private property pagination::$total in /var/www/html/pagina_class.php:134 Stack trace: #0 {main} thrown in /var/www/html/pagina_class.php on line 134

However, if we were to change the property state of $total to public, it would output just fine.

Same thing can be said for the visibility of methods but right now we are going to paginate images from the gallery using the same blueprint but with different settings and using all the methods


<php

$path = "uploads/";
$images = glob($path."*.{jpg,jpeg,gif,png}", GLOB_BRACE);

$gallery = new pagination($images, 12, 3);
$results = $gallery->results();
$pages = $gallery->pages();
$next = $gallery->nextpage();
$prev = $gallery->previouspage();

$perrow = 4;
$count = 0;
foreach($results as $image) {

	$count++;

	if($count % $perrow === 0) {echo '<img src="'.$image.'" width="102" height="85"/><br/>'."\n";}

	else {echo '<img src="'.$image.'" width="102" height="85"/>'."\n";}
}
echo '<br/>';
echo $prev.$pages.$next;

?>

Note that break is used for each 4th image so we can have that gallery effect. It can also be done with a container that would automatically break new images when reaching its maximum width.

As you can see now we have also called for previouspage() and nextpage() methods but still omitted the first and last page. Only to show you that you can use the blueprint differently every time you create an instance from it. So to complete this tutorial, we will now add those pages.


<php

$path = "uploads/";
$images = glob($path."*.{jpg,jpeg,gif,png}", GLOB_BRACE);

$gallery = new pagination($images, 12, 3);
$results = $gallery->results();
$pages = $gallery->pages();
$next = $gallery->nextpage();
$prev = $gallery->previouspage();
$first = $gallery->firstpage();
$last = $gallery->lastpage();

$perrow = 4;
$count = 0;
foreach($results as $image) {

	$count++;

	if($count % $perrow === 0) {echo '<img src="'.$image.'" width="102" height="85"/><br/>'."\n";}

	else {echo '<img src="'.$image.'" width="102" height="85"/>'."\n";}
}
echo '<br/>';
echo $first.$prev.$pages.$next.$last;

?>

With the last example we have concluded this tutorial and introduction to object-oriented programming. There is still more to come, always is, but hopefully you now understand the very basics of OOP and can see why it's convinenient to use it and re-use it but the best thing is that, whenever in need of a change, you can just change the class and all of the objects will change accordingly. On the long run, we could say that it's more efficient than procedural code.

If you have any questions or thoughts regarding the topic, please leave a comment below.


Comments:

Be the first to comment.

Add a comment:










I have read and agree with the Privacy terms and conditions.