Posted on Leave a comment

Contact Form with Custom Image Captcha Validation like Google reCaptcha

Last modified on January 9th, 2020 by Vincy.

A contact form on a website is a medium to the users to contact the site admin or maintainer. It acts as a medium of communication. For many a websites, it is a critical factor in getting a sale.

The Captcha in a form is a mechanism to prevent bots or malicious users from submitting the form. It protects the site from security abuses.

There are components available in the market to render captcha in a form. Google reCAPTCHA is a popular and unbeatable service to make your form captcha-enabled.

  1. Different websites use different types of captcha.
  2. Displaying random alpha-numeric characters.
  3. Requesting to solve puzzles, Google reCAPTCHA-like image captcha.

I created an example code for a contact form in PHP with the reCAPTCHA-like image captcha mechanism.

Contact Form In Php With Captcha

What is inside?

  1. Existing contact form component
  2. About this example
  3. File structure
  4. HTML code to show contact form with image captcha
  5. PHP code to validate image captcha and send contact mail
  6. Contact form captcha image database script
  7. Contact form output with custom image captcha

Ready-made contact form components are existing huge in number around the web. If you want to get one among them, then we need to be sure about the security and robustness of the code.

I have created a secure spam-free contact form script, Iris. I coded this component to easily integrate and configure with your application. If you are looking for a contact form that controls spam without using a captcha, then you will like it.

This also component includes Google reCAPTCHA. You can enable or disable this feature on a need basis. In similar ways, it comes with loads of features that is configurable in minutes.

About this example

This example code has the Name, Email, Subject and Message fields. These are some basic fields that we have seen with other PHP contact form example earlier.

Added to these fields, I have added an image captcha section in this code. This section will show five random images and ask to choose one. The random images are from the database.

I used jQuery script to validate the form field data before posting it to the PHP.

In PHP, it validates the captcha image clicked by the user. Based on the server-side captcha validation, the PHP will respond to the user.

File structure

The PHP contact form with an image captcha example includes less number of files.

The file structure is in the below screenshot. You can see that it has a systematic code with a conventional structure.

Contact Form with Image Captcha File Structure

This code snippet shows the HTML part of the index.php, the landing page. 

This contact form contains some basic fields with custom captcha in PHP.

The captcha section shows the SVG image markup from the database. It displays five random images and requests the user to select one.

The HTML form has the specification of a jQuery validation handler. The form-submit event will invoke this handler to process the form validation.

This HTML includes the response containers to display notifications to the user. These notifications acknowledge the user about the captcha validation or other responses.

index.php (contact form HTML)

<html> <head> <title>Contact Us Form</title> <link rel="stylesheet" type="text/css" href="assets/css/contact-form-style.css" /> <link rel="stylesheet" type="text/css" href="assets/css/phppot-style.css" /> <script src="vendor/jquery/jquery-3.2.1.min.js"></script> </head> <body> <div class="phppot-container"> <h1>PHP contact form with captcha images</h1> <form name="frmContact" id="captcha-cnt-frm" class="phppot-form" frmContact"" method="post" action="" enctype="multipart/form-data" onsubmit="return validateContactForm()"> <div class="phppot-row"> <div class="label"> Name <span id="userName-info" class="validation-message"></span> </div> <input type="text" class="phppot-input" name="userName" id="userName" value="<?php if(!empty($_POST['userName'])&& $type == 'error'){ echo $_POST['userName'];}?>" /> </div> <div class="phppot-row"> <div class="label"> Email <span id="userEmail-info" class="validation-message"></span> </div> <input type="text" class="phppot-input" name="userEmail" id="userEmail" value="<?php if(!empty($_POST['userEmail'])&& $type == 'error'){ echo $_POST['userEmail'];}?>" /> </div> <div class="phppot-row"> <div class="label"> Subject <span id="subject-info" class="validation-message"></span> </div> <input type="text" class="phppot-input" name="subject" id="subject" value="<?php if(!empty($_POST['subject'])&& $type == 'error'){ echo $_POST['subject'];}?>" /> </div> <div class="phppot-row"> <div class="label"> Message <span id="userMessage-info" class="validation-message"></span> </div> <textarea name="content" id="content" class="phppot-input" cols="60" rows="6"><?php if(!empty($_POST['content'])&& $type == 'error'){ echo $_POST['content'];}?></textarea> </div> <?php if (! empty($result)) { ?> <div class="phppot-row"> <div class="captcha-container <?php if(!empty($border)){ echo $border;} ?>"> <p> Select the <span class="text-color"><?php echo $result[0]['name'];?> </span><span id="captcha-info" class="validation-message"></span> </p> <input type="hidden" name="captcha_code" value="<?php echo $result[0]['name'];?>"> <?php shuffle($captchaOutput); if (! empty($captchaOutput)) { foreach ($captchaOutput as $value) { ?> <div class="svg-padding"> <div class="svg"><?php echo $value['captcha_icon'];?> <input type="hidden" class="icons" value="<?php echo $value['name'];?>"> </div> </div> <?php }}?> </div> </div> <?php }?> <div class="phppot-row"> <input type="submit" name="send" class="send-button" value="Send" /> </div> <input type="hidden" name="captcha_chosen" id="captcha-chosen" value=""> </form> <?php if(!empty($message)) { ?> <div id="phppot-message" class="<?php echo $type; ?>"><?php if(isset($message)){ ?> <?php echo $message; }}?> </div> </div> <script src="assets/js/captcha.js"></script> </body> </html> 

jQuery script to validate contact form and highlight captcha selection

This section shows the jQuery script for validation and captcha selection.

The validateContactForm() function is handling the form validation. All the contact form fields are mandatory. This function is making sure about the non-empty state of the form fields.

On selecting one of the lists of captcha images, the script puts the selected value in a form field. Also, it highlights the selected by adding CSS via script.

If the user selects no captcha, then the validation will return false.

assets/js/captcha.js

function validateContactForm() { var valid = true; $("#userName").removeClass("error-field"); $("#userEmail").removeClass("error-field"); $("#subject").removeClass("error-field"); $("#content").removeClass("error-field"); $("#userName-info").html("").hide(); $("#userEmail-info").html("").hide(); $("#subject-info").html("").hide(); $("#content-info").html("").hide(); $(".validation-message").html(""); $(".phppot-input").css('border', '#e0dfdf 1px solid'); $(".captcha-container").css('border', '#e0dfdf 1px solid'); var userName = $("#userName").val(); var userEmail = $("#userEmail").val(); var subject = $("#subject").val(); var content = $("#content").val(); var captcha = $("#captcha-chosen").val(); if (userName.trim() == "") { $("#userName-info").html("required.").css("color", "#ee0000").show(); $("#userName").css('border', '#e66262 1px solid'); $("#userName").addClass("error-field"); valid = false; } if (userEmail.trim() == "") { $("#userEmail-info").html("required.").css("color", "#ee0000").show(); $("#userEmail").css('border', '#e66262 1px solid'); $("#userEmail").addClass("error-field"); valid = false; } if (!userEmail.match(/^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/)) { $("#userEmail-info").html("invalid email address.").css("color", "#ee0000").show(); $("#userEmail").css('border', '#e66262 1px solid'); $("#userEmail").addClass("error-field"); valid = false; } if (subject == "") { $("#subject-info").html("required.").css("color", "#ee0000").show(); $("#subject").css('border', '#e66262 1px solid'); $("#subject").addClass("error-field"); valid = false; } if (content == "") { $("#userMessage-info").html("required.").css("color", "#ee0000").show(); $("#content").css('border', '#e66262 1px solid'); $("#content").addClass("error-field"); valid = false; } if (captcha == "") { $("#captcha-info").html("required."); $(".captcha-container").css('border', '#e66262 1px solid'); valid = false; } if (valid == false) { $('.error-field').first().focus(); valid = false; } return valid; } $(".svg-padding").on('click', function() { $(".svg").removeClass('captcha-selected'); $(this).find(".svg").addClass('captcha-selected'); var icons = $(this).find(".icons").val(); $("#captcha-chosen").val(icons); }); 

CSS created for contact form example

These are the exclusive styles created to present the contact form. Mostly, it contains styles for the captcha section.

I used a generic CSS template for designing other common form components. You can find this CSS in the downloadable.

assets/css/contact-form-style.css

.svg-padding { display: inline-block; } .svg { cursor: pointer; padding: 5px 5px 5px 5px; border-radius: 3px; margin: 0px 5px 0px 5px; border: 1px solid #FFF; } .text-color { font-weight: bold; } .captcha-container { background: #fff; padding: 15px; border: 1px solid #9a9a9a; width: 270px; border-radius: 3px; padding-top: 0px; } .error-field { border: 1px solid #d96557; } .send-button { cursor: pointer; background: #3cb73c; border: #36a536 1px solid; color: #FFF; font-size: 1em; width: 100px; } .captcha-selected { color: #1cb87b; background-color: #e3e3e3; border: #d7d7d7 1px solid; } .border-error-color { border: 1px solid #e66262; } 

On loading the contact form, the PHP code reads the random captcha images from the database. In PHP, it picks one image from the random results as the captcha code.

The HTML form contains a hidden field to have this code.

When the user selects an image and posts it to the PHP, it will validate the selected captcha.

The user-selected captcha is matched with the pre-loaded code, then the PHP code will return true. Then, it will process the contact email sending script.

index.php (Captcha Validation and Mail sending)

<?php namespace Phppot; require_once ("Model/Contact.php"); $contact = new Contact(); if (! empty($_POST['send'])) { if ($_POST['captcha_code'] == $_POST['captcha_chosen']) { $contact->sendContactMail($_POST); $message = "Hi, we have received your message. Thank you."; $type = "success"; } else { $message = "Invalid captcha. Please select the correct image."; $type = "error"; $border = "border-error-color"; } } $result = $contact->getRecord(); $termId = $result[0]['id']; $captchaResult = $contact->getCaptchaIcons($termId); $randomCaptchaResult = $contact->getRandomCaptchaId($termId); $captchaOutput = array_merge($captchaResult, $randomCaptchaResult); ?> 

In this PHP model class, it has the functions to read random captcha images from the database.

The getRecord() method reads a single random record to load captcha code on the page load.

The sendContactMail() function send the contact mail. I used PHP mail() function for this example. If you want to use SMTP for sending the email, you can see the example in the linked article.

Model/Contact.php

<?php namespace Phppot; use Phppot\DataSource; class Contact { private $ds; function __construct() { require_once __DIR__ . './../lib/DataSource.php'; $this->ds = new DataSource(); } function getRecord() { $query = "SELECT * FROM tbl_term ORDER BY RAND() LIMIT 1"; $result = $this->ds->select($query); return $result; } function getCaptchaIcons($id) { $query = "SELECT tbl_captcha_images.*, tbl_term.name FROM tbl_captcha_images JOIN tbl_term ON tbl_captcha_images.term_id = tbl_term.id WHERE term_id != " . $id . " ORDER BY RAND() LIMIT 4"; $captchaResult = $this->ds->select($query); return $captchaResult; } function getRandomCaptchaId($id) { $query = "SELECT tbl_captcha_images.*, tbl_term.name FROM tbl_captcha_images JOIN tbl_term ON tbl_captcha_images.term_id = tbl_term.id WHERE term_id = " . $id . " ORDER BY RAND() LIMIT 1"; $captcha = $this->ds->select($query); return $captcha; } function sendContactMail($postValues) { $name = $postValues["userName"]; $email = $postValues["userEmail"]; $subject = $postValues["subject"]; $content = $postValues["content"]; $toEmail = "SITE_ADMIN_EMAIL"; // Put in place the recipient email $mailHeaders = "From: " . $name . "<" . $email . ">\r\n"; mail($toEmail, $subject, $content, $mailHeaders); } } 

lib/Datasource.php

<?php namespace Phppot; /** * Generic datasource class for handling DB operations. * Uses MySqli and PreparedStatements. * * @version 2.3 */ class DataSource { // PHP 7.1.0 visibility modifiers are allowed for class constants. // when using above 7.1.0, declare the below constants as private const HOST = 'localhost'; const USERNAME = 'root'; const PASSWORD = ''; const DATABASENAME = 'contact_form_captcha'; private $conn; /** * PHP implicitly takes care of cleanup for default connection types. * So no need to worry about closing the connection. * * Singletons not required in PHP as there is no * concept of shared memory. * Every object lives only for a request. * * Keeping things simple and that works! */ function __construct() { $this->conn = $this->getConnection(); } /** * If connection object is needed use this method and get access to it. * Otherwise, use the below methods for insert / update / etc. * * @return \mysqli */ public function getConnection() { $conn = new \mysqli(self::HOST, self::USERNAME, self::PASSWORD, self::DATABASENAME); if (mysqli_connect_errno()) { trigger_error("Problem with connecting to database."); } $conn->set_charset("utf8"); return $conn; } /** * To get database results * * @param string $query * @param string $paramType * @param array $paramArray * @return array */ public function select($query, $paramType = "", $paramArray = array()) { $stmt = $this->conn->prepare($query); if (! empty($paramType) && ! empty($paramArray)) { $this->bindQueryParams($stmt, $paramType, $paramArray); } $stmt->execute(); $result = $stmt->get_result(); if ($result->num_rows > 0) { while ($row = $result->fetch_assoc()) { $resultset[] = $row; } } if (! empty($resultset)) { return $resultset; } } } 

This SQL script includes the structure and data of the tables used to display custom captcha. The tbl_captcha_images table contains the SVG markup of the captcha images.

I have used another table tbl_term to hold the captcha term and title. The captcha term is for stating the user what to select. The captcha title is a slug to add it with a title attribute.

sql/contact_form_captcha.sql

-- -- Database: `contact_form_captcha` -- -- -------------------------------------------------------- -- -- Table structure for table `tbl_captcha_images` -- CREATE TABLE `tbl_captcha_images` ( `id` int(11) NOT NULL, `term_id` int(11) NOT NULL, `captcha_icon` text NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- -- Dumping data for table `tbl_captcha_images` -- INSERT INTO `tbl_captcha_images` (`id`, `term_id`, `captcha_icon`) VALUES (1, 1, '<svg width=\"25px\" height=\"25px\" aria-hidden=\"true\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 512\"><path fill=\"currentColor\" d=\"M192 384h192c53 0 96-43 96-96h32c70.6 0 128-57.4 128-128S582.6 32 512 32H120c-13.3 0-24 10.7-24 24v232c0 53 43 96 96 96zM512 96c35.3 0 64 28.7 64 64s-28.7 64-64 64h-32V96h32zm47.7 384H48.3c-47.6 0-61-64-36-64h583.3c25 0 11.8 64-35.9 64z\"></path></svg>'), (2, 2, '<svg width=\"25px\" height=\"25px\" aria-hidden=\"true\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\"><path fill=\"currentColor\" d=\"M259.3 17.8L194 150.2 47.9 171.5c-26.2 3.8-36.7 36.1-17.7 54.6l105.7 103-25 145.5c-4.5 26.3 23.2 46 46.4 33.7L288 439.6l130.7 68.7c23.2 12.2 50.9-7.4 46.4-33.7l-25-145.5 105.7-103c19-18.5 8.5-50.8-17.7-54.6L382 150.2 316.7 17.8c-11.7-23.6-45.6-23.9-57.4 0z\"></path></svg>'), (3, 3, '<svg width=\"25px\" height=\"25px\" aria-hidden=\"true\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 384 512\"><path fill=\"currentColor\" d=\"M377.33 375.429L293.906 288H328c21.017 0 31.872-25.207 17.448-40.479L262.79 160H296c20.878 0 31.851-24.969 17.587-40.331l-104-112.003c-9.485-10.214-25.676-10.229-35.174 0l-104 112.003C56.206 134.969 67.037 160 88 160h33.21l-82.659 87.521C24.121 262.801 34.993 288 56 288h34.094L6.665 375.429C-7.869 390.655 2.925 416 24.025 416H144c0 32.781-11.188 49.26-33.995 67.506C98.225 492.93 104.914 512 120 512h144c15.086 0 21.776-19.069 9.995-28.494-19.768-15.814-33.992-31.665-33.995-67.496V416h119.97c21.05 0 31.929-25.309 17.36-40.571z\"></path></svg>'), (4, 4, '<svg width=\"25px\" height=\"25px\" aria-hidden=\"true\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 512\"><path fill=\"currentColor\" d=\"M624 352h-16V243.9c0-12.7-5.1-24.9-14.1-33.9L494 110.1c-9-9-21.2-14.1-33.9-14.1H416V48c0-26.5-21.5-48-48-48H48C21.5 0 0 21.5 0 48v320c0 26.5 21.5 48 48 48h16c0 53 43 96 96 96s96-43 96-96h128c0 53 43 96 96 96s96-43 96-96h48c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zM160 464c-26.5 0-48-21.5-48-48s21.5-48 48-48 48 21.5 48 48-21.5 48-48 48zm320 0c-26.5 0-48-21.5-48-48s21.5-48 48-48 48 21.5 48 48-21.5 48-48 48zm80-208H416V144h44.1l99.9 99.9V256z\"></path></svg>'), (5, 5, '<svg width=\"25px\" height=\"25px\" aria-hidden=\"true\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M512 176.001C512 273.203 433.202 352 336 352c-11.22 0-22.19-1.062-32.827-3.069l-24.012 27.014A23.999 23.999 0 0 1 261.223 384H224v40c0 13.255-10.745 24-24 24h-40v40c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24v-78.059c0-6.365 2.529-12.47 7.029-16.971l161.802-161.802C163.108 213.814 160 195.271 160 176 160 78.798 238.797.001 335.999 0 433.488-.001 512 78.511 512 176.001zM336 128c0 26.51 21.49 48 48 48s48-21.49 48-48-21.49-48-48-48-48 21.49-48 48z\"></path></svg>'), (6, 6, '<svg width=\"25px\" height=\"25px\" aria-hidden=\"true\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M499.991 168h-54.815l-7.854-20.944c-9.192-24.513-25.425-45.351-46.942-60.263S343.651 64 317.472 64H194.528c-26.18 0-51.391 7.882-72.908 22.793-21.518 14.912-37.75 35.75-46.942 60.263L66.824 168H12.009c-8.191 0-13.974 8.024-11.384 15.795l8 24A12 12 0 0 0 20.009 216h28.815l-.052.14C29.222 227.093 16 247.997 16 272v48c0 16.225 6.049 31.029 16 42.309V424c0 13.255 10.745 24 24 24h48c13.255 0 24-10.745 24-24v-40h256v40c0 13.255 10.745 24 24 24h48c13.255 0 24-10.745 24-24v-61.691c9.951-11.281 16-26.085 16-42.309v-48c0-24.003-13.222-44.907-32.772-55.86l-.052-.14h28.815a12 12 0 0 0 11.384-8.205l8-24c2.59-7.771-3.193-15.795-11.384-15.795zm-365.388 1.528C143.918 144.689 168 128 194.528 128h122.944c26.528 0 50.61 16.689 59.925 41.528L391.824 208H120.176l14.427-38.472zM88 328c-17.673 0-32-14.327-32-32 0-17.673 14.327-32 32-32s48 30.327 48 48-30.327 16-48 16zm336 0c-17.673 0-48 1.673-48-16 0-17.673 30.327-48 48-48s32 14.327 32 32c0 17.673-14.327 32-32 32z\"></path></svg>'), (7, 7, '<svg width=\"25px\" height=\"25px\" aria-hidden=\"true\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\"><path fill=\"currentColor\" d=\"M414.9 24C361.8 24 312 65.7 288 89.3 264 65.7 214.2 24 161.1 24 70.3 24 16 76.9 16 165.5c0 72.6 66.8 133.3 69.2 135.4l187 180.8c8.8 8.5 22.8 8.5 31.6 0l186.7-180.2c2.7-2.7 69.5-63.5 69.5-136C560 76.9 505.7 24 414.9 24z\"></path></svg>'), (8, 8, '<svg width=\"25px\" height=\"25px\" aria-hidden=\"true\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\"><path fill=\"currentColor\" d=\"M488 312.7V456c0 13.3-10.7 24-24 24H348c-6.6 0-12-5.4-12-12V356c0-6.6-5.4-12-12-12h-72c-6.6 0-12 5.4-12 12v112c0 6.6-5.4 12-12 12H112c-13.3 0-24-10.7-24-24V312.7c0-3.6 1.6-7 4.4-9.3l188-154.8c4.4-3.6 10.8-3.6 15.3 0l188 154.8c2.7 2.3 4.3 5.7 4.3 9.3zm83.6-60.9L488 182.9V44.4c0-6.6-5.4-12-12-12h-56c-6.6 0-12 5.4-12 12V117l-89.5-73.7c-17.7-14.6-43.3-14.6-61 0L4.4 251.8c-5.1 4.2-5.8 11.8-1.6 16.9l25.5 31c4.2 5.1 11.8 5.8 16.9 1.6l235.2-193.7c4.4-3.6 10.8-3.6 15.3 0l235.2 193.7c5.1 4.2 12.7 3.5 16.9-1.6l25.5-31c4.2-5.2 3.4-12.7-1.7-16.9z\"></path></svg>'), (9, 9, '<svg width=\"25px\" height=\"25px\" aria-hidden=\"true\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M349.565 98.783C295.978 98.783 251.721 64 184.348 64c-24.955 0-47.309 4.384-68.045 12.013a55.947 55.947 0 0 0 3.586-23.562C118.117 24.015 94.806 1.206 66.338.048 34.345-1.254 8 24.296 8 56c0 19.026 9.497 35.825 24 45.945V488c0 13.255 10.745 24 24 24h16c13.255 0 24-10.745 24-24v-94.4c28.311-12.064 63.582-22.122 114.435-22.122 53.588 0 97.844 34.783 165.217 34.783 48.169 0 86.667-16.294 122.505-40.858C506.84 359.452 512 349.571 512 339.045v-243.1c0-23.393-24.269-38.87-45.485-29.016-34.338 15.948-76.454 31.854-116.95 31.854z\"></path></svg>'), (10, 10, '<svg width=\"25px\" height=\"25px\" aria-hidden=\"true\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\"><path fill=\"currentColor\" d=\"M472 200H360.211L256.013 5.711A12 12 0 0 0 245.793 0h-57.787c-7.85 0-13.586 7.413-11.616 15.011L209.624 200H99.766l-34.904-58.174A12 12 0 0 0 54.572 136H12.004c-7.572 0-13.252 6.928-11.767 14.353l21.129 105.648L.237 361.646c-1.485 7.426 4.195 14.354 11.768 14.353l42.568-.002c4.215 0 8.121-2.212 10.289-5.826L99.766 312h109.858L176.39 496.989c-1.97 7.599 3.766 15.011 11.616 15.011h57.787a12 12 0 0 0 10.22-5.711L360.212 312H472c57.438 0 104-25.072 104-56s-46.562-56-104-56z\"></path></svg>'); -- -------------------------------------------------------- -- -- Table structure for table `tbl_term` -- CREATE TABLE `tbl_term` ( `id` int(11) NOT NULL, `name` varchar(255) NOT NULL, `slug` varchar(255) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- -- Dumping data for table `tbl_term` -- INSERT INTO `tbl_term` (`id`, `name`, `slug`) VALUES (1, 'cup', 'cup-1'), (2, 'star', 'star-2'), (3, 'tree', 'tree-3'), (4, 'truck', 'truck-4'), (5, 'key', 'key-5'), (6, 'car', 'car-6'), (7, 'heart', 'heart-7'), (8, 'house', 'house-8'), (9, 'flag', 'flag-9'), (10, 'plane', 'plane-10'); -- -- Indexes for dumped tables -- -- -- Indexes for table `tbl_captcha_images` -- ALTER TABLE `tbl_captcha_images` ADD PRIMARY KEY (`id`); -- -- Indexes for table `tbl_term` -- ALTER TABLE `tbl_term` ADD PRIMARY KEY (`id`); -- -- AUTO_INCREMENT for dumped tables -- -- -- AUTO_INCREMENT for table `tbl_captcha_images` -- ALTER TABLE `tbl_captcha_images` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=11; -- -- AUTO_INCREMENT for table `tbl_term` -- ALTER TABLE `tbl_term` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=11; COMMIT; 

Contact Form Captcha Image Database

This screenshot shows the output of this PHP contact form example. It displays the custom image captcha in the form.

It focuses on the captcha section because of the form submitted with invalid captcha.

Contact Form in PHP with Image Captcha

Download

↑ Back to Top

Posted on Leave a comment

User Registration in PHP with Login: Form with MySQL and Code Download

Last modified on January 3rd, 2020 by Vincy.

Are you looking for code to create user registration in PHP? A lightweight form with MySQL database backend. Read on!

There are lots of PHP components for user registration available on the Internet. But these contain heavy stuff and lots of dependencies.

An appropriate code should be lightweight, secure, feature-packed and customizable. I am going to explain how to code this user registration in PHP with a login.

With this code, you can customize or put any add-ons as per your need and enhance it.

User Registration in PHP with Login Form

What is inside?

  1. Example code for user registration in PHP
  2. Create user registration and login form
  3. Registration and login form validation
  4. Process user registration in PHP
  5. PHP login authentication code
  6. User dashboard
  7. MySQL database script
  8. Screenshot of user registration and login form

Example code for user registration in PHP

In this example, I have created user registration in PHP with the login script. In a previous article, we have seen how to create a login script with PHP session.

On a landing page, it shows a login form with a signup link. The registered user can enter their login details with the login form. Once done, he can get into the dashboard after authentication.

If the user does not have an account, then he can click the signup option to create a new account.

The user registration form requests username, email, password from the user. On submission, PHP code allows registration if the email does not already exist.

This example code has client-side validation for validating the entered user details. And also, it includes contains the server-side uniqueness test. The user email is the base to check uniqueness before adding the users to the MySQL database.

This linked article includes a basic example of implementing user registration in PHP and MySQL.

File structure

User Registration File Structure

Create user registration and login form

I have created three HTML view login form, registration form and the dashboard for this code.

Below HMTL code is for displaying the login form to the user. In this form, it has two inputs to allow the user to enter their username and password.

Without these details, a validation code will not allow the login to proceed. The login form tag’s on-click attribute is with loginValidation(). This function contains the login form validation script.

On submitting this login form, the PHP code will validate the user. If the users clear the authentication, then it will redirect him to the dashboard.

If the user attempts to log in with the wrong data, then the code will display a login error message in the login form. If you want to limit the failed login attempts, the linked article has an example of that.

See also login form with forgot password and remember me.

login-form.php

<div class="sign-up-container"> <div class="login-signup"> <a href="user-registration-form.php">Sign up</a> </div> <div class="signup-align"> <form name="login" action="" method="post" onsubmit="return loginValidation()"> <div class="signup-heading">Login</div> <?php if(!empty($loginResult)){?> <div class="error-msg"><?php echo $loginResult;?></div> <?php }?> <div class="row"> <div class="inline-block"> <div class="form-label"> Username<span class="required error" id="username-info"></span> </div> <input class="input-box-330" type="text" name="username" id="username"> </div> </div> <div class="row"> <div class="inline-block"> <div class="form-label"> Password<span class="required error" id="signup-password-info"></span> </div> <input class="input-box-330" type="password" name="signup-password" id="signup-password"> </div> </div> <div class="row"> <input class="sign-up-btn" type="submit" name="login-btn" id="login-btn" value="Login"> </div> </form> </div> </div> 

This is a user registration form getting minimal user data from the user. All form fields are mandatory.

It will pass-through a JavaScript validation before processing the user registration in PHP.

On submitting the registration form fields, it will invoke the signupValidation() JavaScript method. In this method, it validates with the non-empty check, email format, and the password match.

After validation, the PHP registration will take place with the posted form data. 

user-registration-form.php

<HTML> <HEAD> <TITLE>Registration</TITLE> <link href="./assets/css/phppot-style.css" type="text/css" rel="stylesheet" /> <link href="./assets/css/user-registration.css" type="text/css" rel="stylesheet" /> </HEAD> <BODY> <div class="phppot-container"> <div class="sign-up-container"> <div class="login-signup"> <a href="login-form.php">Login</a> </div> <div class=""> <form name="sign-up" action="" method="post" onsubmit="return signupValidation()"> <div class="signup-heading">Registration</div> <?php if(!empty($registrationResponse["status"])) { ?> <?php if($registrationResponse["status"] == "error") { ?> <div class="server-response error-msg"><?php echo $registrationResponse["message"]; ?></div> <?php } else if($registrationResponse["status"] == "success") { ?> <div class="server-response success-msg"><?php echo $registrationResponse["message"]; ?></div> <?php } ?> <?php } ?> <div class="error-msg" id="error-msg"></div> <div class="row"> <div class="inline-block"> <div class="form-label"> Username<span class="required error" id="username-info"></span> </div> <input class="input-box-330" type="text" name="username" id="username"> </div> </div> <div class="row"> <div class="inline-block"> <div class="form-label"> Email<span class="required error" id="email-info"></span> </div> <input class="input-box-330" type="email" name="email" id="email"> </div> </div> <div class="row"> <div class="inline-block"> <div class="form-label"> Password<span class="required error" id="signup-password-info"></span> </div> <input class="input-box-330" type="password" name="signup-password" id="signup-password"> </div> </div> <div class="row"> <div class="inline-block"> <div class="form-label"> Confirm Password<span class="required error" id="confirm-password-info"></span> </div> <input class="input-box-330" type="password" name="confirm-password" id="confirm-password"> </div> </div> <div class="row"> <input class="sign-up-btn" type="submit" name="signup-btn" id="signup-btn" value="Sign up"> </div> </form> </div> </div> </div> </BODY> </HTML> 

Landing page loads login registration forms

index.php

<?php use Phppot\Member; session_start(); ?> <HTML> <HEAD> <TITLE>user-registration</TITLE> <link href="./assets/css/phppot-style.css" type="text/css" rel="stylesheet" /> <link href="./assets/css/user-registration.css" type="text/css" rel="stylesheet" /> <script src="./vendor/jquery/jquery-3.3.1.js" type="text/javascript"></script> </HEAD> <BODY> <div class="phppot-container"> <?php require_once "login-form.php";?> </div> </BODY> </HTML> 

And the below CSS is for presenting this example of user registration in PHP.

user-registration.css

.sign-up-container { border: 1px solid; border-color: #9a9a9a; background: #fff; border-radius: 4px; padding: 10px; width: 350px; margin: 50px auto; } .page-header { float: right; } .login-signup { margin: 10px; text-decoration: none; float: right; } .login-signup a { text-decoration: none; color: #000; font-weight: 700; } .signup-heading { font-size: 2em; font-weight: bold; padding-top: 60px; text-align: center; } .inline-block { display: inline-block; } .row { margin: 15px 0px; text-align: center; } .form-label { margin-bottom: 5px; text-align: left; } input.input-box-330 { width: 250px; } .sign-up-container .error { color: #ee0000; padding: 0px; background: none; border: #ee0000; } .sign-up-container .error-field { border: 1px solid #d96557; } .sign-up-container .error:before { content: '*'; padding: 0 3px; color: #D8000C; } .error-msg { padding-top: 10px; color: #D8000C; text-align: center; } .success-msg { padding-top: 10px; color: #23a600; text-align: center; } input.sign-up-btn { background-color: #ffb932; border-color: #ffc87a #e2a348 #da9d0a; text-align: center; cursor: pointer; color: #000; width: 250px } .signup-align { margin: 0 auto; } .page-content { font-weight: bold; padding-top: 60px; text-align: center; } 

Registration and login form validation

In this section, we are going to see the code created in JavaScript for form validation.

There are two methods for validating the form fields before sending the data to the PHP code.

On invalid data submission, the code will return a boolean false. It forces the user to enter the required fields by highlighting them.

login-form.php (JavaScript)

function loginValidation() { var valid = true; $("#username").removeClass("error-field"); $("#password").removeClass("error-field"); var UserName = $("#username").val(); var Password = $('#signup-password').val(); $("#username-info").html("").hide(); $("#email-info").html("").hide(); if (UserName.trim() == "") { $("#username-info").html("required.").css("color", "#ee0000").show(); $("#username").addClass("error-field"); valid = false; } if (Password.trim() == "") { $("#signup-password-info").html("required.").css("color", "#ee0000").show(); $("#signup-password").addClass("error-field"); valid = false; } if (valid == false) { $('.error-field').first().focus(); valid = false; } return valid; } 

user-registration-form.php (JavaScript)

function signupValidation() { var valid = true; $("#username").removeClass("error-field"); $("#email").removeClass("error-field"); $("#password").removeClass("error-field"); $("#confirm-password").removeClass("error-field"); var UserName = $("#username").val(); var email = $("#email").val(); var Password = $('#signup-password').val(); var ConfirmPassword = $('#confirm-password').val(); var emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; $("#username-info").html("").hide(); $("#email-info").html("").hide(); if (UserName.trim() == "") { $("#username-info").html("required.").css("color", "#ee0000").show(); $("#username").addClass("error-field"); valid = false; } if (email == "") { $("#email-info").html("required").css("color", "#ee0000").show(); $("#email").addClass("error-field"); valid = false; } else if (email.trim() == "") { $("#email-info").html("Invalid email address.").css("color", "#ee0000").show(); $("#email").addClass("error-field"); valid = false; } else if (!emailRegex.test(email)) { $("#email-info").html("Invalid email address.").css("color", "#ee0000") .show(); $("#email").addClass("error-field"); valid = false; } if (Password.trim() == "") { $("#signup-password-info").html("required.").css("color", "#ee0000").show(); $("#signup-password").addClass("error-field"); valid = false; } if (ConfirmPassword.trim() == "") { $("#confirm-password-info").html("required.").css("color", "#ee0000").show(); $("#confirm-password").addClass("error-field"); valid = false; } if(Password != ConfirmPassword){ $("#error-msg").html("Both passwords must be same.").show(); valid=false; } if (valid == false) { $('.error-field').first().focus(); valid = false; } return valid; } 

Process user registration in PHP

After submitting the form details, it processes user registration in the PHP code.

This code uses default form submit to post data to the PHP. If you want the user registration code with AJAX, then we have to prevent the default submit with a script.

I have added this code at the beginning of the user-registration-form.php. It checks if the user submitted the form. Then, it invokes the registerMember() method defined in the Member model.

login-form.php (PHP code)

<?php use Phppot\Member; if (! empty($_POST["signup-btn"])) { require_once './Model/Member.php'; $member = new Member(); $registrationResponse = $member->registerMember(); } ?> 

I have shown the Member model class code below. It contains all the functions related to this user registration and login example.

In the registerMember() function, it checks if the posted email already exists. If so, it truncates the registration flow and returns the error. Otherwise, it creates the Insert query to add the member record into the MySQL database.

The loginMember() function checks if there is any match for the entered login details. If the match found, it clears the authentication and allows the user to access the dashboard.

Model/Member.php

<?php namespace Phppot; class Member { private $ds; function __construct() { require_once __DIR__ . './../lib/DataSource.php'; $this->ds = new DataSource(); } public function isMemberExists($email) { $query = 'SELECT * FROM tbl_member where email = ?'; $paramType = 's'; $paramValue = array( $email ); $insertRecord = $this->ds->select($query, $paramType, $paramValue); $count = 0; if (is_array($insertRecord)) { $count = count($insertRecord); } return $count; } public function registerMember() { $result = $this->isMemberExists($_POST["email"]); if ($result < 1) { if (! empty($_POST["signup-password"])) { $hashedPassword = password_hash($_POST["signup-password"], PASSWORD_DEFAULT); } $query = 'INSERT INTO tbl_member (username, password, email) VALUES (?, ?, ?)'; $paramType = 'sss'; $paramValue = array( $_POST["username"], $hashedPassword, $_POST["email"] ); $memberId = $this->ds->insert($query, $paramType, $paramValue); if(!empty($memberId)) { $response = array("status" => "success", "message" => "You have registered successfully."); } } else if ($result == 1) { $response = array("status" => "error", "message" => "Email already exists."); } return $response; } public function getMember($username) { $query = 'SELECT * FROM tbl_member where username = ?'; $paramType = 's'; $paramValue = array( $username ); $loginUser = $this->ds->select($query, $paramType, $paramValue); return $loginUser; } public function loginMember() { $loginUserResult = $this->getMember($_POST["username"]); if (! empty($_POST["signup-password"])) { $password = $_POST["signup-password"]; } $hashedPassword = $loginUserResult[0]["password"]; $loginPassword = 0; if (password_verify($password, $hashedPassword)) { $loginPassword = 1; } if ($loginPassword == 1) { $_SESSION["username"] = $loginUserResult[0]["username"]; $url = "./home.php"; header("Location: $url"); } else if ($loginPassword == 0) { $loginStatus = "Invalid username or password."; return $loginStatus; } } } 

PHP login authentication code

Below PHP code is for invoking the authentication function after login. It is in the login-form.php file above the HTML code.

user-registration-form.php (PHP Code)

<?php if (! empty($_POST["login-btn"])) { require_once './Model/Member.php'; $member = new Member(); $loginResult = $member->loginMember(); } ?> 

User dashboard

This is the user dashboard HTML code. It shows a welcome message with the logged-in member name. It also has an option to logout from the current session.

home.php

<?php session_start(); $username = $_SESSION["username"]; ?> <HTML> <HEAD> <TITLE>Welcome</TITLE> <link href="./assets/css/phppot-style.css" type="text/css" rel="stylesheet" /> <link href="./assets/css/user-registration.css" type="text/css" rel="stylesheet" /> </HEAD> <BODY> <div class="phppot-container"> <div class="page-header"> <span class="login-signup"><a href="login-form.php">Logout</a></span> </div> <div class="page-content">Welcome <?php echo $username;?></div> </div> </BODY> </HTML> 

DataSource.php

<?php /** * Copyright (C) 2019 Phppot * * Distributed under MIT license with an exception that, * you don’t have to include the full MIT License in your code. * In essense, you can use it on commercial software, modify and distribute free. * Though not mandatory, you are requested to attribute this URL in your code or website. */ namespace Phppot; /** * Generic datasource class for handling DB operations. * Uses MySqli and PreparedStatements. * * @version 2.5 - recordCount function added */ class DataSource { // PHP 7.1.0 visibility modifiers are allowed for class constants. // when using above 7.1.0, declare the below constants as private const HOST = 'localhost'; const USERNAME = 'root'; const PASSWORD = 'test'; const DATABASENAME = 'user-registration'; private $conn; /** * PHP implicitly takes care of cleanup for default connection types. * So no need to worry about closing the connection. * * Singletons not required in PHP as there is no * concept of shared memory. * Every object lives only for a request. * * Keeping things simple and that works! */ function __construct() { $this->conn = $this->getConnection(); } /** * If connection object is needed use this method and get access to it. * Otherwise, use the below methods for insert / update / etc. * * @return \mysqli */ public function getConnection() { $conn = new \mysqli(self::HOST, self::USERNAME, self::PASSWORD, self::DATABASENAME); if (mysqli_connect_errno()) { trigger_error("Problem with connecting to database."); } $conn->set_charset("utf8"); return $conn; } /** * To get database results * * @param string $query * @param string $paramType * @param array $paramArray * @return array */ public function select($query, $paramType = "", $paramArray = array()) { $stmt = $this->conn->prepare($query); if (! empty($paramType) && ! empty($paramArray)) { $this->bindQueryParams($stmt, $paramType, $paramArray); } $stmt->execute(); $result = $stmt->get_result(); if ($result->num_rows > 0) { while ($row = $result->fetch_assoc()) { $resultset[] = $row; } } if (! empty($resultset)) { return $resultset; } } /** * To insert * * @param string $query * @param string $paramType * @param array $paramArray * @return int */ public function insert($query, $paramType, $paramArray) { $stmt = $this->conn->prepare($query); $this->bindQueryParams($stmt, $paramType, $paramArray); $stmt->execute(); $insertId = $stmt->insert_id; return $insertId; } /** * To execute query * * @param string $query * @param string $paramType * @param array $paramArray */ public function execute($query, $paramType = "", $paramArray = array()) { $stmt = $this->conn->prepare($query); if (! empty($paramType) && ! empty($paramArray)) { $this->bindQueryParams($stmt, $paramType, $paramArray); } $stmt->execute(); } /** * 1. * Prepares parameter binding * 2. Bind prameters to the sql statement * * @param string $stmt * @param string $paramType * @param array $paramArray */ public function bindQueryParams($stmt, $paramType, $paramArray = array()) { $paramValueReference[] = & $paramType; for ($i = 0; $i < count($paramArray); $i ++) { $paramValueReference[] = & $paramArray[$i]; } call_user_func_array(array( $stmt, 'bind_param' ), $paramValueReference); } /** * To get database results * * @param string $query * @param string $paramType * @param array $paramArray * @return array */ public function getRecordCount($query, $paramType = "", $paramArray = array()) { $stmt = $this->conn->prepare($query); if (! empty($paramType) && ! empty($paramArray)) { $this->bindQueryParams($stmt, $paramType, $paramArray); } $stmt->execute(); $stmt->store_result(); $recordCount = $stmt->num_rows; return $recordCount; } } 

MySQL database script

Below SQL script shows the MySQL database table’s create statement. It also has the specification for the key and indexes.

User registration MySQL schema

Import this script into your PHP development root to execute this example.

sql/user-registration.sql

-- -- Database: `user-registration` -- -- -------------------------------------------------------- -- -- Table structure for table `tbl_member` -- CREATE TABLE `tbl_member` ( `id` int(11) NOT NULL, `username` varchar(255) NOT NULL, `password` varchar(200) NOT NULL, `email` varchar(255) NOT NULL, `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- -- Indexes for dumped tables -- -- -- Indexes for table `tbl_member` -- ALTER TABLE `tbl_member` ADD PRIMARY KEY (`id`); -- -- AUTO_INCREMENT for dumped tables -- -- -- AUTO_INCREMENT for table `tbl_member` -- ALTER TABLE `tbl_member` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; 

Screenshot of user registration and login form

Login form with validation response:

User Login Form Output

User registration form server-side validation response:

Screenshot User Registration in-php PHP

Download

↑ Back to Top

Posted on Leave a comment

One Page Checkout Script Free with Example Template in PHP

Last modified on December 13th, 2019 by Vincy.

One page checkout reduces friction in the buying process, increases conversion and improves the sales. Do you know the cart abandonment rate of the customers during their purchase? Unnecessary, lengthy checkout process is one of the main reason which increases this rate.

Just by fine-tuning your checkout process, you can increase the sales by a staggering 23%. This could be a game changer for many eCommerce websites. I have cited the Baymard Institute research statistics below. Just check it out.

The right ecommerce software and a shopping cart script will definitely have an option for one page checkout. By increasing steps in the checkout flow, you do not have anything to gain. Then why not one page checkout? Not only Baymard Institute’s research, check any parties research statistics in this area and everything points towards a seamless checkout process.

One Page Checkout Script Free with Example Template in PHP

Before going to provide one page checkout for your shopping cart application, we have to know what it is and the significance behind its usage. This article will help you to know about it and its advantages.

Also, I have created an example script in PHP for the implementation of this one page checkout. In a previous article, we have seen a simple PHP shopping cart code and this is its enhanced version.

What is inside?

  1. What is one page checkout?
  2. About this example
  3. One page checkout example demo
  4. PHP checkout example files
  5. Creating one page checkout template
  6. jQuery script to handle cart events and prepare AJAX request
  7. Process checkout in PHP
  8. Enhancements
  9. One page template example output

What is one page checkout?

One page checkout is a way to make the eCommerce checkout process easier. It reduces the navigation effort, page redirects. It gets the job done by retaining the buyer in a single page.

This makes the checkout experience easier with a single checkout click. We will not compromise any feature to implement one page checkout. It may include add-to-cart control, customer’s cart, payment and shipping and more.

Checkout Cart Abandonment Stats

Source: Baymard Institute

About this example

This example is for creating a one page checkout in PHP. On a single page, I have covered the product gallery and the entire cart flow. It is a lightweight script.

The gallery will show the product tiles with an add-to-cart option. Then the added cart items will have options to edit and delete.

I used jQuery AJAX for requesting the server-side code to perform the cart actions. We have already seen how to perform cart edit with AJAX.

The cart detail table will also a preview for confirmation before checkout. The checkout will need the mandatory billing details customer name and email.

A simple JavaScript validates the billing and payment form. Once the validation step got passed then it will allow the continuing process to checkout in PHP.

One page checkout example demo

I have deployed a demo for this one-page checkout example.

The below button link will redirect you to see this demo. It has the following functionality.

  • Add-to-cart
  • Edit cart item quantity
  • Remove a single item
  • Empty cart
  • Checkout

You can try all the above with this demo.

View Demo

PHP checkout example files

This is the complete file structure on this PHP one-page checkout example.

The figure shows the PHP endpoints, CSS and JS assets, images, data and vendor directories. It also shows the home and other HTML views and PHP classes.

In the following sections, we will see the file’s code and its purpose.

One Page Checkout Example Files

Creating one page checkout template

The one-page checkout template includes three major parts. Those are the gallery, cart and the billing & payment form.

I have created separate files for these three display components. Then, I included all into a landing page that is index.php.

index.php

<?php session_start(); if (isset($_POST["checkout-btn"])) { $order_number = rand(100, 999); } ?> <HTML> <HEAD> <TITLE>One Page Checkout Script Free Template</TITLE> <link href="./assets/css/phppot-style.css" type="text/css" rel="stylesheet" /> <link href="./assets/css/one-page-checkout.css" type="text/css" rel="stylesheet" /> <script src="./vendor/jquery/jquery.min.js" type="text/javascript"></script> <script src="./vendor/jquery/jquery-ui.js"></script> </HEAD> <BODY> <div class="phppot-container"> <div class="page-heading">One Page Checkout Script Free Template</div> <form name="one-page-checkout-form" id="one-page-checkout-form" action="" method="post" onsubmit="return checkout()"> <?php if(!empty($order_number)){?> <div class="order-message order-success"> You order number is <?php echo $order_number;?>. <span class="btn-message-close" onclick="this.parentElement.style.display='none';" title="Close">×</span> </div> <?php }?> <div class="section product-gallery"> <?php require_once './view/product-gallery.php'; ?> </div> <div class="billing-details"> <?php require_once './view/billing-details.php'; ?> </div> <div class="cart-error-message" id="cart-error-message">Cart must not be emty to checkout</div> <div id="shopping-cart" tabindex="1"> <div id="tbl-cart"> <div id="txt-heading"> <div id="cart-heading">Your Shopping Cart</div> <div id="close"></div> </div> <div id="cart-item"> <?php require_once './view/shopping-cart.php'; ?> </div> </div> </div> <div class="payment-details"> <div class="payment-details-heading">Payment details</div> <div class="row"> <div class="inline-block"> <div> <input class="bank-transfer" type="radio" checked="checked" value="Direct bank transfer" name="direct-bank-transfer">Direct bank transfer </div> <div class="info-label">Specify your order number when you make the bank transfer. Your order will be shippied after the amount is credited to us.</div> </div> </div> </div> <div class="row"> <div id="inline-block"> <input type="submit" class="checkout" name="checkout-btn" id="checkout-btn" value="Checkout"> </div> </div> </form> </div> <script src="./assets/js/cart.js"></script> <script> function checkout() { var valid = true; $("#first-name").removeClass("error-field"); $("#email").removeClass("error-field"); $("#shopping-cart").removeClass("error-field"); $("#cart-error-message").hide(); var firstName = $("#first-name").val(); var cartItem = $("#cart-item-count").val(); var email = $("#email").val(); var emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; $("#first-name-info").html("").hide(); $("#email-info").html("").hide(); if (firstName.trim() == "") { $("#first-name-info").html("required.").css("color", "#ee0000").show(); $("#first-name").addClass("error-field"); valid = false; } if (email == "") { $("#email-info").html("required").css("color", "#ee0000").show(); $("#email").addClass("error-field"); valid = false; } else if (email.trim() == "") { $("#email-info").html("Invalid email address.").css("color", "#ee0000").show(); $("#email").addClass("error-field"); valid = false; } else if (!emailRegex.test(email)) { $("#email-info").html("Invalid email address.").css("color", "#ee0000") .show(); $("#email").addClass("error-field"); valid = false; } if(cartItem == 0){ $("#cart-error-message").show(); $("#shopping-cart").addClass("error-field"); valid = false; } if (valid == false) { $('.error-field').first().focus(); valid = false; } return valid; } </script> </BODY> </HTML> 

Product gallery with Add-to-Cart button

This file will show a catalog to display available products in a gallery. I have shown three products in this example gallery.

These products are static from a PHP array. You can also include your database component to make it dynamic.

Each tile contains an add-to-cart button. It calls cartAction() jQuery function on its click event.

product-gallery.php

<div id="product-grid"> <div class="txt-heading">Choose your products</div> <div class="product-item"> <div class="product-image"> <img src="data/camera.jpg" id="<?php echo "3DcAM01";?>" class="product-img"> </div> <div> <strong><?php echo "FinePix Pro2 3D Camera";?></strong> </div> <div class="product-price"><?php echo "1500.00";?></div> <input type="button" id="add_<?php echo "3DcAM01";?>" value="Add to cart" class="btnAddAction" onClick="cartAction('add', '<?php echo "3DcAM01";?>','<?php echo "FinePix Pro2 3D Camera";?>','<?php echo "1500.00";?>')" /> </div> <div class="product-item"> <div class="product-image"> <img src="data/watch.jpg" id="<?php echo "wristWear03";?>" class="product-img"> </div> <div> <strong><?php echo "Luxury Ultra thin Wrist Watch";?></strong> </div> <div class="product-price"><?php echo "300.00";?></div> <input type="button" id="add_<?php echo "wristWear03";?>" value="Add to cart" class="btnAddAction" onClick="cartAction('add', '<?php echo "wristWear03";?>','<?php echo "Luxury Ultra thin Wrist Watch";?>','<?php echo "300.00";?>')" /> </div> <div class="product-item"> <div class="product-image"> <img src="data/laptop.jpg" id="<?php echo "LPN45";?>" class="product-img"> </div> <div> <strong><?php echo "XP 1155 Intel Core Laptop";?></strong> </div> <div class="product-price"><?php echo "800.00";?></div> <input type="button" id="add_<?php echo "LPN45";?>" value="Add to cart" class="btnAddAction" onClick="cartAction('add', '<?php echo "LPN45";?>','<?php echo "XP 1155 Intel Core Laptop";?>','<?php echo "800.00";?>')" /> </div> </div> 

Shopping cart items with edit delete

I used the PHP session for managing the cart items. The below code shows an HTML code by embedding the cart details into it from the session array.

The cart must have at least one item to proceed with checkout. So I stored the total cart item count in a hidden field. I update this field after every successful cart action.

When you submit with an empty cart, then this example will prevent checkout. It will display validation error by focusing on the empty cart section.

shopping-cart.php

<?php use Phppot\Cart; require_once __DIR__ . './../Model/Cart.php'; $cartModel = new Cart(); ?> <input type="hidden" id="cart-item-count" value="<?php echo $cartModel->cartSessionItemCount; ?>"> <?php if ($cartModel->cartSessionItemCount > 0) { ?> <table width="100%" id="cart-table" cellpadding="10" cellspacing="1" border="0"> <tbody> <tr> <th>Name</th> <th>Quantity</th> <th class="text-right">Price</th> <th class="text-right">Action</th> </tr> <?php $item_total = 0; $i = 1; foreach ($_SESSION["cart_item"] as $item) { ?> <tr> <td><?php echo $item["name"]; ?></td> <td><input type="number" name="quantity" class="quantity" value="<?php echo $item['quantity']; ?>" data-code='<?php echo $item["code"]; ?>' size=2 onChange="updatePrice(this)" /> <input type="hidden" class='total' name="total" value="<?php echo $item["price"]; ?>" /></td> <td class="prc text-right" id="price" <?php echo $i;?>><?php echo $item["price"]; ?></td> <?php $i++; ?> <td class="text-right"><a onClick="cartAction('remove','<?php echo $item["code"]; ?>')" class="btnRemoveAction"><img src="./view/images/icon-delete.png" alt="Remove Item" /></a></td> </tr> <?php $item_total += ($item["price"] * $item['quantity']); } ?> <tr id="tot"> <td class="text-right" colspan="3"><strong>Total (USD): </strong> <span id="total"><?php echo $item_total;?></span></td> <td class="text-right"><a id="btnEmpty" onClick="cartAction('empty', '');">Empty Cart</a></td> </tr> </tbody> </table> <?php } else { ?> <div id="empty-cart">Your cart is empty</div> <?php } 

This HTML code is for displaying the billing and payment form to the user. The user has to enter the billing details and select the payment method before checkout.

In this example, I have provided only one payment option, Bank Transfer. At the time of processing payment via direct bank transfer, the users refer to the order number.

billing-details.php

<div class="billing-detail-heading">Billing details</div> <div class="row"> <div class="form-label inline-block"> Name <span class="required error"></span> </div> <div class="inline-block"> <div id="first-name-info"></div> <input class="input-box-330" type="text" name="first-name" id="first-name"> </div> </div> <div class="row"> <div class="form-label">Address</div> <div class="inline-block"> <input class="input-box-330" class="input-box-330" type="text" name="address"> </div> </div> <div class="row"> <div class="form-label">City</div> <div class="inline-block"> <input class="input-box-330" type="text" name="city"> </div> </div> <div class="row"> <div class="form-label">State</div> <div class="inline-block"> <input class="input-box-330" type="text" name="state"> </div> </div> <div class="row"> <div class="form-label">Zipcode</div> <div class="inline-block"> <input class="input-box-330" type="text" name="zipcode"> </div> </div> <div class="row"> <div class="form-label"> Email Address<span class="required error"></span> </div> <div class="inline-block"> <div id="email-info"></div> <input class="input-box-330" type="text" name="email" id="email"> </div> </div> 

jQuery script to handle cart events and prepare AJAX request

The cartAction() method is for handling the add, edit, remove and empty-cart actions. It has a switch case to perform these actions.

It receives the action parameter and sends the control flow to the appropriate case. It also gets the product title, code and price.

I prepare a query string by using the product details and request PHP via AJAX. After performing the cart action, the PHP code will return the response.

In the success block, we can receive this response for updating the cart.

cart.js

function cartAction(action, product_code, productTitle, productPrice) { var queryString = ""; if (action != "") { switch (action) { case "add": queryString = 'action=' + action + '&code=' + product_code + '&quantity=' + 1 + '&productTitle=' + productTitle + '&productPrice=' + productPrice; break; case "remove": queryString = 'action=' + action + '&code=' + product_code; break; case "empty": queryString = 'action=' + action; break; } } jQuery.ajax({ url : "ajax/handle-cart-ep.php", data : queryString, type : "POST", success : function(data) { $("#cart-item").html(data); $("#count").text($("#cart-item-count").val()); }, error : function() { } }); } function updatePrice(obj) { var quantity = $(obj).val(); var code = $(obj).data('code'); queryString = 'action=edit&code=' + code + '&quantity=' + quantity; $.ajax({ type : 'post', url : "ajax/handle-cart-ep.php", data : queryString, success : function(data) { $("#total").text(data); } }); } 

Process checkout in PHP

This is the PHP endpoint called via AJAX. As like as the JavaScript method, this PHP file also contains a switch case to control the cart action.

The Cart.php included in this file is a Model class that executes all the cart operations. 

In this endpoint, I instantiate this model and invoke its methods based on the request.

On each cart action, it iterates the latest cart session to prepare the response.

ajax/handle-cart-ep.php

<?php namespace Phppot; use \Phppot\Cart; require_once __DIR__ . './../Model/Cart.php'; $cartModel = new Cart(); session_start(); if (! empty($_POST["action"])) { switch ($_POST["action"]) { case "add": $cartModel->addToCart(); break; case "edit": $totalPrice = $cartModel->editCart(); print $totalPrice; exit; break; case "remove": $cartModel->removeFromCart(); break; case "empty": $cartModel->emptyCart(); break; } } require_once '../view/shopping-cart.php'; 

The below code shows the Cart class. It handles and updates the cart session on each cart action.

The emptyCart() method wipes out the cart by clearing the current session.

Model/Cart.php

<?php namespace Phppot; class Cart { public $cartSessionItemCount = 0; function __construct() { if (! empty($_SESSION["cart_item"]) && is_array($_SESSION["cart_item"])) { $this->cartSessionItemCount = count($_SESSION["cart_item"]); } } function addToCart() { if (isset($_POST)) { $productCode = $_POST["code"]; $productTitle = $_POST["productTitle"]; $poductQuantity = $_POST["quantity"]; $productPrice = $_POST["productPrice"]; } $cartItem = array( 'code' => $productCode, 'name' => $productTitle, 'quantity' => $poductQuantity, 'price' => $productPrice ); $_SESSION["cart_item"][$productCode] = $cartItem; if (! empty($_SESSION["cart_item"]) && is_array($_SESSION["cart_item"])) { $this->cartSessionItemCount = count($_SESSION["cart_item"]); } } function editCart() { if (! empty($_SESSION["cart_item"])) { $total_price = 0; foreach ($_SESSION["cart_item"] as $k => $v) { if ($_POST["code"] == $k) { $_SESSION["cart_item"][$k]["quantity"] = $_POST["quantity"]; } $total_price = $total_price + ($_SESSION["cart_item"][$k]["quantity"] * $_SESSION["cart_item"][$k]["price"]); } return $total_price; } if (! empty($_SESSION["cart_item"]) && is_array($_SESSION["cart_item"])) { $this->cartSessionItemCount = count($_SESSION["cart_item"]); } } function removeFromCart() { if (! empty($_SESSION["cart_item"])) { foreach ($_SESSION["cart_item"] as $k => $v) { if ($_POST["code"] == $k) unset($_SESSION["cart_item"][$k]); if (empty($_SESSION["cart_item"])) unset($_SESSION["cart_item"]); } } if (! empty($_SESSION["cart_item"]) && is_array($_SESSION["cart_item"])) { $this->cartSessionItemCount = count($_SESSION["cart_item"]); } } function emptyCart() { unset($_SESSION["cart_item"]); $this->cartSessionItemCount = 0; } }

Enhancements

In this example code, there are places to bring the database into the picture. For example, the product gallery and the cart.

I have managed the gallery with a PHP array. And, I handled the cart session with PHP $_SESSION.

If you manage the shopping cart with a database, then it will help you to have a persistent cart. I have already given an example of building a persistent shopping cart in PHP.

You can plugin your database module into this example code to store products. If you want to show your shopping cart products gallery from a database, this link has an example.

One page template example output

See the screenshot shows the product gallery, cart and checkout on one page. The gallery shows three product tiles.

By clicking the Add to cart button, the cart will have a new entry as shown below.

The Checkout button click will place the order for the added cart items.

One Page Checkout Example Output

View DemoDownload

↑ Back to Top

Posted on Leave a comment

PHP Pagination MySQL Database Example Script with Previous Next like Google

Last modified on December 5th, 2019 by Vincy.

Do you want to implement PHP pagination using core PHP with lightweight script? Jump in, this tutorial is all about it.

The pagination functionality is for stacking a list of items on many pages. It helps to fetch and display a huge volume of records in an effective way.

If you want to implement a simple PHP pagination, then it is very easy and straight forward.

PHP Pagination Script with Previous Next

There is just one thing that you should keep in mind when you work on pagination. Do not pull the complete result set and filter it to display to the users.

SELECT only records using LIMIT from database. It can be done via AJAX or on page submit depending on your use case. We will see more about it in the example below. 

There are scenarios to create pagination with AJAX. For example, an online quiz with a paginated question board. We have seen more examples to create PHP pagination with AJAX.

What is inside?

  1. About this example
  2. Plugins to enable pagination feature
  3. File structure
  4. Database script
  5. Design landing page with pagination controls
  6. Php code to get paginated results
  7. PHP AJAX pagination Output

About this example

In this example code, it has two types of control to navigate with the paginated results.

One is the pagination links to redirect us to the corresponding page. The other is an input to enter the page number. By submitting this page number we can jump into the target page from the current page.

I have implemented PHP pagination with a simple page refresh. We have already seen examples for achieving this with AJAX using jQuery.

I have enabled pagination for the list of database results displaying Animal names. The pagination links count varies based on the animal database record count. 

A SELECT query with a specification of start and end limit fetch the page result. The per-page result count is configurable as a Config class constant. 

There are many plugins available to create pagination easily. Pagination.js and jqPaginator are examples of 3-party plugins available in the market.

The Datatables is one of the popular library used for managing tabular data. It has an in-built search, pagination, sort, and more features. In an old article, we had seen how to integrate Datatables to list records with pagination.

Having said all these, better to go with custom code. Because external plugins, libraries will slow down the application.

File structure

The working example code contains the following files in the below hierarchical order.

AJAZ Pagination File Structure

The index.php file is a landing page. It contains the HTML to display pagination links and the current page results.

Two CSS used in this example. The phppot-style.css has generic layout styles. The pagination.css has the exclusive styles created for this example.

Model/Pagination.php includes functions to fetch the MySQL data. It receives parameters to set the limit for the SELECT query.

The file structure includes a SQL script to set up the database for running this example.

Database script

-- -- Database: `pagination` -- -- -------------------------------------------------------- -- -- Table structure for table `tbl_animal` -- CREATE TABLE `tbl_animal` ( `id` int(11) UNSIGNED NOT NULL, `common_name` varchar(255) NOT NULL DEFAULT '', `scientific_name` varchar(255) NOT NULL DEFAULT '' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- -- Dumping data for table `tbl_animal` -- INSERT INTO `tbl_animal` (`id`, `common_name`, `scientific_name`) VALUES (1, 'Bison', 'Bos gaurus\r\n'), (2, 'Black buck', 'Antelope cervicapra'), (3, 'Chinkara', 'Gazella bennettii'), (4, 'Nilgai', 'Boselaphus tragocamelus'), (5, 'Wolf', 'Canis lupus'), (6, 'Lion', 'Panthera leo'), (7, 'Elephant', 'Elephas maximus'), (8, 'Wild Ass', 'Equus africanus asinus'), (9, 'Panther', 'Panthera pardus'), (10, 'Kashmir stag', 'Cervus canadensis hanglu'), (11, 'Peacock', 'Pavo cristatus'), (12, 'Siberian crane', 'Grus leucogeranus'), (13, 'Fox', 'Vulpes vulpes'), (14, 'Rhinoceros', 'Rhinoceros unicornis'), (15, 'Tiger', 'Panthera Tigris'), (16, 'Crocodile', 'Crocodylus palustris'), (17, 'Gavial or Gharial', 'Gavialis gangeticus'), (18, 'Horse', 'Equus caballus'), (19, 'Zebra', 'Equus quagga'), (20, 'Buffalow', 'Babalus bubalis'), (21, 'Wild boar', 'Sus scrofa'), (22, 'Arabian camel', 'Camelus dromedaries'), (23, 'Giraffe', 'GiraffaÊcamelopardalis'), (24, 'House wall Lizard', 'Hemidactylus flaviviridis'), (25, 'Hippopotamus', 'Hippopotamus amphibius'), (26, 'Rhesus monkey', 'Macaca mulatta'), (27, 'Dog', 'Canis lupus familiaris'), (28, 'Cat', 'Felis domesticus'), (29, 'Cheetah', 'Acinonyx jubatus'), (30, 'Black rat', 'Rattus rattus'), (31, 'House mouse', 'Mus musculus'), (32, 'Rabbit', 'Oryctolagus cuniculus'), (33, 'Great horned owl', 'Bubo virginianus'), (34, 'House sparrow', 'Passer domesticus'), (35, 'House crow', 'Corvus splendens'), (36, 'Common myna', 'Acridotheres tristis'), (37, 'Indian parrot', 'Psittacula eupatria'), (38, 'Bulbul', 'Molpastes cafer'), (39, 'Koel', 'Eudynamis scolopaccus'), (40, 'Pigeon', 'Columba livia'), (41, 'Indian Cobra', 'Naja naja'), (42, 'King cobra', 'Ophiophagus hannah'), (43, 'Sea snake', 'Hydrophiinae'), (44, 'Indian python', 'Python molurus'), (45, 'Rat snake', 'Rat snake'); -- -- Indexes for dumped tables -- -- -- Indexes for table `tbl_animal` -- ALTER TABLE `tbl_animal` ADD PRIMARY KEY (`id`); -- -- AUTO_INCREMENT for dumped tables -- -- -- AUTO_INCREMENT for table `tbl_animal` -- ALTER TABLE `tbl_animal` MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=46; 

Designing landing page with pagination controls

The landing page shows the database results with a pagination option. The following HTML has the embedded PHP script to display paginated results.

It contains table rows created in a loop on iterating the data array. Below this table, it shows pagination links with previous next navigation. This navigation allows moving back and forth from the current page.

This example also contains a HMTL form to enter the target page number. If you enter a number within the number of pages, then it will get the appropriate page result.

<!doctype html> <html lang="en"> <head> <title>PHP Pagination MySQL Database Example Script with Previous Next like Google</title> <link rel="stylesheet" type="text/css" href="assets/css/pagination.css"> <link rel="stylesheet" type="text/css" href="assets/css/phppot-style.css"> <script src="vendor/jquery/jquery-3.3.1.js"></script> </head> <body> <div class="phppot-container"> <div class="phppot-form"> <h1>Animal Names</h1> <table> <tr> <th>Id</th> <th>Common Name</th> <th>Scientific Name</th> </tr> <?php if (! empty($pageResult)) { foreach ($pageResult as $page) { ?> <tr> <td><?php echo $page['id'];?></td> <td><?php echo $page['common_name'];?></td> <td><?php echo $page['scientific_name'];?></td> </tr> <?php }} ?> </table> <div class="pagination"> <?php if (($page > 1) && ($pn > 1)) { ?> <a class="previous-page" id="prev-page" href="<?php echo $queryString;?>page=<?php echo (($pn-1));?>" title="Previous Page"><span>❮ Previous</span></a> <?php }?> <?php if (($pn - 1) > 1) { ?> <a href='index.php?page=1'><div class='page-a-link'> 1 </div></a> <div class='page-before-after'>...</div> <?php } for ($i = ($pn - 1); $i <= ($pn + 1); $i ++) { if ($i < 1) continue; if ($i > $totalPages) break; if ($i == $pn) { $class = "active"; } else { $class = "page-a-link"; } ?> <a href='index.php?page=<?php echo $i; ?>'> <div class='<?php echo $class; ?>'><?php echo $i; ?></div> </a> <?php } if (($totalPages - ($pn + 1)) >= 1) { ?> <div class='page-before-after'>...</div> <?php } if (($totalPages - ($pn + 1)) > 0) { if ($pn == $totalPages) { $class = "active"; } else { $class = "page-a-link"; } ?> <a href='index.php?page=<?php echo $totalPages; ?>'><div class='<?php echo $class; ?>'><?php echo $totalPages; ?></div></a> <?php } ?> <?php if (($page > 1) && ($pn < $totalPages)) { ?> <a class="next" id="next-page" href="<?php echo $queryString;?>page=<?php echo (($pn+1));?>" title="Next Page"><span>Next ❯</span></a> <?php } ?> </div> <div class="goto-page"> <form action="" method="GET" onsubmit="return pageValidation()"> <input type="submit" class="goto-button" value="Go to"> <input type="text" class="enter-page-no" name="page" min="1" id="page-no"> <input type="hidden" id="total-page" value="<?php echo $totalPages;?>"> </form> </div> </div> </div> </body> </html> 

The pageValidation() is a JavaScript method to confirm the entered page number. If the page number input contains invalid data, then this script will return false. Then it stops form submit and thereby preventing the page-jump action.

<script> function pageValidation() { var valid=true; var pageNo = $('#page-no').val(); var totalPage = $('#total-page').val(); if(pageNo == ""|| pageNo < 1 || !pageNo.match(/\d+/) || pageNo > parseInt(totalPage)){ $("#page-no").css("border-color","#ee0000").show(); valid=false; } return valid; } </script> 

The following styles are from the pagination.css. It defines the CSS for the table view and the pagination elements.

.pagination { display: inline-flex; } .pagination a { color: #505050; text-decoration: none; } .page-a-link { font-family: arial, verdana; font-size: 12px; border: 1px #afafaf solid; background-color: #fbfbfb; padding: 6px 12px 6px 12px; margin: 6px; text-decoration: none; border-radius: 3px; } .active { font-family: arial, verdana; font-size: 12px; padding: 8px 14px 6px 14px; margin: 3px; background-color: #404040; text-decoration: none; border-radius: 3px; margin: 6px; color: #FFF; } a.previous-page { margin: 10px 10px 10px 0px; } a.prev-next:hover { color: #03a9f4; } a.next { margin: 10px 0px 10px 10px; } input.enter-page-no { width: 42px !important; height: 28px !important; font-size: 12px; padding: 6px 12px 6px 12px !important; margin: 6px; border-radius: 3px !important; text-align: center !important; } input.goto-button { max-width: 80px; font-size: 12px; padding: 6px 12px 6px 12px !important; border: 1px solid #9a9a9a; border-radius: 3px !important; text-align: center !important; background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #dfdd99), color-stop(100%, #bcbd2b)); background: -webkit-linear-gradient(top, #dfdc99, #b8bd2b); border: 1px solid #97a031; box-shadow: inset 0px 1px 0px rgb(255, 255, 211), 0px 1px 4px rgba(199, 199, 199, 0.9); } .goto-page { float: right; } .page-before-after { font-weight: bold; padding-top: 12px; text-decoration: none; } 

Php code to get paginated results

This PHP script is for reading the database results and get the record count, pages count. The pages count varies based on the record count and the LIMIT_PER_PAGE configuration.

If there are more pages, this example will show only limited pages as like as Google pagination. This feature will help to make the pagination block to fit into the viewport.

On successive page navigation, you will get more pagination links to browse further.

<?php namespace Phppot; require_once __DIR__ . '/Model/Pagination.php'; $paginationModel = new Pagination(); $pageResult = $paginationModel->getPage(); $queryString = "?"; if (isset($_GET["page"])) { $pn = $_GET["page"]; } else { $pn = 1; } $limit = Config::LIMIT_PER_PAGE; $totalRecords = $paginationModel->getAllRecords(); $totalPages = ceil($totalRecords / $limit); ?> 

PHP classes, libraries, configs used in this example

This is a common DataSource library used in most examples. Here, I have shown the functions that are only used in this example. 

You can get the full code by downloading the source added to this article.

Datasource.php

<?php /** * Copyright (C) 2019 Phppot * * Distributed under MIT license with an exception that, * you don’t have to include the full MIT License in your code. * In essense, you can use it on commercial software, modify and distribute free. * Though not mandatory, you are requested to attribute this URL in your code or website. */ namespace Phppot; /** * Generic datasource class for handling DB operations. * Uses MySqli and PreparedStatements. * * @version 2.5 - recordCount function added */ class DataSource { // PHP 7.1.0 visibility modifiers are allowed for class constants. // when using above 7.1.0, declare the below constants as private const HOST = 'localhost'; const USERNAME = 'root'; const PASSWORD = 'test'; const DATABASENAME = 'pagination'; private $conn; /** * PHP implicitly takes care of cleanup for default connection types. * So no need to worry about closing the connection. * * Singletons not required in PHP as there is no * concept of shared memory. * Every object lives only for a request. * * Keeping things simple and that works! */ function __construct() { $this->conn = $this->getConnection(); } /** * If connection object is needed use this method and get access to it. * Otherwise, use the below methods for insert / update / etc. * * @return \mysqli */ public function getConnection() { $conn = new \mysqli(self::HOST, self::USERNAME, self::PASSWORD, self::DATABASENAME); if (mysqli_connect_errno()) { trigger_error("Problem with connecting to database."); } $conn->set_charset("utf8"); return $conn; } /** * To get database results * * @param string $query * @param string $paramType * @param array $paramArray * @return array */ public function select($query, $paramType = "", $paramArray = array()) { $stmt = $this->conn->prepare($query); if (! empty($paramType) && ! empty($paramArray)) { $this->bindQueryParams($stmt, $paramType, $paramArray); } $stmt->execute(); $result = $stmt->get_result(); if ($result->num_rows > 0) { while ($row = $result->fetch_assoc()) { $resultset[] = $row; } } if (! empty($resultset)) { return $resultset; } } /** * To insert * * @param string $query * @param string $paramType * @param array $paramArray * @return int */ public function insert($query, $paramType, $paramArray) { //... } /** * To execute query * * @param string $query * @param string $paramType * @param array $paramArray */ public function execute($query, $paramType = "", $paramArray = array()) { //... } /** * 1. * Prepares parameter binding * 2. Bind prameters to the sql statement * * @param string $stmt * @param string $paramType * @param array $paramArray */ public function bindQueryParams($stmt, $paramType, $paramArray = array()) { $paramValueReference[] = & $paramType; for ($i = 0; $i < count($paramArray); $i ++) { $paramValueReference[] = & $paramArray[$i]; } call_user_func_array(array( $stmt, 'bind_param' ), $paramValueReference); } /** * To get database results * * @param string $query * @param string $paramType * @param array $paramArray * @return array */ public function getRecordCount($query, $paramType = "", $paramArray = array()) { $stmt = $this->conn->prepare($query); if (! empty($paramType) && ! empty($paramArray)) { $this->bindQueryParams($stmt, $paramType, $paramArray); } $stmt->execute(); $stmt->store_result(); $recordCount = $stmt->num_rows; return $recordCount; } } 

Config.php

This is the config file added for this example. I kept the per-page record limit as an application constant. I used this for setting the SELECT query limit.

You can add more constants and fine-tune the PHP pagination script to work based on them. For example, create new constant SHOW_ALL_LINKS. Set it on/off to enable/disable the Google-like feature to show limited links. 

<?php namespace Phppot; class Config { const LIMIT_PER_PAGE = '5'; } 

This screenshot shows the output of the PHP pagination example code. It shows five records per page. 

On the left, it shows usual pagination with previous next and separate page links. On the right side, it is a HMTL form to allow the user to enter the page number.

PHP pagination MySQL Example Output

See Also

These links have a variety of pagination scripts.

  1. PHP CRUD with search and pagination
  2. Pagination with Tabular records
  3. How to create Facebook-link infinite scroll pagination

Conclusion

Thus, we created PHP pagination with Google-like limited links and previous next links. We have seen the interlinking of older PHP pagination script along with this article.

We have discussed the third-party libraries that contain in-built pagination feature. It will be helpful to have a choice if are searching for such plugins.

In this custom PHP pagination example, we have provided an option to jump into a target page directly via a form. Sometimes, it itself is enough with previous next navigation.

Hope this article and the example help you to build a nice simple PHP pagination for your web application.

Download

↑ Back to Top

Posted on Leave a comment

How to Integrate 2Checkout Payment Gateway using PHP

Last modified on November 27th, 2019 by Vincy.

Sales, revenue, ROI are the key metrics to measure a business growth. The payment gateway is a sensitive factor that affects these metrics directly. It is the first and foremost item on the checklist for any eCommerce business.

There are many Payment Gateway providers. PayPal being the popular provider and 2Checkout is catching out fast. Earlier I wrote about how to integrate PayPal checkout in ecommerce website. In this payment gateway integration series, now it is time to see about 2Checkout. 

2Checkout Payment Gateway Integration using-php PHP

2Checkout is one of the best payment gateway provider to support card payments. It works similar to Stripe payment gateway. Card payments has less friction and will enable to increase the conversion rate in your eCommerce business.

2Checkout is growing everyday in popularity. It is available in more than 180 countries and used by 17,000 firms.

To integrate 2Checkout to your ecommerce application there are few simple steps. I have described those steps in this article to make your job easier.

Also, we will see the card validator script, payment request-response handlers and more.

What is inside?

  1. Existing payment gateway methods supporting card payment
  2. Advantages of 2Checkout payment
  3. File structure
  4. Steps to integrate 2Checkout payment gateway using PHP
  5. Database script
  6. Go live
  7. 2Checkout Payment gateway example output

Existing payment gateway methods supporting card payment

I have been writing about payment gateways for a while We have already seen many types of payment gateway integration using PHP. I already written tutorials for CCAvenue, PayPal, Stripe, SagePay and Authorize.Net

2Checkout, Stripe, CCAvenue are some of the examples for the methods supporting card-payments. There are many methods like InstaMojo, Cashfree, EBS and Paytm support card-payments.

Advantages of 2Checkout payment

  • Easy to integrate using payment API libraries in PHP, PYTHON and more languages.
  • It allows customers to opt for a variety of payment methods via PayPal account or debit/credit cards.
  • 2Checkout is a secured fraud-proof for selling products or subscription plans online.
  • It provides a localized buying experience for the customer.
  • It supports many currencies that supports global payments.
  • Integratable with the eCommerce tools like Shopify, Bigcommerce.

File structure

This image shows the file structure of the 2Checkout payment gateway PHP example. The root files index.php, payment.php, return.php are in a sequential flow during the payment.

While running this example, it lands with the index.php to show the payment form. Form targets payment.php that processes payment and redirects to the return page.

The redirect URL will have the payment response status parameter. It helps to create a response HTML on the return page. 

The assets folder has the CSS, JS created for this example. TwoCheckoutService.php contains the library includes to verify card details and process payment.

You can see all the third-party components in the vendor directory.

2Checkout Payment File Structure

Steps to integrate 2Checkout payment gateway using PHP

  1. Download the 2Checkout payment API library in PHP.
  2. Create a 2Checkout account and generate API keys.
  3. Create a payment form to collect the card details from the customer.
  4. Generate payment API token and transfer the card details in a secure way.
  5. Verify card details and process charges with 2Checkout Payment API.

Step 1: Download 2Checkout payment gateway

2Checkout payment API library has many features. It helps to manage purchase flow, orders, invoices, billing and more. It simplifies the integration process and reduces the developer’s effort.

Download the 2Checkout payment API library from GitHub. And then, put it into your application vendor directory.

While integrating Stripe Billing, we saw how to integrate third-party API for payment.

Include the TwoCheckout.php service before verifying card details and process payments. This file includes all other library dependencies. It handles card validation and charges on the server-side.  

In this example, the Checkout.php use this library file to perform the payment operations.

Step 2: Creating 2Checkout sandbox account and generate API keys

2Checkout provides a sandbox environment to check payment flow using the API functions. 

This is a common practice to go with a sandbox mode for testing purposes. Then we can go live after verifying the payment flow with this mode.

Let us create a 2Checkout sandbox account via the developer console. By logging into the console, we can get our API keys.

  1. Register and log in to the 2Checkout sandbox account.
  2. Select the API menu from the header and click the Start Integrating button.
  3. Go to the Settings tab to get the API keys.
  4. Copy the Publishable Key and Private Key in the key generation section of the page.

2Checkout Payment API Keys

Click the Generate button if you are not able to see the API keys.

I have added the keys and seller account id in the application config name Config.php.

After configuring the API keys, use the test data to make the payment in the sandbox mode. You can get the test data from the API page as shown below.

2Checkout Sandbox Card Details

Step 3: Create 2Checkout payment form in HTML

This form contains input fields to get the basic credit card details from the buyer. Those are the cardholder’s name and email, card number, CVC, card expiry tenure.

It will also ask the billing address from the buyer.

In this payment form, it has hidden fields for keeping the seller account id and product price, code.

<?php namespace Phppot; use Phppot\Config; require_once "Config.php"; $productCode = "WWPS235"; ?> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="./assets/css/style.css" rel="stylesheet" type="text/css"> <title>2checkout-payment-gateway-integration-using-PHP</title> </head> <body> <div id="formContainer"> <?php $productDetail = Config::productDetail(); $itemName = $productDetail[$productCode]["itemName"]; $itemPrice = $productDetail[$productCode]["itemPrice"]; ?> <div class="product-row"> <p class="product"><?php echo $itemName; ?></p> <div class="price float-right"><?php echo $itemPrice; ?> <?php echo Config::CURRENCY; ?> </div> </div> <form id="paymentForm" novalidate method="post" action="payment.php"> <input type="hidden" id="itemNumber" name="itemNumber" value="<?php echo $productCode; ?>" /> <input type="hidden" id="itemPrice" name="itemPrice" value="<?php echo $itemPrice; ?>" /> <input type="hidden" id="seller_id" value="<?php echo Config::SELLER_ID; ?>" /> <input type="hidden" id="publishable_key" value="<?php echo Config::PUBLISHABLE_KEY; ?>" /> <div class="field-row col2 float-left"> <label>Card Holder Name</label> <input type="text" class="demoInputBox required" name="cardHolderName" id="cardHolderName"> </div> <div class="field-row col2 float-right"> <label>Email</label> <input type="email" class="demoInputBox required" name="cardHolderEmail" id="cardHolderEmail"> </div> <div class="field-row"> <label>Card Number</label> <input type="text" class="demoInputBox required" name="cardNumber" id="cardNumber"> </div> <div class="field-row col2 float-left"> <label>Expiry Month / Year</label> <br /> <select name="expiryMonth" id="expiryMonth" class="demoSelectBox required"> <?php $months = Config::monthArray(); $count = count($months); for ($i = 0; $i < $count; $i ++) { $monthValue = $i + 1; if (strlen($i) < 2) { $monthValue = "0" . $monthValue; } ?> <option value="<?php echo $monthValue; ?>"><?php echo $months[$i]; ?></option> <?php } ?> </select> <select name="expiryYear" id="expiryYear" class="demoSelectBox required"> <?php for ($i = date("Y"); $i <= 2030; $i ++) { $yearValue = substr($i, 2); ?> <option value="<?php echo $yearValue; ?>"><?php echo $i; ?></option> <?php } ?> </select> </div> <div class="field-row"> <label>CVV</label><br /> <input type="text" name="cvv" id="cvv" class="demoInputBox cvv-input required"> </div> <p class="sub-head">Billing Address:</p> <div class="field-row col2 float-left"> <label>Address Line1</label> <input type="text" class="demoInputBox required" name="addressLine1" id="addressLine1"> </div> <div class="field-row col2 float-right"> <label>Address Line2</label> <input type="email" class="demoInputBox" name="addressLine2" id="addressLine2"> </div> <div class="field-row col2 float-left"> <label>Country</label> <input type="text" class="demoInputBox required" name="country" id="country"> </div> <div class="field-row col2 float-right"> <label>State</label> <input type="text" class="demoInputBox required" name="state" id="state"> </div> <div class="field-row col2 float-left"> <label>City</label> <input type="text" class="demoInputBox required" name="city" id="city"> </div> <div class="field-row col2 float-right"> <label>Zip</label> <input type="text" class="demoInputBox required" name="zip" id="zip"> </div> <div class="clear-float"> <input id="token" name="token" type="hidden" value=""> <input type="button" id="submit-btn" class="btnAction" value="Send Payment"> <div id="loader"> <img alt="loader" src="./images/LoaderIcon.gif" /> </div> </div><div id="error-message"></div> </form> </div> </body> </html> 

Javascript for credit card validation

Most of the fields are mandatory. The form validation script helps to verify user’s input before payment.

I have used the jQuery CreditCardValidator script to validate the card details. It will check the card number format, CVC, card expiration and more. 

Note: To pass the card validation, any three-digit code for CVC and a future date for card expiry.

function validate() { var valid = true; $(".demoInputBox").css('background-color', ''); var message = ""; var cardHolderNameRegex = /^[a-z ,.'-]+$/i; var cvvRegex = /^[0-9]{3,3}$/; var cardHolderName = $("#cardHolderName").val(); var cardHolderEmail = $("#cardHolderEmail").val(); var cardNumber = $("#cardNumber").val(); var cvv = $("#cvv").val(); $(".required").each(function() { if($(this).val()=='') { $(this).css('background-color', '#FFFFDF'); valid = false; } }); if(!valid) { message += "<div> Highlighted fields are required.</div>"; } if (cardHolderName != "" && !cardHolderNameRegex.test(cardHolderName)) { message += "<div>Card Holder Name is invalid</div>"; $("#cardHolderName").css('background-color', '#FFFFDF'); valid = false; } if (!cardHolderEmail.match( /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/)) { message += "<div>Email is invalid</div>"; $("#cardHolderEmail").css('background-color', '#FFFFDF'); valid = false; } if (cardNumber != "") { $('#cardNumber').validateCreditCard(function(result) { if (!(result.valid)) { message += "<div>Card Number is Invalid</div>"; $("#card-number").css('background-color', '#FFFFDF'); valid = false; } }); } if (cvv != "" && !cvvRegex.test(cvv)) { message += "<div>CVV is Invalid</div>"; $("#cvv").css('background-color', '#FFFFDF'); valid = false; } if (message != "") { $("#error-message").show(); $("#error-message").html(message); $("#submit-btn").show(); $("#loader").hide(); } return valid; } 

Step 4: 2Checkout JS to request token

This HTML code contains the 2co.js include at the end. This JS script is for getting TCO token by hitting the API’s tokenRequestURL. 

This request requires card details with seller account id, sandbox publishable key. It also specifies the name of the success/error callback functions.

The success callback gets the token and includes it to the HTML form. f anything went wrong, the error-callback handles the case and acknowledge the customer.

This token helps to authenticate the request before processing the payment. Without this token, the payment request will return error by saying unauthorized.

<!-- jQuery library --> <script src="vendor/jquery/jquery-3.2.1.min.js"></script> <script src="vendor/jquery-creditcardvalidator/jquery.creditCardValidator.js"></script> <script src="./assets/js/validation.js"></script> <!-- 2Checkout JavaScript library --> <script src="https://www.2checkout.com/checkout/api/2co.min.js"></script> <script> // A success callback of TCO token request var success = function (data) { // Set the token in the payment form $('#paymentForm #token').val(data.response.token.token); $("#error-message").hide(); $("#error-message").html(""); // Submit the form with TCO token $('#paymentForm').submit(); }; // A Error callback of TCO token request. var error = function (data) { var errorMsg = ""; if (data.errorCode === 200) { tokenRequest(); } else { errorMsg = data.errorMsg; $("#error-message").show(); $("#error-message").html(errorMsg); $("#submit-btn").show(); $("#loader").hide(); } }; function tokenRequest() { var valid = validate(); if (valid == true) { $("#submit-btn").hide(); $("#loader").css("display", "inline-block"); var args = { sellerId: $('#seller_id').val(), publishableKey: $('#publishable_key').val(), ccNo: $("#cardNumber").val(), cvv: $("#cvv").val(), expMonth: $("#expiryMonth").val(), expYear: $("#expiryYear").val() }; // Request 2Checkout token TCO.requestToken(success, error, args); } } $(function () { TCO.loadPubKey('sandbox'); $("#submit-btn").on('click', function (e) { tokenRequest(); return false; }); }); </script> 

Step 5: Verify card details and process charges with 2Checkout payment API

After submitting the tokenized form data, then PHP will handle the payment process. There are two major steps during the payment process authorization and charging card.

The payment.php file is the endpoint that verifies payment and charges the card. The below code states how to receive payment form data and request payment.

Before processing payment, this PHP code inserts order into the database table. In this step, it saves the product, currency, customer and billing data into the database.

After placing the order, the inserOrder method will return a unique reference ID. This refers to the merchantOrderId parameter while charging the card.

With a success response, it will update the transaction id and payment status in the order table.

In case of error, the error message returned by the API will acknowledge the customers.

<?php use Phppot\Config; use Phppot\Model\Checkout; session_start(); require_once 'Config.php'; // Check if token is not empty if (! empty($_POST['token'])) { $token = $_POST['token']; $currency = Config::CURRENCY; // Card information $card_num = $_POST['cardNumber']; $card_cvv = $_POST['cvv']; $card_exp_month = $_POST['expiryMonth']; $card_exp_year = $_POST['expiryYear']; // Customer information $customerDetail['name'] = $_POST['cardHolderName']; $customerDetail['email'] = $_POST['cardHolderEmail']; $billingAddress['addrLine1'] = $_POST['addressLine1']; $billingAddress['addrLine2'] = $_POST['addressLine2']; $billingAddress['city'] = $_POST['city']; $billingAddress['state'] = $_POST['state']; $billingAddress['zipCode'] = $_POST['zip']; $billingAddress['country'] = $_POST['country']; // Product information $product['itemNumber'] = $_POST["itemNumber"]; $product['itemPrice'] = $_POST["itemPrice"]; require_once 'Model/Checkout.php'; $checkout = new Checkout(); $orderID = $checkout->insertOrder($customerDetail, $billingAddress, $product); require_once 'Model/TwoCheckoutService.php'; $twoCheckoutService = new TwoCheckoutService(); $twoCheckoutService->verifyAuthentication(); $successMessage = ""; $errorMessage = ""; $paymentResponse = $twoCheckoutService->chargeCard($orderID, $token, $currency, $customerDetail, $billingAddress, $product['itemPrice']); if (! empty($paymentResponse["charge"])) { $charge = $paymentResponse["charge"]; if ($charge['response']['responseCode'] == 'APPROVED') { $transactionId = $charge['response']['transactionId']; $status = $charge['response']['responseCode']; $checkout->updatePayment($transactionId, $status, $orderID); header("Location: return.php?status=success&itemNumber=".$orderID); } else { $_SESSION["transaction_error"] = "Payment is waiting for approval."; header("Location: return.php?status=transaction_failed"); } } else if($paymentResponse["message"]) { if(!empty($paymentResponse["message"])) { $_SESSION["transaction_error"] = $paymentResponse["message"]; } header("Location: return.php?status=transaction_failed"); } } else { header("Location: return.php?status=invalid_token"); } ?> 

Model/Checkout.php

<?php /** * Copyright (C) 2019 Phppot * * Distributed under MIT license with an exception that, * you don’t have to include the full MIT License in your code. * In essense, you can use it on commercial software, modify and distribute free. * Though not mandatory, you are requested to attribute this URL in your code or website. */ namespace Phppot\Model; use Phppot\DataSource; use Phppot\Config; class Checkout { private $ds; function __construct() { require_once __DIR__ . './../lib/DataSource.php'; $this->ds = new DataSource(); } /** * to get the member record based on the subscription_key * * @param string $subscriptionKey * @return array result record */ public function getOrder($orderId) { $query = 'SELECT * FROM tbl_order where id = ?'; $paramType = 'i'; $paramValue = array( $orderId ); $result = $this->ds->select($query, $paramType, $paramValue); return $result; } public function insertOrder($customerDetail, $billingAddress, $product) { $current_time = date("Y-m-d H:i:s"); $query = 'INSERT INTO tbl_order (name, email, item_code, item_price, currency, address_line_1, address_line_2, country, state, city, zip, create_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; $paramType = 'sssissssssss'; $paramValue = array( $customerDetail["name"], $customerDetail["email"], $product["itemNumber"], $product["itemPrice"], Config::CURRENCY, $billingAddress['addrLine1'], $billingAddress['addrLine2'], $billingAddress['country'], $billingAddress['state'], $billingAddress['city'], $billingAddress['zipCode'], $current_time ); $insertStatus = $this->ds->insert($query, $paramType, $paramValue); return $insertStatus; } public function updatePayment($transactionId, $paymentStatus, $orderID) { $query = "UPDATE tbl_order set txn_id = ?, payment_status = ? WHERE id = ?"; $paramType = 'ssi'; $paramValue = array( $transactionId, $paymentStatus, $orderID ); $this->ds->execute($query, $paramType, $paramValue); } } 

Model/TwoCheckoutService.php

<?php use Phppot\Config; require_once("vendor/2checkout-php-master/lib/Twocheckout.php"); class TwoCheckoutService { function verifyAuthentication() { Twocheckout::verifySSL(false); // Set API key Twocheckout::privateKey(Config::PRIVATE_KEY); // PRIVATE_KEY defined in config.php Twocheckout::sellerId(Config::SELLER_ID); // SELLER_ID defined in config.php Twocheckout::sandbox(true); } function chargeCard($orderID, $token, $currency, $customerDetail, $billingAddress, $itemPrice) { $successMessage = ""; $errorMessage = ""; try { // an array is created with customer sale parameters and passed it in auth() function of Twocheckout_Charge class for authorization. $charge = Twocheckout_Charge::auth(array( "merchantOrderId" => $orderID, "token" => $token, "currency" => $currency, "total" => $itemPrice, "billingAddr" => array( "name" => $customerDetail['name'], "addrLine1" => $billingAddress['addrLine1'], "city" => $billingAddress['city'], "state" => $billingAddress['state'], "zipCode" => $billingAddress['zipCode'], "country" => $billingAddress['country'], "email" => $customerDetail['email'] ) )); $paymentResponse = array( "message" => "", "charge" => $charge ); } catch (Twocheckout_Error $e) { $paymentResponse = array( "message" => $e->getMessage(), "charge" => "" ); } return $paymentResponse; } } 

Config.php

<?php namespace Phppot; class Config { const CURRENCY = 'USD'; const SELLER_ID = ''; const PUBLISHABLE_KEY = ''; const PRIVATE_KEY = ''; public function productDetail() { $product = array( 'WWPS235' => array( "itemName" => 'Kindle Paperwhite (10th gen) - 6" 8GB, WiFi', 'itemPrice' => '130.00' ) ); return $product; } public function monthArray() { $months = array( 'January', 'February', 'March', 'April', 'May', 'June', 'July ', 'August', 'September', 'October', 'November', 'December' ); return $months; } } 

Database script

This is a SQL with the CREATE statement of the tbl_order table. Import this script for your PHP environment to run this example.

-- -- Table structure for table `tbl_order` -- CREATE TABLE `tbl_order` ( `id` int(11) NOT NULL, `name` varchar(25) COLLATE utf8_unicode_ci NOT NULL, `email` varchar(25) COLLATE utf8_unicode_ci NOT NULL, `item_code` varchar(25) COLLATE utf8_unicode_ci NOT NULL, `item_price` float(10,2) NOT NULL, `currency` varchar(10) COLLATE utf8_unicode_ci NOT NULL, `address_line_1` text COLLATE utf8_unicode_ci NOT NULL, `address_line_2` text COLLATE utf8_unicode_ci NOT NULL, `country` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `city` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `state` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `zip` varchar(20) COLLATE utf8_unicode_ci NOT NULL, `txn_id` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `payment_status` varchar(10) COLLATE utf8_unicode_ci NOT NULL, `create_at` datetime NOT NULL, `edit_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); ALTER TABLE `tbl_order` ADD PRIMARY KEY (`id`); ALTER TABLE `tbl_order` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=110; 

Go Live

After testing the payment flow in the Sandbox mode, we can move to production mode.

To go live, change the mode from sandbox to production in the following line.

TCO.loadPubKey('production');

Replace all the test API keys with the live keys in the application config. Also, disable the sandbox mode by setting it as false as shown below.

Twocheckout::privateKey('production-private-key'); Twocheckout::sellerId('production-seller-id'); Twocheckout::sandbox(false); 

2Checkout Payment Form Output

The below screenshot shows the 2Checkout payment form. Most of the form fields are mandatory and so validated by JavaScript before payment.

The product name and price above the form are from the config. We can link your product database to display this information.

2Checkout Payment Form Output

This screenshot shows the success response. The table shown in this screenshot has the basic details about the order placed.

2Checkout Success Response

Download

↑ Back to Top

Posted on Leave a comment

Shopify Like Shopping Cart with Sticky Checkout using PHP

Last modified on December 3rd, 2019 by Vincy.

Do you know online consumers in the US spent $517 billion last year? Which is a 15% increase from the previous year? eCommerce is an old domain, but still, it disrupts the tech world.

These days, the live shopping cart software have a remarkable business scope. They are increasing in the number day by day. There is a significant market today to sell products in an online store.

Shopify Like Shopping Cart with Sticky Checkout using PHP

PHP is the main technology that runs the majority of the shopping cart software on the Internet. Developing a full-fledged PHP shopping cart software is not as easy as it looks.

In this article, we are going to see how to create a featured sticky shopping cart script in PHP.  In Shopify like the eCommerce platform, it gives sticky shopping cart checkout as a feature.

I used jQuery and CSS to make the shopping cart unit sticky on a fixed position on page scroll. So, it will make the cart unit be always near to access and encourage the users to checkout.

This article will guide you with simple steps to create a shopping cart in PHP. If you are planning to create a shopping cart with a modern outlook and features,  it will help you to achieve this.

What is inside?

  1. Advantages of sticky shopping cart
  2. PHP sticky shopping cart example overview
  3. File structure
  4. Landing page with product gallery and shopping cart components
  5. Render product tiles in a gallery
  6. Display cart items from PHP Session
  7. Manage Cart actions edit, delete, empty
  8. Product database table SQL script
  9. PHP Shopify like sticky shopping cart example output

eCommerce software with sticky shopping cart checkout has many advantages,

  • It gives an enriched modern feel to the application.
  • It helps users to see the cart status at any moment.
  • It encourages users to checkout and thus increasing the conversion.

This Shopify-like sticky shopping cart software in PHP uses database and PHP sessions. It manages products in the database and shopping cart items in the PHP session.

It has a product gallery, cart HTML table and supports cart actions like add, edit and delete. These action handlers use AJAX to invoke code added with server endpoints. 

The functionalities supported by this example are here with the corresponding description.

Fetching product data from the database:
A PHP MySQL code section accesses product database to read the data like product name, code, price, and preview images. If data found then this will result in an array of products.

Create product gallery with add-to-cart option:
Create a product gallery with an add-to-cart option. A PHP loop iterates product array to form the gallery view. Each product in the gallery has an add-to-cart button.

Manage the cart with PHP session:
I have used the PHP session array to store and manage the cart items.

Handling the add, edit, delete and empty cart:
This example code supports users to add a new product into the cart. A HTML table will display the cart item with edit quantity, remove an entry and more options.

File structure

The below screenshot shows the file structure of this example. 

Shopify-Like Sticky Shopping Cart in PHP

  • view/product-gallery.php – It includes a PHP loop to iterate the products to form the gallery.
  • view/shopping-cart.php – It contains a HTML table to display the cart item from the PHP session.
  • css/style.css – It has the styles to showcase product gallery. It helps in shopping cart software design containing appropriate cart action controls.
  • cart.js – This JavaScript file contains action handlers to process requests via AJAX. It also contains code to fix the shopping cart panel in a fixed position while scrolling.
  • handle-cart-ep.php – This is the PHP endpoint invoked from AJAX code to add, edit, remove cart items.
  • tblproduct.sql – This SQL contains product database CREATE and INSERT statements.
  • DataSource.php – This is a generic file we used for our examples requires a database.
  • data – This directory is the product image source used while creating the gallery.

This HTML code includes the PHP source to render product gallery, shopping cart software pagee. It also shows a sticky cart icon with the current cart items count.

By clicking this icon, a script will toggle the shopping cart window.

This HTML includes cart.js JavaScript file. It contains all the jQuery AJAX code needed for performing cart actions and UI update.

<?php namespace Phppot; use \Phppot\Cart; session_start(); require_once 'Model/Cart.php'; $cartModel = new Cart(); ?> <HTML> <HEAD> <TITLE>Shopify like sticky shopping cart in PHP</TITLE> <link href="./assets/css/phppot-style.css" type="text/css" rel="stylesheet" /> <script src="./vendor/jquery/jquery.min.js" type="text/javascript"></script> <script src="./vendor/jquery/jquery-ui.js"></script> </HEAD> <BODY> <?php require_once './view/product-gallery.php'; ?> <div id="floating-cart-container"> <div id="cart-icon-container"> <img id="cart-icon" src="./view/images/cart-icon.png" alt="cartimg"> <div id="count"> <?php echo $cartModel->cartSessionItemCount; ?> </div> </div> <div id="shopping-cart"> <div id="tbl-cart"> <div id="txt-heading"> <div id="cart-heading">Shopping Cart</div> <div id="close"></div> </div> <div id="cart-item"> <?php require_once './view/shopping-cart.php'; ?> </div> </div> </div> </div> <script src="./assets/js/cart.js"></script> </BODY> </HTML> 

The product gallery is a HTML container embedded with PHP-MySQL script. The PHP code prepares MySQL select statement to get the product result in an array.

By iterating this array result with a PHP foreach statement, it reads the database row data. With this product data, it forms the gallery tile on each loop iteration.

The gallery tile shows products name, price and a preview image in a card-like view. Also, it contains an add-to-cart button.

This’s button’s click event invokes AJAX to add the particular product to the cart session. Each product has a unique code which is the reference to create and manage each cart session index.

Below code shows the HTML used to show a gallery view for this shopping cart software example.

<?php require_once __DIR__ . './../Model/Product.php'; $productModel = new Product(); ?> <div id="product-grid"> <div class="txt-heading">Products</div> <?php $productResult = $productModel->getAllProduct(); if (! empty($productResult)) { foreach ($productResult as $key => $value) { ?> <div class="product-item" data-name="<?php echo $productResult[$key]["name"]; ?>" data-price="<?php echo "$" . $productResult[$key]["price"]; ?>"> <div class="product-image"> <img src="<?php echo $productResult[$key]["image"]; ?>" id="<?php echo $productResult[$key]["code"]; ?>"> </div> <div> <strong><?php echo $productResult[$key]["name"]; ?></strong> </div> <div class="product-price"><?php echo "$" . $productResult[$key]["price"]; ?></div> <input type="button" id="add_<?php echo $productResult[$key]["code"]; ?>" value="Add to cart" class="btnAddAction" onClick="cartAction('add', '<?php echo $productResult[$key]["code"]; ?>')" /> </div> <?php } } ?> </div> 

Add-to-Cart from Product Gallery

The add-to-cart button in each product tile is the trigger to add a cart item into the PHP session. When the user clicks on the ‘Add to Cart’ button, it calls the cartAction() function to execute the add action via AJAX.

In this function call, it has the product id as an argument. In PHP the code fetches the product code, price to add to the cart session.

In this example, the switch case handles cart actions. It request and process add, edit (item quantity), remove (single cart item) and empty cart.

In the “add” case I checked if the current cart item already exists in the cart session. If so, I will update its quantity, otherwise, I will push the entire item array into the session index. The product code is the session index of each cart item to keep the uniqueness. The addToCart function has the code to add the cart item into the PHP session.

case "add": $cartModel->addToCart(); break; 

Model/Product.php

<?php use \Phppot\DataSource; class Product { private $ds; function __construct() { require_once __DIR__ . './../lib/DataSource.php'; $this->ds = new DataSource(); } function getAllProduct() { $query = "SELECT * FROM tblproduct ORDER BY id ASC"; $result = $this->ds->select($query); return $result; } } 

Display shopping cart items from PHP Session

This code shows the HTML table containing the list of cart items added by the user. The cart data is dynamic from the PHP session.

In shopping-cart.php file, it iterates the $_SESSION[“cart”] array and displays the cart row. Each row has data like product title, price, quantity. It also has the option to edit the item quantity and to delete a single item from the cart.

This cart window is sticky that lets the users access the cart and see the status at any time. Also, it shows the total item price by summing up the individual cart items.

<?php namespace Phppot; use \Phppot\Cart; require_once __DIR__ . './../Model/Cart.php'; $cartModel = new Cart(); ?> <input type="hidden" id="cart-item-count" value="<?php echo $cartModel->cartSessionItemCount; ?>"> <?php if ($cartModel->cartSessionItemCount > 0) { ?> <table width="100%" id="cart-table" cellpadding="10" cellspacing="1" border="0"> <tbody> <tr> <th>Name</th> <th>Quantity</th> <th class="text-right">Price</th> <th class="text-right">Action</th> </tr> <?php $item_total = 0; $i = 1; foreach ($_SESSION["cart_item"] as $item) { ?> <tr> <td><?php echo $item["name"]; ?></td> <td><input type="number" name="quantity" class="quantity" value="<?php echo $item['quantity']; ?>" data-code='<?php echo $item["code"]; ?>' size=2 onChange="updatePrice(this)" /> <input type="hidden" class='total' name="total" value="<?php echo $item["price"]; ?>" /></td> <td align=right class="prc" id="price" <?php echo $i;?>><?php echo $item["price"]; ?></td> <?php $i++; ?> <td class="text-right"><a onClick="cartAction('remove','<?php echo $item["code"]; ?>')" class="btnRemoveAction"><img src="./view/images/icon-delete.png" alt="Remove Item" /></a></td> </tr> <?php $item_total += ($item["price"] * $item['quantity']); } ?> <tr id="tot"> <td colspan="3" align=right><strong>Total (USD): </strong> <span id="total"><?php echo $item_total;?></span></td> <td align="right"><a id="btnEmpty" onClick="cartAction('empty', '');">Empty Cart</a></td> </tr> </tbody> </table> <div id="checkout">Checkout</div> <?php } else { ?> <div id="empty-cart">Your cart is empty</div> <?php } ?> 

Handle shopping cart actions edit, remove, empty on checkout page

In each row of the showing cart tabular window, it displays editable quantity with an input box. Users can increment or decrement the cart item quantity of a particular item.

On changing the quantity, and AJAX code will send the data to get the calculated price based on the new quantity. Then, it will update the price in the row.

There is a remove option for each cart item. By clicking the remove action, an ajax call will request PHP code to perform the remove action. It will pass the product code as an argument to clear the particular cart session index.

Also, the shopping cart window has the option to empty the cart with one single click.

The below code shows the switch cases created to trigger and perform cart actions.

cart.js

function cartAction(action, product_code) { var queryString = ""; if (action != "") { switch (action) { case "add": queryString = 'action=' + action + '&code=' + product_code + '&quantity=' + $("#qty_" + product_code).val(); break; case "remove": queryString = 'action=' + action + '&code=' + product_code; break; case "empty": queryString = 'action=' + action; break; } } jQuery.ajax({ url: "ajax/handle-cart-ep.php", data: queryString, type: "POST", success: function (data) { $("#cart-item").html(data); $("#count").text($("#cart-item-count").val()); }, error: function () {} }); } function updatePrice(obj){ var quantity = $(obj).val(); var code = $(obj).data('code'); queryString = 'action=edit&code=' + code + '&quantity=' + quantity; $.ajax({ type: 'post', url: "ajax/handle-cart-ep.php", data: queryString, success: function(data) { $("#total").text(data); } }); } $(document).ready(function () { $("#cart-icon-container").click(function () { $("#shopping-cart").toggle(); }); var top = parseInt($("#shopping-cart").height())/2; $("#shopping-cart").css("margin-top", "-" + top + "px"); }); 

ajax/handle-cart-ep.php

<?php namespace Phppot; use \Phppot\Cart; require_once __DIR__ . './../Model/Cart.php'; $cartModel = new Cart(); session_start(); if (! empty($_POST["action"])) { switch ($_POST["action"]) { case "add": $cartModel->addToCart(); break; case "edit": $totalPrice = $cartModel->editCart(); print $totalPrice; exit; break; case "remove": $cartModel->removeFromCart(); break; case "empty": $cartModel->emptyCart(); break; } } require_once '../view/shopping-cart.php'; ?> 

Model/Cart.php

This is the model class used to create, edit and clear cart sessions.

<?php namespace Phppot; use \Phppot\DataSource; class Cart { private $ds; public $cartSessionItemCount = 0; function __construct() { require_once __DIR__ . './../lib/DataSource.php'; $this->ds = new DataSource(); if (! empty($_SESSION["cart_item"]) && is_array($_SESSION["cart_item"])) { $this->cartSessionItemCount = count($_SESSION["cart_item"]); } } function addToCart() { $query = "SELECT * FROM tblproduct WHERE code = ?"; $paramType = "s"; $paramArray = array($_POST["code"]); $productByCode = $this->ds->select($query, $paramType, $paramArray); $itemArray = array( $productByCode[0]["code"] => array( 'name' => $productByCode[0]["name"], 'code' => $productByCode[0]["code"], 'quantity' => '1', 'price' => $productByCode[0]["price"] ) ); if (! empty($_SESSION["cart_item"])) { if (in_array($productByCode[0]["code"], $_SESSION["cart_item"])) { foreach ($_SESSION["cart_item"] as $k => $v) { if ($productByCode[0]["code"] == $k) $_SESSION["cart_item"][$k]["quantity"] = $_POST["quantity"]; } } else { $_SESSION["cart_item"] = array_merge($_SESSION["cart_item"], $itemArray); } } else { $_SESSION["cart_item"] = $itemArray; } if (! empty($_SESSION["cart_item"]) && is_array($_SESSION["cart_item"])) { $this->cartSessionItemCount = count($_SESSION["cart_item"]); } } function editCart() { if (! empty($_SESSION["cart_item"])) { $total_price = 0; foreach ($_SESSION["cart_item"] as $k => $v) { if ($_POST["code"] == $k) { $_SESSION["cart_item"][$k]["quantity"] = $_POST["quantity"]; } $total_price = $total_price + ($_SESSION["cart_item"][$k]["quantity"] * $_SESSION["cart_item"][$k]["price"] ); } return $total_price; } if (! empty($_SESSION["cart_item"]) && is_array($_SESSION["cart_item"])) { $this->cartSessionItemCount = count($_SESSION["cart_item"]); } } function removeFromCart() { if (! empty($_SESSION["cart_item"])) { foreach ($_SESSION["cart_item"] as $k => $v) { if ($_POST["code"] == $k) unset($_SESSION["cart_item"][$k]); if (empty($_SESSION["cart_item"])) unset($_SESSION["cart_item"]); } } if (! empty($_SESSION["cart_item"]) && is_array($_SESSION["cart_item"])) { $this->cartSessionItemCount = count($_SESSION["cart_item"]); } } function emptyCart() { unset($_SESSION["cart_item"]); $this->cartSessionItemCount = 0; } } 

Product database table SQL script

The following SQL script has the CREATE and the INSERT query. It will help to put the product table in your development environment.

CREATE TABLE IF NOT EXISTS `tblproduct` ( `id` int(8) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `code` varchar(255) NOT NULL, `image` text NOT NULL, `price` double(10,2) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `product_code` (`code`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=14 ; -- -- Dumping data for table `tblproduct` -- INSERT INTO `tblproduct` (`id`, `name`, `code`, `image`, `price`) VALUES (1, 'FinePix Pro2 3D Camera', '3DcAM01', 'product-images/camera.jpg', 1500.00), (2, 'Luxury Ultra thin Wrist Watch', 'wristWear03', 'product-images/watch.jpg', 300.00), (3, 'XP 1155 Intel Core Laptop', 'LPN45', 'product-images/laptop.jpg', 800.00), (4, 'Water Bottle', 'wristWear02', 'product-images/external-hard-drive.jpg', 600.00); 

Shopify-Like Floating Shopping Cart Output

Download

↑ Back to Top

Posted on Leave a comment

Double Opt-In Subscription Form with Secure Hash using PHP

Last modified on September 24th, 2019 by Vincy.

Do you know that the opening rate of emails by double opt-in confirmed subscribers is a staggering 40%? According to CampaignMonitor, email marketing generates $38 in ROI for every $1 spent.

Email marketing delivers the highest among any channel for marketing. Even in comparison with channels like print, TV and social media.

Double Opt-In Subscription Form with Secure Hash using PHP

Email marketing is the way to go. The primary mode to build your list is using a double opt-in subscription form.

What is inside?

  1. Why do we need double opt-in?
  2. What is the role of secure hash?
  3. Double opt-in subscription form in PHP
  4. Sequence flow for double opt-in subscription
  5. Double opt-in Subscription form UI
  6. PHP AJAX for subscription form submission
  7. URL with secure hash
  8. A PHP utility class for you
  9. Store subscription information to database
  10. A database abstraction layer for you
  11. Send confirmation email to users
  12. Subscription confirmation
  13. Conclusion

Use a double opt-in subscription form to signup to a newsletter, blog or a similar service. It has a two-step subscription process.

In the first step, the user will submit his name and email. Then the site will send an email to the user.

In the second step, the user will click the link in the received email. This will confirm his subscription to the site or service.

We call it the double opt-in because the users consent to the subscription twice. First by submitting the information and second by confirming to in by clicking the link in email.

Why do we need double opt-in?

It is the mechanism used to verify if the subscriber owns the input email. You need to do this verification because there is a chance for misuse by submitting emails that they do not own.

Double opt-in vs single opt-in is well debated and results arrived at. Double opt-in wins hands-on in every critical aspect.

What is the role of a secure hash?

In the confirmation email received by the user, there will be a link. This is the second and important step in the opt-in process. The link should be secure.

  • It should be unique for every user and request.
  • It should not be predictable.
  • It should be immune to a brute-force attack.

Double opt-in subscription form in PHP

I will present you a step by step detail on how to build a double opt-in subscription form with a secure hash using PHP.

You will get a production-grade code which you can use real-time in your live website. You can use this to manage your newsletter subscription.

I am releasing this code to you under MIT license. You can use it free even in commercial projects.

Sequence flow for double opt-in subscription

  1. Show a subscription form to the user.
  2. On AJAX submit, insert a new record in the database.
  3. Send an email to the user with a secure hash link.
  4. On click, the of the link, update the subscription status.
  5. On every step, there will be appropriate validations in place.

Double opt-in Subscription form UI

This is where developers get it wrong. Keep it simple and unobtrusive. For the high conversion, you must keep in minimal.

One field email is enough for the subscription. To address the user in a personal way, you need their name. That’s it. Do not ask for much information on a subscription form.

<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="author" content="Vincy"> <link rel="stylesheet" type="text/css" href="assets/css/phppot-style.css"> <title>Double Opt-In Subscription Form with Secure Hash using PHP</title> </head> <body> <div class="phppot-container"> <h1>Double Opt-in Subscription</h1> <form class="phppot-form" action="" method="POST"> <div class="phppot-row"> <div class="label"> Name </div> <input type="text" id="pp-name" name="pp-name" class="phppot-input"> </div> <div class="phppot-row"> <div class="label"> Email * <div id="email-info" class="validation-message" data-required-message="required." data-validate-message="Invalid email."></div> </div> <input type="text" id="pp-email" name="pp-email" class="required email phppot-input" onfocusout="return validateEmail();"> </div> <div class="phppot-row"> <button type="Submit" id="phppot-btn-send">Subscribe</button> <div id="phppot-loader-icon">Sending ...</div> <div id="phppot-message"></div> </div> </form> </div> <script src="vendor/jquery/jquery-3.3.1.js"></script> <script src="assets/js/subscribe.js"></script></body> </body> </html> 

If you ask for much information, it will drive your users away. The same principle applies when you build a contact form. More or less these two behave in a similar aspect. Check how to build a contact form to know more on it.

Double Opt-in subscription form UI

You should leave the name field can as optional and only the email field should be as required. This will encourage the user to submit the form and subscribe for the newsletter.

Needless to say, the form should be responsive. Any page or form you build should work in mobile, tablet, laptop and desktop devices. You should optimize to work on any viewport.

Google parses webpages in mobile mode for indexing in the search result. The desktop is an old story and gone are those days. You should always design for the mobile. Make it mobile-first!

PHP AJAX for subscription form submission

I have used AJAX to manage the submission. This will help the user to stay on the page after subscription. You can position this subscription form in a sidebar or the footer.

Double Opt-in Subscription Form AJAX Submission

This is a classic example of where you should use the AJAX. I have seen instances where people use AJAX in inappropriate places, for the sake of using it.

Subscription AJAX endpoint

The AJAX endpoint has three major steps:

  1. Verify the user input.
  2. Insert a record in the database.
  3. Send an email with a link for subscription.

subscribe-ep.php is the AJAX endpoint. It starts with an if condition to check if the submit is via the POST method. It is always good to program for POST instead of the GET by default.

<?php use Phppot\Subscription; use Phppot\SupportService; /** * AJAX end point for subscribe action. * 1. validate the user input * 2. store the details in database * 3. send email with link that has secure hash for opt-in confirmation */ session_start(); // to ensure the request via POST if ($_POST) { require_once __DIR__ . './../lib/SupportService.php'; $supportService = new SupportService(); // to Debug set as true $supportService->setDebug(false); // to check if its an ajax request, exit if not $supportService->validateAjaxRequest(); require_once __DIR__ . './../Model/Subscription.php'; $subscription = new Subscription(); // get user input and sanitize if (isset($_POST["pp-email"])) { $userEmail = trim($_POST["pp-email"]); $userEmail = filter_var($userEmail, FILTER_SANITIZE_EMAIL); $subscription->setEmail($userEmail); } else { // server side fallback validation to check if email is empty $output = $supportService->createJsonInstance('Email is empty!'); $supportService->endAction($output); } $memberName = ""; if (isset($_POST["pp-name"])) { $memberName = filter_var($_POST["pp-name"], FILTER_SANITIZE_STRING); } $subscription->setMemberName($memberName); // 1. get a 12 char length random string token $token = $supportService->getToken(12); // 2. make that random token to a secure hash $secureToken = $supportService->getSecureHash($token); // 3. convert that secure hash to a url string $urlSecureToken = $supportService->cleanUrl($secureToken); $subscription->setSubsriptionKey($urlSecureToken); $subscription->setSubsciptionSatus(0); $currentTime = date("Y-m-d H:i:s"); $subscription->setCreateAt($currentTime); $result = $subscription->insert(); // check if the insert is success // if success send email else send message to user $messageType = $supportService->getJsonValue($result, 'type'); if ('error' != $messageType) { $result = $subscription->sendConfirmationMessage($userEmail, $urlSecureToken); } $supportService->endAction($result); } 

I have used the SupportService class to perform common functions.

Input sanitisation is a must. When you collect information using a public website, you should be careful. You could get infected without your knowledge. There are many bots foraging around the Internet and they click on all links and buttons.

To sanitise, do not invent a new function. Use the function provided by PHP and that is safe to use.

URL with secure hash

Generate a unique url for each user subscription. Use this url to confirm the user’s subscription in the second step. Remember, that’s why we call this double opt-in.

I have used a three step process:

  1. Generate a random string token.
  2. Convert the token to secure hash.
  3. Convert the secure hash to safe url.

I have used hexdec, bin2hex and openssl_random_pseudo_bytes to generate random bits. Which forms a random string.

Then to make the random string a secure hash, I have used the PHP’s built-in password_hash function. Never every try to do something on your own. Go with the PHP’s function and it does the job very well.

Before PHP 7, we had the option to supply a user generated salt. Now PHP 7 release has deprecated it. It is a good move because, PHP can generate a better salt than what you will generate. So stick to PHP 7 and use it without supplying your own salt.

The secure hash will contain all sort of special characters. . You can keep those special characters but need to url encode it. But I always wish to keep urls clean and the encoded chars do not look nice.

So no harm in removing them. So I cleanup those and leave only the safe characters. Then as a secondary precaution, I also encode the resultant string.

Thus after going through multi step process, we get a random, hash secure, safe, encoded URL token. Save the user submitted information in database record along with this token.

A PHP utility class for you

This is a utility class which I use in my projects. I am giving it away free for you all. It has functions that I reuse quite often and will be handy in situations. Every method has detailed comments that explain their purpose and usage method.

<?php /** * Copyright (C) 2019 Phppot * * Distributed under MIT license with an exception that, * you don’t have to include the full MIT License in your code. * In essense, you can use it on commercial software, modify and distribute free. * Though not mandatory, you are requested to attribute this URL in your code or website. */ namespace Phppot; class SupportService { /** * Short circuit type function to stop the process flow on validation failure. */ public function validateAjaxRequest() { // to check if its an ajax request, exit if not $http_request = $_SERVER['HTTP_X_REQUESTED_WITH']; if (! isset($http_request) && strtolower($http_request) != 'xmlhttprequest') { $output = $this->createJsonInstance('Not a valid AJAX request!'); $this->endAction($output); } } /** * Last point in the AJAX work flow. * Clearing tokens, handles and resource cleanup can be done here. * * @param string $output * @param boolean $clearToken */ public function endAction($output) { die($output); } public function setDebug($mode) { if ($mode == true) { ini_set('display_errors', 1); set_error_handler(function ($severity, $message, $file, $line) { if (error_reporting() & $severity) { throw new \ErrorException($message, 0, $severity, $file, $line); } }); } } /** * encodes a message string into a json object * * @param string $message * @param string $type * @return \JsonSerializable encoded json object */ public function createJsonInstance($message, $type = 'error') { $messageArray = array( 'type' => $type, 'text' => $message ); $jsonObj = json_encode($messageArray); return $jsonObj; } public function getJsonValue($json, $key) { $jsonArray = json_decode($json, true); return $jsonArray[$key]; } /** * If you are using PHP, this is the best possible secure hash * do not try to implement somthing on your own * * @param string $text * @return string */ public function getSecureHash($text) { $hashedText = password_hash($text, PASSWORD_DEFAULT); return $hashedText; } /** * generates a random token of the length passed * * @param int $length * @return string */ public function getToken($length) { $token = ""; $codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; $codeAlphabet .= "abcdefghijklmnopqrstuvwxyz"; $codeAlphabet .= "0123456789"; $max = strlen($codeAlphabet) - 1; for ($i = 0; $i < $length; $i ++) { $token .= $codeAlphabet[$this->cryptoRandSecure(0, $max)]; } return $token; } public function cryptoRandSecure($min, $max) { $range = $max - $min; if ($range < 1) { return $min; // not so random... } $log = ceil(log($range, 2)); $bytes = (int) ($log / 8) + 1; // length in bytes $bits = (int) $log + 1; // length in bits $filter = (int) (1 << $bits) - 1; // set all lower bits to 1 do { $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes))); $rnd = $rnd & $filter; // discard irrelevant bits } while ($rnd >= $range); return $min + $rnd; } /** * makes the passed string url safe and return encoded url * * @param string $str * @return string */ public function cleanUrl($str, $isEncode = 'true') { $delimiter = "-"; $str = str_replace(' ', $delimiter, $str); // Replaces all spaces with hyphens. $str = preg_replace('/[^A-Za-z0-9\-]/', '', $str); // allows only alphanumeric and - $str = trim($str, $delimiter); // remove delimiter from both ends $regexConseqChars = '/' . $delimiter . $delimiter . '+/'; $str = preg_replace($regexConseqChars, $delimiter, $str); // remove consequtive delimiter $str = mb_strtolower($str, 'UTF-8'); // convert to all lower if ($isEncode) { $str = urldecode($str); // encode to url } return $str; } /** * to mitigate XSS attack */ public function xssafe($data, $encoding = 'UTF-8') { return htmlspecialchars($data, ENT_QUOTES | ENT_HTML401, $encoding); } /** * convenient method to print XSS mitigated text * * @param string $data */ public function xecho($data) { echo $this->xssafe($data); } } 

Store subscription information to the database

Insert a record to the database on submission of the subscription form. We get the user’s name, email, generate a secure hash token, current time, subscription status.

<?php /** * Copyright (C) 2019 Phppot * * Distributed under MIT license with an exception that, * you don’t have to include the full MIT License in your code. * In essense, you can use it on commercial software, modify and distribute free. * Though not mandatory, you are requested to attribute this URL in your code or website. */ namespace Phppot; use Phppot\DataSource; class Subscription { private $ds; private $memberName; private $email; private $subsriptionKey; private $subsciptionSatus; private $createAt; private $supportService; function __construct() { require_once __DIR__ . './../lib/DataSource.php'; $this->ds = new DataSource(); require_once __DIR__ . './../lib/SupportService.php'; $this->supportService = new SupportService(); } public function getMemberName() { return $this->memberName; } public function getEmail() { return $this->email; } public function getSubsriptionKey() { return $this->subsriptionKey; } public function getSubsciptionSatus() { return $this->subsciptionSatus; } public function getCreateAt() { return $this->createAt; } public function setMemberName($memberName) { $this->memberName = $memberName; } public function setEmail($email) { $this->email = $email; } public function setSubsriptionKey($subsriptionKey) { $this->subsriptionKey = $subsriptionKey; } public function setSubsciptionSatus($subsciptionSatus) { $this->subsciptionSatus = $subsciptionSatus; } public function setCreateAt($createAt) { $this->createAt = $createAt; } /** * to get the member record based on the subscription_key * * @param string $subscriptionKey * @return array result record */ public function getMember($subscriptionKey, $subscriptionStatus) { $query = 'SELECT * FROM tbl_subscription where subscription_key = ? and subscription_status = ?'; $paramType = 'si'; $paramValue = array( $subscriptionKey, $subscriptionStatus ); $result = $this->ds->select($query, $paramType, $paramValue); return $result; } public function insert() { $query = 'INSERT INTO tbl_subscription (member_name, email, subscription_key, subscription_status, create_at) VALUES (?, ?, ?, ?, ?)'; $paramType = 'sssis'; $paramValue = array( $this->memberName, $this->email, $this->subsriptionKey, $this->subsciptionSatus, $this->createAt ); $insertStatus = $this->ds->insert($query, $paramType, $paramValue); return $insertStatus; } public function updateStatus($subscriptionKey, $subscriptionStatus) { $query = 'UPDATE tbl_subscription SET subscription_status = ? WHERE subscription_key = ?'; $paramType = 'is'; $paramValue = array( $subscriptionStatus, $subscriptionKey ); $this->ds->execute($query, $paramType, $paramValue); } /** * sends confirmation email, to keep it simple, I am just using the PHP's mail * I reccommend serious users to change it to PHPMailer and set * appropriate headers */ public function sendConfirmationMessage($mailTo, $urlSecureToken) { // following is the opt-in url that will be sent in email to // the subscriber. Replace example.com with your server $confirmOptInUrl = 'http://example.com/confirm.php?q=' . $urlSecureToken; $message = '<p>Howdy!</p> <p>This is an automated message sent for subscription service. You must confirm your request to subscribe to example.com site.</p> <p>Website Name: example</p> <p>Website URL: http://example.com</p> <p>Click the following link to confirm: ' . $confirmOptInUrl . '</p>'; $isSent = mail($mailTo, 'Confirm your subscription', $message); if ($isSent) { $message = "An email is sent to you. You should confirm the subscription by clicking the link in the email."; $result = $this->supportService->createJsonInstance($message, 'message'); } else { $result = $this->supportService->createJsonInstance('Error in sending confirmation email.', 'error'); } return $result; } } 

The reason for storing the current time is to have an expiry for every link. We can set a predefined expiry for the double opt-in process.

For example, you can set one week as expiry for a link from the moment you generate it. The user has to click and confirm before that expiry period.

Subscription status is by default stored as ‘0’ and on confirmation changed to ‘1’.

A database abstraction layer for you

It is my PHP abstraction for minor projects. This works as a layer between controller, business logic and the database. It has generic methods using which we can to the CRUD operations. I have bundled it with the free project download that is available at the end of this tutorial.

Send confirmation email to users

After you insert the record, send an email will to the user to perform the double opt-in confirmation. The user will have a link in the email which he has to click to confirm.

Keep the email simple. It is okay to have text instead of fancy HTML emails. PHP is capable of generating any email and you can code complex email templates. But the spam engines may not like it.

Subscription information database record with secure hash

If you wish to go with HTML emails, then keep the HTML code ratio to as least as possible. As this is also one factor using which the spam engines flag the emails.

Then remember not to use the spam stop words. There are words like “free”, “win”, “cash”, “promo” and “income”. There is a long list and you can get it on the Internet by searching for “email spam filter word list”.

I have used PHP’s mail() function to send the email. I recommend you to change it to PHPMailer to send SMTP based email if you plan to use this code in production.

Subscription confirmation

Create a public landing page and you may use .htaccess for a neat URL mapping. This URL should map with the URL sent to the user and the PHP file that is going to process the request.

As a first step, GET the token and to verify the user against the database. Check,

  1. if such a token exists,
  2. it is not expired,
  3. the user is not already subscribed
  4. add more validation as you deem fit.
<?php use Phppot\Subscription; use Phppot\SupportService; /** * For confirmation action. * 1. Get the secure has from url * 2. validate it against url * 3. update the subscription status in database accordingly. */ session_start(); // to ensure the request via POST require_once __DIR__ . '/lib/SupportService.php'; $supportService = new SupportService(); // to Debug set as true $supportService->setDebug(true); $subscriptionKey = $_GET['q']; require_once __DIR__ . '/Model/Subscription.php'; $subscription = new Subscription(); $result = $subscription->getMember($subscriptionKey, 0); if (count($result) > 0) { // member found, go ahead and update status $subscription->updateStatus($subscriptionKey, 1); $message = $result[0]['member_name'] . ', your subscription is confirmed.'; $messageType = 'success'; } else { // securiy precaution: do not reveal any information here // play subtle with the reported message $message = 'Invalid URL!'; $messageType = 'error'; } ?> <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="author" content="Vincy"> <link rel="stylesheet" type="text/css" href="assets/css/phppot-style.css"> <title>Double Opt-In Subscription Confirmation</title> </head> <body> <div class="phppot-container"> <h1>Double Opt-in Subscription Confirmation</h1> <div class="phppot-row"> <div id="phppot-message" class="<?php echo $messageType; ?>"><?php echo $message;?></div> </div> </div> </body> </body> </html> 

If validation fails, do not reveal any information to the user. You should only say that it has failed.

Subscription double opt-in confirmation url verification

More important do not say, not such email found. This will allow finding who has subscribed to your service. Whenever a validation fails, the displayed message should not reveal internal information.

On validation success, update the subscription status. Then show a happy success message to the user.

Conclusion

I have presented you with a production-grade double opt-in subscription form. I have followed a most secure hash generation method for confirmation URL email. I present it to you under the MIT license. The intention is to be the most permissible. You can download it free and change the code. You can even use it in your commercial projects. I have used the most secure code as possible. You can use this in your live site to manage newsletter subscription. In the coming part, I will include unsubscribe and enhance it further. Leave your comments below with what sort of enhancements you are looking for.

Download

↑ Back to Top

Posted on Leave a comment

How to Create Popup Contact Form Dialog using PHP and jQuery

Last modified on September 5th, 2019 by Vincy.

Contact form is an important element in your website. It encourages communication and acts as a bridge between you and your users. It allows users to post their feedback, comments, questions and more.

It helps you to get ideas and opinions that lead your business to growth. It is one among the important aspects that will decide your success.

There are many popup contact form plugins available online. These plugins help to add a contact form in your application with a modern outlook. You should choose them with care.

I have developed a light-weight contact form component named Iris. It is an interactive, integrative component. You can plug-in this component with your application without putting much effort. If you are searching for a secured simple contact form component, then Iris is the one you are looking for.

You should also read through the simple secure spam-free contact form. It details on the critical elements of a contact form and how they should be designed. It is a highly recommended read from me.

How Display PHP Contact Form Dialog using jQuery

View Demo

Developing a PHP contact form is a simple job. We have seen so many examples for creating a contact form in PHP. The simple contact form example has the basic code skeleton. It has minimal fields and a simple mail sending script.

Generally, the contact form has the name, email, message and more fields. The fields may vary based on the application’s nature or its purpose. 

In some application there may be a lengthy contact form with elaborate list of fields phone, fax and more. But, sleek forms with minimal inputs encourage users to interact.

If you want to get more information, then make it via optional custom fields. Contact form with custom fields will help users who have no objection to give extra data.

In this article, we are going how to code for showing a PHP contact form as a popup dialog. I used the jQuery core to display a popup dialog with the contact form HTML.

What is inside?

  1. Purpose of the contact form on a web application
  2. Things to remember while creating a contact form
  3. About this example
  4. File structure
  5. Create HTML interface for the contact form
  6. PHP script to send contact email
  7. PHP contact form popup with jQuery Output
  8. Conclusion

There are various ways to allow users to contact you via your application interface. Contact form is one of a popular component of an application. It lets users contact the website owner or admin.

Generally, contact forms help the users to send their thoughts, doubts. Some of the main purposes of this contact form interface are here as listed below.

  • To collect feedback, comments from the users or customers.
  • For getting the advantages of user interaction with your application.
  • To receive user’s support request.
  • To allow users to send inquiries for paid services or customization.
  • To get biodata, proof and more references as attachments.

While creating a contact form in your application, you have to remember the following. These points will help to create a safe contact for an interface for your application.

IMPORTANT – Read this!

  • Secure your platform from the bots. Yes! Internet is full of automated bots hungry for spreading spam.
  • Prevent abnormal frequent hits which may stress your server.
  • Ensure data sanitization before processing user data
  • Check request origin to prevent automated software to post the form data.
  • Validate on both client and server side.
  • No design is design. Do not thrust the UI upon the users, let its focus be on good communication and ease of use.

As the internet is an open world, it allows anonymous users. So, there is the possibility of malicious access. So, this kind of safety measures will reduce the risk.

It is not only applicable for the contact form but also for all the interfaces that get data from the end-user.

The contact form pops up to collect name, email, subject and message from the users. While running this example, the landing page will not show the popup on page load. Rather, it shows a clickable contact icon.

By clicking this icon, a contact form will popup with the name, email and more fields. For displaying the popup interface, I used jQuery library functions. It will add an overlay on top of the webpage while showing the popup.

In this example, all the contact details are mandatory. I have added a minimal validation script in JavaScript to validate the form data. This script is to check the availability of the contact data before posting it to the server-side.

When the user submits the form, the PHP code will receive the posted form data. Then, it processes the mail function with this contact information.

In this example, I have used the PHP mail function to send the contact email. If you want to send email using SMPT, the linked article has the code for implementing it.

Merits and demerits of a popup contact form dialog

There are both advantages and disadvantages of showing a popup contact form dialog.

The advantage is to let the user stay on his current page with any navigation or page refresh. The popup dialog interface will give a modern outlook for your application

Some web users hate popups. This is the main disadvantages. Also, it is a little bit of effort taking work to make a popup interface mobile friendly.

File Structure

Below screenshot shows the file structure of this PHP contact form example. It shows the custom files and libraries used in this code.

It has a very minimal amount of dynamic code that is for sending the contact email. Other than that, it has more of the HTML, CSS, JavaScript code.

Contact Form Popup File Structure

The index.php is the landing page which contains HTML to display clickable contact icon. It also has a hidden contact form container and a jQuery script to popup the form.

The vendor directory contains the jQuery library source.

This section is to learn how to create HTML to display the contact form interface in a jQuery popup.

It has the code to show the contact icon which popups contact form on its click event. The click event on the outside of the contact form layout will close the popup dialog.

I have added CSS and jQuery script to reflect the appropriate UI changes based on the user’s click action event. It helps to toggle the contact form popup dialog and acknowledge the user accordingly.

<!DOCTYPE html> <html> <head> <title>How to display PHP contact form popup using jQuery</title> <script src="./vendor/jquery/jquery-3.2.1.min.js"></script> <link rel="stylesheet" href="./css/style.css" /> </head> <body> <div id="contact-icon"> <img src="./icon/icon-contact.png" alt="contact" height="50" width="50"> </div> <!--Contact Form--> <div id="contact-popup"> <form class="contact-form" action="" id="contact-form" method="post" enctype="multipart/form-data"> <h1>Contact Us</h1> <div> <div> <label>Name: </label><span id="userName-info" class="info"></span> </div> <div> <input type="text" id="userName" name="userName" class="inputBox" /> </div> </div> <div> <div> <label>Email: </label><span id="userEmail-info" class="info"></span> </div> <div> <input type="text" id="userEmail" name="userEmail" class="inputBox" /> </div> </div> <div> <div> <label>Subject: </label><span id="subject-info" class="info"></span> </div> <div> <input type="text" id="subject" name="subject" class="inputBox" /> </div> </div> <div> <div> <label>Message: </label><span id="userMessage-info" class="info"></span> </div> <div> <textarea id="message" name="message" class="inputBox"></textarea> </div> </div> <div> <input type="submit" id="send" name="send" value="Send" /> </div> </form> </div> </body> </html> 

Below code shows the styles created for this PHP contact form UI. I have used very less CSS for this example to make it generic for any theme. You can override the below style to customize the form design for your website theme.

body { color: #232323; font-size: 0.95em; font-family: arial; } div#success { text-align: center; box-shadow: 1px 1px 5px #455644; background: #bae8ba; padding: 10px; border-radius: 3px; margin: 0 auto; width: 350px; } .inputBox { width: 100%; margin: 5px 0px 15px 0px; border: #dedede 1px solid; box-sizing: border-box; padding: 15px; } #contact-popup { position: absolute; top: 0px; left: 0px; height: 100%; width: 100%; background: rgba(0, 0, 0, 0.5); display: none; color: #676767; } .contact-form { width: 350px; margin: 0px; background-color: white; font-family: Arial; position: relative; left: 50%; top: 50%; margin-left: -210px; margin-top: -255px; box-shadow: 1px 1px 5px #444444; padding: 20px 40px 40px 40px; } #contact-icon { padding: 10px 5px 5px 12px; width: 58px; color: white; box-shadow: 1px 1px 5px grey; border-radius: 3px; cursor: pointer; margin: 60px auto; } .info { color: #d30a0a; letter-spacing: 2px; padding-left: 5px; } #send { background-color: #09F; border: 1px solid #1398f1; font-family: Arial; color: white; width: 100%; padding: 10px; cursor: pointer; } #contact-popup h1 { font-weight: normal; text-align: center; margin: 10px 0px 20px 0px; } .input-error { border: #e66262 1px solid; } 

jQuery Script to show Contact form popup and validate form fields

Below script shows the jQuery callback function added for the document ready event.

It has two event handling functions. One is to show contact form popup dialog on the click event of the contact icon.

The other is to handle the form submit to validate contact data entered by the user.

The validation script focuses on minimalistic filter appliances. It helps to prevent users from sending the form with an empty data or with invalid data (email) format.

<script> $(document).ready(function () { $("#contact-icon").click(function () { $("#contact-popup").show(); }); //Contact Form validation on click event $("#contact-form").on("submit", function () { var valid = true; $(".info").html(""); $("inputBox").removeClass("input-error"); var userName = $("#userName").val(); var userEmail = $("#userEmail").val(); var subject = $("#subject").val(); var message = $("#message").val(); if (userName == "") { $("#userName-info").html("required."); $("#userName").addClass("input-error"); } if (userEmail == "") { $("#userEmail-info").html("required."); $("#userEmail").addClass("input-error"); valid = false; } if (!userEmail.match(/^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/)) { $("#userEmail-info").html("invalid."); $("#userEmail").addClass("input-error"); valid = false; } if (subject == "") { $("#subject-info").html("required."); $("#subject").addClass("input-error"); valid = false; } if (message == "") { $("#userMessage-info").html("required."); $("#message").addClass("input-error"); valid = false; } return valid; }); }); </script> 

There are many ways of sending email in PHP. In this example, I used the in-built mail function to send the contact email.

Before sending the mail, we have to set the header, recipient, and the other parameters.

Below PHP script gets the posted contact form data using $_POST request array. In PHP, it sets the From data with mail header using the name, email posted via the form.

In this code, we can see the PHP filter_var() applied to sanitize the form data before processing.

Once the email sent, the PHP mail() function will return boolean true. If so, it shows a success message to the user.

<?php if (! empty($_POST["send"])) { $name = filter_var($_POST["userName"], FILTER_SANITIZE_STRING); $email = filter_var($_POST["userEmail"], FILTER_SANITIZE_EMAIL); $subject = filter_var($_POST["subject"], FILTER_SANITIZE_STRING); $message = filter_var($_POST["message"], FILTER_SANITIZE_STRING); $toEmail = "to_email@gmail.com"; $mailHeaders = "From: " . $name . "<" . $email . ">\r\n"; if (mail($toEmail, $subject, $message, $mailHeaders)) { ?> <div id="success">Your contact information is received successfully!</div> <?php } } ?> 

In a previous article, we have seen an example to send an email with Gmail SMTP using PHPMailer library.

PHP contact form popup with jQuery Output

The figure shows the screenshot of the contact form popup dialog. You can see the clickable icon that is behind the overlay.

I have taken this screenshot by sending the form with an empty message and invalid email format. In the following screenshot, you can see the corresponding error message in the popup dialog. This is how this form alerts users about the improper data entered by them.

Contact Form Popup Output

After sending the contact email, this message helps to acknowledge the user. This acknowledgment will toggle off the contact form popup.

Contact Mail Success

Conclusion

Contact form in your application will help to gather information from the user. It will give you valuable feedback, ideas from the end-user to grow your business.

We have seen the advantages and disadvantages of having a popup contact form interface in an application. Also, we have seen lot of information about the purposes, basic need to integrate a contact form in an application.

The example code we have created for this article will reduce your effort to create a contact form for your application. It is a basic solution for the one who wants to deploy a medium to interact with the site users.

View Demo Download

↑ Back to Top

Posted on Leave a comment

Extract images from URL in excel with PHP using PhpSpreadsheet

Last modified on August 7th, 2019 by Vincy.

There are various ways to extract images from a given URL. PHP contains built-in functions for extracting data including the images with a URL.

This article is for PHP code to extract images from URLs existing in an excel file.

I have used PhpSpreadsheet to read the URLs from an Excel file. Then, I created cURL script to extract images from the URL.

Extract Images from URL Read from Excel

PhpSpreadsheet library supports Excel read-write operations. It provides enormous features like formatting content, manipulating data and more. It has a rich set of built-in classes and thereby makes the development process easy.

Working with spreadsheets is a common need while handling excel data via programming. PhpSpreadsheet library reduces the developer’s effort on building applications with excel data handing.

We have already seen several examples of URL extract using PHP. Also, we have created code for getting video thumbnail from Youtube URL.

What is inside?

  1. Uses of extracting images from URL from Excel
  2. Advantages of PhpSpreadsheet Library
  3. Existing PHP libraries used to import-export
  4. File Structure
  5. About this example
  6. PHP code to load and extract image data
  7. Render extracted images in a gallery
  8. Database script
  9. Extract images from URL in excel using PhpSpreadsheet Output

Extracting of images from URL from an excel file will be helpful in many scenarios. Below list shows some scenarios.

  1. To import a large volume of images into your application’s media library.
  2. To migrate media files from one domain to another.
  3. To restore the Excel backup images into a database.
  4. To create a dynamic photo gallery without a database.

Advantages of PhpSpreadsheet Library

PhpSpreadsheet has many features and thereby has more advantages of using it.

  • It provides methods to prepare reports, charts, plans and more.
  • It has an option the read, write from a specified row, column and sheet of a spreadsheet document.
  • It is suitable for handling a large amount of data.
  • It helps to manage checklists, calendars, timesheets, schedules, proposal plans.
  • It provides security to protect spreadsheet data from editing.
  • It supports encryption to prevent the spreadsheet data from viewing. 

Existing PHP libraries used to import-export

There are many PHP libraries available in the market support spreadsheet data handling.

  • PortPHP supports import-export data between Excel, CSV and database storages. It has readers, writers and converters to process data exchange and manipulation.
  • The Spout is a PHP library used to read write spreadsheets in an efficient way. It supports three types of spreadsheets XLS, CSV, ODS.

File structure

Below screenshot shows the file structure of this example. The ExcelImportService class file is an integral part of this example. It loads PhpSpreadsheet library and covers all the operations related to the excel image extract.

The excel_template folder contains an input Excel file with image URLs. This example code loads this file to extract images from the URL.

Instead of using this fixed excel template, you can also allow users to choose an excel file. By adding a HTML form with a file input option user can choose their excel to explore extract.

Extract Images from URL from the Excel File Structure

About this example

This example loads an input Excel file in an Import service class. This sample excel file will contain image URLs.

In this example, I have used PhpSpreadsheet library to read the excel data. This library method helps to get the URLs and store into an array.

Then I iterate this URL array in a loop to extract the image data. I used PHP cURL script to extract images. In a previous tutorial, we have seen how to run PHP cURL script to extract content from a remote URL.

Finally, this code will store the extracted images into a directory and save the path to the database. In a previous article, we import excel data into a database without images. Also, we have seen examples to import data from CSV to a database.

PHP code to load and extract image data

This PHP code loads the ExcelImportService class to load and import image data from an excel.

This is the main PHP class created for this example. It handles all operations during the excel image extract.

<?php use \Phppot\ExcelImportService; require_once 'Class/ExcelImportService.php'; $excelImportService = new ExcelImportService(); $excelDataArray = $excelImportService->loadExcel(); if (! empty($excelDataArray)) { $isNewData = $excelImportService->importImages($excelDataArray); if ($isNewData) { $message = "Images extracted from excel successfully!"; } else { $message = "No new images found during the excel extract!"; } } $imageResult = $excelImportService->getAllImages(); ?> 

ExcelImportService.php

This class loads the PhpSpreadsheet library. It also has the DataSource instance in the class level.

The database access request from this class uses this instance. It is for saving the extracted image path to the database.

Note: Download PhpSpreadsheet library from Github without dependencies. Then run the get the dependencies via composer by using the following command.

composer require phpoffice/phpspreadsheet 

In this class, the loadExcel() function loads the input excel to read the URLs as an array. It returns this array to extract image blob via cURL request. 

The extractImage() function executes the cURL script. It gets the image resource data from the remote URL read from Excel. Then it writes the file into a target as specified in this example.

After putting the extracted images into a folder, then the code saves the to the image database table. The saveImagePath() method contains the insert query and parameters to invoke DataSource insert.

<?php namespace Phppot; use \Phppot\DataSource; require 'Vendor/PhpSpreadsheet/autoload.php'; class ExcelImportService { private $ds; function __construct() { require_once __DIR__ . './DataSource.php'; $this->ds = new DataSource(); } private function isUrlExist($url) { $query = 'SELECT * FROM tbl_images where remote_url = ?'; $paramType = 's'; $paramValue = array($url); $count = $this->ds->numRows($query, $paramType, $paramValue); return $count; } private function extractImage($url) { $path = pathinfo($url); $imageTargetPath = 'uploads/' . time() . $path['basename']; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_VERBOSE, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_AUTOREFERER, false); curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // <-- important to specify curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); // <-- important to specify $resultImage = curl_exec($ch); curl_close($ch); $fp = fopen($imageTargetPath, 'wb'); fwrite($fp, $resultImage); fclose($fp); $imageInfo["image_name"] = $path['basename']; $imageInfo["image_path"] = $imageTargetPath; return $imageInfo; } private function saveImagePath($imageInfo, $remoteUrl) { $query = "INSERT INTO tbl_images (image_name,image_path, remote_url) VALUES (?, ?, ?)"; $paramType = 'sss'; $paramValue = array($imageInfo["image_name"], $imageInfo["image_path"], $remoteUrl); $this->ds->insert($query, $paramType, $paramValue); } public function loadExcel() { //create directly an object instance of the IOFactory class, and load the xlsx file $xlsFile ='Excel_Template/imageURLs.xlsx'; $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($xlsFile); //read excel data and store it into an array $excelData = $spreadsheet->getActiveSheet()->toArray(null, true, true, true); $rowCount = count($excelData); $urlArray = array(); for($i=2;$i<$rowCount;$i++) { $url = $excelData[$i]['A']; if(!empty($url)) { $urlArray[] = $url; } } return $urlArray; } public function importImages($excelDataArray) { $isNewData = false; foreach($excelDataArray as $url) { $isUrlExist = $this->isUrlExist($url); if (empty($isUrlExist)) { $imageInfo = $this->extractImage($url); if(!empty($imageInfo)) { $this->saveImagePath($imageInfo, $url); } $isNewData = true; } } return $isNewData; } public function getAllImages() { $query = 'SELECT * FROM tbl_images'; $result = $this->ds->select($query); return $result; } }

DataSource.php

This is a common PHP class that we have used in many examples. It contains functions to execute the database operations planned for the example code. It establishes the database connection at its constructor.

Model classes used in our PHP examples load this class and instantiate it to access the database.

<?php namespace Phppot; /** * Generic datasource class for handling DB operations. * Uses MySqli and PreparedStatements. * * @version 2.3 */ class DataSource { // PHP 7.1.0 visibility modifiers are allowed for class constants. // when using above 7.1.0, declare the below constants as private const HOST = 'localhost'; const USERNAME = 'root'; const PASSWORD = ''; const DATABASENAME = 'phpsamples'; private $conn; /** * PHP implicitly takes care of cleanup for default connection types. * So no need to worry about closing the connection. * * Singletons not required in PHP as there is no * concept of shared memory. * Every object lives only for a request. * * Keeping things simple and that works! */ function __construct() { $this->conn = $this->getConnection(); } /** * If connection object is needed use this method and get access to it. * Otherwise, use the below methods for insert / update / etc. * * @return \mysqli */ public function getConnection() { $conn = new \mysqli(self::HOST, self::USERNAME, self::PASSWORD, self::DATABASENAME); if (mysqli_connect_errno()) { trigger_error("Problem with connecting to database."); } $conn->set_charset("utf8"); return $conn; } /** * To get database results * @param string $query * @param string $paramType * @param array $paramArray * @return array */ public function select($query, $paramType="", $paramArray=array()) { $stmt = $this->conn->prepare($query); if(!empty($paramType) && !empty($paramArray)) { $this->bindQueryParams($sql, $paramType, $paramArray); } $stmt->execute(); $result = $stmt->get_result(); if ($result->num_rows > 0) { while ($row = $result->fetch_assoc()) { $resultset[] = $row; } } if (! empty($resultset)) { return $resultset; } } /** * To insert * @param string $query * @param string $paramType * @param array $paramArray * @return int */ public function insert($query, $paramType, $paramArray) { $stmt = $this->conn->prepare($query); $this->bindQueryParams($stmt, $paramType, $paramArray); $stmt->execute(); $insertId = $stmt->insert_id; return $insertId; } /** * To execute query * @param string $query * @param string $paramType * @param array $paramArray */ public function execute($query, $paramType="", $paramArray=array()) { $stmt = $this->conn->prepare($query); if(!empty($paramType) && !empty($paramArray)) { $this->bindQueryParams($stmt, $paramType="", $paramArray=array()); } $stmt->execute(); } /** * 1. Prepares parameter binding * 2. Bind prameters to the sql statement * @param string $stmt * @param string $paramType * @param array $paramArray */ public function bindQueryParams($stmt, $paramType, $paramArray=array()) { $paramValueReference[] = & $paramType; for ($i = 0; $i < count($paramArray); $i ++) { $paramValueReference[] = & $paramArray[$i]; } call_user_func_array(array( $stmt, 'bind_param' ), $paramValueReference); } /** * To get database results * @param string $query * @param string $paramType * @param array $paramArray * @return array */ public function numRows($query, $paramType="", $paramArray=array()) { $stmt = $this->conn->prepare($query); if(!empty($paramType) && !empty($paramArray)) { $this->bindQueryParams($stmt, $paramType, $paramArray); } $stmt->execute(); $stmt->store_result(); $recordCount = $stmt->num_rows; return $recordCount; } } 

This is the HTML code to display the extracted images in the UI. I embed PHP code with this HTML to display the image path from the database dynamically.

The getAllImages() method fetches image results from the database. It returns an array of images extracted from the Excel. This array data iteration helps to render images in a gallery view.

<!doctype html> <html> <head> <link rel="stylesheet" type="text/css" href="CSS/style.css"> <title>Extract Images from URL in Excel using PHPSpreadSheet with PHP</title> </head> <body> <div id="gallery"> <div id="image-container"> <h2>Extract Images from URL in Excel using PHPSpreadSheet with PHP</h2> <?php if (! empty($message)) { ?> <div id="txtresponse"><?php echo $message; ?></div> <?php } ?> <ul id="image-list"> <?php if (! empty($imageResult)) { foreach ($imageResult as $k => $v) { ?> <li><img src="<?php echo $imageResult[$k]['image_path']; ?>" class="image-thumb" alt="<?php echo $imageResult[$k]['image_name'];?>"></li> <?php } } ?> </ul> </div> </div> </body> </html> 

After a successful image extract, this UI will acknowledge the user. It shows an appropriate message based on the image extract result.

If you extract an older excel that was already done, then the notification will say “No new images found”.

The following styles are used to present the extracted images in a gallery.

body { font-family: Arial; color: #212121; text-align: center; } #gallery { width: 1057px; margin: 0 auto; } #image-list { list-style-type: none; margin: 0; padding: 0; } #image-list li { margin: 10px 20px 10px 0px; display: inline-block; } #image-list li img { width: 250px; height: 155px; } #image-container { margin-bottom: 14px; } #txtresponse { padding: 10px 40px; border-radius: 3px; margin: 10px 0px 30px 0px; border: #ecdeaa 1px solid; color: #848483; background: #ffefb6; display: inline-block; } .btn-submit { padding: 10px 30px; background: #333; border: #E0E0E0 1px solid; color: #FFF; font-size: 0.9em; width: 100px; border-radius: 0px; cursor: pointer; position: absolute; } .image-thumb { background-color: grey; padding: 10px; } 

Database script

This SQL script is for creating the required database table in your environment. It has the create a statement of the tbl_images database table. This table is the storage the point to store the image local path.

Run this script before executing this example. You can also get the SQL script from the downloadable source code added with this article.

CREATE TABLE IF NOT EXISTS `tbl_images` ( `id` int(11) NOT NULL AUTO_INCREMENT, `image_name` varchar(50) NOT NULL, `image_path` varchar(50) NOT NULL, `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=263 ; 

The screenshot below shows the image gallery output. These images are from the uploads folder of this example. This is the local location to store the extracted images from the database.

This screen shows the user acknowledgment message above the gallery view. This acknowledgment varies based on the input excel file data.

Extract Images from URL from the Excel Output

If the input excel is too older and extracted already, then the below message will notify the user.

No New Images

Hope this article helps you to image extract from URLs present in excel. The example presented is the simplest way of demonstrating image extract from Excel.

Download

↑ Back to Top