Advertisement

Create an SMS Signup Form: Part 1

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →
This post is part of a series called Create an SMS Signup form.
Create an SMS Signup Form: Part 2

SMS is a worldwide technology used by almost everyone with a cell phone. Despite having a much greater reach than the smartphone market, SMS programming receives far less covereage and often remains uncapitalized. In this multipart tutorial, I will show you how to create a web site signup form that allows users to enter a mobile number. I will then show you how to use PHP for server-side phone number verification to check that the mobile number the user has entered is actually his mobile phone. Along the way, we will use jQuery to perform client-side validation and CodeIgniter as our development framework.

Step 1: Planning

Let's begin with an overview of the solution. The user will access a signup form, select a country, and enter a mobile phone number. After submitting the signup form, the user will need to enter a verification code sent to the mobile phone number entered in order to active the new account. If the user closes the page before entering the verification code and later acceses the signup form again, the phone code form will still be displayed. Okay, let's begin!

Step 2: Download CodeIgniter

Download CodeIgniter from www.codeigniter.com. Next, extract the folder and copy it to the webroot of your web server (you must have access to a web server that can parse PHP in order to complete this tutorial). Copying to your webroot is optional, but doing so will help us better navigate through the source files.

In the application dir, navigate to the config directory and open config.php. This is the main configuration, where we need to set some of the settings, like in the code below:

$config['base_url']      =   "http://localhost/sending_sms/";
...
$config['index_page']    =   "index.php";
...
$config['uri_protocol']	 =   "AUTO";

Next, open database.php in the same config folder and complete the following assignments:

db['default']['hostname']  = "localhost";
$db['default']['username'] = "root";
$db['default']['password'] = "__password__";
$db['default']['database'] = "sms_users";

Finally, open up autoload.php in the config folder and let's add some libraries and helpers that we'll need:

$autoload['libraries'] = array('database');
...
$autoload['helper'] = array('url', 'form', 'cookie');

Step 3: Signup Controller

Let's create the signup controller. Create a file called signup.php in the application/controllers folder and let's begin coding the main signup functionality:

class Signup extends Controller {

	function Signup(){
		parent::Controller();
	}
	
	function index(){
		$this->load->view('signup' );
	}
	
}

I have created the Signup class, you'll notice it has the same name as the file, and that it extends Controller. I created the index() function that will handle the display of the signup form. We call it index because this is the default function that is called when accesing with the default url, without any segments. We'll also have to configure the default controller ( signup ) in config/routes.php:

$route['default_controller'] = "signup";

If you test the app now, you'll get an error telling you 'Unable to load the requested file: signup.php'. This is because we haven't created the signup view. Let's do this now.

Step 4: Signup View

Create a new file signup.php in the apllication/views folder and enter the following markup:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
  <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Signup</title>
  <link rel="stylesheet" href="<?php echo base_url(); ?>css/reset.css" type="text/css" />
  <link rel="stylesheet" href="<?php echo base_url(); ?>css/design.css" type="text/css" />
  </head>
  <body>
  <div id="wrap">
  <h2>Signup form</h2>
  <?php echo validation_errors('<div class="error">', '</div>'); ?>
  <?php echo form_open('signup/process', array('id'=>'form') ); ?>
  <?php echo form_label('Name:', 'name'); ?>
  <?php echo form_input( array('name'=>'name', 'id'=>'name', 'class'=>'required', 'value'=>set_value('name') ) ); ?>
  <?php echo form_label('Email:', 'email'); ?>
  <?php echo form_input( array('name'=>'email', 'id'=>'email', 'class'=>'required email', 'value'=>set_value('email') ) ); ?>
  <?php echo form_label('Country:', 'country'); ?>
  <?php echo form_dropdown('country', $countries, '','id="country" class="required"' ); ?>
  <?php echo form_label('Mobile phone:', 'mobile'); ?>
  <div><?php echo form_input( array('name'=>'prefix', 'id'=>'prefix', 'value'=>set_value('prefix') ) ); ?>
  <?php echo form_input( array('name'=>'mobile', 'id'=>'mobile', 'class'=>'required digits', 'value'=>set_value('mobile') ) ); ?>
  </div>
  <div align="center"><?php echo form_submit('register', 'Register', 'class="submit"' ); ?></div>
  </div>
  </body>
  </html>

Let me explain some of the markup. I have added in the head a reset.css file with the usual meyerweb reset CSS rules and a design.css file, which we will code later. I've put these two in a folder in the root called 'css'. I use base_url() to get the root in the view file.
I used the form helper to create the form opening, while giving the id of the form 'form', I created a couple of labels and inputs using the same form helper. You'll notice I pass an array as the second parameter of the form_input() function with id or class keys. For more information on using the form helper, read the CodeIgniter documentation. I used form_dropdown() to create a select box, which gets passed an array of options named $countries. We don't have this array yet, but we'll also make it a bit later.

Step 5: Signup CSS

Create the file design.css in the css directory and put in the apllication/views folder and enter the following css rules:

body {
	background-color: #CFCFCF;
	padding: 0px;
	margin: 0px;
	font-size: 12px;
	font-family: Helvetica, Verdana, Arial, sans-serif;
}

#wrap {
	width: 400px;
	margin-left: auto;
	margin-right: auto;
	margin-top: 40px;
	background-color: #ECECEC;
	border: solid 1px #FCFCFC;
	padding: 10px;
}
h2 {
	margin-bottom: 10px;
	font-size: 18px;
}

label {
	display: block;
}
input, select {
	width: 380px;
	padding: 5px;
	margin-bottom: 10px;
}
select {
	width: 390px;
}
#prefix {
	width: 50px;
	margin-right: 10px;
   	margin-bottom: 0px;
	display:inline;
}
#mobile {
	width: 305px;
	display:inline;
}
input.submit {
	background-color: #E0E0E0;
	border: solid 1px #C5C5C5;
	width: 140px;
	font-weight: bold;
	margin-top: 10px;
}
input.error, select.error {
	margin-bottom: 0px;
	border: solid 1px #FFCCCC;
}
div.error {
	padding: 5px;
	background-color: #ffeeee;
	border: solid 1px #FFCCCC;
	color: #ff0000;
}
div.ok {
	padding: 5px;
	background-color: #CCFFCC;
	border: solid 1px #44CC44;
	color:#336633;
	margin-bottom: 10px;
}

I created the wrap div style, centering it by setting margin-left and margin-right to auto. I then styled the inputs, making sure they are consistent across all browsers, and then created 3 error classes: .error, .ok, and input.error. We'll use this with the jQuery validation plugin. I also used 5px padding where I felt necessary for styling.

Step 6: Countries and Prefixes

If you test now, you'll get a notice saying 'undefined variable countries' and 'invalid argument supplied for foreach()'. How do we add the countries? Well, we could embed them directly on the select, but we'll use a configuration file instead. This ensures that we can change any country code easily and quickly. I copied the table with country codes from countrycode.org in a new file and used a clever bit of jQuery to process all the contents in a single string, giving me the final configuration file. I won't go into the details. If you want to know, leave a comment. Create a new file called 'countries.php' in the application/config folder and add the countries in the #config array using the following model:

$config = array();
$config[''] = '';
$config['93'] = 'Afghanistan';
$config['355'] = 'Albania';
$config['213'] = 'Algeria';
...
$config['967'] = 'Yemen';
$config['260'] = 'Zambia';
$config['263'] = 'Zimbabwe';

I used the prefix codes as keys in the array. How do we access this in the view? We'll load this particular configuration file in the controller function index() we created earlier:

function index(){
	$this->config->load('countries', true);
	$data['countries'] = $this->config->item('countries');
		
	$this->load->view('signup', $data );
}

I used '$this->config->load()' to load the config. You'll notice I also passed true as the second parameter. This makes a separate entry called 'countries' in the global configuration. If we don't pass true, the config settings ( the array from the config ) is added to the global configuration array. After that we just assign the config item to $data['countries'], which is then recognised by the view as $countries. I also pass the $data array to the view as the second parameter. If you test the page now, you'll get all the countries in the select box! Great right?

Step 7: Validation with jQuery

It's time to do some jQuery. We'll use jquery Validation plugin for this. This plugin is already in Microsoft's CDN so we'll get it from there, as well as jQuery from Google's CDN. We also need the metadata plugin in order to write the classes directly in the html:

<!-- html head --><br /><script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script><br /><script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js"></script><br /><script type="text/javascript" src="<?php echo base_url(); ?>js/jquery.metadata.js"></script>
<script type="text/javascript">
$(document).ready( function(){
	$('#form').validate( {'errorElement': 'div' } );
});
</script>
<!-- html head -->

The validation plugin works by suppling a class name to the input: required for requiring non-empty values, email for validation of an email adress, digits for numeric inputs, and more which I won't get into. You'll notice I hard coded the classes in the inputs, just look at the html at the begining. If you test now, you'll see that you get an error div after the input with a class of .error which I coded in the css. I also coded the input.error class, as the input that is not valid is assigned an .error class, too. This validates the form, if the user has JavaScript enabled.

Step 8: Changing the Phone Prefix Based on the Country Select

We'll now use a clever event to change the prefix input's value when the user selects a country. This is done only for visual purposes, as in the backend we'll get the prefix from the country value, but this shows the user how to enter his phone. Let's do this now:

<script type="text/javascript">
$(document).ready( function(){
	$('#form').validate( {'errorElement': 'div' } );
    //change the prefix based on the country select
	$('#country').change( function(){
    	$('#prefix').val( $('#country option:selected').val() );
	});
});
</script>

Ok, so I created a function to the change event of the select, and assigned the selected option's value to the prefix input value. Not a big deal, but it makes our signup form really easy.

This is all we need for the signup view. Let's create the activation function and view.

Step 9: Activation Code View

This is the page that the user will see after succesfully signing up. There will be a message telling him to check his mobile as we have sent him a code that he has to enter. In the signup.php controller, create a new function called activate():

function activate(){
    $data = array();
    $data['error'] = '';
    $this->load->view('activate', $data );
}

In case the activation is incorrect, I have defined an error variable for use in the view. This page will be accessed after we succesfully enter the user details and send the SMS. We'll also create a new file called activate.php in the application/views folder and enter the following markup:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
  <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Activate</title>
  <link rel="stylesheet" href="<?php echo base_url(); ?>css/reset.css" type="text/css" />
  <link rel="stylesheet" href="<?php echo base_url(); ?>css/design.css" type="text/css" />
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
  <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js"></script>
  <script type="text/javascript" src="<?php echo base_url(); ?>js/jquery.metadata.js"></script>
  <script type="text/javascript">
  $(document).ready( function(){
  	$('.ok').hide().fadeIn(800);
  	$('#form').validate( {'errorElement': 'div' } );
  });
  </script>
  </head>
  <body>
<div id="wrap">
  <h2>Confirm mobile phone</h2>
  <div class="ok">Your account has been created. A sms has been sent to your mobile with a 5 digit activation code. Please enter the code to complete signup:</div>
  <?php echo $error; ?>
  <?php echo form_open('signup/activate', array('id'=>'form') ); ?>
  <?php echo form_label('Code:', 'code'); ?>
  <?php echo form_input( array('name'=>'code', 'id'=>'code', 'class'=>'required digits', 'value'=>set_value('code') ) ); ?>
<div align="center"><?php echo form_submit('signup', 'Complete signup', 'class="submit"' ); ?></div>
  <hr />
  If you haven't received the sms, please <a href="#">click here</a> to send it again
</div>
</body>
</html>

The markup is pretty simple here. I have included the same jQuery and validation plugin. Inside the document.ready event, I called jquery.validate() like in the first page. We'll use validate for validating the authorization code. I also fade in the message div, to show a success message. I also put a class name of 'required' and 'digits' to resttrict the input field to numbers and nonempty values. There is also a message with an option for the user to resend the activation code, but I won't go into this functionality, as it is pretty similar to the basic signup.

Step 10: process() Background Function

Let's create the function that will process and insert the data in the database. We'll also use the validation library. It's important to have validation on the server, as some people can turn off JavaScript and so the validation won't work. This is a fallback that assures us we won't have any invalid data in the table. The function process is below:

function process(){
    $this->load->library('form_validation');
    
    if ( $this->form_validation->run() ){
    	$signup = array();
    	$signup['name'] = $this->input->post('name');
        $signup['email'] = $this->input->post('email');
        $signup['country'] = $this->input->post('country');
        $signup['mobile'] = $this->input->post('country').$this->input->post('mobile');
        
        //generate the unique activation code
        mt_rand();
        $signup['activation'] = rand( 11111, 99999 );
        
        //insert into db
        $this->db->insert('users', $signup );
        //send auth sms
        
        set_cookie('signed', $this->db->insert_id(), 86500 );
        //redirect
        redirect('signup/activate');
	} else {
    	$this->config->load('countries', true);
        $data['countries'] = $this->config->item('countries');
        $this->load->view('signup', $data );
    }
}

Well, that is a lot of code! Let's go through all the major parts:

We first load the form validation library. After that we use form validation's run() function to check for the correct values. If all the inputs pass, we make an array with all the post data using $this->input->post('input_name') and insert the data into a table called users, which we'll make in a moment. One thing to note here is that I also generate the auth code. I use mt_rand() to seed the random generator and generate a unique 5 cypher number. We do this by running rand() to generate a number between 11111 and 99999. This always gives us a number of 5 cyphers in length. I didn't use 10000 because we can get a number that repeats its cyphers, like 10002, which doesn't look too random. I also set a cookie called 'signed' with the value of the insert id from the actual insertion in the database and then redirect the user to the activation function. If some fields are invalid, we load the countries again and load the signup view. If you look at the signup view from the begining you'll see that I use set_value() to get the used value from the last signup. So if there is an invalid field, the rest of the fields will be filled with the last data. But there is one line we need to update in the signup view:

<?php echo form_dropdown('country', $countries, @$_POST['country'],'id="country" class="required"' ); ?>

I have replaced the third parameter with $_POST['country'], making sure it doesn't show a notice with @. This will set the select option to the one that was selected before the submit. If we don't have a country in the post data, we won't select anything, as is the case at the begining of the signup.

Step 11: Validation Rules

Ok, I bet you'll ask: How does the server knows what is the right format for the server-side validation ? Well, we have to create the rules for the validation. Fortunately, CodeIgniter allows you to write them in a config file called form_validation.php in the application/config folder. Create the file if it doesn't exist already and enter the following:

$config = array(
	array('field'=>'name',
		  'label'=>'Name',
		  'rules'=>'required'),
		  
	array('field'=>'email',
		  'label'=>'Email',
		  'rules'=>'required|valid_email|callback_check_email_exists'),
		  
	array('field'=>'country',
		  'label'=>'Country',
		  'rules'=>'required'),
		  
	array('field'=>'mobile',
		  'label'=>'Mobile phone',
		  'rules'=>'required|numeric')

);

I use this multidimensional array to set the corresponding rules for the validation. For example, for the name field, I use the key 'field' to set the rules for the name field, the 'label' key for setting the name of the field in the error message ( if it shows ) and the 'rules' key to set the rules. I separate the rules with a pipe characther (i.e. '|'). For more information on the available rules check the form validation documentation in the user guide. I also create a user defined callback ( callback_check_email_exists ) to check if the email exists. Adding callback_ to the rule name searches for a function in the controller and calls it. This function will check for the existence of the same email adress in the database, and the source is:

function check_email_exists( $email ){
	$rs = $this->db->where( 'email', $email )->count_all_results('users');
	$this->form_validation->set_message('check_email_exists', "We're sorry, this email already exists!");
	if( $rs < 1 ){
		return true;
	}
	return false;
}

This is a simple function which accepts an argument ( the value being checked ) and returns true or false depending on the format. We check if there is a result with the e-mail adress in the table, and if there is, we return false. We have to return true if we want to validate and false for invalid, so the function name is a bit akward.

Step 12: MySQL Table

Don't even think to test the actvation now! You'll get a nasty error because we haven't created the table ! Well, you can just download the source code and import the users table from the users.sql file or create it with the following commands:

CREATE TABLE `users` 
( `uid` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` TEXT NOT NULL,
`email` TEXT NOT NULL,
`country` INT NOT NULL,
`mobile` TEXT NOT NULL,
`activation` TEXT NOT NULL,
`active` INT NOT NULL,
PRIMARY KEY (`uid`) );

Run this code in phpMyAdmin and you'll have the table created.

If you test the code now, you'll get all the validation correct. You can just disable JavaScript to try the server validation. You won't get in with invalid data! I warned you!

End of part 1

This is the end of the first part. Next time, in part two of this series, we will cover actually sending SMS using a third party SMS gateway service. I hope you enjoyed my tutorial and thank you for staying to check it all out!

Advertisement