Web Forms: Validation, Submission, and Processing
In this tutorial, I want us to try something rather very popular in web programming but still comes as a challenge to some coders. I’m going to demonstrate how to handle forms in web applications, from creation of the forms with Bootstrap, validating the form with jQuery Form Validator plugin, submitting the form data using Ajax, and processing this data in PHP & MySQL RBMS, and finally handling the feedback from the form processing feed back from PHP.
Let’s start with creating a simple User Registration form using Twitter Bootstrap which is the most popular HTML, CSS, and JavaScript framework for developing responsive, mobile-first web sites. Our form will look like this;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<form id="form" role="form" action="" method="POST" enctype="multipart/form-data"> <div class="form-title"> <h2>Register </h2> <h4>To get started.</h4> </div> <div class="form-group"> <label class="control-label" for="username"> <span class="" id="">Username: </span> </label> <input type="text" class="form-control input-sm" name="username"/> </div> <div class="form-group"> <label class="" for="email"> <span class="" id="">Email: </span> </label> <input type="text" class="form-control input-sm" name="email"/> </div> <div class="form-group"> <label class="" for="password"> <span class="" id="">Password: </span> </label> <input type="password" class="form-control input-sm" id="password" name="password"/> </div> <div class="form-group"> <label class="" for="confirm_password"> <span class="" id="">Confirm Password: </span> </label> <input type="password" class="form-control input-sm" name="confirm_password"/> </div> <div class="form-group"> <div class="controls"> <label class="checkbox-inline"> <input type="checkbox" name="agreement">By signing up, I agree to the <a href="#" target="">Terms & Conditions</a> </label> </div> </div> <input type="button" class="btn btn-default" id="submit" name="submit" value="Register"/> </form> |
Form Validation
With the form ready, let’s add some validation to it using jQuery validator. For validating this form, I extended the plugin with a custom function “regex” and it will be used to validate a field based on a particular regex pattern. For example; below is a regex pattern to enforce a password String to be alphanumeric and 8-16 characters long
1 2 3 4 5 6 |
$reg_pswd = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,16}$/g; $.validator.addMethod( 'regex', function(value, element, pattern) { var regexp = new RegExp(pattern); return this.optional(element) || regexp.test(value); }, "Value entered doesn't match the required pattern" ); |
Commonly when registering users on your website, you will want to check the user is not already registered. This can be achieved by checking if the user’s provided email address is not existent in the database.
To demonstrate this, we have to create a simple MySQL database like this
1 2 3 4 5 6 7 8 9 10 11 |
CREATE DATABASE tut_form; USE tut_form; CREATE TABLE `tut_form`.`user` ( `id` INT NOT NULL AUTO_INCREMENT, `username` VARCHAR(15) NOT NULL , `email` VARCHAR(50) NOT NULL , `password` VARCHAR(100) NOT NULL , PRIMARY KEY (`id`) ) ENGINE = InnoDB; |
Next, the PHP script that will search the MySQL database to find if the provided email already exists in the user table or not
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
$stmt = $dbConn->prepare ( sprintf ( " SELECT email FROM %s WHERE email = :email", $table ) ); if(!empty($_POST['email'])){ $email = $_POST['email']; $stmt->execute ( compact ( 'email' ) ); $existsUser = $stmt->fetch ( \PDO::FETCH_ASSOC ); if($existsUser['email'] == $email) { echo 'false'; } else { echo 'true'; } }else{ echo 'Email address not received'; } |
The jQuery Validator plugin uses the remote method to process url request. Note: the url script must return a String not Json, nor a Boolean
To validate confirm password field, we use the jQuery validator plugin equalTo method and we provide the id of the field whose value is to be compared with.
Below is the complete form validation code snippet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
$('#form').validate({ rules: { username: { required: true, minlength: 5, maxlength: 15 }, email: { required: true, email: true, remote: { url: "isEmailAvailable.php", type: "post", data: { email: function() { return $( "#form :input[name='email']" ).val(); } } } }, password: { required: true, regex: $reg_pswd }, confirm_password: { required: true, equalTo: "#password" }, agreement: { required: true } }, messages: { username: { required: "Please enter a username", minlength: "Username must be 5-15 characters", maxlength: "Username must be 5-15 characters" }, email: { required: "Please provide an email address", email: "Please enter a valid email address.", remote: jQuery.validator.format("Email address: '{0}' is already registered.") }, password: { required: "Please provide your password", regex: "Password must be alphanumeric, 8-16 uppercase &amp; lowercase characters" }, confirm_password: { required: "Please confirm password", equalTo: "Passwords are not matching" }, agreement: { required: "(Agreed?) " } }, /* Make the error message appear in a 'span' */ errorElement: "span", /* Give error span class '.error-message' so that it's highlighted */ errorClass: "error-message", /* Handle where the error message will appear if '.input-group' is used in form-group */ errorPlacement: function(error, element) { if(element.parent('.input-group').length) { error.insertAfter(element.parent()); } else { error.insertAfter(element); } } }); |
Form Submission
Our form has no ‘type – submit’ input simply because we don’t want to reload the page during submission. Instead we use a simple ‘type – button’ input to submit the form data.
We handle an onClick event listener to serialize the form and submit the form data using Ajax to the form processing script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$('#form').on('click', '#submit', function(){ /* Check if the entire form is valid, required fields will be checked here */ if($("#form").valid()){ $.ajax({ url: "processForm.php", type: 'POST', data: $('#form').serialize(), datatype: 'json', success: processResponse, error: function(e) { console.log(e.message); } }); } }); |
Form Processing
Now that validation is in place, lets write the script that will process our form data
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
$stmt = $dbConn->prepare ( sprintf ( ' INSERT INTO %s (username, email, password) VALUES (:username, :email, :password)', $table ) ); $stmt->bindParam ( ':username', $_POST['username'] ); $stmt->bindParam ( ':email', $_POST['email']); $stmt->bindParam ( ':password', sha1($_POST['password']) ); $result = null; try { $result = $stmt->execute (); } catch ( \PDOException $e ) { $response['exception'] = $e->getMessage(); } $response = array(); if(!$result) { $response['error'] = 'Registration failed'; }else{ $response['status'] = 'Registration successful'; } echo json_encode($response); |
Handle Processing Result
Handle form processing feedback, show any errors that might rise from the processing (not validation) plus show success message and refresh form.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function processResponse(response) { response = $.parseJSON(response); if (response.error) { $('<span class="error-message">'+response.error+'</span>').insertAfter( ".form-title" ); console.warn(response.exception); } else { alert(response.status); window.location.replace("index.html"); } } |
This have been a quite lengthy tutorial but is no better way to tackle this challenge plus I don’t like the idea of separating the same topic into many tutorials.
Anyway, I hope you can wrap you head around it and understand the in-depth explanations. Else feel free to leave me a comment in the comment section particularly regarding this very tutorial, or hit the contact page for any further unrelated inquires.