In Part 1 we learned how to build a filtered image gallery with jQuery and Fancybox.
Now we are going to learn how to generate our image gallery from images stored in different folders or sub-directories and also create their corresponding thumbnails automatically using php.
With this method, adding images to our existing gallery would be as easy as just placing them in their corresponding category folder.
The context
In Part 1, this is what we did to create our gallery :
- Separated our images by their corresponding category
- Used our favourite image editor to edit, resize and crop each image and create its image thumbnail (100x100 pixels as in the demo)
- Placed/saved those thumbnails in a separated folder
- Coded each link with its corresponding image path, thumbnail and category
- Optionally, re-arranged those links to mix the categories rather than showing them sequentially (so the filtering functionality makes more sense)
For a gallery of 10 items, that doesn't sound like too much work, but what if we want to create a gallery of 100 items?
Also, take in consideration that we would need to repeat the steps above for each new image we may want to add to our existing gallery. Now it really sounds like a lot of work, doesn't it?
The folder structure
The first step (as in Part 1) is to organize our gallery by different categories :
- Animals
- Landscapes
- Architecture
Based on the category organization above, we would need to create a similar folder structure for our images like :

Notice that we also have a thumbs folder where we will save the automatically generated thumbnails.
The basic html
Since php is going to generate most of the html code (the category selector tabs as well as the thumbnails), this is all what we need to start :
<!-- The category selector html--> <div id="galleryTab"> <a data-rel="all" href="javascript:;" class="filter active">View all</a> </div> <!-- The thumbnails collection wrapper--> <div class="galleryWrap"></div>
The php script
Before we start coding, let's make a list of the things our script should do :
- Get a list of all files under the "gallery" directory and its sub-directories
- Filter that list to include image files only
(we may have other type of files like .ini, .dat, .log or system files that may have been added by other external processes) - Create the image thumbnails from the original images and place them in the "thumbs" sub-directory
- Gather information regarding the location and the category each image belongs to
- Render the html thumbnails' links and the category tabs
The loop
The loop is the script that will iterate through our directory structure and get a list of all the image files and place it in an array. To iterate through the directory structure the loop will use php's foreach and glob() functions :
<?php // general variables $imgListArray = array(); // main image array list $imgExtArray = array("jpg"); // accepted image extensions (in lower-case !important) $thumbsDir = "./gallery/thumbs/"; // path to the thumbnails destination directory $galleryFiles = "./gallery/*/*"; // path to all files and sub-directories (use your own gallery name directory) // iterate all subdirectories and files foreach( glob( $galleryFiles ) as $file ) { $ext = strtolower( pathinfo( $file, PATHINFO_EXTENSION ) ); // get extension in lower-case for validation purposes $imagePath = pathinfo( $file, PATHINFO_DIRNAME ) . "/"; // get path for validation purposes (added trailing slash) // if image extension is valid (is in the $imgExtArray array) AND the image is not inside the "thumbs" sub-directory if( in_array( $ext, $imgExtArray ) && $imagePath != $thumbsDir ){ // additional image variables $imageName = pathinfo( $file, PATHINFO_BASENAME ); // returns "cheeta.jpg" $thumbnail = $thumbsDir.$imageName; // thumbnail full path and name, i.e "./gallery/thumbs/cheeta.jpg" $dataFilter = substr( $file, 10, 4 ); // from "./gallery/animals/cheeta.jpg" returns "anim" // for each image, get width and height $imageSize = getimagesize( $file ); // image size $imageWidth = $imageSize[0]; // extract image width $imageHeight = $imageSize[1]; // extract image height // set the thumb size if( $imageHeight > $imageWidth ){ // images is portrait so set thumbnail width to 100px and calculate height keeping aspect ratio $thumbWidth = 100; $thumbHeight = floor( $imageHeight * ( 100 / $imageWidth ) ); $thumbPosition = "margin-top: -" . floor( ( $thumbHeight - 100 ) / 2 ) . "px; margin-left: 0"; } else { // image is landscape so set thumbnail height to 100px and calculate width keeping aspect ratio $thumbHeight = 100; $thumbWidth = floor( $imageWidth * ( 100 / $imageHeight ) ); $thumbPosition = "margin-top: 0; margin-left: -" . floor( ( $thumbWidth - 100 ) / 2 ) . "px"; } // END else if // verify if thumbnail exists, otherwise create it if ( !file_exists( $thumbnail ) ){ $createFromjpeg = imagecreatefromjpeg( $file ); $thumb_temp = imagecreatetruecolor( $thumbWidth, $thumbHeight ); imagecopyresized( $thumb_temp, $createFromjpeg, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $imageWidth, $imageHeight ); imagejpeg( $thumb_temp, $thumbnail ); } // END if() // Create sub-array for this image // notice the key,value pair $imgListSubArray = array( LinkTo=>$file, ImageName=> $imageName, Datafilter=>$dataFilter, Thumbnail=>$thumbnail, Position=>$thumbPosition ); // Push this sub-array into main array $imgListArray array_push ( $imgListArray, $imgListSubArray ); } // END if() } // END foreach() unset($file); // destroy the reference after foreach() // END the loop ?>
The loop breakdown
The reason we want to place the list in the $imgListArrayvariable array is because our loop
foreach( glob( $galleryFiles ) as $file )
will return the list of files in alphabetical order as in the figure below :
gallery a_folder aa_image ba_image ca_image b_folder ab_image bb_image cb_image c_folder ac_image bc_image cc_image
Since we want to re-arrange the list or sort it in a random order as in Part 1, it's easier to do it inside the array and before the html is rendered.
The image file type
In the array $imgExtArray we can select what type of image files we want to include in our gallery by their extension. Here we can add the image extensions we may want to include, i.e.
$imgExtArray = array("jpg","png","gif");
Notice that all image extensions are written in lower-case.
Since we don't want to set any possible CamelCase combination in our $imgExtArray array, we will convert all the extensions to lowercase via the $ext variable :
$ext = strtolower( pathinfo( $file, PATHINFO_EXTENSION ) );
Filling the gaps
For each image in our gallery, php needs to render an html like this :
<a href="./gallery/animals/cheeta.jpg" data-filter="anim" data-fancybox-group="gallery" class="fancybox imgContainer"> <img src="./gallery/thumbs/cheeta.jpg" alt="image cheeta.jpg" style="margin-top: 0; margin-left: -17px" > </a> ... etc.
so we need to get the information to fill the value of each attribute in the html structure above :
- The href : the path to the big image. We get this value from the $file variable in the loop
foreach( glob( $galleryFiles ) as $file ) // $file returns ./gallery/animals/cheeta.jpg
- The data-filter : the first 4 letters of the sub-directory the image is located in. We get this value from the $dataFilter variable
$dataFilter = substr( $file, 10, 4 ); //returns "anim"
- The src of the img tag : the path to the thumbnail image. We build this value from the $thumbsDir, $imageName and $thumbnail variables
$thumbsDir = "./gallery/thumbs/"; // the thumbs directory $imageName = pathinfo( $file, PATHINFO_BASENAME ); // returns "cheeta.jpg" $thumbnail = $thumbsDir.$imageName; // thumbnail full path and name "./gallery/thumbs/cheeta.jpg"
- The alt : the alternate image information. We get this value from the $imageName variable
$imageName = pathinfo( $file, PATHINFO_BASENAME ); // returns "cheeta.jpg"
- The style : specific inline CSS rules to set the position of the thumbnail (to explain later). We get this value from the $thumbPosition variable.
$thumbPosition = "margin-top: n; margin-left: n;"
Notice that if you use a different name than "gallery", you may need to change the number of the starting position in the substr() function (Number 2) :
$dataFilter = substr($file, 10, 4); // ./gallery/a.. <==10th
In our example above, the first letter in animals has the 10th position in the string ./gallery/animals/cheeta.jpg (starting the count from 0)
However, if your gallery directory name is "picturegallery" for instance, then your $dataFilter variable should look like :
$dataFilter = substr($file, 17, 4); // ./picturegallery/a.. <==17th
because the first letter in animals has the 17th position in the
./picturegallery/animals/cheeta.jpg string.
Calculating the size of thumbnails
We want to create thumbnails that are 100x100px in size. However we need to take in consideration that some of our images may have a different orientation, either landscape or portrait,


or some can be larger than others,
so we can be dealing with images of all sort of sizes and orientation.
Because of that, first we need to get the actual image size if we want to keep the thumbnails' aspect ratio :
$imageSize = getimagesize( $file ); // image size $imageWidth = $imageSize[0]; // extract image width $imageHeight = $imageSize[1]; // extract image height
Second, we will set two rules depending on the image orientation :
- If the image has landscape orientation, the height of the thumbnail will always be 100px
- On the other hand, if the image has portrait orientation, the width of the thumbnail will always be 100px
The first rule will make sure that all landscape thumbnails will fill the 100px height regardless their width as in the image below :

The second rule will make sure that all portrait thumbnails will fill the 100px width regardless their height as in the image below :

Having set our rules and got the image size, then we can create our thumbnails (if they don't exist yet) and place them in their respective directory :
// set the thumb size if( $imageHeight > $imageWidth ){ // images is portrait so set thumbnail width to 100px and calculate height keeping aspect ratio $thumbWidth = 100; $thumbHeight = floor( $imageHeight * ( 100 / $imageWidth ) ); $thumbPosition = "margin-top: -" . floor( ( $thumbHeight - 100 ) / 2 ) . "px; margin-left: 0"; } else { // image is landscape so set thumbnail height to 100px and calculate width keeping aspect ratio $thumbHeight = 100; $thumbWidth = floor( $imageWidth * ( 100 / $imageHeight ) ); $thumbPosition = "margin-top: 0; margin-left: -" . floor( ( $thumbWidth - 100 ) / 2 ) . "px"; } // END else if // verify if thumbnail exists, otherwise create it if ( !file_exists( $thumbnail ) ){ $createFromjpeg = imagecreatefromjpeg( $file ); $thumb_temp = imagecreatetruecolor( $thumbWidth, $thumbHeight ); imagecopyresized( $thumb_temp, $createFromjpeg, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $imageWidth, $imageHeight ); imagejpeg( $thumb_temp, $thumbnail ); } // END if()
The thumbnail window
Since we included the class="imgContainer" in our link ( see "Filling the Gaps" section ), we could set a CSS rule to create a square window of 100px for our thumbnails. In other words, to allow a partial view of the thumbnail as highlighted in red squares of the previous image above.
Here is the basic CSS :
.imgContainer { width: 100px; height: 100px; overflow: hidden; /* don't show anything outside the 100px square */ display: block; float: left; }
Notice that in our loop, we also used the $thumbPosition variable to set the position of the thumbnail (the style attribute in the img tag). That CSS inline rule will center the thumbnail in the visible 100px window as in the following image :

Save the array
We need to save the information for each image in a sub-array $imgListSubArray and push that sub-array into our main array $imgListArray. The purpose of this multidimensional (two-level) array is to randomly sort the gallery items as well as to save the information we need to "fill the gaps" while rendering the thumbnails :
// Create sub-array for each image // notice the "key,value" pair $imgListSubArray = array( LinkTo=>$file, ImageName=> $imageName, Datafilter=>$dataFilter, Thumbnail=>$thumbnail, Position=>$thumbPosition ); // Push this sub-array into the main (multi-dimension) array $imgListArray array_push ( $imgListArray, $imgListSubArray );
Rendering the thumbnails
We have created our thumbnails and placed them in the ./gallery/thumbs/ sub-directory. We also collected the information we needed and saved it in the $imgListArray array. Now we are ready to render the thumbnails in random order in our page.
We are going to do that inside our thumbnails' wrapper :
<!-- The thumbnails collection wrapper--> <div class="galleryWrap"> <?php // render thumbnails here ?> </div>
The php script that renders the thumbnails needs to do :
- randomly sort our $imgListArray array
- count the elements inside $imgListArray
- loop through the $imgListArray array and echo each item
We will use the php's shuffle(), count() and for() functions :
<?php // shuffle and render shuffle( $imgListArray ); // random order otherwise is boring $countedItems = count( $imgListArray ); // number of images in gallery // render html links and thumbnails for ( $row = 0; $row < $countedItems; $row++ ){ // watch out for escaped double quotes echo "<a class=\"fancybox imgContainer\" data-fancybox-group=\"gallery\" href=\"" . $imgListArray[$row][LinkTo] . "\" data-filter=\"" . $imgListArray[$row][Datafilter] . "\"><img src=\"" . $imgListArray[$row][Thumbnail] . "\" style=\"" . $imgListArray[$row][Position] . "\" alt=\"" . $imgListArray[$row][ImageName] . "\" /></a>\n"; } // END for() ?>
The category selector
The category selector will allow us to filter the gallery by categories and browse a specific category in a Fancybox image gallery.
We will render our missing category tabs in our galleryTab container :
<!-- The category selector html--> <div id="galleryTab"> <a data-rel="all" href="javascript:;" class="filter active">View all</a> <?php // render the missing category tabs ?> </div>
We will set the categories iterating through the directory structure using php's foreach() and glob() functions. However, we are going to use a different flag to identify the directory names only :
$galleryDir = "./gallery/*"; // target directories under gallery : notice the star "*" after the trailing slash foreach( glob( $galleryDir, GLOB_ONLYDIR ) as $dir ) { // render category selector tabs and exclude the thumbnail directory if( $dir != "./gallery/thumbs" ){ $dataRel = substr( $dir, 10, 4 ); // return first 4 letters of each folder as category $dirName = trim( substr( $dir, 10, 200 ) ); // returns a trimmed string (200 chars length) with name of folder without parent folder echo "<a data-rel=\"" . $dataRel . "\" href=\"javascript:;\" class=\"filter\">" . $dirName . "</a>"; } // END if() } // END foreach()
The demo
Finally, see it working
DEMO
Also, you can see a real-world application of this tutorial at
http://www.castlehillcontracting.ca/gallery
Final notes
In the first version of this generated gallery I used php's RecursiveDirectoryIterator and RecursiveIteratorIterator classes to iterate through the directory structure of my gallery.
$dir = new RecursiveDirectoryIterator('./gallery/'); //select our images directory foreach (new RecursiveIteratorIterator($dir) as $filename => $file) { // the loop }
I found that this method (arguable) used more allocated server memory though so I decided to use glob() instead.
Another reason is that both classes are available in php 5 only, while glob() is available since php v4.3.0+, which I think gives me more flexibility if php 5 is not an option.
It's up to you what to use and to run you own memory tests.
Download the file
For reference purposes, the complete documented php file is available at
Disclaimer
The images used in this tutorial belong to their respective authors and were used for demo purposes only. Source of the images http://openphoto.net/
Enjoy.