Lesson 5 - Making an object-oriented component in PHP - Image Gallery
In the previous lesson, Encapsulation in PHP, we learned about encapsulation. As promised, today we will create something you may actually find useful for your websites, an image gallery.
Motivation
Image galleries are a common thing to include in a website. Whether it features vacation photos, application screenshots, or anything else, they are a crucial design tool... Coding an HTML table and adding images manually sounds like a drag, especially if we had lots of them (something over 50). What we want is to render images from a folder automatically into a clear table. Ideally, we would want this process to be done by an object so that the code would be reusable.
Creating project structure
Create a new folder and name it "gallery". Inside, create an index.php file and a "classes" folder, in which you will need to create a Gallery.php file. Last of all, create another folder, name it "photos", and drop several images into it. I used the default "Image samples" that the Windows OS provides. We also need to create a thumbnail for each image (using GIMP or any other image editor). PHP could do it for us while it uploads the images, but we could work with that sort of functionality some other time. My preview thumbnails will be 160px wide and have the suffix: "_thumb". Your folder should look something like this:
Gallery
We have the data and the structure ready, now let's get to programming. Obviously, we'll start by defining the Gallery class, whose instances will represent an image gallery. Let's think about the properties it will need to have.
Properties
We want to be able to set the number of thumbnails per row, in other words, the number of columns the image table will have. Also, we will need to store the image folder's path (in programming, we mostly use the term directory when referring to a folder path). We'll keep all of the properties private, and set them through the constructor.
The class' code including the properties and the constructor is as follows:
class Gallery { private $directory; private $columns; public function __construct($directory, $columns) { $this->directory = $directory; $this->columns = $columns; } }
Methods
Now let's move on to the methods! We technically already have one down, the constructor, let's add 2 more.
Load
The load() method will search for a folder and store the thumbnails into memory. PHP provides a Directory class that searches for folders. Remember when I said that the latest versions of PHP provided objects for us to use? Directory is one of them. Unfortunately, we can only create an instance of the Directory class using the dir() function because, well, nothing is perfect. We'll be using 2 methods in particular on the instance:
- read() - Loads the contents of next file or sub-directory in the current directory and returns it as string
- close() - Ends directory reading
Our load() method now looks like this:
public function load() { $directory = dir($this->directory); while ($item = $directory->read()) { } $directory->close(); }
We create an instance of the Directory class. Then, with the help of the read() method, we get the sub-directories and files one by one. The method returns false once it reaches "the end" of a directory (once it reads the very last file or directory). Thanks to this behavior, we are able to read file names in a while loop, which ends when it reads the very last one. The filename will be stored in the $item variable from inside the loop. Assigning variables in the loop condition may be unheard of to you, but it's a very common thing to do.
We load each file twice (thumbnail + original). The function also returns two more spare directories named "." and "..", which stand for current and parent directory. We only want the thumbnails to be displayed, so we will have to add a condition into the loop. The condition will say that we're interested in files whose name ends in "_thumbnail", and store them into an array. The array will need to be accessible for the render() method, so we'll add it to the class as a private property.
private $files = array();
Now, let's move to the while loop body and add the filenames into our array with the aforementioned condition set up. To do so we will use the strpos() function, which returns a substring position. If "_thumb" is part of the filename, the item will be added into the array. Otherwise, the function will return false.
if (strpos($item, '_thumb.')) { $this->files[] = $item; }
The strpos() function returns 0 when a string starts out with the substring (0 as in the very first position). In our case, a value of 0 will be evaluated as false and will not satisfy the condition. That's right! If a file is named "_thumb.jpg", then it is likely bound to an unnamed image, which means it's invalid. If we wanted to check for a substring occurrence in the entire string, we would distinguish 0 and false values using the !== operator:
if (strpos($item, '_thumb.') !== false) { $this->files[] = $item; }
Here, the condition won't apply only when the function returns false and would apply when it returns 0. For our intents and purposes, the previous condition will do.
Printing out
We've bravely defeated the harder part! Now let's move on to printing out the HTML table using the render() method. Even though loading files and printing them into a table could be done in the same method, there are 2 different logical tasks so it's better to split them up into two methods. A method should only perform one task. In other words, we should be able to describe what it does without using the word "and". For example, "The display() method displays an HTML table with thumbnails", not "The display() method loads and displays an image gallery".
The first thing the render() method will do is echo out the start of a table. Then, it will iterate over the thumbnails using a foreach loop and echo them out as table cells using an img tag. Next, It will wrap the img tag with a link to the original image. Getting a link to the original image is very simple, all we'd have to do is replace the "_thumb" with a "." in the thumbnail's filename. To do so, we will use str_replace() function. We will count the columns we've printed out so as to not have the entire table in one row. Then, when we reach the value set in the $columns property, we will close the row, start a new one and reset the column counter.
The render() method will look something like this:
public function render() { echo('<table id="gallery"><tr>'); $column = 0; foreach ($this->files as $file) { $thumbnail = $this->directory . '/' . $file; $image = $this->directory . '/' . str_replace('_thumb.', '.', $file); echo('<td><a href="' . htmlspecialchars($image) . '"><img src="' . htmlspecialchars($thumbnail) . '" alt=""></a></td>'); $column++; if ($column >= $this->columns) { echo('</tr><tr>'); $column = 0; } } echo('</tr></table>'); }
Usage
Let's move over to index.php file. Add a basic HTML structure and create a gallery instance. Then, pass the directory path and the number of columns to the constructor. Once you've done that, call both the load() and render() methods.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Image gallery</title> </head> <body> <h1>Image gallery from ICT.social</h1> <?php require_once('classes/Gallery.php'); $gallery = new Gallery('photos', 4); $gallery->load(); $gallery->render(); ?> </body> </html>
Done!
With a little bit of CSS, the gallery application ends up looking pretty decent (the code can be downloaded in the attachment below the article):
I also added the JavaScript Lightbox plugin to display the original images.
Possible enhancements
The render() method still has a couple of kinks. One of the main ones being that the HTML code is echoed out, which makes working with it kind of confusing. Another added improvement would be to incorporate an image uploading method, that uploads images to the gallery using FTP and creates thumbnails for them automatically. For our purposes, our app is fine the way it is now.
We've programmed an object-oriented component. One that can be added to any number of websites, using various directories and displayed as individual tables.
In the next lesson, Reference and primitive data types in PHP, we will continue working with our Human class instances, and will stick with them for a while. You will learn how PHP works with reference data types.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 231x (1.39 MB)
Application includes source codes in language PHP